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 <shampson@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Commit-Queue: Amit Hilbuch <amithi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26115}
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 2a07e71..268176c 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -66,6 +66,8 @@
"srtptransport.h",
"transportstats.cc",
"transportstats.h",
+ "unique_id_generator.cc",
+ "unique_id_generator.h",
]
deps = [
@@ -90,6 +92,7 @@
"../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 @@
"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 4898ed9..4f7fae1 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 @@
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<uint32_t>* 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<const ContentInfo*>& active_local_contents) {
@@ -401,6 +387,135 @@
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<uint32_t> 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<uint32_t> 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<RidDescription>& rids,
+ const SimulcastLayerList& simulcast_layers) {
+ std::vector<SimulcastLayer> 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 @@
GetStreamByIds(*current_streams, "" /*group_id*/, sender.track_id);
if (!param) {
// This is a new sender.
- std::vector<uint32_t> 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<uint32_t> rtx_ssrcs;
- GenerateSsrcs(*current_streams, static_cast<int>(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<uint32_t> 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 @@
// offer.
template <class C>
static bool CreateMediaContentOffer(
- const std::vector<SenderOptions>& sender_options,
+ const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const std::vector<C>& codecs,
const SecurePolicy& secure_policy,
@@ -709,11 +797,13 @@
}
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 @@
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 @@
const std::string& track_id,
const std::vector<std::string>& 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<std::string>& stream_ids,
+ const std::vector<RidDescription>& 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 @@
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<std::string>& stream_ids,
+ const std::vector<RidDescription>& 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 @@
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 @@
}
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 @@
// 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 d5c26f4..f732cb2 100644
--- a/pc/mediasession.h
+++ b/pc/mediasession.h
@@ -35,9 +35,14 @@
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<std::string> stream_ids;
+ // Use RIDs and Simulcast Layers to indicate spec-compliant Simulcast.
+ std::vector<RidDescription> rids;
+ SimulcastLayerList simulcast_layers;
+ // Use |num_sim_layers| to indicate legacy simulcast.
int num_sim_layers;
};
@@ -55,6 +60,8 @@
const std::vector<std::string>& stream_ids);
void AddVideoSender(const std::string& track_id,
const std::vector<std::string>& stream_ids,
+ const std::vector<RidDescription>& rids,
+ const SimulcastLayerList& simulcast_layers,
int num_sim_layers);
// Internally just uses sender_options.
@@ -69,11 +76,22 @@
// Note: There's no equivalent "RtpReceiverOptions" because only send
// stream information goes in the local descriptions.
std::vector<SenderOptions> 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<RidDescription> receive_rids;
+ SimulcastLayerList receive_simulcast_layers;
private:
// Doesn't DCHECK on |type|.
void AddSenderInternal(const std::string& track_id,
const std::vector<std::string>& stream_ids,
+ const std::vector<RidDescription>& rids,
+ const SimulcastLayerList& simulcast_layers,
int num_sim_layers);
};
diff --git a/pc/mediasession_unittest.cc b/pc/mediasession_unittest.cc
index 2af14d2..16be74f 100644
--- a/pc/mediasession_unittest.cc
+++ b/pc/mediasession_unittest.cc
@@ -36,45 +36,58 @@
typedef std::vector<cricket::Candidate> 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* 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 @@
}
// 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<std::string>& stream_ids,
+ const std::vector<RidDescription>& rids,
+ const SimulcastLayerList& simulcast_layers,
int num_sim_layer,
MediaSessionOptions* session_options) {
auto it = FindFirstMediaDescriptionByMid(mid, session_options);
@@ -311,7 +328,8 @@
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 AttachSenderToMediaDescriptionOptions(
+ const std::string& mid,
+ MediaType type,
+ const std::string& track_id,
+ const std::vector<std::string>& 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 @@
// (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 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
@@ -879,10 +912,10 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
ASSERT_TRUE(offer.get() != NULL);
@@ -907,8 +940,9 @@
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<SessionDescription> offer2(
f1_.CreateOffer(opts, offer1.get()));
ASSERT_TRUE(offer2.get() != NULL);
@@ -916,8 +950,9 @@
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<SessionDescription> offer3(
f1_.CreateOffer(opts, offer2.get()));
ASSERT_TRUE(offer3.get() != NULL);
@@ -1176,15 +1211,17 @@
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<SessionDescription> 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<SessionDescription> offer3(
f1_.CreateOffer(opts, offer2.get()));
ASSERT_TRUE(offer3.get() != NULL);
@@ -1517,10 +1554,12 @@
// 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<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
ASSERT_TRUE(offer.get() != NULL);
@@ -1539,8 +1578,9 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
ASSERT_TRUE(offer.get() != NULL);
@@ -1655,18 +1695,18 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
@@ -1730,14 +1770,14 @@
// 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<SessionDescription> updated_offer(
f1_.CreateOffer(opts, offer.get()));
@@ -1799,13 +1839,15 @@
// 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<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
ASSERT_TRUE(offer.get() != NULL);
@@ -1822,6 +1864,190 @@
EXPECT_EQ(static_cast<size_t>(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<RidDescription>& 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<RidDescription> 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<RidDescription> 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<SessionDescription> 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<SessionDescription> 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<SessionDescription> 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<RidDescription> 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<SessionDescription> 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<SessionDescription> 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<SessionDescription> 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 @@
// 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<SessionDescription> 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<SessionDescription> answer =
@@ -1923,8 +2155,8 @@
// 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<SessionDescription> updated_answer(
@@ -2030,8 +2262,9 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
std::unique_ptr<SessionDescription> answer =
f2_.CreateAnswer(offer.get(), opts, nullptr);
@@ -2056,8 +2289,9 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
auto answer = f2_.CreateAnswer(offer.get(), opts, nullptr);
@@ -2083,8 +2317,9 @@
// 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<SessionDescription> offer = f2_.CreateOffer(opts, nullptr);
std::unique_ptr<SessionDescription> answer =
f1_.CreateAnswer(offer.get(), opts, nullptr);
@@ -2113,8 +2348,9 @@
// 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<SessionDescription> offer = f2_.CreateOffer(opts, nullptr);
std::unique_ptr<SessionDescription> answer =
f1_.CreateAnswer(offer.get(), opts, nullptr);
@@ -2139,8 +2375,9 @@
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<VideoCodec> 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,
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 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
std::unique_ptr<SessionDescription> answer =
@@ -2336,8 +2575,9 @@
// 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<VideoCodec> f1_codecs = MAKE_VECTOR(kVideoCodecs1);
// This creates RTX without associated payload type parameter.
AddRtxCodec(VideoCodec(126, cricket::kRtxCodecName), &f1_codecs);
@@ -2379,8 +2619,9 @@
// 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<VideoCodec> 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,
FilterOutUnsupportedRtxWhenCreatingAnswer) {
MediaSessionOptions opts;
- AddMediaSection(MEDIA_TYPE_VIDEO, "video", RtpTransceiverDirection::kRecvOnly,
- kActive, &opts);
+ AddMediaDescriptionOptions(MEDIA_TYPE_VIDEO, "video",
+ RtpTransceiverDirection::kRecvOnly, kActive,
+ &opts);
std::vector<VideoCodec> 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 @@
// 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<VideoCodec> 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 @@
// 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<VideoCodec> f1_codecs;
@@ -2520,11 +2764,12 @@
// 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<VideoCodec> f1_codecs;
@@ -2560,11 +2805,12 @@
// 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<VideoCodec> f1_codecs;
@@ -2767,16 +3013,18 @@
// 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, 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, 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,
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 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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
std::unique_ptr<SessionDescription> updated_offer(
@@ -3168,25 +3423,29 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
ASSERT_TRUE(offer);
@@ -3223,25 +3482,29 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
ASSERT_TRUE(offer);
@@ -3282,10 +3545,12 @@
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<SessionDescription> offer =
f1_.CreateOffer(offer_opts, nullptr);
ASSERT_TRUE(offer);
@@ -3300,10 +3565,12 @@
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<SessionDescription> offer =
f1_.CreateOffer(offer_opts, nullptr);
ASSERT_TRUE(offer);
@@ -3313,10 +3580,12 @@
// 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<SessionDescription> answer =
f2_.CreateAnswer(offer.get(), answer_opts, nullptr);
ASSERT_EQ(2u, answer->contents().size());
@@ -3330,10 +3599,12 @@
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<SessionDescription> offer =
f1_.CreateOffer(offer_opts, nullptr);
ASSERT_TRUE(offer);
@@ -3343,10 +3614,12 @@
// 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<SessionDescription> answer =
f2_.CreateAnswer(offer.get(), answer_opts, nullptr);
ASSERT_EQ(2u, answer->contents().size());
@@ -3365,10 +3638,12 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
ASSERT_TRUE(offer);
@@ -3382,10 +3657,12 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
ASSERT_TRUE(offer);
@@ -3419,10 +3696,12 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
ASSERT_TRUE(offer);
@@ -3453,10 +3732,12 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
ASSERT_TRUE(offer);
@@ -3509,10 +3790,12 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
ASSERT_TRUE(offer);
@@ -3558,8 +3841,9 @@
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<SessionDescription> offer = f1_.CreateOffer(opts, nullptr);
ASSERT_TRUE(offer);
@@ -3764,12 +4048,13 @@
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<SessionDescription> offer = sf.CreateOffer(opts, NULL);
@@ -3862,12 +4147,13 @@
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<SessionDescription> offer =
@@ -3875,12 +4161,13 @@
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<SessionDescription> answer =
answer_factory.CreateAnswer(offer.get(), answer_opts, NULL);
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index a70ab2e..7f7cebd 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 @@
}
// Add options to |[audio/video]_media_description_options| from |senders|.
-void AddRtpSenderOptions(
+void AddPlanBRtpSenderOptions(
const std::vector<rtc::scoped_refptr<
RtpSenderProxyWithInternal<RtpSenderInternal>>>& senders,
cricket::MediaDescriptionOptions* audio_media_description_options,
@@ -186,8 +187,8 @@
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 @@
!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 @@
!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 8fff520..61849ce 100644
--- a/pc/simulcastdescription.cc
+++ b/pc/simulcastdescription.cc
@@ -20,6 +20,10 @@
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 9f15f78..0781a6a 100644
--- a/pc/simulcastdescription.h
+++ b/pc/simulcastdescription.h
@@ -25,6 +25,7 @@
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 @@
// {SimulcastLayer("4", false), SimulcastLayer("5", false});
class SimulcastLayerList final {
public:
+ // Type definitions required by a container.
+ typedef size_t size_type;
+ typedef std::vector<SimulcastLayer> value_type;
+ typedef std::vector<std::vector<SimulcastLayer>>::const_iterator
+ const_iterator;
+
// Use to add a layer when there will be no alternatives.
void AddLayer(const SimulcastLayer& layer);
@@ -57,13 +64,9 @@
// Read-only access to the contents.
// Note: This object does not allow removal of layers.
- std::vector<std::vector<SimulcastLayer>>::const_iterator begin() const {
- return list_.begin();
- }
+ const_iterator begin() const { return list_.begin(); }
- std::vector<std::vector<SimulcastLayer>>::const_iterator end() const {
- return list_.end();
- }
+ const_iterator end() const { return list_.end(); }
const std::vector<SimulcastLayer>& operator[](size_t index) const;
diff --git a/pc/unique_id_generator.cc b/pc/unique_id_generator.cc
new file mode 100644
index 0000000..b381d44
--- /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 <limits>
+#include <vector>
+
+#include "rtc_base/helpers.h"
+#include "rtc_base/string_to_number.h"
+#include "rtc_base/stringencode.h"
+
+namespace webrtc {
+
+namespace {
+UniqueNumberGenerator<uint32_t> CreateUniqueNumberGenerator(
+ rtc::ArrayView<std::string> known_ids) {
+ std::vector<uint32_t> known_ints;
+ for (const std::string& str : known_ids) {
+ absl::optional<uint32_t> value = rtc::StringToNumber<uint32_t>(str);
+ if (value.has_value()) {
+ known_ints.push_back(value.value());
+ }
+ }
+ return UniqueNumberGenerator<uint32_t>(known_ints);
+}
+} // namespace
+
+UniqueRandomIdGenerator::UniqueRandomIdGenerator() : known_ids_() {}
+UniqueRandomIdGenerator::UniqueRandomIdGenerator(
+ rtc::ArrayView<uint32_t> 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<uint32_t>::max());
+ auto pair = known_ids_.insert(rtc::CreateRandomNonZeroId());
+ if (pair.second) {
+ return *pair.first;
+ }
+ }
+}
+
+UniqueStringGenerator::UniqueStringGenerator() : unique_number_generator_() {}
+UniqueStringGenerator::UniqueStringGenerator(
+ rtc::ArrayView<std::string> 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 0000000..15ba794
--- /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 <limits>
+#include <set>
+#include <string>
+
+#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 <typename TIntegral>
+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<TIntegral> 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<TIntegral>::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<TIntegral> 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<uint32_t> 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<uint32_t> 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<std::string> known_ids);
+ ~UniqueStringGenerator();
+
+ std::string GenerateString();
+ std::string operator()() { return GenerateString(); }
+
+ private:
+ // This implementation will be simple and will generate "0", "1", ...
+ UniqueNumberGenerator<uint32_t> unique_number_generator_;
+};
+
+template <typename TIntegral>
+UniqueNumberGenerator<TIntegral>::UniqueNumberGenerator() : counter_(0) {}
+
+template <typename TIntegral>
+UniqueNumberGenerator<TIntegral>::UniqueNumberGenerator(
+ rtc::ArrayView<TIntegral> known_ids)
+ : counter_(0), known_ids_(known_ids.begin(), known_ids.end()) {}
+
+template <typename TIntegral>
+UniqueNumberGenerator<TIntegral>::~UniqueNumberGenerator() {}
+
+template <typename TIntegral>
+TIntegral UniqueNumberGenerator<TIntegral>::GenerateNumber() {
+ while (true) {
+ RTC_CHECK_LT(counter_, std::numeric_limits<TIntegral>::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 0000000..86d831b
--- /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 <typename Generator>
+class UniqueIdGeneratorTest : public Test {};
+
+using test_types = ::testing::Types<UniqueNumberGenerator<uint8_t>,
+ UniqueNumberGenerator<uint16_t>,
+ UniqueNumberGenerator<uint32_t>,
+ 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<typename Generator::value_type> 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<typename Generator::value_type> 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<typename Generator::value_type> 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<typename Generator::value_type> 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<typename Generator::value_type> intersection;
+ std::set_intersection(values.begin(), values.end(), known_values.begin(),
+ known_values.end(), std::back_inserter(intersection));
+ EXPECT_THAT(intersection, IsEmpty());
+}
+
+} // namespace webrtc