Add support for send_encodings parameters in addTransceiver

This will later allow simulcast to be set up without any SDP
manipulation. Currently limited to only one layer as the SDP
generated is not spec compliant and more work is required
to support simulcast.

Initial encoding parameters are deferred and applied when the ssrc
is set on the sender. This allows parameters to be changed before
negotiation is completed.

Bug: webrtc:7600
Change-Id: I0a31cd1c2bfc72ebb61ce0fa4fa69d87e3d8b74d
Reviewed-on: https://webrtc-review.googlesource.com/95488
Commit-Queue: Florent Castelli <orphis@webrtc.org>
Reviewed-by: Seth Hampson <shampson@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24917}
This commit is contained in:
Florent Castelli
2018-10-01 22:47:20 +02:00
committed by Commit Bot
parent 687a022888
commit 892acf01f6
17 changed files with 591 additions and 127 deletions

View File

@ -20,4 +20,9 @@ RtpSenderInterface::GetFrameEncryptor() const {
return nullptr;
}
std::vector<RtpEncodingParameters> RtpSenderInterface::init_send_encodings()
const {
return {};
}
} // namespace webrtc

View File

@ -55,6 +55,12 @@ class RtpSenderInterface : public rtc::RefCountInterface {
// tracks.
virtual std::vector<std::string> stream_ids() const = 0;
// Returns the list of encoding parameters that will be applied when the SDP
// local description is set. These initial encoding parameters can be set by
// PeerConnection::AddTransceiver, and later updated with Get/SetParameters.
// TODO(orphis): Make it pure virtual once Chrome has updated
virtual std::vector<RtpEncodingParameters> init_send_encodings() const;
virtual RtpParameters GetParameters() = 0;
// Note that only a subset of the parameters can currently be changed. See
// rtpparameters.h
@ -90,6 +96,7 @@ PROXY_CONSTMETHOD0(uint32_t, ssrc)
PROXY_CONSTMETHOD0(cricket::MediaType, media_type)
PROXY_CONSTMETHOD0(std::string, id)
PROXY_CONSTMETHOD0(std::vector<std::string>, stream_ids)
PROXY_CONSTMETHOD0(std::vector<RtpEncodingParameters>, init_send_encodings)
PROXY_METHOD0(RtpParameters, GetParameters);
PROXY_METHOD1(RTCError, SetParameters, const RtpParameters&)
PROXY_CONSTMETHOD0(rtc::scoped_refptr<DtmfSenderInterface>, GetDtmfSender);

View File

@ -27,6 +27,7 @@ class MockRtpSender : public rtc::RefCountedObject<RtpSenderInterface> {
MOCK_CONST_METHOD0(media_type, cricket::MediaType());
MOCK_CONST_METHOD0(id, std::string());
MOCK_CONST_METHOD0(stream_ids, std::vector<std::string>());
MOCK_CONST_METHOD0(init_send_encodings, std::vector<RtpEncodingParameters>());
MOCK_METHOD0(GetParameters, RtpParameters());
MOCK_METHOD1(SetParameters, RTCError(const RtpParameters&));
MOCK_CONST_METHOD0(GetDtmfSender, rtc::scoped_refptr<DtmfSenderInterface>());

View File

@ -151,6 +151,11 @@ class RtpHelper : public Base {
const webrtc::RtpParameters& parameters) {
auto parameters_iterator = rtp_send_parameters_.find(ssrc);
if (parameters_iterator != rtp_send_parameters_.end()) {
auto result =
ValidateRtpParameters(parameters_iterator->second, parameters);
if (!result.ok())
return result;
parameters_iterator->second = parameters;
return webrtc::RTCError::OK();
}

View File

@ -37,4 +37,50 @@ webrtc::RtpParameters CreateRtpParametersWithEncodings(StreamParams sp) {
return parameters;
}
webrtc::RTCError ValidateRtpParameters(
const webrtc::RtpParameters& old_rtp_parameters,
const webrtc::RtpParameters& rtp_parameters) {
using webrtc::RTCErrorType;
if (rtp_parameters.encodings.size() != old_rtp_parameters.encodings.size()) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with different encoding count");
}
if (rtp_parameters.rtcp != old_rtp_parameters.rtcp) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified RTCP parameters");
}
if (rtp_parameters.header_extensions !=
old_rtp_parameters.header_extensions) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified header extensions");
}
for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
if (rtp_parameters.encodings[i].ssrc !=
old_rtp_parameters.encodings[i].ssrc) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified SSRC");
}
if (rtp_parameters.encodings[i].bitrate_priority <= 0) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
"Attempted to set RtpParameters bitrate_priority to "
"an invalid number. bitrate_priority must be > 0.");
}
if (rtp_parameters.encodings[i].min_bitrate_bps &&
rtp_parameters.encodings[i].max_bitrate_bps) {
if (*rtp_parameters.encodings[i].max_bitrate_bps <
*rtp_parameters.encodings[i].min_bitrate_bps) {
LOG_AND_RETURN_ERROR(webrtc::RTCErrorType::INVALID_RANGE,
"Attempted to set RtpParameters min bitrate "
"larger than max bitrate.");
}
}
}
return webrtc::RTCError::OK();
}
}; // namespace cricket

View File

