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 lossless_color, |
| 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 = |
| 121 lossless_color ? kVp9I444ProfileNumber : kVp9I420ProfileNumber; |
115 | 122 |
116 // Disable quantization entirely, putting the encoder in "lossless" mode. | 123 if (lossless_encode) { |
117 config.rc_min_quantizer = 0; | 124 // Disable quantization entirely, putting the encoder in "lossless" mode. |
118 config.rc_max_quantizer = 0; | 125 config.rc_min_quantizer = 0; |
| 126 config.rc_max_quantizer = 0; |
| 127 } else { |
| 128 // Lossy encode using the same settings as for VP8. |
| 129 config.rc_min_quantizer = 20; |
| 130 config.rc_max_quantizer = 30; |
| 131 } |
119 | 132 |
120 if (vpx_codec_enc_init(codec.get(), algo, &config, 0)) | 133 if (vpx_codec_enc_init(codec.get(), algo, &config, 0)) |
121 return ScopedVpxCodec(); | 134 return ScopedVpxCodec(); |
122 | 135 |
123 // VP9 encode doesn't yet support Realtime, so falls back to Good quality, | 136 // VP9 encode doesn't yet support Realtime, so falls back to Good quality, |
124 // for which 4 is the lowest CPU usage. | 137 // for which 4 is the lowest CPU usage. |
125 // Note that this is configured via the same parameter as for VP8. | 138 // Note that this is configured via the same parameter as for VP8. |
126 if (vpx_codec_control(codec.get(), VP8E_SET_CPUUSED, 4)) | 139 if (vpx_codec_control(codec.get(), VP8E_SET_CPUUSED, 4)) |
127 return ScopedVpxCodec(); | 140 return ScopedVpxCodec(); |
128 | 141 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
195 image->stride[2] = uv_stride; | 208 image->stride[2] = uv_stride; |
196 | 209 |
197 *out_image = image.Pass(); | 210 *out_image = image.Pass(); |
198 *out_image_buffer = image_buffer.Pass(); | 211 *out_image_buffer = image_buffer.Pass(); |
199 } | 212 } |
200 | 213 |
201 } // namespace | 214 } // namespace |
202 | 215 |
203 // static | 216 // static |
204 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() { | 217 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() { |
205 return scoped_ptr<VideoEncoderVpx>( | 218 return scoped_ptr<VideoEncoderVpx>(new VideoEncoderVpx(false)); |
206 new VideoEncoderVpx(base::Bind(&CreateVP8Codec), | |
207 base::Bind(&CreateImage, false))); | |
208 } | 219 } |
209 | 220 |
210 // static | 221 // static |
211 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9I420() { | 222 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9() { |
212 return scoped_ptr<VideoEncoderVpx>( | 223 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 } | 224 } |
223 | 225 |
224 VideoEncoderVpx::~VideoEncoderVpx() {} | 226 VideoEncoderVpx::~VideoEncoderVpx() {} |
225 | 227 |
| 228 void VideoEncoderVpx::SetLosslessEncode(bool want_lossless) { |
| 229 if (use_vp9_ && (want_lossless != lossless_encode_)) { |
| 230 lossless_encode_ = want_lossless; |
| 231 codec_.reset(); // Force encoder re-initialization. |
| 232 } |
| 233 } |
| 234 |
| 235 void VideoEncoderVpx::SetLosslessColor(bool want_lossless) { |
| 236 if (use_vp9_ && (want_lossless != lossless_color_)) { |
| 237 lossless_color_ = want_lossless; |
| 238 codec_.reset(); // Force encoder re-initialization. |
| 239 } |
| 240 } |
| 241 |
226 scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( | 242 scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( |
227 const webrtc::DesktopFrame& frame) { | 243 const webrtc::DesktopFrame& frame) { |
228 DCHECK_LE(32, frame.size().width()); | 244 DCHECK_LE(32, frame.size().width()); |
229 DCHECK_LE(32, frame.size().height()); | 245 DCHECK_LE(32, frame.size().height()); |
230 | 246 |
231 base::TimeTicks encode_start_time = base::TimeTicks::Now(); | 247 base::TimeTicks encode_start_time = base::TimeTicks::Now(); |
232 | 248 |
233 if (!codec_ || | 249 if (!codec_ || |
234 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h))) { | 250 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h))) { |
235 bool ret = Initialize(frame.size()); | 251 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(); | 321 Rect* rect = packet->add_dirty_rects(); |
306 rect->set_x(r.rect().left()); | 322 rect->set_x(r.rect().left()); |
307 rect->set_y(r.rect().top()); | 323 rect->set_y(r.rect().top()); |
308 rect->set_width(r.rect().width()); | 324 rect->set_width(r.rect().width()); |
309 rect->set_height(r.rect().height()); | 325 rect->set_height(r.rect().height()); |
310 } | 326 } |
311 | 327 |
312 return packet.Pass(); | 328 return packet.Pass(); |
313 } | 329 } |
314 | 330 |
315 VideoEncoderVpx::VideoEncoderVpx(const CreateCodecCallback& create_codec, | 331 VideoEncoderVpx::VideoEncoderVpx(bool use_vp9) |
316 const CreateImageCallback& create_image) | 332 : use_vp9_(use_vp9), |
317 : create_codec_(create_codec), | 333 lossless_encode_(false), |
318 create_image_(create_image), | 334 lossless_color_(false), |
319 active_map_width_(0), | 335 active_map_width_(0), |
320 active_map_height_(0) { | 336 active_map_height_(0) { |
| 337 if (use_vp9_) { |
| 338 // Use lossless encoding mode by default. |
| 339 SetLosslessEncode(true); |
| 340 |
| 341 // Use I444 colour space, by default, if specified on the command-line. |
| 342 if (CommandLine::ForCurrentProcess()->HasSwitch(kEnableI444SwitchName)) { |
| 343 SetLosslessColor(true); |
| 344 } |
| 345 } |
321 } | 346 } |
322 | 347 |
323 bool VideoEncoderVpx::Initialize(const webrtc::DesktopSize& size) { | 348 bool VideoEncoderVpx::Initialize(const webrtc::DesktopSize& size) { |
| 349 DCHECK(use_vp9_ || !lossless_color_); |
| 350 DCHECK(use_vp9_ || !lossless_encode_); |
| 351 |
324 codec_.reset(); | 352 codec_.reset(); |
325 | 353 |
326 // (Re)Create the VPX image structure and pixel buffer. | 354 // (Re)Create the VPX image structure and pixel buffer. |
327 create_image_.Run(size, &image_, &image_buffer_); | 355 CreateImage(lossless_color_, size, &image_, &image_buffer_); |
328 | 356 |
329 // Initialize active map. | 357 // Initialize active map. |
330 active_map_width_ = (image_->w + kMacroBlockSize - 1) / kMacroBlockSize; | 358 active_map_width_ = (image_->w + kMacroBlockSize - 1) / kMacroBlockSize; |
331 active_map_height_ = (image_->h + kMacroBlockSize - 1) / kMacroBlockSize; | 359 active_map_height_ = (image_->h + kMacroBlockSize - 1) / kMacroBlockSize; |
332 active_map_.reset(new uint8[active_map_width_ * active_map_height_]); | 360 active_map_.reset(new uint8[active_map_width_ * active_map_height_]); |
333 | 361 |
334 // (Re)Initialize the codec. | 362 // (Re)Initialize the codec. |
335 codec_ = create_codec_.Run(size); | 363 if (use_vp9_) { |
| 364 codec_ = CreateVP9Codec(size, lossless_color_, lossless_encode_); |
| 365 } else { |
| 366 codec_ = CreateVP8Codec(size); |
| 367 } |
336 | 368 |
337 return codec_; | 369 return codec_; |
338 } | 370 } |
339 | 371 |
340 void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame, | 372 void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame, |
341 webrtc::DesktopRegion* updated_region) { | 373 webrtc::DesktopRegion* updated_region) { |
342 if (frame.updated_region().is_empty()) { | 374 if (frame.updated_region().is_empty()) { |
343 updated_region->Clear(); | 375 updated_region->Clear(); |
344 return; | 376 return; |
345 } | 377 } |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
429 uint8* map = active_map_.get() + top * active_map_width_; | 461 uint8* map = active_map_.get() + top * active_map_width_; |
430 for (int y = top; y <= bottom; ++y) { | 462 for (int y = top; y <= bottom; ++y) { |
431 for (int x = left; x <= right; ++x) | 463 for (int x = left; x <= right; ++x) |
432 map[x] = 1; | 464 map[x] = 1; |
433 map += active_map_width_; | 465 map += active_map_width_; |
434 } | 466 } |
435 } | 467 } |
436 } | 468 } |
437 | 469 |
438 } // namespace remoting | 470 } // namespace remoting |
OLD | NEW |