Creating Simulcast offer and answer in Peer Connection.
CreateOffer and CreateAnswer will now examine the layers on the transceiver to determine if multiple layers are requested (Simulcast). In this scenario RIDs will be used in the layers (instead of SSRCs). When the offer is created, only RIDs are signalled in the offer. When the offer is set locally SetLocalDescription() SSRCs will be generated for each layer by the Channel and sent downstream to the MediaChannel. The MediaChannel receives configuration that looks identical to that of legacy simulcast, and should be able to integrate the streams correctly regardless of how they were signalled. Setting multiple layers on the transciever is still not supported through the API. Bug: webrtc:10075 Change-Id: Id4ad3637b87b68ef6ca7eec69166fee2d9dfa36f Reviewed-on: https://webrtc-review.googlesource.com/c/119780 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@{#26428}
This commit is contained in:
parent
e76ca61238
commit
bcd39d483d
@ -195,6 +195,38 @@ std::string StreamParams::ToString() const {
|
|||||||
sb << "}";
|
sb << "}";
|
||||||
return sb.str();
|
return sb.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StreamParams::GenerateSsrcs(int num_layers,
|
||||||
|
bool generate_fid,
|
||||||
|
bool generate_fec_fr,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator) {
|
||||||
|
RTC_DCHECK_GE(num_layers, 0);
|
||||||
|
RTC_DCHECK(ssrc_generator);
|
||||||
|
std::vector<uint32_t> primary_ssrcs;
|
||||||
|
for (int i = 0; i < num_layers; ++i) {
|
||||||
|
uint32_t ssrc = ssrc_generator->GenerateId();
|
||||||
|
primary_ssrcs.push_back(ssrc);
|
||||||
|
add_ssrc(ssrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_layers > 1) {
|
||||||
|
SsrcGroup simulcast(kSimSsrcGroupSemantics, primary_ssrcs);
|
||||||
|
ssrc_groups.push_back(simulcast);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generate_fid) {
|
||||||
|
for (uint32_t ssrc : primary_ssrcs) {
|
||||||
|
AddFidSsrc(ssrc, ssrc_generator->GenerateId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generate_fec_fr) {
|
||||||
|
for (uint32_t ssrc : primary_ssrcs) {
|
||||||
|
AddFecFrSsrc(ssrc, ssrc_generator->GenerateId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void StreamParams::GetPrimarySsrcs(std::vector<uint32_t>* ssrcs) const {
|
void StreamParams::GetPrimarySsrcs(std::vector<uint32_t>* ssrcs) const {
|
||||||
const SsrcGroup* sim_group = get_ssrc_group(kSimSsrcGroupSemantics);
|
const SsrcGroup* sim_group = get_ssrc_group(kSimSsrcGroupSemantics);
|
||||||
if (sim_group == NULL) {
|
if (sim_group == NULL) {
|
||||||
|
|||||||
@ -54,6 +54,7 @@
|
|||||||
|
|
||||||
#include "media/base/rid_description.h"
|
#include "media/base/rid_description.h"
|
||||||
#include "rtc_base/constructor_magic.h"
|
#include "rtc_base/constructor_magic.h"
|
||||||
|
#include "rtc_base/unique_id_generator.h"
|
||||||
|
|
||||||
namespace cricket {
|
namespace cricket {
|
||||||
|
|
||||||
@ -154,6 +155,14 @@ struct StreamParams {
|
|||||||
return GetSecondarySsrc(kFecFrSsrcGroupSemantics, primary_ssrc, fecfr_ssrc);
|
return GetSecondarySsrc(kFecFrSsrcGroupSemantics, primary_ssrc, fecfr_ssrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convenience function to populate the StreamParams with the requested number
|
||||||
|
// of SSRCs along with accompanying FID and FEC-FR ssrcs if requested.
|
||||||
|
// SSRCs are generated using the given generator.
|
||||||
|
void GenerateSsrcs(int num_layers,
|
||||||
|
bool generate_fid,
|
||||||
|
bool generate_fec_fr,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||||
|
|
||||||
// Convenience to get all the SIM SSRCs if there are SIM ssrcs, or
|
// Convenience to get all the SIM SSRCs if there are SIM ssrcs, or
|
||||||
// the first SSRC otherwise.
|
// the first SSRC otherwise.
|
||||||
void GetPrimarySsrcs(std::vector<uint32_t>* ssrcs) const;
|
void GetPrimarySsrcs(std::vector<uint32_t>* ssrcs) const;
|
||||||
|
|||||||
@ -14,8 +14,12 @@
|
|||||||
|
|
||||||
#include "media/base/test_utils.h"
|
#include "media/base/test_utils.h"
|
||||||
#include "rtc_base/arraysize.h"
|
#include "rtc_base/arraysize.h"
|
||||||
|
#include "test/gmock.h"
|
||||||
#include "test/gtest.h"
|
#include "test/gtest.h"
|
||||||
|
|
||||||
|
using ::testing::Each;
|
||||||
|
using ::testing::Ne;
|
||||||
|
|
||||||
static const uint32_t kSsrcs1[] = {1};
|
static const uint32_t kSsrcs1[] = {1};
|
||||||
static const uint32_t kSsrcs2[] = {1, 2};
|
static const uint32_t kSsrcs2[] = {1, 2};
|
||||||
static const uint32_t kSsrcs3[] = {1, 2, 3};
|
static const uint32_t kSsrcs3[] = {1, 2, 3};
|
||||||
@ -321,3 +325,72 @@ TEST(StreamParams, TestIsSimulcastStream_InvalidStreams) {
|
|||||||
stream3.ssrc_groups.push_back(sg);
|
stream3.ssrc_groups.push_back(sg);
|
||||||
EXPECT_FALSE(cricket::IsSimulcastStream(stream3));
|
EXPECT_FALSE(cricket::IsSimulcastStream(stream3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(StreamParams, TestGenerateSsrcs_SingleStreamWithRtxAndFlex) {
|
||||||
|
rtc::UniqueRandomIdGenerator generator;
|
||||||
|
cricket::StreamParams stream;
|
||||||
|
stream.GenerateSsrcs(1, true, true, &generator);
|
||||||
|
uint32_t primary_ssrc = stream.first_ssrc();
|
||||||
|
ASSERT_NE(0u, primary_ssrc);
|
||||||
|
uint32_t rtx_ssrc = 0;
|
||||||
|
uint32_t flex_ssrc = 0;
|
||||||
|
EXPECT_EQ(3u, stream.ssrcs.size());
|
||||||
|
EXPECT_TRUE(stream.GetFidSsrc(primary_ssrc, &rtx_ssrc));
|
||||||
|
EXPECT_NE(0u, rtx_ssrc);
|
||||||
|
EXPECT_TRUE(stream.GetFecFrSsrc(primary_ssrc, &flex_ssrc));
|
||||||
|
EXPECT_NE(0u, flex_ssrc);
|
||||||
|
EXPECT_FALSE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
|
||||||
|
EXPECT_TRUE(stream.has_ssrc_group(cricket::kFidSsrcGroupSemantics));
|
||||||
|
EXPECT_TRUE(stream.has_ssrc_group(cricket::kFecFrSsrcGroupSemantics));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StreamParams, TestGenerateSsrcs_SingleStreamWithRtx) {
|
||||||
|
rtc::UniqueRandomIdGenerator generator;
|
||||||
|
cricket::StreamParams stream;
|
||||||
|
stream.GenerateSsrcs(1, true, false, &generator);
|
||||||
|
uint32_t primary_ssrc = stream.first_ssrc();
|
||||||
|
ASSERT_NE(0u, primary_ssrc);
|
||||||
|
uint32_t rtx_ssrc = 0;
|
||||||
|
uint32_t flex_ssrc = 0;
|
||||||
|
EXPECT_EQ(2u, stream.ssrcs.size());
|
||||||
|
EXPECT_TRUE(stream.GetFidSsrc(primary_ssrc, &rtx_ssrc));
|
||||||
|
EXPECT_NE(0u, rtx_ssrc);
|
||||||
|
EXPECT_FALSE(stream.GetFecFrSsrc(primary_ssrc, &flex_ssrc));
|
||||||
|
EXPECT_EQ(0u, flex_ssrc);
|
||||||
|
EXPECT_FALSE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
|
||||||
|
EXPECT_TRUE(stream.has_ssrc_group(cricket::kFidSsrcGroupSemantics));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StreamParams, TestGenerateSsrcs_SingleStreamWithFlex) {
|
||||||
|
rtc::UniqueRandomIdGenerator generator;
|
||||||
|
cricket::StreamParams stream;
|
||||||
|
stream.GenerateSsrcs(1, false, true, &generator);
|
||||||
|
uint32_t primary_ssrc = stream.first_ssrc();
|
||||||
|
ASSERT_NE(0u, primary_ssrc);
|
||||||
|
uint32_t rtx_ssrc = 0;
|
||||||
|
uint32_t flex_ssrc = 0;
|
||||||
|
EXPECT_EQ(2u, stream.ssrcs.size());
|
||||||
|
EXPECT_FALSE(stream.GetFidSsrc(primary_ssrc, &rtx_ssrc));
|
||||||
|
EXPECT_EQ(0u, rtx_ssrc);
|
||||||
|
EXPECT_TRUE(stream.GetFecFrSsrc(primary_ssrc, &flex_ssrc));
|
||||||
|
EXPECT_NE(0u, flex_ssrc);
|
||||||
|
EXPECT_FALSE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
|
||||||
|
EXPECT_TRUE(stream.has_ssrc_group(cricket::kFecFrSsrcGroupSemantics));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StreamParams, TestGenerateSsrcs_SimulcastLayersAndRtx) {
|
||||||
|
const size_t kNumStreams = 3;
|
||||||
|
rtc::UniqueRandomIdGenerator generator;
|
||||||
|
cricket::StreamParams stream;
|
||||||
|
stream.GenerateSsrcs(kNumStreams, true, false, &generator);
|
||||||
|
EXPECT_EQ(kNumStreams * 2, stream.ssrcs.size());
|
||||||
|
std::vector<uint32_t> primary_ssrcs, rtx_ssrcs;
|
||||||
|
stream.GetPrimarySsrcs(&primary_ssrcs);
|
||||||
|
EXPECT_EQ(kNumStreams, primary_ssrcs.size());
|
||||||
|
EXPECT_THAT(primary_ssrcs, Each(Ne(0u)));
|
||||||
|
stream.GetFidSsrcs(primary_ssrcs, &rtx_ssrcs);
|
||||||
|
EXPECT_EQ(kNumStreams, rtx_ssrcs.size());
|
||||||
|
EXPECT_THAT(rtx_ssrcs, Each(Ne(0u)));
|
||||||
|
EXPECT_TRUE(stream.has_ssrc_group(cricket::kSimSsrcGroupSemantics));
|
||||||
|
EXPECT_TRUE(stream.has_ssrc_group(cricket::kFidSsrcGroupSemantics));
|
||||||
|
}
|
||||||
|
|||||||
119
pc/channel.cc
119
pc/channel.cc
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
namespace cricket {
|
namespace cricket {
|
||||||
using rtc::Bind;
|
using rtc::Bind;
|
||||||
|
using rtc::UniqueRandomIdGenerator;
|
||||||
using webrtc::SdpType;
|
using webrtc::SdpType;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -46,6 +47,39 @@ struct SendPacketMessageData : public rtc::MessageData {
|
|||||||
rtc::PacketOptions options;
|
rtc::PacketOptions options;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Finds a stream based on target's Primary SSRC or RIDs.
|
||||||
|
// This struct is used in BaseChannel::UpdateLocalStreams_w.
|
||||||
|
struct StreamFinder {
|
||||||
|
explicit StreamFinder(const StreamParams* target) : target_(target) {
|
||||||
|
RTC_DCHECK(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const StreamParams& sp) const {
|
||||||
|
if (target_->has_ssrcs() && sp.has_ssrcs()) {
|
||||||
|
return sp.has_ssrc(target_->first_ssrc());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target_->has_rids() && !sp.has_rids()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<RidDescription>& target_rids = target_->rids();
|
||||||
|
const std::vector<RidDescription>& source_rids = sp.rids();
|
||||||
|
if (source_rids.size() != target_rids.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all RIDs match.
|
||||||
|
return std::equal(source_rids.begin(), source_rids.end(),
|
||||||
|
target_rids.begin(),
|
||||||
|
[](const RidDescription& lhs, const RidDescription& rhs) {
|
||||||
|
return lhs.rid == rhs.rid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const StreamParams* target_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -102,15 +136,18 @@ BaseChannel::BaseChannel(rtc::Thread* worker_thread,
|
|||||||
std::unique_ptr<MediaChannel> media_channel,
|
std::unique_ptr<MediaChannel> media_channel,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
webrtc::CryptoOptions crypto_options)
|
webrtc::CryptoOptions crypto_options,
|
||||||
|
UniqueRandomIdGenerator* ssrc_generator)
|
||||||
: worker_thread_(worker_thread),
|
: worker_thread_(worker_thread),
|
||||||
network_thread_(network_thread),
|
network_thread_(network_thread),
|
||||||
signaling_thread_(signaling_thread),
|
signaling_thread_(signaling_thread),
|
||||||
content_name_(content_name),
|
content_name_(content_name),
|
||||||
srtp_required_(srtp_required),
|
srtp_required_(srtp_required),
|
||||||
crypto_options_(crypto_options),
|
crypto_options_(crypto_options),
|
||||||
media_channel_(std::move(media_channel)) {
|
media_channel_(std::move(media_channel)),
|
||||||
|
ssrc_generator_(ssrc_generator) {
|
||||||
RTC_DCHECK_RUN_ON(worker_thread_);
|
RTC_DCHECK_RUN_ON(worker_thread_);
|
||||||
|
RTC_DCHECK(ssrc_generator_);
|
||||||
demuxer_criteria_.mid = content_name;
|
demuxer_criteria_.mid = content_name;
|
||||||
RTC_LOG(LS_INFO) << "Created channel for " << content_name;
|
RTC_LOG(LS_INFO) << "Created channel for " << content_name;
|
||||||
}
|
}
|
||||||
@ -581,11 +618,24 @@ bool BaseChannel::RemoveRecvStream_w(uint32_t ssrc) {
|
|||||||
bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams,
|
bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams,
|
||||||
SdpType type,
|
SdpType type,
|
||||||
std::string* error_desc) {
|
std::string* error_desc) {
|
||||||
|
// In the case of RIDs (where SSRCs are not negotiated), this method will
|
||||||
|
// generate an SSRC for each layer in StreamParams. That representation will
|
||||||
|
// be stored internally in |local_streams_|.
|
||||||
|
// In subsequent offers, the same stream can appear in |streams| again
|
||||||
|
// (without the SSRCs), so it should be looked up using RIDs (if available)
|
||||||
|
// and then by primary SSRC.
|
||||||
|
// In both scenarios, it is safe to assume that the media channel will be
|
||||||
|
// created with a StreamParams object with SSRCs. However, it is not safe to
|
||||||
|
// assume that |local_streams_| will always have SSRCs as there are scenarios
|
||||||
|
// in which niether SSRCs or RIDs are negotiated.
|
||||||
|
|
||||||
// Check for streams that have been removed.
|
// Check for streams that have been removed.
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
for (const StreamParams& old_stream : local_streams_) {
|
for (const StreamParams& old_stream : local_streams_) {
|
||||||
if (old_stream.has_ssrcs() &&
|
if (!old_stream.has_ssrcs() ||
|
||||||
!GetStreamBySsrc(streams, old_stream.first_ssrc())) {
|
GetStream(streams, StreamFinder(&old_stream))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!media_channel()->RemoveSendStream(old_stream.first_ssrc())) {
|
if (!media_channel()->RemoveSendStream(old_stream.first_ssrc())) {
|
||||||
rtc::StringBuilder desc;
|
rtc::StringBuilder desc;
|
||||||
desc << "Failed to remove send stream with ssrc "
|
desc << "Failed to remove send stream with ssrc "
|
||||||
@ -594,11 +644,41 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams,
|
|||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Check for new streams.
|
// Check for new streams.
|
||||||
for (const StreamParams& new_stream : streams) {
|
std::vector<StreamParams> all_streams;
|
||||||
if (new_stream.has_ssrcs() &&
|
for (const StreamParams& stream : streams) {
|
||||||
!GetStreamBySsrc(local_streams_, new_stream.first_ssrc())) {
|
StreamParams* existing = GetStream(local_streams_, StreamFinder(&stream));
|
||||||
|
if (existing) {
|
||||||
|
// Parameters cannot change for an existing stream.
|
||||||
|
all_streams.push_back(*existing);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
all_streams.push_back(stream);
|
||||||
|
StreamParams& new_stream = all_streams.back();
|
||||||
|
|
||||||
|
if (!new_stream.has_ssrcs() && !new_stream.has_rids()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTC_DCHECK(new_stream.has_ssrcs() || new_stream.has_rids());
|
||||||
|
if (new_stream.has_ssrcs() && new_stream.has_rids()) {
|
||||||
|
rtc::StringBuilder desc;
|
||||||
|
desc << "Failed to add send stream: " << new_stream.first_ssrc()
|
||||||
|
<< ". Stream has both SSRCs and RIDs.";
|
||||||
|
SafeSetError(desc.str(), error_desc);
|
||||||
|
ret = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we use the legacy simulcast group in StreamParams to
|
||||||
|
// indicate that we want multiple layers to the media channel.
|
||||||
|
if (!new_stream.has_ssrcs()) {
|
||||||
|
// TODO(bugs.webrtc.org/10250): Indicate if flex is desired here.
|
||||||
|
new_stream.GenerateSsrcs(new_stream.rids().size(), /* rtx = */ true,
|
||||||
|
/* flex_fec = */ false, ssrc_generator_);
|
||||||
|
}
|
||||||
|
|
||||||
if (media_channel()->AddSendStream(new_stream)) {
|
if (media_channel()->AddSendStream(new_stream)) {
|
||||||
RTC_LOG(LS_INFO) << "Add send stream ssrc: " << new_stream.ssrcs[0];
|
RTC_LOG(LS_INFO) << "Add send stream ssrc: " << new_stream.ssrcs[0];
|
||||||
} else {
|
} else {
|
||||||
@ -608,8 +688,7 @@ bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams,
|
|||||||
ret = false;
|
ret = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
local_streams_ = all_streams;
|
||||||
local_streams_ = streams;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,19 +808,19 @@ void BaseChannel::SignalSentPacket_w(const rtc::SentPacket& sent_packet) {
|
|||||||
VoiceChannel::VoiceChannel(rtc::Thread* worker_thread,
|
VoiceChannel::VoiceChannel(rtc::Thread* worker_thread,
|
||||||
rtc::Thread* network_thread,
|
rtc::Thread* network_thread,
|
||||||
rtc::Thread* signaling_thread,
|
rtc::Thread* signaling_thread,
|
||||||
// TODO(nisse): Delete unused argument.
|
|
||||||
MediaEngineInterface* /* media_engine */,
|
|
||||||
std::unique_ptr<VoiceMediaChannel> media_channel,
|
std::unique_ptr<VoiceMediaChannel> media_channel,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
webrtc::CryptoOptions crypto_options)
|
webrtc::CryptoOptions crypto_options,
|
||||||
|
UniqueRandomIdGenerator* ssrc_generator)
|
||||||
: BaseChannel(worker_thread,
|
: BaseChannel(worker_thread,
|
||||||
network_thread,
|
network_thread,
|
||||||
signaling_thread,
|
signaling_thread,
|
||||||
std::move(media_channel),
|
std::move(media_channel),
|
||||||
content_name,
|
content_name,
|
||||||
srtp_required,
|
srtp_required,
|
||||||
crypto_options) {}
|
crypto_options,
|
||||||
|
ssrc_generator) {}
|
||||||
|
|
||||||
VoiceChannel::~VoiceChannel() {
|
VoiceChannel::~VoiceChannel() {
|
||||||
if (media_transport()) {
|
if (media_transport()) {
|
||||||
@ -895,14 +974,16 @@ VideoChannel::VideoChannel(rtc::Thread* worker_thread,
|
|||||||
std::unique_ptr<VideoMediaChannel> media_channel,
|
std::unique_ptr<VideoMediaChannel> media_channel,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
webrtc::CryptoOptions crypto_options)
|
webrtc::CryptoOptions crypto_options,
|
||||||
|
UniqueRandomIdGenerator* ssrc_generator)
|
||||||
: BaseChannel(worker_thread,
|
: BaseChannel(worker_thread,
|
||||||
network_thread,
|
network_thread,
|
||||||
signaling_thread,
|
signaling_thread,
|
||||||
std::move(media_channel),
|
std::move(media_channel),
|
||||||
content_name,
|
content_name,
|
||||||
srtp_required,
|
srtp_required,
|
||||||
crypto_options) {}
|
crypto_options,
|
||||||
|
ssrc_generator) {}
|
||||||
|
|
||||||
VideoChannel::~VideoChannel() {
|
VideoChannel::~VideoChannel() {
|
||||||
TRACE_EVENT0("webrtc", "VideoChannel::~VideoChannel");
|
TRACE_EVENT0("webrtc", "VideoChannel::~VideoChannel");
|
||||||
@ -1034,14 +1115,16 @@ RtpDataChannel::RtpDataChannel(rtc::Thread* worker_thread,
|
|||||||
std::unique_ptr<DataMediaChannel> media_channel,
|
std::unique_ptr<DataMediaChannel> media_channel,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
webrtc::CryptoOptions crypto_options)
|
webrtc::CryptoOptions crypto_options,
|
||||||
|
UniqueRandomIdGenerator* ssrc_generator)
|
||||||
: BaseChannel(worker_thread,
|
: BaseChannel(worker_thread,
|
||||||
network_thread,
|
network_thread,
|
||||||
signaling_thread,
|
signaling_thread,
|
||||||
std::move(media_channel),
|
std::move(media_channel),
|
||||||
content_name,
|
content_name,
|
||||||
srtp_required,
|
srtp_required,
|
||||||
crypto_options) {}
|
crypto_options,
|
||||||
|
ssrc_generator) {}
|
||||||
|
|
||||||
RtpDataChannel::~RtpDataChannel() {
|
RtpDataChannel::~RtpDataChannel() {
|
||||||
TRACE_EVENT0("webrtc", "RtpDataChannel::~RtpDataChannel");
|
TRACE_EVENT0("webrtc", "RtpDataChannel::~RtpDataChannel");
|
||||||
|
|||||||
25
pc/channel.h
25
pc/channel.h
@ -41,6 +41,7 @@
|
|||||||
#include "rtc_base/critical_section.h"
|
#include "rtc_base/critical_section.h"
|
||||||
#include "rtc_base/network.h"
|
#include "rtc_base/network.h"
|
||||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||||
|
#include "rtc_base/unique_id_generator.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
class AudioSinkInterface;
|
class AudioSinkInterface;
|
||||||
@ -78,6 +79,8 @@ class BaseChannel : public ChannelInterface,
|
|||||||
public:
|
public:
|
||||||
// If |srtp_required| is true, the channel will not send or receive any
|
// If |srtp_required| is true, the channel will not send or receive any
|
||||||
// RTP/RTCP packets without using SRTP (either using SDES or DTLS-SRTP).
|
// RTP/RTCP packets without using SRTP (either using SDES or DTLS-SRTP).
|
||||||
|
// The BaseChannel does not own the UniqueRandomIdGenerator so it is the
|
||||||
|
// responsibility of the user to ensure it outlives this object.
|
||||||
// TODO(zhihuang:) Create a BaseChannel::Config struct for the parameter lists
|
// TODO(zhihuang:) Create a BaseChannel::Config struct for the parameter lists
|
||||||
// which will make it easier to change the constructor.
|
// which will make it easier to change the constructor.
|
||||||
BaseChannel(rtc::Thread* worker_thread,
|
BaseChannel(rtc::Thread* worker_thread,
|
||||||
@ -86,7 +89,8 @@ class BaseChannel : public ChannelInterface,
|
|||||||
std::unique_ptr<MediaChannel> media_channel,
|
std::unique_ptr<MediaChannel> media_channel,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
webrtc::CryptoOptions crypto_options);
|
webrtc::CryptoOptions crypto_options,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||||
virtual ~BaseChannel();
|
virtual ~BaseChannel();
|
||||||
virtual void Init_w(webrtc::RtpTransportInternal* rtp_transport,
|
virtual void Init_w(webrtc::RtpTransportInternal* rtp_transport,
|
||||||
webrtc::MediaTransportInterface* media_transport);
|
webrtc::MediaTransportInterface* media_transport);
|
||||||
@ -125,10 +129,10 @@ class BaseChannel : public ChannelInterface,
|
|||||||
|
|
||||||
bool Enable(bool enable) override;
|
bool Enable(bool enable) override;
|
||||||
|
|
||||||
const std::vector<StreamParams>& local_streams() const {
|
const std::vector<StreamParams>& local_streams() const override {
|
||||||
return local_streams_;
|
return local_streams_;
|
||||||
}
|
}
|
||||||
const std::vector<StreamParams>& remote_streams() const {
|
const std::vector<StreamParams>& remote_streams() const override {
|
||||||
return remote_streams_;
|
return remote_streams_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,6 +349,11 @@ class BaseChannel : public ChannelInterface,
|
|||||||
webrtc::RtpTransceiverDirection::kInactive;
|
webrtc::RtpTransceiverDirection::kInactive;
|
||||||
|
|
||||||
webrtc::RtpDemuxerCriteria demuxer_criteria_;
|
webrtc::RtpDemuxerCriteria demuxer_criteria_;
|
||||||
|
// This generator is used to generate SSRCs for local streams.
|
||||||
|
// This is needed in cases where SSRCs are not negotiated or set explicitly
|
||||||
|
// like in Simulcast.
|
||||||
|
// This object is not owned by the channel so it must outlive it.
|
||||||
|
rtc::UniqueRandomIdGenerator* const ssrc_generator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// VoiceChannel is a specialization that adds support for early media, DTMF,
|
// VoiceChannel is a specialization that adds support for early media, DTMF,
|
||||||
@ -355,11 +364,11 @@ class VoiceChannel : public BaseChannel,
|
|||||||
VoiceChannel(rtc::Thread* worker_thread,
|
VoiceChannel(rtc::Thread* worker_thread,
|
||||||
rtc::Thread* network_thread,
|
rtc::Thread* network_thread,
|
||||||
rtc::Thread* signaling_thread,
|
rtc::Thread* signaling_thread,
|
||||||
MediaEngineInterface* media_engine,
|
|
||||||
std::unique_ptr<VoiceMediaChannel> channel,
|
std::unique_ptr<VoiceMediaChannel> channel,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
webrtc::CryptoOptions crypto_options);
|
webrtc::CryptoOptions crypto_options,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||||
~VoiceChannel();
|
~VoiceChannel();
|
||||||
|
|
||||||
// downcasts a MediaChannel
|
// downcasts a MediaChannel
|
||||||
@ -402,7 +411,8 @@ class VideoChannel : public BaseChannel {
|
|||||||
std::unique_ptr<VideoMediaChannel> media_channel,
|
std::unique_ptr<VideoMediaChannel> media_channel,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
webrtc::CryptoOptions crypto_options);
|
webrtc::CryptoOptions crypto_options,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||||
~VideoChannel();
|
~VideoChannel();
|
||||||
|
|
||||||
// downcasts a MediaChannel
|
// downcasts a MediaChannel
|
||||||
@ -443,7 +453,8 @@ class RtpDataChannel : public BaseChannel {
|
|||||||
std::unique_ptr<DataMediaChannel> channel,
|
std::unique_ptr<DataMediaChannel> channel,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
webrtc::CryptoOptions crypto_options);
|
webrtc::CryptoOptions crypto_options,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||||
~RtpDataChannel();
|
~RtpDataChannel();
|
||||||
// TODO(zhihuang): Remove this once the RtpTransport can be shared between
|
// TODO(zhihuang): Remove this once the RtpTransport can be shared between
|
||||||
// BaseChannels.
|
// BaseChannels.
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#define PC_CHANNEL_INTERFACE_H_
|
#define PC_CHANNEL_INTERFACE_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "api/jsep.h"
|
#include "api/jsep.h"
|
||||||
#include "api/media_types.h"
|
#include "api/media_types.h"
|
||||||
@ -52,6 +53,10 @@ class ChannelInterface {
|
|||||||
webrtc::SdpType type,
|
webrtc::SdpType type,
|
||||||
std::string* error_desc) = 0;
|
std::string* error_desc) = 0;
|
||||||
|
|
||||||
|
// Access to the local and remote streams that were set on the channel.
|
||||||
|
virtual const std::vector<StreamParams>& local_streams() const = 0;
|
||||||
|
virtual const std::vector<StreamParams>& remote_streams() const = 0;
|
||||||
|
|
||||||
// Set an RTP level transport.
|
// Set an RTP level transport.
|
||||||
// Some examples:
|
// Some examples:
|
||||||
// * An RtpTransport without encryption.
|
// * An RtpTransport without encryption.
|
||||||
|
|||||||
@ -163,12 +163,13 @@ VoiceChannel* ChannelManager::CreateVoiceChannel(
|
|||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
const webrtc::CryptoOptions& crypto_options,
|
const webrtc::CryptoOptions& crypto_options,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator,
|
||||||
const AudioOptions& options) {
|
const AudioOptions& options) {
|
||||||
if (!worker_thread_->IsCurrent()) {
|
if (!worker_thread_->IsCurrent()) {
|
||||||
return worker_thread_->Invoke<VoiceChannel*>(RTC_FROM_HERE, [&] {
|
return worker_thread_->Invoke<VoiceChannel*>(RTC_FROM_HERE, [&] {
|
||||||
return CreateVoiceChannel(call, media_config, rtp_transport,
|
return CreateVoiceChannel(
|
||||||
media_transport, signaling_thread, content_name,
|
call, media_config, rtp_transport, media_transport, signaling_thread,
|
||||||
srtp_required, crypto_options, options);
|
content_name, srtp_required, crypto_options, ssrc_generator, options);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,9 +187,9 @@ VoiceChannel* ChannelManager::CreateVoiceChannel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto voice_channel = absl::make_unique<VoiceChannel>(
|
auto voice_channel = absl::make_unique<VoiceChannel>(
|
||||||
worker_thread_, network_thread_, signaling_thread, media_engine_.get(),
|
worker_thread_, network_thread_, signaling_thread,
|
||||||
absl::WrapUnique(media_channel), content_name, srtp_required,
|
absl::WrapUnique(media_channel), content_name, srtp_required,
|
||||||
crypto_options);
|
crypto_options, ssrc_generator);
|
||||||
|
|
||||||
voice_channel->Init_w(rtp_transport, media_transport);
|
voice_channel->Init_w(rtp_transport, media_transport);
|
||||||
|
|
||||||
@ -231,12 +232,13 @@ VideoChannel* ChannelManager::CreateVideoChannel(
|
|||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
const webrtc::CryptoOptions& crypto_options,
|
const webrtc::CryptoOptions& crypto_options,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator,
|
||||||
const VideoOptions& options) {
|
const VideoOptions& options) {
|
||||||
if (!worker_thread_->IsCurrent()) {
|
if (!worker_thread_->IsCurrent()) {
|
||||||
return worker_thread_->Invoke<VideoChannel*>(RTC_FROM_HERE, [&] {
|
return worker_thread_->Invoke<VideoChannel*>(RTC_FROM_HERE, [&] {
|
||||||
return CreateVideoChannel(call, media_config, rtp_transport,
|
return CreateVideoChannel(
|
||||||
media_transport, signaling_thread, content_name,
|
call, media_config, rtp_transport, media_transport, signaling_thread,
|
||||||
srtp_required, crypto_options, options);
|
content_name, srtp_required, crypto_options, ssrc_generator, options);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +258,7 @@ VideoChannel* ChannelManager::CreateVideoChannel(
|
|||||||
auto video_channel = absl::make_unique<VideoChannel>(
|
auto video_channel = absl::make_unique<VideoChannel>(
|
||||||
worker_thread_, network_thread_, signaling_thread,
|
worker_thread_, network_thread_, signaling_thread,
|
||||||
absl::WrapUnique(media_channel), content_name, srtp_required,
|
absl::WrapUnique(media_channel), content_name, srtp_required,
|
||||||
crypto_options);
|
crypto_options, ssrc_generator);
|
||||||
|
|
||||||
video_channel->Init_w(rtp_transport, media_transport);
|
video_channel->Init_w(rtp_transport, media_transport);
|
||||||
|
|
||||||
@ -296,11 +298,13 @@ RtpDataChannel* ChannelManager::CreateRtpDataChannel(
|
|||||||
rtc::Thread* signaling_thread,
|
rtc::Thread* signaling_thread,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
const webrtc::CryptoOptions& crypto_options) {
|
const webrtc::CryptoOptions& crypto_options,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator) {
|
||||||
if (!worker_thread_->IsCurrent()) {
|
if (!worker_thread_->IsCurrent()) {
|
||||||
return worker_thread_->Invoke<RtpDataChannel*>(RTC_FROM_HERE, [&] {
|
return worker_thread_->Invoke<RtpDataChannel*>(RTC_FROM_HERE, [&] {
|
||||||
return CreateRtpDataChannel(media_config, rtp_transport, signaling_thread,
|
return CreateRtpDataChannel(media_config, rtp_transport, signaling_thread,
|
||||||
content_name, srtp_required, crypto_options);
|
content_name, srtp_required, crypto_options,
|
||||||
|
ssrc_generator);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +319,7 @@ RtpDataChannel* ChannelManager::CreateRtpDataChannel(
|
|||||||
auto data_channel = absl::make_unique<RtpDataChannel>(
|
auto data_channel = absl::make_unique<RtpDataChannel>(
|
||||||
worker_thread_, network_thread_, signaling_thread,
|
worker_thread_, network_thread_, signaling_thread,
|
||||||
absl::WrapUnique(media_channel), content_name, srtp_required,
|
absl::WrapUnique(media_channel), content_name, srtp_required,
|
||||||
crypto_options);
|
crypto_options, ssrc_generator);
|
||||||
data_channel->Init_w(rtp_transport);
|
data_channel->Init_w(rtp_transport);
|
||||||
|
|
||||||
RtpDataChannel* data_channel_ptr = data_channel.get();
|
RtpDataChannel* data_channel_ptr = data_channel.get();
|
||||||
|
|||||||
@ -100,6 +100,7 @@ class ChannelManager final {
|
|||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
const webrtc::CryptoOptions& crypto_options,
|
const webrtc::CryptoOptions& crypto_options,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator,
|
||||||
const AudioOptions& options);
|
const AudioOptions& options);
|
||||||
// Destroys a voice channel created by CreateVoiceChannel.
|
// Destroys a voice channel created by CreateVoiceChannel.
|
||||||
void DestroyVoiceChannel(VoiceChannel* voice_channel);
|
void DestroyVoiceChannel(VoiceChannel* voice_channel);
|
||||||
@ -116,6 +117,7 @@ class ChannelManager final {
|
|||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
const webrtc::CryptoOptions& crypto_options,
|
const webrtc::CryptoOptions& crypto_options,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator,
|
||||||
const VideoOptions& options);
|
const VideoOptions& options);
|
||||||
// Destroys a video channel created by CreateVideoChannel.
|
// Destroys a video channel created by CreateVideoChannel.
|
||||||
void DestroyVideoChannel(VideoChannel* video_channel);
|
void DestroyVideoChannel(VideoChannel* video_channel);
|
||||||
@ -126,7 +128,8 @@ class ChannelManager final {
|
|||||||
rtc::Thread* signaling_thread,
|
rtc::Thread* signaling_thread,
|
||||||
const std::string& content_name,
|
const std::string& content_name,
|
||||||
bool srtp_required,
|
bool srtp_required,
|
||||||
const webrtc::CryptoOptions& crypto_options);
|
const webrtc::CryptoOptions& crypto_options,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||||
// Destroys a data channel created by CreateRtpDataChannel.
|
// Destroys a data channel created by CreateRtpDataChannel.
|
||||||
void DestroyRtpDataChannel(RtpDataChannel* data_channel);
|
void DestroyRtpDataChannel(RtpDataChannel* data_channel);
|
||||||
|
|
||||||
|
|||||||
@ -83,16 +83,17 @@ class ChannelManagerTest : public testing::Test {
|
|||||||
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
|
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
|
||||||
&fake_call_, cricket::MediaConfig(), rtp_transport, media_transport,
|
&fake_call_, cricket::MediaConfig(), rtp_transport, media_transport,
|
||||||
rtc::Thread::Current(), cricket::CN_AUDIO, kDefaultSrtpRequired,
|
rtc::Thread::Current(), cricket::CN_AUDIO, kDefaultSrtpRequired,
|
||||||
webrtc::CryptoOptions(), AudioOptions());
|
webrtc::CryptoOptions(), &ssrc_generator_, AudioOptions());
|
||||||
EXPECT_TRUE(voice_channel != nullptr);
|
EXPECT_TRUE(voice_channel != nullptr);
|
||||||
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
|
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
|
||||||
&fake_call_, cricket::MediaConfig(), rtp_transport, media_transport,
|
&fake_call_, cricket::MediaConfig(), rtp_transport, media_transport,
|
||||||
rtc::Thread::Current(), cricket::CN_VIDEO, kDefaultSrtpRequired,
|
rtc::Thread::Current(), cricket::CN_VIDEO, kDefaultSrtpRequired,
|
||||||
webrtc::CryptoOptions(), VideoOptions());
|
webrtc::CryptoOptions(), &ssrc_generator_, VideoOptions());
|
||||||
EXPECT_TRUE(video_channel != nullptr);
|
EXPECT_TRUE(video_channel != nullptr);
|
||||||
cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
|
cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
|
||||||
cricket::MediaConfig(), rtp_transport, rtc::Thread::Current(),
|
cricket::MediaConfig(), rtp_transport, rtc::Thread::Current(),
|
||||||
cricket::CN_DATA, kDefaultSrtpRequired, webrtc::CryptoOptions());
|
cricket::CN_DATA, kDefaultSrtpRequired, webrtc::CryptoOptions(),
|
||||||
|
&ssrc_generator_);
|
||||||
EXPECT_TRUE(rtp_data_channel != nullptr);
|
EXPECT_TRUE(rtp_data_channel != nullptr);
|
||||||
cm_->DestroyVideoChannel(video_channel);
|
cm_->DestroyVideoChannel(video_channel);
|
||||||
cm_->DestroyVoiceChannel(voice_channel);
|
cm_->DestroyVoiceChannel(voice_channel);
|
||||||
@ -109,6 +110,7 @@ class ChannelManagerTest : public testing::Test {
|
|||||||
std::unique_ptr<cricket::ChannelManager> cm_;
|
std::unique_ptr<cricket::ChannelManager> cm_;
|
||||||
cricket::FakeCall fake_call_;
|
cricket::FakeCall fake_call_;
|
||||||
webrtc::FakeMediaTransportFactory fake_media_transport_factory_;
|
webrtc::FakeMediaTransportFactory fake_media_transport_factory_;
|
||||||
|
rtc::UniqueRandomIdGenerator ssrc_generator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test that we startup/shutdown properly.
|
// Test that we startup/shutdown properly.
|
||||||
|
|||||||
@ -39,6 +39,8 @@
|
|||||||
|
|
||||||
using cricket::DtlsTransportInternal;
|
using cricket::DtlsTransportInternal;
|
||||||
using cricket::FakeVoiceMediaChannel;
|
using cricket::FakeVoiceMediaChannel;
|
||||||
|
using cricket::RidDescription;
|
||||||
|
using cricket::RidDirection;
|
||||||
using cricket::StreamParams;
|
using cricket::StreamParams;
|
||||||
using webrtc::RtpTransceiverDirection;
|
using webrtc::RtpTransceiverDirection;
|
||||||
using webrtc::SdpType;
|
using webrtc::SdpType;
|
||||||
@ -223,10 +225,10 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
|||||||
fake_rtp_dtls_transport2_.get(), fake_rtcp_dtls_transport2_.get(),
|
fake_rtp_dtls_transport2_.get(), fake_rtcp_dtls_transport2_.get(),
|
||||||
flags2);
|
flags2);
|
||||||
|
|
||||||
channel1_ = CreateChannel(worker_thread, network_thread_, &media_engine_,
|
channel1_ = CreateChannel(worker_thread, network_thread_, std::move(ch1),
|
||||||
std::move(ch1), rtp_transport1_.get(), flags1);
|
rtp_transport1_.get(), flags1);
|
||||||
channel2_ = CreateChannel(worker_thread, network_thread_, &media_engine_,
|
channel2_ = CreateChannel(worker_thread, network_thread_, std::move(ch2),
|
||||||
std::move(ch2), rtp_transport2_.get(), flags2);
|
rtp_transport2_.get(), flags2);
|
||||||
channel1_->SignalRtcpMuxFullyActive.connect(
|
channel1_->SignalRtcpMuxFullyActive.connect(
|
||||||
this, &ChannelTest<T>::OnRtcpMuxFullyActive1);
|
this, &ChannelTest<T>::OnRtcpMuxFullyActive1);
|
||||||
channel2_->SignalRtcpMuxFullyActive.connect(
|
channel2_->SignalRtcpMuxFullyActive.connect(
|
||||||
@ -253,14 +255,14 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
|||||||
std::unique_ptr<typename T::Channel> CreateChannel(
|
std::unique_ptr<typename T::Channel> CreateChannel(
|
||||||
rtc::Thread* worker_thread,
|
rtc::Thread* worker_thread,
|
||||||
rtc::Thread* network_thread,
|
rtc::Thread* network_thread,
|
||||||
cricket::MediaEngineInterface* engine,
|
|
||||||
std::unique_ptr<typename T::MediaChannel> ch,
|
std::unique_ptr<typename T::MediaChannel> ch,
|
||||||
webrtc::RtpTransportInternal* rtp_transport,
|
webrtc::RtpTransportInternal* rtp_transport,
|
||||||
int flags) {
|
int flags) {
|
||||||
rtc::Thread* signaling_thread = rtc::Thread::Current();
|
rtc::Thread* signaling_thread = rtc::Thread::Current();
|
||||||
auto channel = absl::make_unique<typename T::Channel>(
|
auto channel = absl::make_unique<typename T::Channel>(
|
||||||
worker_thread, network_thread, signaling_thread, engine, std::move(ch),
|
worker_thread, network_thread, signaling_thread, std::move(ch),
|
||||||
cricket::CN_AUDIO, (flags & DTLS) != 0, webrtc::CryptoOptions());
|
cricket::CN_AUDIO, (flags & DTLS) != 0, webrtc::CryptoOptions(),
|
||||||
|
&ssrc_generator_);
|
||||||
channel->Init_w(rtp_transport, /*media_transport=*/nullptr);
|
channel->Init_w(rtp_transport, /*media_transport=*/nullptr);
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
@ -1434,6 +1436,59 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
|||||||
EXPECT_EQ(kRcvBufSize, option_val);
|
EXPECT_EQ(kRcvBufSize, option_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CreateSimulcastContent(const std::vector<std::string>& rids,
|
||||||
|
typename T::Content* content) {
|
||||||
|
std::vector<RidDescription> rid_descriptions;
|
||||||
|
for (const std::string name : rids) {
|
||||||
|
rid_descriptions.push_back(RidDescription(name, RidDirection::kSend));
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamParams stream;
|
||||||
|
stream.set_rids(rid_descriptions);
|
||||||
|
CreateContent(0, kPcmuCodec, kH264Codec, content);
|
||||||
|
// This is for unified plan, so there can be only one StreamParams.
|
||||||
|
content->mutable_streams().clear();
|
||||||
|
content->AddStream(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifySimulcastStreamParams(const StreamParams& expected,
|
||||||
|
const typename T::Channel* channel) {
|
||||||
|
const std::vector<StreamParams>& streams = channel->local_streams();
|
||||||
|
ASSERT_EQ(1u, streams.size());
|
||||||
|
const StreamParams& result = streams[0];
|
||||||
|
EXPECT_EQ(expected.rids(), result.rids());
|
||||||
|
EXPECT_TRUE(result.has_ssrcs());
|
||||||
|
EXPECT_EQ(expected.rids().size() * 2, result.ssrcs.size());
|
||||||
|
std::vector<uint32_t> primary_ssrcs;
|
||||||
|
result.GetPrimarySsrcs(&primary_ssrcs);
|
||||||
|
EXPECT_EQ(expected.rids().size(), primary_ssrcs.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUpdateLocalStreamsWithSimulcast() {
|
||||||
|
CreateChannels(0, 0);
|
||||||
|
typename T::Content content1, content2, content3;
|
||||||
|
CreateSimulcastContent({"f", "h", "q"}, &content1);
|
||||||
|
EXPECT_TRUE(
|
||||||
|
channel1_->SetLocalContent(&content1, SdpType::kOffer, nullptr));
|
||||||
|
VerifySimulcastStreamParams(content1.streams()[0], channel1_.get());
|
||||||
|
StreamParams stream1 = channel1_->local_streams()[0];
|
||||||
|
|
||||||
|
// Create a similar offer. SetLocalContent should not remove and add.
|
||||||
|
CreateSimulcastContent({"f", "h", "q"}, &content2);
|
||||||
|
EXPECT_TRUE(
|
||||||
|
channel1_->SetLocalContent(&content2, SdpType::kOffer, nullptr));
|
||||||
|
VerifySimulcastStreamParams(content2.streams()[0], channel1_.get());
|
||||||
|
StreamParams stream2 = channel1_->local_streams()[0];
|
||||||
|
// Check that the streams are identical (SSRCs didn't change).
|
||||||
|
EXPECT_EQ(stream1, stream2);
|
||||||
|
|
||||||
|
// Create third offer that has same RIDs in different order.
|
||||||
|
CreateSimulcastContent({"f", "q", "h"}, &content3);
|
||||||
|
EXPECT_TRUE(
|
||||||
|
channel1_->SetLocalContent(&content3, SdpType::kOffer, nullptr));
|
||||||
|
VerifySimulcastStreamParams(content3.streams()[0], channel1_.get());
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void WaitForThreads() { WaitForThreads(rtc::ArrayView<rtc::Thread*>()); }
|
void WaitForThreads() { WaitForThreads(rtc::ArrayView<rtc::Thread*>()); }
|
||||||
static void ProcessThreadQueue(rtc::Thread* thread) {
|
static void ProcessThreadQueue(rtc::Thread* thread) {
|
||||||
@ -1489,6 +1544,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
|
|||||||
int rtcp_mux_activated_callbacks1_ = 0;
|
int rtcp_mux_activated_callbacks1_ = 0;
|
||||||
int rtcp_mux_activated_callbacks2_ = 0;
|
int rtcp_mux_activated_callbacks2_ = 0;
|
||||||
cricket::CandidatePairInterface* last_selected_candidate_pair_;
|
cricket::CandidatePairInterface* last_selected_candidate_pair_;
|
||||||
|
rtc::UniqueRandomIdGenerator ssrc_generator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@ -1562,14 +1618,14 @@ template <>
|
|||||||
std::unique_ptr<cricket::VideoChannel> ChannelTest<VideoTraits>::CreateChannel(
|
std::unique_ptr<cricket::VideoChannel> ChannelTest<VideoTraits>::CreateChannel(
|
||||||
rtc::Thread* worker_thread,
|
rtc::Thread* worker_thread,
|
||||||
rtc::Thread* network_thread,
|
rtc::Thread* network_thread,
|
||||||
cricket::MediaEngineInterface* engine,
|
|
||||||
std::unique_ptr<cricket::FakeVideoMediaChannel> ch,
|
std::unique_ptr<cricket::FakeVideoMediaChannel> ch,
|
||||||
webrtc::RtpTransportInternal* rtp_transport,
|
webrtc::RtpTransportInternal* rtp_transport,
|
||||||
int flags) {
|
int flags) {
|
||||||
rtc::Thread* signaling_thread = rtc::Thread::Current();
|
rtc::Thread* signaling_thread = rtc::Thread::Current();
|
||||||
auto channel = absl::make_unique<cricket::VideoChannel>(
|
auto channel = absl::make_unique<cricket::VideoChannel>(
|
||||||
worker_thread, network_thread, signaling_thread, std::move(ch),
|
worker_thread, network_thread, signaling_thread, std::move(ch),
|
||||||
cricket::CN_VIDEO, (flags & DTLS) != 0, webrtc::CryptoOptions());
|
cricket::CN_VIDEO, (flags & DTLS) != 0, webrtc::CryptoOptions(),
|
||||||
|
&ssrc_generator_);
|
||||||
channel->Init_w(rtp_transport, /*media_transport=*/nullptr);
|
channel->Init_w(rtp_transport, /*media_transport=*/nullptr);
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
@ -2063,6 +2119,10 @@ TEST_F(VideoChannelSingleThreadTest, SocketOptionsMergedOnSetTransport) {
|
|||||||
Base::SocketOptionsMergedOnSetTransport();
|
Base::SocketOptionsMergedOnSetTransport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(VideoChannelSingleThreadTest, UpdateLocalStreamsWithSimulcast) {
|
||||||
|
Base::TestUpdateLocalStreamsWithSimulcast();
|
||||||
|
}
|
||||||
|
|
||||||
// VideoChannelDoubleThreadTest
|
// VideoChannelDoubleThreadTest
|
||||||
TEST_F(VideoChannelDoubleThreadTest, TestInit) {
|
TEST_F(VideoChannelDoubleThreadTest, TestInit) {
|
||||||
Base::TestInit();
|
Base::TestInit();
|
||||||
@ -2231,14 +2291,14 @@ template <>
|
|||||||
std::unique_ptr<cricket::RtpDataChannel> ChannelTest<DataTraits>::CreateChannel(
|
std::unique_ptr<cricket::RtpDataChannel> ChannelTest<DataTraits>::CreateChannel(
|
||||||
rtc::Thread* worker_thread,
|
rtc::Thread* worker_thread,
|
||||||
rtc::Thread* network_thread,
|
rtc::Thread* network_thread,
|
||||||
cricket::MediaEngineInterface* engine,
|
|
||||||
std::unique_ptr<cricket::FakeDataMediaChannel> ch,
|
std::unique_ptr<cricket::FakeDataMediaChannel> ch,
|
||||||
webrtc::RtpTransportInternal* rtp_transport,
|
webrtc::RtpTransportInternal* rtp_transport,
|
||||||
int flags) {
|
int flags) {
|
||||||
rtc::Thread* signaling_thread = rtc::Thread::Current();
|
rtc::Thread* signaling_thread = rtc::Thread::Current();
|
||||||
auto channel = absl::make_unique<cricket::RtpDataChannel>(
|
auto channel = absl::make_unique<cricket::RtpDataChannel>(
|
||||||
worker_thread, network_thread, signaling_thread, std::move(ch),
|
worker_thread, network_thread, signaling_thread, std::move(ch),
|
||||||
cricket::CN_DATA, (flags & DTLS) != 0, webrtc::CryptoOptions());
|
cricket::CN_DATA, (flags & DTLS) != 0, webrtc::CryptoOptions(),
|
||||||
|
&ssrc_generator_);
|
||||||
channel->Init_w(rtp_transport);
|
channel->Init_w(rtp_transport);
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -390,52 +390,24 @@ class UsedRtpHeaderExtensionIds : public UsedIds<webrtc::RtpExtension> {
|
|||||||
static StreamParams CreateStreamParamsForNewSenderWithSsrcs(
|
static StreamParams CreateStreamParamsForNewSenderWithSsrcs(
|
||||||
const SenderOptions& sender,
|
const SenderOptions& sender,
|
||||||
const std::string& rtcp_cname,
|
const std::string& rtcp_cname,
|
||||||
const StreamParamsVec& current_streams,
|
|
||||||
bool include_rtx_streams,
|
bool include_rtx_streams,
|
||||||
bool include_flexfec_stream) {
|
bool include_flexfec_stream,
|
||||||
|
UniqueRandomIdGenerator* ssrc_generator) {
|
||||||
StreamParams result;
|
StreamParams result;
|
||||||
result.id = sender.track_id;
|
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.
|
// TODO(brandtr): Update when we support multistream protection.
|
||||||
if (primary_ssrcs.size() == 1) {
|
if (include_flexfec_stream && sender.num_sim_layers > 1) {
|
||||||
for (uint32_t ssrc : primary_ssrcs) {
|
include_flexfec_stream = false;
|
||||||
result.AddFecFrSsrc(ssrc, ssrc_generator());
|
|
||||||
}
|
|
||||||
} else if (!primary_ssrcs.empty()) {
|
|
||||||
RTC_LOG(LS_WARNING)
|
RTC_LOG(LS_WARNING)
|
||||||
<< "Our FlexFEC implementation only supports protecting "
|
<< "Our FlexFEC implementation only supports protecting "
|
||||||
"a single media streams. This session has multiple "
|
"a single media streams. This session has multiple "
|
||||||
"media streams however, so no FlexFEC SSRC will be generated.";
|
"media streams however, so no FlexFEC SSRC will be generated.";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
result.GenerateSsrcs(sender.num_sim_layers, include_rtx_streams,
|
||||||
|
include_flexfec_stream, ssrc_generator);
|
||||||
|
|
||||||
result.cname = rtcp_cname;
|
result.cname = rtcp_cname;
|
||||||
result.set_stream_ids(sender.stream_ids);
|
result.set_stream_ids(sender.stream_ids);
|
||||||
|
|
||||||
@ -523,6 +495,7 @@ template <class C>
|
|||||||
static bool AddStreamParams(
|
static bool AddStreamParams(
|
||||||
const std::vector<SenderOptions>& sender_options,
|
const std::vector<SenderOptions>& sender_options,
|
||||||
const std::string& rtcp_cname,
|
const std::string& rtcp_cname,
|
||||||
|
UniqueRandomIdGenerator* ssrc_generator,
|
||||||
StreamParamsVec* current_streams,
|
StreamParamsVec* current_streams,
|
||||||
MediaContentDescriptionImpl<C>* content_description) {
|
MediaContentDescriptionImpl<C>* content_description) {
|
||||||
// SCTP streams are not negotiated using SDP/ContentDescriptions.
|
// SCTP streams are not negotiated using SDP/ContentDescriptions.
|
||||||
@ -548,8 +521,8 @@ static bool AddStreamParams(
|
|||||||
?
|
?
|
||||||
// Signal SSRCs and legacy simulcast (if requested).
|
// Signal SSRCs and legacy simulcast (if requested).
|
||||||
CreateStreamParamsForNewSenderWithSsrcs(
|
CreateStreamParamsForNewSenderWithSsrcs(
|
||||||
sender, rtcp_cname, *current_streams, include_rtx_streams,
|
sender, rtcp_cname, include_rtx_streams,
|
||||||
include_flexfec_stream)
|
include_flexfec_stream, ssrc_generator)
|
||||||
:
|
:
|
||||||
// Signal RIDs and spec-compliant simulcast (if requested).
|
// Signal RIDs and spec-compliant simulcast (if requested).
|
||||||
CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname);
|
CreateStreamParamsForNewSenderWithRids(sender, rtcp_cname);
|
||||||
@ -787,6 +760,7 @@ static bool CreateMediaContentOffer(
|
|||||||
const CryptoParamsVec* current_cryptos,
|
const CryptoParamsVec* current_cryptos,
|
||||||
const std::vector<std::string>& crypto_suites,
|
const std::vector<std::string>& crypto_suites,
|
||||||
const RtpHeaderExtensions& rtp_extensions,
|
const RtpHeaderExtensions& rtp_extensions,
|
||||||
|
UniqueRandomIdGenerator* ssrc_generator,
|
||||||
StreamParamsVec* current_streams,
|
StreamParamsVec* current_streams,
|
||||||
MediaContentDescriptionImpl<C>* offer) {
|
MediaContentDescriptionImpl<C>* offer) {
|
||||||
offer->AddCodecs(codecs);
|
offer->AddCodecs(codecs);
|
||||||
@ -798,7 +772,8 @@ static bool CreateMediaContentOffer(
|
|||||||
offer->set_rtp_header_extensions(rtp_extensions);
|
offer->set_rtp_header_extensions(rtp_extensions);
|
||||||
|
|
||||||
if (!AddStreamParams(media_description_options.sender_options,
|
if (!AddStreamParams(media_description_options.sender_options,
|
||||||
session_options.rtcp_cname, current_streams, offer)) {
|
session_options.rtcp_cname, ssrc_generator,
|
||||||
|
current_streams, offer)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1166,6 +1141,7 @@ static bool CreateMediaContentAnswer(
|
|||||||
const SecurePolicy& sdes_policy,
|
const SecurePolicy& sdes_policy,
|
||||||
const CryptoParamsVec* current_cryptos,
|
const CryptoParamsVec* current_cryptos,
|
||||||
const RtpHeaderExtensions& local_rtp_extenstions,
|
const RtpHeaderExtensions& local_rtp_extenstions,
|
||||||
|
UniqueRandomIdGenerator* ssrc_generator,
|
||||||
bool enable_encrypted_rtp_header_extensions,
|
bool enable_encrypted_rtp_header_extensions,
|
||||||
StreamParamsVec* current_streams,
|
StreamParamsVec* current_streams,
|
||||||
bool bundle_enabled,
|
bool bundle_enabled,
|
||||||
@ -1203,7 +1179,8 @@ static bool CreateMediaContentAnswer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!AddStreamParams(media_description_options.sender_options,
|
if (!AddStreamParams(media_description_options.sender_options,
|
||||||
session_options.rtcp_cname, current_streams, answer)) {
|
session_options.rtcp_cname, ssrc_generator,
|
||||||
|
current_streams, answer)) {
|
||||||
return false; // Something went seriously wrong.
|
return false; // Something went seriously wrong.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1343,13 +1320,18 @@ bool MediaSessionOptions::HasMediaDescription(MediaType type) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
|
MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
|
||||||
const TransportDescriptionFactory* transport_desc_factory)
|
const TransportDescriptionFactory* transport_desc_factory,
|
||||||
: transport_desc_factory_(transport_desc_factory) {}
|
rtc::UniqueRandomIdGenerator* ssrc_generator)
|
||||||
|
: ssrc_generator_(ssrc_generator),
|
||||||
|
transport_desc_factory_(transport_desc_factory) {
|
||||||
|
RTC_DCHECK(ssrc_generator_);
|
||||||
|
}
|
||||||
|
|
||||||
MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
|
MediaSessionDescriptionFactory::MediaSessionDescriptionFactory(
|
||||||
ChannelManager* channel_manager,
|
ChannelManager* channel_manager,
|
||||||
const TransportDescriptionFactory* transport_desc_factory)
|
const TransportDescriptionFactory* transport_desc_factory,
|
||||||
: transport_desc_factory_(transport_desc_factory) {
|
rtc::UniqueRandomIdGenerator* ssrc_generator)
|
||||||
|
: MediaSessionDescriptionFactory(transport_desc_factory, ssrc_generator) {
|
||||||
channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
|
channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
|
||||||
channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
|
channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
|
||||||
channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
|
channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
|
||||||
@ -2039,10 +2021,11 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer(
|
|||||||
std::vector<std::string> crypto_suites;
|
std::vector<std::string> crypto_suites;
|
||||||
GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
|
GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
|
||||||
&crypto_suites);
|
&crypto_suites);
|
||||||
if (!CreateMediaContentOffer(
|
if (!CreateMediaContentOffer(media_description_options, session_options,
|
||||||
media_description_options, session_options, filtered_codecs,
|
filtered_codecs, sdes_policy,
|
||||||
sdes_policy, GetCryptos(current_content), crypto_suites,
|
GetCryptos(current_content), crypto_suites,
|
||||||
audio_rtp_extensions, current_streams, audio.get())) {
|
audio_rtp_extensions, ssrc_generator_,
|
||||||
|
current_streams, audio.get())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2109,10 +2092,11 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CreateMediaContentOffer(
|
if (!CreateMediaContentOffer(media_description_options, session_options,
|
||||||
media_description_options, session_options, filtered_codecs,
|
filtered_codecs, sdes_policy,
|
||||||
sdes_policy, GetCryptos(current_content), crypto_suites,
|
GetCryptos(current_content), crypto_suites,
|
||||||
video_rtp_extensions, current_streams, video.get())) {
|
video_rtp_extensions, ssrc_generator_,
|
||||||
|
current_streams, video.get())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2180,7 +2164,7 @@ bool MediaSessionDescriptionFactory::AddDataContentForOffer(
|
|||||||
if (!CreateMediaContentOffer(
|
if (!CreateMediaContentOffer(
|
||||||
media_description_options, session_options, data_codecs, sdes_policy,
|
media_description_options, session_options, data_codecs, sdes_policy,
|
||||||
GetCryptos(current_content), crypto_suites, RtpHeaderExtensions(),
|
GetCryptos(current_content), crypto_suites, RtpHeaderExtensions(),
|
||||||
current_streams, data.get())) {
|
ssrc_generator_, current_streams, data.get())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2283,7 +2267,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer(
|
|||||||
if (!CreateMediaContentAnswer(
|
if (!CreateMediaContentAnswer(
|
||||||
offer_audio_description, media_description_options, session_options,
|
offer_audio_description, media_description_options, session_options,
|
||||||
filtered_codecs, sdes_policy, GetCryptos(current_content),
|
filtered_codecs, sdes_policy, GetCryptos(current_content),
|
||||||
audio_rtp_header_extensions(),
|
audio_rtp_header_extensions(), ssrc_generator_,
|
||||||
enable_encrypted_rtp_header_extensions_, current_streams,
|
enable_encrypted_rtp_header_extensions_, current_streams,
|
||||||
bundle_enabled, audio_answer.get())) {
|
bundle_enabled, audio_answer.get())) {
|
||||||
return false; // Fails the session setup.
|
return false; // Fails the session setup.
|
||||||
@ -2372,7 +2356,7 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer(
|
|||||||
if (!CreateMediaContentAnswer(
|
if (!CreateMediaContentAnswer(
|
||||||
offer_video_description, media_description_options, session_options,
|
offer_video_description, media_description_options, session_options,
|
||||||
filtered_codecs, sdes_policy, GetCryptos(current_content),
|
filtered_codecs, sdes_policy, GetCryptos(current_content),
|
||||||
video_rtp_header_extensions(),
|
video_rtp_header_extensions(), ssrc_generator_,
|
||||||
enable_encrypted_rtp_header_extensions_, current_streams,
|
enable_encrypted_rtp_header_extensions_, current_streams,
|
||||||
bundle_enabled, video_answer.get())) {
|
bundle_enabled, video_answer.get())) {
|
||||||
return false; // Failed the sessin setup.
|
return false; // Failed the sessin setup.
|
||||||
@ -2432,8 +2416,9 @@ bool MediaSessionDescriptionFactory::AddDataContentForAnswer(
|
|||||||
if (!CreateMediaContentAnswer(
|
if (!CreateMediaContentAnswer(
|
||||||
offer_data_description, media_description_options, session_options,
|
offer_data_description, media_description_options, session_options,
|
||||||
data_codecs, sdes_policy, GetCryptos(current_content),
|
data_codecs, sdes_policy, GetCryptos(current_content),
|
||||||
RtpHeaderExtensions(), enable_encrypted_rtp_header_extensions_,
|
RtpHeaderExtensions(), ssrc_generator_,
|
||||||
current_streams, bundle_enabled, data_answer.get())) {
|
enable_encrypted_rtp_header_extensions_, current_streams,
|
||||||
|
bundle_enabled, data_answer.get())) {
|
||||||
return false; // Fails the session setup.
|
return false; // Fails the session setup.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
#include "p2p/base/transport_description_factory.h"
|
#include "p2p/base/transport_description_factory.h"
|
||||||
#include "pc/jsep_transport.h"
|
#include "pc/jsep_transport.h"
|
||||||
#include "pc/session_description.h"
|
#include "pc/session_description.h"
|
||||||
|
#include "rtc_base/unique_id_generator.h"
|
||||||
|
|
||||||
namespace cricket {
|
namespace cricket {
|
||||||
|
|
||||||
@ -127,15 +128,19 @@ struct MediaSessionOptions {
|
|||||||
// of the various fields to determine the proper result.
|
// of the various fields to determine the proper result.
|
||||||
class MediaSessionDescriptionFactory {
|
class MediaSessionDescriptionFactory {
|
||||||
public:
|
public:
|
||||||
// Default ctor; use methods below to set configuration.
|
// Simple constructor that does not set any configuration for the factory.
|
||||||
// The TransportDescriptionFactory is not owned by MediaSessionDescFactory,
|
// When using this constructor, the methods below can be used to set the
|
||||||
// so it must be kept alive by the user of this class.
|
// configuration.
|
||||||
explicit MediaSessionDescriptionFactory(
|
// The TransportDescriptionFactory and the UniqueRandomIdGenerator are not
|
||||||
const TransportDescriptionFactory* factory);
|
// owned by MediaSessionDescriptionFactory, so they must be kept alive by the
|
||||||
|
// user of this class.
|
||||||
|
MediaSessionDescriptionFactory(const TransportDescriptionFactory* factory,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||||
// This helper automatically sets up the factory to get its configuration
|
// This helper automatically sets up the factory to get its configuration
|
||||||
// from the specified ChannelManager.
|
// from the specified ChannelManager.
|
||||||
MediaSessionDescriptionFactory(ChannelManager* cmanager,
|
MediaSessionDescriptionFactory(ChannelManager* cmanager,
|
||||||
const TransportDescriptionFactory* factory);
|
const TransportDescriptionFactory* factory,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||||
|
|
||||||
const AudioCodecs& audio_sendrecv_codecs() const;
|
const AudioCodecs& audio_sendrecv_codecs() const;
|
||||||
const AudioCodecs& audio_send_codecs() const;
|
const AudioCodecs& audio_send_codecs() const;
|
||||||
@ -300,6 +305,8 @@ class MediaSessionDescriptionFactory {
|
|||||||
VideoCodecs video_codecs_;
|
VideoCodecs video_codecs_;
|
||||||
RtpHeaderExtensions video_rtp_extensions_;
|
RtpHeaderExtensions video_rtp_extensions_;
|
||||||
DataCodecs data_codecs_;
|
DataCodecs data_codecs_;
|
||||||
|
// This object is not owned by the channel so it must outlive it.
|
||||||
|
rtc::UniqueRandomIdGenerator* const ssrc_generator_;
|
||||||
bool enable_encrypted_rtp_header_extensions_ = false;
|
bool enable_encrypted_rtp_header_extensions_ = false;
|
||||||
// TODO(zhihuang): Rename secure_ to sdec_policy_; rename the related getter
|
// TODO(zhihuang): Rename secure_ to sdec_policy_; rename the related getter
|
||||||
// and setter.
|
// and setter.
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
#include "rtc_base/message_digest.h"
|
#include "rtc_base/message_digest.h"
|
||||||
#include "rtc_base/ssl_adapter.h"
|
#include "rtc_base/ssl_adapter.h"
|
||||||
#include "rtc_base/strings/string_builder.h"
|
#include "rtc_base/strings/string_builder.h"
|
||||||
|
#include "rtc_base/unique_id_generator.h"
|
||||||
#include "test/gmock.h"
|
#include "test/gmock.h"
|
||||||
|
|
||||||
#define ASSERT_CRYPTO(cd, s, cs) \
|
#define ASSERT_CRYPTO(cd, s, cs) \
|
||||||
@ -79,6 +80,7 @@ using rtc::CS_AEAD_AES_128_GCM;
|
|||||||
using rtc::CS_AEAD_AES_256_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_32;
|
||||||
using rtc::CS_AES_CM_128_HMAC_SHA1_80;
|
using rtc::CS_AES_CM_128_HMAC_SHA1_80;
|
||||||
|
using rtc::UniqueRandomIdGenerator;
|
||||||
using testing::Each;
|
using testing::Each;
|
||||||
using testing::ElementsAreArray;
|
using testing::ElementsAreArray;
|
||||||
using testing::Eq;
|
using testing::Eq;
|
||||||
@ -382,7 +384,8 @@ static MediaSessionOptions CreatePlanBMediaSessionOptions() {
|
|||||||
// these tests may be obsolete as a result, and should be refactored or removed.
|
// these tests may be obsolete as a result, and should be refactored or removed.
|
||||||
class MediaSessionDescriptionFactoryTest : public testing::Test {
|
class MediaSessionDescriptionFactoryTest : public testing::Test {
|
||||||
public:
|
public:
|
||||||
MediaSessionDescriptionFactoryTest() : f1_(&tdf1_), f2_(&tdf2_) {
|
MediaSessionDescriptionFactoryTest()
|
||||||
|
: f1_(&tdf1_, &ssrc_generator1), f2_(&tdf2_, &ssrc_generator2) {
|
||||||
f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
|
f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
|
||||||
MAKE_VECTOR(kAudioCodecs1));
|
MAKE_VECTOR(kAudioCodecs1));
|
||||||
f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1));
|
f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1));
|
||||||
@ -688,6 +691,8 @@ class MediaSessionDescriptionFactoryTest : public testing::Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
UniqueRandomIdGenerator ssrc_generator1;
|
||||||
|
UniqueRandomIdGenerator ssrc_generator2;
|
||||||
MediaSessionDescriptionFactory f1_;
|
MediaSessionDescriptionFactory f1_;
|
||||||
MediaSessionDescriptionFactory f2_;
|
MediaSessionDescriptionFactory f2_;
|
||||||
TransportDescriptionFactory tdf1_;
|
TransportDescriptionFactory tdf1_;
|
||||||
@ -4016,7 +4021,8 @@ TEST_F(MediaSessionDescriptionFactoryTest,
|
|||||||
|
|
||||||
class MediaProtocolTest : public ::testing::TestWithParam<const char*> {
|
class MediaProtocolTest : public ::testing::TestWithParam<const char*> {
|
||||||
public:
|
public:
|
||||||
MediaProtocolTest() : f1_(&tdf1_), f2_(&tdf2_) {
|
MediaProtocolTest()
|
||||||
|
: f1_(&tdf1_, &ssrc_generator1), f2_(&tdf2_, &ssrc_generator2) {
|
||||||
f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
|
f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
|
||||||
MAKE_VECTOR(kAudioCodecs1));
|
MAKE_VECTOR(kAudioCodecs1));
|
||||||
f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1));
|
f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1));
|
||||||
@ -4040,6 +4046,8 @@ class MediaProtocolTest : public ::testing::TestWithParam<const char*> {
|
|||||||
MediaSessionDescriptionFactory f2_;
|
MediaSessionDescriptionFactory f2_;
|
||||||
TransportDescriptionFactory tdf1_;
|
TransportDescriptionFactory tdf1_;
|
||||||
TransportDescriptionFactory tdf2_;
|
TransportDescriptionFactory tdf2_;
|
||||||
|
UniqueRandomIdGenerator ssrc_generator1;
|
||||||
|
UniqueRandomIdGenerator ssrc_generator2;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_P(MediaProtocolTest, TestAudioVideoAcceptance) {
|
TEST_P(MediaProtocolTest, TestAudioVideoAcceptance) {
|
||||||
@ -4074,7 +4082,8 @@ INSTANTIATE_TEST_CASE_P(MediaProtocolDtlsPatternTest,
|
|||||||
|
|
||||||
TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) {
|
TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) {
|
||||||
TransportDescriptionFactory tdf;
|
TransportDescriptionFactory tdf;
|
||||||
MediaSessionDescriptionFactory sf(&tdf);
|
UniqueRandomIdGenerator ssrc_generator;
|
||||||
|
MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator);
|
||||||
std::vector<AudioCodec> send_codecs = MAKE_VECTOR(kAudioCodecs1);
|
std::vector<AudioCodec> send_codecs = MAKE_VECTOR(kAudioCodecs1);
|
||||||
std::vector<AudioCodec> recv_codecs = MAKE_VECTOR(kAudioCodecs2);
|
std::vector<AudioCodec> recv_codecs = MAKE_VECTOR(kAudioCodecs2);
|
||||||
|
|
||||||
@ -4130,7 +4139,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, TestSetAudioCodecs) {
|
|||||||
// an object that is not semantically equivalent to the set object.
|
// an object that is not semantically equivalent to the set object.
|
||||||
TEST_F(MediaSessionDescriptionFactoryTest, VideoHasRidExtensionsInUnifiedPlan) {
|
TEST_F(MediaSessionDescriptionFactoryTest, VideoHasRidExtensionsInUnifiedPlan) {
|
||||||
TransportDescriptionFactory tdf;
|
TransportDescriptionFactory tdf;
|
||||||
MediaSessionDescriptionFactory sf(&tdf);
|
UniqueRandomIdGenerator ssrc_generator;
|
||||||
|
MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator);
|
||||||
sf.set_is_unified_plan(true);
|
sf.set_is_unified_plan(true);
|
||||||
cricket::RtpHeaderExtensions extensions;
|
cricket::RtpHeaderExtensions extensions;
|
||||||
sf.set_video_rtp_header_extensions(extensions);
|
sf.set_video_rtp_header_extensions(extensions);
|
||||||
@ -4155,7 +4165,8 @@ TEST_F(MediaSessionDescriptionFactoryTest, VideoHasRidExtensionsInUnifiedPlan) {
|
|||||||
// an object that is not semantically equivalent to the set object.
|
// an object that is not semantically equivalent to the set object.
|
||||||
TEST_F(MediaSessionDescriptionFactoryTest, AudioHasRidExtensionsInUnifiedPlan) {
|
TEST_F(MediaSessionDescriptionFactoryTest, AudioHasRidExtensionsInUnifiedPlan) {
|
||||||
TransportDescriptionFactory tdf;
|
TransportDescriptionFactory tdf;
|
||||||
MediaSessionDescriptionFactory sf(&tdf);
|
UniqueRandomIdGenerator ssrc_generator;
|
||||||
|
MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator);
|
||||||
sf.set_is_unified_plan(true);
|
sf.set_is_unified_plan(true);
|
||||||
cricket::RtpHeaderExtensions extensions;
|
cricket::RtpHeaderExtensions extensions;
|
||||||
sf.set_audio_rtp_header_extensions(extensions);
|
sf.set_audio_rtp_header_extensions(extensions);
|
||||||
@ -4193,7 +4204,8 @@ bool CodecsMatch(const std::vector<Codec>& codecs1,
|
|||||||
|
|
||||||
void TestAudioCodecsOffer(RtpTransceiverDirection direction) {
|
void TestAudioCodecsOffer(RtpTransceiverDirection direction) {
|
||||||
TransportDescriptionFactory tdf;
|
TransportDescriptionFactory tdf;
|
||||||
MediaSessionDescriptionFactory sf(&tdf);
|
UniqueRandomIdGenerator ssrc_generator;
|
||||||
|
MediaSessionDescriptionFactory sf(&tdf, &ssrc_generator);
|
||||||
const std::vector<AudioCodec> send_codecs = MAKE_VECTOR(kAudioCodecs1);
|
const std::vector<AudioCodec> send_codecs = MAKE_VECTOR(kAudioCodecs1);
|
||||||
const std::vector<AudioCodec> recv_codecs = MAKE_VECTOR(kAudioCodecs2);
|
const std::vector<AudioCodec> recv_codecs = MAKE_VECTOR(kAudioCodecs2);
|
||||||
const std::vector<AudioCodec> sendrecv_codecs =
|
const std::vector<AudioCodec> sendrecv_codecs =
|
||||||
@ -4290,8 +4302,9 @@ void TestAudioCodecsAnswer(RtpTransceiverDirection offer_direction,
|
|||||||
bool add_legacy_stream) {
|
bool add_legacy_stream) {
|
||||||
TransportDescriptionFactory offer_tdf;
|
TransportDescriptionFactory offer_tdf;
|
||||||
TransportDescriptionFactory answer_tdf;
|
TransportDescriptionFactory answer_tdf;
|
||||||
MediaSessionDescriptionFactory offer_factory(&offer_tdf);
|
UniqueRandomIdGenerator ssrc_generator1, ssrc_generator2;
|
||||||
MediaSessionDescriptionFactory answer_factory(&answer_tdf);
|
MediaSessionDescriptionFactory offer_factory(&offer_tdf, &ssrc_generator1);
|
||||||
|
MediaSessionDescriptionFactory answer_factory(&answer_tdf, &ssrc_generator2);
|
||||||
offer_factory.set_audio_codecs(
|
offer_factory.set_audio_codecs(
|
||||||
VectorFromIndices(kOfferAnswerCodecs, kOfferSendCodecs),
|
VectorFromIndices(kOfferAnswerCodecs, kOfferSendCodecs),
|
||||||
VectorFromIndices(kOfferAnswerCodecs, kOfferRecvCodecs));
|
VectorFromIndices(kOfferAnswerCodecs, kOfferRecvCodecs));
|
||||||
|
|||||||
@ -59,8 +59,13 @@ using cricket::ContentInfo;
|
|||||||
using cricket::ContentInfos;
|
using cricket::ContentInfos;
|
||||||
using cricket::MediaContentDescription;
|
using cricket::MediaContentDescription;
|
||||||
using cricket::MediaProtocolType;
|
using cricket::MediaProtocolType;
|
||||||
|
using cricket::RidDescription;
|
||||||
|
using cricket::RidDirection;
|
||||||
using cricket::SessionDescription;
|
using cricket::SessionDescription;
|
||||||
|
using cricket::SimulcastDescription;
|
||||||
|
using cricket::SimulcastLayer;
|
||||||
using cricket::SimulcastLayerList;
|
using cricket::SimulcastLayerList;
|
||||||
|
using cricket::StreamParams;
|
||||||
using cricket::TransportInfo;
|
using cricket::TransportInfo;
|
||||||
|
|
||||||
using cricket::LOCAL_PORT_TYPE;
|
using cricket::LOCAL_PORT_TYPE;
|
||||||
@ -1092,7 +1097,7 @@ bool PeerConnection::Initialize(
|
|||||||
|
|
||||||
webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
|
webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
|
||||||
signaling_thread(), channel_manager(), this, session_id(),
|
signaling_thread(), channel_manager(), this, session_id(),
|
||||||
std::move(dependencies.cert_generator), certificate));
|
std::move(dependencies.cert_generator), certificate, &ssrc_generator_));
|
||||||
webrtc_session_desc_factory_->SignalCertificateReady.connect(
|
webrtc_session_desc_factory_->SignalCertificateReady.connect(
|
||||||
this, &PeerConnection::OnCertificateReady);
|
this, &PeerConnection::OnCertificateReady);
|
||||||
|
|
||||||
@ -2198,18 +2203,20 @@ RTCError PeerConnection::ApplyLocalDescription(
|
|||||||
if (!content) {
|
if (!content) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto& streams = content->media_description()->streams();
|
cricket::ChannelInterface* channel = transceiver->internal()->channel();
|
||||||
if (!content->rejected && !streams.empty()) {
|
if (content->rejected || !channel || channel->local_streams().empty()) {
|
||||||
transceiver->internal()->sender_internal()->set_stream_ids(
|
|
||||||
streams[0].stream_ids());
|
|
||||||
transceiver->internal()->sender_internal()->SetSsrc(
|
|
||||||
streams[0].first_ssrc());
|
|
||||||
} else {
|
|
||||||
// 0 is a special value meaning "this sender has no associated send
|
// 0 is a special value meaning "this sender has no associated send
|
||||||
// stream". Need to call this so the sender won't attempt to configure
|
// stream". Need to call this so the sender won't attempt to configure
|
||||||
// a no longer existing stream and run into DCHECKs in the lower
|
// a no longer existing stream and run into DCHECKs in the lower
|
||||||
// layers.
|
// layers.
|
||||||
transceiver->internal()->sender_internal()->SetSsrc(0);
|
transceiver->internal()->sender_internal()->SetSsrc(0);
|
||||||
|
} else {
|
||||||
|
// Get the StreamParams from the channel which could generate SSRCs.
|
||||||
|
const std::vector<StreamParams>& streams = channel->local_streams();
|
||||||
|
transceiver->internal()->sender_internal()->set_stream_ids(
|
||||||
|
streams[0].stream_ids());
|
||||||
|
transceiver->internal()->sender_internal()->SetSsrc(
|
||||||
|
streams[0].first_ssrc());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -2911,6 +2918,72 @@ RTCError PeerConnection::UpdateDataChannel(
|
|||||||
return RTCError::OK();
|
return RTCError::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This method will extract any send encodings that were sent by the remote
|
||||||
|
// connection. This is currently only relevant for Simulcast scenario (where
|
||||||
|
// the number of layers may be communicated by the server).
|
||||||
|
static std::vector<RtpEncodingParameters> GetSendEncodingsFromRemoteDescription(
|
||||||
|
const MediaContentDescription& desc) {
|
||||||
|
if (!desc.HasSimulcast()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::vector<RtpEncodingParameters> result;
|
||||||
|
const SimulcastDescription& simulcast = desc.simulcast_description();
|
||||||
|
|
||||||
|
// This is a remote description, the parameters we are after should appear
|
||||||
|
// as receive streams.
|
||||||
|
for (const auto& alternatives : simulcast.receive_layers()) {
|
||||||
|
RTC_DCHECK(!alternatives.empty());
|
||||||
|
// There is currently no way to specify or choose from alternatives.
|
||||||
|
// We will always use the first alternative, which is the most preferred.
|
||||||
|
const SimulcastLayer& layer = alternatives[0];
|
||||||
|
RtpEncodingParameters parameters;
|
||||||
|
parameters.rid = layer.rid;
|
||||||
|
parameters.active = !layer.is_paused;
|
||||||
|
result.push_back(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RTCError UpdateSimulcastLayerStatusInSender(
|
||||||
|
const std::vector<SimulcastLayer>& layers,
|
||||||
|
RtpSenderInterface* sender) {
|
||||||
|
RTC_DCHECK(sender);
|
||||||
|
RtpParameters parameters = sender->GetParameters();
|
||||||
|
|
||||||
|
// The simulcast envelope cannot be changed, only the status of the streams.
|
||||||
|
// So we will iterate over the send encodings rather than the layers.
|
||||||
|
for (RtpEncodingParameters& encoding : parameters.encodings) {
|
||||||
|
auto iter = std::find_if(layers.begin(), layers.end(),
|
||||||
|
[&encoding](const SimulcastLayer& layer) {
|
||||||
|
return layer.rid == encoding.rid;
|
||||||
|
});
|
||||||
|
// A layer that cannot be found may have been removed by the remote party.
|
||||||
|
encoding.active = iter != layers.end() && !iter->is_paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sender->SetParameters(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RTCError DisableSimulcastInSender(RtpSenderInterface* sender) {
|
||||||
|
RTC_DCHECK(sender);
|
||||||
|
RtpParameters parameters = sender->GetParameters();
|
||||||
|
if (parameters.encodings.empty()) {
|
||||||
|
return RTCError::OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool first_layer_status = parameters.encodings[0].active;
|
||||||
|
for (RtpEncodingParameters& encoding : parameters.encodings) {
|
||||||
|
// TODO(bugs.webrtc.org/10251): Active does not really disable the layer.
|
||||||
|
// The user might enable it at a later point in time even though it was
|
||||||
|
// rejected.
|
||||||
|
encoding.active = false;
|
||||||
|
}
|
||||||
|
parameters.encodings[0].active = first_layer_status;
|
||||||
|
|
||||||
|
return sender->SetParameters(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
|
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
|
||||||
PeerConnection::AssociateTransceiver(cricket::ContentSource source,
|
PeerConnection::AssociateTransceiver(cricket::ContentSource source,
|
||||||
SdpType type,
|
SdpType type,
|
||||||
@ -2969,8 +3042,10 @@ PeerConnection::AssociateTransceiver(cricket::ContentSource source,
|
|||||||
<< " at i=" << mline_index
|
<< " at i=" << mline_index
|
||||||
<< " in response to the remote description.";
|
<< " in response to the remote description.";
|
||||||
std::string sender_id = rtc::CreateRandomUuid();
|
std::string sender_id = rtc::CreateRandomUuid();
|
||||||
auto sender =
|
std::vector<RtpEncodingParameters> send_encodings =
|
||||||
CreateSender(media_desc->type(), sender_id, nullptr, {}, {});
|
GetSendEncodingsFromRemoteDescription(*media_desc);
|
||||||
|
auto sender = CreateSender(media_desc->type(), sender_id, nullptr, {},
|
||||||
|
send_encodings);
|
||||||
std::string receiver_id;
|
std::string receiver_id;
|
||||||
if (!media_desc->streams().empty()) {
|
if (!media_desc->streams().empty()) {
|
||||||
receiver_id = media_desc->streams()[0].id;
|
receiver_id = media_desc->streams()[0].id;
|
||||||
@ -2982,6 +3057,22 @@ PeerConnection::AssociateTransceiver(cricket::ContentSource source,
|
|||||||
transceiver->internal()->set_direction(
|
transceiver->internal()->set_direction(
|
||||||
RtpTransceiverDirection::kRecvOnly);
|
RtpTransceiverDirection::kRecvOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the offer indicated simulcast but the answer rejected it.
|
||||||
|
// This can happen when simulcast is not supported on the remote party.
|
||||||
|
// This check can be simplified to comparing the number of send encodings,
|
||||||
|
// but that might break legacy implementation in which simulcast is not
|
||||||
|
// signaled in the remote description.
|
||||||
|
if (old_local_content && old_local_content->media_description() &&
|
||||||
|
old_local_content->media_description()->HasSimulcast() &&
|
||||||
|
!media_desc->HasSimulcast()) {
|
||||||
|
RTCError error =
|
||||||
|
DisableSimulcastInSender(transceiver->internal()->sender());
|
||||||
|
if (!error.ok()) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast.";
|
||||||
|
return std::move(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RTC_DCHECK(transceiver);
|
RTC_DCHECK(transceiver);
|
||||||
if (transceiver->media_type() != media_desc->type()) {
|
if (transceiver->media_type() != media_desc->type()) {
|
||||||
@ -2989,6 +3080,20 @@ PeerConnection::AssociateTransceiver(cricket::ContentSource source,
|
|||||||
RTCErrorType::INVALID_PARAMETER,
|
RTCErrorType::INVALID_PARAMETER,
|
||||||
"Transceiver type does not match media description type.");
|
"Transceiver type does not match media description type.");
|
||||||
}
|
}
|
||||||
|
if (media_desc->HasSimulcast()) {
|
||||||
|
std::vector<SimulcastLayer> layers =
|
||||||
|
source == cricket::CS_LOCAL
|
||||||
|
? media_desc->simulcast_description().send_layers().GetAllLayers()
|
||||||
|
: media_desc->simulcast_description()
|
||||||
|
.receive_layers()
|
||||||
|
.GetAllLayers();
|
||||||
|
RTCError error = UpdateSimulcastLayerStatusInSender(
|
||||||
|
layers, transceiver->internal()->sender());
|
||||||
|
if (!error.ok()) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers.";
|
||||||
|
return std::move(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Associate the found or created RtpTransceiver with the m= section by
|
// Associate the found or created RtpTransceiver with the m= section by
|
||||||
// setting the value of the RtpTransceiver's mid property to the MID of the m=
|
// setting the value of the RtpTransceiver's mid property to the MID of the m=
|
||||||
// section, and establish a mapping between the transceiver and the index of
|
// section, and establish a mapping between the transceiver and the index of
|
||||||
@ -4035,6 +4140,20 @@ void PeerConnection::GetOptionsForPlanBOffer(
|
|||||||
offer_answer_options.num_simulcast_layers);
|
offer_answer_options.num_simulcast_layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void GetSimulcastInformationFromRtpParameters(
|
||||||
|
const RtpParameters& parameters,
|
||||||
|
RidDirection direction,
|
||||||
|
std::vector<RidDescription>* rids,
|
||||||
|
SimulcastLayerList* layers) {
|
||||||
|
for (const RtpEncodingParameters& encoding : parameters.encodings) {
|
||||||
|
if (encoding.rid.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rids->push_back(RidDescription(encoding.rid, direction));
|
||||||
|
layers->AddLayer(SimulcastLayer(encoding.rid, !encoding.active));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static cricket::MediaDescriptionOptions
|
static cricket::MediaDescriptionOptions
|
||||||
GetMediaDescriptionOptionsForTransceiver(
|
GetMediaDescriptionOptionsForTransceiver(
|
||||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
|
||||||
@ -4048,18 +4167,46 @@ GetMediaDescriptionOptionsForTransceiver(
|
|||||||
// sendrecv.
|
// sendrecv.
|
||||||
// 2. If the MSID is included, then it must be included in any subsequent
|
// 2. If the MSID is included, then it must be included in any subsequent
|
||||||
// offer/answer exactly the same until the RtpTransceiver is stopped.
|
// offer/answer exactly the same until the RtpTransceiver is stopped.
|
||||||
if (!transceiver->stopped() &&
|
if (transceiver->stopped() ||
|
||||||
(RtpTransceiverDirectionHasSend(transceiver->direction()) ||
|
(!RtpTransceiverDirectionHasSend(transceiver->direction()) &&
|
||||||
transceiver->internal()->has_ever_been_used_to_send())) {
|
!transceiver->internal()->has_ever_been_used_to_send())) {
|
||||||
|
return media_description_options;
|
||||||
|
}
|
||||||
|
|
||||||
cricket::SenderOptions sender_options;
|
cricket::SenderOptions sender_options;
|
||||||
sender_options.track_id = transceiver->sender()->id();
|
sender_options.track_id = transceiver->sender()->id();
|
||||||
sender_options.stream_ids = transceiver->sender()->stream_ids();
|
sender_options.stream_ids = transceiver->sender()->stream_ids();
|
||||||
int num_send_encoding_layers =
|
|
||||||
transceiver->sender()->init_send_encodings().size();
|
// The following sets up RIDs and Simulcast.
|
||||||
sender_options.num_sim_layers =
|
// RIDs are included if Simulcast is requested or if any RID was specified.
|
||||||
!num_send_encoding_layers ? 1 : num_send_encoding_layers;
|
RtpParameters send_parameters = transceiver->sender()->GetParameters();
|
||||||
media_description_options.sender_options.push_back(sender_options);
|
RtpParameters receive_parameters = transceiver->receiver()->GetParameters();
|
||||||
|
bool has_rids = std::any_of(send_parameters.encodings.begin(),
|
||||||
|
send_parameters.encodings.end(),
|
||||||
|
[](const RtpEncodingParameters& encoding) {
|
||||||
|
return !encoding.rid.empty();
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<RidDescription> send_rids, receive_rids;
|
||||||
|
SimulcastLayerList send_layers, receive_layers;
|
||||||
|
GetSimulcastInformationFromRtpParameters(send_parameters, RidDirection::kSend,
|
||||||
|
&send_rids, &send_layers);
|
||||||
|
GetSimulcastInformationFromRtpParameters(receive_parameters,
|
||||||
|
RidDirection::kReceive,
|
||||||
|
&receive_rids, &receive_layers);
|
||||||
|
if (has_rids) {
|
||||||
|
sender_options.rids = send_rids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sender_options.simulcast_layers = send_layers;
|
||||||
|
media_description_options.receive_simulcast_layers = receive_layers;
|
||||||
|
media_description_options.receive_rids = receive_rids;
|
||||||
|
// When RIDs are configured, we must set num_sim_layers to 0 to.
|
||||||
|
// Otherwise, num_sim_layers must be 1 because either there is no
|
||||||
|
// simulcast, or simulcast is acheived by munging the SDP.
|
||||||
|
sender_options.num_sim_layers = has_rids ? 0 : 1;
|
||||||
|
media_description_options.sender_options.push_back(sender_options);
|
||||||
|
|
||||||
return media_description_options;
|
return media_description_options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5930,7 +6077,7 @@ cricket::VoiceChannel* PeerConnection::CreateVoiceChannel(
|
|||||||
cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel(
|
cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel(
|
||||||
call_.get(), configuration_.media_config, rtp_transport, media_transport,
|
call_.get(), configuration_.media_config, rtp_transport, media_transport,
|
||||||
signaling_thread(), mid, SrtpRequired(), GetCryptoOptions(),
|
signaling_thread(), mid, SrtpRequired(), GetCryptoOptions(),
|
||||||
audio_options_);
|
&ssrc_generator_, audio_options_);
|
||||||
if (!voice_channel) {
|
if (!voice_channel) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -5955,7 +6102,7 @@ cricket::VideoChannel* PeerConnection::CreateVideoChannel(
|
|||||||
cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel(
|
cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel(
|
||||||
call_.get(), configuration_.media_config, rtp_transport, media_transport,
|
call_.get(), configuration_.media_config, rtp_transport, media_transport,
|
||||||
signaling_thread(), mid, SrtpRequired(), GetCryptoOptions(),
|
signaling_thread(), mid, SrtpRequired(), GetCryptoOptions(),
|
||||||
video_options_);
|
&ssrc_generator_, video_options_);
|
||||||
if (!video_channel) {
|
if (!video_channel) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -6002,7 +6149,7 @@ bool PeerConnection::CreateDataChannel(const std::string& mid) {
|
|||||||
RtpTransportInternal* rtp_transport = GetRtpTransport(mid);
|
RtpTransportInternal* rtp_transport = GetRtpTransport(mid);
|
||||||
rtp_data_channel_ = channel_manager()->CreateRtpDataChannel(
|
rtp_data_channel_ = channel_manager()->CreateRtpDataChannel(
|
||||||
configuration_.media_config, rtp_transport, signaling_thread(), mid,
|
configuration_.media_config, rtp_transport, signaling_thread(), mid,
|
||||||
SrtpRequired(), GetCryptoOptions());
|
SrtpRequired(), GetCryptoOptions(), &ssrc_generator_);
|
||||||
if (!rtp_data_channel_) {
|
if (!rtp_data_channel_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1149,6 +1149,12 @@ class PeerConnection : public PeerConnectionInternal,
|
|||||||
|
|
||||||
int usage_event_accumulator_ = 0;
|
int usage_event_accumulator_ = 0;
|
||||||
bool return_histogram_very_quickly_ = false;
|
bool return_histogram_very_quickly_ = false;
|
||||||
|
|
||||||
|
// This object should be used to generate any SSRC that is not explicitly
|
||||||
|
// specified by the user (or by the remote party).
|
||||||
|
// The generator is not used directly, instead it is passed on to the
|
||||||
|
// channel manager and the session description factory.
|
||||||
|
rtc::UniqueRandomIdGenerator ssrc_generator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -105,11 +105,13 @@ class RtpSenderReceiverTest : public testing::Test,
|
|||||||
voice_channel_ = channel_manager_.CreateVoiceChannel(
|
voice_channel_ = channel_manager_.CreateVoiceChannel(
|
||||||
&fake_call_, cricket::MediaConfig(), rtp_transport_.get(),
|
&fake_call_, cricket::MediaConfig(), rtp_transport_.get(),
|
||||||
/*media_transport=*/nullptr, rtc::Thread::Current(), cricket::CN_AUDIO,
|
/*media_transport=*/nullptr, rtc::Thread::Current(), cricket::CN_AUDIO,
|
||||||
srtp_required, webrtc::CryptoOptions(), cricket::AudioOptions());
|
srtp_required, webrtc::CryptoOptions(), &ssrc_generator_,
|
||||||
|
cricket::AudioOptions());
|
||||||
video_channel_ = channel_manager_.CreateVideoChannel(
|
video_channel_ = channel_manager_.CreateVideoChannel(
|
||||||
&fake_call_, cricket::MediaConfig(), rtp_transport_.get(),
|
&fake_call_, cricket::MediaConfig(), rtp_transport_.get(),
|
||||||
/*media_transport=*/nullptr, rtc::Thread::Current(), cricket::CN_VIDEO,
|
/*media_transport=*/nullptr, rtc::Thread::Current(), cricket::CN_VIDEO,
|
||||||
srtp_required, webrtc::CryptoOptions(), cricket::VideoOptions());
|
srtp_required, webrtc::CryptoOptions(), &ssrc_generator_,
|
||||||
|
cricket::VideoOptions());
|
||||||
voice_channel_->Enable(true);
|
voice_channel_->Enable(true);
|
||||||
video_channel_->Enable(true);
|
video_channel_->Enable(true);
|
||||||
voice_media_channel_ = media_engine_->GetVoiceChannel(0);
|
voice_media_channel_ = media_engine_->GetVoiceChannel(0);
|
||||||
@ -360,6 +362,7 @@ class RtpSenderReceiverTest : public testing::Test,
|
|||||||
rtc::scoped_refptr<VideoTrackInterface> video_track_;
|
rtc::scoped_refptr<VideoTrackInterface> video_track_;
|
||||||
rtc::scoped_refptr<AudioTrackInterface> audio_track_;
|
rtc::scoped_refptr<AudioTrackInterface> audio_track_;
|
||||||
bool audio_sender_destroyed_signal_fired_ = false;
|
bool audio_sender_destroyed_signal_fired_ = false;
|
||||||
|
rtc::UniqueRandomIdGenerator ssrc_generator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test that |voice_channel_| is updated when an audio track is associated
|
// Test that |voice_channel_| is updated when an audio track is associated
|
||||||
|
|||||||
@ -125,9 +125,9 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
|
|||||||
absl::make_unique<FakeVoiceMediaChannelForStats>();
|
absl::make_unique<FakeVoiceMediaChannelForStats>();
|
||||||
auto* voice_media_channel_ptr = voice_media_channel.get();
|
auto* voice_media_channel_ptr = voice_media_channel.get();
|
||||||
voice_channel_ = absl::make_unique<cricket::VoiceChannel>(
|
voice_channel_ = absl::make_unique<cricket::VoiceChannel>(
|
||||||
worker_thread_, network_thread_, signaling_thread_, nullptr,
|
worker_thread_, network_thread_, signaling_thread_,
|
||||||
std::move(voice_media_channel), mid, kDefaultSrtpRequired,
|
std::move(voice_media_channel), mid, kDefaultSrtpRequired,
|
||||||
webrtc::CryptoOptions());
|
webrtc::CryptoOptions(), &ssrc_generator_);
|
||||||
voice_channel_->set_transport_name_for_testing(transport_name);
|
voice_channel_->set_transport_name_for_testing(transport_name);
|
||||||
GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO)
|
GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO)
|
||||||
->internal()
|
->internal()
|
||||||
@ -145,7 +145,7 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
|
|||||||
video_channel_ = absl::make_unique<cricket::VideoChannel>(
|
video_channel_ = absl::make_unique<cricket::VideoChannel>(
|
||||||
worker_thread_, network_thread_, signaling_thread_,
|
worker_thread_, network_thread_, signaling_thread_,
|
||||||
std::move(video_media_channel), mid, kDefaultSrtpRequired,
|
std::move(video_media_channel), mid, kDefaultSrtpRequired,
|
||||||
webrtc::CryptoOptions());
|
webrtc::CryptoOptions(), &ssrc_generator_);
|
||||||
video_channel_->set_transport_name_for_testing(transport_name);
|
video_channel_->set_transport_name_for_testing(transport_name);
|
||||||
GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)
|
GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)
|
||||||
->internal()
|
->internal()
|
||||||
@ -380,6 +380,8 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
|
|||||||
local_certificates_by_transport_;
|
local_certificates_by_transport_;
|
||||||
std::map<std::string, std::unique_ptr<rtc::SSLCertChain>>
|
std::map<std::string, std::unique_ptr<rtc::SSLCertChain>>
|
||||||
remote_cert_chains_by_transport_;
|
remote_cert_chains_by_transport_;
|
||||||
|
|
||||||
|
rtc::UniqueRandomIdGenerator ssrc_generator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#define PC_TEST_MOCK_CHANNEL_INTERFACE_H_
|
#define PC_TEST_MOCK_CHANNEL_INTERFACE_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "pc/channel_interface.h"
|
#include "pc/channel_interface.h"
|
||||||
#include "test/gmock.h"
|
#include "test/gmock.h"
|
||||||
@ -39,6 +40,8 @@ class MockChannelInterface : public cricket::ChannelInterface {
|
|||||||
bool(const cricket::MediaContentDescription*,
|
bool(const cricket::MediaContentDescription*,
|
||||||
webrtc::SdpType,
|
webrtc::SdpType,
|
||||||
std::string*));
|
std::string*));
|
||||||
|
MOCK_CONST_METHOD0(local_streams, const std::vector<StreamParams>&());
|
||||||
|
MOCK_CONST_METHOD0(remote_streams, const std::vector<StreamParams>&());
|
||||||
MOCK_METHOD1(SetRtpTransport, bool(webrtc::RtpTransportInternal*));
|
MOCK_METHOD1(SetRtpTransport, bool(webrtc::RtpTransportInternal*));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
#include "rtc_base/string_encode.h"
|
#include "rtc_base/string_encode.h"
|
||||||
|
|
||||||
using cricket::MediaSessionOptions;
|
using cricket::MediaSessionOptions;
|
||||||
|
using rtc::UniqueRandomIdGenerator;
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace {
|
namespace {
|
||||||
@ -131,9 +132,12 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
|
|||||||
PeerConnectionInternal* pc,
|
PeerConnectionInternal* pc,
|
||||||
const std::string& session_id,
|
const std::string& session_id,
|
||||||
std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
|
std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
|
||||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
|
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate,
|
||||||
|
UniqueRandomIdGenerator* ssrc_generator)
|
||||||
: signaling_thread_(signaling_thread),
|
: signaling_thread_(signaling_thread),
|
||||||
session_desc_factory_(channel_manager, &transport_desc_factory_),
|
session_desc_factory_(channel_manager,
|
||||||
|
&transport_desc_factory_,
|
||||||
|
ssrc_generator),
|
||||||
// RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
|
// RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
|
||||||
// as the session id and session version. To simplify, it should be fine
|
// as the session id and session version. To simplify, it should be fine
|
||||||
// to just use a random number as session id and start version from
|
// to just use a random number as session id and start version from
|
||||||
|
|||||||
@ -30,6 +30,7 @@
|
|||||||
#include "rtc_base/rtc_certificate_generator.h"
|
#include "rtc_base/rtc_certificate_generator.h"
|
||||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||||
#include "rtc_base/thread.h"
|
#include "rtc_base/thread.h"
|
||||||
|
#include "rtc_base/unique_id_generator.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -82,7 +83,8 @@ class WebRtcSessionDescriptionFactory : public rtc::MessageHandler,
|
|||||||
PeerConnectionInternal* pc,
|
PeerConnectionInternal* pc,
|
||||||
const std::string& session_id,
|
const std::string& session_id,
|
||||||
std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
|
std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
|
||||||
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
|
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate,
|
||||||
|
rtc::UniqueRandomIdGenerator* ssrc_generator);
|
||||||
virtual ~WebRtcSessionDescriptionFactory();
|
virtual ~WebRtcSessionDescriptionFactory();
|
||||||
|
|
||||||
static void CopyCandidatesFromSessionDescription(
|
static void CopyCandidatesFromSessionDescription(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user