Updating XTBs based on .GRDs from branch master
[chromium-blink-merge.git] / remoting / codec / video_decoder_vpx.cc
blobb14d88886cc688a7ec2d0cf510395622045140f9
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 void VideoDecoderVpx::Initialize(const webrtc::DesktopSize& source_size) {
38 // Nothing to do here; the codec handles resizing internally, and returns
39 // the source dimensions as part of the vpx_image_t.
42 bool VideoDecoderVpx::DecodePacket(const VideoPacket& packet) {
43 // Pass the packet to the codec to process.
44 vpx_codec_err_t ret = vpx_codec_decode(
45 codec_.get(), reinterpret_cast<const uint8*>(packet.data().data()),
46 packet.data().size(), nullptr, 0);
47 if (ret != VPX_CODEC_OK) {
48 const char* error = vpx_codec_error(codec_.get());
49 const char* error_detail = vpx_codec_error_detail(codec_.get());
50 LOG(ERROR) << "Decoding failed:" << (error ? error : "(NULL)") << "\n"
51 << "Details: " << (error_detail ? error_detail : "(NULL)");
52 return false;
55 // Fetch the decoded video frame.
56 vpx_codec_iter_t iter = nullptr;
57 image_ = vpx_codec_get_frame(codec_.get(), &iter);
58 if (!image_) {
59 LOG(ERROR) << "No video frame decoded";
60 return false;
62 DCHECK(!image_size().is_empty());
64 // Determine which areas have been updated.
65 webrtc::DesktopRegion region;
66 for (int i = 0; i < packet.dirty_rects_size(); ++i) {
67 Rect remoting_rect = packet.dirty_rects(i);
68 region.AddRect(webrtc::DesktopRect::MakeXYWH(
69 remoting_rect.x(), remoting_rect.y(),
70 remoting_rect.width(), remoting_rect.height()));
72 updated_region_.AddRegion(region);
74 // Process the frame shape, if supplied.
75 if (packet.has_use_desktop_shape()) {
76 if (packet.use_desktop_shape()) {
77 if (!desktop_shape_)
78 desktop_shape_ = make_scoped_ptr(new webrtc::DesktopRegion);
79 desktop_shape_->Clear();
80 for (int i = 0; i < packet.desktop_shape_rects_size(); ++i) {
81 Rect remoting_rect = packet.desktop_shape_rects(i);
82 desktop_shape_->AddRect(webrtc::DesktopRect::MakeXYWH(
83 remoting_rect.x(), remoting_rect.y(), remoting_rect.width(),
84 remoting_rect.height()));
86 } else {
87 desktop_shape_.reset();
91 return true;
94 void VideoDecoderVpx::Invalidate(const webrtc::DesktopSize& view_size,
95 const webrtc::DesktopRegion& region) {
96 DCHECK(!view_size.is_empty());
98 for (webrtc::DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) {
99 updated_region_.AddRect(ScaleRect(i.rect(), view_size, image_size()));
103 void VideoDecoderVpx::RenderFrame(const webrtc::DesktopSize& view_size,
104 const webrtc::DesktopRect& clip_area,
105 uint8* image_buffer,
106 int image_stride,
107 webrtc::DesktopRegion* output_region) {
108 DCHECK(!image_size().is_empty());
109 DCHECK(!view_size.is_empty());
111 // Early-return and do nothing if we haven't yet decoded any frames.
112 if (!image_)
113 return;
115 webrtc::DesktopRect source_clip = webrtc::DesktopRect::MakeSize(image_size());
117 // VP8 only outputs I420 frames, but VP9 can also produce I444.
118 switch (image_->fmt) {
119 case VPX_IMG_FMT_I444: {
120 // TODO(wez): Add scaling support to the I444 conversion path.
121 if (view_size.equals(image_size())) {
122 for (webrtc::DesktopRegion::Iterator i(updated_region_);
123 !i.IsAtEnd(); i.Advance()) {
124 // Determine the scaled area affected by this rectangle changing.
125 webrtc::DesktopRect rect = i.rect();
126 rect.IntersectWith(source_clip);
127 rect.IntersectWith(clip_area);
128 if (rect.is_empty())
129 continue;
131 int image_offset = image_stride * rect.top() +
132 rect.left() * VideoDecoder::kBytesPerPixel;
133 int y_offset = image_->stride[0] * rect.top() + rect.left();
134 int u_offset = image_->stride[1] * rect.top() + rect.left();
135 int v_offset = image_->stride[2] * rect.top() + rect.left();
136 libyuv::I444ToARGB(image_->planes[0] + y_offset, image_->stride[0],
137 image_->planes[1] + u_offset, image_->stride[1],
138 image_->planes[2] + v_offset, image_->stride[2],
139 image_buffer + image_offset, image_stride,
140 rect.width(), rect.height());
142 output_region->AddRect(rect);
145 break;
147 case VPX_IMG_FMT_I420: {
148 // ScaleYUVToRGB32WithRect does not currently support up-scaling. We
149 // won't be asked to up-scale except during resizes or if page zoom is
150 // >100%, so we work-around the limitation by using the slower
151 // ScaleYUVToRGB32.
152 // TODO(wez): Remove this hack if/when ScaleYUVToRGB32WithRect can
153 // up-scale.
154 if (!updated_region_.is_empty() &&
155 (source_clip.width() < view_size.width() ||
156 source_clip.height() < view_size.height())) {
157 // We're scaling only |clip_area| into the |image_buffer|, so we need to
158 // work out which source rectangle that corresponds to.
159 webrtc::DesktopRect source_rect =
160 ScaleRect(clip_area, view_size, image_size());
161 source_rect = webrtc::DesktopRect::MakeLTRB(
162 RoundToTwosMultiple(source_rect.left()),
163 RoundToTwosMultiple(source_rect.top()),
164 source_rect.right(),
165 source_rect.bottom());
167 // If there were no changes within the clip source area then don't
168 // render.
169 webrtc::DesktopRegion intersection(source_rect);
170 intersection.IntersectWith(updated_region_);
171 if (intersection.is_empty())
172 return;
174 // Scale & convert the entire clip area.
175 int y_offset = CalculateYOffset(source_rect.left(), source_rect.top(),
176 image_->stride[0]);
177 int uv_offset = CalculateUVOffset(source_rect.left(), source_rect.top(),
178 image_->stride[1]);
179 ScaleYUVToRGB32(
180 image_->planes[0] + y_offset, image_->planes[1] + uv_offset,
181 image_->planes[2] + uv_offset, image_buffer, source_rect.width(),
182 source_rect.height(), clip_area.width(), clip_area.height(),
183 image_->stride[0], image_->stride[1], image_stride, media::YV12,
184 media::ROTATE_0, media::FILTER_BILINEAR);
186 output_region->AddRect(clip_area);
187 updated_region_.Subtract(source_rect);
188 return;
191 for (webrtc::DesktopRegion::Iterator i(updated_region_);
192 !i.IsAtEnd(); i.Advance()) {
193 // Determine the scaled area affected by this rectangle changing.
194 webrtc::DesktopRect rect = i.rect();
195 rect.IntersectWith(source_clip);
196 if (rect.is_empty())
197 continue;
198 rect = ScaleRect(rect, image_size(), view_size);
199 rect.IntersectWith(clip_area);
200 if (rect.is_empty())
201 continue;
203 ConvertAndScaleYUVToRGB32Rect(
204 image_->planes[0], image_->planes[1], image_->planes[2],
205 image_->stride[0], image_->stride[1], image_size(), source_clip,
206 image_buffer, image_stride, view_size, clip_area, rect);
208 output_region->AddRect(rect);
211 updated_region_.Subtract(ScaleRect(clip_area, view_size, image_size()));
212 break;
214 default: {
215 LOG(ERROR) << "Unsupported image format:" << image_->fmt;
216 return;
220 webrtc::DesktopRect scaled_clip_area =
221 ScaleRect(clip_area, view_size, image_size());
222 updated_region_.Subtract(scaled_clip_area);
225 const webrtc::DesktopRegion* VideoDecoderVpx::GetImageShape() {
226 return desktop_shape_.get();
229 VideoDecoderVpx::VideoDecoderVpx(vpx_codec_iface_t* codec) : image_(nullptr) {
230 codec_.reset(new vpx_codec_ctx_t);
232 vpx_codec_dec_cfg config;
233 config.w = 0;
234 config.h = 0;
235 config.threads = 2;
236 vpx_codec_err_t ret = vpx_codec_dec_init(codec_.get(), codec, &config, 0);
237 CHECK_EQ(VPX_CODEC_OK, ret);
240 webrtc::DesktopSize VideoDecoderVpx::image_size() const {
241 return image_ ? webrtc::DesktopSize(image_->d_w, image_->d_h)
242 : webrtc::DesktopSize();
245 } // namespace remoting