diff --git a/api/video_codecs/BUILD.gn b/api/video_codecs/BUILD.gn index aaa2a5750a..5bdd514a65 100644 --- a/api/video_codecs/BUILD.gn +++ b/api/video_codecs/BUILD.gn @@ -85,6 +85,7 @@ rtc_library("video_codecs_api") { "..: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 59d7b13e51..1a953a0c53 100644 --- a/api/video_codecs/av1_profile.cc +++ b/api/video_codecs/av1_profile.cc @@ -13,13 +13,11 @@ #include #include +#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 StringToAV1Profile(absl::string_view str) { absl::optional 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 bc9767631c..4651d93ef4 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 0f313e84a9..5e311d1134 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 webrtc { 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 bffbdc43d3..f38d46994b 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 @@ struct Dav1dDecoderTemplateAdapter { static std::vector 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 4dc3b18c21..e1e69eb082 100644 --- a/media/base/codec_unittest.cc +++ b/media/base/codec_unittest.cc @@ -228,11 +228,11 @@ TEST(CodecTest, TestAV1CodecMatches) { 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 @@ TEST(CodecTest, TestAV1CodecMatches) { // 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 @@ TEST(CodecTest, TestAV1CodecMatches) { // 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 2af0295a5a..1a7561aa52 100644 --- a/media/base/media_constants.cc +++ b/media/base/media_constants.cc @@ -124,8 +124,14 @@ const char kH265FmtpProfileCompatibilityIndicator[] = 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 877cc7a296..d5af17eb2a 100644 --- a/media/base/media_constants.h +++ b/media/base/media_constants.h @@ -147,8 +147,14 @@ RTC_EXPORT extern const char kH265FmtpProfileCompatibilityIndicator[]; 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 e761fd60c8..e623aeb43a 100644 --- a/media/engine/internal_decoder_factory.cc +++ b/media/engine/internal_decoder_factory.cc @@ -51,9 +51,10 @@ std::vector InternalDecoderFactory::GetSupportedFormats() 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 51d6a94dd6..e06b4c3d77 100644 --- a/media/engine/internal_decoder_factory_unittest.cc +++ b/media/engine/internal_decoder_factory_unittest.cc @@ -121,7 +121,7 @@ TEST(InternalDecoderFactoryTest, Av1Profile1_Dav1dDecoderTrialEnabled) { InternalDecoderFactory factory; std::unique_ptr decoder = factory.CreateVideoDecoder( SdpVideoFormat(cricket::kAv1CodecName, - {{kAV1FmtpProfile, + {{cricket::kAv1FmtpProfile, AV1ProfileToString(AV1Profile::kProfile1).data()}})); EXPECT_EQ(static_cast(decoder), kDav1dIsIncluded); } diff --git a/pc/webrtc_sdp.cc b/pc/webrtc_sdp.cc index 88f1ce0d1b..a255233ccc 100644 --- a/pc/webrtc_sdp.cc +++ b/pc/webrtc_sdp.cc @@ -2615,6 +2615,17 @@ static void BackfillCodecParameters(std::vector& codecs) { 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 0238c97aa0..382a4967d5 100644 --- a/pc/webrtc_sdp_unittest.cc +++ b/pc/webrtc_sdp_unittest.cc @@ -5082,13 +5082,14 @@ TEST_F(WebRtcSdpTest, BackfillsDefaultFmtpValues) { "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 @@ TEST_F(WebRtcSdpTest, BackfillsDefaultFmtpValues) { const auto* description = content.media_description(); ASSERT_NE(description, nullptr); const std::vector 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 @@ TEST_F(WebRtcSdpTest, BackfillsDefaultFmtpValues) { 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; }