Add IsSameRtpCodec method to Codec.
This is similar to MatchesRtpCodec but not an exact match of parameters, unspecified parameters are treated as default. Use IsSameRtpCodec for comparison when codec is configured via encodings. Bug: b:299588022 Change-Id: I0ea800e50af6f5666e3e867a928e15b0aa044635 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/365142 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Åsa Persson <asapersson@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43272}
This commit is contained in:
parent
6d815bdd9b
commit
929c02a479
@ -49,6 +49,10 @@ std::string GetFmtpParameterOrDefault(const CodecParameterMap& params,
|
||||
return default_value;
|
||||
}
|
||||
|
||||
bool HasParameter(const CodecParameterMap& params, const std::string& name) {
|
||||
return params.find(name) != params.end();
|
||||
}
|
||||
|
||||
std::string H264GetPacketizationModeOrDefault(const CodecParameterMap& params) {
|
||||
// If packetization-mode is not present, default to "0".
|
||||
// https://tools.ietf.org/html/rfc6184#section-6.2
|
||||
@ -215,6 +219,66 @@ bool MatchesWithReferenceAttributesAndComparator(
|
||||
return true; // Not a codec with a PT-valued reference.
|
||||
}
|
||||
|
||||
CodecParameterMap InsertDefaultParams(const std::string& name,
|
||||
const CodecParameterMap& params) {
|
||||
CodecParameterMap updated_params = params;
|
||||
if (absl::EqualsIgnoreCase(name, cricket::kVp9CodecName)) {
|
||||
if (!HasParameter(params, kVP9FmtpProfileId)) {
|
||||
if (std::optional<VP9Profile> default_profile =
|
||||
ParseSdpForVP9Profile({})) {
|
||||
updated_params.insert(
|
||||
{kVP9FmtpProfileId, VP9ProfileToString(*default_profile)});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (absl::EqualsIgnoreCase(name, cricket::kAv1CodecName)) {
|
||||
if (!HasParameter(params, cricket::kAv1FmtpProfile)) {
|
||||
if (std::optional<AV1Profile> default_profile =
|
||||
ParseSdpForAV1Profile({})) {
|
||||
updated_params.insert({cricket::kAv1FmtpProfile,
|
||||
AV1ProfileToString(*default_profile).data()});
|
||||
}
|
||||
}
|
||||
if (!HasParameter(params, cricket::kAv1FmtpTier)) {
|
||||
updated_params.insert({cricket::kAv1FmtpTier, AV1GetTierOrDefault({})});
|
||||
}
|
||||
if (!HasParameter(params, cricket::kAv1FmtpLevelIdx)) {
|
||||
updated_params.insert(
|
||||
{cricket::kAv1FmtpLevelIdx, AV1GetLevelIdxOrDefault({})});
|
||||
}
|
||||
}
|
||||
if (absl::EqualsIgnoreCase(name, cricket::kH264CodecName)) {
|
||||
if (!HasParameter(params, cricket::kH264FmtpPacketizationMode)) {
|
||||
updated_params.insert({cricket::kH264FmtpPacketizationMode,
|
||||
H264GetPacketizationModeOrDefault({})});
|
||||
}
|
||||
}
|
||||
#ifdef RTC_ENABLE_H265
|
||||
if (absl::EqualsIgnoreCase(name, cricket::kH265CodecName)) {
|
||||
if (std::optional<H265ProfileTierLevel> default_params =
|
||||
ParseSdpForH265ProfileTierLevel({})) {
|
||||
if (!HasParameter(params, cricket::kH265FmtpProfileId)) {
|
||||
updated_params.insert({cricket::kH265FmtpProfileId,
|
||||
H265ProfileToString(default_params->profile)});
|
||||
}
|
||||
if (!HasParameter(params, cricket::kH265FmtpLevelId)) {
|
||||
updated_params.insert({cricket::kH265FmtpLevelId,
|
||||
H265LevelToString(default_params->level)});
|
||||
}
|
||||
if (!HasParameter(params, cricket::kH265FmtpTierFlag)) {
|
||||
updated_params.insert({cricket::kH265FmtpTierFlag,
|
||||
H265TierToString(default_params->tier)});
|
||||
}
|
||||
}
|
||||
if (!HasParameter(params, cricket::kH265FmtpTxMode)) {
|
||||
updated_params.insert(
|
||||
{cricket::kH265FmtpTxMode, GetH265TxModeOrDefault({})});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return updated_params;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool MatchesWithCodecRules(const Codec& left_codec, const Codec& right_codec) {
|
||||
@ -307,4 +371,15 @@ std::optional<Codec> FindMatchingCodec(const std::vector<Codec>& codecs1,
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool IsSameRtpCodec(const Codec& codec, const RtpCodec& rtp_codec) {
|
||||
RtpCodecParameters rtp_codec2 = codec.ToCodecParameters();
|
||||
|
||||
return absl::EqualsIgnoreCase(rtp_codec.name, rtp_codec2.name) &&
|
||||
rtp_codec.kind == rtp_codec2.kind &&
|
||||
rtp_codec.num_channels == rtp_codec2.num_channels &&
|
||||
rtp_codec.clock_rate == rtp_codec2.clock_rate &&
|
||||
InsertDefaultParams(rtp_codec.name, rtp_codec.parameters) ==
|
||||
InsertDefaultParams(rtp_codec2.name, rtp_codec2.parameters);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "media/base/codec.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -39,6 +40,10 @@ std::optional<cricket::Codec> FindMatchingCodec(
|
||||
const std::vector<cricket::Codec>& codecs2,
|
||||
const cricket::Codec& codec_to_match);
|
||||
|
||||
// Similar to `Codec::MatchesRtpCodec` but not an exact match of parameters.
|
||||
// Unspecified parameters are treated as default.
|
||||
bool IsSameRtpCodec(const cricket::Codec& codec, const RtpCodec& rtp_codec);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MEDIA_BASE_CODEC_COMPARATORS_H_
|
||||
|
||||
@ -9,9 +9,14 @@
|
||||
*/
|
||||
#include "media/base/codec_comparators.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/audio_codecs/audio_format.h"
|
||||
#include "api/video_codecs/sdp_video_format.h"
|
||||
#include "api/video_codecs/vp9_profile.h"
|
||||
#include "media/base/codec.h"
|
||||
#include "media/base/media_constants.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -21,6 +26,8 @@ using cricket::CreateAudioCodec;
|
||||
using cricket::CreateVideoCodec;
|
||||
using cricket::kH264CodecName;
|
||||
using cricket::kH264FmtpPacketizationMode;
|
||||
using ::testing::TestWithParam;
|
||||
using ::testing::ValuesIn;
|
||||
|
||||
TEST(CodecComparatorsTest, CodecMatchesItself) {
|
||||
Codec codec = cricket::CreateVideoCodec("custom");
|
||||
@ -72,4 +79,129 @@ TEST(CodecComparatorsTest, StaticPayloadTypesIgnoreName) {
|
||||
EXPECT_TRUE(MatchesWithCodecRules(codec_1, codec_2));
|
||||
}
|
||||
|
||||
struct TestParams {
|
||||
std::string name;
|
||||
SdpVideoFormat codec1;
|
||||
SdpVideoFormat codec2;
|
||||
bool expected_result;
|
||||
};
|
||||
|
||||
using IsSameRtpCodecTest = TestWithParam<TestParams>;
|
||||
|
||||
TEST_P(IsSameRtpCodecTest, IsSameRtpCodec) {
|
||||
TestParams param = GetParam();
|
||||
Codec codec1 = cricket::CreateVideoCodec(param.codec1);
|
||||
Codec codec2 = cricket::CreateVideoCodec(param.codec2);
|
||||
|
||||
EXPECT_EQ(IsSameRtpCodec(codec1, codec2.ToCodecParameters()),
|
||||
param.expected_result);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
CodecTest,
|
||||
IsSameRtpCodecTest,
|
||||
ValuesIn<TestParams>({
|
||||
{.name = "CodecWithDifferentName",
|
||||
.codec1 = {"VP9", {}},
|
||||
.codec2 = {"VP8", {}},
|
||||
.expected_result = false},
|
||||
{.name = "Vp8WithoutParameters",
|
||||
.codec1 = {"vp8", {}},
|
||||
.codec2 = {"VP8", {}},
|
||||
.expected_result = true},
|
||||
{.name = "Vp8WithSameParameters",
|
||||
.codec1 = {"VP8", {{"x", "1"}}},
|
||||
.codec2 = {"VP8", {{"x", "1"}}},
|
||||
.expected_result = true},
|
||||
{.name = "Vp8WithDifferentParameters",
|
||||
.codec1 = {"VP8", {}},
|
||||
.codec2 = {"VP8", {{"x", "1"}}},
|
||||
.expected_result = false},
|
||||
{.name = "Av1WithoutParameters",
|
||||
.codec1 = {"AV1", {}},
|
||||
.codec2 = {"AV1", {}},
|
||||
.expected_result = true},
|
||||
{.name = "Av1WithSameProfile",
|
||||
.codec1 = {"AV1", SdpVideoFormat::AV1Profile0().parameters},
|
||||
.codec2 = {"AV1", SdpVideoFormat::AV1Profile0().parameters},
|
||||
.expected_result = true},
|
||||
{.name = "Av1WithoutParametersTreatedAsProfile0",
|
||||
.codec1 = {"AV1", SdpVideoFormat::AV1Profile0().parameters},
|
||||
.codec2 = {"AV1", {}},
|
||||
.expected_result = true},
|
||||
{.name = "Av1WithoutProfileTreatedAsProfile0",
|
||||
.codec1 = {"AV1", {{cricket::kAv1FmtpProfile, "0"}, {"x", "1"}}},
|
||||
.codec2 = {"AV1", {{"x", "1"}}},
|
||||
.expected_result = true},
|
||||
{.name = "Av1WithDifferentProfile",
|
||||
.codec1 = {"AV1", SdpVideoFormat::AV1Profile0().parameters},
|
||||
.codec2 = {"AV1", SdpVideoFormat::AV1Profile1().parameters},
|
||||
.expected_result = false},
|
||||
{.name = "Av1WithDifferentParameters",
|
||||
.codec1 = {"AV1", {{cricket::kAv1FmtpProfile, "0"}, {"x", "1"}}},
|
||||
.codec2 = {"AV1", {{cricket::kAv1FmtpProfile, "0"}, {"x", "2"}}},
|
||||
.expected_result = false},
|
||||
{.name = "Vp9WithSameProfile",
|
||||
.codec1 = {"VP9", SdpVideoFormat::VP9Profile0().parameters},
|
||||
.codec2 = {"VP9", SdpVideoFormat::VP9Profile0().parameters},
|
||||
.expected_result = true},
|
||||
{.name = "Vp9WithoutProfileTreatedAsProfile0",
|
||||
.codec1 = {"VP9", {{kVP9FmtpProfileId, "0"}, {"x", "1"}}},
|
||||
.codec2 = {"VP9", {{"x", "1"}}},
|
||||
.expected_result = true},
|
||||
{.name = "Vp9WithDifferentProfile",
|
||||
.codec1 = {"VP9", SdpVideoFormat::VP9Profile0().parameters},
|
||||
.codec2 = {"VP9", SdpVideoFormat::VP9Profile1().parameters},
|
||||
.expected_result = false},
|
||||
{.name = "H264WithSamePacketizationMode",
|
||||
.codec1 = {"H264", {{kH264FmtpPacketizationMode, "0"}}},
|
||||
.codec2 = {"H264", {{kH264FmtpPacketizationMode, "0"}}},
|
||||
.expected_result = true},
|
||||
{.name = "H264WithoutPacketizationModeTreatedAsMode0",
|
||||
.codec1 = {"H264", {{kH264FmtpPacketizationMode, "0"}, {"x", "1"}}},
|
||||
.codec2 = {"H264", {{"x", "1"}}},
|
||||
.expected_result = true},
|
||||
{.name = "H264WithDifferentPacketizationMode",
|
||||
.codec1 = {"H264", {{kH264FmtpPacketizationMode, "0"}}},
|
||||
.codec2 = {"H264", {{kH264FmtpPacketizationMode, "1"}}},
|
||||
.expected_result = false},
|
||||
#ifdef RTC_ENABLE_H265
|
||||
{.name = "H265WithSameProfile",
|
||||
.codec1 = {"H265",
|
||||
{{cricket::kH265FmtpProfileId, "1"},
|
||||
{cricket::kH265FmtpTierFlag, "0"},
|
||||
{cricket::kH265FmtpLevelId, "93"},
|
||||
{cricket::kH265FmtpTxMode, "SRST"}}},
|
||||
.codec2 = {"H265",
|
||||
{{cricket::kH265FmtpProfileId, "1"},
|
||||
{cricket::kH265FmtpTierFlag, "0"},
|
||||
{cricket::kH265FmtpLevelId, "93"},
|
||||
{cricket::kH265FmtpTxMode, "SRST"}}},
|
||||
.expected_result = true},
|
||||
{.name = "H265WithoutParametersTreatedAsDefault",
|
||||
.codec1 = {"H265",
|
||||
{{cricket::kH265FmtpProfileId, "1"},
|
||||
{cricket::kH265FmtpTierFlag, "0"},
|
||||
{cricket::kH265FmtpLevelId, "93"},
|
||||
{cricket::kH265FmtpTxMode, "SRST"}}},
|
||||
.codec2 = {"H265", {}},
|
||||
.expected_result = true},
|
||||
{.name = "H265WithDifferentProfile",
|
||||
.codec1 = {"H265",
|
||||
{{cricket::kH265FmtpProfileId, "1"},
|
||||
{cricket::kH265FmtpTierFlag, "0"},
|
||||
{cricket::kH265FmtpLevelId, "93"},
|
||||
{cricket::kH265FmtpTxMode, "SRST"}}},
|
||||
.codec2 = {"H265",
|
||||
{{cricket::kH265FmtpProfileId, "1"},
|
||||
{cricket::kH265FmtpTierFlag, "1"},
|
||||
{cricket::kH265FmtpLevelId, "93"},
|
||||
{cricket::kH265FmtpTxMode, "SRST"}}},
|
||||
.expected_result = false},
|
||||
#endif
|
||||
}),
|
||||
[](const testing::TestParamInfo<IsSameRtpCodecTest::ParamType>& info) {
|
||||
return info.param.name;
|
||||
});
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -19,10 +19,24 @@
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "api/field_trials_view.h"
|
||||
#include "api/video/video_bitrate_allocation.h"
|
||||
#include "media/base/codec_comparators.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/string_encode.h"
|
||||
|
||||
namespace cricket {
|
||||
namespace {
|
||||
bool SupportsMode(const cricket::Codec& codec,
|
||||
std::optional<std::string> scalability_mode) {
|
||||
if (!scalability_mode.has_value()) {
|
||||
return true;
|
||||
}
|
||||
return absl::c_any_of(
|
||||
codec.scalability_modes, [&](webrtc::ScalabilityMode mode) {
|
||||
return ScalabilityModeToString(mode) == *scalability_mode;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RtpCapabilities::RtpCapabilities() = default;
|
||||
RtpCapabilities::~RtpCapabilities() = default;
|
||||
@ -82,7 +96,8 @@ webrtc::RTCError CheckScalabilityModeValues(
|
||||
if (rtp_parameters.encodings[i].codec) {
|
||||
bool codecFound = false;
|
||||
for (const cricket::Codec& codec : send_codecs) {
|
||||
if (codec.MatchesRtpCodec(*rtp_parameters.encodings[i].codec)) {
|
||||
if (IsSameRtpCodec(codec, *rtp_parameters.encodings[i].codec) &&
|
||||
SupportsMode(codec, rtp_parameters.encodings[i].scalability_mode)) {
|
||||
codecFound = true;
|
||||
send_codec = codec;
|
||||
break;
|
||||
|
||||
@ -70,6 +70,7 @@
|
||||
#include "call/video_send_stream.h"
|
||||
#include "common_video/frame_counts.h"
|
||||
#include "media/base/codec.h"
|
||||
#include "media/base/codec_comparators.h"
|
||||
#include "media/base/media_channel.h"
|
||||
#include "media/base/media_channel_impl.h"
|
||||
#include "media/base/media_config.h"
|
||||
@ -1115,8 +1116,8 @@ bool WebRtcVideoSendChannel::GetChangedSenderParameters(
|
||||
if (rtp_parameters.encodings[0].codec) {
|
||||
auto matched_codec =
|
||||
absl::c_find_if(negotiated_codecs, [&](auto negotiated_codec) {
|
||||
return negotiated_codec.codec.MatchesRtpCodec(
|
||||
*rtp_parameters.encodings[0].codec);
|
||||
return IsSameRtpCodec(negotiated_codec.codec,
|
||||
*rtp_parameters.encodings[0].codec);
|
||||
});
|
||||
if (matched_codec != negotiated_codecs.end()) {
|
||||
force_codec = *matched_codec;
|
||||
@ -1152,7 +1153,7 @@ bool WebRtcVideoSendChannel::GetChangedSenderParameters(
|
||||
if (encoding.codec) {
|
||||
auto matched_codec =
|
||||
absl::c_find_if(negotiated_codecs, [&](auto negotiated_codec) {
|
||||
return negotiated_codec.codec.MatchesRtpCodec(*encoding.codec);
|
||||
return IsSameRtpCodec(negotiated_codec.codec, *encoding.codec);
|
||||
});
|
||||
if (matched_codec != negotiated_codecs.end()) {
|
||||
send_codecs.push_back(*matched_codec);
|
||||
@ -1446,13 +1447,13 @@ webrtc::RTCError WebRtcVideoSendChannel::SetRtpSendParameters(
|
||||
// the first layer.
|
||||
// TODO(orphis): Support mixed-codec simulcast
|
||||
if (parameters.encodings[0].codec && send_codec_ &&
|
||||
!send_codec_->codec.MatchesRtpCodec(*parameters.encodings[0].codec)) {
|
||||
!IsSameRtpCodec(send_codec_->codec, *parameters.encodings[0].codec)) {
|
||||
RTC_LOG(LS_VERBOSE) << "Trying to change codec to "
|
||||
<< parameters.encodings[0].codec->name;
|
||||
auto matched_codec =
|
||||
absl::c_find_if(negotiated_codecs_, [&](auto negotiated_codec) {
|
||||
return negotiated_codec.codec.MatchesRtpCodec(
|
||||
*parameters.encodings[0].codec);
|
||||
return IsSameRtpCodec(negotiated_codec.codec,
|
||||
*parameters.encodings[0].codec);
|
||||
});
|
||||
if (matched_codec == negotiated_codecs_.end()) {
|
||||
return webrtc::InvokeSetParametersCallback(
|
||||
|
||||
@ -1786,6 +1786,36 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan, CheckForInvalidEncodingParameters) {
|
||||
.error()
|
||||
.type());
|
||||
init.send_encodings = default_send_encodings;
|
||||
|
||||
init.send_encodings[0].scalability_mode = std::nullopt;
|
||||
init.send_encodings[0].codec =
|
||||
cricket::CreateVideoCodec(SdpVideoFormat("VP8", {})).ToCodecParameters();
|
||||
EXPECT_EQ(RTCErrorType::NONE,
|
||||
caller->pc()
|
||||
->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init)
|
||||
.error()
|
||||
.type());
|
||||
init.send_encodings = default_send_encodings;
|
||||
|
||||
init.send_encodings[0].scalability_mode = "L1T2";
|
||||
init.send_encodings[0].codec =
|
||||
cricket::CreateVideoCodec(SdpVideoFormat("VP8", {})).ToCodecParameters();
|
||||
EXPECT_EQ(RTCErrorType::NONE,
|
||||
caller->pc()
|
||||
->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init)
|
||||
.error()
|
||||
.type());
|
||||
init.send_encodings = default_send_encodings;
|
||||
|
||||
init.send_encodings[0].scalability_mode = "L2T2";
|
||||
init.send_encodings[0].codec =
|
||||
cricket::CreateVideoCodec(SdpVideoFormat("VP8", {})).ToCodecParameters();
|
||||
EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,
|
||||
caller->pc()
|
||||
->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init)
|
||||
.error()
|
||||
.type());
|
||||
init.send_encodings = default_send_encodings;
|
||||
}
|
||||
|
||||
// Test that AddTransceiver transfers the send_encodings to the sender and they
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user