diff --git a/README.md b/README.md index 202705de7..3b6b987f1 100755 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ Other important wiki: ## V4 changes +* v4.0, 2021-04-29, RTC: Support av1 for Chrome M90. 4.0.91 * v4.0, 2021-04-24, Change push-RTSP as deprecated feature. * v4.0, 2021-04-24, Player: Change the default from RTMP to HTTP-FLV. * v4.0, 2021-04-24, Disable CherryPy by --cherrypy=off. 4.0.90 diff --git a/trunk/research/players/js/srs.sdk.js b/trunk/research/players/js/srs.sdk.js index 2b9f21feb..6bde3fb8f 100644 --- a/trunk/research/players/js/srs.sdk.js +++ b/trunk/research/players/js/srs.sdk.js @@ -470,3 +470,32 @@ function SrsRtcPlayerAsync() { return self; } +// Format the codec of RTCRtpSender, kind(audio/video) is optional filter. +// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs +function SrsRtcFormatSenders(senders, kind) { + var codecs = []; + senders.forEach(function (sender) { + sender.getParameters().codecs.forEach(function(c) { + if (kind && sender.track.kind !== kind) { + return; + } + + if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) { + return; + } + + var s = ''; + + s += c.mimeType.replace('audio/', '').replace('video/', ''); + s += ', ' + c.clockRate + 'HZ'; + if (sender.track.kind === "audio") { + s += ', channels: ' + c.channels; + } + s += ', pt: ' + c.payloadType; + + codecs.push(s); + }); + }); + return codecs.join(", "); +} + diff --git a/trunk/research/players/rtc_publisher.html b/trunk/research/players/rtc_publisher.html index 4eac03193..dda6e80f8 100644 --- a/trunk/research/players/rtc_publisher.html +++ b/trunk/research/players/rtc_publisher.html @@ -55,6 +55,10 @@ SessionID: + + Audio:
+ Video: + Simulator: Drop @@ -81,6 +85,14 @@ $('#rtc_media_player').prop('srcObject', event.stream); }; + // https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs + sdk.pc.onicegatheringstatechange = function (event) { + if (sdk.pc.iceGatheringState === "complete") { + $('#acodecs').html(SrsRtcFormatSenders(sdk.pc.getSenders(), "audio")); + $('#vcodecs').html(SrsRtcFormatSenders(sdk.pc.getSenders(), "video")); + } + }; + // For example: // webrtc://r.ossrs.net/live/livestream var url = $("#txt_url").val(); diff --git a/trunk/src/app/srs_app_rtc_api.cpp b/trunk/src/app/srs_app_rtc_api.cpp index 2ff98ef1b..0916ec340 100644 --- a/trunk/src/app/srs_app_rtc_api.cpp +++ b/trunk/src/app/srs_app_rtc_api.cpp @@ -134,18 +134,20 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe // For client to specifies the EIP of server. string eip = r->query_get("eip"); + string codec = r->query_get("codec"); // For client to specifies whether encrypt by SRTP. string srtp = r->query_get("encrypt"); string dtls = r->query_get("dtls"); - srs_trace("RTC play %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, srtp=%s, dtls=%s", + srs_trace("RTC play %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s, srtp=%s, dtls=%s", streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str(), - srtp.c_str(), dtls.c_str() + codec.c_str(), srtp.c_str(), dtls.c_str() ); // The RTC user config object. SrsRtcUserConfig ruc; ruc.eip_ = eip; + ruc.codec_ = codec; ruc.publish_ = false; ruc.dtls_ = (dtls != "false"); @@ -500,14 +502,17 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter* w, ISrsHtt // For client to specifies the EIP of server. string eip = r->query_get("eip"); + string codec = r->query_get("codec"); - srs_trace("RTC publish %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s", - streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str() + srs_trace("RTC publish %s, api=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s", + streamurl.c_str(), api.c_str(), clientip.c_str(), app.c_str(), stream_name.c_str(), remote_sdp_str.length(), eip.c_str(), + codec.c_str() ); // The RTC user config object. SrsRtcUserConfig ruc; ruc.eip_ = eip; + ruc.codec_ = codec; ruc.publish_ = true; ruc.dtls_ = ruc.srtp_ = true; diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 2a83cb19c..c220f0f83 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -2771,6 +2771,38 @@ srs_error_t SrsRtcConnection::negotiate_publish_capability(SrsRtcUserConfig* ruc // Only choose one match opus codec. break; } + } else if (remote_media_desc.is_video() && ruc->codec_ == "av1") { + std::vector payloads = remote_media_desc.find_media_with_encoding_name("AV1X"); + if (payloads.empty()) { + return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid AV1 payload type"); + } + + for (int j = 0; j < (int)payloads.size(); j++) { + const SrsMediaPayloadType& payload = payloads.at(j); + + // Generate video payload for av1. + SrsVideoPayload* video_payload = new SrsVideoPayload(payload.payload_type_, payload.encoding_name_, payload.clock_rate_); + + // TODO: FIXME: Only support some transport algorithms. + for (int k = 0; k < (int)payload.rtcp_fb_.size(); ++k) { + const string& rtcp_fb = payload.rtcp_fb_.at(k); + + if (nack_enabled) { + if (rtcp_fb == "nack" || rtcp_fb == "nack pli") { + video_payload->rtcp_fbs_.push_back(rtcp_fb); + } + } + if (twcc_enabled && remote_twcc_id) { + if (rtcp_fb == "transport-cc") { + video_payload->rtcp_fbs_.push_back(rtcp_fb); + } + } + } + + track_desc->type_ = "video"; + track_desc->set_codec_payload((SrsCodecPayload*)video_payload); + break; + } } else if (remote_media_desc.is_video()) { std::vector payloads = remote_media_desc.find_media_with_encoding_name("H264"); if (payloads.empty()) { @@ -3050,6 +3082,14 @@ srs_error_t SrsRtcConnection::negotiate_play_capability(SrsRtcUserConfig* ruc, s remote_payload = payloads.at(0); track_descs = source->get_track_desc("audio", "opus"); + } else if (remote_media_desc.is_video() && ruc->codec_ == "av1") { + std::vector payloads = remote_media_desc.find_media_with_encoding_name("AV1X"); + if (payloads.empty()) { + return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid AV1 payload type"); + } + + remote_payload = payloads.at(0); + track_descs = source->get_track_desc("video", "AV1X"); } else if (remote_media_desc.is_video()) { // TODO: check opus format specific param vector payloads = remote_media_desc.find_media_with_encoding_name("H264"); diff --git a/trunk/src/app/srs_app_rtc_server.hpp b/trunk/src/app/srs_app_rtc_server.hpp index 1a375b241..988d65115 100644 --- a/trunk/src/app/srs_app_rtc_server.hpp +++ b/trunk/src/app/srs_app_rtc_server.hpp @@ -92,6 +92,7 @@ public: // Original variables from API. SrsSdp remote_sdp_; std::string eip_; + std::string codec_; // Generated data. SrsRequest* req_; diff --git a/trunk/src/core/srs_core_version4.hpp b/trunk/src/core/srs_core_version4.hpp index 8a2e0bb42..441c0fa7d 100644 --- a/trunk/src/core/srs_core_version4.hpp +++ b/trunk/src/core/srs_core_version4.hpp @@ -26,6 +26,6 @@ #define VERSION_MAJOR 4 #define VERSION_MINOR 0 -#define VERSION_REVISION 90 +#define VERSION_REVISION 91 #endif