Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "remoting/codec/video_encoder_vpx.h" | 5 #include "remoting/codec/video_encoder_vpx.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/command_line.h" | |
| 8 #include "base/logging.h" | 9 #include "base/logging.h" |
| 9 #include "base/sys_info.h" | 10 #include "base/sys_info.h" |
| 10 #include "remoting/base/util.h" | 11 #include "remoting/base/util.h" |
| 11 #include "remoting/proto/video.pb.h" | 12 #include "remoting/proto/video.pb.h" |
| 12 #include "third_party/libyuv/include/libyuv/convert_from_argb.h" | 13 #include "third_party/libyuv/include/libyuv/convert_from_argb.h" |
| 13 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" | 14 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
| 14 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" | 15 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" |
| 15 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" | 16 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" |
| 16 | 17 |
| 17 extern "C" { | 18 extern "C" { |
| 18 #define VPX_CODEC_DISABLE_COMPAT 1 | 19 #define VPX_CODEC_DISABLE_COMPAT 1 |
| 19 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" | 20 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" |
| 20 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" | 21 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" |
| 21 } | 22 } |
| 22 | 23 |
| 23 namespace remoting { | 24 namespace remoting { |
| 24 | 25 |
| 25 namespace { | 26 namespace { |
| 26 | 27 |
| 28 // Name of command-line flag to enable VP9 to use I444 by default. | |
| 29 const char kEnableI444SwitchName[] = "enable-i444"; | |
| 30 | |
| 27 // Number of bytes in an RGBx pixel. | 31 // Number of bytes in an RGBx pixel. |
| 28 const int kBytesPerRgbPixel = 4; | 32 const int kBytesPerRgbPixel = 4; |
| 29 | 33 |
| 30 // Defines the dimension of a macro block. This is used to compute the active | 34 // Defines the dimension of a macro block. This is used to compute the active |
| 31 // map for the encoder. | 35 // map for the encoder. |
| 32 const int kMacroBlockSize = 16; | 36 const int kMacroBlockSize = 16; |
| 33 | 37 |
| 34 // Magic encoder profile numbers for I420 and I444 input formats. | 38 // Magic encoder profile numbers for I420 and I444 input formats. |
| 35 const int kVp9I420ProfileNumber = 0; | 39 const int kVp9I420ProfileNumber = 0; |
| 36 const int kVp9I444ProfileNumber = 1; | 40 const int kVp9I444ProfileNumber = 1; |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 90 return ScopedVpxCodec(); | 94 return ScopedVpxCodec(); |
| 91 | 95 |
| 92 // Use the lowest level of noise sensitivity so as to spend less time | 96 // Use the lowest level of noise sensitivity so as to spend less time |
| 93 // on motion estimation and inter-prediction mode. | 97 // on motion estimation and inter-prediction mode. |
| 94 if (vpx_codec_control(codec.get(), VP8E_SET_NOISE_SENSITIVITY, 0)) | 98 if (vpx_codec_control(codec.get(), VP8E_SET_NOISE_SENSITIVITY, 0)) |
| 95 return ScopedVpxCodec(); | 99 return ScopedVpxCodec(); |
| 96 | 100 |
| 97 return codec.Pass(); | 101 return codec.Pass(); |
| 98 } | 102 } |
| 99 | 103 |
| 100 ScopedVpxCodec CreateVP9Codec(bool use_i444, const webrtc::DesktopSize& size) { | 104 ScopedVpxCodec CreateVP9Codec(const webrtc::DesktopSize& size, |
| 105 bool use_i444, | |
|
Jamie
2014/05/29 22:19:41
Is i444 the same as lossless colour? Since that's
Wez
2014/05/29 22:39:26
Done.
| |
| 106 bool lossless_encode) { | |
| 101 ScopedVpxCodec codec(new vpx_codec_ctx_t); | 107 ScopedVpxCodec codec(new vpx_codec_ctx_t); |
| 102 | 108 |
| 103 // Configure the encoder. | 109 // Configure the encoder. |
| 104 vpx_codec_enc_cfg_t config; | 110 vpx_codec_enc_cfg_t config; |
| 105 const vpx_codec_iface_t* algo = vpx_codec_vp9_cx(); | 111 const vpx_codec_iface_t* algo = vpx_codec_vp9_cx(); |
| 106 CHECK(algo); | 112 CHECK(algo); |
| 107 vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0); | 113 vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0); |
| 108 if (ret != VPX_CODEC_OK) | 114 if (ret != VPX_CODEC_OK) |
| 109 return ScopedVpxCodec(); | 115 return ScopedVpxCodec(); |
| 110 | 116 |
| 111 SetCommonCodecParameters(size, &config); | 117 SetCommonCodecParameters(size, &config); |
| 112 | 118 |
| 113 // Configure VP9 for I420 or I444 source frames. | 119 // Configure VP9 for I420 or I444 source frames. |
| 114 config.g_profile = use_i444 ? kVp9I444ProfileNumber : kVp9I420ProfileNumber; | 120 config.g_profile = use_i444 ? kVp9I444ProfileNumber : kVp9I420ProfileNumber; |
| 115 | 121 |
| 116 // Disable quantization entirely, putting the encoder in "lossless" mode. | 122 if (lossless_encode) { |
| 117 config.rc_min_quantizer = 0; | 123 // Disable quantization entirely, putting the encoder in "lossless" mode. |
| 118 config.rc_max_quantizer = 0; | 124 config.rc_min_quantizer = 0; |
| 125 config.rc_max_quantizer = 0; | |
| 126 } else { | |
| 127 // Lossy encode using the same settings as for VP8. | |
| 128 config.rc_min_quantizer = 20; | |
| 129 config.rc_max_quantizer = 30; | |
| 130 } | |
| 119 | 131 |
| 120 if (vpx_codec_enc_init(codec.get(), algo, &config, 0)) | 132 if (vpx_codec_enc_init(codec.get(), algo, &config, 0)) |
| 121 return ScopedVpxCodec(); | 133 return ScopedVpxCodec(); |
| 122 | 134 |
| 123 // VP9 encode doesn't yet support Realtime, so falls back to Good quality, | 135 // VP9 encode doesn't yet support Realtime, so falls back to Good quality, |
| 124 // for which 4 is the lowest CPU usage. | 136 // for which 4 is the lowest CPU usage. |
| 125 // Note that this is configured via the same parameter as for VP8. | 137 // Note that this is configured via the same parameter as for VP8. |
| 126 if (vpx_codec_control(codec.get(), VP8E_SET_CPUUSED, 4)) | 138 if (vpx_codec_control(codec.get(), VP8E_SET_CPUUSED, 4)) |
| 127 return ScopedVpxCodec(); | 139 return ScopedVpxCodec(); |
| 128 | 140 |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 195 image->stride[2] = uv_stride; | 207 image->stride[2] = uv_stride; |
| 196 | 208 |
| 197 *out_image = image.Pass(); | 209 *out_image = image.Pass(); |
| 198 *out_image_buffer = image_buffer.Pass(); | 210 *out_image_buffer = image_buffer.Pass(); |
| 199 } | 211 } |
| 200 | 212 |
| 201 } // namespace | 213 } // namespace |
| 202 | 214 |
| 203 // static | 215 // static |
| 204 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() { | 216 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() { |
| 205 return scoped_ptr<VideoEncoderVpx>( | 217 return scoped_ptr<VideoEncoderVpx>(new VideoEncoderVpx(false)); |
| 206 new VideoEncoderVpx(base::Bind(&CreateVP8Codec), | |
| 207 base::Bind(&CreateImage, false))); | |
| 208 } | 218 } |
| 209 | 219 |
| 210 // static | 220 // static |
| 211 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9I420() { | 221 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9() { |
| 212 return scoped_ptr<VideoEncoderVpx>( | 222 return scoped_ptr<VideoEncoderVpx>(new VideoEncoderVpx(true)); |
| 213 new VideoEncoderVpx(base::Bind(&CreateVP9Codec, false), | |
| 214 base::Bind(&CreateImage, false))); | |
| 215 } | |
| 216 | |
| 217 // static | |
| 218 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9I444() { | |
| 219 return scoped_ptr<VideoEncoderVpx>( | |
| 220 new VideoEncoderVpx(base::Bind(&CreateVP9Codec, true), | |
| 221 base::Bind(&CreateImage, true))); | |
| 222 } | 223 } |
| 223 | 224 |
| 224 VideoEncoderVpx::~VideoEncoderVpx() {} | 225 VideoEncoderVpx::~VideoEncoderVpx() {} |
| 225 | 226 |
| 227 void VideoEncoderVpx::SetLosslessEncode(bool want_lossless) { | |
| 228 if (use_vp9_ && (want_lossless != lossless_encode_)) { | |
| 229 lossless_encode_ = want_lossless; | |
| 230 codec_.reset(); // Force encoder re-initialization. | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 void VideoEncoderVpx::SetLosslessColor(bool want_lossless) { | |
| 235 if (use_vp9_ && (want_lossless != lossless_color_)) { | |
| 236 lossless_color_ = want_lossless; | |
| 237 codec_.reset(); // Force encoder re-initialization. | |
| 238 } | |
| 239 } | |
| 240 | |
| 226 scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( | 241 scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( |
| 227 const webrtc::DesktopFrame& frame) { | 242 const webrtc::DesktopFrame& frame) { |
| 228 DCHECK_LE(32, frame.size().width()); | 243 DCHECK_LE(32, frame.size().width()); |
| 229 DCHECK_LE(32, frame.size().height()); | 244 DCHECK_LE(32, frame.size().height()); |
| 230 | 245 |
| 231 base::TimeTicks encode_start_time = base::TimeTicks::Now(); | 246 base::TimeTicks encode_start_time = base::TimeTicks::Now(); |
| 232 | 247 |
| 233 if (!codec_ || | 248 if (!codec_ || |
| 234 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h))) { | 249 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h))) { |
| 235 bool ret = Initialize(frame.size()); | 250 bool ret = Initialize(frame.size()); |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 Rect* rect = packet->add_dirty_rects(); | 320 Rect* rect = packet->add_dirty_rects(); |
| 306 rect->set_x(r.rect().left()); | 321 rect->set_x(r.rect().left()); |
| 307 rect->set_y(r.rect().top()); | 322 rect->set_y(r.rect().top()); |
| 308 rect->set_width(r.rect().width()); | 323 rect->set_width(r.rect().width()); |
| 309 rect->set_height(r.rect().height()); | 324 rect->set_height(r.rect().height()); |
| 310 } | 325 } |
| 311 | 326 |
| 312 return packet.Pass(); | 327 return packet.Pass(); |
| 313 } | 328 } |
| 314 | 329 |
| 315 VideoEncoderVpx::VideoEncoderVpx(const CreateCodecCallback& create_codec, | 330 VideoEncoderVpx::VideoEncoderVpx(bool use_vp9) |
| 316 const CreateImageCallback& create_image) | 331 : use_vp9_(use_vp9), |
| 317 : create_codec_(create_codec), | 332 lossless_encode_(false), |
| 318 create_image_(create_image), | 333 lossless_color_(false), |
| 319 active_map_width_(0), | 334 active_map_width_(0), |
| 320 active_map_height_(0) { | 335 active_map_height_(0) { |
| 336 if (use_vp9_) { | |
| 337 // Use lossless encoding mode by default. | |
| 338 SetLosslessEncode(true); | |
| 339 | |
| 340 // Use I444 colour space, by default, if specified on the command-line. | |
| 341 if (CommandLine::ForCurrentProcess()->HasSwitch(kEnableI444SwitchName)) { | |
| 342 SetLosslessColor(true); | |
| 343 } | |
| 344 } | |
| 321 } | 345 } |
| 322 | 346 |
| 323 bool VideoEncoderVpx::Initialize(const webrtc::DesktopSize& size) { | 347 bool VideoEncoderVpx::Initialize(const webrtc::DesktopSize& size) { |
| 348 DCHECK(use_vp9_ || !lossless_color_); | |
| 349 DCHECK(use_vp9_ || !lossless_encode_); | |
| 350 | |
| 324 codec_.reset(); | 351 codec_.reset(); |
| 325 | 352 |
| 326 // (Re)Create the VPX image structure and pixel buffer. | 353 // (Re)Create the VPX image structure and pixel buffer. |
| 327 create_image_.Run(size, &image_, &image_buffer_); | 354 CreateImage(lossless_color_, size, &image_, &image_buffer_); |
| 328 | 355 |
| 329 // Initialize active map. | 356 // Initialize active map. |
| 330 active_map_width_ = (image_->w + kMacroBlockSize - 1) / kMacroBlockSize; | 357 active_map_width_ = (image_->w + kMacroBlockSize - 1) / kMacroBlockSize; |
| 331 active_map_height_ = (image_->h + kMacroBlockSize - 1) / kMacroBlockSize; | 358 active_map_height_ = (image_->h + kMacroBlockSize - 1) / kMacroBlockSize; |
| 332 active_map_.reset(new uint8[active_map_width_ * active_map_height_]); | 359 active_map_.reset(new uint8[active_map_width_ * active_map_height_]); |
| 333 | 360 |
| 334 // (Re)Initialize the codec. | 361 // (Re)Initialize the codec. |
| 335 codec_ = create_codec_.Run(size); | 362 if (use_vp9_) { |
| 363 codec_ = CreateVP9Codec(size, lossless_color_, lossless_encode_); | |
| 364 } else { | |
| 365 codec_ = CreateVP8Codec(size); | |
| 366 } | |
| 336 | 367 |
| 337 return codec_; | 368 return codec_; |
| 338 } | 369 } |
| 339 | 370 |
| 340 void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame, | 371 void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame, |
| 341 webrtc::DesktopRegion* updated_region) { | 372 webrtc::DesktopRegion* updated_region) { |
| 342 if (frame.updated_region().is_empty()) { | 373 if (frame.updated_region().is_empty()) { |
| 343 updated_region->Clear(); | 374 updated_region->Clear(); |
| 344 return; | 375 return; |
| 345 } | 376 } |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 429 uint8* map = active_map_.get() + top * active_map_width_; | 460 uint8* map = active_map_.get() + top * active_map_width_; |
| 430 for (int y = top; y <= bottom; ++y) { | 461 for (int y = top; y <= bottom; ++y) { |
| 431 for (int x = left; x <= right; ++x) | 462 for (int x = left; x <= right; ++x) |
| 432 map[x] = 1; | 463 map[x] = 1; |
| 433 map += active_map_width_; | 464 map += active_map_width_; |
| 434 } | 465 } |
| 435 } | 466 } |
| 436 } | 467 } |
| 437 | 468 |
| 438 } // namespace remoting | 469 } // namespace remoting |
| OLD | NEW |