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 |