Chromium Code Reviews| 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"; |
| } |