Chromium Code Reviews| Index: webrtc/test/fake_encoder.cc |
| diff --git a/webrtc/test/fake_encoder.cc b/webrtc/test/fake_encoder.cc |
| index fce12c61a804c8af841435b001779374b912066c..8de59a1d0b8ec57b9aa52a46f8cedf70f233aeae 100644 |
| --- a/webrtc/test/fake_encoder.cc |
| +++ b/webrtc/test/fake_encoder.cc |
| @@ -27,8 +27,9 @@ namespace test { |
| FakeEncoder::FakeEncoder(Clock* clock) |
| : clock_(clock), |
| callback_(nullptr), |
| + configured_input_framerate_(-1), |
| max_target_bitrate_kbps_(-1), |
| - last_encode_time_ms_(0) { |
| + pending_keyframe_(true) { |
| // Generate some arbitrary not-all-zero data |
| for (size_t i = 0; i < sizeof(encoded_buffer_); ++i) { |
| encoded_buffer_[i] = static_cast<uint8_t>(i); |
| @@ -47,6 +48,8 @@ int32_t FakeEncoder::InitEncode(const VideoCodec* config, |
| rtc::CritScope cs(&crit_sect_); |
| config_ = *config; |
| target_bitrate_.SetBitrate(0, 0, config_.startBitrate * 1000); |
| + configured_input_framerate_ = config_.maxFramerate; |
| + pending_keyframe_ = true; |
| return 0; |
| } |
| @@ -59,9 +62,10 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image, |
| EncodedImageCallback* callback; |
| uint32_t target_bitrate_sum_kbps; |
| int max_target_bitrate_kbps; |
| - int64_t last_encode_time_ms; |
| size_t num_encoded_bytes; |
| + int framerate; |
| VideoCodecMode mode; |
| + bool keyframe; |
| { |
| rtc::CritScope cs(&crit_sect_); |
| max_framerate = config_.maxFramerate; |
| @@ -72,42 +76,33 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image, |
| callback = callback_; |
| target_bitrate_sum_kbps = target_bitrate_.get_sum_kbps(); |
| max_target_bitrate_kbps = max_target_bitrate_kbps_; |
| - last_encode_time_ms = last_encode_time_ms_; |
| num_encoded_bytes = sizeof(encoded_buffer_); |
| mode = config_.mode; |
| + if (configured_input_framerate_ > 0) { |
| + framerate = configured_input_framerate_; |
| + } else { |
| + framerate = max_framerate; |
| + } |
| + keyframe = pending_keyframe_; |
| + pending_keyframe_ = false; |
| } |
| - int64_t time_now_ms = clock_->TimeInMilliseconds(); |
| - const bool first_encode = (last_encode_time_ms == 0); |
| - RTC_DCHECK_GT(max_framerate, 0); |
| - int64_t time_since_last_encode_ms = 1000 / max_framerate; |
| - if (!first_encode) { |
| - // For all frames but the first we can estimate the display time by looking |
| - // at the display time of the previous frame. |
| - time_since_last_encode_ms = time_now_ms - last_encode_time_ms; |
| - } |
| - if (time_since_last_encode_ms > 3 * 1000 / max_framerate) { |
| - // Rudimentary check to make sure we don't widely overshoot bitrate target |
| - // when resuming encoding after a suspension. |
| - time_since_last_encode_ms = 3 * 1000 / max_framerate; |
| + for (FrameType frame_type : *frame_types) { |
| + if (frame_type == kVideoFrameKey) { |
| + keyframe = true; |
| + break; |
| + } |
| } |
| - size_t bits_available = |
| - static_cast<size_t>(target_bitrate_sum_kbps * time_since_last_encode_ms); |
| - size_t min_bits = static_cast<size_t>(simulcast_streams[0].minBitrate * |
| - time_since_last_encode_ms); |
| + RTC_DCHECK_GT(max_framerate, 0); |
| - if (bits_available < min_bits) |
| - bits_available = min_bits; |
| - size_t max_bits = |
| - static_cast<size_t>(max_target_bitrate_kbps * time_since_last_encode_ms); |
| - if (max_bits > 0 && max_bits < bits_available) |
| - bits_available = max_bits; |
| + size_t bitrate = target_bitrate_sum_kbps; |
| + bitrate = |
| + std::max(bitrate, static_cast<size_t>(simulcast_streams[0].minBitrate)); |
| + if (max_target_bitrate_kbps > 0) |
| + bitrate = std::min(bitrate, static_cast<size_t>(max_target_bitrate_kbps)); |
| - { |
| - rtc::CritScope cs(&crit_sect_); |
| - last_encode_time_ms_ = time_now_ms; |
| - } |
| + size_t bits_available = target_bitrate_sum_kbps * 1000 / framerate; |
| RTC_DCHECK_GT(num_simulcast_streams, 0); |
| for (unsigned char i = 0; i < num_simulcast_streams; ++i) { |
| @@ -116,18 +111,38 @@ int32_t FakeEncoder::Encode(const VideoFrame& input_image, |
| specifics.codecType = kVideoCodecGeneric; |
| specifics.codecSpecific.generic.simulcast_idx = i; |
| size_t min_stream_bits = static_cast<size_t>( |
| - simulcast_streams[i].minBitrate * time_since_last_encode_ms); |
| + (simulcast_streams[i].minBitrate * 1000) / framerate); |
| size_t max_stream_bits = static_cast<size_t>( |
| - simulcast_streams[i].maxBitrate * time_since_last_encode_ms); |
| + (simulcast_streams[i].maxBitrate * 1000) / framerate); |
| size_t stream_bits = (bits_available > max_stream_bits) ? max_stream_bits : |
| bits_available; |
| size_t stream_bytes = (stream_bits + 7) / 8; |
| - if (first_encode) { |
| + if (keyframe) { |
| // The first frame is a key frame and should be larger. |
| - // TODO(holmer): The FakeEncoder should store the bits_available between |
| - // encodes so that it can compensate for oversized frames. |
| - stream_bytes *= 10; |
| + // Store the overshoot bytes and distribute them over the coming frames, |
| + // so that we on average meet the bitrate target. |
| + const int kKeyframeSizeFactor = 10; |
| + // Pay 1/2 of a frame debt factor every frame. |
| + const int kNumDebtPayments = (kKeyframeSizeFactor - 1) * 2; |
| + debt_bytes_.resize(kNumDebtPayments); |
| + size_t total_debt_bytes = (kKeyframeSizeFactor - 1) * stream_bytes; |
| + for (int i = 0; i < kNumDebtPayments; ++i) |
| + debt_bytes_[i] += total_debt_bytes / kNumDebtPayments; |
|
stefan-webrtc
2017/05/23 17:03:20
An option is to just keep track of a total debt an
sprang_webrtc
2017/05/24 09:07:07
Maybe, but then paying of the whole debt will take
holmer
2017/06/02 11:38:05
You could have a total_debt and a payed_debt and t
|
| + stream_bytes *= kKeyframeSizeFactor; |
| + } else { |
| + if (!debt_bytes_.empty()) { |
| + size_t debt_bytes = std::min(stream_bytes, debt_bytes_[0]); |
| + debt_bytes_.pop_front(); |
| + if (debt_bytes >= stream_bytes) { |
|
stefan-webrtc
2017/05/23 17:03:20
This can only be true if == right? What is it that
sprang_webrtc
2017/05/24 09:07:07
Oops, the std::min above shouldn't be there.
I'll
|
| + if (debt_bytes_.empty()) |
| + debt_bytes_.resize(1); |
| + debt_bytes_[0] += debt_bytes - stream_bytes + 1; |
| + debt_bytes = stream_bytes - 1; |
| + } |
| + stream_bytes -= debt_bytes; |
| + } |
| } |
| + |
| if (stream_bytes > num_encoded_bytes) |
| stream_bytes = num_encoded_bytes; |
| @@ -175,6 +190,7 @@ int32_t FakeEncoder::SetRateAllocation(const BitrateAllocation& rate_allocation, |
| uint32_t framerate) { |
| rtc::CritScope cs(&crit_sect_); |
| target_bitrate_ = rate_allocation; |
| + configured_input_framerate_ = framerate; |
| return 0; |
| } |
| @@ -183,6 +199,11 @@ const char* FakeEncoder::ImplementationName() const { |
| return kImplementationName; |
| } |
| +int FakeEncoder::GetConfiguredInputFramerate() { |
| + rtc::CritScope cs(&crit_sect_); |
| + return configured_input_framerate_; |
| +} |
| + |
| FakeH264Encoder::FakeH264Encoder(Clock* clock) |
| : FakeEncoder(clock), callback_(nullptr), idr_counter_(0) { |
| FakeEncoder::RegisterEncodeCompleteCallback(this); |