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
3 // found in the LICENSE file.
5 #include "remoting/codec/video_decoder_vpx.h"
9 #include "base/logging.h"
10 #include "remoting/base/util.h"
11 #include "remoting/proto/video.pb.h"
12 #include "third_party/libyuv/include/libyuv/convert_argb.h"
13 #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_region.h"
18 #define VPX_CODEC_DISABLE_COMPAT 1
19 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
20 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
27 void RenderRect(vpx_image_t
* image
,
28 webrtc::DesktopRect rect
,
29 webrtc::DesktopFrame
* frame
) {
31 case VPX_IMG_FMT_I420
: {
32 // Align position of the top left corner so that its coordinates are
34 rect
= webrtc::DesktopRect::MakeLTRB(rect
.left() & ~1, rect
.top() & ~1,
35 rect
.right(), rect
.bottom());
36 uint8_t* image_data_ptr
= frame
->GetFrameDataAtPos(rect
.top_left());
37 int y_offset
= rect
.top() * image
->stride
[0] + rect
.left();
38 int u_offset
= rect
.top() / 2 * image
->stride
[1] + rect
.left() / 2;
39 int v_offset
= rect
.top() / 2 * image
->stride
[2] + rect
.left() / 2;
40 libyuv::I420ToARGB(image
->planes
[0] + y_offset
, image
->stride
[0],
41 image
->planes
[1] + u_offset
, image
->stride
[1],
42 image
->planes
[2] + v_offset
, image
->stride
[2],
43 image_data_ptr
, frame
->stride(),
44 rect
.width(), rect
.height());
47 // VP8 only outputs I420 frames, but VP9 can also produce I444.
48 case VPX_IMG_FMT_I444
: {
49 uint8_t* image_data_ptr
= frame
->GetFrameDataAtPos(rect
.top_left());
50 int y_offset
= rect
.top() * image
->stride
[0] + rect
.left();
51 int u_offset
= rect
.top() * image
->stride
[1] + rect
.left();
52 int v_offset
= rect
.top() * image
->stride
[2] + rect
.left();
53 libyuv::I444ToARGB(image
->planes
[0] + y_offset
, image
->stride
[0],
54 image
->planes
[1] + u_offset
, image
->stride
[1],
55 image
->planes
[2] + v_offset
, image
->stride
[2],
56 image_data_ptr
, frame
->stride(),
57 rect
.width(), rect
.height());
61 LOG(ERROR
) << "Unsupported image format:" << image
->fmt
;
70 scoped_ptr
<VideoDecoderVpx
> VideoDecoderVpx::CreateForVP8() {
71 return make_scoped_ptr(new VideoDecoderVpx(vpx_codec_vp8_dx()));
75 scoped_ptr
<VideoDecoderVpx
> VideoDecoderVpx::CreateForVP9() {
76 return make_scoped_ptr(new VideoDecoderVpx(vpx_codec_vp9_dx()));
79 VideoDecoderVpx::~VideoDecoderVpx() {}
81 bool VideoDecoderVpx::DecodePacket(const VideoPacket
& packet
,
82 webrtc::DesktopFrame
* frame
) {
83 // Pass the packet to the codec to process.
84 vpx_codec_err_t ret
= vpx_codec_decode(
85 codec_
.get(), reinterpret_cast<const uint8
*>(packet
.data().data()),
86 packet
.data().size(), nullptr, 0);
87 if (ret
!= VPX_CODEC_OK
) {
88 const char* error
= vpx_codec_error(codec_
.get());
89 const char* error_detail
= vpx_codec_error_detail(codec_
.get());
90 LOG(ERROR
) << "Decoding failed:" << (error
? error
: "(NULL)") << "\n"
91 << "Details: " << (error_detail
? error_detail
: "(NULL)");
95 // Fetch the decoded video frame.
96 vpx_codec_iter_t iter
= nullptr;
97 vpx_image_t
* image
= vpx_codec_get_frame(codec_
.get(), &iter
);
99 LOG(ERROR
) << "No video frame decoded.";
102 if (!webrtc::DesktopSize(image
->d_w
, image
->d_h
).equals(frame
->size())) {
103 LOG(ERROR
) << "Size of the encoded frame doesn't match size in the header.";
107 // Determine which areas have been updated.
108 webrtc::DesktopRegion
* region
= frame
->mutable_updated_region();
110 for (int i
= 0; i
< packet
.dirty_rects_size(); ++i
) {
111 Rect proto_rect
= packet
.dirty_rects(i
);
112 webrtc::DesktopRect rect
=
113 webrtc::DesktopRect::MakeXYWH(proto_rect
.x(), proto_rect
.y(),
114 proto_rect
.width(), proto_rect
.height());
115 region
->AddRect(rect
);
116 RenderRect(image
, rect
, frame
);
119 // Process the frame shape, if supplied.
120 if (packet
.has_use_desktop_shape()) {
121 if (packet
.use_desktop_shape()) {
123 desktop_shape_
= make_scoped_ptr(new webrtc::DesktopRegion
);
124 desktop_shape_
->Clear();
125 for (int i
= 0; i
< packet
.desktop_shape_rects_size(); ++i
) {
126 Rect proto_rect
= packet
.desktop_shape_rects(i
);
127 desktop_shape_
->AddRect(webrtc::DesktopRect::MakeXYWH(
128 proto_rect
.x(), proto_rect
.y(), proto_rect
.width(),
129 proto_rect
.height()));
132 desktop_shape_
.reset();
137 frame
->set_shape(new webrtc::DesktopRegion(*desktop_shape_
));
142 VideoDecoderVpx::VideoDecoderVpx(vpx_codec_iface_t
* codec
) {
143 codec_
.reset(new vpx_codec_ctx_t
);
145 vpx_codec_dec_cfg config
;
149 vpx_codec_err_t ret
= vpx_codec_dec_init(codec_
.get(), codec
, &config
, 0);
150 CHECK_EQ(VPX_CODEC_OK
, ret
);
153 } // namespace remoting