Refactor and remove media_optimization::MediaOptimization.

This CL removes MediaOptmization and folds some of its functionality
into VideoStreamEncoder.

The FPS tracking is now handled by a RateStatistics instance. Frame
dropping is still handled by FrameDropper. Both of these now live
directly in VideoStreamEncoder.
There is no intended change in behavior from this CL, but due to a new
way of measuring frame rate, some minor perf changes can be expected.

A small change in behavior is that OnBitrateUpdated is now called
directly rather than on the next frame. Since both encoding frame and
setting rate allocations happen on the encoder worker thread, there's
really no reason to cache bitrates and wait until the next frame.
An edge case though is that if a new bitrate is set before the first
frame, we must remember that bitrate and then apply it after the video
bitrate allocator has been first created.

In addition to existing unit tests, manual tests have been used to
confirm that frame dropping works as expected with misbehaving encoders.

Bug: webrtc:10164
Change-Id: I7ee9c8d3c4f2bcf23c8c420310b05a4d35d94744
Reviewed-on: https://webrtc-review.googlesource.com/c/115620
Commit-Queue: Erik Språng <sprang@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26147}
This commit is contained in:
Erik Språng
2019-01-07 15:11:16 +01:00
committed by Commit Bot
parent 73eb41fe9e
commit 07276e4f89
14 changed files with 371 additions and 517 deletions

View File

@ -553,6 +553,16 @@ class VideoStreamEncoderTest : public ::testing::Test {
force_init_encode_failed_ = force_failure;
}
void SimulateOvershoot(double rate_factor) {
rtc::CritScope lock(&local_crit_sect_);
rate_factor_ = rate_factor;
}
uint32_t GetLastFramerate() {
rtc::CritScope lock(&local_crit_sect_);
return last_framerate_;
}
private:
int32_t Encode(const VideoFrame& input_image,
const CodecSpecificInfo* codec_specific_info,
@ -599,6 +609,25 @@ class VideoStreamEncoderTest : public ::testing::Test {
return res;
}
int32_t SetRateAllocation(const VideoBitrateAllocation& rate_allocation,
uint32_t framerate) {
rtc::CritScope lock(&local_crit_sect_);
VideoBitrateAllocation adjusted_rate_allocation;
for (size_t si = 0; si < kMaxSpatialLayers; ++si) {
for (size_t ti = 0; ti < kMaxTemporalStreams; ++ti) {
if (rate_allocation.HasBitrate(si, ti)) {
adjusted_rate_allocation.SetBitrate(
si, ti,
static_cast<uint32_t>(rate_allocation.GetBitrate(si, ti) *
rate_factor_));
}
}
}
last_framerate_ = framerate;
return FakeEncoder::SetRateAllocation(adjusted_rate_allocation,
framerate);
}
rtc::CriticalSection local_crit_sect_;
bool block_next_encode_ RTC_GUARDED_BY(local_crit_sect_) = false;
rtc::Event continue_encode_event_;
@ -610,6 +639,8 @@ class VideoStreamEncoderTest : public ::testing::Test {
std::vector<std::unique_ptr<Vp8TemporalLayers>> allocated_temporal_layers_
RTC_GUARDED_BY(local_crit_sect_);
bool force_init_encode_failed_ RTC_GUARDED_BY(local_crit_sect_) = false;
double rate_factor_ RTC_GUARDED_BY(local_crit_sect_) = 1.0;
uint32_t last_framerate_ RTC_GUARDED_BY(local_crit_sect_) = 0;
};
class TestSink : public VideoStreamEncoder::EncoderSink {
@ -2093,9 +2124,8 @@ TEST_F(VideoStreamEncoderTest, CallsBitrateObserver) {
DefaultVideoBitrateAllocator(fake_encoder_.codec_config())
.GetAllocation(kLowTargetBitrateBps, kDefaultFps);
// First called on bitrate updated, then again on first frame.
EXPECT_CALL(bitrate_observer, OnBitrateAllocationUpdated(expected_bitrate))
.Times(2);
.Times(1);
video_stream_encoder_->OnBitrateUpdated(kLowTargetBitrateBps, 0, 0);
const int64_t kStartTimeMs = 1;
@ -3156,9 +3186,6 @@ TEST_F(VideoStreamEncoderTest, DoesNotUpdateBitrateAllocationWhenSuspended) {
MockBitrateObserver bitrate_observer;
video_stream_encoder_->SetBitrateAllocationObserver(&bitrate_observer);
EXPECT_CALL(bitrate_observer, OnBitrateAllocationUpdated(_)).Times(1);
// Initial bitrate update.
video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
video_stream_encoder_->WaitUntilTaskQueueIsIdle();
@ -3227,4 +3254,80 @@ TEST_F(VideoStreamEncoderTest,
video_stream_encoder_->Stop();
}
TEST_F(VideoStreamEncoderTest, DropsFramesWhenEncoderOvershoots) {
const int kFrameWidth = 320;
const int kFrameHeight = 240;
const int kFps = 30;
const int kTargetBitrateBps = 120000;
const int kNumFramesInRun = kFps * 5; // Runs of five seconds.
video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
int64_t timestamp_ms = fake_clock_.TimeNanos() / rtc::kNumNanosecsPerMillisec;
max_framerate_ = kFps;
// Insert 3 seconds of video, verify number of drops with normal bitrate.
fake_encoder_.SimulateOvershoot(1.0);
int num_dropped = 0;
for (int i = 0; i < kNumFramesInRun; ++i) {
video_source_.IncomingCapturedFrame(
CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
// Wait up to two frame durations for a frame to arrive.
if (!TimedWaitForEncodedFrame(timestamp_ms, 2 * 1000 / kFps)) {
++num_dropped;
}
timestamp_ms += 1000 / kFps;
}
// Frame drops should be less than 5%
EXPECT_LT(num_dropped, 5 * kNumFramesInRun / 100);
// Make encoder produce frames at double the expected bitrate during 3 seconds
// of video, verify number of drops. Rate needs to be slightly changed in
// order to force the rate to be reconfigured.
fake_encoder_.SimulateOvershoot(2.0);
video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps + 1000, 0, 0);
num_dropped = 0;
for (int i = 0; i < kNumFramesInRun; ++i) {
video_source_.IncomingCapturedFrame(
CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
// Wait up to two frame durations for a frame to arrive.
if (!TimedWaitForEncodedFrame(timestamp_ms, 2 * 1000 / kFps)) {
++num_dropped;
}
timestamp_ms += 1000 / kFps;
}
// Frame drops should be more than 40%.
EXPECT_GT(num_dropped, 40 * kNumFramesInRun / 100);
video_stream_encoder_->Stop();
}
TEST_F(VideoStreamEncoderTest, ConfiguresCorrectFrameRate) {
const int kFrameWidth = 320;
const int kFrameHeight = 240;
const int kActualInputFps = 24;
const int kTargetBitrateBps = 120000;
ASSERT_GT(max_framerate_, kActualInputFps);
int64_t timestamp_ms = fake_clock_.TimeNanos() / rtc::kNumNanosecsPerMillisec;
max_framerate_ = kActualInputFps;
video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
// Insert 3 seconds of video, with an input fps lower than configured max.
for (int i = 0; i < kActualInputFps * 3; ++i) {
video_source_.IncomingCapturedFrame(
CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
// Wait up to two frame durations for a frame to arrive.
WaitForEncodedFrame(timestamp_ms);
timestamp_ms += 1000 / kActualInputFps;
}
EXPECT_NEAR(kActualInputFps, fake_encoder_.GetLastFramerate(), 1);
video_stream_encoder_->Stop();
}
} // namespace webrtc