summaryrefslogtreecommitdiffstats
path: root/chromium/media/cast/receiver/video_decoder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/cast/receiver/video_decoder.cc')
-rw-r--r--chromium/media/cast/receiver/video_decoder.cc259
1 files changed, 259 insertions, 0 deletions
diff --git a/chromium/media/cast/receiver/video_decoder.cc b/chromium/media/cast/receiver/video_decoder.cc
new file mode 100644
index 00000000000..6db3fd35f39
--- /dev/null
+++ b/chromium/media/cast/receiver/video_decoder.cc
@@ -0,0 +1,259 @@
+// Copyright 2014 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 "media/cast/receiver/video_decoder.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/json/json_reader.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "media/base/video_util.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/cast_environment.h"
+// VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide
+// backwards compatibility for legacy applications using the library.
+#define VPX_CODEC_DISABLE_COMPAT 1
+#include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
+#include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
+#include "ui/gfx/size.h"
+
+namespace media {
+namespace cast {
+
+// Base class that handles the common problem of detecting dropped frames, and
+// then invoking the Decode() method implemented by the subclasses to convert
+// the encoded payload data into a usable video frame.
+class VideoDecoder::ImplBase
+ : public base::RefCountedThreadSafe<VideoDecoder::ImplBase> {
+ public:
+ ImplBase(const scoped_refptr<CastEnvironment>& cast_environment,
+ transport::VideoCodec codec)
+ : cast_environment_(cast_environment),
+ codec_(codec),
+ cast_initialization_status_(STATUS_VIDEO_UNINITIALIZED),
+ seen_first_frame_(false) {}
+
+ CastInitializationStatus InitializationResult() const {
+ return cast_initialization_status_;
+ }
+
+ void DecodeFrame(scoped_ptr<transport::EncodedFrame> encoded_frame,
+ const DecodeFrameCallback& callback) {
+ DCHECK_EQ(cast_initialization_status_, STATUS_VIDEO_INITIALIZED);
+
+ COMPILE_ASSERT(sizeof(encoded_frame->frame_id) == sizeof(last_frame_id_),
+ size_of_frame_id_types_do_not_match);
+ bool is_continuous = true;
+ if (seen_first_frame_) {
+ const uint32 frames_ahead = encoded_frame->frame_id - last_frame_id_;
+ if (frames_ahead > 1) {
+ RecoverBecauseFramesWereDropped();
+ is_continuous = false;
+ }
+ } else {
+ seen_first_frame_ = true;
+ }
+ last_frame_id_ = encoded_frame->frame_id;
+
+ const scoped_refptr<VideoFrame> decoded_frame = Decode(
+ encoded_frame->mutable_bytes(),
+ static_cast<int>(encoded_frame->data.size()));
+ cast_environment_->PostTask(
+ CastEnvironment::MAIN,
+ FROM_HERE,
+ base::Bind(callback, decoded_frame, is_continuous));
+ }
+
+ protected:
+ friend class base::RefCountedThreadSafe<ImplBase>;
+ virtual ~ImplBase() {}
+
+ virtual void RecoverBecauseFramesWereDropped() {}
+
+ // Note: Implementation of Decode() is allowed to mutate |data|.
+ virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) = 0;
+
+ const scoped_refptr<CastEnvironment> cast_environment_;
+ const transport::VideoCodec codec_;
+
+ // Subclass' ctor is expected to set this to STATUS_VIDEO_INITIALIZED.
+ CastInitializationStatus cast_initialization_status_;
+
+ private:
+ bool seen_first_frame_;
+ uint32 last_frame_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImplBase);
+};
+
+class VideoDecoder::Vp8Impl : public VideoDecoder::ImplBase {
+ public:
+ explicit Vp8Impl(const scoped_refptr<CastEnvironment>& cast_environment)
+ : ImplBase(cast_environment, transport::kVp8) {
+ if (ImplBase::cast_initialization_status_ != STATUS_VIDEO_UNINITIALIZED)
+ return;
+
+ vpx_codec_dec_cfg_t cfg = {0};
+ // TODO(miu): Revisit this for typical multi-core desktop use case. This
+ // feels like it should be 4 or 8.
+ cfg.threads = 1;
+
+ DCHECK(vpx_codec_get_caps(vpx_codec_vp8_dx()) & VPX_CODEC_CAP_POSTPROC);
+ if (vpx_codec_dec_init(&context_,
+ vpx_codec_vp8_dx(),
+ &cfg,
+ VPX_CODEC_USE_POSTPROC) != VPX_CODEC_OK) {
+ ImplBase::cast_initialization_status_ =
+ STATUS_INVALID_VIDEO_CONFIGURATION;
+ return;
+ }
+ ImplBase::cast_initialization_status_ = STATUS_VIDEO_INITIALIZED;
+ }
+
+ private:
+ virtual ~Vp8Impl() {
+ if (ImplBase::cast_initialization_status_ == STATUS_VIDEO_INITIALIZED)
+ CHECK_EQ(VPX_CODEC_OK, vpx_codec_destroy(&context_));
+ }
+
+ virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) OVERRIDE {
+ if (len <= 0 || vpx_codec_decode(&context_,
+ data,
+ static_cast<unsigned int>(len),
+ NULL,
+ 0) != VPX_CODEC_OK) {
+ return NULL;
+ }
+
+ vpx_codec_iter_t iter = NULL;
+ vpx_image_t* const image = vpx_codec_get_frame(&context_, &iter);
+ if (!image)
+ return NULL;
+ if (image->fmt != VPX_IMG_FMT_I420 && image->fmt != VPX_IMG_FMT_YV12) {
+ NOTREACHED();
+ return NULL;
+ }
+ DCHECK(vpx_codec_get_frame(&context_, &iter) == NULL)
+ << "Should have only decoded exactly one frame.";
+
+ const gfx::Size frame_size(image->d_w, image->d_h);
+ // Note: Timestamp for the VideoFrame will be set in VideoReceiver.
+ const scoped_refptr<VideoFrame> decoded_frame =
+ VideoFrame::CreateFrame(VideoFrame::YV12,
+ frame_size,
+ gfx::Rect(frame_size),
+ frame_size,
+ base::TimeDelta());
+ CopyYPlane(image->planes[VPX_PLANE_Y],
+ image->stride[VPX_PLANE_Y],
+ image->d_h,
+ decoded_frame);
+ CopyUPlane(image->planes[VPX_PLANE_U],
+ image->stride[VPX_PLANE_U],
+ (image->d_h + 1) / 2,
+ decoded_frame);
+ CopyVPlane(image->planes[VPX_PLANE_V],
+ image->stride[VPX_PLANE_V],
+ (image->d_h + 1) / 2,
+ decoded_frame);
+ return decoded_frame;
+ }
+
+ // VPX decoder context (i.e., an instantiation).
+ vpx_codec_ctx_t context_;
+
+ DISALLOW_COPY_AND_ASSIGN(Vp8Impl);
+};
+
+#ifndef OFFICIAL_BUILD
+// A fake video decoder that always output 2x2 black frames.
+class VideoDecoder::FakeImpl : public VideoDecoder::ImplBase {
+ public:
+ explicit FakeImpl(const scoped_refptr<CastEnvironment>& cast_environment)
+ : ImplBase(cast_environment, transport::kFakeSoftwareVideo),
+ last_decoded_id_(-1) {
+ if (ImplBase::cast_initialization_status_ != STATUS_VIDEO_UNINITIALIZED)
+ return;
+ ImplBase::cast_initialization_status_ = STATUS_VIDEO_INITIALIZED;
+ }
+
+ private:
+ virtual ~FakeImpl() {}
+
+ virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) OVERRIDE {
+ base::JSONReader reader;
+ scoped_ptr<base::Value> values(
+ reader.Read(base::StringPiece(reinterpret_cast<char*>(data))));
+ base::DictionaryValue* dict = NULL;
+ values->GetAsDictionary(&dict);
+
+ bool key = false;
+ int id = 0;
+ int ref = 0;
+ dict->GetBoolean("key", &key);
+ dict->GetInteger("id", &id);
+ dict->GetInteger("ref", &ref);
+ DCHECK(id == last_decoded_id_ + 1);
+ last_decoded_id_ = id;
+ return media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2));
+ }
+
+ int last_decoded_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeImpl);
+};
+#endif
+
+VideoDecoder::VideoDecoder(
+ const scoped_refptr<CastEnvironment>& cast_environment,
+ transport::VideoCodec codec)
+ : cast_environment_(cast_environment) {
+ switch (codec) {
+#ifndef OFFICIAL_BUILD
+ case transport::kFakeSoftwareVideo:
+ impl_ = new FakeImpl(cast_environment);
+ break;
+#endif
+ case transport::kVp8:
+ impl_ = new Vp8Impl(cast_environment);
+ break;
+ case transport::kH264:
+ // TODO(miu): Need implementation.
+ NOTIMPLEMENTED();
+ break;
+ default:
+ NOTREACHED() << "Unknown or unspecified codec.";
+ break;
+ }
+}
+
+VideoDecoder::~VideoDecoder() {}
+
+CastInitializationStatus VideoDecoder::InitializationResult() const {
+ if (impl_)
+ return impl_->InitializationResult();
+ return STATUS_UNSUPPORTED_VIDEO_CODEC;
+}
+
+void VideoDecoder::DecodeFrame(
+ scoped_ptr<transport::EncodedFrame> encoded_frame,
+ const DecodeFrameCallback& callback) {
+ DCHECK(encoded_frame.get());
+ DCHECK(!callback.is_null());
+ if (!impl_ || impl_->InitializationResult() != STATUS_VIDEO_INITIALIZED) {
+ callback.Run(make_scoped_refptr<VideoFrame>(NULL), false);
+ return;
+ }
+ cast_environment_->PostTask(CastEnvironment::VIDEO,
+ FROM_HERE,
+ base::Bind(&VideoDecoder::ImplBase::DecodeFrame,
+ impl_,
+ base::Passed(&encoded_frame),
+ callback));
+}
+
+} // namespace cast
+} // namespace media