Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Unified Diff: remoting/codec/webrtc_video_encoder_vpx.cc

Issue 1908203002: Adapt encoder behavior to target bitrate (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Moved webrtc encoder to its own file Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: remoting/codec/webrtc_video_encoder_vpx.cc
diff --git a/remoting/codec/video_encoder_vpx.cc b/remoting/codec/webrtc_video_encoder_vpx.cc
similarity index 77%
copy from remoting/codec/video_encoder_vpx.cc
copy to remoting/codec/webrtc_video_encoder_vpx.cc
index e281ef58efbc9c9e092c3b2efadf04b528dbebdf..5071a783edff468e64a1a466a3709e39e0c1f6c9 100644
--- a/remoting/codec/video_encoder_vpx.cc
+++ b/remoting/codec/webrtc_video_encoder_vpx.cc
@@ -1,8 +1,8 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "remoting/codec/video_encoder_vpx.h"
+#include "remoting/codec/webrtc_video_encoder_vpx.h"
#include <utility>
@@ -42,11 +42,13 @@ const int kVp9I444ProfileNumber = 1;
const int kVp9AqModeNone = 0;
const int kVp9AqModeCyclicRefresh = 3;
+const int kDefaultTargetBitrateKbps = 1000;
+
void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config,
const webrtc::DesktopSize& size) {
// Use millisecond granularity time base.
config->g_timebase.num = 1;
- config->g_timebase.den = 1000;
+ config->g_timebase.den = base::Time::kMicrosecondsPerSecond;
config->g_w = size.width();
config->g_h = size.height();
@@ -61,19 +63,20 @@ void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config,
config->kf_min_dist = 10000;
config->kf_max_dist = 10000;
- // Using 2 threads gives a great boost in performance for most systems with
- // adequate processing power. NB: Going to multiple threads on low end
- // windows systems can really hurt performance.
- // http://crbug.com/99179
- config->g_threads = (base::SysInfo::NumberOfProcessors() > 2) ? 2 : 1;
+ // Allow multiple cores on a system to be used for encoding for
+ // performance while at the same time ensuring we do not saturate.
+ config->g_threads = (base::SysInfo::NumberOfProcessors() + 1) / 2;
+
+ // Do not drop any frames at encoder.
+ config->rc_dropframe_thresh = 0;
+ // We do not want variations in bandwidth.
+ config->rc_end_usage = VPX_CBR;
+ config->rc_undershoot_pct = 100;
+ config->rc_overshoot_pct = 15;
}
void SetVp8CodecParameters(vpx_codec_enc_cfg_t* config,
const webrtc::DesktopSize& size) {
- // Adjust default target bit-rate to account for actual desktop size.
- config->rc_target_bitrate = size.width() * size.height() *
- config->rc_target_bitrate / config->g_w / config->g_h;
-
SetCommonCodecParameters(config, size);
// Value of 2 means using the real time profile. This is basically a
@@ -81,9 +84,11 @@ void SetVp8CodecParameters(vpx_codec_enc_cfg_t* config,
// encoding.
config->g_profile = 2;
- // Clamping the quantizer constrains the worst-case quality and CPU usage.
+ // To enable remoting to be highly interactive and allow the target bitrate
+ // to be met, we relax the max quantizer. The quality will get topped-off
+ // in subsequent frames.
config->rc_min_quantizer = 20;
- config->rc_max_quantizer = 30;
+ config->rc_max_quantizer = 63;
}
void SetVp9CodecParameters(vpx_codec_enc_cfg_t* config,
@@ -139,8 +144,7 @@ void SetVp9CodecOptions(vpx_codec_ctx_t* codec, bool lossless_encode) {
DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set noise sensitivity";
// Configure the codec to tune it for screen media.
- ret = vpx_codec_control(
- codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN);
+ ret = vpx_codec_control(codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN);
DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set screen content mode";
// Set cyclic refresh (aka "top-off") only for lossy encoding.
@@ -205,11 +209,12 @@ void CreateImage(bool use_i444,
// Assuming macroblocks are 16x16, aligning the planes' strides above also
// macroblock aligned them.
static_assert(kMacroBlockSize == 16, "macroblock_size_not_16");
- const int y_rows = ((image->h - 1) & ~(kMacroBlockSize-1)) + kMacroBlockSize;
+ const int y_rows =
+ ((image->h - 1) & ~(kMacroBlockSize - 1)) + kMacroBlockSize;
const int uv_rows = y_rows >> image->y_chroma_shift;
// Allocate a YUV buffer large enough for the aligned data & padding.
- const int buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows;
+ const int buffer_size = y_stride * y_rows + 2 * uv_stride * uv_rows;
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[buffer_size]);
// Reset image value to 128 so we just need to fill in the y plane.
@@ -232,22 +237,22 @@ void CreateImage(bool use_i444,
} // namespace
// static
-std::unique_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() {
- return base::WrapUnique(new VideoEncoderVpx(false));
+std::unique_ptr<WebRtcVideoEncoderVpx> WebRtcVideoEncoderVpx::CreateForVP8() {
+ return base::WrapUnique(new WebRtcVideoEncoderVpx(false));
}
// static
-std::unique_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9() {
- return base::WrapUnique(new VideoEncoderVpx(true));
+std::unique_ptr<WebRtcVideoEncoderVpx> WebRtcVideoEncoderVpx::CreateForVP9() {
+ return base::WrapUnique(new WebRtcVideoEncoderVpx(true));
}
-VideoEncoderVpx::~VideoEncoderVpx() {}
+WebRtcVideoEncoderVpx::~WebRtcVideoEncoderVpx() {}
-void VideoEncoderVpx::SetTickClockForTests(base::TickClock* tick_clock) {
+void WebRtcVideoEncoderVpx::SetTickClockForTests(base::TickClock* tick_clock) {
clock_ = tick_clock;
}
-void VideoEncoderVpx::SetLosslessEncode(bool want_lossless) {
+void WebRtcVideoEncoderVpx::SetLosslessEncode(bool want_lossless) {
if (use_vp9_ && (want_lossless != lossless_encode_)) {
lossless_encode_ = want_lossless;
if (codec_)
@@ -256,7 +261,7 @@ void VideoEncoderVpx::SetLosslessEncode(bool want_lossless) {
}
}
-void VideoEncoderVpx::SetLosslessColor(bool want_lossless) {
+void WebRtcVideoEncoderVpx::SetLosslessColor(bool want_lossless) {
if (use_vp9_ && (want_lossless != lossless_color_)) {
lossless_color_ = want_lossless;
// TODO(wez): Switch to ConfigureCodec() path once libvpx supports it.
@@ -268,14 +273,33 @@ void VideoEncoderVpx::SetLosslessColor(bool want_lossless) {
}
}
-std::unique_ptr<VideoPacket> VideoEncoderVpx::Encode(
+void WebRtcVideoEncoderVpx::UpdateTargetBitrate(uint32_t new_bitrate) {
+ target_bitrate_kbps_ = new_bitrate;
+ // Configuration not initialized.
+ if (config_.g_timebase.den == 0)
+ return;
+
+ if (config_.rc_target_bitrate == new_bitrate)
+ return;
+ config_.rc_target_bitrate = new_bitrate;
+
+ // Update encoder context.
+ if (vpx_codec_enc_config_set(codec_.get(), &config_))
+ NOTREACHED() << "Unable to set encoder config";
+
+ VLOG(1) << "New rc_target_bitrate: " << new_bitrate << " kbps";
+}
+
+std::unique_ptr<VideoPacket> WebRtcVideoEncoderVpx::Encode(
const webrtc::DesktopFrame& frame,
uint32_t flags) {
DCHECK_LE(32, frame.size().width());
DCHECK_LE(32, frame.size().height());
- // If there is nothing to encode, and nothing to top-off, then return nothing.
- if (frame.updated_region().is_empty() && !encode_unchanged_frame_)
+ // VP8: Encode top-off is controlled at the call site.
+ // VP9: Based on information fetching active map, we return here if there is
+ // nothing to top-off.
+ if (use_vp9_ && frame.updated_region().is_empty() && !encode_unchanged_frame_)
return nullptr;
// Create or reconfigure the codec to match the size of |frame|.
@@ -285,29 +309,31 @@ std::unique_ptr<VideoPacket> VideoEncoderVpx::Encode(
Configure(frame.size());
}
- // Convert the updated capture data ready for encode.
- webrtc::DesktopRegion updated_region;
- PrepareImage(frame, &updated_region);
-
- // Update active map based on updated region.
- SetActiveMapFromRegion(updated_region);
-
- // Apply active map to the encoder.
vpx_active_map_t act_map;
act_map.rows = active_map_size_.height();
act_map.cols = active_map_size_.width();
act_map.active_map = active_map_.get();
- if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) {
- LOG(ERROR) << "Unable to apply active map";
- }
- if (flags & REQUEST_KEY_FRAME)
- vpx_codec_control(codec_.get(), VP8E_SET_FRAME_FLAGS, VPX_EFLAG_FORCE_KF);
+ webrtc::DesktopRegion updated_region;
+ if (!frame.updated_region().is_empty()) {
+ // Convert the updated capture data ready for encode.
+ PrepareImage(frame, &updated_region);
+
+ // Update active map based on updated region.
+ SetActiveMapFromRegion(updated_region);
- // Do the actual encoding.
- int timestamp = (clock_->NowTicks() - timestamp_base_).InMilliseconds();
+ // Apply active map to the encoder.
+
+ if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) {
+ LOG(ERROR) << "Unable to apply active map";
+ }
+ }
+
+ // Frame rate is adapted based on how well the encoder meets the target
+ // bandwidth requiremetn. We specify a target rate of 1 / 15 fps here.
Sergey Ulanov 2016/05/10 00:00:58 typo: requiremetn
Sergey Ulanov 2016/05/10 00:00:58 why is it 1/15 fps instead of 30 fps?
Irfan 2016/05/10 16:30:45 I added a TODO here. Basically, we need to further
vpx_codec_err_t ret = vpx_codec_encode(
- codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME);
+ codec_.get(), image_.get(), 0, 66666,
+ (flags & REQUEST_KEY_FRAME) ? VPX_EFLAG_FORCE_KF : 0, VPX_DL_REALTIME);
DCHECK_EQ(ret, VPX_CODEC_OK)
<< "Encoding error: " << vpx_codec_err_to_string(ret) << "\n"
<< "Details: " << vpx_codec_error(codec_.get()) << "\n"
@@ -316,8 +342,8 @@ std::unique_ptr<VideoPacket> VideoEncoderVpx::Encode(
if (use_vp9_ && !lossless_encode_) {
ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map);
DCHECK_EQ(ret, VPX_CODEC_OK)
- << "Failed to fetch active map: "
- << vpx_codec_err_to_string(ret) << "\n";
+ << "Failed to fetch active map: " << vpx_codec_err_to_string(ret)
+ << "\n";
UpdateRegionFromActiveMap(&updated_region);
// If the encoder output no changes then there's nothing left to top-off.
@@ -340,11 +366,16 @@ std::unique_ptr<VideoPacket> VideoEncoderVpx::Encode(
if (!vpx_packet)
continue;
+ int quantizer = -1;
switch (vpx_packet->kind) {
case VPX_CODEC_CX_FRAME_PKT:
got_data = true;
packet->set_data(vpx_packet->data.frame.buf, vpx_packet->data.frame.sz);
packet->set_key_frame(vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY);
+ CHECK_EQ(vpx_codec_control(codec_.get(), VP8E_GET_LAST_QUANTIZER_64,
+ &quantizer),
+ VPX_CODEC_OK);
+ packet->set_quantizer(quantizer);
break;
default:
break;
@@ -354,12 +385,16 @@ std::unique_ptr<VideoPacket> VideoEncoderVpx::Encode(
return packet;
}
-VideoEncoderVpx::VideoEncoderVpx(bool use_vp9)
+WebRtcVideoEncoderVpx::WebRtcVideoEncoderVpx(bool use_vp9)
: use_vp9_(use_vp9),
+ target_bitrate_kbps_(kDefaultTargetBitrateKbps),
encode_unchanged_frame_(false),
- clock_(&default_tick_clock_) {}
+ clock_(&default_tick_clock_) {
+ // Indicates config is still uninitialized.
+ config_.g_timebase.den = 0;
+}
-void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) {
+void WebRtcVideoEncoderVpx::Configure(const webrtc::DesktopSize& size) {
DCHECK(use_vp9_ || !lossless_color_);
DCHECK(use_vp9_ || !lossless_encode_);
@@ -385,32 +420,27 @@ void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) {
}
}
- // (Re)Set the base for frame timestamps if the codec is being (re)created.
- if (!codec_) {
- timestamp_base_ = clock_->NowTicks();
- }
-
// Fetch a default configuration for the desired codec.
const vpx_codec_iface_t* interface =
use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx();
- vpx_codec_enc_cfg_t config;
- vpx_codec_err_t ret = vpx_codec_enc_config_default(interface, &config, 0);
+ vpx_codec_err_t ret = vpx_codec_enc_config_default(interface, &config_, 0);
DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to fetch default configuration";
// Customize the default configuration to our needs.
if (use_vp9_) {
- SetVp9CodecParameters(&config, size, lossless_color_, lossless_encode_);
+ SetVp9CodecParameters(&config_, size, lossless_color_, lossless_encode_);
} else {
- SetVp8CodecParameters(&config, size);
+ SetVp8CodecParameters(&config_, size);
}
+ config_.rc_target_bitrate = target_bitrate_kbps_;
// Initialize or re-configure the codec with the custom configuration.
if (!codec_) {
codec_.reset(new vpx_codec_ctx_t);
- ret = vpx_codec_enc_init(codec_.get(), interface, &config, 0);
+ ret = vpx_codec_enc_init(codec_.get(), interface, &config_, 0);
CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to initialize codec";
} else {
- ret = vpx_codec_enc_config_set(codec_.get(), &config);
+ ret = vpx_codec_enc_config_set(codec_.get(), &config_);
CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to reconfigure codec";
}
@@ -422,8 +452,9 @@ void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) {
}
}
-void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame,
- webrtc::DesktopRegion* updated_region) {
+void WebRtcVideoEncoderVpx::PrepareImage(
+ const webrtc::DesktopFrame& frame,
+ webrtc::DesktopRegion* updated_region) {
if (frame.updated_region().is_empty()) {
updated_region->Clear();
return;
@@ -474,13 +505,12 @@ void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame,
for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
r.Advance()) {
const webrtc::DesktopRect& rect = r.rect();
- int rgb_offset = rgb_stride * rect.top() +
- rect.left() * kBytesPerRgbPixel;
+ int rgb_offset =
+ rgb_stride * rect.top() + rect.left() * kBytesPerRgbPixel;
int yuv_offset = uv_stride * rect.top() + rect.left();
libyuv::ARGBToI444(rgb_data + rgb_offset, rgb_stride,
- y_data + yuv_offset, y_stride,
- u_data + yuv_offset, uv_stride,
- v_data + yuv_offset, uv_stride,
+ y_data + yuv_offset, y_stride, u_data + yuv_offset,
+ uv_stride, v_data + yuv_offset, uv_stride,
rect.width(), rect.height());
}
break;
@@ -488,15 +518,14 @@ void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame,
for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
r.Advance()) {
const webrtc::DesktopRect& rect = r.rect();
- int rgb_offset = rgb_stride * rect.top() +
- rect.left() * kBytesPerRgbPixel;
+ int rgb_offset =
+ rgb_stride * rect.top() + rect.left() * kBytesPerRgbPixel;
int y_offset = y_stride * rect.top() + rect.left();
int uv_offset = uv_stride * rect.top() / 2 + rect.left() / 2;
- libyuv::ARGBToI420(rgb_data + rgb_offset, rgb_stride,
- y_data + y_offset, y_stride,
- u_data + uv_offset, uv_stride,
- v_data + uv_offset, uv_stride,
- rect.width(), rect.height());
+ libyuv::ARGBToI420(rgb_data + rgb_offset, rgb_stride, y_data + y_offset,
+ y_stride, u_data + uv_offset, uv_stride,
+ v_data + uv_offset, uv_stride, rect.width(),
+ rect.height());
}
break;
default:
@@ -505,7 +534,7 @@ void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame,
}
}
-void VideoEncoderVpx::SetActiveMapFromRegion(
+void WebRtcVideoEncoderVpx::SetActiveMapFromRegion(
const webrtc::DesktopRegion& updated_region) {
// Clear active map first.
memset(active_map_.get(), 0,
@@ -531,7 +560,7 @@ void VideoEncoderVpx::SetActiveMapFromRegion(
}
}
-void VideoEncoderVpx::UpdateRegionFromActiveMap(
+void WebRtcVideoEncoderVpx::UpdateRegionFromActiveMap(
webrtc::DesktopRegion* updated_region) {
const uint8_t* map = active_map_.get();
for (int y = 0; y < active_map_size_.height(); ++y) {

Powered by Google App Engine
This is Rietveld 408576698