@ -38,6 +38,10 @@ class Call;
namespace cricket {
webrtc::RTCError ValidateRtpParameters(
const webrtc::RtpParameters& old_parameters,
const webrtc::RtpParameters& new_parameters);
struct RtpCapabilities {
RtpCapabilities();
~RtpCapabilities();

View File

@ -1722,7 +1722,8 @@ void WebRtcVideoChannel::WebRtcVideoSendStream::SetSendParameters(
webrtc::RTCError WebRtcVideoChannel::WebRtcVideoSendStream::SetRtpParameters(
const webrtc::RtpParameters& new_parameters) {
RTC_DCHECK_RUN_ON(&thread_checker_);
webrtc::RTCError error = ValidateRtpParameters(new_parameters);
webrtc::RTCError error =
ValidateRtpParameters(rtp_parameters_, new_parameters);
if (!error.ok()) {
return error;
}
@ -1784,49 +1785,6 @@ WebRtcVideoChannel::WebRtcVideoSendStream::GetRtpParameters() const {
return rtp_parameters_;
}
webrtc::RTCError
WebRtcVideoChannel::WebRtcVideoSendStream::ValidateRtpParameters(
const webrtc::RtpParameters& rtp_parameters) {
using webrtc::RTCErrorType;
RTC_DCHECK_RUN_ON(&thread_checker_);
if (rtp_parameters.encodings.size() != rtp_parameters_.encodings.size()) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with different encoding count");
}
if (rtp_parameters.rtcp != rtp_parameters_.rtcp) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified RTCP parameters");
}
if (rtp_parameters.header_extensions != rtp_parameters_.header_extensions) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified header extensions");
}
if (rtp_parameters.encodings[0].ssrc != rtp_parameters_.encodings[0].ssrc) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified SSRC");
}
if (rtp_parameters.encodings[0].bitrate_priority <= 0) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
"Attempted to set RtpParameters bitrate_priority to "
"an invalid number. bitrate_priority must be > 0.");
}
for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
if (rtp_parameters.encodings[i].min_bitrate_bps &&
rtp_parameters.encodings[i].max_bitrate_bps) {
if (*rtp_parameters.encodings[i].max_bitrate_bps <
*rtp_parameters.encodings[i].min_bitrate_bps) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
"Attempted to set RtpParameters min bitrate "
"larger than max bitrate.");
}
}
}
return webrtc::RTCError::OK();
}
void WebRtcVideoChannel::WebRtcVideoSendStream::UpdateSendState() {
RTC_DCHECK_RUN_ON(&thread_checker_);
if (sending_) {

View File

@ -303,8 +303,6 @@ class WebRtcVideoChannel : public VideoMediaChannel, public webrtc::Transport {
webrtc::VideoEncoderConfig CreateVideoEncoderConfig(
const VideoCodec& codec) const;
void ReconfigureEncoder();
webrtc::RTCError ValidateRtpParameters(
const webrtc::RtpParameters& parameters);
// Calls Start or Stop according to whether or not |sending_| is true,
// and whether or not the encoding in |rtp_parameters_| is active.

View File

@ -900,38 +900,8 @@ class WebRtcVoiceMediaChannel::WebRtcAudioSendStream
return rtp_parameters_;
}
webrtc::RTCError ValidateRtpParameters(
const webrtc::RtpParameters& rtp_parameters) {
using webrtc::RTCErrorType;
if (rtp_parameters.encodings.size() != rtp_parameters_.encodings.size()) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with different encoding count");
}
if (rtp_parameters.rtcp != rtp_parameters_.rtcp) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified RTCP parameters");
}
if (rtp_parameters.header_extensions != rtp_parameters_.header_extensions) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified header extensions");
}
if (rtp_parameters.encodings[0].ssrc != rtp_parameters_.encodings[0].ssrc) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified SSRC");
}
if (rtp_parameters.encodings[0].bitrate_priority <= 0) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
"Attempted to set RtpParameters bitrate_priority to "
"an invalid number.");
}
return webrtc::RTCError::OK();
}
webrtc::RTCError SetRtpParameters(const webrtc::RtpParameters& parameters) {
webrtc::RTCError error = ValidateRtpParameters(parameters);
webrtc::RTCError error = ValidateRtpParameters(rtp_parameters_, parameters);
if (!error.ok()) {
return error;
}

View File

@ -221,8 +221,6 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
int CreateVoEChannel();
bool DeleteVoEChannel(int channel);
bool SetMaxSendBitrate(int bps);
webrtc::RTCError ValidateRtpParameters(
const webrtc::RtpParameters& parameters);
void SetupRecording();
// Check if 'ssrc' is an unsignaled stream, and if so mark it as not being
// unsignaled anymore (i.e. it is now removed, or signaled), and return true.

View File

@ -1181,7 +1181,7 @@ PeerConnection::AddTrackPlanB(
? cricket::MEDIA_TYPE_AUDIO
: cricket::MEDIA_TYPE_VIDEO);
auto new_sender =
CreateSender(media_type, track->id(), track, adjusted_stream_ids);
CreateSender(media_type, track->id(), track, adjusted_stream_ids, {});
if (track->kind() == MediaStreamTrackInterface::kAudioKind) {
new_sender->internal()->SetVoiceMediaChannel(voice_media_channel());
GetAudioTransceiver()->internal()->AddSender(new_sender);
@ -1237,7 +1237,7 @@ PeerConnection::AddTrackUnifiedPlan(
if (FindSenderById(sender_id)) {
sender_id = rtc::CreateRandomUuid();
}
auto sender = CreateSender(media_type, sender_id, track, stream_ids);
auto sender = CreateSender(media_type, sender_id, track, stream_ids, {});
auto receiver = CreateReceiver(media_type, rtc::CreateRandomUuid());
transceiver = CreateAndAddTransceiver(sender, receiver);
transceiver->internal()->set_created_by_addtrack(true);
@ -1379,6 +1379,27 @@ PeerConnection::AddTransceiver(
}
// TODO(bugs.webrtc.org/7600): Verify init.
if (init.send_encodings.size() > 1) {
LOG_AND_RETURN_ERROR(
RTCErrorType::UNSUPPORTED_PARAMETER,
"Attempted to create an encoder with more than 1 encoding parameter.");
}
for (const auto& encoding : init.send_encodings) {
if (encoding.ssrc.has_value()) {
LOG_AND_RETURN_ERROR(
RTCErrorType::UNSUPPORTED_PARAMETER,
"Attempted to set an unimplemented parameter of RtpParameters.");
}
}
RtpParameters parameters;
parameters.encodings = init.send_encodings;
if (UnimplementedRtpParameterHasValue(parameters)) {
LOG_AND_RETURN_ERROR(
RTCErrorType::UNSUPPORTED_PARAMETER,
"Attempted to set an unimplemented parameter of RtpParameters.");
}
RTC_LOG(LS_INFO) << "Adding " << cricket::MediaTypeToString(media_type)
<< " transceiver in response to a call to AddTransceiver.";
@ -1387,7 +1408,8 @@ PeerConnection::AddTransceiver(
std::string sender_id =
(track && !FindSenderById(track->id()) ? track->id()
: rtc::CreateRandomUuid());
auto sender = CreateSender(media_type, sender_id, track, init.stream_ids);
auto sender = CreateSender(media_type, sender_id, track, init.stream_ids,
init.send_encodings);
auto receiver = CreateReceiver(media_type, rtc::CreateRandomUuid());
auto transceiver = CreateAndAddTransceiver(sender, receiver);
transceiver->internal()->set_direction(init.direction);
@ -1404,7 +1426,8 @@ PeerConnection::CreateSender(
cricket::MediaType media_type,
const std::string& id,
rtc::scoped_refptr<MediaStreamTrackInterface> track,
const std::vector<std::string>& stream_ids) {
const std::vector<std::string>& stream_ids,
const std::vector<RtpEncodingParameters>& send_encodings) {
rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender;
if (media_type == cricket::MEDIA_TYPE_AUDIO) {
RTC_DCHECK(!track ||
@ -1424,6 +1447,7 @@ PeerConnection::CreateSender(
bool set_track_succeeded = sender->SetTrack(track);
RTC_DCHECK(set_track_succeeded);
sender->internal()->set_stream_ids(stream_ids);
sender->internal()->set_init_send_encodings(send_encodings);
return sender;
}
@ -2733,7 +2757,8 @@ PeerConnection::AssociateTransceiver(cricket::ContentSource source,
<< " at i=" << mline_index
<< " in response to the remote description.";
std::string sender_id = rtc::CreateRandomUuid();
auto sender = CreateSender(media_desc->type(), sender_id, nullptr, {});
auto sender =
CreateSender(media_desc->type(), sender_id, nullptr, {}, {});
std::string receiver_id;
if (!media_desc->streams().empty()) {
receiver_id = media_desc->streams()[0].id;
@ -3382,7 +3407,7 @@ void PeerConnection::AddAudioTrack(AudioTrackInterface* track,
// Normal case; we've never seen this track before.
auto new_sender = CreateSender(cricket::MEDIA_TYPE_AUDIO, track->id(), track,
{stream->id()});
{stream->id()}, {});
new_sender->internal()->SetVoiceMediaChannel(voice_media_channel());
GetAudioTransceiver()->internal()->AddSender(new_sender);
// If the sender has already been configured in SDP, we call SetSsrc,
@ -3427,7 +3452,7 @@ void PeerConnection::AddVideoTrack(VideoTrackInterface* track,
// Normal case; we've never seen this track before.
auto new_sender = CreateSender(cricket::MEDIA_TYPE_VIDEO, track->id(), track,
{stream->id()});
{stream->id()}, {});
new_sender->internal()->SetVideoMediaChannel(video_media_channel());
GetVideoTransceiver()->internal()->AddSender(new_sender);
const RtpSenderInfo* sender_info =
@ -3739,9 +3764,10 @@ GetMediaDescriptionOptionsForTransceiver(
cricket::SenderOptions sender_options;
sender_options.track_id = transceiver->sender()->id();
sender_options.stream_ids = transceiver->sender()->stream_ids();
// TODO(bugs.webrtc.org/7600): Set num_sim_layers to the number of encodings
// set in the RTP parameters when the transceiver was added.
sender_options.num_sim_layers = 1;
int num_send_encoding_layers =
transceiver->sender()->init_send_encodings().size();
sender_options.num_sim_layers =
!num_send_encoding_layers ? 1 : num_send_encoding_layers;
media_description_options.sender_options.push_back(sender_options);
}
return media_description_options;

View File

@ -357,7 +357,8 @@ class PeerConnection : public PeerConnectionInternal,
CreateSender(cricket::MediaType media_type,
const std::string& id,
rtc::scoped_refptr<MediaStreamTrackInterface> track,
const std::vector<std::string>& stream_ids);
const std::vector<std::string>& stream_ids,
const std::vector<RtpEncodingParameters>& send_encodings);
rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
CreateReceiver(cricket::MediaType media_type, const std::string& receiver_id);

View File

@ -1376,6 +1376,132 @@ TEST_F(PeerConnectionRtpTestUnifiedPlan,
EXPECT_FALSE(caller->observer()->negotiation_needed());
}
// Test that AddTransceiver fails if trying to use simulcast using
// send_encodings as it isn't currently supported.
TEST_F(PeerConnectionRtpTestUnifiedPlan, CheckForUnsupportedSimulcast) {
auto caller = CreatePeerConnection();
RtpTransceiverInit init;
init.send_encodings.emplace_back();
init.send_encodings.emplace_back();
auto result = caller->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
EXPECT_EQ(result.error().type(), RTCErrorType::UNSUPPORTED_PARAMETER);
}
// Test that AddTransceiver fails if trying to use unimplemented RTP encoding
// parameters with the send_encodings parameters.
TEST_F(PeerConnectionRtpTestUnifiedPlan,
CheckForUnsupportedEncodingParameters) {
auto caller = CreatePeerConnection();
RtpTransceiverInit init;
init.send_encodings.emplace_back();
auto default_send_encodings = init.send_encodings;
// Unimplemented RtpParameters: ssrc, codec_payload_type, fec, rtx, dtx,
// ptime, scale_resolution_down_by, scale_framerate_down_by, rid,
// dependency_rids.
init.send_encodings[0].ssrc = 1;
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
caller->pc()
->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init)
.error()
.type());
init.send_encodings = default_send_encodings;
init.send_encodings[0].codec_payload_type = 1;
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
caller->pc()
->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init)
.error()
.type());
init.send_encodings = default_send_encodings;
init.send_encodings[0].fec = RtpFecParameters();
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
caller->pc()
->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init)
.error()
.type());
init.send_encodings = default_send_encodings;
init.send_encodings[0].rtx = RtpRtxParameters();
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
caller->pc()
->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init)
.error()
.type());
init.send_encodings = default_send_encodings;
init.send_encodings[0].dtx = DtxStatus::ENABLED;
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
caller->pc()
->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init)
.error()
.type());
init.send_encodings = default_send_encodings;
init.send_encodings[0].ptime = 1;
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
caller->pc()
->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init)
.error()
.type());
init.send_encodings = default_send_encodings;
init.send_encodings[0].scale_resolution_down_by = 2.0;
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
caller->pc()
->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init)
.error()
.type());
init.send_encodings = default_send_encodings;
init.send_encodings[0].rid = "dummy_rid";
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
caller->pc()
->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init)
.error()
.type());
init.send_encodings = default_send_encodings;
init.send_encodings[0].dependency_rids.push_back("dummy_rid");
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
caller->pc()
->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init)
.error()
.type());
}
// Test that AddTransceiver transfers the send_encodings to the sender and they
// are retained after SetLocalDescription().
TEST_F(PeerConnectionRtpTestUnifiedPlan, SendEncodingsPassedToSender) {
auto caller = CreatePeerConnection();
RtpTransceiverInit init;
init.send_encodings.emplace_back();
init.send_encodings[0].active = false;
init.send_encodings[0].max_bitrate_bps = 180000;
auto result = caller->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO, init);
ASSERT_TRUE(result.ok());
auto init_send_encodings = result.value()->sender()->init_send_encodings();
EXPECT_FALSE(init_send_encodings[0].active);
EXPECT_EQ(init_send_encodings[0].max_bitrate_bps, 180000);
auto parameters = result.value()->sender()->GetParameters();
EXPECT_FALSE(parameters.encodings[0].active);
EXPECT_EQ(parameters.encodings[0].max_bitrate_bps, 180000);
ASSERT_TRUE(caller->SetLocalDescription(caller->CreateOffer()));
parameters = result.value()->sender()->GetParameters();
EXPECT_FALSE(parameters.encodings[0].active);
EXPECT_EQ(parameters.encodings[0].max_bitrate_bps, 180000);
}
// Test MSID signaling between Unified Plan and Plan B endpoints. There are two
// options for this kind of signaling: media section based (a=msid) and ssrc
// based (a=ssrc MSID). While JSEP only specifies media section MSID signaling,

