Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / remoting / codec / video_decoder_vpx.cc
blobe414ee8e5e7a026c9e35a6351b16881c4259c19c
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"
16 extern "C" {
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"
22 namespace remoting {
24 namespace {
26 const uint32 kTransparentColor = 0;
28 // Fills the rectangle |rect| with the given ARGB color |color| in |buffer|.
29 void FillRect(uint8* buffer,
30 int stride,
31 const webrtc::DesktopRect& rect,
32 uint32 color) {
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;
42 } // namespace
44 // static
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
49 // machine.
50 vpx_codec_dec_cfg config;
51 config.w = 0;
52 config.h = 0;
53 config.threads = 2;
54 vpx_codec_err_t ret =
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()));
64 // static
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
69 // machine.
70 vpx_codec_dec_cfg config;
71 config.w = 0;
72 config.h = 0;
73 config.threads = 2;
74 vpx_codec_err_t ret =
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)");
106 return false;
109 // Gets the decoded data.
110 vpx_codec_iter_t iter = NULL;
111 vpx_image_t* image = vpx_codec_get_frame(codec_.get(), &iter);
112 if (!image) {
113 LOG(ERROR) << "No video frame decoded";
114 return false;
116 last_image_ = image;
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()));
137 } else {
138 // Fallback for the case when the host didn't include the desktop shape
139 // region.
140 desktop_shape_region =
141 webrtc::DesktopRegion(webrtc::DesktopRect::MakeSize(screen_size_));
144 UpdateImageShapeRegion(&desktop_shape_region);
146 return true;
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,
167 uint8* image_buffer,
168 int image_stride,
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.
174 if (!last_image_)
175 return;
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()),
194 source_rect.right(),
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())
201 return;
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,
211 image_buffer,
212 source_rect.width(),
213 source_rect.height(),
214 clip_area.width(),
215 clip_area.height(),
216 last_image_->stride[0],
217 last_image_->stride[1],
218 image_stride,
219 media::YV12,
220 media::ROTATE_0,
221 media::FILTER_BILINEAR);
223 output_region->AddRect(clip_area);
224 updated_region_.Subtract(source_rect);
225 return;
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);
233 if (rect.is_empty())
234 continue;
235 rect = ScaleRect(rect, screen_size_, view_size);
236 rect.IntersectWith(clip_area);
237 if (rect.is_empty())
238 continue;
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],
245 screen_size_,
246 source_clip,
247 image_buffer,
248 image_stride,
249 view_size,
250 clip_area,
251 rect);
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);
263 if (rect.is_empty())
264 continue;
265 rect = ScaleRect(rect, screen_size_, view_size);
266 rect.IntersectWith(clip_area);
267 if (rect.is_empty())
268 continue;
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()),
287 last_image_(NULL) {
288 DCHECK(codec_);
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
300 // desktop shape.
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