diff --git a/webrtc/modules/video_coding/main/source/media_optimization.cc b/webrtc/modules/video_coding/main/source/media_optimization.cc index 524a7b2b7..4dbdf44d5 100644 --- a/webrtc/modules/video_coding/main/source/media_optimization.cc +++ b/webrtc/modules/video_coding/main/source/media_optimization.cc @@ -631,8 +631,9 @@ void MediaOptimization::ProcessIncomingFrameRate(int64_t now) { } } if (num > 1) { - const int64_t diff = now - incoming_frame_times_[num - 1]; - incoming_frame_rate_ = 1.0; + const int64_t diff = + incoming_frame_times_[0] - incoming_frame_times_[num - 1]; + incoming_frame_rate_ = 0.0; // No frame rate estimate available. if (diff > 0) { incoming_frame_rate_ = nr_of_frames * 1000.0f / static_cast(diff); } diff --git a/webrtc/modules/video_coding/main/source/media_optimization.h b/webrtc/modules/video_coding/main/source/media_optimization.h index 3ab31f29a..e0010db4e 100644 --- a/webrtc/modules/video_coding/main/source/media_optimization.h +++ b/webrtc/modules/video_coding/main/source/media_optimization.h @@ -79,6 +79,7 @@ class MediaOptimization { // Informs Media Optimization of encoded output. int32_t UpdateWithEncodedData(const EncodedImage& encoded_image); + // InputFrameRate 0 = no frame rate estimate available. uint32_t InputFrameRate(); uint32_t SentFrameRate(); uint32_t SentBitRate(); diff --git a/webrtc/modules/video_coding/main/source/video_coding_impl.h b/webrtc/modules/video_coding/main/source/video_coding_impl.h index c8f822158..d738a7cef 100644 --- a/webrtc/modules/video_coding/main/source/video_coding_impl.h +++ b/webrtc/modules/video_coding/main/source/video_coding_impl.h @@ -93,6 +93,7 @@ class VideoSender { int32_t SetChannelParameters(uint32_t target_bitrate, // bits/s. uint8_t lossRate, int64_t rtt); + int32_t UpdateEncoderParameters(); int32_t RegisterTransportCallback(VCMPacketizationCallback* transport); int32_t RegisterSendStatisticsCallback(VCMSendStatisticsCallback* sendStats); @@ -132,6 +133,15 @@ class VideoSender { VCMQMSettingsCallback* const qm_settings_callback_; VCMProtectionCallback* protection_callback_; + + rtc::CriticalSection params_lock_; + struct EncoderParameters { + uint32_t target_bitrate; + uint8_t loss_rate; + int64_t rtt; + uint32_t input_frame_rate; + bool updated; + } encoder_params_ GUARDED_BY(params_lock_); }; class VideoReceiver { diff --git a/webrtc/modules/video_coding/main/source/video_sender.cc b/webrtc/modules/video_coding/main/source/video_sender.cc index 9b855da16..fb04b0e87 100644 --- a/webrtc/modules/video_coding/main/source/video_sender.cc +++ b/webrtc/modules/video_coding/main/source/video_sender.cc @@ -42,6 +42,7 @@ VideoSender::VideoSender(Clock* clock, current_codec_(), qm_settings_callback_(qm_settings_callback), protection_callback_(nullptr) { + encoder_params_ = {0, 0, 0, 0, false}; // Allow VideoSender to be created on one thread but used on another, post // construction. This is currently how this class is being used by at least // one external project (diffractor). @@ -67,6 +68,14 @@ int32_t VideoSender::Process() { } } + { + rtc::CritScope cs(¶ms_lock_); + // Force an encoder parameters update, so that incoming frame rate is + // updated even if bandwidth hasn't changed. + encoder_params_.input_frame_rate = _mediaOpt.InputFrameRate(); + encoder_params_.updated = true; + } + return returnValue; } @@ -216,27 +225,42 @@ int VideoSender::FrameRate(unsigned int* framerate) const { int32_t VideoSender::SetChannelParameters(uint32_t target_bitrate, uint8_t lossRate, int64_t rtt) { - // TODO(tommi,mflodman): This method is called on the network thread via the - // OnNetworkChanged event (ViEEncoder::OnNetworkChanged). Could we instead - // post the updated information to the encoding thread and not grab a lock - // here? This effectively means that the network thread will be blocked for - // as much as frame encoding period. + uint32_t target_rate = + _mediaOpt.SetTargetRates(target_bitrate, lossRate, rtt, + protection_callback_, qm_settings_callback_); - uint32_t target_rate = _mediaOpt.SetTargetRates(target_bitrate, - lossRate, - rtt, - protection_callback_, - qm_settings_callback_); uint32_t input_frame_rate = _mediaOpt.InputFrameRate(); + rtc::CritScope cs(¶ms_lock_); + encoder_params_ = + EncoderParameters{target_rate, lossRate, rtt, input_frame_rate, true}; + + return VCM_OK; +} + +int32_t VideoSender::UpdateEncoderParameters() { + EncoderParameters params; + { + rtc::CritScope cs(¶ms_lock_); + params = encoder_params_; + encoder_params_.updated = false; + } + + if (!params.updated || params.target_bitrate == 0) + return VCM_OK; + CriticalSectionScoped sendCs(_sendCritSect); int32_t ret = VCM_UNINITIALIZED; static_assert(VCM_UNINITIALIZED < 0, "VCM_UNINITIALIZED must be negative."); + if (params.input_frame_rate == 0) { + // No frame rate estimate available, use default. + params.input_frame_rate = current_codec_.maxFramerate; + } if (_encoder != nullptr) { - ret = _encoder->SetChannelParameters(lossRate, rtt); + ret = _encoder->SetChannelParameters(params.loss_rate, params.rtt); if (ret >= 0) { - ret = _encoder->SetRates(target_rate, input_frame_rate); + ret = _encoder->SetRates(params.target_bitrate, params.input_frame_rate); } } return ret; @@ -300,6 +324,7 @@ void VideoSender::SetVideoProtection(bool enable, int32_t VideoSender::AddVideoFrame(const VideoFrame& videoFrame, const VideoContentMetrics* contentMetrics, const CodecSpecificInfo* codecSpecificInfo) { + UpdateEncoderParameters(); CriticalSectionScoped cs(_sendCritSect); if (_encoder == nullptr) { return VCM_UNINITIALIZED; diff --git a/webrtc/modules/video_coding/main/source/video_sender_unittest.cc b/webrtc/modules/video_coding/main/source/video_sender_unittest.cc index fe1ea9847..88cb7b3ac 100644 --- a/webrtc/modules/video_coding/main/source/video_sender_unittest.cc +++ b/webrtc/modules/video_coding/main/source/video_sender_unittest.cc @@ -314,6 +314,20 @@ TEST_F(TestVideoSenderWithMockEncoder, TestIntraRequestsInternalCapture) { EXPECT_EQ(-1, sender_->IntraFrameRequest(-1)); } +TEST_F(TestVideoSenderWithMockEncoder, EncoderFramerateUpdatedViaProcess) { + sender_->SetChannelParameters(settings_.startBitrate, 0, 200); + const int64_t kRateStatsWindowMs = 2000; + const uint32_t kInputFps = 20; + int64_t start_time = clock_.TimeInMilliseconds(); + while (clock_.TimeInMilliseconds() < start_time + kRateStatsWindowMs) { + AddFrame(); + clock_.AdvanceTimeMilliseconds(1000 / kInputFps); + } + EXPECT_CALL(encoder_, SetRates(_, kInputFps)).Times(1).WillOnce(Return(0)); + sender_->Process(); + AddFrame(); +} + class TestVideoSenderWithVp8 : public TestVideoSender { public: TestVideoSenderWithVp8() @@ -354,15 +368,13 @@ class TestVideoSenderWithVp8 : public TestVideoSender { EXPECT_CALL(post_encode_callback_, Encoded(_, NULL, NULL)) .WillOnce(Return(0)); AddFrame(); - // SetChannelParameters needs to be called frequently to propagate // framerate from the media optimization into the encoder. // Note: SetChannelParameters fails if less than 2 frames are in the // buffer since it will fail to calculate the framerate. if (i != 0) { - EXPECT_EQ(VCM_OK, - sender_->SetChannelParameters( - available_bitrate_kbps_ * 1000, 0, 200)); + EXPECT_EQ(VCM_OK, sender_->SetChannelParameters( + available_bitrate_kbps_ * 1000, 0, 200)); } } }