diff --git a/webrtc/api/api.gyp b/webrtc/api/api.gyp index 93db18bf24..cb902fb6f1 100644 --- a/webrtc/api/api.gyp +++ b/webrtc/api/api.gyp @@ -301,6 +301,7 @@ 'remoteaudiosource.h', 'remotevideocapturer.cc', 'remotevideocapturer.h', + 'rtpparameters.h', 'rtpreceiver.cc', 'rtpreceiver.h', 'rtpreceiverinterface.h', diff --git a/webrtc/api/mediastreamprovider.h b/webrtc/api/mediastreamprovider.h index 0db7a5ca3e..6814c416e2 100644 --- a/webrtc/api/mediastreamprovider.h +++ b/webrtc/api/mediastreamprovider.h @@ -11,6 +11,7 @@ #ifndef WEBRTC_API_MEDIASTREAMPROVIDER_H_ #define WEBRTC_API_MEDIASTREAMPROVIDER_H_ +#include "webrtc/api/rtpsenderinterface.h" #include "webrtc/base/basictypes.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/media/base/videosinkinterface.h" @@ -62,6 +63,10 @@ class AudioProviderInterface { uint32_t ssrc, rtc::scoped_ptr sink) = 0; + virtual RtpParameters GetAudioRtpParameters(uint32_t ssrc) const = 0; + virtual bool SetAudioRtpParameters(uint32_t ssrc, + const RtpParameters& parameters) = 0; + protected: virtual ~AudioProviderInterface() {} }; @@ -82,6 +87,10 @@ class VideoProviderInterface { bool enable, const cricket::VideoOptions* options) = 0; + virtual RtpParameters GetVideoRtpParameters(uint32_t ssrc) const = 0; + virtual bool SetVideoRtpParameters(uint32_t ssrc, + const RtpParameters& parameters) = 0; + protected: virtual ~VideoProviderInterface() {} }; diff --git a/webrtc/api/rtpparameters.h b/webrtc/api/rtpparameters.h new file mode 100644 index 0000000000..e71a1aebba --- /dev/null +++ b/webrtc/api/rtpparameters.h @@ -0,0 +1,30 @@ +/* + * Copyright 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_API_RTPPARAMETERS_H_ +#define WEBRTC_API_RTPPARAMETERS_H_ + +#include + +namespace webrtc { + +// These structures are defined as part of the RtpSender interface. +// See http://w3c.github.io/webrtc-pc/#rtcrtpsender-interface for details. +struct RtpEncodingParameters { + int max_bitrate_bps = -1; +}; + +struct RtpParameters { + std::vector encodings; +}; + +} // namespace webrtc + +#endif // WEBRTC_API_RTPPARAMETERS_H_ diff --git a/webrtc/api/rtpsender.cc b/webrtc/api/rtpsender.cc index 9c6dfb8092..1e28883cf9 100644 --- a/webrtc/api/rtpsender.cc +++ b/webrtc/api/rtpsender.cc @@ -199,6 +199,14 @@ void AudioRtpSender::SetAudioSend() { provider_->SetAudioSend(ssrc_, track_->enabled(), options, source); } +RtpParameters AudioRtpSender::GetParameters() const { + return provider_->GetAudioRtpParameters(ssrc_); +} + +bool AudioRtpSender::SetParameters(const RtpParameters& parameters) { + return provider_->SetAudioRtpParameters(ssrc_, parameters); +} + VideoRtpSender::VideoRtpSender(VideoTrackInterface* track, const std::string& stream_id, VideoProviderInterface* provider) @@ -330,4 +338,12 @@ void VideoRtpSender::SetVideoSend() { provider_->SetVideoSend(ssrc_, track_->enabled(), &options); } +RtpParameters VideoRtpSender::GetParameters() const { + return provider_->GetVideoRtpParameters(ssrc_); +} + +bool VideoRtpSender::SetParameters(const RtpParameters& parameters) { + return provider_->SetVideoRtpParameters(ssrc_, parameters); +} + } // namespace webrtc diff --git a/webrtc/api/rtpsender.h b/webrtc/api/rtpsender.h index 25cc4ed14a..879a332209 100644 --- a/webrtc/api/rtpsender.h +++ b/webrtc/api/rtpsender.h @@ -97,6 +97,9 @@ class AudioRtpSender : public ObserverInterface, void Stop() override; + RtpParameters GetParameters() const; + bool SetParameters(const RtpParameters& parameters); + private: bool can_send_track() const { return track_ && ssrc_; } // Helper function to construct options for @@ -158,6 +161,9 @@ class VideoRtpSender : public ObserverInterface, void Stop() override; + RtpParameters GetParameters() const; + bool SetParameters(const RtpParameters& parameters); + private: bool can_send_track() const { return track_ && ssrc_; } // Helper function to construct options for diff --git a/webrtc/api/rtpsenderinterface.h b/webrtc/api/rtpsenderinterface.h index c3dd52fe16..776d01ace6 100644 --- a/webrtc/api/rtpsenderinterface.h +++ b/webrtc/api/rtpsenderinterface.h @@ -18,6 +18,7 @@ #include "webrtc/api/mediastreaminterface.h" #include "webrtc/api/proxy.h" +#include "webrtc/api/rtpparameters.h" #include "webrtc/base/refcount.h" #include "webrtc/base/scoped_ref_ptr.h" #include "webrtc/pc/mediasession.h" @@ -51,6 +52,9 @@ class RtpSenderInterface : public rtc::RefCountInterface { virtual void Stop() = 0; + virtual RtpParameters GetParameters() const = 0; + virtual bool SetParameters(const RtpParameters& parameters) = 0; + protected: virtual ~RtpSenderInterface() {} }; @@ -66,6 +70,8 @@ PROXY_CONSTMETHOD0(std::string, id) PROXY_METHOD1(void, set_stream_id, const std::string&) PROXY_CONSTMETHOD0(std::string, stream_id) PROXY_METHOD0(void, Stop) +PROXY_CONSTMETHOD0(RtpParameters, GetParameters); +PROXY_METHOD1(bool, SetParameters, const RtpParameters&) END_PROXY() } // namespace webrtc diff --git a/webrtc/api/rtpsenderreceiver_unittest.cc b/webrtc/api/rtpsenderreceiver_unittest.cc index 22fa14f661..b26526fffd 100644 --- a/webrtc/api/rtpsenderreceiver_unittest.cc +++ b/webrtc/api/rtpsenderreceiver_unittest.cc @@ -27,6 +27,7 @@ using ::testing::_; using ::testing::Exactly; +using ::testing::Return; static const char kStreamLabel1[] = "local_stream_1"; static const char kVideoTrackId[] = "video_1"; @@ -52,6 +53,9 @@ class MockAudioProvider : public AudioProviderInterface { const cricket::AudioOptions& options, cricket::AudioSource* source)); MOCK_METHOD2(SetAudioPlayoutVolume, void(uint32_t ssrc, double volume)); + MOCK_CONST_METHOD1(GetAudioRtpParameters, RtpParameters(uint32_t ssrc)); + MOCK_METHOD2(SetAudioRtpParameters, + bool(uint32_t ssrc, const RtpParameters&)); void SetRawAudioSink(uint32_t, rtc::scoped_ptr sink) override { @@ -76,6 +80,10 @@ class MockVideoProvider : public VideoProviderInterface { void(uint32_t ssrc, bool enable, const cricket::VideoOptions* options)); + + MOCK_CONST_METHOD1(GetVideoRtpParameters, RtpParameters(uint32_t ssrc)); + MOCK_METHOD2(SetVideoRtpParameters, + bool(uint32_t ssrc, const RtpParameters&)); }; class FakeVideoTrackSource : public VideoTrackSource { @@ -497,4 +505,30 @@ TEST_F(RtpSenderReceiverTest, VideoSenderSsrcChanged) { EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc2, false, _)).Times(1); } +TEST_F(RtpSenderReceiverTest, AudioSenderCanSetParameters) { + CreateAudioRtpSender(); + + EXPECT_CALL(audio_provider_, GetAudioRtpParameters(kAudioSsrc)) + .WillOnce(Return(RtpParameters())); + EXPECT_CALL(audio_provider_, SetAudioRtpParameters(kAudioSsrc, _)) + .WillOnce(Return(true)); + RtpParameters params = audio_rtp_sender_->GetParameters(); + EXPECT_TRUE(audio_rtp_sender_->SetParameters(params)); + + DestroyAudioRtpSender(); +} + +TEST_F(RtpSenderReceiverTest, VideoSenderCanSetParameters) { + CreateVideoRtpSender(); + + EXPECT_CALL(video_provider_, GetVideoRtpParameters(kVideoSsrc)) + .WillOnce(Return(RtpParameters())); + EXPECT_CALL(video_provider_, SetVideoRtpParameters(kVideoSsrc, _)) + .WillOnce(Return(true)); + RtpParameters params = video_rtp_sender_->GetParameters(); + EXPECT_TRUE(video_rtp_sender_->SetParameters(params)); + + DestroyVideoRtpSender(); +} + } // namespace webrtc diff --git a/webrtc/api/webrtcsession.cc b/webrtc/api/webrtcsession.cc index 3d9b6fbc89..593d429286 100644 --- a/webrtc/api/webrtcsession.cc +++ b/webrtc/api/webrtcsession.cc @@ -1244,6 +1244,23 @@ void WebRtcSession::SetRawAudioSink(uint32_t ssrc, voice_channel_->SetRawAudioSink(ssrc, rtc::ScopedToUnique(std::move(sink))); } +RtpParameters WebRtcSession::GetAudioRtpParameters(uint32_t ssrc) const { + ASSERT(signaling_thread()->IsCurrent()); + if (voice_channel_) { + return voice_channel_->GetRtpParameters(ssrc); + } + return RtpParameters(); +} + +bool WebRtcSession::SetAudioRtpParameters(uint32_t ssrc, + const RtpParameters& parameters) { + ASSERT(signaling_thread()->IsCurrent()); + if (!voice_channel_) { + return false; + } + return voice_channel_->SetRtpParameters(ssrc, parameters); +} + bool WebRtcSession::SetCaptureDevice(uint32_t ssrc, cricket::VideoCapturer* camera) { ASSERT(signaling_thread()->IsCurrent()); @@ -1297,6 +1314,23 @@ void WebRtcSession::SetVideoSend(uint32_t ssrc, } } +RtpParameters WebRtcSession::GetVideoRtpParameters(uint32_t ssrc) const { + ASSERT(signaling_thread()->IsCurrent()); + if (video_channel_) { + return video_channel_->GetRtpParameters(ssrc); + } + return RtpParameters(); +} + +bool WebRtcSession::SetVideoRtpParameters(uint32_t ssrc, + const RtpParameters& parameters) { + ASSERT(signaling_thread()->IsCurrent()); + if (!video_channel_) { + return false; + } + return video_channel_->SetRtpParameters(ssrc, parameters); +} + bool WebRtcSession::CanInsertDtmf(const std::string& track_id) { ASSERT(signaling_thread()->IsCurrent()); if (!voice_channel_) { diff --git a/webrtc/api/webrtcsession.h b/webrtc/api/webrtcsession.h index 9495116471..e884678c0f 100644 --- a/webrtc/api/webrtcsession.h +++ b/webrtc/api/webrtcsession.h @@ -245,6 +245,10 @@ class WebRtcSession : public AudioProviderInterface, void SetRawAudioSink(uint32_t ssrc, rtc::scoped_ptr sink) override; + RtpParameters GetAudioRtpParameters(uint32_t ssrc) const override; + bool SetAudioRtpParameters(uint32_t ssrc, + const RtpParameters& parameters) override; + // Implements VideoMediaProviderInterface. bool SetCaptureDevice(uint32_t ssrc, cricket::VideoCapturer* camera) override; void SetVideoPlayout( @@ -255,6 +259,10 @@ class WebRtcSession : public AudioProviderInterface, bool enable, const cricket::VideoOptions* options) override; + RtpParameters GetVideoRtpParameters(uint32_t ssrc) const override; + bool SetVideoRtpParameters(uint32_t ssrc, + const RtpParameters& parameters) override; + // Implements DtmfProviderInterface. virtual bool CanInsertDtmf(const std::string& track_id); virtual bool InsertDtmf(const std::string& track_id, diff --git a/webrtc/api/webrtcsession_unittest.cc b/webrtc/api/webrtcsession_unittest.cc index c0fff52513..77c4854680 100644 --- a/webrtc/api/webrtcsession_unittest.cc +++ b/webrtc/api/webrtcsession_unittest.cc @@ -3385,6 +3385,25 @@ TEST_F(WebRtcSessionTest, SetAudioPlayout) { EXPECT_EQ(1, volume); } +TEST_F(WebRtcSessionTest, AudioMaxSendBitrateNotImplemented) { + // This test verifies that RtpParameters for audio RtpSenders cannot be + // changed. + // TODO(skvlad): Update the test after adding support for bitrate limiting in + // WebRtcAudioSendStream. + + Init(); + SendAudioVideoStream1(); + CreateAndSetRemoteOfferAndLocalAnswer(); + cricket::FakeVoiceMediaChannel* channel = media_engine_->GetVoiceChannel(0); + ASSERT_TRUE(channel != NULL); + uint32_t send_ssrc = channel->send_streams()[0].first_ssrc(); + webrtc::RtpParameters params = session_->GetAudioRtpParameters(send_ssrc); + + EXPECT_EQ(0, params.encodings.size()); + params.encodings.push_back(webrtc::RtpEncodingParameters()); + EXPECT_FALSE(session_->SetAudioRtpParameters(send_ssrc, params)); +} + TEST_F(WebRtcSessionTest, SetAudioSend) { Init(); SendAudioVideoStream1(); @@ -3451,6 +3470,34 @@ TEST_F(WebRtcSessionTest, SetVideoPlayout) { EXPECT_TRUE(channel->sinks().begin()->second == NULL); } +TEST_F(WebRtcSessionTest, SetVideoMaxSendBitrate) { + Init(); + SendAudioVideoStream1(); + CreateAndSetRemoteOfferAndLocalAnswer(); + cricket::FakeVideoMediaChannel* channel = media_engine_->GetVideoChannel(0); + ASSERT_TRUE(channel != NULL); + uint32_t send_ssrc = channel->send_streams()[0].first_ssrc(); + EXPECT_EQ(-1, channel->max_bps()); + webrtc::RtpParameters params = session_->GetVideoRtpParameters(send_ssrc); + EXPECT_EQ(1, params.encodings.size()); + EXPECT_EQ(-1, params.encodings[0].max_bitrate_bps); + params.encodings[0].max_bitrate_bps = 1000; + EXPECT_TRUE(session_->SetVideoRtpParameters(send_ssrc, params)); + + // Read back the parameters and verify they have been changed. + params = session_->GetVideoRtpParameters(send_ssrc); + EXPECT_EQ(1, params.encodings.size()); + EXPECT_EQ(1000, params.encodings[0].max_bitrate_bps); + + // Verify that the video channel received the new parameters. + params = channel->GetRtpParameters(send_ssrc); + EXPECT_EQ(1, params.encodings.size()); + EXPECT_EQ(1000, params.encodings[0].max_bitrate_bps); + + // Verify that the global bitrate limit has not been changed. + EXPECT_EQ(-1, channel->max_bps()); +} + TEST_F(WebRtcSessionTest, SetVideoSend) { Init(); SendAudioVideoStream1(); diff --git a/webrtc/media/base/fakemediaengine.h b/webrtc/media/base/fakemediaengine.h index 831ac0671c..7f9561d25e 100644 --- a/webrtc/media/base/fakemediaengine.h +++ b/webrtc/media/base/fakemediaengine.h @@ -96,9 +96,14 @@ template class RtpHelper : public Base { return false; } send_streams_.push_back(sp); + rtp_parameters_[sp.first_ssrc()] = CreateRtpParametersWithOneEncoding(); return true; } virtual bool RemoveSendStream(uint32_t ssrc) { + auto parameters_iterator = rtp_parameters_.find(ssrc); + if (parameters_iterator != rtp_parameters_.end()) { + rtp_parameters_.erase(parameters_iterator); + } return RemoveStreamBySsrc(&send_streams_, ssrc); } virtual bool AddRecvStream(const StreamParams& sp) { @@ -112,6 +117,26 @@ template class RtpHelper : public Base { virtual bool RemoveRecvStream(uint32_t ssrc) { return RemoveStreamBySsrc(&receive_streams_, ssrc); } + + virtual webrtc::RtpParameters GetRtpParameters(uint32_t ssrc) const { + auto parameters_iterator = rtp_parameters_.find(ssrc); + if (parameters_iterator != rtp_parameters_.end()) { + return parameters_iterator->second; + } + return webrtc::RtpParameters(); + } + virtual bool SetRtpParameters(uint32_t ssrc, + const webrtc::RtpParameters& parameters) { + auto parameters_iterator = rtp_parameters_.find(ssrc); + if (parameters_iterator != rtp_parameters_.end()) { + parameters_iterator->second = parameters; + return true; + } + // Replicate the behavior of the real media channel: return false + // when setting parameters for unknown SSRCs. + return false; + } + bool IsStreamMuted(uint32_t ssrc) const { bool ret = muted_streams_.find(ssrc) != muted_streams_.end(); // If |ssrc = 0| check if the first send stream is muted. @@ -204,6 +229,7 @@ template class RtpHelper : public Base { std::vector send_streams_; std::vector receive_streams_; std::set muted_streams_; + std::map rtp_parameters_; bool fail_set_send_codecs_; bool fail_set_recv_codecs_; uint32_t send_ssrc_; @@ -224,8 +250,7 @@ class FakeVoiceMediaChannel : public RtpHelper { }; explicit FakeVoiceMediaChannel(FakeVoiceEngine* engine, const AudioOptions& options) - : engine_(engine), - time_since_last_typing_(-1) { + : engine_(engine), time_since_last_typing_(-1), max_bps_(-1) { output_scalings_[0] = 1.0; // For default channel. SetOptions(options); } @@ -237,7 +262,7 @@ class FakeVoiceMediaChannel : public RtpHelper { return dtmf_info_queue_; } const AudioOptions& options() const { return options_; } - + int max_bps() const { return max_bps_; } virtual bool SetSendParameters(const AudioSendParameters& params) { return (SetSendCodecs(params.codecs) && SetSendRtpHeaderExtensions(params.extensions) && @@ -249,6 +274,7 @@ class FakeVoiceMediaChannel : public RtpHelper { return (SetRecvCodecs(params.codecs) && SetRecvRtpHeaderExtensions(params.extensions)); } + virtual bool SetPlayout(bool playout) { set_playout(playout); return true; @@ -374,7 +400,10 @@ class FakeVoiceMediaChannel : public RtpHelper { send_codecs_ = codecs; return true; } - bool SetMaxSendBandwidth(int bps) { return true; } + bool SetMaxSendBandwidth(int bps) { + max_bps_ = bps; + return true; + } bool SetOptions(const AudioOptions& options) { // Does a "merge" of current options and set options. options_.SetAll(options); @@ -407,6 +436,7 @@ class FakeVoiceMediaChannel : public RtpHelper { AudioOptions options_; std::map local_sinks_; std::unique_ptr sink_; + int max_bps_; }; // A helper function to compare the FakeVoiceMediaChannel::DtmfInfo. @@ -443,7 +473,6 @@ class FakeVideoMediaChannel : public RtpHelper { SetSendRtpHeaderExtensions(params.extensions) && SetMaxSendBandwidth(params.max_bandwidth_bps)); } - virtual bool SetRecvParameters(const VideoRecvParameters& params) { return (SetRecvCodecs(params.codecs) && SetRecvRtpHeaderExtensions(params.extensions)); diff --git a/webrtc/media/base/mediachannel.h b/webrtc/media/base/mediachannel.h index 68efbe7984..f07c33a4ca 100644 --- a/webrtc/media/base/mediachannel.h +++ b/webrtc/media/base/mediachannel.h @@ -15,6 +15,7 @@ #include #include +#include "webrtc/api/rtpparameters.h" #include "webrtc/base/basictypes.h" #include "webrtc/base/buffer.h" #include "webrtc/base/dscp.h" @@ -79,6 +80,17 @@ static std::string VectorToString(const std::vector& vals) { return ost.str(); } +template +static T MinPositive(T a, T b) { + if (a <= 0) { + return b; + } + if (b <= 0) { + return a; + } + return std::min(a, b); +} + // Construction-time settings, passed to // MediaControllerInterface::Create, and passed on when creating // MediaChannels. @@ -975,6 +987,9 @@ class VideoMediaChannel : public MediaChannel { virtual bool SetSendParameters(const VideoSendParameters& params) = 0; virtual bool SetRecvParameters(const VideoRecvParameters& params) = 0; + virtual webrtc::RtpParameters GetRtpParameters(uint32_t ssrc) const = 0; + virtual bool SetRtpParameters(uint32_t ssrc, + const webrtc::RtpParameters& parameters) = 0; // Gets the currently set codecs/payload types to be used for outgoing media. virtual bool GetSendCodec(VideoCodec* send_codec) = 0; // Starts or stops transmission (and potentially capture) of local video. diff --git a/webrtc/media/base/mediaengine.cc b/webrtc/media/base/mediaengine.cc index df17ee7839..4027137565 100644 --- a/webrtc/media/base/mediaengine.cc +++ b/webrtc/media/base/mediaengine.cc @@ -35,3 +35,14 @@ MediaEngineFactory::MediaEngineCreateFunction }; // namespace cricket #endif // DISABLE_MEDIA_ENGINE_FACTORY + +namespace cricket { + +webrtc::RtpParameters CreateRtpParametersWithOneEncoding() { + webrtc::RtpParameters parameters; + webrtc::RtpEncodingParameters encoding; + parameters.encodings.push_back(encoding); + return parameters; +} + +}; // namespace cricket \ No newline at end of file diff --git a/webrtc/media/base/mediaengine.h b/webrtc/media/base/mediaengine.h index f113840333..001eb1ff88 100644 --- a/webrtc/media/base/mediaengine.h +++ b/webrtc/media/base/mediaengine.h @@ -19,6 +19,7 @@ #include #include "webrtc/audio_state.h" +#include "webrtc/api/rtpparameters.h" #include "webrtc/base/fileutils.h" #include "webrtc/base/sigslotrepeater.h" #include "webrtc/media/base/codec.h" @@ -204,6 +205,8 @@ class DataEngineInterface { virtual const std::vector& data_codecs() = 0; }; +webrtc::RtpParameters CreateRtpParametersWithOneEncoding(); + } // namespace cricket #endif // WEBRTC_MEDIA_BASE_MEDIAENGINE_H_ diff --git a/webrtc/media/engine/webrtcvideoengine2.cc b/webrtc/media/engine/webrtcvideoengine2.cc index 6442613081..5fe36c4386 100644 --- a/webrtc/media/engine/webrtcvideoengine2.cc +++ b/webrtc/media/engine/webrtcvideoengine2.cc @@ -818,6 +818,32 @@ bool WebRtcVideoChannel2::SetSendParameters(const VideoSendParameters& params) { send_params_ = params; return true; } +webrtc::RtpParameters WebRtcVideoChannel2::GetRtpParameters( + uint32_t ssrc) const { + rtc::CritScope stream_lock(&stream_crit_); + auto it = send_streams_.find(ssrc); + if (it == send_streams_.end()) { + LOG(LS_WARNING) << "Attempting to get RTP parameters for stream with ssrc " + << ssrc << " which doesn't exist."; + return webrtc::RtpParameters(); + } + + return it->second->rtp_parameters(); +} + +bool WebRtcVideoChannel2::SetRtpParameters( + uint32_t ssrc, + const webrtc::RtpParameters& parameters) { + rtc::CritScope stream_lock(&stream_crit_); + auto it = send_streams_.find(ssrc); + if (it == send_streams_.end()) { + LOG(LS_ERROR) << "Attempting to set RTP parameters for stream with ssrc " + << ssrc << " which doesn't exist."; + return false; + } + + return it->second->SetRtpParameters(parameters); +} bool WebRtcVideoChannel2::GetChangedRecvParameters( const VideoRecvParameters& params, @@ -1476,6 +1502,7 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::WebRtcVideoSendStream( external_encoder_factory_(external_encoder_factory), stream_(nullptr), parameters_(config, options, max_bitrate_bps, codec_settings), + rtp_parameters_(CreateRtpParametersWithOneEncoding()), pending_encoder_reconfiguration_(false), allocated_encoder_(nullptr, webrtc::kVideoCodecUnknown, false), sending_(false), @@ -1763,8 +1790,6 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::SetSendParameters( recreate_stream = true; } if (params.max_bandwidth_bps) { - // Max bitrate has changed, reconfigure encoder settings on the next frame - // or stream recreation. parameters_.max_bitrate_bps = *params.max_bandwidth_bps; pending_encoder_reconfiguration_ = true; } @@ -1798,6 +1823,31 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::SetSendParameters( } } +bool WebRtcVideoChannel2::WebRtcVideoSendStream::SetRtpParameters( + const webrtc::RtpParameters& new_parameters) { + if (!ValidateRtpParameters(new_parameters)) { + return false; + } + + rtc::CritScope cs(&lock_); + if (new_parameters.encodings[0].max_bitrate_bps != + rtp_parameters_.encodings[0].max_bitrate_bps) { + pending_encoder_reconfiguration_ = true; + } + rtp_parameters_ = new_parameters; + return true; +} + +bool WebRtcVideoChannel2::WebRtcVideoSendStream::ValidateRtpParameters( + const webrtc::RtpParameters& rtp_parameters) { + if (rtp_parameters.encodings.size() != 1) { + LOG(LS_ERROR) + << "Attempted to set RtpParameters without exactly one encoding"; + return false; + } + return true; +} + webrtc::VideoEncoderConfig WebRtcVideoChannel2::WebRtcVideoSendStream::CreateVideoEncoderConfig( const Dimensions& dimensions, @@ -1837,9 +1887,11 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::CreateVideoEncoderConfig( stream_count = 1; } - encoder_config.streams = - CreateVideoStreams(clamped_codec, parameters_.options, - parameters_.max_bitrate_bps, stream_count); + int stream_max_bitrate = + MinPositive(rtp_parameters_.encodings[0].max_bitrate_bps, + parameters_.max_bitrate_bps); + encoder_config.streams = CreateVideoStreams( + clamped_codec, parameters_.options, stream_max_bitrate, stream_count); // Conference mode screencast uses 2 temporal layers split at 100kbit. if (parameters_.conference_mode && is_screencast && diff --git a/webrtc/media/engine/webrtcvideoengine2.h b/webrtc/media/engine/webrtcvideoengine2.h index 4461027e91..850bcaef63 100644 --- a/webrtc/media/engine/webrtcvideoengine2.h +++ b/webrtc/media/engine/webrtcvideoengine2.h @@ -145,6 +145,9 @@ class WebRtcVideoChannel2 : public VideoMediaChannel, public webrtc::Transport { bool SetSendParameters(const VideoSendParameters& params) override; bool SetRecvParameters(const VideoRecvParameters& params) override; + webrtc::RtpParameters GetRtpParameters(uint32_t ssrc) const override; + bool SetRtpParameters(uint32_t ssrc, + const webrtc::RtpParameters& parameters) override; bool GetSendCodec(VideoCodec* send_codec) override; bool SetSend(bool send) override; bool SetVideoSend(uint32_t ssrc, @@ -245,6 +248,7 @@ class WebRtcVideoChannel2 : public VideoMediaChannel, public webrtc::Transport { void SetOptions(const VideoOptions& options); // TODO(pbos): Move logic from SetOptions into this method. void SetSendParameters(const ChangedSendParameters& send_params); + bool SetRtpParameters(const webrtc::RtpParameters& parameters); void OnFrame(const cricket::VideoFrame& frame) override; bool SetCapturer(VideoCapturer* capturer); @@ -254,6 +258,8 @@ class WebRtcVideoChannel2 : public VideoMediaChannel, public webrtc::Transport { void Start(); void Stop(); + webrtc::RtpParameters rtp_parameters() const { return rtp_parameters_; } + // Implements webrtc::LoadObserver. void OnLoadUpdate(Load load) override; @@ -338,6 +344,7 @@ class WebRtcVideoChannel2 : public VideoMediaChannel, public webrtc::Transport { const VideoCodec& codec) const EXCLUSIVE_LOCKS_REQUIRED(lock_); void SetDimensions(int width, int height) EXCLUSIVE_LOCKS_REQUIRED(lock_); + bool ValidateRtpParameters(const webrtc::RtpParameters& parameters); rtc::ThreadChecker thread_checker_; rtc::AsyncInvoker invoker_; @@ -358,7 +365,15 @@ class WebRtcVideoChannel2 : public VideoMediaChannel, public webrtc::Transport { rtc::CriticalSection lock_; webrtc::VideoSendStream* stream_ GUARDED_BY(lock_); + // Contains settings that are the same for all streams in the MediaChannel, + // such as codecs, header extensions, and the global bitrate limit for the + // entire channel. VideoSendStreamParameters parameters_ GUARDED_BY(lock_); + // Contains settings that are unique for each stream, such as max_bitrate. + // TODO(skvlad): Move ssrcs_ and ssrc_groups_ into rtp_parameters_. + // TODO(skvlad): Combine parameters_ and rtp_parameters_ once we have only + // one stream per MediaChannel. + webrtc::RtpParameters rtp_parameters_; bool pending_encoder_reconfiguration_ GUARDED_BY(lock_); VideoEncoderSettings encoder_settings_ GUARDED_BY(lock_); AllocatedEncoder allocated_encoder_ GUARDED_BY(lock_); diff --git a/webrtc/media/engine/webrtcvideoengine2_unittest.cc b/webrtc/media/engine/webrtcvideoengine2_unittest.cc index f5e4cff94d..b44eb9907d 100644 --- a/webrtc/media/engine/webrtcvideoengine2_unittest.cc +++ b/webrtc/media/engine/webrtcvideoengine2_unittest.cc @@ -1080,6 +1080,38 @@ class WebRtcVideoChannel2Test : public WebRtcVideoEngine2Test { return AddSendStream(CreateSimStreamParams("cname", ssrcs)); } + int GetMaxEncoderBitrate(cricket::FakeVideoCapturer& capturer) { + EXPECT_TRUE(capturer.CaptureFrame()); + + std::vector streams = + fake_call_->GetVideoSendStreams(); + EXPECT_TRUE(streams.size() > 0); + FakeVideoSendStream* stream = streams[streams.size() - 1]; + + webrtc::VideoEncoderConfig encoder_config = stream->GetEncoderConfig(); + EXPECT_EQ(1, encoder_config.streams.size()); + return encoder_config.streams[0].max_bitrate_bps; + } + + void SetAndExpectMaxBitrate(cricket::FakeVideoCapturer& capturer, + int global_max, + int stream_max, + int expected_encoder_bitrate) { + VideoSendParameters limited_send_params = send_parameters_; + limited_send_params.max_bandwidth_bps = global_max; + EXPECT_TRUE(channel_->SetSendParameters(limited_send_params)); + webrtc::RtpParameters parameters = channel_->GetRtpParameters(last_ssrc_); + EXPECT_EQ(1UL, parameters.encodings.size()); + parameters.encodings[0].max_bitrate_bps = stream_max; + EXPECT_TRUE(channel_->SetRtpParameters(last_ssrc_, parameters)); + // Read back the parameteres and verify they have the correct value + parameters = channel_->GetRtpParameters(last_ssrc_); + EXPECT_EQ(1UL, parameters.encodings.size()); + EXPECT_EQ(stream_max, parameters.encodings[0].max_bitrate_bps); + // Verify that the new value propagated down to the encoder + EXPECT_EQ(expected_encoder_bitrate, GetMaxEncoderBitrate(capturer)); + } + std::unique_ptr fake_call_; std::unique_ptr channel_; cricket::VideoSendParameters send_parameters_; @@ -3113,6 +3145,66 @@ TEST_F(WebRtcVideoChannel2Test, RedRtxPacketDoesntCreateUnsignalledStream) { TestReceiveUnsignalledSsrcPacket(kRedRtxPayloadType, false); } +TEST_F(WebRtcVideoChannel2Test, CanSentMaxBitrateForExistingStream) { + AddSendStream(); + + cricket::FakeVideoCapturer capturer; + EXPECT_TRUE(channel_->SetCapturer(last_ssrc_, &capturer)); + cricket::VideoFormat capture_format_hd = + capturer.GetSupportedFormats()->front(); + EXPECT_EQ(1280, capture_format_hd.width); + EXPECT_EQ(720, capture_format_hd.height); + EXPECT_EQ(cricket::CS_RUNNING, capturer.Start(capture_format_hd)); + EXPECT_TRUE(channel_->SetSend(true)); + + int default_encoder_bitrate = GetMaxEncoderBitrate(capturer); + EXPECT_TRUE(default_encoder_bitrate > 1000); + + // TODO(skvlad): Resolve the inconsistency between the interpretation + // of the global bitrate limit for audio and video: + // - Audio: max_bandwidth_bps = 0 - fail the operation, + // max_bandwidth_bps = -1 - remove the bandwidth limit + // - Video: max_bandwidth_bps = 0 - remove the bandwidth limit, + // max_bandwidth_bps = -1 - do not change the previously set + // limit. + + SetAndExpectMaxBitrate(capturer, 1000, 0, 1000); + SetAndExpectMaxBitrate(capturer, 1000, 800, 800); + SetAndExpectMaxBitrate(capturer, 600, 800, 600); + SetAndExpectMaxBitrate(capturer, 0, 800, 800); + SetAndExpectMaxBitrate(capturer, 0, 0, default_encoder_bitrate); + + EXPECT_TRUE(channel_->SetCapturer(last_ssrc_, NULL)); +} + +TEST_F(WebRtcVideoChannel2Test, CannotSetMaxBitrateForNonexistentStream) { + webrtc::RtpParameters nonexistent_parameters = + channel_->GetRtpParameters(last_ssrc_); + EXPECT_EQ(0, nonexistent_parameters.encodings.size()); + + nonexistent_parameters.encodings.push_back(webrtc::RtpEncodingParameters()); + EXPECT_FALSE(channel_->SetRtpParameters(last_ssrc_, nonexistent_parameters)); +} + +TEST_F(WebRtcVideoChannel2Test, + CannotSetRtpParametersWithIncorrectNumberOfEncodings) { + // This test verifies that setting RtpParameters succeeds only if + // the structure contains exactly one encoding. + // TODO(skvlad): Update this test when we strat supporting setting parameters + // for each encoding individually. + + AddSendStream(); + // Setting RtpParameters with no encoding is expected to fail. + webrtc::RtpParameters parameters; + EXPECT_FALSE(channel_->SetRtpParameters(last_ssrc_, parameters)); + // Setting RtpParameters with exactly one encoding should succeed. + parameters.encodings.push_back(webrtc::RtpEncodingParameters()); + EXPECT_TRUE(channel_->SetRtpParameters(last_ssrc_, parameters)); + // Two or more encodings should result in failure. + parameters.encodings.push_back(webrtc::RtpEncodingParameters()); + EXPECT_FALSE(channel_->SetRtpParameters(last_ssrc_, parameters)); +} + void WebRtcVideoChannel2Test::TestReceiverLocalSsrcConfiguration( bool receiver_first) { EXPECT_TRUE(channel_->SetSendParameters(send_parameters_)); diff --git a/webrtc/pc/channel.cc b/webrtc/pc/channel.cc index 2920af53d0..d9aa8512aa 100644 --- a/webrtc/pc/channel.cc +++ b/webrtc/pc/channel.cc @@ -1384,6 +1384,30 @@ void VoiceChannel::SetRawAudioSink( InvokeOnWorker(Bind(&SetRawAudioSink_w, media_channel(), ssrc, &sink)); } +webrtc::RtpParameters VoiceChannel::GetRtpParameters(uint32_t ssrc) const { + return worker_thread()->Invoke( + Bind(&VoiceChannel::GetRtpParameters_w, this, ssrc)); +} + +webrtc::RtpParameters VoiceChannel::GetRtpParameters_w(uint32_t ssrc) const { + // Not yet implemented. + // TODO(skvlad): Add support for limiting send bitrate for audio channels. + return webrtc::RtpParameters(); +} + +bool VoiceChannel::SetRtpParameters(uint32_t ssrc, + const webrtc::RtpParameters& parameters) { + return InvokeOnWorker( + Bind(&VoiceChannel::SetRtpParameters_w, this, ssrc, parameters)); +} + +bool VoiceChannel::SetRtpParameters_w(uint32_t ssrc, + webrtc::RtpParameters parameters) { + // Not yet implemented. + // TODO(skvlad): Add support for limiting send bitrate for audio channels. + return false; +} + bool VoiceChannel::GetStats(VoiceMediaInfo* stats) { return InvokeOnWorker(Bind(&VoiceMediaChannel::GetStats, media_channel(), stats)); @@ -1536,7 +1560,9 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content, if (audio->agc_minus_10db()) { send_params.options.adjust_agc_delta = rtc::Optional(kAgcMinus10db); } - if (!media_channel()->SetSendParameters(send_params)) { + + bool parameters_applied = media_channel()->SetSendParameters(send_params); + if (!parameters_applied) { SafeSetError("Failed to set remote audio description send parameters.", error_desc); return false; @@ -1661,6 +1687,25 @@ bool VideoChannel::SetVideoSend(uint32_t ssrc, ssrc, mute, options)); } +webrtc::RtpParameters VideoChannel::GetRtpParameters(uint32_t ssrc) const { + return worker_thread()->Invoke( + Bind(&VideoChannel::GetRtpParameters_w, this, ssrc)); +} + +webrtc::RtpParameters VideoChannel::GetRtpParameters_w(uint32_t ssrc) const { + return media_channel()->GetRtpParameters(ssrc); +} + +bool VideoChannel::SetRtpParameters(uint32_t ssrc, + const webrtc::RtpParameters& parameters) { + return InvokeOnWorker( + Bind(&VideoChannel::SetRtpParameters_w, this, ssrc, parameters)); +} + +bool VideoChannel::SetRtpParameters_w(uint32_t ssrc, + webrtc::RtpParameters parameters) { + return media_channel()->SetRtpParameters(ssrc, parameters); +} void VideoChannel::ChangeState() { // Send outgoing data if we're the active call, we have the remote content, // and we have had some form of connectivity. @@ -1768,7 +1813,10 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content, if (video->conference_mode()) { send_params.conference_mode = true; } - if (!media_channel()->SetSendParameters(send_params)) { + + bool parameters_applied = media_channel()->SetSendParameters(send_params); + + if (!parameters_applied) { SafeSetError("Failed to set remote video description send parameters.", error_desc); return false; diff --git a/webrtc/pc/channel.h b/webrtc/pc/channel.h index 60b19f8bba..1937b0483f 100644 --- a/webrtc/pc/channel.h +++ b/webrtc/pc/channel.h @@ -361,6 +361,8 @@ class VoiceChannel : public BaseChannel { bool SetOutputVolume(uint32_t ssrc, double volume); void SetRawAudioSink(uint32_t ssrc, std::unique_ptr sink); + webrtc::RtpParameters GetRtpParameters(uint32_t ssrc) const; + bool SetRtpParameters(uint32_t ssrc, const webrtc::RtpParameters& parameters); // Get statistics about the current media session. bool GetStats(VoiceMediaInfo* stats); @@ -381,6 +383,8 @@ class VoiceChannel : public BaseChannel { int GetInputLevel_w(); int GetOutputLevel_w(); void GetActiveStreams_w(AudioInfo::StreamList* actives); + webrtc::RtpParameters GetRtpParameters_w(uint32_t ssrc) const; + bool SetRtpParameters_w(uint32_t ssrc, webrtc::RtpParameters parameters); private: // overrides from BaseChannel @@ -452,6 +456,8 @@ class VideoChannel : public BaseChannel { sigslot::signal2 SignalMediaMonitor; bool SetVideoSend(uint32_t ssrc, bool enable, const VideoOptions* options); + webrtc::RtpParameters GetRtpParameters(uint32_t ssrc) const; + bool SetRtpParameters(uint32_t ssrc, const webrtc::RtpParameters& parameters); private: // overrides from BaseChannel @@ -464,6 +470,8 @@ class VideoChannel : public BaseChannel { ContentAction action, std::string* error_desc); bool GetStats_w(VideoMediaInfo* stats); + webrtc::RtpParameters GetRtpParameters_w(uint32_t ssrc) const; + bool SetRtpParameters_w(uint32_t ssrc, webrtc::RtpParameters parameters); virtual void OnMessage(rtc::Message* pmsg); virtual void GetSrtpCryptoSuites(std::vector* crypto_suites) const; diff --git a/webrtc/pc/channel_unittest.cc b/webrtc/pc/channel_unittest.cc index 7b4a31a1b3..3752db1f55 100644 --- a/webrtc/pc/channel_unittest.cc +++ b/webrtc/pc/channel_unittest.cc @@ -1769,6 +1769,53 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { EXPECT_FALSE(media_channel1_->ready_to_send()); } + bool SetRemoteContentWithBitrateLimit(int remote_limit) { + typename T::Content content; + CreateContent(0, kPcmuCodec, kH264Codec, &content); + content.set_bandwidth(remote_limit); + return channel1_->SetRemoteContent(&content, CA_OFFER, NULL); + } + + webrtc::RtpParameters BitrateLimitedParameters(int limit) { + webrtc::RtpParameters parameters; + webrtc::RtpEncodingParameters encoding; + encoding.max_bitrate_bps = limit; + parameters.encodings.push_back(encoding); + return parameters; + } + + void VerifyMaxBitrate(const webrtc::RtpParameters& parameters, + int expected_bitrate) { + EXPECT_EQ(1UL, parameters.encodings.size()); + EXPECT_EQ(expected_bitrate, parameters.encodings[0].max_bitrate_bps); + } + + void DefaultMaxBitrateIsUnlimited() { + CreateChannels(0, 0); + EXPECT_TRUE( + channel1_->SetLocalContent(&local_media_content1_, CA_OFFER, NULL)); + EXPECT_EQ(media_channel1_->max_bps(), -1); + VerifyMaxBitrate(media_channel1_->GetRtpParameters(kSsrc1), -1); + } + + void CanChangeMaxBitrate() { + CreateChannels(0, 0); + EXPECT_TRUE( + channel1_->SetLocalContent(&local_media_content1_, CA_OFFER, NULL)); + + EXPECT_TRUE( + channel1_->SetRtpParameters(kSsrc1, BitrateLimitedParameters(1000))); + VerifyMaxBitrate(channel1_->GetRtpParameters(kSsrc1), 1000); + VerifyMaxBitrate(media_channel1_->GetRtpParameters(kSsrc1), 1000); + EXPECT_EQ(-1, media_channel1_->max_bps()); + + EXPECT_TRUE( + channel1_->SetRtpParameters(kSsrc1, BitrateLimitedParameters(-1))); + VerifyMaxBitrate(channel1_->GetRtpParameters(kSsrc1), -1); + VerifyMaxBitrate(media_channel1_->GetRtpParameters(kSsrc1), -1); + EXPECT_EQ(-1, media_channel1_->max_bps()); + } + protected: // TODO(pbos): Remove playout from all media channels and let renderers mute // themselves. @@ -2229,6 +2276,26 @@ TEST_F(VoiceChannelTest, SendBundleToBundleWithRtcpMuxSecure) { Base::SendBundleToBundle(kAudioPts, arraysize(kAudioPts), true, true); } +TEST_F(VoiceChannelTest, GetRtpParametersIsNotImplemented) { + // These tests verify that the Get/SetRtpParameters methods for VoiceChannel + // always fail as they are not implemented. + // TODO(skvlad): Replace with full tests when support for bitrate limiting + // for audio RtpSenders is added. + CreateChannels(0, 0); + EXPECT_TRUE( + channel1_->SetLocalContent(&local_media_content1_, CA_OFFER, NULL)); + webrtc::RtpParameters voice_parameters = channel1_->GetRtpParameters(kSsrc1); + EXPECT_EQ(0UL, voice_parameters.encodings.size()); +} + +TEST_F(VoiceChannelTest, SetRtpParametersIsNotImplemented) { + CreateChannels(0, 0); + EXPECT_TRUE( + channel1_->SetLocalContent(&local_media_content1_, CA_OFFER, NULL)); + EXPECT_FALSE( + channel1_->SetRtpParameters(kSsrc1, BitrateLimitedParameters(1000))); +} + // VideoChannelTest TEST_F(VideoChannelTest, TestInit) { Base::TestInit(); @@ -2457,6 +2524,14 @@ TEST_F(VideoChannelTest, TestOnReadyToSendWithRtcpMux) { Base::TestOnReadyToSendWithRtcpMux(); } +TEST_F(VideoChannelTest, DefaultMaxBitrateIsUnlimited) { + Base::DefaultMaxBitrateIsUnlimited(); +} + +TEST_F(VideoChannelTest, CanChangeMaxBitrate) { + Base::CanChangeMaxBitrate(); +} + // DataChannelTest class DataChannelTest