From c63ddb2a3f9abbeeeebe16c00231fd2014973cbe Mon Sep 17 00:00:00 2001 From: Amit Hilbuch Date: Wed, 2 Jan 2019 10:13:58 -0800 Subject: [PATCH] Negotiating Simulcast in the initial offer/answer - Part1. This change adds Simulcast negotiation to media session offers/answers. Next step is to add negotiation logic to PeerConnection. Bug: webrtc:10075 Change-Id: Iea3a1084c16058f0efbc974cf623ec05c3c7a74f Reviewed-on: https://webrtc-review.googlesource.com/c/115790 Reviewed-by: Seth Hampson Reviewed-by: Steve Anton Commit-Queue: Amit Hilbuch Cr-Commit-Position: refs/heads/master@{#26115} --- pc/BUILD.gn | 4 + pc/mediasession.cc | 246 ++++++--- pc/mediasession.h | 18 + pc/mediasession_unittest.cc | 773 ++++++++++++++++++++--------- pc/peerconnection.cc | 23 +- pc/simulcastdescription.cc | 4 + pc/simulcastdescription.h | 15 +- pc/unique_id_generator.cc | 64 +++ pc/unique_id_generator.h | 122 +++++ pc/unique_id_generator_unittest.cc | 79 +++ 10 files changed, 1019 insertions(+), 329 deletions(-) create mode 100644 pc/unique_id_generator.cc create mode 100644 pc/unique_id_generator.h create mode 100644 pc/unique_id_generator_unittest.cc diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 2a07e71e73..268176c6cf 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -66,6 +66,8 @@ rtc_static_library("rtc_pc_base") { "srtptransport.h", "transportstats.cc", "transportstats.h", + "unique_id_generator.cc", + "unique_id_generator.h", ] deps = [ @@ -90,6 +92,7 @@ rtc_static_library("rtc_pc_base") { "../rtc_base:checks", "../rtc_base:rtc_base", "../rtc_base:rtc_task_queue", + "../rtc_base:stringutils", "../rtc_base/third_party/base64", "../rtc_base/third_party/sigslot", "../system_wrappers:metrics", @@ -257,6 +260,7 @@ if (rtc_include_tests) { "srtpsession_unittest.cc", "srtptestutil.h", "srtptransport_unittest.cc", + "unique_id_generator_unittest.cc", ] include_dirs = [ "//third_party/libsrtp/srtp" ] diff --git a/pc/mediasession.cc b/pc/mediasession.cc index 4898ed9f03..4f7fae1077 100644 --- a/pc/mediasession.cc +++ b/pc/mediasession.cc @@ -28,6 +28,7 @@ #include "pc/channelmanager.h" #include "pc/rtpmediautils.h" #include "pc/srtpfilter.h" +#include "pc/unique_id_generator.h" #include "rtc_base/checks.h" #include "rtc_base/helpers.h" #include "rtc_base/logging.h" @@ -36,6 +37,7 @@ namespace { using webrtc::RtpTransceiverDirection; +using webrtc::UniqueRandomIdGenerator; const char kInline[] = "inline:"; @@ -272,22 +274,6 @@ static bool SelectCrypto(const MediaContentDescription* offer, return false; } -// Generate random SSRC values that are not already present in |params_vec|. -// The generated values are added to |ssrcs|. -// |num_ssrcs| is the number of the SSRC will be generated. -static void GenerateSsrcs(const StreamParamsVec& params_vec, - int num_ssrcs, - std::vector* ssrcs) { - for (int i = 0; i < num_ssrcs; i++) { - uint32_t candidate; - do { - candidate = rtc::CreateRandomNonZeroId(); - } while (GetStreamBySsrc(params_vec, candidate) || - std::count(ssrcs->begin(), ssrcs->end(), candidate) > 0); - ssrcs->push_back(candidate); - } -} - // Finds all StreamParams of all media types and attach them to stream_params. static StreamParamsVec GetCurrentStreamParams( const std::vector& active_local_contents) { @@ -401,6 +387,135 @@ class UsedRtpHeaderExtensionIds : public UsedIds { private: }; +static StreamParams CreateStreamParamsForNewSenderWithSsrcs( + const SenderOptions& sender, + const std::string& rtcp_cname, + const StreamParamsVec& current_streams, + bool include_rtx_streams, + bool include_flexfec_stream) { + StreamParams result; + result.id = sender.track_id; + + std::vector known_ssrcs; + for (const StreamParams& params : current_streams) { + for (uint32_t ssrc : params.ssrcs) { + known_ssrcs.push_back(ssrc); + } + } + + UniqueRandomIdGenerator ssrc_generator(known_ssrcs); + // We need to keep |primary_ssrcs| separate from |result.ssrcs| because + // iterators are invalidated when rtx and flexfec ssrcs are added to the list. + std::vector primary_ssrcs; + for (int i = 0; i < sender.num_sim_layers; ++i) { + primary_ssrcs.push_back(ssrc_generator()); + } + result.ssrcs = primary_ssrcs; + + if (sender.num_sim_layers > 1) { + SsrcGroup group(kSimSsrcGroupSemantics, result.ssrcs); + result.ssrc_groups.push_back(group); + } + // Generate an RTX ssrc for every ssrc in the group. + if (include_rtx_streams) { + for (uint32_t ssrc : primary_ssrcs) { + result.AddFidSsrc(ssrc, ssrc_generator()); + } + } + // Generate extra ssrc for include_flexfec_stream case. + if (include_flexfec_stream) { + // TODO(brandtr): Update when we support multistream protection. + if (result.ssrcs.size() == 1) { + for (uint32_t ssrc : primary_ssrcs) { + result.AddFecFrSsrc(ssrc, ssrc_generator()); + } + } else if (!result.ssrcs.empty()) { + RTC_LOG(LS_WARNING) + << "Our FlexFEC implementation only supports protecting " + "a single media streams. This session has multiple " + "media streams however, so no FlexFEC SSRC will be generated."; + } + } + result.cname = rtcp_cname; + result.set_stream_ids(sender.stream_ids); + + return result; +} + +static bool ValidateSimulcastLayers( + const std::vector& rids, + const SimulcastLayerList& simulcast_layers) { + std::vector all_layers = simulcast_layers.GetAllLayers(); + return std::all_of(all_layers.begin(), all_layers.end(), + [&rids](const SimulcastLayer& layer) { + return std::find_if(rids.begin(), rids.end(), + [&layer](const RidDescription& rid) { + return rid.rid == layer.rid; + }) != rids.end(); + }); +} + +static StreamParams CreateStreamParamsForNewSenderWithRids( + const SenderOptions& sender, + const std::string& rtcp_cname) { + RTC_DCHECK(!sender.rids.empty()); + RTC_DCHECK_EQ(sender.num_sim_layers, 0) + << "RIDs are the compliant way to indicate simulcast."; + RTC_DCHECK(ValidateSimulcastLayers(sender.rids, sender.simulcast_layers)); + StreamParams result; + result.id = sender.track_id; + result.cname = rtcp_cname; + result.set_stream_ids(sender.stream_ids); + + // More than one rid should be signaled. + if (sender.rids.size() > 1) { + result.set_rids(sender.rids); + } + + return result; +} + +// Adds SimulcastDescription if indicated by the media description options. +// MediaContentDescription should already be set up with the send rids. +static void AddSimulcastToMediaDescription( + const MediaDescriptionOptions& media_description_options, + MediaContentDescription* description) { + RTC_DCHECK(description); + + // Check if we are using RIDs in this scenario. + if (std::all_of( + description->streams().begin(), description->streams().end(), + [](const StreamParams& params) { return !params.has_rids(); })) { + return; + } + + RTC_DCHECK_EQ(1, description->streams().size()) + << "RIDs are only supported in Unified Plan semantics."; + RTC_DCHECK_EQ(1, media_description_options.sender_options.size()); + RTC_DCHECK(description->type() == MediaType::MEDIA_TYPE_AUDIO || + description->type() == MediaType::MEDIA_TYPE_VIDEO); + + // One RID or less indicates that simulcast is not needed. + if (description->streams()[0].rids().size() <= 1) { + return; + } + + RTC_DCHECK(!media_description_options.receive_rids.empty()); + RTC_DCHECK(ValidateSimulcastLayers( + media_description_options.receive_rids, + media_description_options.receive_simulcast_layers)); + StreamParams receive_stream; + receive_stream.set_rids(media_description_options.receive_rids); + description->set_receive_stream(receive_stream); + + SimulcastDescription simulcast; + simulcast.send_layers() = + media_description_options.sender_options[0].simulcast_layers; + simulcast.receive_layers() = + media_description_options.receive_simulcast_layers; + description->set_simulcast_description(simulcast); +} + // Adds a StreamParams for each SenderOptions in |sender_options| to // content_description. // |current_params| - All currently known StreamParams of any media type. @@ -428,44 +543,17 @@ static bool AddStreamParams( GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id); if (!param) { // This is a new sender. - std::vector ssrcs; - GenerateSsrcs(*current_streams, sender.num_sim_layers, &ssrcs); - StreamParams stream_param; - stream_param.id = sender.track_id; - // Add the generated ssrc. - for (size_t i = 0; i < ssrcs.size(); ++i) { - stream_param.ssrcs.push_back(ssrcs[i]); - } - if (sender.num_sim_layers > 1) { - SsrcGroup group(kSimSsrcGroupSemantics, stream_param.ssrcs); - stream_param.ssrc_groups.push_back(group); - } - // Generate extra ssrcs for include_rtx_streams case. - if (include_rtx_streams) { - // Generate an RTX ssrc for every ssrc in the group. - std::vector rtx_ssrcs; - GenerateSsrcs(*current_streams, static_cast(ssrcs.size()), - &rtx_ssrcs); - for (size_t i = 0; i < ssrcs.size(); ++i) { - stream_param.AddFidSsrc(ssrcs[i], rtx_ssrcs[i]); - } - } - // Generate extra ssrc for include_flexfec_stream case. - if (include_flexfec_stream) { - // TODO(brandtr): Update when we support multistream protection. - if (ssrcs.size() == 1) { - std::vector flexfec_ssrcs; - GenerateSsrcs(*current_streams, 1, &flexfec_ssrcs); - stream_param.AddFecFrSsrc(ssrcs[0], flexfec_ssrcs[0]); - } else if (!ssrcs.empty()) { - RTC_LOG(LS_WARNING) - << "Our FlexFEC implementation only supports protecting " - "a single media streams. This session has multiple " - "media streams however, so no FlexFEC SSRC will be generated."; - } - } - stream_param.cname = rtcp_cname; - stream_param.set_stream_ids(sender.stream_ids); + StreamParams stream_param = + sender.rids.empty() + ? + // Signal SSRCs and legacy simulcast (if requested). + CreateStreamParamsForNewSenderWithSsrcs( + sender, rtcp_cname, *current_streams, include_rtx_streams, + include_flexfec_stream) + : + // Signal RIDs and spec-compliant simulcast (if requested). + CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname); + content_description->AddStream(stream_param); // Store the new StreamParams in current_streams. @@ -692,7 +780,7 @@ static bool IsFlexfecCodec(const C& codec) { // offer. template static bool CreateMediaContentOffer( - const std::vector& sender_options, + const MediaDescriptionOptions& media_description_options, const MediaSessionOptions& session_options, const std::vector& codecs, const SecurePolicy& secure_policy, @@ -709,11 +797,13 @@ static bool CreateMediaContentOffer( } offer->set_rtp_header_extensions(rtp_extensions); - if (!AddStreamParams(sender_options, session_options.rtcp_cname, - current_streams, offer)) { + if (!AddStreamParams(media_description_options.sender_options, + session_options.rtcp_cname, current_streams, offer)) { return false; } + AddSimulcastToMediaDescription(media_description_options, offer); + if (secure_policy != SEC_DISABLED) { if (current_cryptos) { AddMediaCryptos(*current_cryptos, offer); @@ -1117,6 +1207,8 @@ static bool CreateMediaContentAnswer( return false; // Something went seriously wrong. } + AddSimulcastToMediaDescription(media_description_options, answer); + answer->set_direction(NegotiateRtpTransceiverDirection( offer->direction(), media_description_options.direction)); return true; @@ -1200,15 +1292,21 @@ void MediaDescriptionOptions::AddAudioSender( const std::string& track_id, const std::vector& stream_ids) { RTC_DCHECK(type == MEDIA_TYPE_AUDIO); - AddSenderInternal(track_id, stream_ids, 1); + 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); - AddSenderInternal(track_id, stream_ids, num_sim_layers); + 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::AddRtpDataChannel(const std::string& track_id, @@ -1216,16 +1314,24 @@ void MediaDescriptionOptions::AddRtpDataChannel(const std::string& track_id, RTC_DCHECK(type == MEDIA_TYPE_DATA); // TODO(steveanton): Is it the case that RtpDataChannel will never have more // than one stream? - AddSenderInternal(track_id, {stream_id}, 1); + AddSenderInternal(track_id, {stream_id}, {}, SimulcastLayerList(), 1); } 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); - sender_options.push_back(SenderOptions{track_id, stream_ids, num_sim_layers}); + 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 { @@ -1928,9 +2034,9 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options, &crypto_suites); if (!CreateMediaContentOffer( - media_description_options.sender_options, session_options, - filtered_codecs, sdes_policy, GetCryptos(current_content), - crypto_suites, audio_rtp_extensions, current_streams, audio.get())) { + media_description_options, session_options, filtered_codecs, + sdes_policy, GetCryptos(current_content), crypto_suites, + audio_rtp_extensions, current_streams, audio.get())) { return false; } @@ -1998,9 +2104,9 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( } if (!CreateMediaContentOffer( - media_description_options.sender_options, session_options, - filtered_codecs, sdes_policy, GetCryptos(current_content), - crypto_suites, video_rtp_extensions, current_streams, video.get())) { + media_description_options, session_options, filtered_codecs, + sdes_policy, GetCryptos(current_content), crypto_suites, + video_rtp_extensions, current_streams, video.get())) { return false; } @@ -2066,9 +2172,9 @@ bool MediaSessionDescriptionFactory::AddDataContentForOffer( // Even SCTP uses a "codec". if (!CreateMediaContentOffer( - media_description_options.sender_options, session_options, - data_codecs, sdes_policy, GetCryptos(current_content), crypto_suites, - RtpHeaderExtensions(), current_streams, data.get())) { + media_description_options, session_options, data_codecs, sdes_policy, + GetCryptos(current_content), crypto_suites, RtpHeaderExtensions(), + current_streams, data.get())) { return false; } diff --git a/pc/mediasession.h b/pc/mediasession.h index d5c26f42ef..f732cb24f8 100644 --- a/pc/mediasession.h +++ b/pc/mediasession.h @@ -35,9 +35,14 @@ class ChannelManager; 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; }; @@ -55,6 +60,8 @@ struct MediaDescriptionOptions { 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); // Internally just uses sender_options. @@ -69,11 +76,22 @@ struct MediaDescriptionOptions { // Note: There's no equivalent "RtpReceiverOptions" because only send // stream information goes in the local descriptions. std::vector sender_options; + // |receive_rids| and |receive_simulcast_layers| are used with spec-compliant + // simulcast. When Simulcast is used, they should both not be empty. + // All RIDs in |receive_simulcast_layers| must appear in receive_rids as well. + // |receive_rids| could also be used outside of simulcast. It is possible to + // add restrictions on the incoming stream during negotiation outside the + // simulcast scenario. This is currently not fully supported, as meaningful + // restrictions are not handled by this library. + std::vector receive_rids; + SimulcastLayerList receive_simulcast_layers; 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); }; diff --git a/pc/mediasession_unittest.cc b/pc/mediasession_unittest.cc index 2af14d2df3..16be74f664 100644 --- a/pc/mediasession_unittest.cc +++ b/pc/mediasession_unittest.cc @@ -36,45 +36,58 @@ typedef std::vector Candidates; +using cricket::AudioCodec; +using cricket::AudioContentDescription; +using cricket::ContentInfo; +using cricket::CryptoParamsVec; +using cricket::DataCodec; +using cricket::DataContentDescription; +using cricket::GetFirstAudioContent; +using cricket::GetFirstAudioContentDescription; +using cricket::GetFirstDataContent; +using cricket::GetFirstDataContentDescription; +using cricket::GetFirstVideoContent; +using cricket::GetFirstVideoContentDescription; +using cricket::kAutoBandwidth; +using cricket::MEDIA_TYPE_AUDIO; +using cricket::MEDIA_TYPE_DATA; +using cricket::MEDIA_TYPE_VIDEO; using cricket::MediaContentDescription; -using cricket::MediaSessionDescriptionFactory; using cricket::MediaDescriptionOptions; +using cricket::MediaProtocolType; +using cricket::MediaSessionDescriptionFactory; using cricket::MediaSessionOptions; using cricket::MediaType; -using cricket::MediaProtocolType; +using cricket::RidDescription; +using cricket::RidDirection; +using cricket::SEC_DISABLED; +using cricket::SEC_ENABLED; +using cricket::SEC_REQUIRED; using cricket::SessionDescription; +using cricket::SimulcastDescription; +using cricket::SimulcastLayer; +using cricket::SimulcastLayerList; using cricket::SsrcGroup; using cricket::StreamParams; using cricket::StreamParamsVec; using cricket::TransportDescription; using cricket::TransportDescriptionFactory; using cricket::TransportInfo; -using cricket::ContentInfo; -using cricket::CryptoParamsVec; -using cricket::AudioContentDescription; -using cricket::VideoContentDescription; -using cricket::DataContentDescription; -using cricket::GetFirstAudioContent; -using cricket::GetFirstVideoContent; -using cricket::GetFirstDataContent; -using cricket::GetFirstAudioContentDescription; -using cricket::GetFirstVideoContentDescription; -using cricket::GetFirstDataContentDescription; -using cricket::kAutoBandwidth; -using cricket::AudioCodec; using cricket::VideoCodec; -using cricket::DataCodec; -using cricket::MEDIA_TYPE_AUDIO; -using cricket::MEDIA_TYPE_VIDEO; -using cricket::MEDIA_TYPE_DATA; -using cricket::SEC_DISABLED; -using cricket::SEC_ENABLED; -using cricket::SEC_REQUIRED; -using rtc::CS_AES_CM_128_HMAC_SHA1_32; -using rtc::CS_AES_CM_128_HMAC_SHA1_80; +using cricket::VideoContentDescription; using rtc::CS_AEAD_AES_128_GCM; using rtc::CS_AEAD_AES_256_GCM; +using rtc::CS_AES_CM_128_HMAC_SHA1_32; +using rtc::CS_AES_CM_128_HMAC_SHA1_80; +using testing::Each; using testing::ElementsAreArray; +using testing::Eq; +using testing::Field; +using testing::IsEmpty; +using testing::IsFalse; +using testing::Ne; +using testing::Pointwise; +using testing::SizeIs; using webrtc::RtpExtension; using webrtc::RtpTransceiverDirection; @@ -223,8 +236,8 @@ static const char* kMediaProtocolsDtls[] = { static const char* kDefaultSrtpCryptoSuite = CS_AES_CM_128_HMAC_SHA1_80; static const char* kDefaultSrtpCryptoSuiteGcm = CS_AEAD_AES_256_GCM; -// These constants are used to make the code using "AddMediaSection" more -// readable. +// These constants are used to make the code using "AddMediaDescriptionOptions" +// more readable. static constexpr bool kStopped = true; static constexpr bool kActive = false; @@ -276,33 +289,37 @@ FindFirstMediaDescriptionByMid(const std::string& mid, } // Add a media section to the |session_options|. -static void AddMediaSection(MediaType type, - const std::string& mid, - RtpTransceiverDirection direction, - bool stopped, - MediaSessionOptions* opts) { +static void AddMediaDescriptionOptions(MediaType type, + const std::string& mid, + RtpTransceiverDirection direction, + bool stopped, + MediaSessionOptions* opts) { opts->media_description_options.push_back( MediaDescriptionOptions(type, mid, direction, stopped)); } static void AddAudioVideoSections(RtpTransceiverDirection direction, MediaSessionOptions* opts) { - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", direction, kActive, opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video", direction, kActive, opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", direction, kActive, + opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", direction, kActive, + opts); } static void AddDataSection(cricket::DataChannelType dct, RtpTransceiverDirection direction, MediaSessionOptions* opts) { opts->data_channel_type = dct; - AddMediaSection(MEDIA_TYPE_DATA, "data", direction, kActive, opts); + AddMediaDescriptionOptions(MEDIA_TYPE_DATA, "data", direction, kActive, opts); } -static void AttachSenderToMediaSection( +static void AttachSenderToMediaDescriptionOptions( const std::string& mid, MediaType type, const std::string& track_id, const std::vector& stream_ids, + const std::vector& rids, + const SimulcastLayerList& simulcast_layers, int num_sim_layer, MediaSessionOptions* session_options) { auto it = FindFirstMediaDescriptionByMid(mid, session_options); @@ -311,7 +328,8 @@ static void AttachSenderToMediaSection( it->AddAudioSender(track_id, stream_ids); break; case MEDIA_TYPE_VIDEO: - it->AddVideoSender(track_id, stream_ids, num_sim_layer); + it->AddVideoSender(track_id, stream_ids, rids, simulcast_layers, + num_sim_layer); break; case MEDIA_TYPE_DATA: RTC_CHECK(stream_ids.size() == 1U); @@ -322,6 +340,18 @@ static void AttachSenderToMediaSection( } } +static void AttachSenderToMediaDescriptionOptions( + const std::string& mid, + MediaType type, + const std::string& track_id, + const std::vector& stream_ids, + int num_sim_layer, + MediaSessionOptions* session_options) { + AttachSenderToMediaDescriptionOptions(mid, type, track_id, stream_ids, {}, + SimulcastLayerList(), num_sim_layer, + session_options); +} + static void DetachSenderFromMediaSection(const std::string& mid, const std::string& track_id, MediaSessionOptions* session_options) { @@ -340,8 +370,9 @@ static void DetachSenderFromMediaSection(const std::string& mid, // (https://tools.ietf.org/html/draft-uberti-rtcweb-plan-00). static MediaSessionOptions CreatePlanBMediaSessionOptions() { MediaSessionOptions session_options; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &session_options); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &session_options); return session_options; } @@ -754,10 +785,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, f1_.set_secure(SEC_ENABLED); f2_.set_secure(SEC_ENABLED); MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kInactive, - kStopped, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kInactive, kStopped, + &opts); opts.data_channel_type = cricket::DCT_NONE; opts.bundle_enabled = true; std::unique_ptr offer = f1_.CreateOffer(opts, NULL); @@ -879,10 +912,10 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSendOnlyOffer) { MediaSessionOptions opts; AddAudioVideoSections(RtpTransceiverDirection::kSendOnly, &opts); - AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack1, - {kMediaStream1}, 1, &opts); - AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1, - {kMediaStream1}, 1, &opts); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, + {kMediaStream1}, 1, &opts); + AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack1, + {kMediaStream1}, 1, &opts); std::unique_ptr offer = f1_.CreateOffer(opts, NULL); ASSERT_TRUE(offer.get() != NULL); @@ -907,8 +940,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateOfferContentOrder) { EXPECT_EQ(1u, offer1->contents().size()); EXPECT_TRUE(IsMediaContentOfType(&offer1->contents()[0], MEDIA_TYPE_DATA)); - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::unique_ptr offer2( f1_.CreateOffer(opts, offer1.get())); ASSERT_TRUE(offer2.get() != NULL); @@ -916,8 +950,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateOfferContentOrder) { EXPECT_TRUE(IsMediaContentOfType(&offer2->contents()[0], MEDIA_TYPE_DATA)); EXPECT_TRUE(IsMediaContentOfType(&offer2->contents()[1], MEDIA_TYPE_VIDEO)); - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::unique_ptr offer3( f1_.CreateOffer(opts, offer2.get())); ASSERT_TRUE(offer3.get() != NULL); @@ -1176,15 +1211,17 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAnswerContentOrder) { ASSERT_TRUE(offer1.get() != NULL); // Appends audio to the offer. - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::unique_ptr offer2( f1_.CreateOffer(opts, offer1.get())); ASSERT_TRUE(offer2.get() != NULL); // Appends video to the offer. - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::unique_ptr offer3( f1_.CreateOffer(opts, offer2.get())); ASSERT_TRUE(offer3.get() != NULL); @@ -1517,10 +1554,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateVideoAnswerRtcpMux) { // Create an audio-only answer to a video offer. TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerToVideo) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::unique_ptr offer = f1_.CreateOffer(opts, NULL); ASSERT_TRUE(offer.get() != NULL); @@ -1539,8 +1578,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateAudioAnswerToVideo) { TEST_F(MediaSessionDescriptionFactoryTest, TestCreateNoDataAnswerToDataOffer) { MediaSessionOptions opts = CreatePlanBMediaSessionOptions(); opts.data_channel_type = cricket::DCT_RTP; - AddMediaSection(MEDIA_TYPE_DATA, "data", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_DATA, "data", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::unique_ptr offer = f1_.CreateOffer(opts, NULL); ASSERT_TRUE(offer.get() != NULL); @@ -1655,18 +1695,18 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) { MediaSessionOptions opts; AddAudioVideoSections(RtpTransceiverDirection::kSendRecv, &opts); - AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack1, - {kMediaStream1}, 1, &opts); - AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1, - {kMediaStream1}, 1, &opts); - AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack2, - {kMediaStream1}, 1, &opts); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, + {kMediaStream1}, 1, &opts); + AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack1, + {kMediaStream1}, 1, &opts); + AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack2, + {kMediaStream1}, 1, &opts); AddDataSection(cricket::DCT_RTP, RtpTransceiverDirection::kSendRecv, &opts); - AttachSenderToMediaSection("data", MEDIA_TYPE_DATA, kDataTrack1, - {kMediaStream1}, 1, &opts); - AttachSenderToMediaSection("data", MEDIA_TYPE_DATA, kDataTrack2, - {kMediaStream1}, 1, &opts); + AttachSenderToMediaDescriptionOptions("data", MEDIA_TYPE_DATA, kDataTrack1, + {kMediaStream1}, 1, &opts); + AttachSenderToMediaDescriptionOptions("data", MEDIA_TYPE_DATA, kDataTrack2, + {kMediaStream1}, 1, &opts); f1_.set_secure(SEC_ENABLED); std::unique_ptr offer = f1_.CreateOffer(opts, NULL); @@ -1730,14 +1770,14 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) { // Update the offer. Add a new video track that is not synched to the // other tracks and replace audio track 2 with audio track 3. - AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack2, - {kMediaStream2}, 1, &opts); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack2, + {kMediaStream2}, 1, &opts); DetachSenderFromMediaSection("audio", kAudioTrack2, &opts); - AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack3, - {kMediaStream1}, 1, &opts); + AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack3, + {kMediaStream1}, 1, &opts); DetachSenderFromMediaSection("data", kDataTrack2, &opts); - AttachSenderToMediaSection("data", MEDIA_TYPE_DATA, kDataTrack3, - {kMediaStream1}, 1, &opts); + AttachSenderToMediaDescriptionOptions("data", MEDIA_TYPE_DATA, kDataTrack3, + {kMediaStream1}, 1, &opts); std::unique_ptr updated_offer( f1_.CreateOffer(opts, offer.get())); @@ -1799,13 +1839,15 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoOffer) { // Create an offer with simulcast video stream. TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSimulcastVideoOffer) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); const int num_sim_layers = 3; - AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack1, - {kMediaStream1}, num_sim_layers, &opts); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, + {kMediaStream1}, num_sim_layers, &opts); std::unique_ptr offer = f1_.CreateOffer(opts, NULL); ASSERT_TRUE(offer.get() != NULL); @@ -1822,6 +1864,190 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSimulcastVideoOffer) { EXPECT_EQ(static_cast(num_sim_layers), sim_ssrc_group->ssrcs.size()); } +MATCHER(RidDescriptionEquals, "Verifies that two RidDescriptions are equal.") { + const RidDescription& rid1 = ::testing::get<0>(arg); + const RidDescription& rid2 = ::testing::get<1>(arg); + return rid1.rid == rid2.rid && rid1.direction == rid2.direction; +} + +static void CheckSimulcastInSessionDescription( + const SessionDescription* description, + const std::string& content_name, + const std::vector& send_rids, + const SimulcastLayerList& send_layers, + const RidDescription& receive_rid, + const SimulcastLayer& receive_layer) { + ASSERT_NE(description, nullptr); + const ContentInfo* content = description->GetContentByName(content_name); + ASSERT_NE(content, nullptr); + const MediaContentDescription* cd = content->media_description(); + ASSERT_NE(cd, nullptr); + const StreamParamsVec& streams = cd->streams(); + ASSERT_THAT(streams, SizeIs(1)); + const StreamParams& stream = streams[0]; + ASSERT_THAT(stream.ssrcs, IsEmpty()); + EXPECT_TRUE(stream.has_rids()); + const std::vector rids = stream.rids(); + + EXPECT_THAT(rids, Pointwise(RidDescriptionEquals(), send_rids)); + + ASSERT_TRUE(cd->has_receive_stream()); + const StreamParams& receive_stream = cd->receive_stream(); + EXPECT_THAT(receive_stream.ssrcs, IsEmpty()); + EXPECT_TRUE(receive_stream.has_rids()); + ASSERT_THAT(receive_stream.rids(), SizeIs(1)); + + EXPECT_EQ(receive_rid.rid, receive_stream.rids()[0].rid); + EXPECT_EQ(receive_rid.direction, receive_stream.rids()[0].direction); + + EXPECT_TRUE(cd->HasSimulcast()); + const SimulcastDescription& simulcast = cd->simulcast_description(); + EXPECT_THAT(simulcast.send_layers(), SizeIs(send_layers.size())); + EXPECT_THAT(simulcast.send_layers(), Pointwise(Eq(), send_layers)); + + ASSERT_THAT(simulcast.receive_layers().GetAllLayers(), SizeIs(1)); + EXPECT_EQ(receive_layer, simulcast.receive_layers().GetAllLayers()[0]); +} + +// Create an offer with spec-compliant simulcast video stream. +TEST_F(MediaSessionDescriptionFactoryTest, TestCreateCompliantSimulcastOffer) { + MediaSessionOptions opts; + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + RidDescription receive_rid("1", RidDirection::kReceive); + SimulcastLayer receive_layer(receive_rid.rid, false); + opts.media_description_options[0].receive_rids = {receive_rid}; + opts.media_description_options[0].receive_simulcast_layers.AddLayer( + receive_layer); + std::vector send_rids; + send_rids.push_back(RidDescription("f", RidDirection::kSend)); + send_rids.push_back(RidDescription("h", RidDirection::kSend)); + send_rids.push_back(RidDescription("q", RidDirection::kSend)); + SimulcastLayerList simulcast_layers; + simulcast_layers.AddLayer(SimulcastLayer(send_rids[0].rid, false)); + simulcast_layers.AddLayer(SimulcastLayer(send_rids[1].rid, true)); + simulcast_layers.AddLayer(SimulcastLayer(send_rids[2].rid, false)); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, + {kMediaStream1}, send_rids, + simulcast_layers, 0, &opts); + std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); + + CheckSimulcastInSessionDescription(offer.get(), "video", send_rids, + simulcast_layers, receive_rid, + receive_layer); +} + +// Create an offer that signals RIDs (not SSRCs) without Simulcast. +// In this scenario, RIDs do not need to be negotiated (there is only one). +TEST_F(MediaSessionDescriptionFactoryTest, TestOfferWithRidsNoSimulcast) { + MediaSessionOptions opts; + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + RidDescription rid("f", RidDirection::kSend); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, + {kMediaStream1}, {rid}, + SimulcastLayerList(), 0, &opts); + std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); + + ASSERT_NE(offer.get(), nullptr); + const ContentInfo* content = offer->GetContentByName("video"); + ASSERT_NE(content, nullptr); + const MediaContentDescription* cd = content->media_description(); + ASSERT_NE(cd, nullptr); + EXPECT_FALSE(cd->has_receive_stream()); + const StreamParamsVec& streams = cd->streams(); + ASSERT_THAT(streams, SizeIs(1)); + const StreamParams& stream = streams[0]; + ASSERT_THAT(stream.ssrcs, IsEmpty()); + EXPECT_FALSE(stream.has_rids()); + EXPECT_FALSE(cd->HasSimulcast()); +} + +// Create an answer with spec-compliant simulcast video stream. +// In this scenario, the SFU is the caller requesting that we send Simulcast. +TEST_F(MediaSessionDescriptionFactoryTest, TestCreateCompliantSimulcastAnswer) { + MediaSessionOptions offer_opts; + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &offer_opts); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, + {kMediaStream1}, 1, &offer_opts); + std::unique_ptr offer = + f1_.CreateOffer(offer_opts, nullptr); + + MediaSessionOptions answer_opts; + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &answer_opts); + + RidDescription receive_rid("1", RidDirection::kReceive); + SimulcastLayer receive_layer(receive_rid.rid, false); + answer_opts.media_description_options[0].receive_rids = {receive_rid}; + answer_opts.media_description_options[0].receive_simulcast_layers.AddLayer( + receive_layer); + std::vector rid_descriptions{ + RidDescription("f", RidDirection::kSend), + RidDescription("h", RidDirection::kSend), + RidDescription("q", RidDirection::kSend), + }; + SimulcastLayerList simulcast_layers; + simulcast_layers.AddLayer(SimulcastLayer(rid_descriptions[0].rid, false)); + simulcast_layers.AddLayer(SimulcastLayer(rid_descriptions[1].rid, true)); + simulcast_layers.AddLayer(SimulcastLayer(rid_descriptions[2].rid, false)); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, + {kMediaStream1}, rid_descriptions, + simulcast_layers, 0, &answer_opts); + std::unique_ptr answer = + f2_.CreateAnswer(offer.get(), answer_opts, nullptr); + + CheckSimulcastInSessionDescription(answer.get(), "video", rid_descriptions, + simulcast_layers, receive_rid, + receive_layer); +} + +// Create an answer that signals RIDs (not SSRCs) without Simulcast. +// In this scenario, RIDs do not need to be negotiated (there is only one). +// Note that RID Direction is not the same as the transceiver direction. +TEST_F(MediaSessionDescriptionFactoryTest, TestAnswerWithRidsNoSimulcast) { + MediaSessionOptions offer_opts; + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &offer_opts); + RidDescription rid_offer("f", RidDirection::kSend); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, + {kMediaStream1}, {rid_offer}, + SimulcastLayerList(), 0, &offer_opts); + std::unique_ptr offer = + f1_.CreateOffer(offer_opts, nullptr); + + MediaSessionOptions answer_opts; + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &answer_opts); + + RidDescription rid_answer("f", RidDirection::kReceive); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, + {kMediaStream1}, {rid_answer}, + SimulcastLayerList(), 0, &answer_opts); + std::unique_ptr answer = + f2_.CreateAnswer(offer.get(), answer_opts, nullptr); + + ASSERT_NE(answer.get(), nullptr); + const ContentInfo* content = offer->GetContentByName("video"); + ASSERT_NE(content, nullptr); + const MediaContentDescription* cd = content->media_description(); + ASSERT_NE(cd, nullptr); + EXPECT_FALSE(cd->has_receive_stream()); + const StreamParamsVec& streams = cd->streams(); + ASSERT_THAT(streams, SizeIs(1)); + const StreamParams& stream = streams[0]; + ASSERT_THAT(stream.ssrcs, IsEmpty()); + EXPECT_FALSE(stream.has_rids()); + EXPECT_FALSE(cd->HasSimulcast()); +} + // Create an audio and video answer to a standard video offer with: // - one video track // - two audio tracks @@ -1830,35 +2056,41 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateSimulcastVideoOffer) { // adding a new video track and removes one of the audio tracks. TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoAnswer) { MediaSessionOptions offer_opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &offer_opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly, - kActive, &offer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &offer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kRecvOnly, kActive, + &offer_opts); offer_opts.data_channel_type = cricket::DCT_RTP; - AddMediaSection(MEDIA_TYPE_DATA, "data", RtpTransceiverDirection::kRecvOnly, - kActive, &offer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_DATA, "data", + RtpTransceiverDirection::kRecvOnly, kActive, + &offer_opts); f1_.set_secure(SEC_ENABLED); f2_.set_secure(SEC_ENABLED); std::unique_ptr offer = f1_.CreateOffer(offer_opts, NULL); MediaSessionOptions answer_opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kSendRecv, - kActive, &answer_opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, - kActive, &answer_opts); - AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack1, - {kMediaStream1}, 1, &answer_opts); - AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1, - {kMediaStream1}, 1, &answer_opts); - AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack2, - {kMediaStream1}, 1, &answer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kSendRecv, kActive, + &answer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &answer_opts); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack1, + {kMediaStream1}, 1, &answer_opts); + AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack1, + {kMediaStream1}, 1, &answer_opts); + AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, kAudioTrack2, + {kMediaStream1}, 1, &answer_opts); - AddMediaSection(MEDIA_TYPE_DATA, "data", RtpTransceiverDirection::kSendRecv, - kActive, &answer_opts); - AttachSenderToMediaSection("data", MEDIA_TYPE_DATA, kDataTrack1, - {kMediaStream1}, 1, &answer_opts); - AttachSenderToMediaSection("data", MEDIA_TYPE_DATA, kDataTrack2, - {kMediaStream1}, 1, &answer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_DATA, "data", + RtpTransceiverDirection::kSendRecv, kActive, + &answer_opts); + AttachSenderToMediaDescriptionOptions("data", MEDIA_TYPE_DATA, kDataTrack1, + {kMediaStream1}, 1, &answer_opts); + AttachSenderToMediaDescriptionOptions("data", MEDIA_TYPE_DATA, kDataTrack2, + {kMediaStream1}, 1, &answer_opts); answer_opts.data_channel_type = cricket::DCT_RTP; std::unique_ptr answer = @@ -1923,8 +2155,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestCreateMultiStreamVideoAnswer) { // Update the answer. Add a new video track that is not synched to the // other tracks and remove 1 audio track. - AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, kVideoTrack2, - {kMediaStream2}, 1, &answer_opts); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, kVideoTrack2, + {kMediaStream2}, 1, &answer_opts); DetachSenderFromMediaSection("audio", kAudioTrack2, &answer_opts); DetachSenderFromMediaSection("data", kDataTrack2, &answer_opts); std::unique_ptr updated_answer( @@ -2030,8 +2262,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, f2_.set_video_codecs({}); MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "a0", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "a0", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); std::unique_ptr answer = f2_.CreateAnswer(offer.get(), opts, nullptr); @@ -2056,8 +2289,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, f2_.set_audio_codecs({}, {}); MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "v0", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "v0", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); auto answer = f2_.CreateAnswer(offer.get(), opts, nullptr); @@ -2083,8 +2317,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, // Perform initial offer/answer in reverse (|f2_| as offerer) so that the // second offer/answer is forward (|f1_| as offerer). MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "a0", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "a0", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); std::unique_ptr offer = f2_.CreateOffer(opts, nullptr); std::unique_ptr answer = f1_.CreateAnswer(offer.get(), opts, nullptr); @@ -2113,8 +2348,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, // Perform initial offer/answer in reverse (|f2_| as offerer) so that the // second offer/answer is forward (|f1_| as offerer). MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "v0", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "v0", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); std::unique_ptr offer = f2_.CreateOffer(opts, nullptr); std::unique_ptr answer = f1_.CreateAnswer(offer.get(), opts, nullptr); @@ -2139,8 +2375,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, RespondentCreatesOfferAfterCreatingAnswerWithRtx) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates rtx for H264 with the payload type |f1_| uses. AddRtxCodec(VideoCodec::CreateRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs); @@ -2188,8 +2425,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, RespondentCreatesOfferAfterCreatingAnswerWithRemappedRtxPayloadType) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); // We specifically choose different preferred payload types for VP8 to // trigger the issue. cricket::VideoCodec vp8_offerer(100, "VP8"); @@ -2246,8 +2484,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, f1_.set_video_codecs(f1_codecs); MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::unique_ptr offer = f1_.CreateOffer(opts, NULL); std::unique_ptr answer = @@ -2336,8 +2575,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, // Test that RTX is ignored when there is no associated payload type parameter. TEST_F(MediaSessionDescriptionFactoryTest, RtxWithoutApt) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates RTX without associated payload type parameter. AddRtxCodec(VideoCodec(126, cricket::kRtxCodecName), &f1_codecs); @@ -2379,8 +2619,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, RtxWithoutApt) { // type doesn't match the local value. TEST_F(MediaSessionDescriptionFactoryTest, FilterOutRtxIfAptDoesntMatch) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates RTX for H264 in sender. AddRtxCodec(VideoCodec::CreateRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs); @@ -2409,8 +2650,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, FilterOutRtxIfAptDoesntMatch) { TEST_F(MediaSessionDescriptionFactoryTest, FilterOutUnsupportedRtxWhenCreatingAnswer) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates RTX for H264-SVC in sender. AddRtxCodec(VideoCodec::CreateRtxCodec(125, kVideoCodecs1[0].id), &f1_codecs); @@ -2444,8 +2686,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, // to add another. TEST_F(MediaSessionDescriptionFactoryTest, AddSecondRtxInNewOffer) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); std::vector f1_codecs = MAKE_VECTOR(kVideoCodecs1); // This creates RTX for H264 for the offerer. AddRtxCodec(VideoCodec::CreateRtxCodec(126, kVideoCodecs1[1].id), &f1_codecs); @@ -2479,11 +2722,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, AddSecondRtxInNewOffer) { // generated for each simulcast ssrc and correctly grouped. TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateMultipleRtxSsrcs) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); // Add simulcast streams. - AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, "stream1", - {"stream1label"}, 3, &opts); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, "stream1", + {"stream1label"}, 3, &opts); // Use a single real codec, and then add RTX for it. std::vector f1_codecs; @@ -2520,11 +2764,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateMultipleRtxSsrcs) { // together with a FEC-FR grouping. TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); // Add single stream. - AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, "stream1", - {"stream1label"}, 1, &opts); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, "stream1", + {"stream1label"}, 1, &opts); // Use a single real codec, and then add FlexFEC for it. std::vector f1_codecs; @@ -2560,11 +2805,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, GenerateFlexfecSsrc) { // multiple FlexfecSenders, or through multistream protection. TEST_F(MediaSessionDescriptionFactoryTest, SimSsrcsGenerateNoFlexfecSsrcs) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); // Add simulcast streams. - AttachSenderToMediaSection("video", MEDIA_TYPE_VIDEO, "stream1", - {"stream1label"}, 3, &opts); + AttachSenderToMediaDescriptionOptions("video", MEDIA_TYPE_VIDEO, "stream1", + {"stream1label"}, 3, &opts); // Use a single real codec, and then add FlexFEC for it. std::vector f1_codecs; @@ -2767,16 +3013,18 @@ TEST(MediaSessionDescription, CopySessionDescription) { // ensure the TransportInfo in the SessionDescription matches what we expect. TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferAudio) { MediaSessionOptions options; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &options); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &options); TestTransportInfo(true, options, false); } TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferIceRenomination) { MediaSessionOptions options; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &options); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &options); options.media_description_options[0] .transport_options.enable_ice_renomination = true; TestTransportInfo(true, options, false); @@ -2784,8 +3032,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoOfferAudioCurrent) { MediaSessionOptions options; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &options); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &options); TestTransportInfo(true, options, true); } @@ -2827,16 +3076,18 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerAudio) { MediaSessionOptions options; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &options); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &options); TestTransportInfo(false, options, false); } TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerIceRenomination) { MediaSessionOptions options; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &options); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &options); options.media_description_options[0] .transport_options.enable_ice_renomination = true; TestTransportInfo(false, options, false); @@ -2845,8 +3096,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, TestTransportInfoAnswerAudioCurrent) { MediaSessionOptions options; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kRecvOnly, - kActive, &options); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kRecvOnly, kActive, + &options); TestTransportInfo(false, options, true); } @@ -3139,13 +3391,16 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestVADEnableOption) { // Test that the generated MIDs match the existing offer. TEST_F(MediaSessionDescriptionFactoryTest, TestMIDsMatchesExistingOffer) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio_modified", - RtpTransceiverDirection::kRecvOnly, kActive, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video_modified", - RtpTransceiverDirection::kRecvOnly, kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio_modified", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video_modified", + RtpTransceiverDirection::kRecvOnly, kActive, + &opts); opts.data_channel_type = cricket::DCT_SCTP; - AddMediaSection(MEDIA_TYPE_DATA, "data_modified", - RtpTransceiverDirection::kSendRecv, kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_DATA, "data_modified", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); // Create offer. std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); std::unique_ptr updated_offer( @@ -3168,25 +3423,29 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestMIDsMatchesExistingOffer) { TEST_F(MediaSessionDescriptionFactoryTest, CreateOfferWithMultipleAVMediaSections) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio_1", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AttachSenderToMediaSection("audio_1", MEDIA_TYPE_AUDIO, kAudioTrack1, - {kMediaStream1}, 1, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio_1", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AttachSenderToMediaDescriptionOptions( + "audio_1", MEDIA_TYPE_AUDIO, kAudioTrack1, {kMediaStream1}, 1, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video_1", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AttachSenderToMediaSection("video_1", MEDIA_TYPE_VIDEO, kVideoTrack1, - {kMediaStream1}, 1, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video_1", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AttachSenderToMediaDescriptionOptions( + "video_1", MEDIA_TYPE_VIDEO, kVideoTrack1, {kMediaStream1}, 1, &opts); - AddMediaSection(MEDIA_TYPE_AUDIO, "audio_2", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AttachSenderToMediaSection("audio_2", MEDIA_TYPE_AUDIO, kAudioTrack2, - {kMediaStream2}, 1, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio_2", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AttachSenderToMediaDescriptionOptions( + "audio_2", MEDIA_TYPE_AUDIO, kAudioTrack2, {kMediaStream2}, 1, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video_2", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AttachSenderToMediaSection("video_2", MEDIA_TYPE_VIDEO, kVideoTrack2, - {kMediaStream2}, 1, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video_2", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AttachSenderToMediaDescriptionOptions( + "video_2", MEDIA_TYPE_VIDEO, kVideoTrack2, {kMediaStream2}, 1, &opts); std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); ASSERT_TRUE(offer); @@ -3223,25 +3482,29 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerWithMultipleAVMediaSections) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio_1", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AttachSenderToMediaSection("audio_1", MEDIA_TYPE_AUDIO, kAudioTrack1, - {kMediaStream1}, 1, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio_1", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AttachSenderToMediaDescriptionOptions( + "audio_1", MEDIA_TYPE_AUDIO, kAudioTrack1, {kMediaStream1}, 1, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video_1", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AttachSenderToMediaSection("video_1", MEDIA_TYPE_VIDEO, kVideoTrack1, - {kMediaStream1}, 1, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video_1", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AttachSenderToMediaDescriptionOptions( + "video_1", MEDIA_TYPE_VIDEO, kVideoTrack1, {kMediaStream1}, 1, &opts); - AddMediaSection(MEDIA_TYPE_AUDIO, "audio_2", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AttachSenderToMediaSection("audio_2", MEDIA_TYPE_AUDIO, kAudioTrack2, - {kMediaStream2}, 1, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio_2", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AttachSenderToMediaDescriptionOptions( + "audio_2", MEDIA_TYPE_AUDIO, kAudioTrack2, {kMediaStream2}, 1, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video_2", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AttachSenderToMediaSection("video_2", MEDIA_TYPE_VIDEO, kVideoTrack2, - {kMediaStream2}, 1, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video_2", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AttachSenderToMediaDescriptionOptions( + "video_2", MEDIA_TYPE_VIDEO, kVideoTrack2, {kMediaStream2}, 1, &opts); std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); ASSERT_TRUE(offer); @@ -3282,10 +3545,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, CreateOfferWithMediaSectionStoppedByOfferer) { // Create an offer with two audio sections and one of them is stopped. MediaSessionOptions offer_opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio1", - RtpTransceiverDirection::kSendRecv, kActive, &offer_opts); - AddMediaSection(MEDIA_TYPE_AUDIO, "audio2", - RtpTransceiverDirection::kInactive, kStopped, &offer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio1", + RtpTransceiverDirection::kSendRecv, kActive, + &offer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio2", + RtpTransceiverDirection::kInactive, kStopped, + &offer_opts); std::unique_ptr offer = f1_.CreateOffer(offer_opts, nullptr); ASSERT_TRUE(offer); @@ -3300,10 +3565,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerWithMediaSectionStoppedByOfferer) { // Create an offer with two audio sections and one of them is stopped. MediaSessionOptions offer_opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio1", - RtpTransceiverDirection::kSendRecv, kActive, &offer_opts); - AddMediaSection(MEDIA_TYPE_AUDIO, "audio2", - RtpTransceiverDirection::kInactive, kStopped, &offer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio1", + RtpTransceiverDirection::kSendRecv, kActive, + &offer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio2", + RtpTransceiverDirection::kInactive, kStopped, + &offer_opts); std::unique_ptr offer = f1_.CreateOffer(offer_opts, nullptr); ASSERT_TRUE(offer); @@ -3313,10 +3580,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, // Create an answer based on the offer. MediaSessionOptions answer_opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio1", - RtpTransceiverDirection::kSendRecv, kActive, &answer_opts); - AddMediaSection(MEDIA_TYPE_AUDIO, "audio2", - RtpTransceiverDirection::kSendRecv, kActive, &answer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio1", + RtpTransceiverDirection::kSendRecv, kActive, + &answer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio2", + RtpTransceiverDirection::kSendRecv, kActive, + &answer_opts); std::unique_ptr answer = f2_.CreateAnswer(offer.get(), answer_opts, nullptr); ASSERT_EQ(2u, answer->contents().size()); @@ -3330,10 +3599,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerWithMediaSectionRejectedByAnswerer) { // Create an offer with two audio sections. MediaSessionOptions offer_opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio1", - RtpTransceiverDirection::kSendRecv, kActive, &offer_opts); - AddMediaSection(MEDIA_TYPE_AUDIO, "audio2", - RtpTransceiverDirection::kSendRecv, kActive, &offer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio1", + RtpTransceiverDirection::kSendRecv, kActive, + &offer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio2", + RtpTransceiverDirection::kSendRecv, kActive, + &offer_opts); std::unique_ptr offer = f1_.CreateOffer(offer_opts, nullptr); ASSERT_TRUE(offer); @@ -3343,10 +3614,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, // The answerer rejects one of the audio sections. MediaSessionOptions answer_opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio1", - RtpTransceiverDirection::kSendRecv, kActive, &answer_opts); - AddMediaSection(MEDIA_TYPE_AUDIO, "audio2", - RtpTransceiverDirection::kInactive, kStopped, &answer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio1", + RtpTransceiverDirection::kSendRecv, kActive, + &answer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio2", + RtpTransceiverDirection::kInactive, kStopped, + &answer_opts); std::unique_ptr answer = f2_.CreateAnswer(offer.get(), answer_opts, nullptr); ASSERT_EQ(2u, answer->contents().size()); @@ -3365,10 +3638,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, MediaSessionOptions opts; // This tests put video section first because normally audio comes first by // default. - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, - kActive, &opts); - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); ASSERT_TRUE(offer); @@ -3382,10 +3657,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, PayloadTypesSharedByMediaSectionsOfSameType) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video1", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video2", - RtpTransceiverDirection::kSendRecv, kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video2", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); // Create an offer with two video sections using same codecs. std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); ASSERT_TRUE(offer); @@ -3419,10 +3696,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, CreateOfferRespectsCodecPreferenceOrder) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video1", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video2", - RtpTransceiverDirection::kSendRecv, kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video2", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); // Create an offer with two video sections using same codecs. std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); ASSERT_TRUE(offer); @@ -3453,10 +3732,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerRespectsCodecPreferenceOrder) { MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video1", - RtpTransceiverDirection::kSendRecv, kActive, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video2", - RtpTransceiverDirection::kSendRecv, kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video1", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video2", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); // Create an offer with two video sections using same codecs. std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); ASSERT_TRUE(offer); @@ -3509,10 +3790,12 @@ TEST_F(MediaSessionDescriptionFactoryTest, CreateAnswerWithLocalCodecParams) { f2_.set_video_codecs(video_codecs2); MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", RtpTransceiverDirection::kSendRecv, - kActive, &opts); - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); ASSERT_TRUE(offer); @@ -3558,8 +3841,9 @@ TEST_F(MediaSessionDescriptionFactoryTest, f2_.set_video_codecs({h264_pm1}); MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kSendRecv, - kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video", + RtpTransceiverDirection::kSendRecv, kActive, + &opts); std::unique_ptr offer = f1_.CreateOffer(opts, nullptr); ASSERT_TRUE(offer); @@ -3764,12 +4048,13 @@ void TestAudioCodecsOffer(RtpTransceiverDirection direction) { sf.set_audio_codecs(send_codecs, recv_codecs); MediaSessionOptions opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", direction, kActive, &opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", direction, kActive, + &opts); if (direction == RtpTransceiverDirection::kSendRecv || direction == RtpTransceiverDirection::kSendOnly) { - AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1, - {kMediaStream1}, 1, &opts); + AttachSenderToMediaDescriptionOptions( + "audio", MEDIA_TYPE_AUDIO, kAudioTrack1, {kMediaStream1}, 1, &opts); } std::unique_ptr offer = sf.CreateOffer(opts, NULL); @@ -3862,12 +4147,13 @@ void TestAudioCodecsAnswer(RtpTransceiverDirection offer_direction, VectorFromIndices(kOfferAnswerCodecs, kAnswerRecvCodecs)); MediaSessionOptions offer_opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", offer_direction, kActive, - &offer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", offer_direction, + kActive, &offer_opts); if (webrtc::RtpTransceiverDirectionHasSend(offer_direction)) { - AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1, - {kMediaStream1}, 1, &offer_opts); + AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, + kAudioTrack1, {kMediaStream1}, 1, + &offer_opts); } std::unique_ptr offer = @@ -3875,12 +4161,13 @@ void TestAudioCodecsAnswer(RtpTransceiverDirection offer_direction, ASSERT_TRUE(offer.get() != NULL); MediaSessionOptions answer_opts; - AddMediaSection(MEDIA_TYPE_AUDIO, "audio", answer_direction, kActive, - &answer_opts); + AddMediaDescriptionOptions(MEDIA_TYPE_AUDIO, "audio", answer_direction, + kActive, &answer_opts); if (webrtc::RtpTransceiverDirectionHasSend(answer_direction)) { - AttachSenderToMediaSection("audio", MEDIA_TYPE_AUDIO, kAudioTrack1, - {kMediaStream1}, 1, &answer_opts); + AttachSenderToMediaDescriptionOptions("audio", MEDIA_TYPE_AUDIO, + kAudioTrack1, {kMediaStream1}, 1, + &answer_opts); } std::unique_ptr answer = answer_factory.CreateAnswer(offer.get(), answer_opts, NULL); diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index a70ab2e95a..7f7cebd9fd 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -58,8 +58,9 @@ using cricket::ContentInfo; using cricket::ContentInfos; using cricket::MediaContentDescription; -using cricket::SessionDescription; using cricket::MediaProtocolType; +using cricket::SessionDescription; +using cricket::SimulcastLayerList; using cricket::TransportInfo; using cricket::LOCAL_PORT_TYPE; @@ -170,7 +171,7 @@ bool IsValidOfferToReceiveMedia(int value) { } // Add options to |[audio/video]_media_description_options| from |senders|. -void AddRtpSenderOptions( +void AddPlanBRtpSenderOptions( const std::vector>>& senders, cricket::MediaDescriptionOptions* audio_media_description_options, @@ -186,8 +187,8 @@ void AddRtpSenderOptions( RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO); if (video_media_description_options) { video_media_description_options->AddVideoSender( - sender->id(), sender->internal()->stream_ids(), - num_sim_layers); + sender->id(), sender->internal()->stream_ids(), {}, + SimulcastLayerList(), num_sim_layers); } } } @@ -4014,9 +4015,10 @@ void PeerConnection::GetOptionsForPlanBOffer( !video_index ? nullptr : &session_options->media_description_options[*video_index]; - AddRtpSenderOptions(GetSendersInternal(), audio_media_description_options, - video_media_description_options, - offer_answer_options.num_simulcast_layers); + AddPlanBRtpSenderOptions(GetSendersInternal(), + audio_media_description_options, + video_media_description_options, + offer_answer_options.num_simulcast_layers); } static cricket::MediaDescriptionOptions @@ -4241,9 +4243,10 @@ void PeerConnection::GetOptionsForPlanBAnswer( !video_index ? nullptr : &session_options->media_description_options[*video_index]; - AddRtpSenderOptions(GetSendersInternal(), audio_media_description_options, - video_media_description_options, - offer_answer_options.num_simulcast_layers); + AddPlanBRtpSenderOptions(GetSendersInternal(), + audio_media_description_options, + video_media_description_options, + offer_answer_options.num_simulcast_layers); } void PeerConnection::GetOptionsForUnifiedPlanAnswer( diff --git a/pc/simulcastdescription.cc b/pc/simulcastdescription.cc index 8fff520fa0..61849ce5dc 100644 --- a/pc/simulcastdescription.cc +++ b/pc/simulcastdescription.cc @@ -20,6 +20,10 @@ SimulcastLayer::SimulcastLayer(const std::string& rid, bool is_paused) RTC_DCHECK(!rid.empty()); } +bool SimulcastLayer::operator==(const SimulcastLayer& other) const { + return rid == other.rid && is_paused == other.is_paused; +} + void SimulcastLayerList::AddLayer(const SimulcastLayer& layer) { list_.push_back({layer}); } diff --git a/pc/simulcastdescription.h b/pc/simulcastdescription.h index 9f15f78f43..0781a6a283 100644 --- a/pc/simulcastdescription.h +++ b/pc/simulcastdescription.h @@ -25,6 +25,7 @@ struct SimulcastLayer final { SimulcastLayer(const SimulcastLayer& other) = default; SimulcastLayer& operator=(const SimulcastLayer& other) = default; + bool operator==(const SimulcastLayer& other) const; std::string rid; bool is_paused; @@ -48,6 +49,12 @@ struct SimulcastLayer final { // {SimulcastLayer("4", false), SimulcastLayer("5", false}); class SimulcastLayerList final { public: + // Type definitions required by a container. + typedef size_t size_type; + typedef std::vector value_type; + typedef std::vector>::const_iterator + const_iterator; + // Use to add a layer when there will be no alternatives. void AddLayer(const SimulcastLayer& layer); @@ -57,13 +64,9 @@ class SimulcastLayerList final { // Read-only access to the contents. // Note: This object does not allow removal of layers. - std::vector>::const_iterator begin() const { - return list_.begin(); - } + const_iterator begin() const { return list_.begin(); } - std::vector>::const_iterator end() const { - return list_.end(); - } + const_iterator end() const { return list_.end(); } const std::vector& operator[](size_t index) const; diff --git a/pc/unique_id_generator.cc b/pc/unique_id_generator.cc new file mode 100644 index 0000000000..b381d44e22 --- /dev/null +++ b/pc/unique_id_generator.cc @@ -0,0 +1,64 @@ +/* + * Copyright 2018 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/unique_id_generator.h" + +#include +#include + +#include "rtc_base/helpers.h" +#include "rtc_base/string_to_number.h" +#include "rtc_base/stringencode.h" + +namespace webrtc { + +namespace { +UniqueNumberGenerator CreateUniqueNumberGenerator( + rtc::ArrayView known_ids) { + std::vector known_ints; + for (const std::string& str : known_ids) { + absl::optional value = rtc::StringToNumber(str); + if (value.has_value()) { + known_ints.push_back(value.value()); + } + } + return UniqueNumberGenerator(known_ints); +} +} // namespace + +UniqueRandomIdGenerator::UniqueRandomIdGenerator() : known_ids_() {} +UniqueRandomIdGenerator::UniqueRandomIdGenerator( + rtc::ArrayView known_ids) + : known_ids_(known_ids.begin(), known_ids.end()) {} + +UniqueRandomIdGenerator::~UniqueRandomIdGenerator() = default; + +uint32_t UniqueRandomIdGenerator::GenerateId() { + while (true) { + RTC_CHECK_LT(known_ids_.size(), std::numeric_limits::max()); + auto pair = known_ids_.insert(rtc::CreateRandomNonZeroId()); + if (pair.second) { + return *pair.first; + } + } +} + +UniqueStringGenerator::UniqueStringGenerator() : unique_number_generator_() {} +UniqueStringGenerator::UniqueStringGenerator( + rtc::ArrayView known_ids) + : unique_number_generator_(CreateUniqueNumberGenerator(known_ids)) {} + +UniqueStringGenerator::~UniqueStringGenerator() = default; + +std::string UniqueStringGenerator::GenerateString() { + return rtc::ToString(unique_number_generator_.GenerateNumber()); +} + +} // namespace webrtc diff --git a/pc/unique_id_generator.h b/pc/unique_id_generator.h new file mode 100644 index 0000000000..15ba79446b --- /dev/null +++ b/pc/unique_id_generator.h @@ -0,0 +1,122 @@ +/* + * Copyright 2018 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_UNIQUE_ID_GENERATOR_H_ +#define PC_UNIQUE_ID_GENERATOR_H_ + +#include +#include +#include + +#include "api/array_view.h" + +namespace webrtc { + +// This class will generate numbers. A common use case is for identifiers. +// The generated numbers will be unique, in the local scope of the generator. +// This means that a generator will never generate the same number twice. +// The generator can also be initialized with a sequence of known ids. +// In such a case, it will never generate an id from that list. +// Recommendedations: +// * Prefer unsigned types. +// * Prefer larger types (uint8_t will run out quickly). +template +class UniqueNumberGenerator { + public: + typedef TIntegral value_type; + UniqueNumberGenerator(); + // Creates a generator that will never return any value from the given list. + explicit UniqueNumberGenerator(rtc::ArrayView known_ids); + ~UniqueNumberGenerator(); + + // Generates a number that this generator has never produced before. + // If there are no available numbers to generate, this method will fail + // with an |RTC_CHECK|. + TIntegral GenerateNumber(); + TIntegral operator()() { return GenerateNumber(); } + + private: + static_assert(std::is_integral::value, "Must be integral type."); + TIntegral counter_; + // This class can be further optimized by removing the known_ids_ set when + // the generator was created without a sequence of ids to ignore. + // In such a case, the implementation uses a counter which is sufficient to + // prevent repetitions of the generated values. + std::set known_ids_; +}; + +// This class will generate unique ids. Ids are 32 bit unsigned integers. +// The generated ids will be unique, in the local scope of the generator. +// This means that a generator will never generate the same id twice. +// The generator can also be initialized with a sequence of known ids. +// In such a case, it will never generate an id from that list. +class UniqueRandomIdGenerator { + public: + typedef uint32_t value_type; + UniqueRandomIdGenerator(); + // Create a generator that will never return any value from the given list. + explicit UniqueRandomIdGenerator(rtc::ArrayView known_ids); + ~UniqueRandomIdGenerator(); + + // Generates a random id that this generator has never produced before. + // This method becomes more expensive with each use, as the probability of + // collision for the randomly generated numbers increases. + uint32_t GenerateId(); + uint32_t operator()() { return GenerateId(); } + + private: + std::set known_ids_; +}; + +// This class will generate strings. A common use case is for identifiers. +// The generated strings will be unique, in the local scope of the generator. +// This means that a generator will never generate the same string twice. +// The generator can also be initialized with a sequence of known ids. +// In such a case, it will never generate an id from that list. +class UniqueStringGenerator { + public: + typedef std::string value_type; + UniqueStringGenerator(); + explicit UniqueStringGenerator(rtc::ArrayView known_ids); + ~UniqueStringGenerator(); + + std::string GenerateString(); + std::string operator()() { return GenerateString(); } + + private: + // This implementation will be simple and will generate "0", "1", ... + UniqueNumberGenerator unique_number_generator_; +}; + +template +UniqueNumberGenerator::UniqueNumberGenerator() : counter_(0) {} + +template +UniqueNumberGenerator::UniqueNumberGenerator( + rtc::ArrayView known_ids) + : counter_(0), known_ids_(known_ids.begin(), known_ids.end()) {} + +template +UniqueNumberGenerator::~UniqueNumberGenerator() {} + +template +TIntegral UniqueNumberGenerator::GenerateNumber() { + while (true) { + RTC_CHECK_LT(counter_, std::numeric_limits::max()); + auto pair = known_ids_.insert(counter_++); + if (pair.second) { + return *pair.first; + } + } +} + +} // namespace webrtc + +#endif // PC_UNIQUE_ID_GENERATOR_H_ diff --git a/pc/unique_id_generator_unittest.cc b/pc/unique_id_generator_unittest.cc new file mode 100644 index 0000000000..86d831b9ae --- /dev/null +++ b/pc/unique_id_generator_unittest.cc @@ -0,0 +1,79 @@ +/* + * Copyright 2018 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 "algorithm" +#include "string" +#include "vector" + +#include "api/array_view.h" +#include "pc/unique_id_generator.h" +#include "rtc_base/gunit.h" +#include "rtc_base/helpers.h" +#include "test/gmock.h" + +using ::testing::IsEmpty; +using ::testing::Test; + +namespace webrtc { + +template +class UniqueIdGeneratorTest : public Test {}; + +using test_types = ::testing::Types, + UniqueNumberGenerator, + UniqueNumberGenerator, + UniqueRandomIdGenerator, + UniqueStringGenerator>; + +TYPED_TEST_CASE(UniqueIdGeneratorTest, test_types); + +TYPED_TEST(UniqueIdGeneratorTest, ElementsDoNotRepeat) { + typedef TypeParam Generator; + const size_t num_elements = 255; + Generator generator; + std::vector values; + for (size_t i = 0; i < num_elements; i++) { + values.push_back(generator()); + } + + EXPECT_EQ(num_elements, values.size()); + // Use a set to check uniqueness. + std::set set(values.begin(), values.end()); + EXPECT_EQ(values.size(), set.size()) << "Returned values were not unique."; +} + +TYPED_TEST(UniqueIdGeneratorTest, KnownElementsAreNotGenerated) { + typedef TypeParam Generator; + const size_t num_elements = 100; + rtc::InitRandom(0); + Generator generator1; + std::vector known_values; + for (size_t i = 0; i < num_elements; i++) { + known_values.push_back(generator1()); + } + EXPECT_EQ(num_elements, known_values.size()); + + rtc::InitRandom(0); + Generator generator2(known_values); + + std::vector values; + for (size_t i = 0; i < num_elements; i++) { + values.push_back(generator2()); + } + EXPECT_THAT(values, ::testing::SizeIs(num_elements)); + std::sort(values.begin(), values.end()); + std::sort(known_values.begin(), known_values.end()); + std::vector intersection; + std::set_intersection(values.begin(), values.end(), known_values.begin(), + known_values.end(), std::back_inserter(intersection)); + EXPECT_THAT(intersection, IsEmpty()); +} + +} // namespace webrtc