View File

@ -31,6 +31,20 @@ int GenerateUniqueId() {
return ++g_unique_id;
}
// Attaches the frame encryptor to the media channel through an invoke on a
// worker thread. This set must be done on the corresponding worker thread that
// the media channel was created on.
void AttachFrameEncryptorToMediaChannel(
rtc::Thread* worker_thread,
webrtc::FrameEncryptorInterface* frame_encryptor,
cricket::MediaChannel* media_channel) {
if (media_channel) {
return worker_thread->Invoke<void>(RTC_FROM_HERE, [&] {
media_channel->SetFrameEncryptor(frame_encryptor);
});
}
}
// Returns an true if any RtpEncodingParameters member that isn't implemented
// contains a value.
bool UnimplementedRtpEncodingParameterHasValue(
@ -64,6 +78,8 @@ bool PerSenderRtpEncodingParameterHasValue(
return false;
}
} // namespace
// Returns true if any RtpParameters member that isn't implemented contains a
// value.
bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters) {
@ -84,22 +100,6 @@ bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters) {
return false;
}
// Attaches the frame encryptor to the media channel through an invoke on a
// worker thread. This set must be done on the corresponding worker thread that
// the media channel was created on.
void AttachFrameEncryptorToMediaChannel(
rtc::Thread* worker_thread,
webrtc::FrameEncryptorInterface* frame_encryptor,
cricket::MediaChannel* media_channel) {
if (media_channel) {
return worker_thread->Invoke<void>(RTC_FROM_HERE, [&] {
media_channel->SetFrameEncryptor(frame_encryptor);
});
}
}
} // namespace
LocalAudioSinkAdapter::LocalAudioSinkAdapter() : sink_(nullptr) {}
LocalAudioSinkAdapter::~LocalAudioSinkAdapter() {
@ -137,6 +137,7 @@ AudioRtpSender::AudioRtpSender(rtc::Thread* worker_thread,
DtmfSender::Create(rtc::Thread::Current(), this))),
sink_adapter_(new LocalAudioSinkAdapter()) {
RTC_DCHECK(worker_thread);
init_parameters_.encodings.emplace_back();
}
AudioRtpSender::~AudioRtpSender() {
@ -242,9 +243,15 @@ bool AudioRtpSender::SetTrack(MediaStreamTrackInterface* track) {
}
RtpParameters AudioRtpSender::GetParameters() {
if (!media_channel_ || stopped_) {
if (stopped_) {
return RtpParameters();
}
if (!media_channel_) {
RtpParameters result = init_parameters_;
last_transaction_id_ = rtc::CreateRandomUuid();
result.transaction_id = last_transaction_id_.value();
return result;
}
return worker_thread_->Invoke<RtpParameters>(RTC_FROM_HERE, [&] {
RtpParameters result = media_channel_->GetRtpSendParameters(ssrc_);
last_transaction_id_ = rtc::CreateRandomUuid();
@ -255,7 +262,7 @@ RtpParameters AudioRtpSender::GetParameters() {
RTCError AudioRtpSender::SetParameters(const RtpParameters& parameters) {
TRACE_EVENT0("webrtc", "AudioRtpSender::SetParameters");
if (!media_channel_ || stopped_) {
if (stopped_) {
return RTCError(RTCErrorType::INVALID_STATE);
}
if (!last_transaction_id_) {
@ -276,6 +283,13 @@ RTCError AudioRtpSender::SetParameters(const RtpParameters& parameters) {
RTCErrorType::UNSUPPORTED_PARAMETER,
"Attempted to set an unimplemented parameter of RtpParameters.");
}
if (!media_channel_) {
auto result = cricket::ValidateRtpParameters(init_parameters_, parameters);
if (result.ok()) {
init_parameters_ = parameters;
}
return result;
}
return worker_thread_->Invoke<RTCError>(RTC_FROM_HERE, [&] {
RTCError result = media_channel_->SetRtpSendParameters(ssrc_, parameters);
last_transaction_id_.reset();
@ -318,6 +332,28 @@ void AudioRtpSender::SetSsrc(uint32_t ssrc) {
stats_->AddLocalAudioTrack(track_.get(), ssrc_);
}
}
if (!init_parameters_.encodings.empty()) {
worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
RTC_DCHECK(media_channel_);
// Get the current parameters, which are constructed from the SDP.
// The number of layers in the SDP is currently authoritative to support
// SDP munging for Plan-B simulcast with "a=ssrc-group:SIM <ssrc-id>..."
// lines as described in RFC 5576.
// All fields should be default constructed and the SSRC field set, which
// we need to copy.
RtpParameters current_parameters =
media_channel_->GetRtpSendParameters(ssrc_);
for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) {
init_parameters_.encodings[i].ssrc =
current_parameters.encodings[i].ssrc;
current_parameters.encodings[i] = init_parameters_.encodings[i];
}
current_parameters.degradation_preference =
init_parameters_.degradation_preference;
media_channel_->SetRtpSendParameters(ssrc_, current_parameters);
init_parameters_.encodings.clear();
});
}
}
void AudioRtpSender::Stop() {
@ -399,6 +435,7 @@ VideoRtpSender::VideoRtpSender(rtc::Thread* worker_thread,
const std::string& id)
: worker_thread_(worker_thread), id_(id) {
RTC_DCHECK(worker_thread);
init_parameters_.encodings.emplace_back();
}
VideoRtpSender::~VideoRtpSender() {
@ -456,9 +493,15 @@ bool VideoRtpSender::SetTrack(MediaStreamTrackInterface* track) {
}
RtpParameters VideoRtpSender::GetParameters() {
if (!media_channel_ || stopped_) {
if (stopped_) {
return RtpParameters();
}
if (!media_channel_) {
RtpParameters result = init_parameters_;
last_transaction_id_ = rtc::CreateRandomUuid();
result.transaction_id = last_transaction_id_.value();
return result;
}
return worker_thread_->Invoke<RtpParameters>(RTC_FROM_HERE, [&] {
RtpParameters result = media_channel_->GetRtpSendParameters(ssrc_);
last_transaction_id_ = rtc::CreateRandomUuid();
@ -469,7 +512,7 @@ RtpParameters VideoRtpSender::GetParameters() {
RTCError VideoRtpSender::SetParameters(const RtpParameters& parameters) {
TRACE_EVENT0("webrtc", "VideoRtpSender::SetParameters");
if (!media_channel_ || stopped_) {
if (stopped_) {
return RTCError(RTCErrorType::INVALID_STATE);
}
if (!last_transaction_id_) {
@ -490,6 +533,13 @@ RTCError VideoRtpSender::SetParameters(const RtpParameters& parameters) {
RTCErrorType::UNSUPPORTED_PARAMETER,
"Attempted to set an unimplemented parameter of RtpParameters.");
}
if (!media_channel_) {
auto result = cricket::ValidateRtpParameters(init_parameters_, parameters);
if (result.ok()) {
init_parameters_ = parameters;
}
return result;
}
return worker_thread_->Invoke<RTCError>(RTC_FROM_HERE, [&] {
RTCError result = media_channel_->SetRtpSendParameters(ssrc_, parameters);
last_transaction_id_.reset();
@ -527,6 +577,28 @@ void VideoRtpSender::SetSsrc(uint32_t ssrc) {
if (can_send_track()) {
SetVideoSend();
}
if (!init_parameters_.encodings.empty()) {
worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
RTC_DCHECK(media_channel_);
// Get the current parameters, which are constructed from the SDP.
// The number of layers in the SDP is currently authoritative to support
// SDP munging for Plan-B simulcast with "a=ssrc-group:SIM <ssrc-id>..."
// lines as described in RFC 5576.
// All fields should be default constructed and the SSRC field set, which
// we need to copy.
RtpParameters current_parameters =
media_channel_->GetRtpSendParameters(ssrc_);
for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) {
init_parameters_.encodings[i].ssrc =
current_parameters.encodings[i].ssrc;
current_parameters.encodings[i] = init_parameters_.encodings[i];
}
current_parameters.degradation_preference =
init_parameters_.degradation_preference;
media_channel_->SetRtpSendParameters(ssrc_, current_parameters);
init_parameters_.encodings.clear();
});
}
}
void VideoRtpSender::Stop() {

View File

@ -30,6 +30,8 @@ namespace webrtc {
class StatsCollector;
bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters);
// Internal interface used by PeerConnection.
class RtpSenderInternal : public RtpSenderInterface {
public:
@ -50,6 +52,8 @@ class RtpSenderInternal : public RtpSenderInterface {
virtual void SetSsrc(uint32_t ssrc) = 0;
virtual void set_stream_ids(const std::vector<std::string>& stream_ids) = 0;
virtual void set_init_send_encodings(
const std::vector<RtpEncodingParameters>& init_send_encodings) = 0;
virtual void Stop() = 0;
@ -140,6 +144,14 @@ class AudioRtpSender : public DtmfProviderInterface,
stream_ids_ = stream_ids;
}
void set_init_send_encodings(
const std::vector<RtpEncodingParameters>& init_send_encodings) override {
init_parameters_.encodings = init_send_encodings;
}
std::vector<RtpEncodingParameters> init_send_encodings() const override {
return init_parameters_.encodings;
}
void Stop() override;
int AttachmentId() const override { return attachment_id_; }
@ -167,6 +179,7 @@ class AudioRtpSender : public DtmfProviderInterface,
rtc::Thread* const worker_thread_;
const std::string id_;
std::vector<std::string> stream_ids_;
RtpParameters init_parameters_;
cricket::VoiceMediaChannel* media_channel_ = nullptr;
StatsCollector* stats_ = nullptr;
rtc::scoped_refptr<AudioTrackInterface> track_;
@ -211,6 +224,14 @@ class VideoRtpSender : public ObserverInterface,
std::vector<std::string> stream_ids() const override { return stream_ids_; }
void set_init_send_encodings(
const std::vector<RtpEncodingParameters>& init_send_encodings) override {
init_parameters_.encodings = init_send_encodings;
}
std::vector<RtpEncodingParameters> init_send_encodings() const override {
return init_parameters_.encodings;
}
RtpParameters GetParameters() override;
RTCError SetParameters(const RtpParameters& parameters) override;
@ -251,6 +272,7 @@ class VideoRtpSender : public ObserverInterface,
rtc::Thread* worker_thread_;
const std::string id_;
std::vector<std::string> stream_ids_;
RtpParameters init_parameters_;
cricket::VideoMediaChannel* media_channel_ = nullptr;
rtc::scoped_refptr<VideoTrackInterface> track_;
absl::optional<std::string> last_transaction_id_;

View File

@ -47,6 +47,8 @@ static const uint32_t kVideoSsrc = 98;
static const uint32_t kVideoSsrc2 = 100;
static const uint32_t kAudioSsrc = 99;
static const uint32_t kAudioSsrc2 = 101;
static const uint32_t kVideoSsrcSimulcast = 102;
static const uint32_t kVideoSimulcastLayerCount = 2;
static const int kDefaultTimeout = 10000; // 10 seconds.
} // namespace
@ -170,6 +172,18 @@ class RtpSenderReceiverTest : public testing::Test,
void CreateVideoRtpSender() { CreateVideoRtpSender(false); }
void CreateVideoRtpSenderWithSimulcast(
int num_layers = kVideoSimulcastLayerCount) {
std::vector<uint32_t> ssrcs;
for (int i = 0; i < num_layers; ++i)
ssrcs.push_back(kVideoSsrcSimulcast + i);
cricket::StreamParams stream_params =
cricket::CreateSimStreamParams("cname", ssrcs);
video_media_channel_->AddSendStream(stream_params);
uint32_t primary_ssrc = stream_params.first_ssrc();
CreateVideoRtpSender(primary_ssrc);
}
void CreateVideoRtpSender(bool is_screencast, uint32_t ssrc = kVideoSsrc) {
AddVideoTrack(is_screencast);
video_rtp_sender_ = new VideoRtpSender(worker_thread_, video_track_->id());
@ -179,7 +193,6 @@ class RtpSenderReceiverTest : public testing::Test,
video_rtp_sender_->SetSsrc(ssrc);
VerifyVideoChannelInput(ssrc);
}
void CreateVideoRtpSenderWithNoTrack() {
video_rtp_sender_ = new VideoRtpSender(worker_thread_, /*id=*/"");
video_rtp_sender_->SetVideoMediaChannel(video_media_channel_);
@ -612,6 +625,63 @@ TEST_F(RtpSenderReceiverTest, AudioSenderCanSetParameters) {
DestroyAudioRtpSender();
}
TEST_F(RtpSenderReceiverTest, AudioSenderCanSetParametersBeforeNegotiation) {
audio_rtp_sender_ = new AudioRtpSender(worker_thread_, /*id=*/"", nullptr);
RtpParameters params = audio_rtp_sender_->GetParameters();
ASSERT_EQ(1u, params.encodings.size());
params.encodings[0].max_bitrate_bps = 90000;
EXPECT_TRUE(audio_rtp_sender_->SetParameters(params).ok());
params = audio_rtp_sender_->GetParameters();
EXPECT_TRUE(audio_rtp_sender_->SetParameters(params).ok());
EXPECT_EQ(params.encodings[0].max_bitrate_bps, 90000);
DestroyAudioRtpSender();
}
TEST_F(RtpSenderReceiverTest, AudioSenderInitParametersMovedAfterNegotiation) {
audio_track_ = AudioTrack::Create(kAudioTrackId, nullptr);
EXPECT_TRUE(local_stream_->AddTrack(audio_track_));
audio_rtp_sender_ =
new AudioRtpSender(worker_thread_, audio_track_->id(), nullptr);
ASSERT_TRUE(audio_rtp_sender_->SetTrack(audio_track_));
audio_rtp_sender_->set_stream_ids({local_stream_->id()});
std::vector<RtpEncodingParameters> init_encodings(1);
init_encodings[0].max_bitrate_bps = 60000;
audio_rtp_sender_->set_init_send_encodings(init_encodings);
RtpParameters params = audio_rtp_sender_->GetParameters();
ASSERT_EQ(1u, params.encodings.size());
EXPECT_EQ(params.encodings[0].max_bitrate_bps, 60000);
// Simulate the setLocalDescription call
std::vector<uint32_t> ssrcs(1, 1);
cricket::StreamParams stream_params =
cricket::CreateSimStreamParams("cname", ssrcs);
voice_media_channel_->AddSendStream(stream_params);
audio_rtp_sender_->SetVoiceMediaChannel(voice_media_channel_);
audio_rtp_sender_->SetSsrc(1);
params = audio_rtp_sender_->GetParameters();
ASSERT_EQ(1u, params.encodings.size());
EXPECT_EQ(params.encodings[0].max_bitrate_bps, 60000);
DestroyAudioRtpSender();
}
TEST_F(RtpSenderReceiverTest,
AudioSenderMustCallGetParametersBeforeSetParametersBeforeNegotiation) {
audio_rtp_sender_ = new AudioRtpSender(worker_thread_, /*id=*/"", nullptr);
RtpParameters params;
RTCError result = audio_rtp_sender_->SetParameters(params);
EXPECT_EQ(RTCErrorType::INVALID_STATE, result.type());
DestroyAudioRtpSender();
}
TEST_F(RtpSenderReceiverTest,
AudioSenderMustCallGetParametersBeforeSetParameters) {
CreateAudioRtpSender();
@ -792,6 +862,100 @@ TEST_F(RtpSenderReceiverTest, VideoSenderCanSetParameters) {
DestroyVideoRtpSender();
}
TEST_F(RtpSenderReceiverTest, VideoSenderCanSetParametersBeforeNegotiation) {
video_rtp_sender_ = new VideoRtpSender(worker_thread_, /*id=*/"");
RtpParameters params = video_rtp_sender_->GetParameters();
ASSERT_EQ(1u, params.encodings.size());
params.encodings[0].max_bitrate_bps = 90000;
EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok());
params = video_rtp_sender_->GetParameters();
EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok());
EXPECT_EQ(params.encodings[0].max_bitrate_bps, 90000);
DestroyVideoRtpSender();
}
TEST_F(RtpSenderReceiverTest, VideoSenderInitParametersMovedAfterNegotiation) {
AddVideoTrack(false);
video_rtp_sender_ = new VideoRtpSender(worker_thread_, video_track_->id());
ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_));
video_rtp_sender_->set_stream_ids({local_stream_->id()});
std::vector<RtpEncodingParameters> init_encodings(2);
init_encodings[0].max_bitrate_bps = 60000;
init_encodings[1].max_bitrate_bps = 900000;
video_rtp_sender_->set_init_send_encodings(init_encodings);
RtpParameters params = video_rtp_sender_->GetParameters();
ASSERT_EQ(2u, params.encodings.size());
EXPECT_EQ(params.encodings[0].max_bitrate_bps, 60000);
EXPECT_EQ(params.encodings[1].max_bitrate_bps, 900000);
// Simulate the setLocalDescription call
std::vector<uint32_t> ssrcs;
for (int i = 0; i < 2; ++i)
ssrcs.push_back(kVideoSsrcSimulcast + i);
cricket::StreamParams stream_params =
cricket::CreateSimStreamParams("cname", ssrcs);
video_media_channel_->AddSendStream(stream_params);
video_rtp_sender_->SetVideoMediaChannel(video_media_channel_);
video_rtp_sender_->SetSsrc(kVideoSsrcSimulcast);
params = video_rtp_sender_->GetParameters();
ASSERT_EQ(2u, params.encodings.size());
EXPECT_EQ(params.encodings[0].max_bitrate_bps, 60000);
EXPECT_EQ(params.encodings[1].max_bitrate_bps, 900000);
DestroyVideoRtpSender();
}
TEST_F(RtpSenderReceiverTest,
VideoSenderInitParametersMovedAfterManualSimulcastAndNegotiation) {
AddVideoTrack(false);
video_rtp_sender_ = new VideoRtpSender(worker_thread_, video_track_->id());
ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_));
video_rtp_sender_->set_stream_ids({local_stream_->id()});
std::vector<RtpEncodingParameters> init_encodings(1);
init_encodings[0].max_bitrate_bps = 60000;
video_rtp_sender_->set_init_send_encodings(init_encodings);
RtpParameters params = video_rtp_sender_->GetParameters();
ASSERT_EQ(1u, params.encodings.size());
EXPECT_EQ(params.encodings[0].max_bitrate_bps, 60000);
// Simulate the setLocalDescription call as if the user used SDP munging
// to enable simulcast
std::vector<uint32_t> ssrcs;
for (int i = 0; i < 2; ++i)
ssrcs.push_back(kVideoSsrcSimulcast + i);
cricket::StreamParams stream_params =
cricket::CreateSimStreamParams("cname", ssrcs);
video_media_channel_->AddSendStream(stream_params);
video_rtp_sender_->SetVideoMediaChannel(video_media_channel_);
video_rtp_sender_->SetSsrc(kVideoSsrcSimulcast);
params = video_rtp_sender_->GetParameters();
ASSERT_EQ(2u, params.encodings.size());
EXPECT_EQ(params.encodings[0].max_bitrate_bps, 60000);
DestroyVideoRtpSender();
}
TEST_F(RtpSenderReceiverTest,
VideoSenderMustCallGetParametersBeforeSetParametersBeforeNegotiation) {
video_rtp_sender_ = new VideoRtpSender(worker_thread_, /*id=*/"");
RtpParameters params;
RTCError result = video_rtp_sender_->SetParameters(params);
EXPECT_EQ(RTCErrorType::INVALID_STATE, result.type());
DestroyVideoRtpSender();
}
TEST_F(RtpSenderReceiverTest,
VideoSenderMustCallGetParametersBeforeSetParameters) {
CreateVideoRtpSender();
@ -915,6 +1079,58 @@ TEST_F(RtpSenderReceiverTest,
DestroyVideoRtpSender();
}
TEST_F(RtpSenderReceiverTest,
VideoSenderCantSetUnimplementedEncodingParametersWithSimulcast) {
CreateVideoRtpSenderWithSimulcast();
RtpParameters params = video_rtp_sender_->GetParameters();
EXPECT_EQ(kVideoSimulcastLayerCount, params.encodings.size());
// Unimplemented RtpParameters: codec_payload_type, fec, rtx, dtx, ptime,
// scale_resolution_down_by, scale_framerate_down_by, rid, dependency_rids.
for (size_t i = 0; i < params.encodings.size(); i++) {
params.encodings[i].codec_payload_type = 1;
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
video_rtp_sender_->SetParameters(params).type());
params = video_rtp_sender_->GetParameters();
params.encodings[i].fec = RtpFecParameters();
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
video_rtp_sender_->SetParameters(params).type());
params = video_rtp_sender_->GetParameters();
params.encodings[i].rtx = RtpRtxParameters();
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
video_rtp_sender_->SetParameters(params).type());
params = video_rtp_sender_->GetParameters();
params.encodings[i].dtx = DtxStatus::ENABLED;
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
video_rtp_sender_->SetParameters(params).type());
params = video_rtp_sender_->GetParameters();
params.encodings[i].ptime = 1;
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
video_rtp_sender_->SetParameters(params).type());
params = video_rtp_sender_->GetParameters();
params.encodings[i].scale_resolution_down_by = 2.0;
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
video_rtp_sender_->SetParameters(params).type());
params = video_rtp_sender_->GetParameters();
params.encodings[i].rid = "dummy_rid";
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
video_rtp_sender_->SetParameters(params).type());
params = video_rtp_sender_->GetParameters();
params.encodings[i].dependency_rids.push_back("dummy_rid");
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
video_rtp_sender_->SetParameters(params).type());
}
DestroyVideoRtpSender();
}
// A video sender can have multiple simulcast layers, in which case it will
// contain multiple RtpEncodingParameters. This tests that if this is the case
// (simulcast), then we can't set the bitrate_priority, or max_bitrate_bps
@ -922,14 +1138,9 @@ TEST_F(RtpSenderReceiverTest,
// "per-sender."
TEST_F(RtpSenderReceiverTest, VideoSenderCantSetPerSenderEncodingParameters) {
// Add a simulcast specific send stream that contains 2 encoding parameters.
std::vector<uint32_t> ssrcs({1, 2});
cricket::StreamParams stream_params =
cricket::CreateSimStreamParams("cname", ssrcs);
video_media_channel_->AddSendStream(stream_params);
uint32_t primary_ssrc = stream_params.first_ssrc();
CreateVideoRtpSender(primary_ssrc);
CreateVideoRtpSenderWithSimulcast();
RtpParameters params = video_rtp_sender_->GetParameters();
EXPECT_EQ(ssrcs.size(), params.encodings.size());
EXPECT_EQ(kVideoSimulcastLayerCount, params.encodings.size());
params.encodings[1].bitrate_priority = 2.0;
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
@ -939,6 +1150,22 @@ TEST_F(RtpSenderReceiverTest, VideoSenderCantSetPerSenderEncodingParameters) {
DestroyVideoRtpSender();
}
TEST_F(RtpSenderReceiverTest, VideoSenderCantSetReadOnlyEncodingParameters) {
// Add a simulcast specific send stream that contains 2 encoding parameters.
CreateVideoRtpSenderWithSimulcast();
RtpParameters params = video_rtp_sender_->GetParameters();
EXPECT_EQ(kVideoSimulcastLayerCount, params.encodings.size());
for (size_t i = 0; i < params.encodings.size(); i++) {
params.encodings[i].ssrc = 1337;
EXPECT_EQ(RTCErrorType::INVALID_MODIFICATION,
video_rtp_sender_->SetParameters(params).type());
params = video_rtp_sender_->GetParameters();
}
DestroyVideoRtpSender();
}
TEST_F(RtpSenderReceiverTest, SetVideoMinMaxSendBitrate) {
CreateVideoRtpSender();
@ -971,15 +1198,10 @@ TEST_F(RtpSenderReceiverTest, SetVideoMinMaxSendBitrate) {
TEST_F(RtpSenderReceiverTest, SetVideoMinMaxSendBitrateSimulcast) {
// Add a simulcast specific send stream that contains 2 encoding parameters.
std::vector<uint32_t> ssrcs({1, 2});
cricket::StreamParams stream_params =
cricket::CreateSimStreamParams("cname", ssrcs);
video_media_channel_->AddSendStream(stream_params);
uint32_t primary_ssrc = stream_params.first_ssrc();
CreateVideoRtpSender(primary_ssrc);
CreateVideoRtpSenderWithSimulcast();
RtpParameters params = video_rtp_sender_->GetParameters();
EXPECT_EQ(ssrcs.size(), params.encodings.size());
EXPECT_EQ(kVideoSimulcastLayerCount, params.encodings.size());
params.encodings[0].min_bitrate_bps = 100;
params.encodings[0].max_bitrate_bps = 1000;
params.encodings[1].min_bitrate_bps = 200;
@ -987,8 +1209,8 @@ TEST_F(RtpSenderReceiverTest, SetVideoMinMaxSendBitrateSimulcast) {
EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok());
// Verify that the video channel received the new parameters.
params = video_media_channel_->GetRtpSendParameters(primary_ssrc);
EXPECT_EQ(ssrcs.size(), params.encodings.size());
params = video_media_channel_->GetRtpSendParameters(kVideoSsrcSimulcast);
EXPECT_EQ(kVideoSimulcastLayerCount, params.encodings.size());
EXPECT_EQ(100, params.encodings[0].min_bitrate_bps);
EXPECT_EQ(1000, params.encodings[0].max_bitrate_bps);
EXPECT_EQ(200, params.encodings[1].min_bitrate_bps);

View File

@ -29,6 +29,7 @@ class MockRtpSenderInternal : public RtpSenderInternal {
MOCK_CONST_METHOD0(media_type, cricket::MediaType());
MOCK_CONST_METHOD0(id, std::string());
MOCK_CONST_METHOD0(stream_ids, std::vector<std::string>());
MOCK_CONST_METHOD0(init_send_encodings, std::vector<RtpEncodingParameters>());
MOCK_METHOD0(GetParameters, RtpParameters());
MOCK_METHOD1(SetParameters, RTCError(const RtpParameters&));
MOCK_CONST_METHOD0(GetDtmfSender, rtc::scoped_refptr<DtmfSenderInterface>());
@ -42,6 +43,8 @@ class MockRtpSenderInternal : public RtpSenderInternal {
MOCK_METHOD1(SetVideoMediaChannel, void(cricket::VideoMediaChannel*));
MOCK_METHOD1(SetSsrc, void(uint32_t));
MOCK_METHOD1(set_stream_ids, void(const std::vector<std::string>&));
MOCK_METHOD1(set_init_send_encodings,
void(const std::vector<RtpEncodingParameters>&));
MOCK_METHOD0(Stop, void());
MOCK_CONST_METHOD0(AttachmentId, int());
};