| 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..801556b64cd666fc388d4c6949a0a2e96af85618 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(int new_bitrate_kbps) {
|
| + target_bitrate_kbps_ = new_bitrate_kbps;
|
| + // Configuration not initialized.
|
| + if (config_.g_timebase.den == 0)
|
| + return;
|
| +
|
| + if (config_.rc_target_bitrate == static_cast<unsigned int>(new_bitrate_kbps))
|
| + return;
|
| + config_.rc_target_bitrate = new_bitrate_kbps;
|
| +
|
| + // 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 << " 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,32 @@ 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 requirement. We specify a target rate of 1 / 15 fps here.
|
| + // TODO(isheriff): Investigate if it makes sense to increase the target FPS.
|
| 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 +343,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 +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;
|
| @@ -354,12 +386,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 +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";
|
| }
|
|
|
| @@ -422,8 +453,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 +506,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 +519,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 +535,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 +561,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) {
|
|
|