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