[email protected] | a43cfae | 2013-10-19 22:14:54 | [diff] [blame] | 1 | // Copyright 2013 The Chromium Authors. All rights reserved. |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | a43cfae | 2013-10-19 22:14:54 | [diff] [blame] | 5 | #include "remoting/codec/video_decoder_vpx.h" |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 6 | |
[email protected] | 82156e8 | 2011-12-20 07:09:01 | [diff] [blame] | 7 | #include <math.h> |
avi | 5a080f01 | 2015-12-22 23:15:43 | [diff] [blame] | 8 | #include <stdint.h> |
[email protected] | 82156e8 | 2011-12-20 07:09:01 | [diff] [blame] | 9 | |
[email protected] | 1e1cb3b | 2011-11-10 02:07:41 | [diff] [blame] | 10 | #include "base/logging.h" |
dcheng | 0765c49 | 2016-04-06 22:41:53 | [diff] [blame] | 11 | #include "base/memory/ptr_util.h" |
[email protected] | c3af26f33 | 2010-10-06 22:46:00 | [diff] [blame] | 12 | #include "remoting/base/util.h" |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 13 | #include "remoting/proto/video.pb.h" |
[email protected] | 0dd9fb6 | 2014-05-26 16:24:49 | [diff] [blame] | 14 | #include "third_party/libyuv/include/libyuv/convert_argb.h" |
Frank Barchard | 638ac13f6 | 2016-07-12 22:43:01 | [diff] [blame] | 15 | #include "third_party/libyuv/include/libyuv/convert_from.h" |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 16 | #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
| 17 | #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" |
| 18 | #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 19 | |
| 20 | extern "C" { |
| 21 | #define VPX_CODEC_DISABLE_COMPAT 1 |
johannkoenig | 8cdf0347 | 2016-03-02 13:54:31 | [diff] [blame] | 22 | #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h" |
| 23 | #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h" |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 24 | } |
| 25 | |
| 26 | namespace remoting { |
| 27 | |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 28 | namespace { |
| 29 | |
| 30 | void RenderRect(vpx_image_t* image, |
| 31 | webrtc::DesktopRect rect, |
sergeyu | 4ba8b73 | 2016-06-24 05:51:32 | [diff] [blame] | 32 | VideoDecoder::PixelFormat pixel_format, |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 33 | webrtc::DesktopFrame* frame) { |
sergeyu | 4ba8b73 | 2016-06-24 05:51:32 | [diff] [blame] | 34 | auto yuv_to_rgb_function = libyuv::I420ToARGB; |
| 35 | int u_offset; |
| 36 | int v_offset; |
| 37 | |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 38 | switch (image->fmt) { |
| 39 | case VPX_IMG_FMT_I420: { |
| 40 | // Align position of the top left corner so that its coordinates are |
| 41 | // always even. |
| 42 | rect = webrtc::DesktopRect::MakeLTRB(rect.left() & ~1, rect.top() & ~1, |
| 43 | rect.right(), rect.bottom()); |
sergeyu | 4ba8b73 | 2016-06-24 05:51:32 | [diff] [blame] | 44 | u_offset = rect.top() / 2 * image->stride[1] + rect.left() / 2; |
| 45 | v_offset = rect.top() / 2 * image->stride[2] + rect.left() / 2; |
| 46 | yuv_to_rgb_function = (pixel_format == VideoDecoder::PixelFormat::BGRA) |
| 47 | ? libyuv::I420ToARGB |
| 48 | : libyuv::I420ToABGR; |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 49 | break; |
| 50 | } |
| 51 | // VP8 only outputs I420 frames, but VP9 can also produce I444. |
| 52 | case VPX_IMG_FMT_I444: { |
sergeyu | 4ba8b73 | 2016-06-24 05:51:32 | [diff] [blame] | 53 | u_offset = rect.top() * image->stride[1] + rect.left(); |
| 54 | v_offset = rect.top() * image->stride[2] + rect.left(); |
| 55 | yuv_to_rgb_function = (pixel_format == VideoDecoder::PixelFormat::BGRA) |
| 56 | ? libyuv::I444ToARGB |
| 57 | : libyuv::I444ToABGR; |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 58 | break; |
| 59 | } |
| 60 | default: { |
| 61 | LOG(ERROR) << "Unsupported image format:" << image->fmt; |
| 62 | return; |
| 63 | } |
| 64 | } |
sergeyu | 4ba8b73 | 2016-06-24 05:51:32 | [diff] [blame] | 65 | |
| 66 | int y_offset = rect.top() * image->stride[0] + rect.left(); |
| 67 | uint8_t* image_data_ptr = frame->GetFrameDataAtPos(rect.top_left()); |
| 68 | yuv_to_rgb_function(image->planes[0] + y_offset, image->stride[0], |
| 69 | image->planes[1] + u_offset, image->stride[1], |
| 70 | image->planes[2] + v_offset, image->stride[2], |
| 71 | image_data_ptr, frame->stride(), rect.width(), |
| 72 | rect.height()); |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 73 | } |
| 74 | |
| 75 | } // namespace |
| 76 | |
[email protected] | a43cfae | 2013-10-19 22:14:54 | [diff] [blame] | 77 | // static |
dcheng | 0765c49 | 2016-04-06 22:41:53 | [diff] [blame] | 78 | std::unique_ptr<VideoDecoderVpx> VideoDecoderVpx::CreateForVP8() { |
| 79 | return base::WrapUnique(new VideoDecoderVpx(vpx_codec_vp8_dx())); |
[email protected] | a43cfae | 2013-10-19 22:14:54 | [diff] [blame] | 80 | } |
| 81 | |
[email protected] | 87196ce | 2013-10-22 01:53:39 | [diff] [blame] | 82 | // static |
dcheng | 0765c49 | 2016-04-06 22:41:53 | [diff] [blame] | 83 | std::unique_ptr<VideoDecoderVpx> VideoDecoderVpx::CreateForVP9() { |
| 84 | return base::WrapUnique(new VideoDecoderVpx(vpx_codec_vp9_dx())); |
[email protected] | 87196ce | 2013-10-22 01:53:39 | [diff] [blame] | 85 | } |
| 86 | |
Chris Watkins | 6fe52aa | 2017-11-28 03:24:05 | [diff] [blame^] | 87 | VideoDecoderVpx::~VideoDecoderVpx() = default; |
[email protected] | a43cfae | 2013-10-19 22:14:54 | [diff] [blame] | 88 | |
sergeyu | 4ba8b73 | 2016-06-24 05:51:32 | [diff] [blame] | 89 | void VideoDecoderVpx::SetPixelFormat(PixelFormat pixel_format) { |
| 90 | pixel_format_ = pixel_format; |
| 91 | } |
| 92 | |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 93 | bool VideoDecoderVpx::DecodePacket(const VideoPacket& packet, |
| 94 | webrtc::DesktopFrame* frame) { |
wez | 070889b | 2015-07-17 03:19:15 | [diff] [blame] | 95 | // Pass the packet to the codec to process. |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 96 | vpx_codec_err_t ret = vpx_codec_decode( |
avi | 5a080f01 | 2015-12-22 23:15:43 | [diff] [blame] | 97 | codec_.get(), reinterpret_cast<const uint8_t*>(packet.data().data()), |
wez | 9ad574b2 | 2015-07-14 16:24:53 | [diff] [blame] | 98 | packet.data().size(), nullptr, 0); |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 99 | if (ret != VPX_CODEC_OK) { |
[email protected] | 1fd715b | 2014-04-09 04:13:04 | [diff] [blame] | 100 | const char* error = vpx_codec_error(codec_.get()); |
| 101 | const char* error_detail = vpx_codec_error_detail(codec_.get()); |
| 102 | LOG(ERROR) << "Decoding failed:" << (error ? error : "(NULL)") << "\n" |
| 103 | << "Details: " << (error_detail ? error_detail : "(NULL)"); |
[email protected] | 41b93ad3 | 2013-09-23 18:53:52 | [diff] [blame] | 104 | return false; |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 105 | } |
| 106 | |
wez | 070889b | 2015-07-17 03:19:15 | [diff] [blame] | 107 | // Fetch the decoded video frame. |
wez | 9ad574b2 | 2015-07-14 16:24:53 | [diff] [blame] | 108 | vpx_codec_iter_t iter = nullptr; |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 109 | vpx_image_t* image = vpx_codec_get_frame(codec_.get(), &iter); |
| 110 | if (!image) { |
| 111 | LOG(ERROR) << "No video frame decoded."; |
[email protected] | 41b93ad3 | 2013-09-23 18:53:52 | [diff] [blame] | 112 | return false; |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 113 | } |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 114 | if (!webrtc::DesktopSize(image->d_w, image->d_h).equals(frame->size())) { |
| 115 | LOG(ERROR) << "Size of the encoded frame doesn't match size in the header."; |
| 116 | return false; |
| 117 | } |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 118 | |
wez | 070889b | 2015-07-17 03:19:15 | [diff] [blame] | 119 | // Determine which areas have been updated. |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 120 | webrtc::DesktopRegion* region = frame->mutable_updated_region(); |
| 121 | region->Clear(); |
[email protected] | 41b93ad3 | 2013-09-23 18:53:52 | [diff] [blame] | 122 | for (int i = 0; i < packet.dirty_rects_size(); ++i) { |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 123 | Rect proto_rect = packet.dirty_rects(i); |
| 124 | webrtc::DesktopRect rect = |
| 125 | webrtc::DesktopRect::MakeXYWH(proto_rect.x(), proto_rect.y(), |
| 126 | proto_rect.width(), proto_rect.height()); |
| 127 | region->AddRect(rect); |
sergeyu | 4ba8b73 | 2016-06-24 05:51:32 | [diff] [blame] | 128 | RenderRect(image, rect, pixel_format_, frame); |
[email protected] | 5068614 | 2011-02-04 02:08:32 | [diff] [blame] | 129 | } |
[email protected] | da4daa7f | 2013-06-21 10:16:41 | [diff] [blame] | 130 | |
[email protected] | 41b93ad3 | 2013-09-23 18:53:52 | [diff] [blame] | 131 | return true; |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 132 | } |
| 133 | |
sergeyu | ef92de20 | 2015-08-20 22:47:37 | [diff] [blame] | 134 | VideoDecoderVpx::VideoDecoderVpx(vpx_codec_iface_t* codec) { |
wez | 070889b | 2015-07-17 03:19:15 | [diff] [blame] | 135 | codec_.reset(new vpx_codec_ctx_t); |
| 136 | |
| 137 | vpx_codec_dec_cfg config; |
| 138 | config.w = 0; |
| 139 | config.h = 0; |
| 140 | config.threads = 2; |
| 141 | vpx_codec_err_t ret = vpx_codec_dec_init(codec_.get(), codec, &config, 0); |
| 142 | CHECK_EQ(VPX_CODEC_OK, ret); |
[email protected] | da4daa7f | 2013-06-21 10:16:41 | [diff] [blame] | 143 | } |
| 144 | |
[email protected] | 622b788 | 2010-09-29 17:34:32 | [diff] [blame] | 145 | } // namespace remoting |