Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(74)

Side by Side Diff: remoting/codec/video_encoder_vpx.cc

Issue 304653002: Extend VideoControl to allow clients to request lossless modes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address review comments Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « remoting/codec/video_encoder_vpx.h ('k') | remoting/codec/video_encoder_vpx_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « remoting/codec/video_encoder_vpx.h ('k') | remoting/codec/video_encoder_vpx_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698