Update encoder settings periodically, not only on new bandwidth estimate

Also moved actual update call to encoder thread, and tweaked frame rate
estimate to be less noisy.

This is a re-upload of https://review.webrtc.org/47249004

BUG=chromium:476469
R=stefan@webrtc.org

Review URL: https://codereview.webrtc.org/1180623005.

Cr-Commit-Position: refs/heads/master@{#9417}
This commit is contained in:
Erik Språng 2015-06-11 14:20:07 +02:00
parent 78fb3b3f8f
commit 66a641a9c6
5 changed files with 67 additions and 18 deletions

View File

@ -631,8 +631,9 @@ void MediaOptimization::ProcessIncomingFrameRate(int64_t now) {
} }
} }
if (num > 1) { if (num > 1) {
const int64_t diff = now - incoming_frame_times_[num - 1]; const int64_t diff =
incoming_frame_rate_ = 1.0; incoming_frame_times_[0] - incoming_frame_times_[num - 1];
incoming_frame_rate_ = 0.0; // No frame rate estimate available.
if (diff > 0) { if (diff > 0) {
incoming_frame_rate_ = nr_of_frames * 1000.0f / static_cast<float>(diff); incoming_frame_rate_ = nr_of_frames * 1000.0f / static_cast<float>(diff);
} }

View File

@ -79,6 +79,7 @@ class MediaOptimization {
// Informs Media Optimization of encoded output. // Informs Media Optimization of encoded output.
int32_t UpdateWithEncodedData(const EncodedImage& encoded_image); int32_t UpdateWithEncodedData(const EncodedImage& encoded_image);
// InputFrameRate 0 = no frame rate estimate available.
uint32_t InputFrameRate(); uint32_t InputFrameRate();
uint32_t SentFrameRate(); uint32_t SentFrameRate();
uint32_t SentBitRate(); uint32_t SentBitRate();

View File

@ -93,6 +93,7 @@ class VideoSender {
int32_t SetChannelParameters(uint32_t target_bitrate, // bits/s. int32_t SetChannelParameters(uint32_t target_bitrate, // bits/s.
uint8_t lossRate, uint8_t lossRate,
int64_t rtt); int64_t rtt);
int32_t UpdateEncoderParameters();
int32_t RegisterTransportCallback(VCMPacketizationCallback* transport); int32_t RegisterTransportCallback(VCMPacketizationCallback* transport);
int32_t RegisterSendStatisticsCallback(VCMSendStatisticsCallback* sendStats); int32_t RegisterSendStatisticsCallback(VCMSendStatisticsCallback* sendStats);
@ -132,6 +133,15 @@ class VideoSender {
VCMQMSettingsCallback* const qm_settings_callback_; VCMQMSettingsCallback* const qm_settings_callback_;
VCMProtectionCallback* protection_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 { class VideoReceiver {

View File

@ -42,6 +42,7 @@ VideoSender::VideoSender(Clock* clock,
current_codec_(), current_codec_(),
qm_settings_callback_(qm_settings_callback), qm_settings_callback_(qm_settings_callback),
protection_callback_(nullptr) { protection_callback_(nullptr) {
encoder_params_ = {0, 0, 0, 0, false};
// Allow VideoSender to be created on one thread but used on another, post // 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 // construction. This is currently how this class is being used by at least
// one external project (diffractor). // one external project (diffractor).
@ -67,6 +68,14 @@ int32_t VideoSender::Process() {
} }
} }
{
rtc::CritScope cs(&params_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; return returnValue;
} }
@ -216,27 +225,42 @@ int VideoSender::FrameRate(unsigned int* framerate) const {
int32_t VideoSender::SetChannelParameters(uint32_t target_bitrate, int32_t VideoSender::SetChannelParameters(uint32_t target_bitrate,
uint8_t lossRate, uint8_t lossRate,
int64_t rtt) { int64_t rtt) {
// TODO(tommi,mflodman): This method is called on the network thread via the uint32_t target_rate =
// OnNetworkChanged event (ViEEncoder::OnNetworkChanged). Could we instead _mediaOpt.SetTargetRates(target_bitrate, lossRate, rtt,
// post the updated information to the encoding thread and not grab a lock protection_callback_, qm_settings_callback_);
// 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 input_frame_rate = _mediaOpt.InputFrameRate(); uint32_t input_frame_rate = _mediaOpt.InputFrameRate();
rtc::CritScope cs(&params_lock_);
encoder_params_ =
EncoderParameters{target_rate, lossRate, rtt, input_frame_rate, true};
return VCM_OK;
}
int32_t VideoSender::UpdateEncoderParameters() {
EncoderParameters params;
{
rtc::CritScope cs(&params_lock_);
params = encoder_params_;
encoder_params_.updated = false;
}
if (!params.updated || params.target_bitrate == 0)
return VCM_OK;
CriticalSectionScoped sendCs(_sendCritSect); CriticalSectionScoped sendCs(_sendCritSect);
int32_t ret = VCM_UNINITIALIZED; int32_t ret = VCM_UNINITIALIZED;
static_assert(VCM_UNINITIALIZED < 0, "VCM_UNINITIALIZED must be negative."); 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) { if (_encoder != nullptr) {
ret = _encoder->SetChannelParameters(lossRate, rtt); ret = _encoder->SetChannelParameters(params.loss_rate, params.rtt);
if (ret >= 0) { if (ret >= 0) {
ret = _encoder->SetRates(target_rate, input_frame_rate); ret = _encoder->SetRates(params.target_bitrate, params.input_frame_rate);
} }
} }
return ret; return ret;
@ -300,6 +324,7 @@ void VideoSender::SetVideoProtection(bool enable,
int32_t VideoSender::AddVideoFrame(const VideoFrame& videoFrame, int32_t VideoSender::AddVideoFrame(const VideoFrame& videoFrame,
const VideoContentMetrics* contentMetrics, const VideoContentMetrics* contentMetrics,
const CodecSpecificInfo* codecSpecificInfo) { const CodecSpecificInfo* codecSpecificInfo) {
UpdateEncoderParameters();
CriticalSectionScoped cs(_sendCritSect); CriticalSectionScoped cs(_sendCritSect);
if (_encoder == nullptr) { if (_encoder == nullptr) {
return VCM_UNINITIALIZED; return VCM_UNINITIALIZED;

View File

@ -314,6 +314,20 @@ TEST_F(TestVideoSenderWithMockEncoder, TestIntraRequestsInternalCapture) {
EXPECT_EQ(-1, sender_->IntraFrameRequest(-1)); 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 { class TestVideoSenderWithVp8 : public TestVideoSender {
public: public:
TestVideoSenderWithVp8() TestVideoSenderWithVp8()
@ -354,14 +368,12 @@ class TestVideoSenderWithVp8 : public TestVideoSender {
EXPECT_CALL(post_encode_callback_, Encoded(_, NULL, NULL)) EXPECT_CALL(post_encode_callback_, Encoded(_, NULL, NULL))
.WillOnce(Return(0)); .WillOnce(Return(0));
AddFrame(); AddFrame();
// SetChannelParameters needs to be called frequently to propagate // SetChannelParameters needs to be called frequently to propagate
// framerate from the media optimization into the encoder. // framerate from the media optimization into the encoder.
// Note: SetChannelParameters fails if less than 2 frames are in the // Note: SetChannelParameters fails if less than 2 frames are in the
// buffer since it will fail to calculate the framerate. // buffer since it will fail to calculate the framerate.
if (i != 0) { if (i != 0) {
EXPECT_EQ(VCM_OK, EXPECT_EQ(VCM_OK, sender_->SetChannelParameters(
sender_->SetChannelParameters(
available_bitrate_kbps_ * 1000, 0, 200)); available_bitrate_kbps_ * 1000, 0, 200));
} }
} }