summaryrefslogtreecommitdiffstats
path: root/remoting/codec/video_decoder_vp8.cc
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/codec/video_decoder_vp8.cc')
-rw-r--r--remoting/codec/video_decoder_vp8.cc211
1 files changed, 211 insertions, 0 deletions
diff --git a/remoting/codec/video_decoder_vp8.cc b/remoting/codec/video_decoder_vp8.cc
new file mode 100644
index 0000000..6a8d7c0
--- /dev/null
+++ b/remoting/codec/video_decoder_vp8.cc
@@ -0,0 +1,211 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/codec/video_decoder_vp8.h"
+
+#include <math.h>
+
+#include "base/logging.h"
+#include "media/base/media.h"
+#include "media/base/yuv_convert.h"
+#include "remoting/base/util.h"
+
+extern "C" {
+#define VPX_CODEC_DISABLE_COMPAT 1
+#include "third_party/libvpx/libvpx.h"
+}
+
+namespace remoting {
+
+DecoderVp8::DecoderVp8()
+ : state_(kUninitialized),
+ codec_(NULL),
+ last_image_(NULL),
+ screen_size_(SkISize::Make(0, 0)) {
+}
+
+DecoderVp8::~DecoderVp8() {
+ if (codec_) {
+ vpx_codec_err_t ret = vpx_codec_destroy(codec_);
+ CHECK(ret == VPX_CODEC_OK) << "Failed to destroy codec";
+ }
+ delete codec_;
+}
+
+void DecoderVp8::Initialize(const SkISize& screen_size) {
+ DCHECK(!screen_size.isEmpty());
+
+ screen_size_ = screen_size;
+ state_ = kReady;
+}
+
+Decoder::DecodeResult DecoderVp8::DecodePacket(const VideoPacket* packet) {
+ DCHECK_EQ(kReady, state_);
+
+ // Initialize the codec as needed.
+ if (!codec_) {
+ codec_ = new vpx_codec_ctx_t();
+
+ // TODO(hclam): Scale the number of threads with number of cores of the
+ // machine.
+ vpx_codec_dec_cfg config;
+ config.w = 0;
+ config.h = 0;
+ config.threads = 2;
+ vpx_codec_err_t ret =
+ vpx_codec_dec_init(
+ codec_, vpx_codec_vp8_dx(), &config, 0);
+ if (ret != VPX_CODEC_OK) {
+ LOG(INFO) << "Cannot initialize codec.";
+ delete codec_;
+ codec_ = NULL;
+ state_ = kError;
+ return DECODE_ERROR;
+ }
+ }
+
+ // Do the actual decoding.
+ vpx_codec_err_t ret = vpx_codec_decode(
+ codec_, reinterpret_cast<const uint8*>(packet->data().data()),
+ packet->data().size(), NULL, 0);
+ if (ret != VPX_CODEC_OK) {
+ LOG(INFO) << "Decoding failed:" << vpx_codec_err_to_string(ret) << "\n"
+ << "Details: " << vpx_codec_error(codec_) << "\n"
+ << vpx_codec_error_detail(codec_);
+ return DECODE_ERROR;
+ }
+
+ // Gets the decoded data.
+ vpx_codec_iter_t iter = NULL;
+ vpx_image_t* image = vpx_codec_get_frame(codec_, &iter);
+ if (!image) {
+ LOG(INFO) << "No video frame decoded";
+ return DECODE_ERROR;
+ }
+ last_image_ = image;
+
+ SkRegion region;
+ for (int i = 0; i < packet->dirty_rects_size(); ++i) {
+ Rect remoting_rect = packet->dirty_rects(i);
+ SkIRect rect = SkIRect::MakeXYWH(remoting_rect.x(),
+ remoting_rect.y(),
+ remoting_rect.width(),
+ remoting_rect.height());
+ region.op(rect, SkRegion::kUnion_Op);
+ }
+
+ updated_region_.op(region, SkRegion::kUnion_Op);
+ return DECODE_DONE;
+}
+
+bool DecoderVp8::IsReadyForData() {
+ return state_ == kReady;
+}
+
+VideoPacketFormat::Encoding DecoderVp8::Encoding() {
+ return VideoPacketFormat::ENCODING_VP8;
+}
+
+void DecoderVp8::Invalidate(const SkISize& view_size,
+ const SkRegion& region) {
+ DCHECK_EQ(kReady, state_);
+ DCHECK(!view_size.isEmpty());
+
+ for (SkRegion::Iterator i(region); !i.done(); i.next()) {
+ SkIRect rect = i.rect();
+ rect = ScaleRect(rect, view_size, screen_size_);
+ updated_region_.op(rect, SkRegion::kUnion_Op);
+ }
+}
+
+void DecoderVp8::RenderFrame(const SkISize& view_size,
+ const SkIRect& clip_area,
+ uint8* image_buffer,
+ int image_stride,
+ SkRegion* output_region) {
+ DCHECK_EQ(kReady, state_);
+ DCHECK(!view_size.isEmpty());
+
+ // Early-return and do nothing if we haven't yet decoded any frames.
+ if (!last_image_)
+ return;
+
+ SkIRect source_clip = SkIRect::MakeWH(last_image_->d_w, last_image_->d_h);
+
+ // ScaleYUVToRGB32WithRect does not currently support up-scaling. We won't
+ // be asked to up-scale except during resizes or if page zoom is >100%, so
+ // we work-around the limitation by using the slower ScaleYUVToRGB32.
+ // TODO(wez): Remove this hack if/when ScaleYUVToRGB32WithRect can up-scale.
+ if (!updated_region_.isEmpty() &&
+ (source_clip.width() < view_size.width() ||
+ source_clip.height() < view_size.height())) {
+ // We're scaling only |clip_area| into the |image_buffer|, so we need to
+ // work out which source rectangle that corresponds to.
+ SkIRect source_rect = ScaleRect(clip_area, view_size, screen_size_);
+ source_rect = SkIRect::MakeLTRB(RoundToTwosMultiple(source_rect.left()),
+ RoundToTwosMultiple(source_rect.top()),
+ source_rect.right(),
+ source_rect.bottom());
+
+ // If there were no changes within the clip source area then don't render.
+ if (!updated_region_.intersects(source_rect))
+ return;
+
+ // Scale & convert the entire clip area.
+ int y_offset = CalculateYOffset(source_rect.x(),
+ source_rect.y(),
+ last_image_->stride[0]);
+ int uv_offset = CalculateUVOffset(source_rect.x(),
+ source_rect.y(),
+ last_image_->stride[1]);
+ ScaleYUVToRGB32(last_image_->planes[0] + y_offset,
+ last_image_->planes[1] + uv_offset,
+ last_image_->planes[2] + uv_offset,
+ image_buffer,
+ source_rect.width(),
+ source_rect.height(),
+ clip_area.width(),
+ clip_area.height(),
+ last_image_->stride[0],
+ last_image_->stride[1],
+ image_stride,
+ media::YV12,
+ media::ROTATE_0,
+ media::FILTER_BILINEAR);
+
+ output_region->op(clip_area, SkRegion::kUnion_Op);
+ updated_region_.op(source_rect, SkRegion::kDifference_Op);
+ return;
+ }
+
+ for (SkRegion::Iterator i(updated_region_); !i.done(); i.next()) {
+ // Determine the scaled area affected by this rectangle changing.
+ SkIRect rect = i.rect();
+ if (!rect.intersect(source_clip))
+ continue;
+ rect = ScaleRect(rect, screen_size_, view_size);
+ if (!rect.intersect(clip_area))
+ continue;
+
+ ConvertAndScaleYUVToRGB32Rect(last_image_->planes[0],
+ last_image_->planes[1],
+ last_image_->planes[2],
+ last_image_->stride[0],
+ last_image_->stride[1],
+ screen_size_,
+ source_clip,
+ image_buffer,
+ image_stride,
+ view_size,
+ clip_area,
+ rect);
+
+ output_region->op(rect, SkRegion::kUnion_Op);
+ }
+
+ updated_region_.op(ScaleRect(clip_area, view_size, screen_size_),
+ SkRegion::kDifference_Op);
+}
+
+} // namespace remoting