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"
11 #include "base/logging.h"
12 #include "media/base/media.h"
13 #include "media/base/yuv_convert.h"
14 #include "remoting/base/util.h"
15 #include "third_party/libyuv/include/libyuv/convert_argb.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"
26 scoped_ptr
<VideoDecoderVpx
> VideoDecoderVpx::CreateForVP8() {
27 return make_scoped_ptr(new VideoDecoderVpx(vpx_codec_vp8_dx()));
31 scoped_ptr
<VideoDecoderVpx
> VideoDecoderVpx::CreateForVP9() {
32 return make_scoped_ptr(new VideoDecoderVpx(vpx_codec_vp9_dx()));
35 VideoDecoderVpx::~VideoDecoderVpx() {}
37 bool VideoDecoderVpx::DecodePacket(const VideoPacket
& packet
) {
38 // Pass the packet to the codec to process.
39 vpx_codec_err_t ret
= vpx_codec_decode(
40 codec_
.get(), reinterpret_cast<const uint8
*>(packet
.data().data()),
41 packet
.data().size(), nullptr, 0);
42 if (ret
!= VPX_CODEC_OK
) {
43 const char* error
= vpx_codec_error(codec_
.get());
44 const char* error_detail
= vpx_codec_error_detail(codec_
.get());
45 LOG(ERROR
) << "Decoding failed:" << (error
? error
: "(NULL)") << "\n"
46 << "Details: " << (error_detail
? error_detail
: "(NULL)");
50 // Fetch the decoded video frame.
51 vpx_codec_iter_t iter
= nullptr;
52 image_
= vpx_codec_get_frame(codec_
.get(), &iter
);
54 LOG(ERROR
) << "No video frame decoded";
57 DCHECK(!image_size().is_empty());
59 // Determine which areas have been updated.
60 webrtc::DesktopRegion region
;
61 for (int i
= 0; i
< packet
.dirty_rects_size(); ++i
) {
62 Rect remoting_rect
= packet
.dirty_rects(i
);
63 region
.AddRect(webrtc::DesktopRect::MakeXYWH(
64 remoting_rect
.x(), remoting_rect
.y(),
65 remoting_rect
.width(), remoting_rect
.height()));
67 updated_region_
.AddRegion(region
);
69 // Process the frame shape, if supplied.
70 if (packet
.has_use_desktop_shape()) {
71 if (packet
.use_desktop_shape()) {
73 desktop_shape_
= make_scoped_ptr(new webrtc::DesktopRegion
);
74 desktop_shape_
->Clear();
75 for (int i
= 0; i
< packet
.desktop_shape_rects_size(); ++i
) {
76 Rect remoting_rect
= packet
.desktop_shape_rects(i
);
77 desktop_shape_
->AddRect(webrtc::DesktopRect::MakeXYWH(
78 remoting_rect
.x(), remoting_rect
.y(), remoting_rect
.width(),
79 remoting_rect
.height()));
82 desktop_shape_
.reset();
89 void VideoDecoderVpx::Invalidate(const webrtc::DesktopSize
& view_size
,
90 const webrtc::DesktopRegion
& region
) {
91 DCHECK(!view_size
.is_empty());
93 for (webrtc::DesktopRegion::Iterator
i(region
); !i
.IsAtEnd(); i
.Advance()) {
94 updated_region_
.AddRect(ScaleRect(i
.rect(), view_size
, image_size()));
98 void VideoDecoderVpx::RenderFrame(const webrtc::DesktopSize
& view_size
,
99 const webrtc::DesktopRect
& clip_area
,
102 webrtc::DesktopRegion
* output_region
) {
103 DCHECK(!image_size().is_empty());
104 DCHECK(!view_size
.is_empty());
106 // Early-return and do nothing if we haven't yet decoded any frames.
110 webrtc::DesktopRect source_clip
= webrtc::DesktopRect::MakeSize(image_size());
112 // VP8 only outputs I420 frames, but VP9 can also produce I444.
113 switch (image_
->fmt
) {
114 case VPX_IMG_FMT_I444
: {
115 // TODO(wez): Add scaling support to the I444 conversion path.
116 if (view_size
.equals(image_size())) {
117 for (webrtc::DesktopRegion::Iterator
i(updated_region_
);
118 !i
.IsAtEnd(); i
.Advance()) {
119 // Determine the scaled area affected by this rectangle changing.
120 webrtc::DesktopRect rect
= i
.rect();
121 rect
.IntersectWith(source_clip
);
122 rect
.IntersectWith(clip_area
);
126 int image_offset
= image_stride
* rect
.top() +
127 rect
.left() * VideoDecoder::kBytesPerPixel
;
128 int y_offset
= image_
->stride
[0] * rect
.top() + rect
.left();
129 int u_offset
= image_
->stride
[1] * rect
.top() + rect
.left();
130 int v_offset
= image_
->stride
[2] * rect
.top() + rect
.left();
131 libyuv::I444ToARGB(image_
->planes
[0] + y_offset
, image_
->stride
[0],
132 image_
->planes
[1] + u_offset
, image_
->stride
[1],
133 image_
->planes
[2] + v_offset
, image_
->stride
[2],
134 image_buffer
+ image_offset
, image_stride
,
135 rect
.width(), rect
.height());
137 output_region
->AddRect(rect
);
142 case VPX_IMG_FMT_I420
: {
143 // ScaleYUVToRGB32WithRect does not currently support up-scaling. We
144 // won't be asked to up-scale except during resizes or if page zoom is
145 // >100%, so we work-around the limitation by using the slower
147 // TODO(wez): Remove this hack if/when ScaleYUVToRGB32WithRect can
149 if (!updated_region_
.is_empty() &&
150 (source_clip
.width() < view_size
.width() ||
151 source_clip
.height() < view_size
.height())) {
152 // We're scaling only |clip_area| into the |image_buffer|, so we need to
153 // work out which source rectangle that corresponds to.
154 webrtc::DesktopRect source_rect
=
155 ScaleRect(clip_area
, view_size
, image_size());
156 source_rect
= webrtc::DesktopRect::MakeLTRB(
157 RoundToTwosMultiple(source_rect
.left()),
158 RoundToTwosMultiple(source_rect
.top()),
160 source_rect
.bottom());
162 // If there were no changes within the clip source area then don't
164 webrtc::DesktopRegion
intersection(source_rect
);
165 intersection
.IntersectWith(updated_region_
);
166 if (intersection
.is_empty())
169 // Scale & convert the entire clip area.
170 int y_offset
= CalculateYOffset(source_rect
.left(), source_rect
.top(),
172 int uv_offset
= CalculateUVOffset(source_rect
.left(), source_rect
.top(),
175 image_
->planes
[0] + y_offset
, image_
->planes
[1] + uv_offset
,
176 image_
->planes
[2] + uv_offset
, image_buffer
, source_rect
.width(),
177 source_rect
.height(), clip_area
.width(), clip_area
.height(),
178 image_
->stride
[0], image_
->stride
[1], image_stride
, media::YV12
,
179 media::ROTATE_0
, media::FILTER_BILINEAR
);
181 output_region
->AddRect(clip_area
);
182 updated_region_
.Subtract(source_rect
);
186 for (webrtc::DesktopRegion::Iterator
i(updated_region_
);
187 !i
.IsAtEnd(); i
.Advance()) {
188 // Determine the scaled area affected by this rectangle changing.
189 webrtc::DesktopRect rect
= i
.rect();
190 rect
.IntersectWith(source_clip
);
193 rect
= ScaleRect(rect
, image_size(), view_size
);
194 rect
.IntersectWith(clip_area
);
198 ConvertAndScaleYUVToRGB32Rect(
199 image_
->planes
[0], image_
->planes
[1], image_
->planes
[2],
200 image_
->stride
[0], image_
->stride
[1], image_size(), source_clip
,
201 image_buffer
, image_stride
, view_size
, clip_area
, rect
);
203 output_region
->AddRect(rect
);
206 updated_region_
.Subtract(ScaleRect(clip_area
, view_size
, image_size()));
210 LOG(ERROR
) << "Unsupported image format:" << image_
->fmt
;
215 webrtc::DesktopRect scaled_clip_area
=
216 ScaleRect(clip_area
, view_size
, image_size());
217 updated_region_
.Subtract(scaled_clip_area
);
220 const webrtc::DesktopRegion
* VideoDecoderVpx::GetImageShape() {
221 return desktop_shape_
.get();
224 VideoDecoderVpx::VideoDecoderVpx(vpx_codec_iface_t
* codec
) : image_(nullptr) {
225 codec_
.reset(new vpx_codec_ctx_t
);
227 vpx_codec_dec_cfg config
;
231 vpx_codec_err_t ret
= vpx_codec_dec_init(codec_
.get(), codec
, &config
, 0);
232 CHECK_EQ(VPX_CODEC_OK
, ret
);
235 webrtc::DesktopSize
VideoDecoderVpx::image_size() const {
236 return image_
? webrtc::DesktopSize(image_
->d_w
, image_
->d_h
)
237 : webrtc::DesktopSize();
240 } // namespace remoting