Index: remoting/codec/video_encoder_vpx.cc |
diff --git a/remoting/codec/video_encoder_vpx.cc b/remoting/codec/video_encoder_vpx.cc |
index e281ef58efbc9c9e092c3b2efadf04b528dbebdf..ea557b94818f8f31e86301657921517ce51b2c39 100644 |
--- a/remoting/codec/video_encoder_vpx.cc |
+++ b/remoting/codec/video_encoder_vpx.cc |
@@ -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, |
@@ -268,14 +273,34 @@ void VideoEncoderVpx::SetLosslessColor(bool want_lossless) { |
} |
} |
+void VideoEncoderVpx::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> VideoEncoderVpx::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. |
Sergey Ulanov
2016/04/21 22:55:49
nit: comment formatting
|
+ 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 +310,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); |
+ |
+ // Apply active map to the encoder. |
+ |
+ if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { |
+ LOG(ERROR) << "Unable to apply active map"; |
+ } |
+ } |
- // Do the actual encoding. |
- int timestamp = (clock_->NowTicks() - timestamp_base_).InMilliseconds(); |
+ // 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. |
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" |
@@ -340,11 +367,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; |
@@ -356,8 +388,12 @@ std::unique_ptr<VideoPacket> VideoEncoderVpx::Encode( |
VideoEncoderVpx::VideoEncoderVpx(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) { |
DCHECK(use_vp9_ || !lossless_color_); |
@@ -385,32 +421,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"; |
} |