sdp: backfill default codec parameters for AV1
as required by
https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters
Also unify usage of profile fmtp parameter. Most notably this causes
SDP answers to include the default values.
These default values correspond to libaom's default values for AV1E_SET_TARGET_SEQ_LEVEL_IDX, AV1E_SET_TIER_MASK as used in
https://source.chromium.org/chromium/chromium/src/+/main:third_party/libaom/source/libaom/aom/aomcx.h
and g_profile in aom_codec_enc_cfg
https://source.chromium.org/chromium/chromium/src/+/main:third_party/libaom/source/libaom/aom/aom_encoder.h;l=415;drc=b58207f5aecc39db7d3da766e7d171e5d2c3598e
Note: AV1 is inconsistently cased in variable/struct/method/class names. The canonical casing should probably be "Av1" since it is an acronym standing for "AOMedia Video 1".
BUG=webrtc:15703
Change-Id: I11864b7666fea906cd1a0759c7ad45997beab90e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/331360
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#41654}
diff --git a/api/video_codecs/BUILD.gn b/api/video_codecs/BUILD.gn
index aaa2a57..5bdd514 100644
--- a/api/video_codecs/BUILD.gn
+++ b/api/video_codecs/BUILD.gn
@@ -85,6 +85,7 @@
"..:scoped_refptr",
"../../api:array_view",
"../../api:rtp_parameters",
+ "../../media:media_constants",
"../../modules/video_coding:codec_globals_headers",
"../../rtc_base:checks",
"../../rtc_base:logging",
diff --git a/api/video_codecs/av1_profile.cc b/api/video_codecs/av1_profile.cc
index 59d7b13..1a953a0 100644
--- a/api/video_codecs/av1_profile.cc
+++ b/api/video_codecs/av1_profile.cc
@@ -13,13 +13,11 @@
#include <map>
#include <utility>
+#include "media/base/media_constants.h"
#include "rtc_base/string_to_number.h"
namespace webrtc {
-// Parameter name in the format parameter map for AV1 video.
-const char kAV1FmtpProfile[] = "profile";
-
absl::string_view AV1ProfileToString(AV1Profile profile) {
switch (profile) {
case AV1Profile::kProfile0:
@@ -51,7 +49,7 @@
absl::optional<AV1Profile> ParseSdpForAV1Profile(
const CodecParameterMap& params) {
- const auto profile_it = params.find(kAV1FmtpProfile);
+ const auto profile_it = params.find(cricket::kAv1FmtpProfile);
if (profile_it == params.end())
return AV1Profile::kProfile0;
const std::string& profile_str = profile_it->second;
diff --git a/api/video_codecs/av1_profile.h b/api/video_codecs/av1_profile.h
index bc97676..4651d93 100644
--- a/api/video_codecs/av1_profile.h
+++ b/api/video_codecs/av1_profile.h
@@ -20,9 +20,6 @@
namespace webrtc {
-// Profile information for AV1 video.
-extern RTC_EXPORT const char kAV1FmtpProfile[];
-
// Profiles can be found at:
// https://aomedia.org/av1/specification/annex-a/#profiles
// The enum values match the number specified in the SDP.
diff --git a/api/video_codecs/sdp_video_format.cc b/api/video_codecs/sdp_video_format.cc
index 0f313e8..5e311d1 100644
--- a/api/video_codecs/sdp_video_format.cc
+++ b/api/video_codecs/sdp_video_format.cc
@@ -20,6 +20,7 @@
#endif
#include "api/video_codecs/video_codec.h"
#include "api/video_codecs/vp9_profile.h"
+#include "media/base/media_constants.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/strings/string_builder.h"
@@ -29,8 +30,7 @@
namespace {
std::string H264GetPacketizationModeOrDefault(const CodecParameterMap& params) {
- constexpr char kH264FmtpPacketizationMode[] = "packetization-mode";
- const auto it = params.find(kH264FmtpPacketizationMode);
+ const auto it = params.find(cricket::kH264FmtpPacketizationMode);
if (it != params.end()) {
return it->second;
}
diff --git a/api/video_codecs/video_decoder_factory_template_dav1d_adapter.h b/api/video_codecs/video_decoder_factory_template_dav1d_adapter.h
index bffbdc4..f38d469 100644
--- a/api/video_codecs/video_decoder_factory_template_dav1d_adapter.h
+++ b/api/video_codecs/video_decoder_factory_template_dav1d_adapter.h
@@ -23,7 +23,7 @@
static std::vector<SdpVideoFormat> SupportedFormats() {
return {SdpVideoFormat("AV1"),
SdpVideoFormat(
- "AV1", {{kAV1FmtpProfile,
+ "AV1", {{"profile",
AV1ProfileToString(AV1Profile::kProfile1).data()}})};
}
diff --git a/media/base/codec_unittest.cc b/media/base/codec_unittest.cc
index 4dc3b18..e1e69eb 100644
--- a/media/base/codec_unittest.cc
+++ b/media/base/codec_unittest.cc
@@ -228,11 +228,11 @@
VideoCodec c_no_profile =
cricket::CreateVideoCodec(95, cricket::kAv1CodecName);
VideoCodec c_profile0 = cricket::CreateVideoCodec(95, cricket::kAv1CodecName);
- c_profile0.params[webrtc::kAV1FmtpProfile] = kProfile0;
+ c_profile0.params[cricket::kAv1FmtpProfile] = kProfile0;
VideoCodec c_profile1 = cricket::CreateVideoCodec(95, cricket::kAv1CodecName);
- c_profile1.params[webrtc::kAV1FmtpProfile] = kProfile1;
+ c_profile1.params[cricket::kAv1FmtpProfile] = kProfile1;
VideoCodec c_profile2 = cricket::CreateVideoCodec(95, cricket::kAv1CodecName);
- c_profile2.params[webrtc::kAV1FmtpProfile] = kProfile2;
+ c_profile2.params[cricket::kAv1FmtpProfile] = kProfile2;
// An AV1 entry with no profile specified should be treated as profile-0.
EXPECT_TRUE(c_profile0.Matches(c_no_profile));
@@ -248,7 +248,7 @@
// Two AV1 entries with profile 0 specified are treated as duplicates.
VideoCodec c_profile0_eq =
cricket::CreateVideoCodec(95, cricket::kAv1CodecName);
- c_profile0_eq.params[webrtc::kAV1FmtpProfile] = kProfile0;
+ c_profile0_eq.params[cricket::kAv1FmtpProfile] = kProfile0;
EXPECT_TRUE(c_profile0.Matches(c_profile0_eq));
}
@@ -256,7 +256,7 @@
// Two AV1 entries with profile 1 specified are treated as duplicates.
VideoCodec c_profile1_eq =
cricket::CreateVideoCodec(95, cricket::kAv1CodecName);
- c_profile1_eq.params[webrtc::kAV1FmtpProfile] = kProfile1;
+ c_profile1_eq.params[cricket::kAv1FmtpProfile] = kProfile1;
EXPECT_TRUE(c_profile1.Matches(c_profile1_eq));
}
diff --git a/media/base/media_constants.cc b/media/base/media_constants.cc
index 2af0295..1a7561a 100644
--- a/media/base/media_constants.cc
+++ b/media/base/media_constants.cc
@@ -124,8 +124,14 @@
const char kH265FmtpInteropConstraints[] = "interop-constraints";
const char kH265FmtpTxMode[] = "tx-mode";
+// draft-ietf-payload-vp9
const char kVP9ProfileId[] = "profile-id";
+// https://aomediacodec.github.io/av1-rtp-spec/
+const char kAv1FmtpProfile[] = "profile";
+const char kAv1FmtpLevelIdx[] = "level-idx";
+const char kAv1FmtpTier[] = "tier";
+
const int kDefaultVideoMaxFramerate = 60;
// Max encode quantizer for VP8/9 and AV1 encoders assuming libvpx/libaom API
// range [0, 63]
diff --git a/media/base/media_constants.h b/media/base/media_constants.h
index 877cc7a..d5af17e 100644
--- a/media/base/media_constants.h
+++ b/media/base/media_constants.h
@@ -147,8 +147,14 @@
RTC_EXPORT extern const char kH265FmtpInteropConstraints[];
RTC_EXPORT extern const char kH265FmtpTxMode[];
+// draft-ietf-payload-vp9
extern const char kVP9ProfileId[];
+// https://aomediacodec.github.io/av1-rtp-spec/
+extern const char kAv1FmtpProfile[];
+extern const char kAv1FmtpLevelIdx[];
+extern const char kAv1FmtpTier[];
+
extern const int kDefaultVideoMaxFramerate;
extern const int kDefaultVideoMaxQpVpx;
extern const int kDefaultVideoMaxQpH26x;
diff --git a/media/engine/internal_decoder_factory.cc b/media/engine/internal_decoder_factory.cc
index e761fd6..e623aeb 100644
--- a/media/engine/internal_decoder_factory.cc
+++ b/media/engine/internal_decoder_factory.cc
@@ -51,9 +51,10 @@
if (kDav1dIsIncluded) {
formats.push_back(SdpVideoFormat(cricket::kAv1CodecName));
- formats.push_back(SdpVideoFormat(
- cricket::kAv1CodecName,
- {{kAV1FmtpProfile, AV1ProfileToString(AV1Profile::kProfile1).data()}}));
+ formats.push_back(
+ SdpVideoFormat(cricket::kAv1CodecName,
+ {{cricket::kAv1FmtpProfile,
+ AV1ProfileToString(AV1Profile::kProfile1).data()}}));
}
return formats;
diff --git a/media/engine/internal_decoder_factory_unittest.cc b/media/engine/internal_decoder_factory_unittest.cc
index 51d6a94..e06b4c3 100644
--- a/media/engine/internal_decoder_factory_unittest.cc
+++ b/media/engine/internal_decoder_factory_unittest.cc
@@ -121,7 +121,7 @@
InternalDecoderFactory factory;
std::unique_ptr<VideoDecoder> decoder = factory.CreateVideoDecoder(
SdpVideoFormat(cricket::kAv1CodecName,
- {{kAV1FmtpProfile,
+ {{cricket::kAv1FmtpProfile,
AV1ProfileToString(AV1Profile::kProfile1).data()}}));
EXPECT_EQ(static_cast<bool>(decoder), kDav1dIsIncluded);
}
diff --git a/pc/webrtc_sdp.cc b/pc/webrtc_sdp.cc
index 88f1ce0..a255233 100644
--- a/pc/webrtc_sdp.cc
+++ b/pc/webrtc_sdp.cc
@@ -2615,6 +2615,17 @@
if (!codec.GetParam(cricket::kH264FmtpPacketizationMode, &unused_value)) {
codec.SetParam(cricket::kH264FmtpPacketizationMode, "0");
}
+ } else if (absl::EqualsIgnoreCase(cricket::kAv1CodecName, codec.name)) {
+ // https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters
+ if (!codec.GetParam(cricket::kAv1FmtpProfile, &unused_value)) {
+ codec.SetParam(cricket::kAv1FmtpProfile, "0");
+ }
+ if (!codec.GetParam(cricket::kAv1FmtpLevelIdx, &unused_value)) {
+ codec.SetParam(cricket::kAv1FmtpLevelIdx, "5");
+ }
+ if (!codec.GetParam(cricket::kAv1FmtpTier, &unused_value)) {
+ codec.SetParam(cricket::kAv1FmtpTier, "0");
+ }
}
}
}
diff --git a/pc/webrtc_sdp_unittest.cc b/pc/webrtc_sdp_unittest.cc
index 0238c97..382a496 100644
--- a/pc/webrtc_sdp_unittest.cc
+++ b/pc/webrtc_sdp_unittest.cc
@@ -5082,13 +5082,14 @@
"a=setup:actpass\r\n"
"a=ice-ufrag:ETEn\r\n"
"a=ice-pwd:OtSK0WpNtpUjkY4+86js7Z/l\r\n"
- "m=video 9 UDP/TLS/RTP/SAVPF 96 97\r\n"
+ "m=video 9 UDP/TLS/RTP/SAVPF 96 97 98\r\n"
"c=IN IP4 0.0.0.0\r\n"
"a=rtcp-mux\r\n"
"a=sendonly\r\n"
"a=mid:0\r\n"
"a=rtpmap:96 H264/90000\r\n"
"a=rtpmap:97 VP9/90000\r\n"
+ "a=rtpmap:98 AV1/90000\r\n"
"a=ssrc:1234 cname:test\r\n";
JsepSessionDescription jdesc(kDummyType);
EXPECT_TRUE(SdpDeserialize(sdp, &jdesc));
@@ -5097,7 +5098,7 @@
const auto* description = content.media_description();
ASSERT_NE(description, nullptr);
const std::vector<cricket::Codec> codecs = description->codecs();
- ASSERT_EQ(codecs.size(), 2u);
+ ASSERT_EQ(codecs.size(), 3u);
std::string value;
EXPECT_EQ(codecs[0].name, "H264");
@@ -5107,4 +5108,13 @@
EXPECT_EQ(codecs[1].name, "VP9");
EXPECT_TRUE(codecs[1].GetParam("profile-id", &value));
EXPECT_EQ(value, "0");
+
+ EXPECT_EQ(codecs[2].name, "AV1");
+ EXPECT_TRUE(codecs[2].GetParam("profile", &value));
+ EXPECT_EQ(value, "0");
+ EXPECT_TRUE(codecs[2].GetParam("level-idx", &value));
+ EXPECT_EQ(value, "5");
+ EXPECT_TRUE(codecs[2].GetParam("tier", &value));
+ EXPECT_EQ(value, "0");
+ RTC_LOG(LS_ERROR) << sdp;
}