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}
This commit is contained in:
parent
bba675db3e
commit
c63ddb2a3f
@ -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" ]
|
||||
|
||||
@ -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<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 @@ class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
|
||||
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 @@ static bool AddStreamParams(
|
||||
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 @@ static bool IsFlexfecCodec(const C& codec) {
|
||||
// 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 @@ 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<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 @@ 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<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 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@ -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<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 @@ struct MediaDescriptionOptions {
|
||||
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 @@ struct MediaDescriptionOptions {
|
||||
// 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);
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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<rtc::scoped_refptr<
|
||||
RtpSenderProxyWithInternal<RtpSenderInternal>>>& 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(
|
||||
|
||||
@ -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});
|
||||
}
|
||||
|
||||
@ -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<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 @@ class SimulcastLayerList final {
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
64
pc/unique_id_generator.cc
Normal file
64
pc/unique_id_generator.cc
Normal file
@ -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
|
||||
122
pc/unique_id_generator.h
Normal file
122
pc/unique_id_generator.h
Normal file
@ -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_
|
||||
79
pc/unique_id_generator_unittest.cc
Normal file
79
pc/unique_id_generator_unittest.cc
Normal file
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user