18bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
58bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "remoting/codec/video_encoder_vpx.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
78bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "base/bind.h"
8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/command_line.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/sys_info.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "remoting/base/util.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "remoting/proto/video.pb.h"
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "third_party/libyuv/include/libyuv/convert_from_argb.h"
1490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
1590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
16d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)extern "C" {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define VPX_CODEC_DISABLE_COMPAT 1
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
248bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)namespace remoting {
258bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Name of command-line flag to enable VP9 to use I444 by default.
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const char kEnableI444SwitchName[] = "enable-i444";
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Number of bytes in an RGBx pixel.
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const int kBytesPerRgbPixel = 4;
33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Defines the dimension of a macro block. This is used to compute the active
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// map for the encoder.
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kMacroBlockSize = 16;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Magic encoder profile numbers for I420 and I444 input formats.
39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const int kVp9I420ProfileNumber = 0;
40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const int kVp9I444ProfileNumber = 1;
41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
42010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void SetCommonCodecParameters(const webrtc::DesktopSize& size,
43010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                              vpx_codec_enc_cfg_t* config) {
44010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // Use millisecond granularity time base.
45010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  config->g_timebase.num = 1;
46010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  config->g_timebase.den = 1000;
47010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
48010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // Adjust default target bit-rate to account for actual desktop size.
49010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  config->rc_target_bitrate = size.width() * size.height() *
50010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      config->rc_target_bitrate / config->g_w / config->g_h;
51010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
52010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  config->g_w = size.width();
53010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  config->g_h = size.height();
54010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  config->g_pass = VPX_RC_ONE_PASS;
55010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
56010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // Start emitting packets immediately.
57010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  config->g_lag_in_frames = 0;
58010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
59010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // Using 2 threads gives a great boost in performance for most systems with
60010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // adequate processing power. NB: Going to multiple threads on low end
61010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // windows systems can really hurt performance.
62010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // http://crbug.com/99179
63010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  config->g_threads = (base::SysInfo::NumberOfProcessors() > 2) ? 2 : 1;
64010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
65010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
668bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)ScopedVpxCodec CreateVP8Codec(const webrtc::DesktopSize& size) {
678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  ScopedVpxCodec codec(new vpx_codec_ctx_t);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
698bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Configure the encoder.
708bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  vpx_codec_enc_cfg_t config;
718bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  const vpx_codec_iface_t* algo = vpx_codec_vp8_cx();
728bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  CHECK(algo);
738bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0);
748bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (ret != VPX_CODEC_OK)
758bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return ScopedVpxCodec();
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
77010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  SetCommonCodecParameters(size, &config);
788bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
798bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Value of 2 means using the real time profile. This is basically a
808bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // redundant option since we explicitly select real time mode when doing
818bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // encoding.
828bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  config.g_profile = 2;
838bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
84010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // Clamping the quantizer constrains the worst-case quality and CPU usage.
858bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  config.rc_min_quantizer = 20;
868bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  config.rc_max_quantizer = 30;
878bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
888bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (vpx_codec_enc_init(codec.get(), algo, &config, 0))
898bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return ScopedVpxCodec();
908bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
918bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Value of 16 will have the smallest CPU load. This turns off subpixel
928bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // motion search.
938bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (vpx_codec_control(codec.get(), VP8E_SET_CPUUSED, 16))
948bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return ScopedVpxCodec();
958bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
968bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Use the lowest level of noise sensitivity so as to spend less time
978bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // on motion estimation and inter-prediction mode.
988bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (vpx_codec_control(codec.get(), VP8E_SET_NOISE_SENSITIVITY, 0))
998bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return ScopedVpxCodec();
1008bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return codec.Pass();
1028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1038bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)ScopedVpxCodec CreateVP9Codec(const webrtc::DesktopSize& size,
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                              bool lossless_color,
106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                              bool lossless_encode) {
107e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  ScopedVpxCodec codec(new vpx_codec_ctx_t);
108e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
109e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // Configure the encoder.
110e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  vpx_codec_enc_cfg_t config;
111e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  const vpx_codec_iface_t* algo = vpx_codec_vp9_cx();
112e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  CHECK(algo);
113e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0);
114e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (ret != VPX_CODEC_OK)
115e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    return ScopedVpxCodec();
116e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
117010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  SetCommonCodecParameters(size, &config);
118e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Configure VP9 for I420 or I444 source frames.
120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  config.g_profile =
121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      lossless_color ? kVp9I444ProfileNumber : kVp9I420ProfileNumber;
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (lossless_encode) {
124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // Disable quantization entirely, putting the encoder in "lossless" mode.
125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    config.rc_min_quantizer = 0;
126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    config.rc_max_quantizer = 0;
127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  } else {
128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // Lossy encode using the same settings as for VP8.
129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    config.rc_min_quantizer = 20;
130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    config.rc_max_quantizer = 30;
131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
132e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
133e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (vpx_codec_enc_init(codec.get(), algo, &config, 0))
134e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    return ScopedVpxCodec();
135e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
136e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // VP9 encode doesn't yet support Realtime, so falls back to Good quality,
137e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // for which 4 is the lowest CPU usage.
138e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // Note that this is configured via the same parameter as for VP8.
139e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (vpx_codec_control(codec.get(), VP8E_SET_CPUUSED, 4))
140e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    return ScopedVpxCodec();
141e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
142e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // Use the lowest level of noise sensitivity so as to spend less time
143e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // on motion estimation and inter-prediction mode.
144e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  // Note that this is configured via the same parameter as for VP8.
145e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (vpx_codec_control(codec.get(), VP8E_SET_NOISE_SENSITIVITY, 0))
146e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    return ScopedVpxCodec();
147e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
148e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  return codec.Pass();
149e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch}
150e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void CreateImage(bool use_i444,
152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 const webrtc::DesktopSize& size,
153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 scoped_ptr<vpx_image_t>* out_image,
154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 scoped_ptr<uint8[]>* out_image_buffer) {
155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!size.is_empty());
156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  scoped_ptr<vpx_image_t> image(new vpx_image_t());
158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  memset(image.get(), 0, sizeof(vpx_image_t));
159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // libvpx seems to require both to be assigned.
161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  image->d_w = size.width();
162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  image->w = size.width();
163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  image->d_h = size.height();
164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  image->h = size.height();
165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // libvpx should derive chroma shifts from|fmt| but currently has a bug:
167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // https://code.google.com/p/webm/issues/detail?id=627
168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (use_i444) {
169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    image->fmt = VPX_IMG_FMT_I444;
170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    image->x_chroma_shift = 0;
171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    image->y_chroma_shift = 0;
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  } else { // I420
173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    image->fmt = VPX_IMG_FMT_YV12;
174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    image->x_chroma_shift = 1;
175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    image->y_chroma_shift = 1;
176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // libyuv's fast-path requires 16-byte aligned pointers and strides, so pad
179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // the Y, U and V planes' strides to multiples of 16 bytes.
180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const int y_stride = ((image->w - 1) & ~15) + 16;
181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const int uv_unaligned_stride = y_stride >> image->x_chroma_shift;
182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const int uv_stride = ((uv_unaligned_stride - 1) & ~15) + 16;
183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // libvpx accesses the source image in macro blocks, and will over-read
185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // if the image is not padded out to the next macroblock: crbug.com/119633.
186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Pad the Y, U and V planes' height out to compensate.
187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Assuming macroblocks are 16x16, aligning the planes' strides above also
188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // macroblock aligned them.
189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK_EQ(16, kMacroBlockSize);
190cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const int y_rows = ((image->h - 1) & ~(kMacroBlockSize-1)) + kMacroBlockSize;
191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const int uv_rows = y_rows >> image->y_chroma_shift;
192cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
193cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Allocate a YUV buffer large enough for the aligned data & padding.
194cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const int buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows;
195cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  scoped_ptr<uint8[]> image_buffer(new uint8[buffer_size]);
196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Reset image value to 128 so we just need to fill in the y plane.
198cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  memset(image_buffer.get(), 128, buffer_size);
199cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
200cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Fill in the information for |image_|.
201cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  unsigned char* uchar_buffer =
202cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      reinterpret_cast<unsigned char*>(image_buffer.get());
203cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  image->planes[0] = uchar_buffer;
204cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  image->planes[1] = image->planes[0] + y_stride * y_rows;
205cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  image->planes[2] = image->planes[1] + uv_stride * uv_rows;
206cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  image->stride[0] = y_stride;
207cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  image->stride[1] = uv_stride;
208cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  image->stride[2] = uv_stride;
209cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  *out_image = image.Pass();
211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  *out_image_buffer = image_buffer.Pass();
212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
214cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} // namespace
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2168bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// static
2178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() {
218cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return scoped_ptr<VideoEncoderVpx>(new VideoEncoderVpx(false));
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
221e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch// static
222e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdochscoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9() {
223cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  return scoped_ptr<VideoEncoderVpx>(new VideoEncoderVpx(true));
224e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch}
225e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch
2268bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)VideoEncoderVpx::~VideoEncoderVpx() {}
2278bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
228cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void VideoEncoderVpx::SetLosslessEncode(bool want_lossless) {
229cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (use_vp9_ && (want_lossless != lossless_encode_)) {
230cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    lossless_encode_ = want_lossless;
231cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    codec_.reset(); // Force encoder re-initialization.
232cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
233cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
235cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void VideoEncoderVpx::SetLosslessColor(bool want_lossless) {
236cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (use_vp9_ && (want_lossless != lossless_color_)) {
237cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    lossless_color_ = want_lossless;
238cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    codec_.reset(); // Force encoder re-initialization.
239cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
240cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
241cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
2428bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)scoped_ptr<VideoPacket> VideoEncoderVpx::Encode(
2438bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const webrtc::DesktopFrame& frame) {
2448bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  DCHECK_LE(32, frame.size().width());
2458bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  DCHECK_LE(32, frame.size().height());
2468bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
247010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  base::TimeTicks encode_start_time = base::TimeTicks::Now();
2488bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2498bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (!codec_ ||
2508bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h))) {
2518bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    bool ret = Initialize(frame.size());
2528bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    // TODO(hclam): Handle error better.
2538bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    CHECK(ret) << "Initialization of encoder failed";
254010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
255010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    // Set now as the base for timestamp calculation.
256010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    timestamp_base_ = encode_start_time;
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2588bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2598bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Convert the updated capture data ready for encode.
2608bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  webrtc::DesktopRegion updated_region;
2618bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  PrepareImage(frame, &updated_region);
2628bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2638bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Update active map based on updated region.
2648bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  PrepareActiveMap(updated_region);
2658bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2668bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Apply active map to the encoder.
2678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  vpx_active_map_t act_map;
2688bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  act_map.rows = active_map_height_;
2698bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  act_map.cols = active_map_width_;
2708bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  act_map.active_map = active_map_.get();
2718bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) {
2728bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    LOG(ERROR) << "Unable to apply active map";
2738bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
2748bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2758bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Do the actual encoding.
276010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  int timestamp = (encode_start_time - timestamp_base_).InMilliseconds();
277010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  vpx_codec_err_t ret = vpx_codec_encode(
278010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME);
2798bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  DCHECK_EQ(ret, VPX_CODEC_OK)
2808bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n"
2818bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      << "Details: " << vpx_codec_error(codec_.get()) << "\n"
2828bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      << vpx_codec_error_detail(codec_.get());
2838bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2848bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Read the encoded data.
2858bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  vpx_codec_iter_t iter = NULL;
2868bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  bool got_data = false;
2878bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2888bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // TODO(hclam): Make sure we get exactly one frame from the packet.
2898bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // TODO(hclam): We should provide the output buffer to avoid one copy.
2908bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  scoped_ptr<VideoPacket> packet(new VideoPacket());
2918bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2928bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  while (!got_data) {
2938bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const vpx_codec_cx_pkt_t* vpx_packet =
2948bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        vpx_codec_get_cx_data(codec_.get(), &iter);
2958bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    if (!vpx_packet)
2968bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      continue;
2978bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
2988bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    switch (vpx_packet->kind) {
2998bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      case VPX_CODEC_CX_FRAME_PKT:
3008bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        got_data = true;
3018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        packet->set_data(vpx_packet->data.frame.buf, vpx_packet->data.frame.sz);
3028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        break;
3038bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      default:
3048bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        break;
3058bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    }
3068bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
3078bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
3088bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Construct the VideoPacket message.
3098bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8);
3108bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  packet->mutable_format()->set_screen_width(frame.size().width());
3118bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  packet->mutable_format()->set_screen_height(frame.size().height());
3128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  packet->set_capture_time_ms(frame.capture_time_ms());
3138bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  packet->set_encode_time_ms(
314010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      (base::TimeTicks::Now() - encode_start_time).InMillisecondsRoundedUp());
3158bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (!frame.dpi().is_zero()) {
3168bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    packet->mutable_format()->set_x_dpi(frame.dpi().x());
3178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    packet->mutable_format()->set_y_dpi(frame.dpi().y());
3188bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
3198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd();
3208bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)       r.Advance()) {
3218bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    Rect* rect = packet->add_dirty_rects();
3228bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    rect->set_x(r.rect().left());
3238bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    rect->set_y(r.rect().top());
3248bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    rect->set_width(r.rect().width());
3258bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    rect->set_height(r.rect().height());
3268bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
3278bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
3288bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return packet.Pass();
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
331cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)VideoEncoderVpx::VideoEncoderVpx(bool use_vp9)
332cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    : use_vp9_(use_vp9),
333cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      lossless_encode_(false),
334cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      lossless_color_(false),
3358bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      active_map_width_(0),
336010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      active_map_height_(0) {
337cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (use_vp9_) {
338cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // Use lossless encoding mode by default.
339cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    SetLosslessEncode(true);
340cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
341cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // Use I444 colour space, by default, if specified on the command-line.
342cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if (CommandLine::ForCurrentProcess()->HasSwitch(kEnableI444SwitchName)) {
343cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      SetLosslessColor(true);
344cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    }
345cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
3468bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
3478bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
3488bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)bool VideoEncoderVpx::Initialize(const webrtc::DesktopSize& size) {
349cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(use_vp9_ || !lossless_color_);
350cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(use_vp9_ || !lossless_encode_);
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
352cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  codec_.reset();
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
354cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // (Re)Create the VPX image structure and pixel buffer.
355cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  CreateImage(lossless_color_, size, &image_, &image_buffer_);
3568bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Initialize active map.
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  active_map_width_ = (image_->w + kMacroBlockSize - 1) / kMacroBlockSize;
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  active_map_height_ = (image_->h + kMacroBlockSize - 1) / kMacroBlockSize;
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  active_map_.reset(new uint8[active_map_width_ * active_map_height_]);
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
362cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // (Re)Initialize the codec.
363cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (use_vp9_) {
364cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    codec_ = CreateVP9Codec(size, lossless_color_, lossless_encode_);
365cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  } else {
366cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    codec_ = CreateVP8Codec(size);
367cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3698bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return codec_;
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3728bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame,
373d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                   webrtc::DesktopRegion* updated_region) {
374d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (frame.updated_region().is_empty()) {
375d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    updated_region->Clear();
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Align the region to macroblocks, to avoid encoding artefacts.
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This also ensures that all rectangles have even-aligned top-left, which
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // is required for ConvertRGBToYUVWithRect() to work.
382d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  std::vector<webrtc::DesktopRect> aligned_rects;
383d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  for (webrtc::DesktopRegion::Iterator r(frame.updated_region());
38490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)       !r.IsAtEnd(); r.Advance()) {
38590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const webrtc::DesktopRect& rect = r.rect();
386d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    aligned_rects.push_back(AlignRect(webrtc::DesktopRect::MakeLTRB(
387d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        rect.left(), rect.top(), rect.right(), rect.bottom())));
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!aligned_rects.empty());
390d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  updated_region->Clear();
391d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  updated_region->AddRects(&aligned_rects[0], aligned_rects.size());
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Clip back to the screen dimensions, in case they're not macroblock aligned.
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The conversion routines don't require even width & height, so this is safe
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // even if the source dimensions are not even.
396d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  updated_region->IntersectWith(
397d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      webrtc::DesktopRect::MakeWH(image_->w, image_->h));
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Convert the updated region to YUV ready for encoding.
400d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const uint8* rgb_data = frame.data();
401d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const int rgb_stride = frame.stride();
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int y_stride = image_->stride[0];
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(image_->stride[1], image_->stride[2]);
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int uv_stride = image_->stride[1];
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint8* y_data = image_->planes[0];
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint8* u_data = image_->planes[1];
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint8* v_data = image_->planes[2];
408cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
409cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  switch (image_->fmt) {
410cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case VPX_IMG_FMT_I444:
411cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
412cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)           r.Advance()) {
413cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        const webrtc::DesktopRect& rect = r.rect();
414cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        int rgb_offset = rgb_stride * rect.top() +
415cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                         rect.left() * kBytesPerRgbPixel;
416cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        int yuv_offset = uv_stride * rect.top() + rect.left();
417cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        libyuv::ARGBToI444(rgb_data + rgb_offset, rgb_stride,
418cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           y_data + yuv_offset, y_stride,
419cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           u_data + yuv_offset, uv_stride,
420cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           v_data + yuv_offset, uv_stride,
421cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           rect.width(), rect.height());
422cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      }
423cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      break;
424cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    case VPX_IMG_FMT_YV12:
425cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
426cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)           r.Advance()) {
427cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        const webrtc::DesktopRect& rect = r.rect();
428cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        int rgb_offset = rgb_stride * rect.top() +
429cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                         rect.left() * kBytesPerRgbPixel;
430cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        int y_offset = y_stride * rect.top() + rect.left();
431cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        int uv_offset = uv_stride * rect.top() / 2 + rect.left() / 2;
432cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        libyuv::ARGBToI420(rgb_data + rgb_offset, rgb_stride,
433cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           y_data + y_offset, y_stride,
434cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           u_data + uv_offset, uv_stride,
435cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           v_data + uv_offset, uv_stride,
436cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           rect.width(), rect.height());
437cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      }
438cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      break;
439cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    default:
440cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      NOTREACHED();
441cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      break;
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4458bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void VideoEncoderVpx::PrepareActiveMap(
446d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const webrtc::DesktopRegion& updated_region) {
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Clear active map first.
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memset(active_map_.get(), 0, active_map_width_ * active_map_height_);
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Mark updated areas active.
451d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd();
452d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)       r.Advance()) {
453d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const webrtc::DesktopRect& rect = r.rect();
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int left = rect.left() / kMacroBlockSize;
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int right = (rect.right() - 1) / kMacroBlockSize;
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int top = rect.top() / kMacroBlockSize;
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int bottom = (rect.bottom() - 1) / kMacroBlockSize;
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LT(right, active_map_width_);
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LT(bottom, active_map_height_);
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uint8* map = active_map_.get() + top * active_map_width_;
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int y = top; y <= bottom; ++y) {
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (int x = left; x <= right; ++x)
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        map[x] = 1;
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      map += active_map_width_;
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace remoting
471