Move some codec-comparing functions to a single file.
This CL is a pure move; later CLs will try to increase consistency between the functions. Bug: webrtc:360058654 Change-Id: I6662b3d35f8e2dab60c2778a4755454fe3029fe2 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/365100 Commit-Queue: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Florent Castelli <orphis@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43210}
This commit is contained in:
parent
fea3280c13
commit
19bbd6f02f
@ -61,6 +61,7 @@ specific_include_rules = {
|
||||
],
|
||||
"payload_type_picker\.cc": [
|
||||
"+media/base/codec.h",
|
||||
"+media/base/codec_comparators.h",
|
||||
"+media/base/media_constants.h",
|
||||
],
|
||||
"payload_type_picker_unittest\.cc": [
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include "api/rtc_error.h"
|
||||
#include "call/payload_type.h"
|
||||
#include "media/base/codec.h"
|
||||
#include "media/base/codec_comparators.h"
|
||||
#include "media/base/media_constants.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
@ -40,14 +41,6 @@ static const int kLastDynamicPayloadTypeUpperRange = 127;
|
||||
// fmtp parameters. The use of cricket::Codec, which contains more fields,
|
||||
// is only a temporary measure.
|
||||
|
||||
bool MatchesForSdp(const cricket::Codec& codec_1,
|
||||
const cricket::Codec& codec_2) {
|
||||
return absl::EqualsIgnoreCase(codec_1.name, codec_2.name) &&
|
||||
codec_1.type == codec_2.type && codec_1.channels == codec_2.channels &&
|
||||
codec_1.clockrate == codec_2.clockrate &&
|
||||
codec_1.params == codec_2.params;
|
||||
}
|
||||
|
||||
struct MapTableEntry {
|
||||
webrtc::SdpAudioFormat format;
|
||||
int payload_type;
|
||||
|
||||
@ -355,6 +355,11 @@ rtc_library("codec") {
|
||||
sources = [
|
||||
"base/codec.cc",
|
||||
"base/codec.h",
|
||||
|
||||
# Because Codec::Matches uses a function from codec_comparators,
|
||||
# there's a mutual dependency between these two files.
|
||||
"base/codec_comparators.cc",
|
||||
"base/codec_comparators.h",
|
||||
]
|
||||
deps = [
|
||||
":media_constants",
|
||||
|
||||
@ -22,13 +22,12 @@
|
||||
#include "api/audio_codecs/audio_format.h"
|
||||
#include "api/media_types.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/video_codecs/av1_profile.h"
|
||||
#include "api/video_codecs/h264_profile_level_id.h"
|
||||
#include "api/video_codecs/sdp_video_format.h"
|
||||
#ifdef RTC_ENABLE_H265
|
||||
#include "api/video_codecs/h265_profile_tier_level.h"
|
||||
#endif
|
||||
#include "api/video_codecs/vp9_profile.h"
|
||||
#include "media/base/codec_comparators.h"
|
||||
#include "media/base/media_constants.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
@ -38,97 +37,6 @@
|
||||
namespace cricket {
|
||||
namespace {
|
||||
|
||||
// TODO(bugs.webrtc.org/15847): remove code duplication of IsSameCodecSpecific
|
||||
// in api/video_codecs/sdp_video_format.cc
|
||||
std::string GetFmtpParameterOrDefault(const webrtc::CodecParameterMap& params,
|
||||
const std::string& name,
|
||||
const std::string& default_value) {
|
||||
const auto it = params.find(name);
|
||||
if (it != params.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
std::string H264GetPacketizationModeOrDefault(
|
||||
const webrtc::CodecParameterMap& params) {
|
||||
// If packetization-mode is not present, default to "0".
|
||||
// https://tools.ietf.org/html/rfc6184#section-6.2
|
||||
return GetFmtpParameterOrDefault(params, cricket::kH264FmtpPacketizationMode,
|
||||
"0");
|
||||
}
|
||||
|
||||
bool H264IsSamePacketizationMode(const webrtc::CodecParameterMap& left,
|
||||
const webrtc::CodecParameterMap& right) {
|
||||
return H264GetPacketizationModeOrDefault(left) ==
|
||||
H264GetPacketizationModeOrDefault(right);
|
||||
}
|
||||
|
||||
std::string AV1GetTierOrDefault(const webrtc::CodecParameterMap& params) {
|
||||
// If the parameter is not present, the tier MUST be inferred to be 0.
|
||||
// https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters
|
||||
return GetFmtpParameterOrDefault(params, cricket::kAv1FmtpTier, "0");
|
||||
}
|
||||
|
||||
bool AV1IsSameTier(const webrtc::CodecParameterMap& left,
|
||||
const webrtc::CodecParameterMap& right) {
|
||||
return AV1GetTierOrDefault(left) == AV1GetTierOrDefault(right);
|
||||
}
|
||||
|
||||
std::string AV1GetLevelIdxOrDefault(const webrtc::CodecParameterMap& params) {
|
||||
// If the parameter is not present, it MUST be inferred to be 5 (level 3.1).
|
||||
// https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters
|
||||
return GetFmtpParameterOrDefault(params, cricket::kAv1FmtpLevelIdx, "5");
|
||||
}
|
||||
|
||||
bool AV1IsSameLevelIdx(const webrtc::CodecParameterMap& left,
|
||||
const webrtc::CodecParameterMap& right) {
|
||||
return AV1GetLevelIdxOrDefault(left) == AV1GetLevelIdxOrDefault(right);
|
||||
}
|
||||
|
||||
#ifdef RTC_ENABLE_H265
|
||||
std::string GetH265TxModeOrDefault(const webrtc::CodecParameterMap& params) {
|
||||
// If TxMode is not present, a value of "SRST" must be inferred.
|
||||
// https://tools.ietf.org/html/rfc7798@section-7.1
|
||||
return GetFmtpParameterOrDefault(params, kH265FmtpTxMode, "SRST");
|
||||
}
|
||||
|
||||
bool IsSameH265TxMode(const webrtc::CodecParameterMap& left,
|
||||
const webrtc::CodecParameterMap& right) {
|
||||
return absl::EqualsIgnoreCase(GetH265TxModeOrDefault(left),
|
||||
GetH265TxModeOrDefault(right));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Some (video) codecs are actually families of codecs and rely on parameters
|
||||
// to distinguish different incompatible family members.
|
||||
bool IsSameCodecSpecific(const std::string& name1,
|
||||
const webrtc::CodecParameterMap& params1,
|
||||
const std::string& name2,
|
||||
const webrtc::CodecParameterMap& params2) {
|
||||
// The names might not necessarily match, so check both.
|
||||
auto either_name_matches = [&](const std::string name) {
|
||||
return absl::EqualsIgnoreCase(name, name1) ||
|
||||
absl::EqualsIgnoreCase(name, name2);
|
||||
};
|
||||
if (either_name_matches(kH264CodecName))
|
||||
return webrtc::H264IsSameProfile(params1, params2) &&
|
||||
H264IsSamePacketizationMode(params1, params2);
|
||||
if (either_name_matches(kVp9CodecName))
|
||||
return webrtc::VP9IsSameProfile(params1, params2);
|
||||
if (either_name_matches(kAv1CodecName))
|
||||
return webrtc::AV1IsSameProfile(params1, params2) &&
|
||||
AV1IsSameTier(params1, params2) &&
|
||||
AV1IsSameLevelIdx(params1, params2);
|
||||
#ifdef RTC_ENABLE_H265
|
||||
if (either_name_matches(kH265CodecName)) {
|
||||
return webrtc::H265IsSameProfile(params1, params2) &&
|
||||
webrtc::H265IsSameTier(params1, params2) &&
|
||||
IsSameH265TxMode(params1, params2);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -227,60 +135,7 @@ bool Codec::operator==(const Codec& c) const {
|
||||
}
|
||||
|
||||
bool Codec::Matches(const Codec& codec) const {
|
||||
// Match the codec id/name based on the typical static/dynamic name rules.
|
||||
// Matching is case-insensitive.
|
||||
|
||||
// We support the ranges [96, 127] and more recently [35, 65].
|
||||
// https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-1
|
||||
// Within those ranges we match by codec name, outside by codec id.
|
||||
// We also match by name if either ID is unassigned.
|
||||
// Since no codecs are assigned an id in the range [66, 95] by us, these will
|
||||
// never match.
|
||||
const int kLowerDynamicRangeMin = 35;
|
||||
const int kLowerDynamicRangeMax = 65;
|
||||
const int kUpperDynamicRangeMin = 96;
|
||||
const int kUpperDynamicRangeMax = 127;
|
||||
const bool is_id_in_dynamic_range =
|
||||
(id >= kLowerDynamicRangeMin && id <= kLowerDynamicRangeMax) ||
|
||||
(id >= kUpperDynamicRangeMin && id <= kUpperDynamicRangeMax);
|
||||
const bool is_codec_id_in_dynamic_range =
|
||||
(codec.id >= kLowerDynamicRangeMin &&
|
||||
codec.id <= kLowerDynamicRangeMax) ||
|
||||
(codec.id >= kUpperDynamicRangeMin && codec.id <= kUpperDynamicRangeMax);
|
||||
bool matches_id;
|
||||
if ((is_id_in_dynamic_range && is_codec_id_in_dynamic_range) ||
|
||||
id == kIdNotSet || codec.id == kIdNotSet) {
|
||||
matches_id = absl::EqualsIgnoreCase(name, codec.name);
|
||||
} else {
|
||||
matches_id = (id == codec.id);
|
||||
}
|
||||
|
||||
auto matches_type_specific = [&]() {
|
||||
switch (type) {
|
||||
case Type::kAudio:
|
||||
// If a nonzero clockrate is specified, it must match the actual
|
||||
// clockrate. If a nonzero bitrate is specified, it must match the
|
||||
// actual bitrate, unless the codec is VBR (0), where we just force the
|
||||
// supplied value. The number of channels must match exactly, with the
|
||||
// exception that channels=0 is treated synonymously as channels=1, per
|
||||
// RFC 4566 section 6: " [The channels] parameter is OPTIONAL and may be
|
||||
// omitted if the number of channels is one."
|
||||
// Preference is ignored.
|
||||
// TODO(juberti): Treat a zero clockrate as 8000Hz, the RTP default
|
||||
// clockrate.
|
||||
return ((codec.clockrate == 0 /*&& clockrate == 8000*/) ||
|
||||
clockrate == codec.clockrate) &&
|
||||
(codec.bitrate == 0 || bitrate <= 0 ||
|
||||
bitrate == codec.bitrate) &&
|
||||
((codec.channels < 2 && channels < 2) ||
|
||||
channels == codec.channels);
|
||||
|
||||
case Type::kVideo:
|
||||
return IsSameCodecSpecific(name, params, codec.name, codec.params);
|
||||
}
|
||||
};
|
||||
|
||||
return matches_id && matches_type_specific();
|
||||
return webrtc::MatchesWithCodecRules(*this, codec);
|
||||
}
|
||||
|
||||
bool Codec::MatchesRtpCodec(const webrtc::RtpCodec& codec_capability) const {
|
||||
|
||||
301
media/base/codec_comparators.cc
Normal file
301
media/base/codec_comparators.cc
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright 2024 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#include "media/base/codec_comparators.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/video_codecs/av1_profile.h"
|
||||
#include "api/video_codecs/h264_profile_level_id.h"
|
||||
#ifdef RTC_ENABLE_H265
|
||||
#include "api/video_codecs/h265_profile_tier_level.h"
|
||||
#endif
|
||||
#include "api/video_codecs/vp9_profile.h"
|
||||
#include "media/base/codec.h"
|
||||
#include "media/base/media_constants.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/string_encode.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
using cricket::Codec;
|
||||
|
||||
// TODO(bugs.webrtc.org/15847): remove code duplication of IsSameCodecSpecific
|
||||
// in api/video_codecs/sdp_video_format.cc
|
||||
std::string GetFmtpParameterOrDefault(const CodecParameterMap& params,
|
||||
const std::string& name,
|
||||
const std::string& default_value) {
|
||||
const auto it = params.find(name);
|
||||
if (it != params.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
std::string H264GetPacketizationModeOrDefault(const CodecParameterMap& params) {
|
||||
// If packetization-mode is not present, default to "0".
|
||||
// https://tools.ietf.org/html/rfc6184#section-6.2
|
||||
return GetFmtpParameterOrDefault(params, cricket::kH264FmtpPacketizationMode,
|
||||
"0");
|
||||
}
|
||||
|
||||
bool H264IsSamePacketizationMode(const CodecParameterMap& left,
|
||||
const CodecParameterMap& right) {
|
||||
return H264GetPacketizationModeOrDefault(left) ==
|
||||
H264GetPacketizationModeOrDefault(right);
|
||||
}
|
||||
|
||||
std::string AV1GetTierOrDefault(const CodecParameterMap& params) {
|
||||
// If the parameter is not present, the tier MUST be inferred to be 0.
|
||||
// https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters
|
||||
return GetFmtpParameterOrDefault(params, cricket::kAv1FmtpTier, "0");
|
||||
}
|
||||
|
||||
bool AV1IsSameTier(const CodecParameterMap& left,
|
||||
const CodecParameterMap& right) {
|
||||
return AV1GetTierOrDefault(left) == AV1GetTierOrDefault(right);
|
||||
}
|
||||
|
||||
std::string AV1GetLevelIdxOrDefault(const CodecParameterMap& params) {
|
||||
// If the parameter is not present, it MUST be inferred to be 5 (level 3.1).
|
||||
// https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters
|
||||
return GetFmtpParameterOrDefault(params, cricket::kAv1FmtpLevelIdx, "5");
|
||||
}
|
||||
|
||||
bool AV1IsSameLevelIdx(const CodecParameterMap& left,
|
||||
const CodecParameterMap& right) {
|
||||
return AV1GetLevelIdxOrDefault(left) == AV1GetLevelIdxOrDefault(right);
|
||||
}
|
||||
|
||||
#ifdef RTC_ENABLE_H265
|
||||
std::string GetH265TxModeOrDefault(const CodecParameterMap& params) {
|
||||
// If TxMode is not present, a value of "SRST" must be inferred.
|
||||
// https://tools.ietf.org/html/rfc7798@section-7.1
|
||||
return GetFmtpParameterOrDefault(params, cricket::kH265FmtpTxMode, "SRST");
|
||||
}
|
||||
|
||||
bool IsSameH265TxMode(const CodecParameterMap& left,
|
||||
const CodecParameterMap& right) {
|
||||
return absl::EqualsIgnoreCase(GetH265TxModeOrDefault(left),
|
||||
GetH265TxModeOrDefault(right));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Some (video) codecs are actually families of codecs and rely on parameters
|
||||
// to distinguish different incompatible family members.
|
||||
bool IsSameCodecSpecific(const std::string& name1,
|
||||
const CodecParameterMap& params1,
|
||||
const std::string& name2,
|
||||
const CodecParameterMap& params2) {
|
||||
// The names might not necessarily match, so check both.
|
||||
auto either_name_matches = [&](const std::string name) {
|
||||
return absl::EqualsIgnoreCase(name, name1) ||
|
||||
absl::EqualsIgnoreCase(name, name2);
|
||||
};
|
||||
if (either_name_matches(cricket::kH264CodecName))
|
||||
return H264IsSameProfile(params1, params2) &&
|
||||
H264IsSamePacketizationMode(params1, params2);
|
||||
if (either_name_matches(cricket::kVp9CodecName))
|
||||
return VP9IsSameProfile(params1, params2);
|
||||
if (either_name_matches(cricket::kAv1CodecName))
|
||||
return AV1IsSameProfile(params1, params2) &&
|
||||
AV1IsSameTier(params1, params2) &&
|
||||
AV1IsSameLevelIdx(params1, params2);
|
||||
#ifdef RTC_ENABLE_H265
|
||||
if (either_name_matches(cricket::kH265CodecName)) {
|
||||
return H265IsSameProfile(params1, params2) &&
|
||||
H265IsSameTier(params1, params2) &&
|
||||
IsSameH265TxMode(params1, params2);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReferencedCodecsMatch(const std::vector<Codec>& codecs1,
|
||||
const int codec1_id,
|
||||
const std::vector<Codec>& codecs2,
|
||||
const int codec2_id) {
|
||||
const Codec* codec1 = FindCodecById(codecs1, codec1_id);
|
||||
const Codec* codec2 = FindCodecById(codecs2, codec2_id);
|
||||
return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool MatchesForSdp(const Codec& codec_1, const Codec& codec_2) {
|
||||
return absl::EqualsIgnoreCase(codec_1.name, codec_2.name) &&
|
||||
codec_1.type == codec_2.type && codec_1.channels == codec_2.channels &&
|
||||
codec_1.clockrate == codec_2.clockrate &&
|
||||
codec_1.params == codec_2.params;
|
||||
}
|
||||
|
||||
bool MatchesWithCodecRules(const Codec& left_codec, const Codec& right_codec) {
|
||||
// Match the codec id/name based on the typical static/dynamic name rules.
|
||||
// Matching is case-insensitive.
|
||||
|
||||
// We support the ranges [96, 127] and more recently [35, 65].
|
||||
// https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-1
|
||||
// Within those ranges we match by codec name, outside by codec id.
|
||||
// We also match by name if either ID is unassigned.
|
||||
// Since no codecs are assigned an id in the range [66, 95] by us, these will
|
||||
// never match.
|
||||
const int kLowerDynamicRangeMin = 35;
|
||||
const int kLowerDynamicRangeMax = 65;
|
||||
const int kUpperDynamicRangeMin = 96;
|
||||
const int kUpperDynamicRangeMax = 127;
|
||||
const bool is_id_in_dynamic_range =
|
||||
(left_codec.id >= kLowerDynamicRangeMin &&
|
||||
left_codec.id <= kLowerDynamicRangeMax) ||
|
||||
(left_codec.id >= kUpperDynamicRangeMin &&
|
||||
left_codec.id <= kUpperDynamicRangeMax);
|
||||
const bool is_codec_id_in_dynamic_range =
|
||||
(right_codec.id >= kLowerDynamicRangeMin &&
|
||||
right_codec.id <= kLowerDynamicRangeMax) ||
|
||||
(right_codec.id >= kUpperDynamicRangeMin &&
|
||||
right_codec.id <= kUpperDynamicRangeMax);
|
||||
bool matches_id;
|
||||
if ((is_id_in_dynamic_range && is_codec_id_in_dynamic_range) ||
|
||||
left_codec.id == Codec::kIdNotSet || right_codec.id == Codec::kIdNotSet) {
|
||||
matches_id = absl::EqualsIgnoreCase(left_codec.name, right_codec.name);
|
||||
} else {
|
||||
matches_id = (left_codec.id == right_codec.id);
|
||||
}
|
||||
|
||||
auto matches_type_specific = [&]() {
|
||||
switch (left_codec.type) {
|
||||
case Codec::Type::kAudio:
|
||||
// If a nonzero clockrate is specified, it must match the actual
|
||||
// clockrate. If a nonzero bitrate is specified, it must match the
|
||||
// actual bitrate, unless the codec is VBR (0), where we just force the
|
||||
// supplied value. The number of channels must match exactly, with the
|
||||
// exception that channels=0 is treated synonymously as channels=1, per
|
||||
// RFC 4566 section 6: " [The channels] parameter is OPTIONAL and may be
|
||||
// omitted if the number of channels is one."
|
||||
// Preference is ignored.
|
||||
// TODO(juberti): Treat a zero clockrate as 8000Hz, the RTP default
|
||||
// clockrate.
|
||||
return ((right_codec.clockrate == 0 /*&& clockrate == 8000*/) ||
|
||||
left_codec.clockrate == right_codec.clockrate) &&
|
||||
(right_codec.bitrate == 0 || left_codec.bitrate <= 0 ||
|
||||
left_codec.bitrate == right_codec.bitrate) &&
|
||||
((right_codec.channels < 2 && left_codec.channels < 2) ||
|
||||
left_codec.channels == right_codec.channels);
|
||||
|
||||
case Codec::Type::kVideo:
|
||||
return IsSameCodecSpecific(left_codec.name, left_codec.params,
|
||||
right_codec.name, right_codec.params);
|
||||
}
|
||||
};
|
||||
|
||||
return matches_id && matches_type_specific();
|
||||
}
|
||||
|
||||
// Finds a codec in `codecs2` that matches `codec_to_match`, which is
|
||||
// a member of `codecs1`. If `codec_to_match` is an RED or RTX codec, both
|
||||
// the codecs themselves and their associated codecs must match.
|
||||
std::optional<Codec> FindMatchingCodec(const std::vector<Codec>& codecs1,
|
||||
const std::vector<Codec>& codecs2,
|
||||
const Codec& codec_to_match) {
|
||||
// `codec_to_match` should be a member of `codecs1`, in order to look up
|
||||
// RED/RTX codecs' associated codecs correctly. If not, that's a programming
|
||||
// error.
|
||||
RTC_DCHECK(absl::c_any_of(codecs1, [&codec_to_match](const Codec& codec) {
|
||||
return &codec == &codec_to_match;
|
||||
}));
|
||||
for (const Codec& potential_match : codecs2) {
|
||||
if (potential_match.Matches(codec_to_match)) {
|
||||
if (codec_to_match.GetResiliencyType() == Codec::ResiliencyType::kRtx) {
|
||||
int apt_value_1 = 0;
|
||||
int apt_value_2 = 0;
|
||||
if (!codec_to_match.GetParam(cricket::kCodecParamAssociatedPayloadType,
|
||||
&apt_value_1) ||
|
||||
!potential_match.GetParam(cricket::kCodecParamAssociatedPayloadType,
|
||||
&apt_value_2)) {
|
||||
RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
|
||||
continue;
|
||||
}
|
||||
if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
|
||||
apt_value_2)) {
|
||||
continue;
|
||||
}
|
||||
} else if (codec_to_match.GetResiliencyType() ==
|
||||
Codec::ResiliencyType::kRed) {
|
||||
auto red_parameters_1 = codec_to_match.params.find(
|
||||
cricket::kCodecParamNotInNameValueFormat);
|
||||
auto red_parameters_2 = potential_match.params.find(
|
||||
cricket::kCodecParamNotInNameValueFormat);
|
||||
bool has_parameters_1 = red_parameters_1 != codec_to_match.params.end();
|
||||
bool has_parameters_2 =
|
||||
red_parameters_2 != potential_match.params.end();
|
||||
// If codec_to_match has unassigned PT and no parameter,
|
||||
// we assume that it'll be assigned later and return a match.
|
||||
if (potential_match.id == Codec::kIdNotSet && !has_parameters_2) {
|
||||
return potential_match;
|
||||
}
|
||||
if (codec_to_match.id == Codec::kIdNotSet && !has_parameters_1) {
|
||||
return potential_match;
|
||||
}
|
||||
if (has_parameters_1 && has_parameters_2) {
|
||||
// Mixed reference codecs (i.e. 111/112) are not supported.
|
||||
// Different levels of redundancy between offer and answer are
|
||||
// since RED is considered to be declarative.
|
||||
std::vector<absl::string_view> redundant_payloads_1 =
|
||||
rtc::split(red_parameters_1->second, '/');
|
||||
std::vector<absl::string_view> redundant_payloads_2 =
|
||||
rtc::split(red_parameters_2->second, '/');
|
||||
if (redundant_payloads_1.size() > 0 &&
|
||||
redundant_payloads_2.size() > 0) {
|
||||
bool consistent = true;
|
||||
for (size_t i = 1; i < redundant_payloads_1.size(); i++) {
|
||||
if (redundant_payloads_1[i] != redundant_payloads_1[0]) {
|
||||
consistent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (size_t i = 1; i < redundant_payloads_2.size(); i++) {
|
||||
if (redundant_payloads_2[i] != redundant_payloads_2[0]) {
|
||||
consistent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!consistent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int red_value_1;
|
||||
int red_value_2;
|
||||
if (rtc::FromString(redundant_payloads_1[0], &red_value_1) &&
|
||||
rtc::FromString(redundant_payloads_2[0], &red_value_2)) {
|
||||
if (!ReferencedCodecsMatch(codecs1, red_value_1, codecs2,
|
||||
red_value_2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (has_parameters_1 != has_parameters_2) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return potential_match;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
39
media/base/codec_comparators.h
Normal file
39
media/base/codec_comparators.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2024 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MEDIA_BASE_CODEC_COMPARATORS_H_
|
||||
#define MEDIA_BASE_CODEC_COMPARATORS_H_
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "media/base/codec.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Comparison used in the PayloadTypePicker
|
||||
bool MatchesForSdp(const cricket::Codec& codec_1,
|
||||
const cricket::Codec& codec_2);
|
||||
|
||||
// Comparison used for the Codec::Matches function
|
||||
bool MatchesWithCodecRules(const cricket::Codec& left_codec,
|
||||
const cricket::Codec& codec);
|
||||
|
||||
// Finds a codec in `codecs2` that matches `codec_to_match`, which is
|
||||
// a member of `codecs1`. If `codec_to_match` is an RED or RTX codec, both
|
||||
// the codecs themselves and their associated codecs must match.
|
||||
std::optional<cricket::Codec> FindMatchingCodec(
|
||||
const std::vector<cricket::Codec>& codecs1,
|
||||
const std::vector<cricket::Codec>& codecs2,
|
||||
const cricket::Codec& codec_to_match);
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MEDIA_BASE_CODEC_COMPARATORS_H_
|
||||
@ -31,6 +31,7 @@
|
||||
#include "api/rtp_transceiver_direction.h"
|
||||
#include "call/payload_type.h"
|
||||
#include "media/base/codec.h"
|
||||
#include "media/base/codec_comparators.h"
|
||||
#include "media/base/media_constants.h"
|
||||
#include "media/base/media_engine.h"
|
||||
#include "media/base/rid_description.h"
|
||||
@ -439,15 +440,6 @@ RTCError CreateMediaContentOffer(
|
||||
offer);
|
||||
}
|
||||
|
||||
bool ReferencedCodecsMatch(const std::vector<Codec>& codecs1,
|
||||
const int codec1_id,
|
||||
const std::vector<Codec>& codecs2,
|
||||
const int codec2_id) {
|
||||
const Codec* codec1 = FindCodecById(codecs1, codec1_id);
|
||||
const Codec* codec2 = FindCodecById(codecs2, codec2_id);
|
||||
return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
|
||||
}
|
||||
|
||||
void NegotiatePacketization(const Codec& local_codec,
|
||||
const Codec& remote_codec,
|
||||
Codec* negotiated_codec) {
|
||||
@ -512,105 +504,13 @@ webrtc::RTCError AssignCodecIdsAndLinkRed(
|
||||
return webrtc::RTCError::OK();
|
||||
}
|
||||
|
||||
// Finds a codec in `codecs2` that matches `codec_to_match`, which is
|
||||
// a member of `codecs1`. If `codec_to_match` is an RED or RTX codec, both
|
||||
// the codecs themselves and their associated codecs must match.
|
||||
std::optional<Codec> FindMatchingCodec(const std::vector<Codec>& codecs1,
|
||||
const std::vector<Codec>& codecs2,
|
||||
const Codec& codec_to_match) {
|
||||
// `codec_to_match` should be a member of `codecs1`, in order to look up
|
||||
// RED/RTX codecs' associated codecs correctly. If not, that's a programming
|
||||
// error.
|
||||
RTC_DCHECK(absl::c_any_of(codecs1, [&codec_to_match](const Codec& codec) {
|
||||
return &codec == &codec_to_match;
|
||||
}));
|
||||
for (const Codec& potential_match : codecs2) {
|
||||
if (potential_match.Matches(codec_to_match)) {
|
||||
if (codec_to_match.GetResiliencyType() == Codec::ResiliencyType::kRtx) {
|
||||
int apt_value_1 = 0;
|
||||
int apt_value_2 = 0;
|
||||
if (!codec_to_match.GetParam(kCodecParamAssociatedPayloadType,
|
||||
&apt_value_1) ||
|
||||
!potential_match.GetParam(kCodecParamAssociatedPayloadType,
|
||||
&apt_value_2)) {
|
||||
RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
|
||||
continue;
|
||||
}
|
||||
if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
|
||||
apt_value_2)) {
|
||||
continue;
|
||||
}
|
||||
} else if (codec_to_match.GetResiliencyType() ==
|
||||
Codec::ResiliencyType::kRed) {
|
||||
auto red_parameters_1 =
|
||||
codec_to_match.params.find(kCodecParamNotInNameValueFormat);
|
||||
auto red_parameters_2 =
|
||||
potential_match.params.find(kCodecParamNotInNameValueFormat);
|
||||
bool has_parameters_1 = red_parameters_1 != codec_to_match.params.end();
|
||||
bool has_parameters_2 =
|
||||
red_parameters_2 != potential_match.params.end();
|
||||
// If codec_to_match has unassigned PT and no parameter,
|
||||
// we assume that it'll be assigned later and return a match.
|
||||
if (potential_match.id == Codec::kIdNotSet && !has_parameters_2) {
|
||||
return potential_match;
|
||||
}
|
||||
if (codec_to_match.id == Codec::kIdNotSet && !has_parameters_1) {
|
||||
return potential_match;
|
||||
}
|
||||
if (has_parameters_1 && has_parameters_2) {
|
||||
// Mixed reference codecs (i.e. 111/112) are not supported.
|
||||
// Different levels of redundancy between offer and answer are
|
||||
// since RED is considered to be declarative.
|
||||
std::vector<absl::string_view> redundant_payloads_1 =
|
||||
rtc::split(red_parameters_1->second, '/');
|
||||
std::vector<absl::string_view> redundant_payloads_2 =
|
||||
rtc::split(red_parameters_2->second, '/');
|
||||
if (redundant_payloads_1.size() > 0 &&
|
||||
redundant_payloads_2.size() > 0) {
|
||||
bool consistent = true;
|
||||
for (size_t i = 1; i < redundant_payloads_1.size(); i++) {
|
||||
if (redundant_payloads_1[i] != redundant_payloads_1[0]) {
|
||||
consistent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (size_t i = 1; i < redundant_payloads_2.size(); i++) {
|
||||
if (redundant_payloads_2[i] != redundant_payloads_2[0]) {
|
||||
consistent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!consistent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int red_value_1;
|
||||
int red_value_2;
|
||||
if (rtc::FromString(redundant_payloads_1[0], &red_value_1) &&
|
||||
rtc::FromString(redundant_payloads_2[0], &red_value_2)) {
|
||||
if (!ReferencedCodecsMatch(codecs1, red_value_1, codecs2,
|
||||
red_value_2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (has_parameters_1 != has_parameters_2) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return potential_match;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void NegotiateCodecs(const std::vector<Codec>& local_codecs,
|
||||
const std::vector<Codec>& offered_codecs,
|
||||
std::vector<Codec>* negotiated_codecs,
|
||||
bool keep_offer_order) {
|
||||
for (const Codec& ours : local_codecs) {
|
||||
std::optional<Codec> theirs =
|
||||
FindMatchingCodec(local_codecs, offered_codecs, ours);
|
||||
webrtc::FindMatchingCodec(local_codecs, offered_codecs, ours);
|
||||
// Note that we intentionally only find one matching codec for each of our
|
||||
// local codecs, in case the remote offer contains duplicate codecs.
|
||||
if (theirs) {
|
||||
@ -620,7 +520,8 @@ void NegotiateCodecs(const std::vector<Codec>& local_codecs,
|
||||
if (negotiated.GetResiliencyType() == Codec::ResiliencyType::kRtx) {
|
||||
const auto apt_it =
|
||||
theirs->params.find(kCodecParamAssociatedPayloadType);
|
||||
// FindMatchingCodec shouldn't return something with no apt value.
|
||||
// webrtc::FindMatchingCodec shouldn't return something with no apt
|
||||
// value.
|
||||
RTC_DCHECK(apt_it != theirs->params.end());
|
||||
negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second);
|
||||
|
||||
@ -749,8 +650,8 @@ void MergeCodecs(const std::vector<Codec>& reference_codecs,
|
||||
for (const Codec& reference_codec : reference_codecs) {
|
||||
if (reference_codec.GetResiliencyType() != Codec::ResiliencyType::kRtx &&
|
||||
reference_codec.GetResiliencyType() != Codec::ResiliencyType::kRed &&
|
||||
!FindMatchingCodec(reference_codecs, *offered_codecs,
|
||||
reference_codec)) {
|
||||
!webrtc::FindMatchingCodec(reference_codecs, *offered_codecs,
|
||||
reference_codec)) {
|
||||
Codec codec = reference_codec;
|
||||
used_pltypes->FindAndSetIdUsed(&codec);
|
||||
offered_codecs->push_back(codec);
|
||||
@ -760,8 +661,8 @@ void MergeCodecs(const std::vector<Codec>& reference_codecs,
|
||||
// Add all new RTX or RED codecs.
|
||||
for (const Codec& reference_codec : reference_codecs) {
|
||||
if (reference_codec.GetResiliencyType() == Codec::ResiliencyType::kRtx &&
|
||||
!FindMatchingCodec(reference_codecs, *offered_codecs,
|
||||
reference_codec)) {
|
||||
!webrtc::FindMatchingCodec(reference_codecs, *offered_codecs,
|
||||
reference_codec)) {
|
||||
Codec rtx_codec = reference_codec;
|
||||
const Codec* associated_codec =
|
||||
GetAssociatedCodecForRtx(reference_codecs, rtx_codec);
|
||||
@ -770,7 +671,7 @@ void MergeCodecs(const std::vector<Codec>& reference_codecs,
|
||||
}
|
||||
// Find a codec in the offered list that matches the reference codec.
|
||||
// Its payload type may be different than the reference codec.
|
||||
std::optional<Codec> matching_codec = FindMatchingCodec(
|
||||
std::optional<Codec> matching_codec = webrtc::FindMatchingCodec(
|
||||
reference_codecs, *offered_codecs, *associated_codec);
|
||||
if (!matching_codec) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
@ -784,13 +685,13 @@ void MergeCodecs(const std::vector<Codec>& reference_codecs,
|
||||
offered_codecs->push_back(rtx_codec);
|
||||
} else if (reference_codec.GetResiliencyType() ==
|
||||
Codec::ResiliencyType::kRed &&
|
||||
!FindMatchingCodec(reference_codecs, *offered_codecs,
|
||||
reference_codec)) {
|
||||
!webrtc::FindMatchingCodec(reference_codecs, *offered_codecs,
|
||||
reference_codec)) {
|
||||
Codec red_codec = reference_codec;
|
||||
const Codec* associated_codec =
|
||||
GetAssociatedCodecForRed(reference_codecs, red_codec);
|
||||
if (associated_codec) {
|
||||
std::optional<Codec> matching_codec = FindMatchingCodec(
|
||||
std::optional<Codec> matching_codec = webrtc::FindMatchingCodec(
|
||||
reference_codecs, *offered_codecs, *associated_codec);
|
||||
if (!matching_codec) {
|
||||
RTC_LOG(LS_WARNING) << "Couldn't find matching "
|
||||
@ -843,7 +744,7 @@ std::vector<Codec> MatchCodecPreference(
|
||||
|
||||
if (found_codec != supported_codecs.end()) {
|
||||
std::optional<Codec> found_codec_with_correct_pt =
|
||||
FindMatchingCodec(supported_codecs, codecs, *found_codec);
|
||||
webrtc::FindMatchingCodec(supported_codecs, codecs, *found_codec);
|
||||
if (found_codec_with_correct_pt) {
|
||||
// RED may already have been added if its primary codec is before RED
|
||||
// in the codec list.
|
||||
@ -1190,7 +1091,7 @@ webrtc::RTCErrorOr<Codecs> GetNegotiatedCodecsForOffer(
|
||||
}
|
||||
const MediaContentDescription* mcd = current_content->media_description();
|
||||
for (const Codec& codec : mcd->codecs()) {
|
||||
if (FindMatchingCodec(mcd->codecs(), codecs, codec)) {
|
||||
if (webrtc::FindMatchingCodec(mcd->codecs(), codecs, codec)) {
|
||||
filtered_codecs.push_back(codec);
|
||||
}
|
||||
}
|
||||
@ -1198,9 +1099,9 @@ webrtc::RTCErrorOr<Codecs> GetNegotiatedCodecsForOffer(
|
||||
// Add other supported codecs.
|
||||
for (const Codec& codec : supported_codecs) {
|
||||
std::optional<Codec> found_codec =
|
||||
FindMatchingCodec(supported_codecs, codecs, codec);
|
||||
if (found_codec &&
|
||||
!FindMatchingCodec(supported_codecs, filtered_codecs, codec)) {
|
||||
webrtc::FindMatchingCodec(supported_codecs, codecs, codec);
|
||||
if (found_codec && !webrtc::FindMatchingCodec(supported_codecs,
|
||||
filtered_codecs, codec)) {
|
||||
// Use the `found_codec` from `codecs` because it has the
|
||||
// correctly mapped payload type.
|
||||
// This is only done for video since we do not yet have rtx for audio.
|
||||
@ -1213,8 +1114,9 @@ webrtc::RTCErrorOr<Codecs> GetNegotiatedCodecsForOffer(
|
||||
RTC_DCHECK(referenced_codec);
|
||||
|
||||
// Find the codec we should be referencing and point to it.
|
||||
std::optional<Codec> changed_referenced_codec = FindMatchingCodec(
|
||||
supported_codecs, filtered_codecs, *referenced_codec);
|
||||
std::optional<Codec> changed_referenced_codec =
|
||||
webrtc::FindMatchingCodec(supported_codecs, filtered_codecs,
|
||||
*referenced_codec);
|
||||
if (changed_referenced_codec) {
|
||||
found_codec->SetParam(kCodecParamAssociatedPayloadType,
|
||||
changed_referenced_codec->id);
|
||||
@ -1266,7 +1168,7 @@ webrtc::RTCErrorOr<Codecs> GetNegotiatedCodecsForAnswer(
|
||||
}
|
||||
const MediaContentDescription* mcd = current_content->media_description();
|
||||
for (const Codec& codec : mcd->codecs()) {
|
||||
if (FindMatchingCodec(mcd->codecs(), codecs, codec)) {
|
||||
if (webrtc::FindMatchingCodec(mcd->codecs(), codecs, codec)) {
|
||||
filtered_codecs.push_back(codec);
|
||||
}
|
||||
}
|
||||
@ -1274,8 +1176,9 @@ webrtc::RTCErrorOr<Codecs> GetNegotiatedCodecsForAnswer(
|
||||
// Add other supported codecs.
|
||||
std::vector<Codec> other_codecs;
|
||||
for (const Codec& codec : supported_codecs) {
|
||||
if (FindMatchingCodec(supported_codecs, codecs, codec) &&
|
||||
!FindMatchingCodec(supported_codecs, filtered_codecs, codec)) {
|
||||
if (webrtc::FindMatchingCodec(supported_codecs, codecs, codec) &&
|
||||
!webrtc::FindMatchingCodec(supported_codecs, filtered_codecs,
|
||||
codec)) {
|
||||
// We should use the local codec with local parameters and the codec id
|
||||
// would be correctly mapped in `NegotiateCodecs`.
|
||||
other_codecs.push_back(codec);
|
||||
@ -1887,20 +1790,22 @@ void MediaSessionDescriptionFactory::GetCodecsForAnswer(
|
||||
if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
|
||||
std::vector<Codec> offered_codecs = content.media_description()->codecs();
|
||||
for (const Codec& offered_audio_codec : offered_codecs) {
|
||||
if (!FindMatchingCodec(offered_codecs, filtered_offered_audio_codecs,
|
||||
offered_audio_codec) &&
|
||||
FindMatchingCodec(offered_codecs, all_audio_codecs_,
|
||||
offered_audio_codec)) {
|
||||
if (!webrtc::FindMatchingCodec(offered_codecs,
|
||||
filtered_offered_audio_codecs,
|
||||
offered_audio_codec) &&
|
||||
webrtc::FindMatchingCodec(offered_codecs, all_audio_codecs_,
|
||||
offered_audio_codec)) {
|
||||
filtered_offered_audio_codecs.push_back(offered_audio_codec);
|
||||
}
|
||||
}
|
||||
} else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) {
|
||||
std::vector<Codec> offered_codecs = content.media_description()->codecs();
|
||||
for (const Codec& offered_video_codec : offered_codecs) {
|
||||
if (!FindMatchingCodec(offered_codecs, filtered_offered_video_codecs,
|
||||
offered_video_codec) &&
|
||||
FindMatchingCodec(offered_codecs, all_video_codecs_,
|
||||
offered_video_codec)) {
|
||||
if (!webrtc::FindMatchingCodec(offered_codecs,
|
||||
filtered_offered_video_codecs,
|
||||
offered_video_codec) &&
|
||||
webrtc::FindMatchingCodec(offered_codecs, all_video_codecs_,
|
||||
offered_video_codec)) {
|
||||
filtered_offered_video_codecs.push_back(offered_video_codec);
|
||||
}
|
||||
}
|
||||
@ -2414,14 +2319,16 @@ void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() {
|
||||
// Compute the audio codecs union.
|
||||
for (const Codec& send : audio_send_codecs_) {
|
||||
all_audio_codecs_.push_back(send);
|
||||
if (!FindMatchingCodec(audio_send_codecs_, audio_recv_codecs_, send)) {
|
||||
if (!webrtc::FindMatchingCodec(audio_send_codecs_, audio_recv_codecs_,
|
||||
send)) {
|
||||
// It doesn't make sense to have an RTX codec we support sending but not
|
||||
// receiving.
|
||||
RTC_DCHECK(send.GetResiliencyType() != Codec::ResiliencyType::kRtx);
|
||||
}
|
||||
}
|
||||
for (const Codec& recv : audio_recv_codecs_) {
|
||||
if (!FindMatchingCodec(audio_recv_codecs_, audio_send_codecs_, recv)) {
|
||||
if (!webrtc::FindMatchingCodec(audio_recv_codecs_, audio_send_codecs_,
|
||||
recv)) {
|
||||
all_audio_codecs_.push_back(recv);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user