webrtc_video_encoder_vpx.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "remoting/codec/webrtc_video_encoder_vpx.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/callback.h"
  9. #include "base/cxx17_backports.h"
  10. #include "base/logging.h"
  11. #include "base/memory/ptr_util.h"
  12. #include "base/system/sys_info.h"
  13. #include "build/build_config.h"
  14. #include "build/chromeos_buildflags.h"
  15. #include "remoting/base/cpu_utils.h"
  16. #include "remoting/base/util.h"
  17. #include "remoting/proto/video.pb.h"
  18. #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
  19. #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h"
  20. #include "third_party/libyuv/include/libyuv/convert_from_argb.h"
  21. #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
  22. #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
  23. #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
  24. namespace remoting {
  25. namespace {
  26. // Number of bytes in an RGBx pixel.
  27. constexpr int kBytesPerRgbPixel = 4;
  28. // Magic encoder profile numbers for I420 and I444 input formats.
  29. constexpr int kVp9I420ProfileNumber = 0;
  30. constexpr int kVp9I444ProfileNumber = 1;
  31. // Magic encoder constants for adaptive quantization strategy.
  32. constexpr int kVp9AqModeNone = 0;
  33. constexpr int kVp9AqModeCyclicRefresh = 3;
  34. constexpr int kDefaultTargetBitrateKbps = 1000;
  35. // Minimum target bitrate per megapixel. The value is chosen experimentally such
  36. // that when screen is not changing the codec converges to the target quantizer
  37. // above in less than 10 frames.
  38. // TODO(zijiehe): This value is for VP8 only; reconsider the value for VP9.
  39. constexpr int kVp8MinimumTargetBitrateKbpsPerMegapixel = 2500;
  40. // Default values for the encoder speed of the supported codecs.
  41. constexpr int kVp9LosslessEncodeSpeed = 5;
  42. constexpr int kVp9DefaultEncoderSpeed = 6;
  43. constexpr int kVp9MaxEncoderSpeed = 9;
  44. void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config,
  45. const webrtc::DesktopSize& size) {
  46. // Use millisecond granularity time base.
  47. config->g_timebase.num = 1;
  48. config->g_timebase.den = base::Time::kMicrosecondsPerSecond;
  49. config->g_w = size.width();
  50. config->g_h = size.height();
  51. config->g_pass = VPX_RC_ONE_PASS;
  52. // TODO(joedow): Determine whether it is possible to support portrait mode.
  53. // VP9 only supports breaking an image up into columns for parallel encoding,
  54. // this means that a display in portrait mode cannot be broken up into as many
  55. // columns as a display in landscape mode can so performance will be degraded.
  56. config->g_threads = WebrtcVideoEncoder::GetEncoderThreadCount(config->g_w);
  57. // Start emitting packets immediately.
  58. config->g_lag_in_frames = 0;
  59. // Since the transport layer is reliable, keyframes should not be necessary.
  60. // However, due to crbug.com/440223, decoding fails after 30,000 non-key
  61. // frames, so take the hit of an "unnecessary" key-frame every 10,000 frames.
  62. config->kf_min_dist = 10000;
  63. config->kf_max_dist = 10000;
  64. // Do not drop any frames at encoder.
  65. config->rc_dropframe_thresh = 0;
  66. // We do not want variations in bandwidth.
  67. config->rc_end_usage = VPX_CBR;
  68. config->rc_undershoot_pct = 100;
  69. config->rc_overshoot_pct = 15;
  70. }
  71. void SetVp8CodecParameters(vpx_codec_enc_cfg_t* config,
  72. const webrtc::DesktopSize& size) {
  73. SetCommonCodecParameters(config, size);
  74. #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS_LACROS)
  75. // On Linux, using too many threads for VP8 encoding has been linked to high
  76. // CPU usage on machines that are under stress. See http://crbug.com/1151148.
  77. // 5/3/2022 update: Perf testing has shown that doubling the number of threads
  78. // on machines with a large number of cores will improve performance at higher
  79. // desktop resolutions. Doubling the number of threads leads to ~30% increase
  80. // in framerate at 4K. This could be increased further however we don't want
  81. // to risk reintroducing the problem from the bug above so take the safe gains
  82. // and leave plenty of cores for the non-remoting workload.
  83. uint threshold = config->g_threads >= 16 ? 4U : 2U;
  84. config->g_threads = std::min(config->g_threads, threshold);
  85. #endif // BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS_LACROS)
  86. // Value of 2 means using the real time profile. This is basically a
  87. // redundant option since we explicitly select real time mode when doing
  88. // encoding.
  89. config->g_profile = 2;
  90. }
  91. void SetVp9CodecParameters(vpx_codec_enc_cfg_t* config,
  92. const webrtc::DesktopSize& size,
  93. bool lossless_color,
  94. bool lossless_encode) {
  95. SetCommonCodecParameters(config, size);
  96. // Configure VP9 for I420 or I444 source frames.
  97. config->g_profile =
  98. lossless_color ? kVp9I444ProfileNumber : kVp9I420ProfileNumber;
  99. if (lossless_encode) {
  100. // Disable quantization entirely, putting the encoder in "lossless" mode.
  101. config->rc_min_quantizer = 0;
  102. config->rc_max_quantizer = 0;
  103. config->rc_end_usage = VPX_VBR;
  104. } else {
  105. config->rc_end_usage = VPX_CBR;
  106. // In the absence of a good bandwidth estimator set the target bitrate to a
  107. // conservative default.
  108. config->rc_target_bitrate = 500;
  109. }
  110. }
  111. void SetVp8CodecOptions(vpx_codec_ctx_t* codec) {
  112. // CPUUSED of 16 will have the smallest CPU load. This turns off sub-pixel
  113. // motion search.
  114. vpx_codec_err_t ret = vpx_codec_control(codec, VP8E_SET_CPUUSED, 16);
  115. DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set CPUUSED";
  116. // Use the lowest level of noise sensitivity so as to spend less time
  117. // on motion estimation and inter-prediction mode.
  118. ret = vpx_codec_control(codec, VP8E_SET_NOISE_SENSITIVITY, 0);
  119. DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set noise sensitivity";
  120. }
  121. void SetVp9CodecOptions(vpx_codec_ctx_t* codec,
  122. bool lossless_encode,
  123. int encoder_speed) {
  124. // Note that this knob uses the same parameter name as VP8.
  125. vpx_codec_err_t ret =
  126. vpx_codec_control(codec, VP8E_SET_CPUUSED, encoder_speed);
  127. DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set CPUUSED";
  128. // Turn on row-based multi-threading if more than one thread is available.
  129. if (codec->config.enc->g_threads > 1) {
  130. vpx_codec_control(codec, VP9E_SET_ROW_MT, 1);
  131. }
  132. // The param for this knob is a log2 value so 0 is reasonable here.
  133. vpx_codec_control(codec, VP9E_SET_TILE_COLUMNS,
  134. static_cast<int>(std::log2(codec->config.enc->g_threads)));
  135. // Use the lowest level of noise sensitivity so as to spend less time
  136. // on motion estimation and inter-prediction mode.
  137. ret = vpx_codec_control(codec, VP9E_SET_NOISE_SENSITIVITY, 0);
  138. DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set noise sensitivity";
  139. // Configure the codec to tune it for screen media.
  140. ret = vpx_codec_control(codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN);
  141. DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set screen content mode";
  142. // Set cyclic refresh (aka "top-off") only for lossy encoding.
  143. int aq_mode = lossless_encode ? kVp9AqModeNone : kVp9AqModeCyclicRefresh;
  144. ret = vpx_codec_control(codec, VP9E_SET_AQ_MODE, aq_mode);
  145. DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set aq mode";
  146. }
  147. } // namespace
  148. // static
  149. std::unique_ptr<WebrtcVideoEncoder> WebrtcVideoEncoderVpx::CreateForVP8() {
  150. LOG(WARNING) << "VP8 video encoder is created.";
  151. return base::WrapUnique(new WebrtcVideoEncoderVpx(false));
  152. }
  153. // static
  154. std::unique_ptr<WebrtcVideoEncoder> WebrtcVideoEncoderVpx::CreateForVP9() {
  155. LOG(WARNING) << "VP9 video encoder is created.";
  156. return base::WrapUnique(new WebrtcVideoEncoderVpx(true));
  157. }
  158. WebrtcVideoEncoderVpx::~WebrtcVideoEncoderVpx() = default;
  159. void WebrtcVideoEncoderVpx::SetTickClockForTests(
  160. const base::TickClock* tick_clock) {
  161. clock_ = tick_clock;
  162. }
  163. void WebrtcVideoEncoderVpx::SetLosslessEncode(bool want_lossless) {
  164. if (!use_vp9_)
  165. return;
  166. if (want_lossless != lossless_encode_) {
  167. lossless_encode_ = want_lossless;
  168. SetEncoderSpeed(lossless_encode_ ? kVp9LosslessEncodeSpeed
  169. : kVp9DefaultEncoderSpeed);
  170. if (codec_)
  171. Configure(webrtc::DesktopSize(codec_->config.enc->g_w,
  172. codec_->config.enc->g_h));
  173. }
  174. }
  175. void WebrtcVideoEncoderVpx::SetLosslessColor(bool want_lossless) {
  176. if (!use_vp9_)
  177. return;
  178. if (want_lossless != lossless_color_) {
  179. lossless_color_ = want_lossless;
  180. // TODO(wez): Switch to ConfigureCodec() path once libvpx supports it.
  181. // See https://code.google.com/p/webm/issues/detail?id=913.
  182. // if (codec_)
  183. // Configure(webrtc::DesktopSize(codec_->config.enc->g_w,
  184. // codec_->config.enc->g_h));
  185. codec_.reset();
  186. }
  187. }
  188. void WebrtcVideoEncoderVpx::SetEncoderSpeed(int encoder_speed) {
  189. if (!use_vp9_)
  190. return;
  191. vp9_encoder_speed_ = base::clamp<int>(encoder_speed, kVp9LosslessEncodeSpeed,
  192. kVp9MaxEncoderSpeed);
  193. }
  194. void WebrtcVideoEncoderVpx::Encode(std::unique_ptr<webrtc::DesktopFrame> frame,
  195. const FrameParams& params,
  196. EncodeCallback done) {
  197. // TODO(zijiehe): Replace "if (frame)" with "DCHECK(frame)".
  198. if (frame) {
  199. bitrate_filter_.SetFrameSize(frame->size().width(), frame->size().height());
  200. }
  201. webrtc::DesktopSize previous_frame_size =
  202. image_ ? webrtc::DesktopSize(image_->d_w, image_->d_h)
  203. : webrtc::DesktopSize();
  204. webrtc::DesktopSize frame_size = frame ? frame->size() : previous_frame_size;
  205. // Don't need to send anything until we get the first non-null frame.
  206. if (frame_size.is_empty()) {
  207. std::move(done).Run(EncodeResult::SUCCEEDED, nullptr);
  208. return;
  209. }
  210. DCHECK_GE(frame_size.width(), 32);
  211. DCHECK_GE(frame_size.height(), 32);
  212. // Create or reconfigure the codec to match the size of |frame|.
  213. if (!codec_ || !frame_size.equals(previous_frame_size)) {
  214. Configure(frame_size);
  215. }
  216. UpdateConfig(params);
  217. webrtc::DesktopRegion updated_region;
  218. // Convert the updated capture data ready for encode.
  219. PrepareImage(frame.get(), &updated_region);
  220. vpx_active_map_t act_map;
  221. if (use_active_map_) {
  222. if (params.clear_active_map)
  223. active_map_.Clear();
  224. if (params.key_frame)
  225. updated_region.SetRect(webrtc::DesktopRect::MakeSize(frame_size));
  226. active_map_.Update(updated_region);
  227. act_map.rows = active_map_.height();
  228. act_map.cols = active_map_.width();
  229. act_map.active_map = active_map_.data();
  230. if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) {
  231. LOG(ERROR) << "Unable to apply active map";
  232. }
  233. }
  234. vpx_codec_err_t ret = vpx_codec_encode(
  235. codec_.get(), image_.get(), 0, params.duration.InMicroseconds(),
  236. (params.key_frame) ? VPX_EFLAG_FORCE_KF : 0, VPX_DL_REALTIME);
  237. if (ret != VPX_CODEC_OK) {
  238. const char* error_detail = vpx_codec_error_detail(codec_.get());
  239. LOG(ERROR) << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n "
  240. << "Details: " << vpx_codec_error(codec_.get()) << "\n "
  241. << (error_detail ? error_detail : "No error details");
  242. // TODO(zijiehe): A more exact error type is preferred.
  243. std::move(done).Run(EncodeResult::UNKNOWN_ERROR, nullptr);
  244. return;
  245. }
  246. if (use_active_map_) {
  247. // VP8 doesn't return an active map so we assume it hasn't changed.
  248. if (use_vp9_ && !lossless_encode_) {
  249. ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map);
  250. DCHECK_EQ(ret, VPX_CODEC_OK)
  251. << "Failed to fetch active map: " << vpx_codec_err_to_string(ret)
  252. << "\n";
  253. }
  254. }
  255. // Read the encoded data.
  256. vpx_codec_iter_t iter = nullptr;
  257. bool got_data = false;
  258. auto encoded_frame = std::make_unique<EncodedFrame>();
  259. encoded_frame->dimensions = frame_size;
  260. if (use_vp9_) {
  261. encoded_frame->codec = webrtc::kVideoCodecVP9;
  262. } else {
  263. encoded_frame->codec = webrtc::kVideoCodecVP8;
  264. }
  265. while (!got_data) {
  266. const vpx_codec_cx_pkt_t* vpx_packet =
  267. vpx_codec_get_cx_data(codec_.get(), &iter);
  268. if (!vpx_packet)
  269. continue;
  270. switch (vpx_packet->kind) {
  271. case VPX_CODEC_CX_FRAME_PKT: {
  272. got_data = true;
  273. encoded_frame->data = webrtc::EncodedImageBuffer::Create(
  274. reinterpret_cast<const uint8_t*>(vpx_packet->data.frame.buf),
  275. vpx_packet->data.frame.sz);
  276. encoded_frame->key_frame =
  277. vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY;
  278. CHECK_EQ(vpx_codec_control(codec_.get(), VP8E_GET_LAST_QUANTIZER_64,
  279. &(encoded_frame->quantizer)),
  280. VPX_CODEC_OK);
  281. break;
  282. }
  283. default:
  284. break;
  285. }
  286. }
  287. std::move(done).Run(EncodeResult::SUCCEEDED, std::move(encoded_frame));
  288. }
  289. WebrtcVideoEncoderVpx::WebrtcVideoEncoderVpx(bool use_vp9)
  290. : use_vp9_(use_vp9),
  291. image_(nullptr, vpx_img_free),
  292. clock_(base::DefaultTickClock::GetInstance()),
  293. bitrate_filter_(kVp8MinimumTargetBitrateKbpsPerMegapixel) {
  294. // Indicates config is still uninitialized.
  295. config_.g_timebase.den = 0;
  296. if (use_vp9_) {
  297. SetEncoderSpeed(kVp9DefaultEncoderSpeed);
  298. }
  299. }
  300. void WebrtcVideoEncoderVpx::Configure(const webrtc::DesktopSize& size) {
  301. DCHECK(use_vp9_ || !lossless_color_);
  302. DCHECK(use_vp9_ || !lossless_encode_);
  303. if (use_vp9_) {
  304. VLOG(0) << "Configuring VP9 encoder with lossless-color="
  305. << (lossless_color_ ? "true" : "false")
  306. << ", lossless-encode=" << (lossless_encode_ ? "true" : "false")
  307. << ".";
  308. }
  309. // Tear down |image_| if it doesn't match the new frame size.
  310. if (image_ && !size.equals(webrtc::DesktopSize(image_->d_w, image_->d_h))) {
  311. image_.reset();
  312. }
  313. // Tear down |codec_| if the new size does not match what this codec instance
  314. // was configured for.
  315. if (codec_ && !size.equals(webrtc::DesktopSize(codec_->config.enc->g_w,
  316. codec_->config.enc->g_h))) {
  317. codec_.reset();
  318. }
  319. if (use_active_map_) {
  320. active_map_.Initialize(size);
  321. }
  322. // Fetch a default configuration for the desired codec.
  323. const vpx_codec_iface_t* interface =
  324. use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx();
  325. vpx_codec_err_t ret = vpx_codec_enc_config_default(interface, &config_, 0);
  326. DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to fetch default configuration";
  327. // Customize the default configuration to our needs.
  328. if (use_vp9_) {
  329. SetVp9CodecParameters(&config_, size, lossless_color_, lossless_encode_);
  330. } else {
  331. SetVp8CodecParameters(&config_, size);
  332. }
  333. config_.rc_target_bitrate = kDefaultTargetBitrateKbps;
  334. // Initialize or re-configure the codec with the custom configuration.
  335. if (!codec_) {
  336. codec_.reset(new vpx_codec_ctx_t);
  337. ret = vpx_codec_enc_init(codec_.get(), interface, &config_, 0);
  338. CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to initialize codec";
  339. } else {
  340. ret = vpx_codec_enc_config_set(codec_.get(), &config_);
  341. CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to reconfigure codec";
  342. }
  343. // Apply further customizations to the codec now it's initialized.
  344. if (use_vp9_) {
  345. SetVp9CodecOptions(codec_.get(), lossless_encode_, vp9_encoder_speed_);
  346. } else {
  347. SetVp8CodecOptions(codec_.get());
  348. }
  349. }
  350. void WebrtcVideoEncoderVpx::UpdateConfig(const FrameParams& params) {
  351. // Configuration not initialized.
  352. if (config_.g_timebase.den == 0)
  353. return;
  354. bool changed = false;
  355. if (params.bitrate_kbps >= 0) {
  356. bitrate_filter_.SetBandwidthEstimateKbps(params.bitrate_kbps);
  357. if (config_.rc_target_bitrate !=
  358. static_cast<unsigned int>(bitrate_filter_.GetTargetBitrateKbps())) {
  359. config_.rc_target_bitrate = bitrate_filter_.GetTargetBitrateKbps();
  360. changed = true;
  361. }
  362. }
  363. if (params.vpx_min_quantizer >= 0 &&
  364. config_.rc_min_quantizer !=
  365. static_cast<unsigned int>(params.vpx_min_quantizer)) {
  366. config_.rc_min_quantizer = params.vpx_min_quantizer;
  367. changed = true;
  368. }
  369. if (params.vpx_max_quantizer >= 0 &&
  370. config_.rc_max_quantizer !=
  371. static_cast<unsigned int>(params.vpx_max_quantizer)) {
  372. config_.rc_max_quantizer = params.vpx_max_quantizer;
  373. changed = true;
  374. }
  375. if (!changed)
  376. return;
  377. // Update encoder context.
  378. if (vpx_codec_enc_config_set(codec_.get(), &config_))
  379. NOTREACHED() << "Unable to set encoder config";
  380. }
  381. void WebrtcVideoEncoderVpx::PrepareImage(
  382. const webrtc::DesktopFrame* frame,
  383. webrtc::DesktopRegion* updated_region) {
  384. updated_region->Clear();
  385. if (!frame) {
  386. return;
  387. }
  388. if (image_) {
  389. // Pad each rectangle to avoid the block-artifact filters in libvpx from
  390. // introducing artifacts; VP9 includes up to 8px either side, and VP8 up to
  391. // 3px, so unchanged pixels up to that far out may still be affected by the
  392. // changes in the updated region, and so must be listed in the active map.
  393. // After padding we align each rectangle to 16x16 active-map macroblocks.
  394. // This implicitly ensures all rects have even top-left coords, which is
  395. // is required by ConvertRGBToYUVWithRect().
  396. // TODO(wez): Do we still need 16x16 align, or is even alignment sufficient?
  397. int padding = use_vp9_ ? 8 : 3;
  398. for (webrtc::DesktopRegion::Iterator r(frame->updated_region());
  399. !r.IsAtEnd(); r.Advance()) {
  400. const webrtc::DesktopRect& rect = r.rect();
  401. updated_region->AddRect(AlignRect(webrtc::DesktopRect::MakeLTRB(
  402. rect.left() - padding, rect.top() - padding, rect.right() + padding,
  403. rect.bottom() + padding)));
  404. }
  405. // Clip back to the screen dimensions, in case they're not macroblock
  406. // aligned. The conversion routines don't require even width & height,
  407. // so this is safe even if the source dimensions are not even.
  408. updated_region->IntersectWith(
  409. webrtc::DesktopRect::MakeWH(image_->d_w, image_->d_h));
  410. } else {
  411. vpx_img_fmt_t fmt = lossless_color_ ? VPX_IMG_FMT_I444 : VPX_IMG_FMT_YV12;
  412. image_.reset(vpx_img_alloc(nullptr, fmt, frame->size().width(),
  413. frame->size().height(),
  414. GetSimdMemoryAlignment()));
  415. updated_region->AddRect(
  416. webrtc::DesktopRect::MakeWH(image_->d_w, image_->d_h));
  417. }
  418. // Convert the updated region to YUV ready for encoding.
  419. const uint8_t* rgb_data = frame->data();
  420. const int rgb_stride = frame->stride();
  421. const int y_stride = image_->stride[0];
  422. DCHECK_EQ(image_->stride[1], image_->stride[2]);
  423. const int uv_stride = image_->stride[1];
  424. uint8_t* y_data = image_->planes[0];
  425. uint8_t* u_data = image_->planes[1];
  426. uint8_t* v_data = image_->planes[2];
  427. switch (image_->fmt) {
  428. case VPX_IMG_FMT_I444:
  429. for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
  430. r.Advance()) {
  431. webrtc::DesktopRect rect = GetRowAlignedRect(r.rect(), image_->d_w);
  432. int rgb_offset =
  433. rgb_stride * rect.top() + rect.left() * kBytesPerRgbPixel;
  434. int yuv_offset = uv_stride * rect.top() + rect.left();
  435. libyuv::ARGBToI444(rgb_data + rgb_offset, rgb_stride,
  436. y_data + yuv_offset, y_stride, u_data + yuv_offset,
  437. uv_stride, v_data + yuv_offset, uv_stride,
  438. rect.width(), rect.height());
  439. }
  440. break;
  441. case VPX_IMG_FMT_YV12:
  442. for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
  443. r.Advance()) {
  444. webrtc::DesktopRect rect = GetRowAlignedRect(r.rect(), image_->d_w);
  445. int rgb_offset =
  446. rgb_stride * rect.top() + rect.left() * kBytesPerRgbPixel;
  447. int y_offset = y_stride * rect.top() + rect.left();
  448. int uv_offset = uv_stride * rect.top() / 2 + rect.left() / 2;
  449. libyuv::ARGBToI420(rgb_data + rgb_offset, rgb_stride, y_data + y_offset,
  450. y_stride, u_data + uv_offset, uv_stride,
  451. v_data + uv_offset, uv_stride, rect.width(),
  452. rect.height());
  453. }
  454. break;
  455. default:
  456. NOTREACHED();
  457. break;
  458. }
  459. }
  460. } // namespace remoting