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"
17 #define VPX_CODEC_DISABLE_COMPAT 1
18 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
19 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
26 const uint32 kTransparentColor
= 0;
28 // Fills the rectangle |rect| with the given ARGB color |color| in |buffer|.
29 void FillRect(uint8
* buffer
,
31 const webrtc::DesktopRect
& rect
,
33 uint32
* ptr
= reinterpret_cast<uint32
*>(buffer
+ (rect
.top() * stride
) +
34 (rect
.left() * VideoDecoder::kBytesPerPixel
));
35 int width
= rect
.width();
36 for (int height
= rect
.height(); height
> 0; --height
) {
37 std::fill(ptr
, ptr
+ width
, color
);
38 ptr
+= stride
/ VideoDecoder::kBytesPerPixel
;
45 scoped_ptr
<VideoDecoderVpx
> VideoDecoderVpx::CreateForVP8() {
46 ScopedVpxCodec
codec(new vpx_codec_ctx_t
);
48 // TODO(hclam): Scale the number of threads with number of cores of the
50 vpx_codec_dec_cfg config
;
55 vpx_codec_dec_init(codec
.get(), vpx_codec_vp8_dx(), &config
, 0);
56 if (ret
!= VPX_CODEC_OK
) {
57 LOG(ERROR
) << "Cannot initialize codec.";
58 return scoped_ptr
<VideoDecoderVpx
>();
61 return scoped_ptr
<VideoDecoderVpx
>(new VideoDecoderVpx(codec
.Pass()));
65 scoped_ptr
<VideoDecoderVpx
> VideoDecoderVpx::CreateForVP9() {
66 ScopedVpxCodec
codec(new vpx_codec_ctx_t
);
68 // TODO(hclam): Scale the number of threads with number of cores of the
70 vpx_codec_dec_cfg config
;
75 vpx_codec_dec_init(codec
.get(), vpx_codec_vp9_dx(), &config
, 0);
76 if (ret
!= VPX_CODEC_OK
) {
77 LOG(ERROR
) << "Cannot initialize codec.";
78 return scoped_ptr
<VideoDecoderVpx
>();
81 return scoped_ptr
<VideoDecoderVpx
>(new VideoDecoderVpx(codec
.Pass()));
84 VideoDecoderVpx::~VideoDecoderVpx() {}
86 void VideoDecoderVpx::Initialize(const webrtc::DesktopSize
& screen_size
) {
87 DCHECK(!screen_size
.is_empty());
89 screen_size_
= screen_size
;
91 transparent_region_
.SetRect(webrtc::DesktopRect::MakeSize(screen_size_
));
94 bool VideoDecoderVpx::DecodePacket(const VideoPacket
& packet
) {
95 DCHECK(!screen_size_
.is_empty());
97 // Do the actual decoding.
98 vpx_codec_err_t ret
= vpx_codec_decode(
99 codec_
.get(), reinterpret_cast<const uint8
*>(packet
.data().data()),
100 packet
.data().size(), NULL
, 0);
101 if (ret
!= VPX_CODEC_OK
) {
102 const char* error
= vpx_codec_error(codec_
.get());
103 const char* error_detail
= vpx_codec_error_detail(codec_
.get());
104 LOG(ERROR
) << "Decoding failed:" << (error
? error
: "(NULL)") << "\n"
105 << "Details: " << (error_detail
? error_detail
: "(NULL)");
109 // Gets the decoded data.
110 vpx_codec_iter_t iter
= NULL
;
111 vpx_image_t
* image
= vpx_codec_get_frame(codec_
.get(), &iter
);
113 LOG(ERROR
) << "No video frame decoded";
118 webrtc::DesktopRegion region
;
119 for (int i
= 0; i
< packet
.dirty_rects_size(); ++i
) {
120 Rect remoting_rect
= packet
.dirty_rects(i
);
121 region
.AddRect(webrtc::DesktopRect::MakeXYWH(
122 remoting_rect
.x(), remoting_rect
.y(),
123 remoting_rect
.width(), remoting_rect
.height()));
126 updated_region_
.AddRegion(region
);
128 // Update the desktop shape region.
129 webrtc::DesktopRegion desktop_shape_region
;
130 if (packet
.has_use_desktop_shape()) {
131 for (int i
= 0; i
< packet
.desktop_shape_rects_size(); ++i
) {
132 Rect remoting_rect
= packet
.desktop_shape_rects(i
);
133 desktop_shape_region
.AddRect(webrtc::DesktopRect::MakeXYWH(
134 remoting_rect
.x(), remoting_rect
.y(),
135 remoting_rect
.width(), remoting_rect
.height()));
138 // Fallback for the case when the host didn't include the desktop shape
140 desktop_shape_region
=
141 webrtc::DesktopRegion(webrtc::DesktopRect::MakeSize(screen_size_
));
144 UpdateImageShapeRegion(&desktop_shape_region
);
149 void VideoDecoderVpx::Invalidate(const webrtc::DesktopSize
& view_size
,
150 const webrtc::DesktopRegion
& region
) {
151 DCHECK(!view_size
.is_empty());
153 for (webrtc::DesktopRegion::Iterator
i(region
); !i
.IsAtEnd(); i
.Advance()) {
154 updated_region_
.AddRect(ScaleRect(i
.rect(), view_size
, screen_size_
));
157 // Updated areas outside of the new desktop shape region should be made
158 // transparent, not repainted.
159 webrtc::DesktopRegion difference
= updated_region_
;
160 difference
.Subtract(desktop_shape_
);
161 updated_region_
.Subtract(difference
);
162 transparent_region_
.AddRegion(difference
);
165 void VideoDecoderVpx::RenderFrame(const webrtc::DesktopSize
& view_size
,
166 const webrtc::DesktopRect
& clip_area
,
169 webrtc::DesktopRegion
* output_region
) {
170 DCHECK(!screen_size_
.is_empty());
171 DCHECK(!view_size
.is_empty());
173 // Early-return and do nothing if we haven't yet decoded any frames.
177 webrtc::DesktopRect source_clip
=
178 webrtc::DesktopRect::MakeWH(last_image_
->d_w
, last_image_
->d_h
);
180 // ScaleYUVToRGB32WithRect does not currently support up-scaling. We won't
181 // be asked to up-scale except during resizes or if page zoom is >100%, so
182 // we work-around the limitation by using the slower ScaleYUVToRGB32.
183 // TODO(wez): Remove this hack if/when ScaleYUVToRGB32WithRect can up-scale.
184 if (!updated_region_
.is_empty() &&
185 (source_clip
.width() < view_size
.width() ||
186 source_clip
.height() < view_size
.height())) {
187 // We're scaling only |clip_area| into the |image_buffer|, so we need to
188 // work out which source rectangle that corresponds to.
189 webrtc::DesktopRect source_rect
=
190 ScaleRect(clip_area
, view_size
, screen_size_
);
191 source_rect
= webrtc::DesktopRect::MakeLTRB(
192 RoundToTwosMultiple(source_rect
.left()),
193 RoundToTwosMultiple(source_rect
.top()),
195 source_rect
.bottom());
197 // If there were no changes within the clip source area then don't render.
198 webrtc::DesktopRegion
intersection(source_rect
);
199 intersection
.IntersectWith(updated_region_
);
200 if (intersection
.is_empty())
203 // Scale & convert the entire clip area.
204 int y_offset
= CalculateYOffset(source_rect
.left(), source_rect
.top(),
205 last_image_
->stride
[0]);
206 int uv_offset
= CalculateUVOffset(source_rect
.left(), source_rect
.top(),
207 last_image_
->stride
[1]);
208 ScaleYUVToRGB32(last_image_
->planes
[0] + y_offset
,
209 last_image_
->planes
[1] + uv_offset
,
210 last_image_
->planes
[2] + uv_offset
,
213 source_rect
.height(),
216 last_image_
->stride
[0],
217 last_image_
->stride
[1],
221 media::FILTER_BILINEAR
);
223 output_region
->AddRect(clip_area
);
224 updated_region_
.Subtract(source_rect
);
228 for (webrtc::DesktopRegion::Iterator
i(updated_region_
);
229 !i
.IsAtEnd(); i
.Advance()) {
230 // Determine the scaled area affected by this rectangle changing.
231 webrtc::DesktopRect rect
= i
.rect();
232 rect
.IntersectWith(source_clip
);
235 rect
= ScaleRect(rect
, screen_size_
, view_size
);
236 rect
.IntersectWith(clip_area
);
240 ConvertAndScaleYUVToRGB32Rect(last_image_
->planes
[0],
241 last_image_
->planes
[1],
242 last_image_
->planes
[2],
243 last_image_
->stride
[0],
244 last_image_
->stride
[1],
253 output_region
->AddRect(rect
);
256 updated_region_
.Subtract(ScaleRect(clip_area
, view_size
, screen_size_
));
258 for (webrtc::DesktopRegion::Iterator
i(transparent_region_
);
259 !i
.IsAtEnd(); i
.Advance()) {
260 // Determine the scaled area affected by this rectangle changing.
261 webrtc::DesktopRect rect
= i
.rect();
262 rect
.IntersectWith(source_clip
);
265 rect
= ScaleRect(rect
, screen_size_
, view_size
);
266 rect
.IntersectWith(clip_area
);
270 // Fill the rectange with transparent pixels.
271 FillRect(image_buffer
, image_stride
, rect
, kTransparentColor
);
272 output_region
->AddRect(rect
);
275 webrtc::DesktopRect scaled_clip_area
=
276 ScaleRect(clip_area
, view_size
, screen_size_
);
277 updated_region_
.Subtract(scaled_clip_area
);
278 transparent_region_
.Subtract(scaled_clip_area
);
281 const webrtc::DesktopRegion
* VideoDecoderVpx::GetImageShape() {
282 return &desktop_shape_
;
285 VideoDecoderVpx::VideoDecoderVpx(ScopedVpxCodec codec
)
286 : codec_(codec
.Pass()),
291 void VideoDecoderVpx::UpdateImageShapeRegion(
292 webrtc::DesktopRegion
* new_desktop_shape
) {
293 // Add all areas that have been updated or become transparent to the
294 // transparent region. Exclude anything within the new desktop shape.
295 transparent_region_
.AddRegion(desktop_shape_
);
296 transparent_region_
.AddRegion(updated_region_
);
297 transparent_region_
.Subtract(*new_desktop_shape
);
299 // Add newly exposed areas to the update region and limit updates to the new
301 webrtc::DesktopRegion difference
= *new_desktop_shape
;
302 difference
.Subtract(desktop_shape_
);
303 updated_region_
.AddRegion(difference
);
304 updated_region_
.IntersectWith(*new_desktop_shape
);
306 // Set the new desktop shape region.
307 desktop_shape_
.Swap(new_desktop_shape
);
310 } // namespace remoting