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}
This commit is contained in:
Philipp Hancke 2024-01-03 16:47:05 +01:00 committed by WebRTC LUCI CQ
parent 89cf26f1e0
commit c1cc6a36b2
12 changed files with 51 additions and 21 deletions

View File

@ -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",

View File

@ -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> StringToAV1Profile(absl::string_view str) {
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;

View File

@ -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.

View File

@ -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;
}

View File

@ -23,7 +23,7 @@ struct Dav1dDecoderTemplateAdapter {
static std::vector<SdpVideoFormat> SupportedFormats() {
return {SdpVideoFormat("AV1"),
SdpVideoFormat(
"AV1", {{kAV1FmtpProfile,
"AV1", {{"profile",
AV1ProfileToString(AV1Profile::kProfile1).data()}})};
}

View File

@ -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));
}

View File

@ -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]

View File

@ -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;

View File

@ -51,9 +51,10 @@ std::vector<SdpVideoFormat> 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;

View File

@ -121,7 +121,7 @@ TEST(InternalDecoderFactoryTest, Av1Profile1_Dav1dDecoderTrialEnabled) {
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);
}

View File

@ -2615,6 +2615,17 @@ static void BackfillCodecParameters(std::vector<cricket::Codec>& 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");
}
}
}
}

View File

@ -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<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 @@ 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;
}