Roll src/third_party/WebKit 176b227:44a3ab6 (svn 200849:200851)
[chromium-blink-merge.git] / remoting / codec / video_decoder_vpx.cc
bloba54c704ad1e0061b38b2eb29c0f2b84eea13e160
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"
7 #include <math.h>
9 #include <algorithm>
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"
17 extern "C" {
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"
23 namespace remoting {
25 // static
26 scoped_ptr<VideoDecoderVpx> VideoDecoderVpx::CreateForVP8() {
27 return make_scoped_ptr(new VideoDecoderVpx(vpx_codec_vp8_dx()));
30 // static
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)");
47 return false;
50 // Fetch the decoded video frame.
51 vpx_codec_iter_t iter = nullptr;
52 image_ = vpx_codec_get_frame(codec_.get(), &iter);
53 if (!image_) {
54 LOG(ERROR) << "No video frame decoded";
55 return false;
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()) {
72 if (!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()));
81 } else {
82 desktop_shape_.reset();
86 return true;
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,
100 uint8* image_buffer,
101 int image_stride,
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.
107 if (!image_)
108 return;
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);
123 if (rect.is_empty())
124 continue;
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);
140 break;
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
146 // ScaleYUVToRGB32.
147 // TODO(wez): Remove this hack if/when ScaleYUVToRGB32WithRect can
148 // up-scale.
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()),
159 source_rect.right(),
160 source_rect.bottom());
162 // If there were no changes within the clip source area then don't
163 // render.
164 webrtc::DesktopRegion intersection(source_rect);
165 intersection.IntersectWith(updated_region_);
166 if (intersection.is_empty())
167 return;
169 // Scale & convert the entire clip area.
170 int y_offset = CalculateYOffset(source_rect.left(), source_rect.top(),
171 image_->stride[0]);
172 int uv_offset = CalculateUVOffset(source_rect.left(), source_rect.top(),
173 image_->stride[1]);
174 ScaleYUVToRGB32(
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);
183 return;
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);
191 if (rect.is_empty())
192 continue;
193 rect = ScaleRect(rect, image_size(), view_size);
194 rect.IntersectWith(clip_area);
195 if (rect.is_empty())
196 continue;
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()));
207 break;
209 default: {
210 LOG(ERROR) << "Unsupported image format:" << image_->fmt;
211 return;
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;
228 config.w = 0;
229 config.h = 0;
230 config.threads = 2;
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