From 13170bd177dd07330d39db63ea8f3129d17cf45a Mon Sep 17 00:00:00 2001 From: Harald Alvestrand Date: Mon, 27 Jan 2025 14:51:55 +0000 Subject: [PATCH] Refactor media_session to move codec handling to new class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new class "CodecVendor" is intended to handle all logic dealing with codecs. This CL is a no-behavior-change CL, later CLs will change the logic. Bug: webrtc:360058654 Change-Id: I44e76f0e0bd364eeb7d4506f3e01e9e00e2843a2 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/375500 Commit-Queue: Harald Alvestrand Reviewed-by: Henrik Boström Cr-Commit-Position: refs/heads/main@{#43806} --- pc/BUILD.gn | 64 ++- pc/codec_vendor.cc | 892 ++++++++++++++++++++++++++++++++++ pc/codec_vendor.h | 123 +++++ pc/media_options.cc | 82 ++++ pc/media_options.h | 120 +++++ pc/media_session.cc | 903 +---------------------------------- pc/media_session.h | 152 +----- pc/media_session_unittest.cc | 430 ++++++++++------- 8 files changed, 1557 insertions(+), 1209 deletions(-) create mode 100644 pc/codec_vendor.cc create mode 100644 pc/codec_vendor.h create mode 100644 pc/media_options.cc create mode 100644 pc/media_options.h diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 91ca10db55..4afc0a2140 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -357,19 +357,17 @@ rtc_source_set("media_session") { "media_session.h", ] deps = [ - ":jsep_transport", + ":codec_vendor", + ":media_options", ":media_protocol_names", ":rtp_media_utils", ":session_description", ":simulcast_description", ":used_ids", "../api:field_trials_view", - "../api:libjingle_peerconnection_api", "../api:rtc_error", "../api:rtp_parameters", "../api:rtp_transceiver_direction", - "../api/crypto:options", - "../api/video_codecs:video_codecs_api", "../call:payload_type", "../media:codec", "../media:codec_list", @@ -377,16 +375,13 @@ rtc_source_set("media_session") { "../media:media_engine", "../media:rid_description", "../media:rtc_data_sctp_transport_internal", - "../media:rtc_sdp_video_format_utils", "../media:stream_params", "../p2p:ice_credentials_iterator", "../p2p:p2p_constants", - "../p2p:transport_description", + "../p2p:rtc_p2p", "../p2p:transport_description_factory", - "../p2p:transport_info", "../rtc_base:checks", "../rtc_base:logging", - "../rtc_base:ssl", "../rtc_base:stringutils", "../rtc_base:unique_id_generator", "../rtc_base/memory:always_valid_pointer", @@ -396,6 +391,56 @@ rtc_source_set("media_session") { ] } +rtc_library("media_options") { + visibility = [ ":*" ] + sources = [ + "media_options.cc", + "media_options.h", + ] + deps = [ + ":simulcast_description", + "../api:rtp_parameters", + "../api:rtp_transceiver_direction", + "../api/crypto:options", + "../media:codec", + "../media:rid_description", + "../p2p:rtc_p2p", + "../p2p:transport_description_factory", + "../rtc_base:checks", + "//third_party/abseil-cpp/absl/algorithm:container", + ] +} + +rtc_library("codec_vendor") { + visibility = [ ":*" ] + sources = [ + "codec_vendor.cc", + "codec_vendor.h", + ] + deps = [ + ":media_options", + ":rtp_media_utils", + ":session_description", + ":used_ids", + "../api:rtc_error", + "../api:rtp_parameters", + "../api:rtp_transceiver_direction", + "../api/video_codecs:video_codecs_api", + "../media:codec", + "../media:codec_list", + "../media:media_constants", + "../media:media_engine", + "../media:rtc_sdp_video_format_utils", + "../rtc_base:checks", + "../rtc_base:logging", + "../rtc_base:stringutils", + "../rtc_base:unique_id_generator", + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/strings:string_view", + ] +} + rtc_source_set("media_stream_proxy") { visibility = [ ":*" ] sources = [ "media_stream_proxy.h" ] @@ -1997,6 +2042,7 @@ if (rtc_include_tests && !build_with_chromium) { ":jsep_transport", ":jsep_transport_controller", ":libjingle_peerconnection", + ":media_options", ":media_protocol_names", ":media_session", ":pc_test_utils", @@ -2043,11 +2089,13 @@ if (rtc_include_tests && !build_with_chromium) { "../api/video:builtin_video_bitrate_allocator_factory", "../api/video:recordable_encoded_frame", "../api/video/test:mock_recordable_encoded_frame", + "../api/video_codecs:video_codecs_api", "../call:fake_payload_type_suggester", "../call:payload_type_picker", "../call:rtp_interfaces", "../call:rtp_receiver", "../media:codec", + "../media:codec_list", "../media:media_channel", "../media:media_constants", "../media:rid_description", diff --git a/pc/codec_vendor.cc b/pc/codec_vendor.cc new file mode 100644 index 0000000000..0ab676a9cd --- /dev/null +++ b/pc/codec_vendor.cc @@ -0,0 +1,892 @@ +/* + * Copyright 2004 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 "pc/codec_vendor.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" +#include "api/media_types.h" +#include "api/rtc_error.h" +#include "api/rtp_parameters.h" +#include "api/rtp_transceiver_direction.h" +#include "media/base/codec.h" +#include "media/base/codec_comparators.h" +#include "media/base/codec_list.h" +#include "media/base/media_constants.h" +#include "media/base/media_engine.h" +#include "media/base/sdp_video_format_utils.h" +#include "pc/media_options.h" +#include "pc/rtp_media_utils.h" +#include "pc/session_description.h" +#include "pc/used_ids.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/string_encode.h" +#include "rtc_base/unique_id_generator.h" + +#ifdef RTC_ENABLE_H265 +#include "api/video_codecs/h265_profile_tier_level.h" +#endif + +namespace cricket { + +namespace { + +using rtc::UniqueRandomIdGenerator; +using webrtc::RTCError; +using webrtc::RTCErrorType; +using webrtc::RtpTransceiverDirection; + +bool IsRtxCodec(const webrtc::RtpCodecCapability& capability) { + return absl::EqualsIgnoreCase(capability.name, kRtxCodecName); +} + +bool IsRedCodec(const webrtc::RtpCodecCapability& capability) { + return absl::EqualsIgnoreCase(capability.name, kRedCodecName); +} + +bool IsComfortNoiseCodec(const Codec& codec) { + return absl::EqualsIgnoreCase(codec.name, kComfortNoiseCodecName); +} + +// Wrapper for FindMatchingCodecs that uses CodecList +std::optional FindMatchingCodec(const CodecList& codecs1, + const CodecList& codecs2, + const Codec& codec_to_match) { + return webrtc::FindMatchingCodec(codecs1.codecs(), codecs2.codecs(), + codec_to_match); +} + +void StripCNCodecs(CodecList& audio_codecs) { + audio_codecs.writable_codecs().erase( + std::remove_if( + audio_codecs.begin(), audio_codecs.end(), + [](const Codec& codec) { return IsComfortNoiseCodec(codec); }), + audio_codecs.end()); +} + +bool IsMediaContentOfType(const ContentInfo* content, MediaType media_type) { + if (!content || !content->media_description()) { + return false; + } + return content->media_description()->type() == media_type; +} +// Find the codec in `codec_list` that `rtx_codec` is associated with. +const Codec* GetAssociatedCodecForRtx(const CodecList& codec_list, + const Codec& rtx_codec) { + std::string associated_pt_str; + if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType, + &associated_pt_str)) { + RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.id + << " is missing an associated payload type."; + return nullptr; + } + + int associated_pt; + if (!rtc::FromString(associated_pt_str, &associated_pt)) { + RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str + << " of RTX codec " << rtx_codec.id + << " to an integer."; + return nullptr; + } + + // Find the associated codec for the RTX codec. + const Codec* associated_codec = + FindCodecById(codec_list.codecs(), associated_pt); + if (!associated_codec) { + RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type " + << associated_pt << " for RTX codec " << rtx_codec.id + << "."; + } + return associated_codec; +} + +// Find the codec in `codec_list` that `red_codec` is associated with. +const Codec* GetAssociatedCodecForRed(const CodecList& codec_list, + const Codec& red_codec) { + std::string fmtp; + if (!red_codec.GetParam(kCodecParamNotInNameValueFormat, &fmtp)) { + // Don't log for video/RED where this is normal. + if (red_codec.type == Codec::Type::kAudio) { + RTC_LOG(LS_WARNING) << "RED codec " << red_codec.id + << " is missing an associated payload type."; + } + return nullptr; + } + + std::vector redundant_payloads = rtc::split(fmtp, '/'); + if (redundant_payloads.size() < 2) { + return nullptr; + } + + absl::string_view associated_pt_str = redundant_payloads[0]; + int associated_pt; + if (!rtc::FromString(associated_pt_str, &associated_pt)) { + RTC_LOG(LS_WARNING) << "Couldn't convert first payload type " + << associated_pt_str << " of RED codec " << red_codec.id + << " to an integer."; + return nullptr; + } + + // Find the associated codec for the RED codec. + const Codec* associated_codec = + FindCodecById(codec_list.codecs(), associated_pt); + if (!associated_codec) { + RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type " + << associated_pt << " for RED codec " << red_codec.id + << "."; + } + return associated_codec; +} + +// Adds all codecs from `reference_codecs` to `offered_codecs` that don't +// already exist in `offered_codecs` and ensure the payload types don't +// collide. +void MergeCodecs(const CodecList& reference_codecs, + CodecList& offered_codecs, + UsedPayloadTypes* used_pltypes) { + // Add all new codecs that are not RTX/RED codecs. + // The two-pass splitting of the loops means preferring payload types + // of actual codecs with respect to collisions. + 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)) { + Codec codec = reference_codec; + used_pltypes->FindAndSetIdUsed(&codec); + offered_codecs.push_back(codec); + } + } + + // 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)) { + Codec rtx_codec = reference_codec; + const Codec* associated_codec = + GetAssociatedCodecForRtx(reference_codecs, rtx_codec); + if (!associated_codec) { + continue; + } + // 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( + reference_codecs, offered_codecs, *associated_codec); + if (!matching_codec) { + RTC_LOG(LS_WARNING) + << "Couldn't find matching " << associated_codec->name << " codec."; + continue; + } + + rtx_codec.params[kCodecParamAssociatedPayloadType] = + rtc::ToString(matching_codec->id); + used_pltypes->FindAndSetIdUsed(&rtx_codec); + offered_codecs.push_back(rtx_codec); + } else if (reference_codec.GetResiliencyType() == + Codec::ResiliencyType::kRed && + !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( + reference_codecs, offered_codecs, *associated_codec); + if (!matching_codec) { + RTC_LOG(LS_WARNING) << "Couldn't find matching " + << associated_codec->name << " codec."; + continue; + } + + red_codec.params[kCodecParamNotInNameValueFormat] = + rtc::ToString(matching_codec->id) + "/" + + rtc::ToString(matching_codec->id); + } + used_pltypes->FindAndSetIdUsed(&red_codec); + offered_codecs.push_back(red_codec); + } + } + offered_codecs.CheckConsistency(); +} + +// `codecs` is a full list of codecs with correct payload type mappings, which +// don't conflict with mappings of the other media type; `supported_codecs` is +// a list filtered for the media section`s direction but with default payload +// types. +// static +CodecList MatchCodecPreference( + const std::vector& codec_preferences, + const CodecList& codecs, + const CodecList& supported_codecs) { + CodecList filtered_codecs; + bool want_rtx = false; + bool want_red = false; + + for (const auto& codec_preference : codec_preferences) { + if (IsRtxCodec(codec_preference)) { + want_rtx = true; + } else if (IsRedCodec(codec_preference)) { + want_red = true; + } + } + bool red_was_added = false; + for (const auto& codec_preference : codec_preferences) { + auto found_codec = absl::c_find_if( + supported_codecs, [&codec_preference](const Codec& codec) { + // We should not filter out the codec in |codec_preferences| if it + // has a higher level than the codec in |supported_codecs|, as the + // codec in |supported_codecs| may be only with lower level in + // |send_codecs_| and |recv_codecs_| for the same codec. + return IsSameRtpCodecIgnoringLevel(codec, codec_preference); + }); + + if (found_codec != supported_codecs.end()) { + std::optional found_codec_with_correct_pt = + 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. + bool is_red_codec = found_codec_with_correct_pt->GetResiliencyType() == + Codec::ResiliencyType::kRed; + if (!is_red_codec || !red_was_added) { + filtered_codecs.push_back(*found_codec_with_correct_pt); + red_was_added = is_red_codec ? true : red_was_added; + } + std::string id = rtc::ToString(found_codec_with_correct_pt->id); + // Search for the matching rtx or red codec. + if (want_red || want_rtx) { + for (const auto& codec : codecs) { + if (want_rtx && + codec.GetResiliencyType() == Codec::ResiliencyType::kRtx) { + const auto apt = + codec.params.find(cricket::kCodecParamAssociatedPayloadType); + if (apt != codec.params.end() && apt->second == id) { + filtered_codecs.push_back(codec); + break; + } + } else if (want_red && codec.GetResiliencyType() == + Codec::ResiliencyType::kRed) { + // For RED, do not insert the codec again if it was already + // inserted. audio/red for opus gets enabled by having RED before + // the primary codec. + const auto fmtp = + codec.params.find(cricket::kCodecParamNotInNameValueFormat); + if (fmtp != codec.params.end()) { + std::vector redundant_payloads = + rtc::split(fmtp->second, '/'); + if (!redundant_payloads.empty() && + redundant_payloads[0] == id) { + if (!red_was_added) { + filtered_codecs.push_back(codec); + red_was_added = true; + } + break; + } + } + } + } + } + } + } + } + + return filtered_codecs; +} + +// Compute the union of `codecs1` and `codecs2`. +CodecList ComputeCodecsUnion(const CodecList codecs1, const CodecList codecs2) { + CodecList all_codecs; + UsedPayloadTypes used_payload_types; + for (const Codec& codec : codecs1) { + Codec codec_mutable = codec; + used_payload_types.FindAndSetIdUsed(&codec_mutable); + all_codecs.push_back(codec_mutable); + } + + // Use MergeCodecs to merge the second half of our list as it already checks + // and fixes problems with duplicate payload types. + MergeCodecs(codecs2, all_codecs, &used_payload_types); + + return all_codecs; +} + +void MergeCodecsFromDescription( + const std::vector& current_active_contents, + CodecList& audio_codecs, + CodecList& video_codecs, + UsedPayloadTypes* used_pltypes) { + for (const ContentInfo* content : current_active_contents) { + if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) { + MergeCodecs(CodecList{content->media_description()->codecs()}, + audio_codecs, used_pltypes); + } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) { + MergeCodecs(CodecList{content->media_description()->codecs()}, + video_codecs, used_pltypes); + } + } +} + +void NegotiatePacketization(const Codec& local_codec, + const Codec& remote_codec, + Codec* negotiated_codec) { + negotiated_codec->packetization = + (local_codec.packetization == remote_codec.packetization) + ? local_codec.packetization + : std::nullopt; +} + +#ifdef RTC_ENABLE_H265 +void NegotiateTxMode(const Codec& local_codec, + const Codec& remote_codec, + Codec* negotiated_codec) { + negotiated_codec->tx_mode = (local_codec.tx_mode == remote_codec.tx_mode) + ? local_codec.tx_mode + : std::nullopt; +} +#endif + +// For offer, negotiated codec must have the same level-id as that in +// |supported_codecs| with same profile. +void NegotiateVideoCodecLevelsForOffer( + const MediaDescriptionOptions& media_description_options, + const CodecList& supported_codecs, + CodecList& filtered_codecs) { + if (filtered_codecs.empty() || supported_codecs.empty()) { + return; + } + + // TODO(http://crbugs.com/376306259): We should handle level-idx for AV1. + // Ideally this should be done for all codecs, but RFCs of other codecs + // do not clear define the expected behavior for the level in the offer. +#ifdef RTC_ENABLE_H265 + if (media_description_options.type == MEDIA_TYPE_VIDEO) { + std::unordered_map + supported_h265_profiles; + // The assumption here is that H.265 codecs with the same profile and tier + // are already with highest level for that profile in both + // |supported_codecs| and |filtered_codecs|. + for (const Codec& supported_codec : supported_codecs) { + if (absl::EqualsIgnoreCase(supported_codec.name, kH265CodecName)) { + std::optional supported_ptl = + webrtc::ParseSdpForH265ProfileTierLevel(supported_codec.params); + if (supported_ptl.has_value()) { + supported_h265_profiles[supported_ptl->profile] = + supported_ptl->level; + } + } + } + + if (supported_h265_profiles.empty()) { + return; + } + + for (auto& filtered_codec : filtered_codecs) { + if (absl::EqualsIgnoreCase(filtered_codec.name, kH265CodecName)) { + std::optional filtered_ptl = + webrtc::ParseSdpForH265ProfileTierLevel(filtered_codec.params); + if (filtered_ptl.has_value()) { + auto it = supported_h265_profiles.find(filtered_ptl->profile); + + if (it != supported_h265_profiles.end() && + filtered_ptl->level != it->second) { + filtered_codec.params[kH265FmtpLevelId] = + webrtc::H265LevelToString(it->second); + } + } + } + } + } +#endif +} + +} // namespace + +webrtc::RTCErrorOr> CodecVendor::GetNegotiatedCodecsForOffer( + const MediaDescriptionOptions& media_description_options, + const MediaSessionOptions& session_options, + const ContentInfo* current_content, + const CodecList& codecs) { + CodecList filtered_codecs; + CodecList supported_codecs = + media_description_options.type == MEDIA_TYPE_AUDIO + ? GetAudioCodecsForOffer(media_description_options.direction) + : GetVideoCodecsForOffer(media_description_options.direction); + + if (!media_description_options.codec_preferences.empty()) { + // Add the codecs from the current transceiver's codec preferences. + // They override any existing codecs from previous negotiations. + filtered_codecs = MatchCodecPreference( + media_description_options.codec_preferences, codecs, supported_codecs); + } else { + // Add the codecs from current content if it exists and is not rejected nor + // recycled. + if (current_content && !current_content->rejected && + current_content->mid() == media_description_options.mid) { + if (!IsMediaContentOfType(current_content, + media_description_options.type)) { + // Can happen if the remote side re-uses a MID while recycling. + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, + "Media type for content with mid='" + + current_content->mid() + + "' does not match previous type."); + } + const MediaContentDescription* mcd = current_content->media_description(); + for (const Codec& codec : mcd->codecs()) { + if (webrtc::FindMatchingCodec(mcd->codecs(), codecs.codecs(), codec)) { + filtered_codecs.push_back(codec); + } + } + } + // 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)) { + // 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. + if (media_description_options.type == MEDIA_TYPE_VIDEO && + found_codec->GetResiliencyType() == Codec::ResiliencyType::kRtx) { + // For RTX we might need to adjust the apt parameter if we got a + // remote offer without RTX for a codec for which we support RTX. + auto referenced_codec = + GetAssociatedCodecForRtx(supported_codecs, codec); + 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); + if (changed_referenced_codec) { + found_codec->SetParam(kCodecParamAssociatedPayloadType, + changed_referenced_codec->id); + } + } + filtered_codecs.push_back(*found_codec); + } + } + } + + if (media_description_options.type == MEDIA_TYPE_AUDIO && + !session_options.vad_enabled) { + // If application doesn't want CN codecs in offer. + StripCNCodecs(filtered_codecs); + } else if (media_description_options.type == MEDIA_TYPE_VIDEO && + session_options.raw_packetization_for_video) { + for (Codec& codec : filtered_codecs) { + if (codec.IsMediaCodec()) { + codec.packetization = kPacketizationParamRaw; + } + } + } + NegotiateVideoCodecLevelsForOffer(media_description_options, supported_codecs, + filtered_codecs); + return filtered_codecs.codecs(); +} + +webrtc::RTCErrorOr CodecVendor::GetNegotiatedCodecsForAnswer( + const MediaDescriptionOptions& media_description_options, + const MediaSessionOptions& session_options, + webrtc::RtpTransceiverDirection offer_rtd, + webrtc::RtpTransceiverDirection answer_rtd, + const ContentInfo* current_content, + const CodecList& codecs) { + CodecList filtered_codecs; + + const CodecList& supported_codecs = + media_description_options.type == MEDIA_TYPE_AUDIO + ? GetAudioCodecsForAnswer(offer_rtd, answer_rtd) + : GetVideoCodecsForAnswer(offer_rtd, answer_rtd); + if (!media_description_options.codec_preferences.empty()) { + filtered_codecs = MatchCodecPreference( + media_description_options.codec_preferences, codecs, supported_codecs); + } else { + // Add the codecs from current content if it exists and is not rejected nor + // recycled. + if (current_content && !current_content->rejected && + current_content->mid() == media_description_options.mid) { + if (!IsMediaContentOfType(current_content, + media_description_options.type)) { + // Can happen if the remote side re-uses a MID while recycling. + LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, + "Media type for content with mid='" + + current_content->mid() + + "' does not match previous type."); + } + const MediaContentDescription* mcd = current_content->media_description(); + for (const Codec& codec : mcd->codecs()) { + if (webrtc::FindMatchingCodec(mcd->codecs(), codecs.codecs(), codec)) { + filtered_codecs.push_back(codec); + } + } + } + // Add other supported codecs. + CodecList other_codecs; + for (const Codec& codec : supported_codecs) { + if (FindMatchingCodec(supported_codecs, codecs, codec) && + !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); + } + } + + // Use ComputeCodecsUnion to avoid having duplicate payload IDs. + // This is a no-op for audio until RTX is added. + filtered_codecs = ComputeCodecsUnion(filtered_codecs, other_codecs); + } + + if (media_description_options.type == MEDIA_TYPE_AUDIO && + !session_options.vad_enabled) { + // If application doesn't want CN codecs in offer. + StripCNCodecs(filtered_codecs); + } else if (media_description_options.type == MEDIA_TYPE_VIDEO && + session_options.raw_packetization_for_video) { + for (Codec& codec : filtered_codecs) { + if (codec.IsMediaCodec()) { + codec.packetization = kPacketizationParamRaw; + } + } + } + return filtered_codecs.codecs(); +} + +void CodecVendor::NegotiateCodecs(const CodecList& local_codecs, + const CodecList& 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); + // 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) { + Codec negotiated = ours; + NegotiatePacketization(ours, *theirs, &negotiated); + negotiated.IntersectFeedbackParams(*theirs); + if (negotiated.GetResiliencyType() == Codec::ResiliencyType::kRtx) { + const auto apt_it = + theirs->params.find(kCodecParamAssociatedPayloadType); + // webrtc::FindMatchingCodec shouldn't return something with no apt + // value. + RTC_DCHECK(apt_it != theirs->params.end()); + negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second); + + // We support parsing the declarative rtx-time parameter. + const auto rtx_time_it = theirs->params.find(kCodecParamRtxTime); + if (rtx_time_it != theirs->params.end()) { + negotiated.SetParam(kCodecParamRtxTime, rtx_time_it->second); + } + } else if (negotiated.GetResiliencyType() == + Codec::ResiliencyType::kRed) { + const auto red_it = + theirs->params.find(kCodecParamNotInNameValueFormat); + if (red_it != theirs->params.end()) { + negotiated.SetParam(kCodecParamNotInNameValueFormat, red_it->second); + } + } + if (absl::EqualsIgnoreCase(ours.name, kH264CodecName)) { + webrtc::H264GenerateProfileLevelIdForAnswer(ours.params, theirs->params, + &negotiated.params); + } +#ifdef RTC_ENABLE_H265 + if (absl::EqualsIgnoreCase(ours.name, kH265CodecName)) { + webrtc::H265GenerateProfileTierLevelForAnswer( + ours.params, theirs->params, &negotiated.params); + NegotiateTxMode(ours, *theirs, &negotiated); + } +#endif + negotiated.id = theirs->id; + negotiated.name = theirs->name; + negotiated_codecs->push_back(std::move(negotiated)); + } + } + if (keep_offer_order) { + // RFC3264: Although the answerer MAY list the formats in their desired + // order of preference, it is RECOMMENDED that unless there is a + // specific reason, the answerer list formats in the same relative order + // they were present in the offer. + // This can be skipped when the transceiver has any codec preferences. + std::unordered_map payload_type_preferences; + int preference = static_cast(offered_codecs.size() + 1); + for (const Codec& codec : offered_codecs) { + payload_type_preferences[codec.id] = preference--; + } + absl::c_sort(*negotiated_codecs, [&payload_type_preferences]( + const Codec& a, const Codec& b) { + return payload_type_preferences[a.id] > payload_type_preferences[b.id]; + }); + } +} + +CodecVendor::CodecVendor(MediaEngineInterface* media_engine, bool rtx_enabled) { + // Null media_engine is permitted in order to allow unit testing where + // the codecs are explicitly set by the test. + if (media_engine) { + audio_send_codecs_ = CodecList(media_engine->voice().send_codecs()); + audio_recv_codecs_ = CodecList(media_engine->voice().recv_codecs()); + video_send_codecs_ = + CodecList(media_engine->video().send_codecs(rtx_enabled)); + video_recv_codecs_ = + CodecList(media_engine->video().recv_codecs(rtx_enabled)); + ComputeAudioCodecsIntersectionAndUnion(); + ComputeVideoCodecsIntersectionAndUnion(); + } +} + +const CodecList& CodecVendor::audio_sendrecv_codecs() const { + return audio_sendrecv_codecs_; +} + +const CodecList& CodecVendor::audio_send_codecs() const { + return audio_send_codecs_; +} + +const CodecList& CodecVendor::audio_recv_codecs() const { + return audio_recv_codecs_; +} + +void CodecVendor::set_audio_codecs(const CodecList& send_codecs, + const CodecList& recv_codecs) { + audio_send_codecs_ = send_codecs; + audio_recv_codecs_ = recv_codecs; + ComputeAudioCodecsIntersectionAndUnion(); +} + +const CodecList& CodecVendor::video_sendrecv_codecs() const { + return video_sendrecv_codecs_; +} + +const CodecList& CodecVendor::video_send_codecs() const { + return video_send_codecs_; +} + +const CodecList& CodecVendor::video_recv_codecs() const { + return video_recv_codecs_; +} + +void CodecVendor::set_video_codecs(const CodecList& send_codecs, + const CodecList& recv_codecs) { + video_send_codecs_ = send_codecs; + video_recv_codecs_ = recv_codecs; + ComputeVideoCodecsIntersectionAndUnion(); +} +// Getting codecs for an offer involves these steps: +// +// 1. Construct payload type -> codec mappings for current description. +// 2. Add any reference codecs that weren't already present +// 3. For each individual media description (m= section), filter codecs based +// on the directional attribute (happens in another method). +void CodecVendor::GetCodecsForOffer( + const std::vector& current_active_contents, + Codecs* audio_codecs, + Codecs* video_codecs) const { + // First - get all codecs from the current description if the media type + // is used. Add them to `used_pltypes` so the payload type is not reused if a + // new media type is added. + UsedPayloadTypes used_pltypes; + CodecList video_codec_list(*video_codecs); + CodecList audio_codec_list(*audio_codecs); + MergeCodecsFromDescription(current_active_contents, audio_codec_list, + video_codec_list, &used_pltypes); + + // Add our codecs that are not in the current description. + MergeCodecs(CodecList{all_audio_codecs_}, audio_codec_list, &used_pltypes); + MergeCodecs(CodecList{all_video_codecs_}, video_codec_list, &used_pltypes); + *audio_codecs = audio_codec_list.codecs(); + *video_codecs = video_codec_list.codecs(); +} + +// Getting codecs for an answer involves these steps: +// +// 1. Construct payload type -> codec mappings for current description. +// 2. Add any codecs from the offer that weren't already present. +// 3. Add any remaining codecs that weren't already present. +// 4. For each individual media description (m= section), filter codecs based +// on the directional attribute (happens in another method). +void CodecVendor::GetCodecsForAnswer( + const std::vector& current_active_contents, + const SessionDescription& remote_offer, + Codecs* audio_codecs, + Codecs* video_codecs) const { + // First - get all codecs from the current description if the media type + // is used. Add them to `used_pltypes` so the payload type is not reused if a + // new media type is added. + UsedPayloadTypes used_pltypes; + CodecList video_codec_list(*video_codecs); + CodecList audio_codec_list(*audio_codecs); + MergeCodecsFromDescription(current_active_contents, audio_codec_list, + video_codec_list, &used_pltypes); + + // Second - filter out codecs that we don't support at all and should ignore. + CodecList filtered_offered_audio_codecs; + CodecList filtered_offered_video_codecs; + for (const ContentInfo& content : remote_offer.contents()) { + if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) { + CodecList 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)) { + filtered_offered_audio_codecs.push_back(offered_audio_codec); + } + } + } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) { + CodecList 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)) { + filtered_offered_video_codecs.push_back(offered_video_codec); + } + } + } + } + + // Add codecs that are not in the current description but were in + // `remote_offer`. + MergeCodecs(filtered_offered_audio_codecs, audio_codec_list, &used_pltypes); + MergeCodecs(filtered_offered_video_codecs, video_codec_list, &used_pltypes); + *audio_codecs = audio_codec_list.codecs(); + *video_codecs = video_codec_list.codecs(); +} + +const CodecList& CodecVendor::GetVideoCodecsForOffer( + const RtpTransceiverDirection& direction) const { + switch (direction) { + // If stream is inactive - generate list as if sendrecv. + case RtpTransceiverDirection::kSendRecv: + case RtpTransceiverDirection::kStopped: + case RtpTransceiverDirection::kInactive: + return video_sendrecv_codecs_; + case RtpTransceiverDirection::kSendOnly: + return video_send_codecs_; + case RtpTransceiverDirection::kRecvOnly: + return video_recv_codecs_; + } + RTC_CHECK_NOTREACHED(); +} + +const CodecList& CodecVendor::GetVideoCodecsForAnswer( + const RtpTransceiverDirection& offer, + const RtpTransceiverDirection& answer) const { + switch (answer) { + // For inactive and sendrecv answers, generate lists as if we were to accept + // the offer's direction. See RFC 3264 Section 6.1. + case RtpTransceiverDirection::kSendRecv: + case RtpTransceiverDirection::kStopped: + case RtpTransceiverDirection::kInactive: + return GetVideoCodecsForOffer( + webrtc::RtpTransceiverDirectionReversed(offer)); + case RtpTransceiverDirection::kSendOnly: + return video_send_codecs_; + case RtpTransceiverDirection::kRecvOnly: + return video_recv_codecs_; + } + RTC_CHECK_NOTREACHED(); +} + +const CodecList& CodecVendor::GetAudioCodecsForOffer( + const RtpTransceiverDirection& direction) const { + switch (direction) { + // If stream is inactive - generate list as if sendrecv. + case RtpTransceiverDirection::kSendRecv: + case RtpTransceiverDirection::kStopped: + case RtpTransceiverDirection::kInactive: + return audio_sendrecv_codecs_; + case RtpTransceiverDirection::kSendOnly: + return audio_send_codecs_; + case RtpTransceiverDirection::kRecvOnly: + return audio_recv_codecs_; + } + RTC_CHECK_NOTREACHED(); +} + +const CodecList& CodecVendor::GetAudioCodecsForAnswer( + const RtpTransceiverDirection& offer, + const RtpTransceiverDirection& answer) const { + switch (answer) { + // For inactive and sendrecv answers, generate lists as if we were to accept + // the offer's direction. See RFC 3264 Section 6.1. + case RtpTransceiverDirection::kSendRecv: + case RtpTransceiverDirection::kStopped: + case RtpTransceiverDirection::kInactive: + return GetAudioCodecsForOffer( + webrtc::RtpTransceiverDirectionReversed(offer)); + case RtpTransceiverDirection::kSendOnly: + return audio_send_codecs_; + case RtpTransceiverDirection::kRecvOnly: + return audio_recv_codecs_; + } + RTC_CHECK_NOTREACHED(); +} + +void CodecVendor::ComputeAudioCodecsIntersectionAndUnion() { + audio_sendrecv_codecs_.clear(); + all_audio_codecs_.clear(); + // 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)) { + // 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)) { + all_audio_codecs_.push_back(recv); + } + } + // Use NegotiateCodecs to merge our codec lists, since the operation is + // essentially the same. Put send_codecs as the offered_codecs, which is the + // order we'd like to follow. The reasoning is that encoding is usually more + // expensive than decoding, and prioritizing a codec in the send list probably + // means it's a codec we can handle efficiently. + std::vector audio_sendrecv_codecs_vector; + NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_, + &audio_sendrecv_codecs_vector, true); + audio_sendrecv_codecs_ = CodecList(audio_sendrecv_codecs_vector); +} + +void CodecVendor::ComputeVideoCodecsIntersectionAndUnion() { + video_sendrecv_codecs_.clear(); + + // Use ComputeCodecsUnion to avoid having duplicate payload IDs + all_video_codecs_ = ComputeCodecsUnion(CodecList(video_recv_codecs_), + CodecList(video_send_codecs_)); + + // Use NegotiateCodecs to merge our codec lists, since the operation is + // essentially the same. Put send_codecs as the offered_codecs, which is the + // order we'd like to follow. The reasoning is that encoding is usually more + // expensive than decoding, and prioritizing a codec in the send list probably + // means it's a codec we can handle efficiently. + // Also for the same profile of a codec, if there are different levels in the + // send and receive codecs, |video_sendrecv_codecs_| will contain the lower + // level of the two for that profile. + std::vector video_sendrecv_codecs_vector; + NegotiateCodecs(video_recv_codecs_, video_send_codecs_, + &video_sendrecv_codecs_vector, true); + video_sendrecv_codecs_ = CodecList(video_sendrecv_codecs_vector); +} + +} // namespace cricket diff --git a/pc/codec_vendor.h b/pc/codec_vendor.h new file mode 100644 index 0000000000..3cb7262e3f --- /dev/null +++ b/pc/codec_vendor.h @@ -0,0 +1,123 @@ +/* + * Copyright 2025 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 PC_CODEC_VENDOR_H_ +#define PC_CODEC_VENDOR_H_ + +#include + +#include "api/rtc_error.h" +#include "api/rtp_transceiver_direction.h" +#include "media/base/codec.h" +#include "media/base/codec_list.h" +#include "media/base/media_engine.h" +#include "pc/media_options.h" +#include "pc/session_description.h" + +namespace cricket { + +// This class contains the functions required to compute the list of codecs +// for SDP offer/answer. It is exposed to MediaSessionDescriptionFactory +// for the construction of offers and answers. + +// TODO: bugs.webrtc.org/360058654 - complete the architectural changes +// The list of things to be done: +// - Make as much as possible private. +// - Split object usage into a video object and an audio object. +// - Remove audio/video from the call names, merge code where possible. +// - Make the class instances owned by transceivers, so that codec +// lists can differ per transceiver. +// For cleanliness: +// - Thread guard +class CodecVendor { + public: + CodecVendor(MediaEngineInterface* media_engine, bool rtx_enabled); + + public: + void GetCodecsForOffer( + const std::vector& current_active_contents, + Codecs* audio_codecs, + Codecs* video_codecs) const; + void GetCodecsForAnswer( + const std::vector& current_active_contents, + const SessionDescription& remote_offer, + Codecs* audio_codecs, + Codecs* video_codecs) const; + + webrtc::RTCErrorOr> GetNegotiatedCodecsForOffer( + const MediaDescriptionOptions& media_description_options, + const MediaSessionOptions& session_options, + const ContentInfo* current_content, + const CodecList& codecs); + + webrtc::RTCErrorOr GetNegotiatedCodecsForAnswer( + const MediaDescriptionOptions& media_description_options, + const MediaSessionOptions& session_options, + webrtc::RtpTransceiverDirection offer_rtd, + webrtc::RtpTransceiverDirection answer_rtd, + const ContentInfo* current_content, + const CodecList& codecs); + + static void NegotiateCodecs(const CodecList& local_codecs, + const CodecList& offered_codecs, + std::vector* negotiated_codecs, + bool keep_offer_order); + // Functions exposed for testing + void set_audio_codecs(const CodecList& send_codecs, + const CodecList& recv_codecs); + void set_audio_codecs(const std::vector& send_codecs, + const std::vector& recv_codecs) { + set_audio_codecs(CodecList(send_codecs), CodecList(recv_codecs)); + } + void set_video_codecs(const CodecList& send_codecs, + const CodecList& recv_codecs); + void set_video_codecs(const std::vector& send_codecs, + const std::vector& recv_codecs) { + set_video_codecs(CodecList(send_codecs), CodecList(recv_codecs)); + } + const CodecList& audio_sendrecv_codecs() const; + const CodecList& audio_send_codecs() const; + const CodecList& audio_recv_codecs() const; + const CodecList& video_sendrecv_codecs() const; + const CodecList& video_send_codecs() const; + const CodecList& video_recv_codecs() const; + + private: + const CodecList& GetAudioCodecsForOffer( + const webrtc::RtpTransceiverDirection& direction) const; + const CodecList& GetAudioCodecsForAnswer( + const webrtc::RtpTransceiverDirection& offer, + const webrtc::RtpTransceiverDirection& answer) const; + const CodecList& GetVideoCodecsForOffer( + const webrtc::RtpTransceiverDirection& direction) const; + const CodecList& GetVideoCodecsForAnswer( + const webrtc::RtpTransceiverDirection& offer, + const webrtc::RtpTransceiverDirection& answer) const; + void ComputeAudioCodecsIntersectionAndUnion(); + + void ComputeVideoCodecsIntersectionAndUnion(); + + CodecList audio_send_codecs_; + CodecList audio_recv_codecs_; + // Intersection of send and recv. + CodecList audio_sendrecv_codecs_; + // Union of send and recv. + CodecList all_audio_codecs_; + CodecList video_send_codecs_; + CodecList video_recv_codecs_; + // Intersection of send and recv. + CodecList video_sendrecv_codecs_; + // Union of send and recv. + CodecList all_video_codecs_; +}; + +} // namespace cricket + +#endif // PC_CODEC_VENDOR_H_ diff --git a/pc/media_options.cc b/pc/media_options.cc new file mode 100644 index 0000000000..5ad87958b4 --- /dev/null +++ b/pc/media_options.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2025 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 "pc/media_options.h" + +#include +#include + +#include "absl/algorithm/container.h" +#include "api/media_types.h" +#include "media/base/rid_description.h" +#include "pc/simulcast_description.h" +#include "rtc_base/checks.h" + +namespace cricket { + +namespace { +// note: function duplicated in media_session.cc +bool ValidateSimulcastLayers(const std::vector& rids, + const SimulcastLayerList& simulcast_layers) { + return absl::c_all_of( + simulcast_layers.GetAllLayers(), [&rids](const SimulcastLayer& layer) { + return absl::c_any_of(rids, [&layer](const RidDescription& rid) { + return rid.rid == layer.rid; + }); + }); +} + +} // namespace + +void MediaDescriptionOptions::AddAudioSender( + const std::string& track_id, + const std::vector& stream_ids) { + RTC_DCHECK(type == MEDIA_TYPE_AUDIO); + AddSenderInternal(track_id, stream_ids, {}, SimulcastLayerList(), 1); +} + +void MediaDescriptionOptions::AddVideoSender( + const std::string& track_id, + const std::vector& stream_ids, + const std::vector& rids, + const SimulcastLayerList& simulcast_layers, + int num_sim_layers) { + RTC_DCHECK(type == MEDIA_TYPE_VIDEO); + RTC_DCHECK(rids.empty() || num_sim_layers == 0) + << "RIDs are the compliant way to indicate simulcast."; + RTC_DCHECK(ValidateSimulcastLayers(rids, simulcast_layers)); + AddSenderInternal(track_id, stream_ids, rids, simulcast_layers, + num_sim_layers); +} + +void MediaDescriptionOptions::AddSenderInternal( + const std::string& track_id, + const std::vector& stream_ids, + const std::vector& rids, + const SimulcastLayerList& simulcast_layers, + int num_sim_layers) { + // TODO(steveanton): Support any number of stream ids. + RTC_CHECK(stream_ids.size() == 1U); + SenderOptions options; + options.track_id = track_id; + options.stream_ids = stream_ids; + options.simulcast_layers = simulcast_layers; + options.rids = rids; + options.num_sim_layers = num_sim_layers; + sender_options.push_back(options); +} + +bool MediaSessionOptions::HasMediaDescription(MediaType type) const { + return absl::c_any_of( + media_description_options, + [type](const MediaDescriptionOptions& t) { return t.type == type; }); +} + +} // namespace cricket diff --git a/pc/media_options.h b/pc/media_options.h new file mode 100644 index 0000000000..c70e956eda --- /dev/null +++ b/pc/media_options.h @@ -0,0 +1,120 @@ +/* + * Copyright 2025 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. + */ + +// Option structures for MediaSession APIs. +#ifndef PC_MEDIA_OPTIONS_H_ +#define PC_MEDIA_OPTIONS_H_ + +#include +#include + +#include "api/crypto/crypto_options.h" +#include "api/media_types.h" +#include "api/rtp_parameters.h" +#include "api/rtp_transceiver_direction.h" +#include "media/base/codec.h" +#include "media/base/rid_description.h" +#include "p2p/base/transport_description.h" +#include "p2p/base/transport_description_factory.h" +#include "pc/simulcast_description.h" + +namespace cricket { + +// Default RTCP CNAME for unit tests. +const char kDefaultRtcpCname[] = "DefaultRtcpCname"; + +// Options for an RtpSender contained with an media description/"m=" section. +// Note: Spec-compliant Simulcast and legacy simulcast are mutually exclusive. +struct SenderOptions { + std::string track_id; + std::vector stream_ids; + // Use RIDs and Simulcast Layers to indicate spec-compliant Simulcast. + std::vector rids; + SimulcastLayerList simulcast_layers; + // Use `num_sim_layers` to indicate legacy simulcast. + int num_sim_layers; +}; + +// Options for an individual media description/"m=" section. +struct MediaDescriptionOptions { + MediaDescriptionOptions(MediaType type, + const std::string& mid, + webrtc::RtpTransceiverDirection direction, + bool stopped) + : type(type), mid(mid), direction(direction), stopped(stopped) {} + + // TODO(deadbeef): When we don't support Plan B, there will only be one + // sender per media description and this can be simplified. + void AddAudioSender(const std::string& track_id, + const std::vector& stream_ids); + void AddVideoSender(const std::string& track_id, + const std::vector& stream_ids, + const std::vector& rids, + const SimulcastLayerList& simulcast_layers, + int num_sim_layers); + + MediaType type; + std::string mid; + webrtc::RtpTransceiverDirection direction; + bool stopped; + TransportOptions transport_options; + // Note: There's no equivalent "RtpReceiverOptions" because only send + // stream information goes in the local descriptions. + std::vector sender_options; + std::vector codec_preferences; + std::vector header_extensions; + // Codecs to include in a generated offer or answer. + // If this is used, session-level codec lists MUST be ignored. + std::vector codecs_to_include; + + private: + // Doesn't DCHECK on `type`. + void AddSenderInternal(const std::string& track_id, + const std::vector& stream_ids, + const std::vector& rids, + const SimulcastLayerList& simulcast_layers, + int num_sim_layers); +}; + +// Provides a mechanism for describing how m= sections should be generated. +// The m= section with index X will use media_description_options[X]. There +// must be an option for each existing section if creating an answer, or a +// subsequent offer. +struct MediaSessionOptions { + MediaSessionOptions() {} + + bool has_audio() const { return HasMediaDescription(MEDIA_TYPE_AUDIO); } + bool has_video() const { return HasMediaDescription(MEDIA_TYPE_VIDEO); } + bool has_data() const { return HasMediaDescription(MEDIA_TYPE_DATA); } + + bool HasMediaDescription(MediaType type) const; + + bool vad_enabled = true; // When disabled, removes all CN codecs from SDP. + bool rtcp_mux_enabled = true; + bool bundle_enabled = false; + bool offer_extmap_allow_mixed = false; + bool raw_packetization_for_video = false; + std::string rtcp_cname = kDefaultRtcpCname; + webrtc::CryptoOptions crypto_options; + // List of media description options in the same order that the media + // descriptions will be generated. + std::vector media_description_options; + std::vector pooled_ice_credentials; + + // Use the draft-ietf-mmusic-sctp-sdp-03 obsolete syntax for SCTP + // datachannels. + // Default is true for backwards compatibility with clients that use + // this internal interface. + bool use_obsolete_sctp_sdp = true; +}; + +} // namespace cricket + +#endif // PC_MEDIA_OPTIONS_H_ diff --git a/pc/media_session.cc b/pc/media_session.cc index 6f7cf2073e..c5c262a6f7 100644 --- a/pc/media_session.cc +++ b/pc/media_session.cc @@ -13,11 +13,9 @@ #include #include -#include #include #include #include -#include #include #include @@ -31,12 +29,10 @@ #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/codec_list.h" #include "media/base/media_constants.h" #include "media/base/media_engine.h" #include "media/base/rid_description.h" -#include "media/base/sdp_video_format_utils.h" #include "media/base/stream_params.h" #include "media/sctp/sctp_transport_internal.h" #include "p2p/base/ice_credentials_iterator.h" @@ -44,6 +40,8 @@ #include "p2p/base/transport_description.h" #include "p2p/base/transport_description_factory.h" #include "p2p/base/transport_info.h" +#include "pc/codec_vendor.h" +#include "pc/media_options.h" #include "pc/media_protocol_names.h" #include "pc/rtp_media_utils.h" #include "pc/session_description.h" @@ -51,12 +49,10 @@ #include "pc/used_ids.h" #include "rtc_base/checks.h" #include "rtc_base/logging.h" -#include "rtc_base/string_encode.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/unique_id_generator.h" #ifdef RTC_ENABLE_H265 -#include "api/video_codecs/h265_profile_tier_level.h" #endif namespace { @@ -122,20 +118,12 @@ namespace cricket { namespace { -bool IsRtxCodec(const webrtc::RtpCodecCapability& capability) { - return absl::EqualsIgnoreCase(capability.name, kRtxCodecName); -} - bool ContainsRtxCodec(const std::vector& codecs) { return absl::c_find_if(codecs, [](const Codec& c) { return c.GetResiliencyType() == Codec::ResiliencyType::kRtx; }) != codecs.end(); } -bool IsRedCodec(const webrtc::RtpCodecCapability& capability) { - return absl::EqualsIgnoreCase(capability.name, kRedCodecName); -} - bool ContainsFlexfecCodec(const std::vector& codecs) { return absl::c_find_if(codecs, [](const Codec& c) { return c.GetResiliencyType() == Codec::ResiliencyType::kFlexfec; @@ -146,22 +134,6 @@ bool IsComfortNoiseCodec(const Codec& codec) { return absl::EqualsIgnoreCase(codec.name, kComfortNoiseCodecName); } -// Wrapper for FindMatchingCodecs that uses CodecList -std::optional FindMatchingCodec(const CodecList& codecs1, - const CodecList& codecs2, - const Codec& codec_to_match) { - return webrtc::FindMatchingCodec(codecs1.codecs(), codecs2.codecs(), - codec_to_match); -} - -void StripCNCodecs(CodecList& audio_codecs) { - audio_codecs.writable_codecs().erase( - std::remove_if( - audio_codecs.begin(), audio_codecs.end(), - [](const Codec& codec) { return IsComfortNoiseCodec(codec); }), - audio_codecs.end()); -} - RtpTransceiverDirection NegotiateRtpTransceiverDirection( RtpTransceiverDirection offer, RtpTransceiverDirection wants) { @@ -453,25 +425,6 @@ RTCError CreateMediaContentOffer( offer); } -void NegotiatePacketization(const Codec& local_codec, - const Codec& remote_codec, - Codec* negotiated_codec) { - negotiated_codec->packetization = - (local_codec.packetization == remote_codec.packetization) - ? local_codec.packetization - : std::nullopt; -} - -#ifdef RTC_ENABLE_H265 -void NegotiateTxMode(const Codec& local_codec, - const Codec& remote_codec, - Codec* negotiated_codec) { - negotiated_codec->tx_mode = (local_codec.tx_mode == remote_codec.tx_mode) - ? local_codec.tx_mode - : std::nullopt; -} -#endif - // Update the ID fields of the codec vector. // If any codec has an ID with value "kIdNotSet", use the payload type suggester // to assign and record a payload type for it. @@ -517,312 +470,6 @@ webrtc::RTCError AssignCodecIdsAndLinkRed( return webrtc::RTCError::OK(); } -void NegotiateCodecs(const CodecList& local_codecs, - const CodecList& 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); - // 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) { - Codec negotiated = ours; - NegotiatePacketization(ours, *theirs, &negotiated); - negotiated.IntersectFeedbackParams(*theirs); - if (negotiated.GetResiliencyType() == Codec::ResiliencyType::kRtx) { - const auto apt_it = - theirs->params.find(kCodecParamAssociatedPayloadType); - // webrtc::FindMatchingCodec shouldn't return something with no apt - // value. - RTC_DCHECK(apt_it != theirs->params.end()); - negotiated.SetParam(kCodecParamAssociatedPayloadType, apt_it->second); - - // We support parsing the declarative rtx-time parameter. - const auto rtx_time_it = theirs->params.find(kCodecParamRtxTime); - if (rtx_time_it != theirs->params.end()) { - negotiated.SetParam(kCodecParamRtxTime, rtx_time_it->second); - } - } else if (negotiated.GetResiliencyType() == - Codec::ResiliencyType::kRed) { - const auto red_it = - theirs->params.find(kCodecParamNotInNameValueFormat); - if (red_it != theirs->params.end()) { - negotiated.SetParam(kCodecParamNotInNameValueFormat, red_it->second); - } - } - if (absl::EqualsIgnoreCase(ours.name, kH264CodecName)) { - webrtc::H264GenerateProfileLevelIdForAnswer(ours.params, theirs->params, - &negotiated.params); - } -#ifdef RTC_ENABLE_H265 - if (absl::EqualsIgnoreCase(ours.name, kH265CodecName)) { - webrtc::H265GenerateProfileTierLevelForAnswer( - ours.params, theirs->params, &negotiated.params); - NegotiateTxMode(ours, *theirs, &negotiated); - } -#endif - negotiated.id = theirs->id; - negotiated.name = theirs->name; - negotiated_codecs->push_back(std::move(negotiated)); - } - } - if (keep_offer_order) { - // RFC3264: Although the answerer MAY list the formats in their desired - // order of preference, it is RECOMMENDED that unless there is a - // specific reason, the answerer list formats in the same relative order - // they were present in the offer. - // This can be skipped when the transceiver has any codec preferences. - std::unordered_map payload_type_preferences; - int preference = static_cast(offered_codecs.size() + 1); - for (const Codec& codec : offered_codecs) { - payload_type_preferences[codec.id] = preference--; - } - absl::c_sort(*negotiated_codecs, [&payload_type_preferences]( - const Codec& a, const Codec& b) { - return payload_type_preferences[a.id] > payload_type_preferences[b.id]; - }); - } -} - -// Find the codec in `codec_list` that `rtx_codec` is associated with. -const Codec* GetAssociatedCodecForRtx(const CodecList& codec_list, - const Codec& rtx_codec) { - std::string associated_pt_str; - if (!rtx_codec.GetParam(kCodecParamAssociatedPayloadType, - &associated_pt_str)) { - RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.id - << " is missing an associated payload type."; - return nullptr; - } - - int associated_pt; - if (!rtc::FromString(associated_pt_str, &associated_pt)) { - RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str - << " of RTX codec " << rtx_codec.id - << " to an integer."; - return nullptr; - } - - // Find the associated codec for the RTX codec. - const Codec* associated_codec = - FindCodecById(codec_list.codecs(), associated_pt); - if (!associated_codec) { - RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type " - << associated_pt << " for RTX codec " << rtx_codec.id - << "."; - } - return associated_codec; -} - -// Find the codec in `codec_list` that `red_codec` is associated with. -const Codec* GetAssociatedCodecForRed(const CodecList& codec_list, - const Codec& red_codec) { - std::string fmtp; - if (!red_codec.GetParam(kCodecParamNotInNameValueFormat, &fmtp)) { - // Don't log for video/RED where this is normal. - if (red_codec.type == Codec::Type::kAudio) { - RTC_LOG(LS_WARNING) << "RED codec " << red_codec.id - << " is missing an associated payload type."; - } - return nullptr; - } - - std::vector redundant_payloads = rtc::split(fmtp, '/'); - if (redundant_payloads.size() < 2) { - return nullptr; - } - - absl::string_view associated_pt_str = redundant_payloads[0]; - int associated_pt; - if (!rtc::FromString(associated_pt_str, &associated_pt)) { - RTC_LOG(LS_WARNING) << "Couldn't convert first payload type " - << associated_pt_str << " of RED codec " << red_codec.id - << " to an integer."; - return nullptr; - } - - // Find the associated codec for the RED codec. - const Codec* associated_codec = - FindCodecById(codec_list.codecs(), associated_pt); - if (!associated_codec) { - RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type " - << associated_pt << " for RED codec " << red_codec.id - << "."; - } - return associated_codec; -} - -// Adds all codecs from `reference_codecs` to `offered_codecs` that don't -// already exist in `offered_codecs` and ensure the payload types don't -// collide. -void MergeCodecs(const CodecList& reference_codecs, - CodecList& offered_codecs, - UsedPayloadTypes* used_pltypes) { - // Add all new codecs that are not RTX/RED codecs. - // The two-pass splitting of the loops means preferring payload types - // of actual codecs with respect to collisions. - 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)) { - Codec codec = reference_codec; - used_pltypes->FindAndSetIdUsed(&codec); - offered_codecs.push_back(codec); - } - } - - // 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)) { - Codec rtx_codec = reference_codec; - const Codec* associated_codec = - GetAssociatedCodecForRtx(reference_codecs, rtx_codec); - if (!associated_codec) { - continue; - } - // 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( - reference_codecs, offered_codecs, *associated_codec); - if (!matching_codec) { - RTC_LOG(LS_WARNING) - << "Couldn't find matching " << associated_codec->name << " codec."; - continue; - } - - rtx_codec.params[kCodecParamAssociatedPayloadType] = - rtc::ToString(matching_codec->id); - used_pltypes->FindAndSetIdUsed(&rtx_codec); - offered_codecs.push_back(rtx_codec); - } else if (reference_codec.GetResiliencyType() == - Codec::ResiliencyType::kRed && - !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( - reference_codecs, offered_codecs, *associated_codec); - if (!matching_codec) { - RTC_LOG(LS_WARNING) << "Couldn't find matching " - << associated_codec->name << " codec."; - continue; - } - - red_codec.params[kCodecParamNotInNameValueFormat] = - rtc::ToString(matching_codec->id) + "/" + - rtc::ToString(matching_codec->id); - } - used_pltypes->FindAndSetIdUsed(&red_codec); - offered_codecs.push_back(red_codec); - } - } - offered_codecs.CheckConsistency(); -} - -// `codecs` is a full list of codecs with correct payload type mappings, which -// don't conflict with mappings of the other media type; `supported_codecs` is -// a list filtered for the media section`s direction but with default payload -// types. -CodecList MatchCodecPreference( - const std::vector& codec_preferences, - const CodecList& codecs, - const CodecList& supported_codecs) { - CodecList filtered_codecs; - bool want_rtx = false; - bool want_red = false; - - for (const auto& codec_preference : codec_preferences) { - if (IsRtxCodec(codec_preference)) { - want_rtx = true; - } else if (IsRedCodec(codec_preference)) { - want_red = true; - } - } - bool red_was_added = false; - for (const auto& codec_preference : codec_preferences) { - auto found_codec = absl::c_find_if( - supported_codecs, [&codec_preference](const Codec& codec) { - // We should not filter out the codec in |codec_preferences| if it - // has a higher level than the codec in |supported_codecs|, as the - // codec in |supported_codecs| may be only with lower level in - // |send_codecs_| and |recv_codecs_| for the same codec. - return IsSameRtpCodecIgnoringLevel(codec, codec_preference); - }); - - if (found_codec != supported_codecs.end()) { - std::optional found_codec_with_correct_pt = - 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. - bool is_red_codec = found_codec_with_correct_pt->GetResiliencyType() == - Codec::ResiliencyType::kRed; - if (!is_red_codec || !red_was_added) { - filtered_codecs.push_back(*found_codec_with_correct_pt); - red_was_added = is_red_codec ? true : red_was_added; - } - std::string id = rtc::ToString(found_codec_with_correct_pt->id); - // Search for the matching rtx or red codec. - if (want_red || want_rtx) { - for (const auto& codec : codecs) { - if (want_rtx && - codec.GetResiliencyType() == Codec::ResiliencyType::kRtx) { - const auto apt = - codec.params.find(cricket::kCodecParamAssociatedPayloadType); - if (apt != codec.params.end() && apt->second == id) { - filtered_codecs.push_back(codec); - break; - } - } else if (want_red && codec.GetResiliencyType() == - Codec::ResiliencyType::kRed) { - // For RED, do not insert the codec again if it was already - // inserted. audio/red for opus gets enabled by having RED before - // the primary codec. - const auto fmtp = - codec.params.find(cricket::kCodecParamNotInNameValueFormat); - if (fmtp != codec.params.end()) { - std::vector redundant_payloads = - rtc::split(fmtp->second, '/'); - if (!redundant_payloads.empty() && - redundant_payloads[0] == id) { - if (!red_was_added) { - filtered_codecs.push_back(codec); - red_was_added = true; - } - break; - } - } - } - } - } - } - } - } - - return filtered_codecs; -} - -// Compute the union of `codecs1` and `codecs2`. -CodecList ComputeCodecsUnion(const CodecList codecs1, const CodecList codecs2) { - CodecList all_codecs; - UsedPayloadTypes used_payload_types; - for (const Codec& codec : codecs1) { - Codec codec_mutable = codec; - used_payload_types.FindAndSetIdUsed(&codec_mutable); - all_codecs.push_back(codec_mutable); - } - - // Use MergeCodecs to merge the second half of our list as it already checks - // and fixes problems with duplicate payload types. - MergeCodecs(codecs2, all_codecs, &used_payload_types); - - return all_codecs; -} - // Adds all extensions from `reference_extensions` to `offered_extensions` that // don't already exist in `offered_extensions` and ensures the IDs don't // collide. If an extension is added, it's also added to @@ -1075,247 +722,9 @@ const TransportDescription* GetTransportDescription( return desc; } -// For offer, negotiated codec must have the same level-id as that in -// |supported_codecs| with same profile. -void NegotiateVideoCodecLevelsForOffer( - const MediaDescriptionOptions& media_description_options, - const CodecList& supported_codecs, - CodecList& filtered_codecs) { - if (filtered_codecs.empty() || supported_codecs.empty()) { - return; - } - - // TODO(http://crbugs.com/376306259): We should handle level-idx for AV1. - // Ideally this should be done for all codecs, but RFCs of other codecs - // do not clear define the expected behavior for the level in the offer. -#ifdef RTC_ENABLE_H265 - if (media_description_options.type == MEDIA_TYPE_VIDEO) { - std::unordered_map - supported_h265_profiles; - // The assumption here is that H.265 codecs with the same profile and tier - // are already with highest level for that profile in both - // |supported_codecs| and |filtered_codecs|. - for (const Codec& supported_codec : supported_codecs) { - if (absl::EqualsIgnoreCase(supported_codec.name, kH265CodecName)) { - std::optional supported_ptl = - webrtc::ParseSdpForH265ProfileTierLevel(supported_codec.params); - if (supported_ptl.has_value()) { - supported_h265_profiles[supported_ptl->profile] = - supported_ptl->level; - } - } - } - - if (supported_h265_profiles.empty()) { - return; - } - - for (auto& filtered_codec : filtered_codecs) { - if (absl::EqualsIgnoreCase(filtered_codec.name, kH265CodecName)) { - std::optional filtered_ptl = - webrtc::ParseSdpForH265ProfileTierLevel(filtered_codec.params); - if (filtered_ptl.has_value()) { - auto it = supported_h265_profiles.find(filtered_ptl->profile); - - if (it != supported_h265_profiles.end() && - filtered_ptl->level != it->second) { - filtered_codec.params[kH265FmtpLevelId] = - webrtc::H265LevelToString(it->second); - } - } - } - } - } -#endif -} - -webrtc::RTCErrorOr> GetNegotiatedCodecsForOffer( - const MediaDescriptionOptions& media_description_options, - const MediaSessionOptions& session_options, - const ContentInfo* current_content, - const CodecList& codecs, - const CodecList& supported_codecs) { - CodecList filtered_codecs; - if (!media_description_options.codec_preferences.empty()) { - // Add the codecs from the current transceiver's codec preferences. - // They override any existing codecs from previous negotiations. - filtered_codecs = MatchCodecPreference( - media_description_options.codec_preferences, codecs, supported_codecs); - } else { - // Add the codecs from current content if it exists and is not rejected nor - // recycled. - if (current_content && !current_content->rejected && - current_content->mid() == media_description_options.mid) { - if (!IsMediaContentOfType(current_content, - media_description_options.type)) { - // Can happen if the remote side re-uses a MID while recycling. - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Media type for content with mid='" + - current_content->mid() + - "' does not match previous type."); - } - const MediaContentDescription* mcd = current_content->media_description(); - for (const Codec& codec : mcd->codecs()) { - if (webrtc::FindMatchingCodec(mcd->codecs(), codecs.codecs(), codec)) { - filtered_codecs.push_back(codec); - } - } - } - // 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)) { - // 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. - if (media_description_options.type == MEDIA_TYPE_VIDEO && - found_codec->GetResiliencyType() == Codec::ResiliencyType::kRtx) { - // For RTX we might need to adjust the apt parameter if we got a - // remote offer without RTX for a codec for which we support RTX. - auto referenced_codec = - GetAssociatedCodecForRtx(supported_codecs, codec); - 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); - if (changed_referenced_codec) { - found_codec->SetParam(kCodecParamAssociatedPayloadType, - changed_referenced_codec->id); - } - } - filtered_codecs.push_back(*found_codec); - } - } - } - - if (media_description_options.type == MEDIA_TYPE_AUDIO && - !session_options.vad_enabled) { - // If application doesn't want CN codecs in offer. - StripCNCodecs(filtered_codecs); - } else if (media_description_options.type == MEDIA_TYPE_VIDEO && - session_options.raw_packetization_for_video) { - for (Codec& codec : filtered_codecs) { - if (codec.IsMediaCodec()) { - codec.packetization = kPacketizationParamRaw; - } - } - } - NegotiateVideoCodecLevelsForOffer(media_description_options, supported_codecs, - filtered_codecs); - return filtered_codecs.codecs(); -} - -webrtc::RTCErrorOr GetNegotiatedCodecsForAnswer( - const MediaDescriptionOptions& media_description_options, - const MediaSessionOptions& session_options, - const ContentInfo* current_content, - const CodecList& codecs, - const CodecList& supported_codecs) { - CodecList filtered_codecs; - - if (!media_description_options.codec_preferences.empty()) { - filtered_codecs = MatchCodecPreference( - media_description_options.codec_preferences, codecs, supported_codecs); - } else { - // Add the codecs from current content if it exists and is not rejected nor - // recycled. - if (current_content && !current_content->rejected && - current_content->mid() == media_description_options.mid) { - if (!IsMediaContentOfType(current_content, - media_description_options.type)) { - // Can happen if the remote side re-uses a MID while recycling. - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Media type for content with mid='" + - current_content->mid() + - "' does not match previous type."); - } - const MediaContentDescription* mcd = current_content->media_description(); - for (const Codec& codec : mcd->codecs()) { - if (webrtc::FindMatchingCodec(mcd->codecs(), codecs.codecs(), codec)) { - filtered_codecs.push_back(codec); - } - } - } - // Add other supported codecs. - CodecList other_codecs; - for (const Codec& codec : supported_codecs) { - if (FindMatchingCodec(supported_codecs, codecs, codec) && - !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); - } - } - - // Use ComputeCodecsUnion to avoid having duplicate payload IDs. - // This is a no-op for audio until RTX is added. - filtered_codecs = ComputeCodecsUnion(filtered_codecs, other_codecs); - } - - if (media_description_options.type == MEDIA_TYPE_AUDIO && - !session_options.vad_enabled) { - // If application doesn't want CN codecs in offer. - StripCNCodecs(filtered_codecs); - } else if (media_description_options.type == MEDIA_TYPE_VIDEO && - session_options.raw_packetization_for_video) { - for (Codec& codec : filtered_codecs) { - if (codec.IsMediaCodec()) { - codec.packetization = kPacketizationParamRaw; - } - } - } - return filtered_codecs.codecs(); -} } // namespace -void MediaDescriptionOptions::AddAudioSender( - const std::string& track_id, - const std::vector& stream_ids) { - RTC_DCHECK(type == MEDIA_TYPE_AUDIO); - AddSenderInternal(track_id, stream_ids, {}, SimulcastLayerList(), 1); -} - -void MediaDescriptionOptions::AddVideoSender( - const std::string& track_id, - const std::vector& stream_ids, - const std::vector& rids, - const SimulcastLayerList& simulcast_layers, - int num_sim_layers) { - RTC_DCHECK(type == MEDIA_TYPE_VIDEO); - RTC_DCHECK(rids.empty() || num_sim_layers == 0) - << "RIDs are the compliant way to indicate simulcast."; - RTC_DCHECK(ValidateSimulcastLayers(rids, simulcast_layers)); - AddSenderInternal(track_id, stream_ids, rids, simulcast_layers, - num_sim_layers); -} - -void MediaDescriptionOptions::AddSenderInternal( - const std::string& track_id, - const std::vector& stream_ids, - const std::vector& rids, - const SimulcastLayerList& simulcast_layers, - int num_sim_layers) { - // TODO(steveanton): Support any number of stream ids. - RTC_CHECK(stream_ids.size() == 1U); - SenderOptions options; - options.track_id = track_id; - options.stream_ids = stream_ids; - options.simulcast_layers = simulcast_layers; - options.rids = rids; - options.num_sim_layers = num_sim_layers; - sender_options.push_back(options); -} - -bool MediaSessionOptions::HasMediaDescription(MediaType type) const { - return absl::c_any_of( - media_description_options, - [type](const MediaDescriptionOptions& t) { return t.type == type; }); -} - MediaSessionDescriptionFactory::MediaSessionDescriptionFactory( cricket::MediaEngineInterface* media_engine, bool rtx_enabled, @@ -1329,56 +738,7 @@ MediaSessionDescriptionFactory::MediaSessionDescriptionFactory( transport_desc_factory_->trials().IsEnabled( "WebRTC-PayloadTypesInTransport")) { RTC_CHECK(transport_desc_factory_); - if (media_engine) { - audio_send_codecs_ = CodecList(media_engine->voice().send_codecs()); - audio_recv_codecs_ = CodecList(media_engine->voice().recv_codecs()); - video_send_codecs_ = - CodecList(media_engine->video().send_codecs(rtx_enabled)); - video_recv_codecs_ = - CodecList(media_engine->video().recv_codecs(rtx_enabled)); - } - ComputeAudioCodecsIntersectionAndUnion(); - ComputeVideoCodecsIntersectionAndUnion(); -} - -const CodecList& MediaSessionDescriptionFactory::audio_sendrecv_codecs() const { - return audio_sendrecv_codecs_; -} - -const CodecList& MediaSessionDescriptionFactory::audio_send_codecs() const { - return audio_send_codecs_; -} - -const CodecList& MediaSessionDescriptionFactory::audio_recv_codecs() const { - return audio_recv_codecs_; -} - -void MediaSessionDescriptionFactory::set_audio_codecs( - const CodecList& send_codecs, - const CodecList& recv_codecs) { - audio_send_codecs_ = send_codecs; - audio_recv_codecs_ = recv_codecs; - ComputeAudioCodecsIntersectionAndUnion(); -} - -const CodecList& MediaSessionDescriptionFactory::video_sendrecv_codecs() const { - return video_sendrecv_codecs_; -} - -const CodecList& MediaSessionDescriptionFactory::video_send_codecs() const { - return video_send_codecs_; -} - -const CodecList& MediaSessionDescriptionFactory::video_recv_codecs() const { - return video_recv_codecs_; -} - -void MediaSessionDescriptionFactory::set_video_codecs( - const CodecList& send_codecs, - const CodecList& recv_codecs) { - video_send_codecs_ = send_codecs; - video_recv_codecs_ = recv_codecs; - ComputeVideoCodecsIntersectionAndUnion(); + codec_vendor_ = std::make_unique(media_engine, rtx_enabled); } RtpHeaderExtensions @@ -1423,8 +783,10 @@ MediaSessionDescriptionFactory::CreateOfferOrError( Codecs offer_audio_codecs; Codecs offer_video_codecs; - GetCodecsForOffer(current_active_contents, &offer_audio_codecs, - &offer_video_codecs); + if (codec_vendor_) { + codec_vendor_->GetCodecsForOffer(current_active_contents, + &offer_audio_codecs, &offer_video_codecs); + } AudioVideoRtpHeaderExtensions extensions_with_ids = GetOfferedRtpHeaderExtensionsWithIds( @@ -1573,8 +935,11 @@ MediaSessionDescriptionFactory::CreateAnswerOrError( // sections. Codecs answer_audio_codecs; Codecs answer_video_codecs; - GetCodecsForAnswer(current_active_contents, *offer, &answer_audio_codecs, - &answer_video_codecs); + if (codec_vendor_) { + codec_vendor_->GetCodecsForAnswer(current_active_contents, *offer, + &answer_audio_codecs, + &answer_video_codecs); + } auto answer = std::make_unique(); @@ -1747,173 +1112,6 @@ MediaSessionDescriptionFactory::CreateAnswerOrError( return answer; } -const CodecList& MediaSessionDescriptionFactory::GetAudioCodecsForOffer( - const RtpTransceiverDirection& direction) const { - switch (direction) { - // If stream is inactive - generate list as if sendrecv. - case RtpTransceiverDirection::kSendRecv: - case RtpTransceiverDirection::kStopped: - case RtpTransceiverDirection::kInactive: - return audio_sendrecv_codecs_; - case RtpTransceiverDirection::kSendOnly: - return audio_send_codecs_; - case RtpTransceiverDirection::kRecvOnly: - return audio_recv_codecs_; - } - RTC_CHECK_NOTREACHED(); -} - -const CodecList& MediaSessionDescriptionFactory::GetAudioCodecsForAnswer( - const RtpTransceiverDirection& offer, - const RtpTransceiverDirection& answer) const { - switch (answer) { - // For inactive and sendrecv answers, generate lists as if we were to accept - // the offer's direction. See RFC 3264 Section 6.1. - case RtpTransceiverDirection::kSendRecv: - case RtpTransceiverDirection::kStopped: - case RtpTransceiverDirection::kInactive: - return GetAudioCodecsForOffer( - webrtc::RtpTransceiverDirectionReversed(offer)); - case RtpTransceiverDirection::kSendOnly: - return audio_send_codecs_; - case RtpTransceiverDirection::kRecvOnly: - return audio_recv_codecs_; - } - RTC_CHECK_NOTREACHED(); -} - -const CodecList& MediaSessionDescriptionFactory::GetVideoCodecsForOffer( - const RtpTransceiverDirection& direction) const { - switch (direction) { - // If stream is inactive - generate list as if sendrecv. - case RtpTransceiverDirection::kSendRecv: - case RtpTransceiverDirection::kStopped: - case RtpTransceiverDirection::kInactive: - return video_sendrecv_codecs_; - case RtpTransceiverDirection::kSendOnly: - return video_send_codecs_; - case RtpTransceiverDirection::kRecvOnly: - return video_recv_codecs_; - } - RTC_CHECK_NOTREACHED(); -} - -const CodecList& MediaSessionDescriptionFactory::GetVideoCodecsForAnswer( - const RtpTransceiverDirection& offer, - const RtpTransceiverDirection& answer) const { - switch (answer) { - // For inactive and sendrecv answers, generate lists as if we were to accept - // the offer's direction. See RFC 3264 Section 6.1. - case RtpTransceiverDirection::kSendRecv: - case RtpTransceiverDirection::kStopped: - case RtpTransceiverDirection::kInactive: - return GetVideoCodecsForOffer( - webrtc::RtpTransceiverDirectionReversed(offer)); - case RtpTransceiverDirection::kSendOnly: - return video_send_codecs_; - case RtpTransceiverDirection::kRecvOnly: - return video_recv_codecs_; - } - RTC_CHECK_NOTREACHED(); -} - -void MergeCodecsFromDescription( - const std::vector& current_active_contents, - CodecList& audio_codecs, - CodecList& video_codecs, - UsedPayloadTypes* used_pltypes) { - for (const ContentInfo* content : current_active_contents) { - if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) { - MergeCodecs(CodecList{content->media_description()->codecs()}, - audio_codecs, used_pltypes); - } else if (IsMediaContentOfType(content, MEDIA_TYPE_VIDEO)) { - MergeCodecs(CodecList{content->media_description()->codecs()}, - video_codecs, used_pltypes); - } - } -} - -// Getting codecs for an offer involves these steps: -// -// 1. Construct payload type -> codec mappings for current description. -// 2. Add any reference codecs that weren't already present -// 3. For each individual media description (m= section), filter codecs based -// on the directional attribute (happens in another method). -void MediaSessionDescriptionFactory::GetCodecsForOffer( - const std::vector& current_active_contents, - Codecs* audio_codecs, - Codecs* video_codecs) const { - // First - get all codecs from the current description if the media type - // is used. Add them to `used_pltypes` so the payload type is not reused if a - // new media type is added. - UsedPayloadTypes used_pltypes; - CodecList video_codec_list(*video_codecs); - CodecList audio_codec_list(*audio_codecs); - MergeCodecsFromDescription(current_active_contents, audio_codec_list, - video_codec_list, &used_pltypes); - - // Add our codecs that are not in the current description. - MergeCodecs(CodecList{all_audio_codecs_}, audio_codec_list, &used_pltypes); - MergeCodecs(CodecList{all_video_codecs_}, video_codec_list, &used_pltypes); - *audio_codecs = audio_codec_list.codecs(); - *video_codecs = video_codec_list.codecs(); -} - -// Getting codecs for an answer involves these steps: -// -// 1. Construct payload type -> codec mappings for current description. -// 2. Add any codecs from the offer that weren't already present. -// 3. Add any remaining codecs that weren't already present. -// 4. For each individual media description (m= section), filter codecs based -// on the directional attribute (happens in another method). -void MediaSessionDescriptionFactory::GetCodecsForAnswer( - const std::vector& current_active_contents, - const SessionDescription& remote_offer, - Codecs* audio_codecs, - Codecs* video_codecs) const { - // First - get all codecs from the current description if the media type - // is used. Add them to `used_pltypes` so the payload type is not reused if a - // new media type is added. - UsedPayloadTypes used_pltypes; - CodecList video_codec_list(*video_codecs); - CodecList audio_codec_list(*audio_codecs); - MergeCodecsFromDescription(current_active_contents, audio_codec_list, - video_codec_list, &used_pltypes); - - // Second - filter out codecs that we don't support at all and should ignore. - CodecList filtered_offered_audio_codecs; - CodecList filtered_offered_video_codecs; - for (const ContentInfo& content : remote_offer.contents()) { - if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) { - CodecList 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)) { - filtered_offered_audio_codecs.push_back(offered_audio_codec); - } - } - } else if (IsMediaContentOfType(&content, MEDIA_TYPE_VIDEO)) { - CodecList 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)) { - filtered_offered_video_codecs.push_back(offered_video_codec); - } - } - } - } - - // Add codecs that are not in the current description but were in - // `remote_offer`. - MergeCodecs(filtered_offered_audio_codecs, audio_codec_list, &used_pltypes); - MergeCodecs(filtered_offered_video_codecs, video_codec_list, &used_pltypes); - *audio_codecs = audio_codec_list.codecs(); - *video_codecs = video_codec_list.codecs(); -} MediaSessionDescriptionFactory::AudioVideoRtpHeaderExtensions MediaSessionDescriptionFactory::GetOfferedRtpHeaderExtensionsWithIds( @@ -2048,14 +1246,10 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForOffer( std::vector codecs_to_include; if (media_description_options.codecs_to_include.empty()) { - CodecList supported_codecs = - media_description_options.type == MEDIA_TYPE_AUDIO - ? GetAudioCodecsForOffer(media_description_options.direction) - : GetVideoCodecsForOffer(media_description_options.direction); webrtc::RTCErrorOr> error_or_filtered_codecs = - GetNegotiatedCodecsForOffer(media_description_options, session_options, - current_content, CodecList(codecs), - CodecList(supported_codecs)); + codec_vendor_->GetNegotiatedCodecsForOffer( + media_description_options, session_options, current_content, + CodecList(codecs)); if (!error_or_filtered_codecs.ok()) { return error_or_filtered_codecs.MoveError(); } @@ -2218,14 +1412,10 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForAnswer( std::vector codecs_to_include; bool negotiate; if (media_description_options.codecs_to_include.empty()) { - const CodecList& supported_codecs = - media_description_options.type == MEDIA_TYPE_AUDIO - ? GetAudioCodecsForAnswer(offer_rtd, answer_rtd) - : GetVideoCodecsForAnswer(offer_rtd, answer_rtd); webrtc::RTCErrorOr> error_or_filtered_codecs = - GetNegotiatedCodecsForAnswer(media_description_options, session_options, - current_content, CodecList(codecs), - CodecList(supported_codecs)); + codec_vendor_->GetNegotiatedCodecsForAnswer( + media_description_options, session_options, offer_rtd, answer_rtd, + current_content, CodecList(codecs)); if (!error_or_filtered_codecs.ok()) { return error_or_filtered_codecs.MoveError(); } @@ -2252,10 +1442,10 @@ RTCError MediaSessionDescriptionFactory::AddRtpContentForAnswer( } if (negotiate) { std::vector negotiated_codecs; - NegotiateCodecs(CodecList(codecs_to_include), - CodecList(offer_content_description->codecs()), - &negotiated_codecs, - media_description_options.codec_preferences.empty()); + CodecVendor::NegotiateCodecs( + CodecList(codecs_to_include), + CodecList(offer_content_description->codecs()), &negotiated_codecs, + media_description_options.codec_preferences.empty()); codecs_to_include = negotiated_codecs; } AssignCodecIdsAndLinkRed(pt_suggester_, media_description_options.mid, @@ -2425,55 +1615,6 @@ RTCError MediaSessionDescriptionFactory::AddUnsupportedContentForAnswer( return RTCError::OK(); } -void MediaSessionDescriptionFactory::ComputeAudioCodecsIntersectionAndUnion() { - audio_sendrecv_codecs_.clear(); - all_audio_codecs_.clear(); - // 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)) { - // 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)) { - all_audio_codecs_.push_back(recv); - } - } - // Use NegotiateCodecs to merge our codec lists, since the operation is - // essentially the same. Put send_codecs as the offered_codecs, which is the - // order we'd like to follow. The reasoning is that encoding is usually more - // expensive than decoding, and prioritizing a codec in the send list probably - // means it's a codec we can handle efficiently. - std::vector audio_sendrecv_codecs_vector; - NegotiateCodecs(audio_recv_codecs_, audio_send_codecs_, - &audio_sendrecv_codecs_vector, true); - audio_sendrecv_codecs_ = CodecList(audio_sendrecv_codecs_vector); -} - -void MediaSessionDescriptionFactory::ComputeVideoCodecsIntersectionAndUnion() { - video_sendrecv_codecs_.clear(); - - // Use ComputeCodecsUnion to avoid having duplicate payload IDs - all_video_codecs_ = ComputeCodecsUnion(CodecList(video_recv_codecs_), - CodecList(video_send_codecs_)); - - // Use NegotiateCodecs to merge our codec lists, since the operation is - // essentially the same. Put send_codecs as the offered_codecs, which is the - // order we'd like to follow. The reasoning is that encoding is usually more - // expensive than decoding, and prioritizing a codec in the send list probably - // means it's a codec we can handle efficiently. - // Also for the same profile of a codec, if there are different levels in the - // send and receive codecs, |video_sendrecv_codecs_| will contain the lower - // level of the two for that profile. - std::vector video_sendrecv_codecs_vector; - NegotiateCodecs(video_recv_codecs_, video_send_codecs_, - &video_sendrecv_codecs_vector, true); - video_sendrecv_codecs_ = CodecList(video_sendrecv_codecs_vector); -} - bool IsMediaContent(const ContentInfo* content) { return (content && (content->type == MediaProtocolType::kRtp || content->type == MediaProtocolType::kSctp)); diff --git a/pc/media_session.h b/pc/media_session.h index ea89acb87d..5d29a969bc 100644 --- a/pc/media_session.h +++ b/pc/media_session.h @@ -17,22 +17,18 @@ #include #include -#include "api/crypto/crypto_options.h" #include "api/media_types.h" #include "api/rtc_error.h" -#include "api/rtp_parameters.h" -#include "api/rtp_transceiver_direction.h" #include "call/payload_type.h" #include "media/base/codec.h" -#include "media/base/codec_list.h" -#include "media/base/rid_description.h" #include "media/base/stream_params.h" #include "p2p/base/ice_credentials_iterator.h" #include "p2p/base/transport_description.h" #include "p2p/base/transport_description_factory.h" #include "p2p/base/transport_info.h" +#include "pc/codec_vendor.h" +#include "pc/media_options.h" #include "pc/session_description.h" -#include "pc/simulcast_description.h" #include "rtc_base/memory/always_valid_pointer.h" #include "rtc_base/unique_id_generator.h" @@ -47,94 +43,6 @@ namespace cricket { class MediaEngineInterface; -// Default RTCP CNAME for unit tests. -const char kDefaultRtcpCname[] = "DefaultRtcpCname"; - -// Options for an RtpSender contained with an media description/"m=" section. -// Note: Spec-compliant Simulcast and legacy simulcast are mutually exclusive. -struct SenderOptions { - std::string track_id; - std::vector stream_ids; - // Use RIDs and Simulcast Layers to indicate spec-compliant Simulcast. - std::vector rids; - SimulcastLayerList simulcast_layers; - // Use `num_sim_layers` to indicate legacy simulcast. - int num_sim_layers; -}; - -// Options for an individual media description/"m=" section. -struct MediaDescriptionOptions { - MediaDescriptionOptions(MediaType type, - const std::string& mid, - webrtc::RtpTransceiverDirection direction, - bool stopped) - : type(type), mid(mid), direction(direction), stopped(stopped) {} - - // TODO(deadbeef): When we don't support Plan B, there will only be one - // sender per media description and this can be simplified. - void AddAudioSender(const std::string& track_id, - const std::vector& stream_ids); - void AddVideoSender(const std::string& track_id, - const std::vector& stream_ids, - const std::vector& rids, - const SimulcastLayerList& simulcast_layers, - int num_sim_layers); - - MediaType type; - std::string mid; - webrtc::RtpTransceiverDirection direction; - bool stopped; - TransportOptions transport_options; - // Note: There's no equivalent "RtpReceiverOptions" because only send - // stream information goes in the local descriptions. - std::vector sender_options; - std::vector codec_preferences; - std::vector header_extensions; - // Codecs to include in a generated offer or answer. - // If this is used, session-level codec lists MUST be ignored. - std::vector codecs_to_include; - - private: - // Doesn't DCHECK on `type`. - void AddSenderInternal(const std::string& track_id, - const std::vector& stream_ids, - const std::vector& rids, - const SimulcastLayerList& simulcast_layers, - int num_sim_layers); -}; - -// Provides a mechanism for describing how m= sections should be generated. -// The m= section with index X will use media_description_options[X]. There -// must be an option for each existing section if creating an answer, or a -// subsequent offer. -struct MediaSessionOptions { - MediaSessionOptions() {} - - bool has_audio() const { return HasMediaDescription(MEDIA_TYPE_AUDIO); } - bool has_video() const { return HasMediaDescription(MEDIA_TYPE_VIDEO); } - bool has_data() const { return HasMediaDescription(MEDIA_TYPE_DATA); } - - bool HasMediaDescription(MediaType type) const; - - bool vad_enabled = true; // When disabled, removes all CN codecs from SDP. - bool rtcp_mux_enabled = true; - bool bundle_enabled = false; - bool offer_extmap_allow_mixed = false; - bool raw_packetization_for_video = false; - std::string rtcp_cname = kDefaultRtcpCname; - webrtc::CryptoOptions crypto_options; - // List of media description options in the same order that the media - // descriptions will be generated. - std::vector media_description_options; - std::vector pooled_ice_credentials; - - // Use the draft-ietf-mmusic-sctp-sdp-03 obsolete syntax for SCTP - // datachannels. - // Default is true for backwards compatibility with clients that use - // this internal interface. - bool use_obsolete_sctp_sdp = true; -}; - // Creates media session descriptions according to the supplied codecs and // other fields, as well as the supplied per-call options. // When creating answers, performs the appropriate negotiation @@ -152,24 +60,6 @@ class MediaSessionDescriptionFactory { const TransportDescriptionFactory* factory, webrtc::PayloadTypeSuggester* pt_suggester); - const CodecList& audio_sendrecv_codecs() const; - const CodecList& audio_send_codecs() const; - const CodecList& audio_recv_codecs() const; - void set_audio_codecs(const CodecList& send_codecs, - const CodecList& recv_codecs); - void set_audio_codecs(const std::vector& send_codecs, - const std::vector& recv_codecs) { - set_audio_codecs(CodecList(send_codecs), CodecList(recv_codecs)); - } - const CodecList& video_sendrecv_codecs() const; - const CodecList& video_send_codecs() const; - const CodecList& video_recv_codecs() const; - void set_video_codecs(const CodecList& send_codecs, - const CodecList& recv_codecs); - void set_video_codecs(const std::vector& send_codecs, - const std::vector& recv_codecs) { - set_video_codecs(CodecList(send_codecs), CodecList(recv_codecs)); - } RtpHeaderExtensions filtered_rtp_header_extensions( RtpHeaderExtensions extensions) const; @@ -189,31 +79,14 @@ class MediaSessionDescriptionFactory { const MediaSessionOptions& options, const SessionDescription* current_description) const; + CodecVendor* CodecVendorForTesting() { return codec_vendor_.get(); } + private: struct AudioVideoRtpHeaderExtensions { RtpHeaderExtensions audio; RtpHeaderExtensions video; }; - const CodecList& GetAudioCodecsForOffer( - const webrtc::RtpTransceiverDirection& direction) const; - const CodecList& GetAudioCodecsForAnswer( - const webrtc::RtpTransceiverDirection& offer, - const webrtc::RtpTransceiverDirection& answer) const; - const CodecList& GetVideoCodecsForOffer( - const webrtc::RtpTransceiverDirection& direction) const; - const CodecList& GetVideoCodecsForAnswer( - const webrtc::RtpTransceiverDirection& offer, - const webrtc::RtpTransceiverDirection& answer) const; - void GetCodecsForOffer( - const std::vector& current_active_contents, - Codecs* audio_codecs, - Codecs* video_codecs) const; - void GetCodecsForAnswer( - const std::vector& current_active_contents, - const SessionDescription& remote_offer, - Codecs* audio_codecs, - Codecs* video_codecs) const; AudioVideoRtpHeaderExtensions GetOfferedRtpHeaderExtensionsWithIds( const std::vector& current_active_contents, bool extmap_allow_mixed, @@ -305,27 +178,11 @@ class MediaSessionDescriptionFactory { SessionDescription* answer, IceCredentialsIterator* ice_credentials) const; - void ComputeAudioCodecsIntersectionAndUnion(); - - void ComputeVideoCodecsIntersectionAndUnion(); - rtc::UniqueRandomIdGenerator* ssrc_generator() const { return ssrc_generator_.get(); } bool is_unified_plan_ = false; - CodecList audio_send_codecs_; - CodecList audio_recv_codecs_; - // Intersection of send and recv. - CodecList audio_sendrecv_codecs_; - // Union of send and recv. - CodecList all_audio_codecs_; - CodecList video_send_codecs_; - CodecList video_recv_codecs_; - // Intersection of send and recv. - CodecList video_sendrecv_codecs_; - // Union of send and recv. - CodecList all_video_codecs_; // This object may or may not be owned by this class. webrtc::AlwaysValidPointer const ssrc_generator_; @@ -334,6 +191,7 @@ class MediaSessionDescriptionFactory { // Payoad type tracker interface. Must live longer than this object. webrtc::PayloadTypeSuggester* pt_suggester_; bool payload_types_in_transport_trial_enabled_; + std::unique_ptr codec_vendor_; }; // Convenience functions. diff --git a/pc/media_session_unittest.cc b/pc/media_session_unittest.cc index 5ea64260c4..7c8f012a91 100644 --- a/pc/media_session_unittest.cc +++ b/pc/media_session_unittest.cc @@ -29,8 +29,10 @@ #include "api/media_types.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" +#include "api/video_codecs/sdp_video_format.h" #include "call/fake_payload_type_suggester.h" #include "media/base/codec.h" +#include "media/base/codec_list.h" #include "media/base/media_constants.h" #include "media/base/rid_description.h" #include "media/base/stream_params.h" @@ -40,6 +42,7 @@ #include "p2p/base/transport_description.h" #include "p2p/base/transport_description_factory.h" #include "p2p/base/transport_info.h" +#include "pc/media_options.h" #include "pc/media_protocol_names.h" #include "pc/rtp_media_utils.h" #include "pc/rtp_parameters_conversion.h" @@ -509,14 +512,14 @@ class MediaSessionDescriptionFactoryTest : public testing::Test { tdf2_(field_trials), f1_(nullptr, false, &ssrc_generator1, &tdf1_, &pt_suggester_1_), f2_(nullptr, false, &ssrc_generator2, &tdf2_, &pt_suggester_2_) { - f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1), - MAKE_VECTOR(kAudioCodecs1)); - f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1), - MAKE_VECTOR(kVideoCodecs1)); - f2_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs2), - MAKE_VECTOR(kAudioCodecs2)); - f2_.set_video_codecs(MAKE_VECTOR(kVideoCodecs2), - MAKE_VECTOR(kVideoCodecs2)); + f1_.CodecVendorForTesting()->set_audio_codecs(MAKE_VECTOR(kAudioCodecs1), + MAKE_VECTOR(kAudioCodecs1)); + f1_.CodecVendorForTesting()->set_video_codecs(MAKE_VECTOR(kVideoCodecs1), + MAKE_VECTOR(kVideoCodecs1)); + f2_.CodecVendorForTesting()->set_audio_codecs(MAKE_VECTOR(kAudioCodecs2), + MAKE_VECTOR(kAudioCodecs2)); + f2_.CodecVendorForTesting()->set_video_codecs(MAKE_VECTOR(kVideoCodecs2), + MAKE_VECTOR(kVideoCodecs2)); tdf1_.set_certificate(rtc::RTCCertificate::Create( std::unique_ptr(new rtc::FakeSSLIdentity("id1")))); tdf2_.set_certificate(rtc::RTCCertificate::Create( @@ -775,7 +778,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOffer) { EXPECT_EQ(MediaProtocolType::kRtp, ac->type); const MediaContentDescription* acd = ac->media_description(); EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); - EXPECT_THAT(f1_.audio_sendrecv_codecs(), ElementsAreArray(acd->codecs())); + EXPECT_THAT(f1_.CodecVendorForTesting()->audio_sendrecv_codecs(), + ElementsAreArray(acd->codecs())); EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached. EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto) EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on @@ -787,10 +791,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOfferWithJustOpusAndRed) { // First, prefer to only use opus and red. std::vector preferences; - preferences.push_back( - webrtc::ToRtpCodecCapability(f1_.audio_sendrecv_codecs()[0])); - preferences.push_back( - webrtc::ToRtpCodecCapability(f1_.audio_sendrecv_codecs()[1])); + preferences.push_back(webrtc::ToRtpCodecCapability( + f1_.CodecVendorForTesting()->audio_sendrecv_codecs()[0])); + preferences.push_back(webrtc::ToRtpCodecCapability( + f1_.CodecVendorForTesting()->audio_sendrecv_codecs()[1])); EXPECT_EQ("opus", preferences[0].name); EXPECT_EQ("red", preferences[1].name); @@ -815,10 +819,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioOfferWithRedForOpus) { // First, prefer to only use opus and red. std::vector preferences; - preferences.push_back( - webrtc::ToRtpCodecCapability(f1_.audio_sendrecv_codecs()[1])); - preferences.push_back( - webrtc::ToRtpCodecCapability(f1_.audio_sendrecv_codecs()[0])); + preferences.push_back(webrtc::ToRtpCodecCapability( + f1_.CodecVendorForTesting()->audio_sendrecv_codecs()[1])); + preferences.push_back(webrtc::ToRtpCodecCapability( + f1_.CodecVendorForTesting()->audio_sendrecv_codecs()[0])); EXPECT_EQ("red", preferences[0].name); EXPECT_EQ("opus", preferences[1].name); @@ -855,13 +859,15 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoOffer) { const MediaContentDescription* acd = ac->media_description(); const MediaContentDescription* vcd = vc->media_description(); EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); - EXPECT_EQ(f1_.audio_sendrecv_codecs().codecs(), acd->codecs()); + EXPECT_EQ(f1_.CodecVendorForTesting()->audio_sendrecv_codecs().codecs(), + acd->codecs()); EXPECT_EQ(0U, acd->first_ssrc()); // no sender is attached EXPECT_EQ(kAutoBandwidth, acd->bandwidth()); // default bandwidth (auto) EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on EXPECT_EQ(kMediaProtocolDtlsSavpf, acd->protocol()); EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type()); - EXPECT_EQ(f1_.video_sendrecv_codecs().codecs(), vcd->codecs()); + EXPECT_EQ(f1_.CodecVendorForTesting()->video_sendrecv_codecs().codecs(), + vcd->codecs()); EXPECT_EQ(0U, vcd->first_ssrc()); // no sender is attached EXPECT_EQ(kAutoBandwidth, vcd->bandwidth()); // default bandwidth (auto) EXPECT_TRUE(vcd->rtcp_mux()); // rtcp-mux defaults on @@ -960,8 +966,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAnswerWithCustomCodecs) { // RTP paylod type. The test verifies that the offer don't contain the // duplicate RTP payload types. TEST_F(MediaSessionDescriptionFactoryTest, TestBundleOfferWithSameCodecPlType) { - const Codec& offered_video_codec = f2_.video_sendrecv_codecs()[0]; - const Codec& offered_audio_codec = f2_.audio_sendrecv_codecs()[0]; + const Codec& offered_video_codec = + f2_.CodecVendorForTesting()->video_sendrecv_codecs()[0]; + const Codec& offered_audio_codec = + f2_.CodecVendorForTesting()->audio_sendrecv_codecs()[0]; ASSERT_EQ(offered_video_codec.id, offered_audio_codec.id); MediaSessionOptions opts; @@ -1410,10 +1418,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, RtpTransceiverDirection::kSendRecv, kActive, &opts); std::vector f1_codecs = {CreateAudioCodec(96, "opus", 48000, 1)}; - f1_.set_audio_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_audio_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = {CreateAudioCodec(0, "PCMU", 8000, 1)}; - f2_.set_audio_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_audio_codecs(f2_codecs, f2_codecs); std::unique_ptr offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue(); @@ -1461,10 +1469,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, RtpTransceiverDirection::kSendRecv, kActive, &opts); std::vector f1_codecs = {CreateVideoCodec(96, "H264")}; - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = {CreateVideoCodec(97, "VP8")}; - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); std::unique_ptr offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue(); @@ -1485,11 +1493,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, &opts); std::vector f1_codecs = {CreateVideoCodec(96, "H264"), CreateVideoCodec(118, "flexfec-03")}; - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = {CreateVideoCodec(97, "VP8"), CreateVideoCodec(118, "flexfec-03")}; - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); std::unique_ptr offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue(); @@ -2617,7 +2625,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) { const MediaContentDescription* acd = ac->media_description(); const MediaContentDescription* vcd = vc->media_description(); EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type()); - EXPECT_EQ(f1_.audio_sendrecv_codecs().codecs(), acd->codecs()); + EXPECT_EQ(f1_.CodecVendorForTesting()->audio_sendrecv_codecs().codecs(), + acd->codecs()); const StreamParamsVec& audio_streams = acd->streams(); ASSERT_EQ(2U, audio_streams.size()); @@ -2633,7 +2642,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) { EXPECT_TRUE(acd->rtcp_mux()); // rtcp-mux defaults on EXPECT_EQ(MEDIA_TYPE_VIDEO, vcd->type()); - EXPECT_EQ(f1_.video_sendrecv_codecs().codecs(), vcd->codecs()); + EXPECT_EQ(f1_.CodecVendorForTesting()->video_sendrecv_codecs().codecs(), + vcd->codecs()); const StreamParamsVec& video_streams = vcd->streams(); ASSERT_EQ(1U, video_streams.size()); @@ -3028,8 +3038,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, // that is being recycled. TEST_F(MediaSessionDescriptionFactoryTest, ReOfferDoesNotReUseRecycledAudioCodecs) { - f1_.set_video_codecs(CodecList{}, CodecList{}); - f2_.set_video_codecs(CodecList{}, CodecList{}); + f1_.CodecVendorForTesting()->set_video_codecs(CodecList{}, CodecList{}); + f2_.CodecVendorForTesting()->set_video_codecs(CodecList{}, CodecList{}); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "a0", @@ -3062,8 +3072,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, // that is being recycled. TEST_F(MediaSessionDescriptionFactoryTest, ReOfferDoesNotReUseRecycledVideoCodecs) { - f1_.set_audio_codecs(CodecList{}, CodecList{}); - f2_.set_audio_codecs(CodecList{}, CodecList{}); + f1_.CodecVendorForTesting()->set_audio_codecs(CodecList{}, CodecList{}); + f2_.CodecVendorForTesting()->set_audio_codecs(CodecList{}, CodecList{}); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "v0", @@ -3089,8 +3099,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, // section that is being recycled. TEST_F(MediaSessionDescriptionFactoryTest, ReAnswerDoesNotReUseRecycledAudioCodecs) { - f1_.set_video_codecs(CodecList{}, CodecList{}); - f2_.set_video_codecs(CodecList{}, CodecList{}); + f1_.CodecVendorForTesting()->set_video_codecs(CodecList{}, CodecList{}); + f2_.CodecVendorForTesting()->set_video_codecs(CodecList{}, CodecList{}); // Perform initial offer/answer in reverse (`f2_` as offerer) so that the // second offer/answer is forward (`f1_` as offerer). @@ -3121,8 +3131,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, // section that is being recycled. TEST_F(MediaSessionDescriptionFactoryTest, ReAnswerDoesNotReUseRecycledVideoCodecs) { - f1_.set_audio_codecs(CodecList{}, CodecList{}); - f2_.set_audio_codecs(CodecList{}, CodecList{}); + f1_.CodecVendorForTesting()->set_audio_codecs(CodecList{}, CodecList{}); + f2_.CodecVendorForTesting()->set_audio_codecs(CodecList{}, CodecList{}); // Perform initial offer/answer in reverse (`f2_` as offerer) so that the // second offer/answer is forward (`f1_` as offerer). @@ -3161,12 +3171,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates rtx for H264 with the payload type `f1_` uses. AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = MAKE_VECTOR(kVideoCodecs2); // This creates rtx for H264 with the payload type `f2_` uses. AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[0].id), &f2_codecs); - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); std::unique_ptr offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue(); @@ -3223,11 +3233,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, // *doesn't* honor the existing preferred codec (VP8) we'll notice. std::vector f2_codecs = {vp9, vp9_rtx, vp8_answerer, vp8_answerer_rtx}; - f1_.set_video_codecs(f1_codecs, f1_codecs); - f2_.set_video_codecs(f2_codecs, f2_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); std::vector audio_codecs; - f1_.set_audio_codecs(audio_codecs, audio_codecs); - f2_.set_audio_codecs(audio_codecs, audio_codecs); + f1_.CodecVendorForTesting()->set_audio_codecs(audio_codecs, audio_codecs); + f2_.CodecVendorForTesting()->set_audio_codecs(audio_codecs, audio_codecs); // Offer will be {VP8, RTX for VP8}. Answer will be the same. std::unique_ptr offer = @@ -3260,7 +3270,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates rtx for H264 with the payload type `f1_` uses. AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", @@ -3286,7 +3296,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, int used_pl_type = acd->codecs()[0].id; f2_codecs[0].id = used_pl_type; // Set the payload type for H264. AddRtxCodec(CreateVideoRtxCodec(125, used_pl_type), &f2_codecs); - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); std::unique_ptr updated_offer( f2_.CreateOfferOrError(opts, answer.get()).MoveValue()); @@ -3323,7 +3333,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, std::vector f2_codecs = MAKE_VECTOR(kVideoCodecs2); // This creates rtx for H264 with the payload type `f2_` uses. AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[0].id), &f2_codecs); - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); std::unique_ptr offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue(); @@ -3362,12 +3372,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, RtxWithoutApt) { std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates RTX without associated payload type parameter. AddRtxCodec(CreateVideoCodec(126, kRtxCodecName), &f1_codecs); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = MAKE_VECTOR(kVideoCodecs2); // This creates RTX for H264 with the payload type `f2_` uses. AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[0].id), &f2_codecs); - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); std::unique_ptr offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue(); @@ -3405,12 +3415,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, FilterOutRtxIfAptDoesntMatch) { std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates RTX for H264 in sender. AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = MAKE_VECTOR(kVideoCodecs2); // This creates RTX for H263 in receiver. AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs2[1].id), &f2_codecs); - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); std::unique_ptr offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue(); @@ -3436,16 +3446,16 @@ TEST_F(MediaSessionDescriptionFactoryTest, std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates RTX for H264-SVC in sender. AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs1[0].id), &f1_codecs); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); // This creates RTX for H264 in sender. AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = MAKE_VECTOR(kVideoCodecs2); // This creates RTX for H264 in receiver. AddRtxCodec(CreateVideoRtxCodec(124, kVideoCodecs2[0].id), &f2_codecs); - f2_.set_video_codecs(f2_codecs, f1_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f1_codecs); // H264-SVC codec is removed in the answer, therefore, associated RTX codec // for H264-SVC should also be removed. @@ -3472,7 +3482,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, AddSecondRtxInNewOffer) { std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates RTX for H264 for the offerer. AddRtxCodec(CreateVideoRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::unique_ptr offer = f1_.CreateOfferOrError(opts, nullptr).MoveValue(); @@ -3486,7 +3496,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, AddSecondRtxInNewOffer) { // Now, attempt to add RTX for H264-SVC. AddRtxCodec(CreateVideoRtxCodec(125, kVideoCodecs1[0].id), &f1_codecs); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::unique_ptr updated_offer( f1_.CreateOfferOrError(opts, offer.get()).MoveValue()); @@ -3512,7 +3522,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateMultipleRtxSsrcs) { std::vector f1_codecs; f1_codecs.push_back(CreateVideoCodec(97, "H264")); AddRtxCodec(CreateVideoRtxCodec(125, 97), &f1_codecs); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); // Ensure that the offer has an RTX ssrc for each regular ssrc, and that there // is a FID ssrc + grouping for each. @@ -3556,7 +3566,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) { std::vector f1_codecs; f1_codecs.push_back(CreateVideoCodec(97, "H264")); f1_codecs.push_back(CreateVideoCodec(118, "flexfec-03")); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); // Ensure that the offer has a single FlexFEC ssrc and that // there is no FEC-FR ssrc + grouping for each. @@ -3599,7 +3609,7 @@ TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateNoFlexfecSsrcs) { std::vector f1_codecs; f1_codecs.push_back(CreateVideoCodec(97, "H264")); f1_codecs.push_back(CreateVideoCodec(118, "flexfec-03")); - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); // Ensure that the offer has no FlexFEC ssrcs for each regular ssrc, and that // there is no FEC-FR ssrc + grouping for each. @@ -4265,11 +4275,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, H265TxModeIsEqualRetainIt) { std::vector f1_codecs = {CreateVideoCodec(96, "H265")}; f1_codecs.back().tx_mode = "mrst"; - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = {CreateVideoCodec(96, "H265")}; f2_codecs.back().tx_mode = "mrst"; - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1", @@ -4301,11 +4311,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, H265TxModeIsEqualRetainIt) { TEST_F(MediaSessionDescriptionFactoryTest, H265TxModeIsDifferentDropCodecs) { std::vector f1_codecs = {CreateVideoCodec(96, "H265")}; f1_codecs.back().tx_mode = "mrst"; - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = {CreateVideoCodec(96, "H265")}; f2_codecs.back().tx_mode = "mrmt"; - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1", @@ -4338,11 +4348,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, H265TxModeIsDifferentDropCodecs) { TEST_F(MediaSessionDescriptionFactoryTest, PacketizationIsEqual) { std::vector f1_codecs = {CreateVideoCodec(96, "H264")}; f1_codecs.back().packetization = "raw"; - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = {CreateVideoCodec(96, "H264")}; f2_codecs.back().packetization = "raw"; - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1", @@ -4374,11 +4384,11 @@ TEST_F(MediaSessionDescriptionFactoryTest, PacketizationIsEqual) { TEST_F(MediaSessionDescriptionFactoryTest, PacketizationIsDifferent) { std::vector f1_codecs = {CreateVideoCodec(96, "H264")}; f1_codecs.back().packetization = "raw"; - f1_.set_video_codecs(f1_codecs, f1_codecs); + f1_.CodecVendorForTesting()->set_video_codecs(f1_codecs, f1_codecs); std::vector f2_codecs = {CreateVideoCodec(96, "H264")}; f2_codecs.back().packetization = "notraw"; - f2_.set_video_codecs(f2_codecs, f2_codecs); + f2_.CodecVendorForTesting()->set_video_codecs(f2_codecs, f2_codecs); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1", @@ -4498,10 +4508,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerWithLocalCodecParams) { audio_codecs2[0].SetParam(audio_param_name, audio_value2); video_codecs2[0].SetParam(video_param_name, video_value2); - f1_.set_audio_codecs(audio_codecs1, audio_codecs1); - f1_.set_video_codecs(video_codecs1, video_codecs1); - f2_.set_audio_codecs(audio_codecs2, audio_codecs2); - f2_.set_video_codecs(video_codecs2, video_codecs2); + f1_.CodecVendorForTesting()->set_audio_codecs(audio_codecs1, audio_codecs1); + f1_.CodecVendorForTesting()->set_video_codecs(video_codecs1, video_codecs1); + f2_.CodecVendorForTesting()->set_audio_codecs(audio_codecs2, audio_codecs2); + f2_.CodecVendorForTesting()->set_video_codecs(video_codecs2, video_codecs2); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", @@ -4552,8 +4562,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, // Offerer will send both codecs, answerer should choose the one with matching // packetization mode (and not the first one it sees). - f1_.set_video_codecs({h264_pm0, h264_pm1}, {h264_pm0, h264_pm1}); - f2_.set_video_codecs({h264_pm1}, {h264_pm1}); + f1_.CodecVendorForTesting()->set_video_codecs({h264_pm0, h264_pm1}, + {h264_pm0, h264_pm1}); + f2_.CodecVendorForTesting()->set_video_codecs({h264_pm1}, {h264_pm1}); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -4584,14 +4595,14 @@ class MediaProtocolTest : public testing::TestWithParam { tdf2_(field_trials_), f1_(nullptr, false, &ssrc_generator1, &tdf1_, &pt_suggester_1_), f2_(nullptr, false, &ssrc_generator2, &tdf2_, &pt_suggester_2_) { - f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1), - MAKE_VECTOR(kAudioCodecs1)); - f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1), - MAKE_VECTOR(kVideoCodecs1)); - f2_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs2), - MAKE_VECTOR(kAudioCodecs2)); - f2_.set_video_codecs(MAKE_VECTOR(kVideoCodecs2), - MAKE_VECTOR(kVideoCodecs2)); + f1_.CodecVendorForTesting()->set_audio_codecs(MAKE_VECTOR(kAudioCodecs1), + MAKE_VECTOR(kAudioCodecs1)); + f1_.CodecVendorForTesting()->set_video_codecs(MAKE_VECTOR(kVideoCodecs1), + MAKE_VECTOR(kVideoCodecs1)); + f2_.CodecVendorForTesting()->set_audio_codecs(MAKE_VECTOR(kAudioCodecs2), + MAKE_VECTOR(kAudioCodecs2)); + f2_.CodecVendorForTesting()->set_video_codecs(MAKE_VECTOR(kVideoCodecs2), + MAKE_VECTOR(kVideoCodecs2)); tdf1_.set_certificate(rtc::RTCCertificate::Create( std::unique_ptr(new rtc::FakeSSLIdentity("id1")))); tdf2_.set_certificate(rtc::RTCCertificate::Create( @@ -4675,28 +4686,40 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) { recv_codecs[2].name = "ilbc"; // Test proper merge - sf.set_audio_codecs(send_codecs, recv_codecs); - EXPECT_EQ(send_codecs, sf.audio_send_codecs().codecs()); - EXPECT_EQ(recv_codecs, sf.audio_recv_codecs().codecs()); - EXPECT_EQ(sendrecv_codecs, sf.audio_sendrecv_codecs().codecs()); + sf.CodecVendorForTesting()->set_audio_codecs(send_codecs, recv_codecs); + EXPECT_EQ(send_codecs, + sf.CodecVendorForTesting()->audio_send_codecs().codecs()); + EXPECT_EQ(recv_codecs, + sf.CodecVendorForTesting()->audio_recv_codecs().codecs()); + EXPECT_EQ(sendrecv_codecs, + sf.CodecVendorForTesting()->audio_sendrecv_codecs().codecs()); // Test empty send codecs list - sf.set_audio_codecs(no_codecs, recv_codecs); - EXPECT_EQ(no_codecs, sf.audio_send_codecs().codecs()); - EXPECT_EQ(recv_codecs, sf.audio_recv_codecs().codecs()); - EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs().codecs()); + sf.CodecVendorForTesting()->set_audio_codecs(no_codecs, recv_codecs); + EXPECT_EQ(no_codecs, + sf.CodecVendorForTesting()->audio_send_codecs().codecs()); + EXPECT_EQ(recv_codecs, + sf.CodecVendorForTesting()->audio_recv_codecs().codecs()); + EXPECT_EQ(no_codecs, + sf.CodecVendorForTesting()->audio_sendrecv_codecs().codecs()); // Test empty recv codecs list - sf.set_audio_codecs(send_codecs, no_codecs); - EXPECT_EQ(send_codecs, sf.audio_send_codecs().codecs()); - EXPECT_EQ(no_codecs, sf.audio_recv_codecs().codecs()); - EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs().codecs()); + sf.CodecVendorForTesting()->set_audio_codecs(send_codecs, no_codecs); + EXPECT_EQ(send_codecs, + sf.CodecVendorForTesting()->audio_send_codecs().codecs()); + EXPECT_EQ(no_codecs, + sf.CodecVendorForTesting()->audio_recv_codecs().codecs()); + EXPECT_EQ(no_codecs, + sf.CodecVendorForTesting()->audio_sendrecv_codecs().codecs()); // Test all empty codec lists - sf.set_audio_codecs(no_codecs, no_codecs); - EXPECT_EQ(no_codecs, sf.audio_send_codecs().codecs()); - EXPECT_EQ(no_codecs, sf.audio_recv_codecs().codecs()); - EXPECT_EQ(no_codecs, sf.audio_sendrecv_codecs().codecs()); + sf.CodecVendorForTesting()->set_audio_codecs(no_codecs, no_codecs); + EXPECT_EQ(no_codecs, + sf.CodecVendorForTesting()->audio_send_codecs().codecs()); + EXPECT_EQ(no_codecs, + sf.CodecVendorForTesting()->audio_recv_codecs().codecs()); + EXPECT_EQ(no_codecs, + sf.CodecVendorForTesting()->audio_sendrecv_codecs().codecs()); } // Compare the two vectors of codecs ignoring the payload type. @@ -4727,7 +4750,7 @@ void TestAudioCodecsOffer(RtpTransceiverDirection direction) { const std::vector send_codecs = MAKE_VECTOR(kAudioCodecs1); const std::vector recv_codecs = MAKE_VECTOR(kAudioCodecs2); const std::vector sendrecv_codecs = MAKE_VECTOR(kAudioCodecsAnswer); - sf.set_audio_codecs(send_codecs, recv_codecs); + sf.CodecVendorForTesting()->set_audio_codecs(send_codecs, recv_codecs); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", direction, kActive, @@ -4835,10 +4858,10 @@ void TestAudioCodecsAnswer(RtpTransceiverDirection offer_direction, MediaSessionDescriptionFactory answer_factory( nullptr, false, &ssrc_generator2, &answer_tdf, &answer_pt_suggester); - offer_factory.set_audio_codecs( + offer_factory.CodecVendorForTesting()->set_audio_codecs( VectorFromIndices(kOfferAnswerCodecs, kOfferSendCodecs), VectorFromIndices(kOfferAnswerCodecs, kOfferRecvCodecs)); - answer_factory.set_audio_codecs( + answer_factory.CodecVendorForTesting()->set_audio_codecs( VectorFromIndices(kOfferAnswerCodecs, kAnswerSendCodecs), VectorFromIndices(kOfferAnswerCodecs, kAnswerRecvCodecs)); @@ -5026,9 +5049,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, TestSendRecvSymmetrical) { const std::vector recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); const std::vector sendrecv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); - sf_offerer_.set_video_codecs(send_codecs, recv_codecs); - sf_answerer_.set_video_codecs(recv_codecs, send_codecs); - EXPECT_EQ(sendrecv_codecs, sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(send_codecs, + recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(recv_codecs, + send_codecs); + EXPECT_EQ( + sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5073,9 +5100,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, TestSendOnlySymmetrical) { const std::vector recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level6); const std::vector sendrecv_codecs = MAKE_VECTOR(kVideoCodecsH265Level6); - sf_offerer_.set_video_codecs(send_codecs, recv_codecs); - sf_answerer_.set_video_codecs(recv_codecs, send_codecs); - EXPECT_EQ(sendrecv_codecs, sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(send_codecs, + recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(recv_codecs, + send_codecs); + EXPECT_EQ( + sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5117,9 +5148,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, TestRecvOnlySymmetrical) { const std::vector recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); const std::vector sendrecv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); - sf_offerer_.set_video_codecs(send_codecs, recv_codecs); - sf_answerer_.set_video_codecs(recv_codecs, send_codecs); - EXPECT_EQ(sendrecv_codecs, sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(send_codecs, + recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(recv_codecs, + send_codecs); + EXPECT_EQ( + sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5167,10 +5202,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level6); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5223,10 +5261,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level52); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level6); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5279,10 +5320,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level31); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level5); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5345,10 +5389,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level4); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level6); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5401,10 +5448,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level6); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5457,10 +5507,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level6); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5510,10 +5563,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level52); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level6); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5563,10 +5619,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level31); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level5); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5616,10 +5675,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level4); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level6); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5669,10 +5731,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level6); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5722,10 +5787,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level6); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5776,10 +5844,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level52); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level6); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5830,10 +5901,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level31); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level5); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5884,10 +5958,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level4); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level6); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5938,10 +6015,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level6); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -5988,10 +6068,13 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, MAKE_VECTOR(kVideoCodecsH265Level6); const std::vector answerer_recv_codecs = MAKE_VECTOR(kVideoCodecsH265Level52); - sf_offerer_.set_video_codecs(offerer_send_codecs, offerer_recv_codecs); - sf_answerer_.set_video_codecs(answerer_send_codecs, answerer_recv_codecs); - EXPECT_EQ(offerer_sendrecv_codecs, - sf_offerer_.video_sendrecv_codecs().codecs()); + sf_offerer_.CodecVendorForTesting()->set_video_codecs(offerer_send_codecs, + offerer_recv_codecs); + sf_answerer_.CodecVendorForTesting()->set_video_codecs(answerer_send_codecs, + answerer_recv_codecs); + EXPECT_EQ( + offerer_sendrecv_codecs, + sf_offerer_.CodecVendorForTesting()->video_sendrecv_codecs().codecs()); MediaSessionOptions opts; AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", @@ -6001,7 +6084,8 @@ TEST_F(VideoCodecsOfferH265LevelIdTest, AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, {kMediaStream1}, 1, &opts); std::vector preferences; - for (const auto& codec : sf_offerer_.video_recv_codecs()) { + for (const auto& codec : + sf_offerer_.CodecVendorForTesting()->video_recv_codecs()) { preferences.push_back(webrtc::ToRtpCodecCapability(codec)); } opts.media_description_options[0].codec_preferences = preferences;