diff --git a/api/rtpsenderinterface.cc b/api/rtpsenderinterface.cc index bbf3901353..11c0a69de7 100644 --- a/api/rtpsenderinterface.cc +++ b/api/rtpsenderinterface.cc @@ -20,4 +20,9 @@ RtpSenderInterface::GetFrameEncryptor() const { return nullptr; } +std::vector RtpSenderInterface::init_send_encodings() + const { + return {}; +} + } // namespace webrtc diff --git a/api/rtpsenderinterface.h b/api/rtpsenderinterface.h index 96c2669eff..9554c1c4da 100644 --- a/api/rtpsenderinterface.h +++ b/api/rtpsenderinterface.h @@ -55,6 +55,12 @@ class RtpSenderInterface : public rtc::RefCountInterface { // tracks. virtual std::vector 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 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, stream_ids) +PROXY_CONSTMETHOD0(std::vector, init_send_encodings) PROXY_METHOD0(RtpParameters, GetParameters); PROXY_METHOD1(RTCError, SetParameters, const RtpParameters&) PROXY_CONSTMETHOD0(rtc::scoped_refptr, GetDtmfSender); diff --git a/api/test/mock_rtpsender.h b/api/test/mock_rtpsender.h index 22f391b86c..37d6e0c693 100644 --- a/api/test/mock_rtpsender.h +++ b/api/test/mock_rtpsender.h @@ -27,6 +27,7 @@ class MockRtpSender : public rtc::RefCountedObject { MOCK_CONST_METHOD0(media_type, cricket::MediaType()); MOCK_CONST_METHOD0(id, std::string()); MOCK_CONST_METHOD0(stream_ids, std::vector()); + MOCK_CONST_METHOD0(init_send_encodings, std::vector()); MOCK_METHOD0(GetParameters, RtpParameters()); MOCK_METHOD1(SetParameters, RTCError(const RtpParameters&)); MOCK_CONST_METHOD0(GetDtmfSender, rtc::scoped_refptr()); diff --git a/media/base/fakemediaengine.h b/media/base/fakemediaengine.h index cdea187bf5..62bd6ba5ba 100644 --- a/media/base/fakemediaengine.h +++ b/media/base/fakemediaengine.h @@ -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(); } diff --git a/media/base/mediaengine.cc b/media/base/mediaengine.cc index b0fede3a2a..aaca55c62d 100644 --- a/media/base/mediaengine.cc +++ b/media/base/mediaengine.cc @@ -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 diff --git a/media/base/mediaengine.h b/media/base/mediaengine.h index 57979c2757..01c6fb6526 100644 --- a/media/base/mediaengine.h +++ b/media/base/mediaengine.h @@ -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(); diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc index 73585a84c8..f248fab086 100644 --- a/media/engine/webrtcvideoengine.cc +++ b/media/engine/webrtcvideoengine.cc @@ -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_) { diff --git a/media/engine/webrtcvideoengine.h b/media/engine/webrtcvideoengine.h index 40c39afd5e..8a1e6ad4d1 100644 --- a/media/engine/webrtcvideoengine.h +++ b/media/engine/webrtcvideoengine.h @@ -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. diff --git a/media/engine/webrtcvoiceengine.cc b/media/engine/webrtcvoiceengine.cc index df424a947c..bbe48a2f5a 100644 --- a/media/engine/webrtcvoiceengine.cc +++ b/media/engine/webrtcvoiceengine.cc @@ -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; } diff --git a/media/engine/webrtcvoiceengine.h b/media/engine/webrtcvoiceengine.h index 91e40c844e..dcf8ebe92a 100644 --- a/media/engine/webrtcvoiceengine.h +++ b/media/engine/webrtcvoiceengine.h @@ -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. diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index 7ad2e3d973..6d67329242 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -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 track, - const std::vector& stream_ids) { + const std::vector& stream_ids, + const std::vector& send_encodings) { rtc::scoped_refptr> 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; diff --git a/pc/peerconnection.h b/pc/peerconnection.h index 2942709253..37c9d73bd4 100644 --- a/pc/peerconnection.h +++ b/pc/peerconnection.h @@ -357,7 +357,8 @@ class PeerConnection : public PeerConnectionInternal, CreateSender(cricket::MediaType media_type, const std::string& id, rtc::scoped_refptr track, - const std::vector& stream_ids); + const std::vector& stream_ids, + const std::vector& send_encodings); rtc::scoped_refptr> CreateReceiver(cricket::MediaType media_type, const std::string& receiver_id); diff --git a/pc/peerconnection_rtp_unittest.cc b/pc/peerconnection_rtp_unittest.cc index bc21a3e295..bc3662cbbd 100644 --- a/pc/peerconnection_rtp_unittest.cc +++ b/pc/peerconnection_rtp_unittest.cc @@ -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, diff --git a/pc/rtpsender.cc b/pc/rtpsender.cc index 52710d9204..20130a187c 100644 --- a/pc/rtpsender.cc +++ b/pc/rtpsender.cc @@ -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(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(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(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(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(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 ..." + // 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(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(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(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 ..." + // 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() { diff --git a/pc/rtpsender.h b/pc/rtpsender.h index bbfaabc6ff..2a8289f417 100644 --- a/pc/rtpsender.h +++ b/pc/rtpsender.h @@ -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& stream_ids) = 0; + virtual void set_init_send_encodings( + const std::vector& 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& init_send_encodings) override { + init_parameters_.encodings = init_send_encodings; + } + std::vector 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 stream_ids_; + RtpParameters init_parameters_; cricket::VoiceMediaChannel* media_channel_ = nullptr; StatsCollector* stats_ = nullptr; rtc::scoped_refptr track_; @@ -211,6 +224,14 @@ class VideoRtpSender : public ObserverInterface, std::vector stream_ids() const override { return stream_ids_; } + void set_init_send_encodings( + const std::vector& init_send_encodings) override { + init_parameters_.encodings = init_send_encodings; + } + std::vector 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 stream_ids_; + RtpParameters init_parameters_; cricket::VideoMediaChannel* media_channel_ = nullptr; rtc::scoped_refptr track_; absl::optional last_transaction_id_; diff --git a/pc/rtpsenderreceiver_unittest.cc b/pc/rtpsenderreceiver_unittest.cc index 837f6b79c9..2af2ebf066 100644 --- a/pc/rtpsenderreceiver_unittest.cc +++ b/pc/rtpsenderreceiver_unittest.cc @@ -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 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 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 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 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 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 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 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 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 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); diff --git a/pc/test/mock_rtpsenderinternal.h b/pc/test/mock_rtpsenderinternal.h index 8e2bf73d41..5e14f7ad7d 100644 --- a/pc/test/mock_rtpsenderinternal.h +++ b/pc/test/mock_rtpsenderinternal.h @@ -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()); + MOCK_CONST_METHOD0(init_send_encodings, std::vector()); MOCK_METHOD0(GetParameters, RtpParameters()); MOCK_METHOD1(SetParameters, RTCError(const RtpParameters&)); MOCK_CONST_METHOD0(GetDtmfSender, rtc::scoped_refptr()); @@ -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&)); + MOCK_METHOD1(set_init_send_encodings, + void(const std::vector&)); MOCK_METHOD0(Stop, void()); MOCK_CONST_METHOD0(AttachmentId, int()); };