From 19bbd6f02f628236afc098eb5936dc27ac12c13b Mon Sep 17 00:00:00 2001 From: Harald Alvestrand Date: Wed, 9 Oct 2024 14:05:51 +0000 Subject: [PATCH] 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 Reviewed-by: Florent Castelli Cr-Commit-Position: refs/heads/main@{#43210} --- call/DEPS | 1 + call/payload_type_picker.cc | 9 +- media/BUILD.gn | 5 + media/base/codec.cc | 149 +--------------- media/base/codec_comparators.cc | 301 ++++++++++++++++++++++++++++++++ media/base/codec_comparators.h | 39 +++++ pc/media_session.cc | 169 ++++-------------- 7 files changed, 387 insertions(+), 286 deletions(-) create mode 100644 media/base/codec_comparators.cc create mode 100644 media/base/codec_comparators.h diff --git a/call/DEPS b/call/DEPS index 1fe352db20..e74b22f677 100644 --- a/call/DEPS +++ b/call/DEPS @@ -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": [ diff --git a/call/payload_type_picker.cc b/call/payload_type_picker.cc index 765d716cd6..1c3c586646 100644 --- a/call/payload_type_picker.cc +++ b/call/payload_type_picker.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; diff --git a/media/BUILD.gn b/media/BUILD.gn index c39f75c8c8..9810764709 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -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", diff --git a/media/base/codec.cc b/media/base/codec.cc index 6b56596155..e1a114dc9a 100644 --- a/media/base/codec.cc +++ b/media/base/codec.cc @@ -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 { diff --git a/media/base/codec_comparators.cc b/media/base/codec_comparators.cc new file mode 100644 index 0000000000..30b1f2c480 --- /dev/null +++ b/media/base/codec_comparators.cc @@ -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 +#include +#include +#include + +#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& codecs1, + const int codec1_id, + const std::vector& 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 FindMatchingCodec(const std::vector& codecs1, + const std::vector& 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 redundant_payloads_1 = + rtc::split(red_parameters_1->second, '/'); + std::vector 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 diff --git a/media/base/codec_comparators.h b/media/base/codec_comparators.h new file mode 100644 index 0000000000..e36e93557a --- /dev/null +++ b/media/base/codec_comparators.h @@ -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 +#include + +#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 FindMatchingCodec( + const std::vector& codecs1, + const std::vector& codecs2, + const cricket::Codec& codec_to_match); + +} // namespace webrtc + +#endif // MEDIA_BASE_CODEC_COMPARATORS_H_ diff --git a/pc/media_session.cc b/pc/media_session.cc index c5d7713c56..3d6eccda06 100644 --- a/pc/media_session.cc +++ b/pc/media_session.cc @@ -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& codecs1, - const int codec1_id, - const std::vector& 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 FindMatchingCodec(const std::vector& codecs1, - const std::vector& 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 redundant_payloads_1 = - rtc::split(red_parameters_1->second, '/'); - std::vector 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& local_codecs, const std::vector& offered_codecs, std::vector* negotiated_codecs, bool keep_offer_order) { for (const Codec& ours : local_codecs) { std::optional 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& 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& 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& 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& 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 matching_codec = FindMatchingCodec( + std::optional 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& 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 matching_codec = FindMatchingCodec( + std::optional 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 MatchCodecPreference( if (found_codec != supported_codecs.end()) { std::optional 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 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 GetNegotiatedCodecsForOffer( // Add other supported codecs. for (const Codec& codec : supported_codecs) { std::optional 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 GetNegotiatedCodecsForOffer( RTC_DCHECK(referenced_codec); // Find the codec we should be referencing and point to it. - std::optional changed_referenced_codec = FindMatchingCodec( - supported_codecs, filtered_codecs, *referenced_codec); + std::optional 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 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 GetNegotiatedCodecsForAnswer( // Add other supported codecs. std::vector 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 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 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); } }