| Index: media/cast/video_receiver/video_decoder.cc | 
| diff --git a/media/cast/video_receiver/video_decoder.cc b/media/cast/video_receiver/video_decoder.cc | 
| index f85e117232eda349a389b4a84276e735cf8beb89..0964c07ede7cb7979bcb78f1daf3de2d7e6289b8 100644 | 
| --- a/media/cast/video_receiver/video_decoder.cc | 
| +++ b/media/cast/video_receiver/video_decoder.cc | 
| @@ -4,38 +4,218 @@ | 
|  | 
| #include "media/cast/video_receiver/video_decoder.h" | 
|  | 
| -#include <stdint.h> | 
| - | 
| #include "base/bind.h" | 
| +#include "base/bind_helpers.h" | 
| +#include "base/location.h" | 
| #include "base/logging.h" | 
| -#include "base/message_loop/message_loop.h" | 
| -#include "media/cast/video_receiver/codecs/vp8/vp8_decoder.h" | 
| +#include "base/stl_util.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 { | 
|  | 
| -VideoDecoder::VideoDecoder(const VideoReceiverConfig& video_config, | 
| -                           scoped_refptr<CastEnvironment> cast_environment) | 
| -    : codec_(video_config.codec), vp8_decoder_() { | 
| +// 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::EncodedVideoFrame> encoded_frame, | 
| +                   const DecodeFrameCallback& callback) { | 
| +    DCHECK_EQ(cast_initialization_status_, STATUS_VIDEO_INITIALIZED); | 
| + | 
| +    if (encoded_frame->codec != codec_) { | 
| +      NOTREACHED(); | 
| +      cast_environment_->PostTask( | 
| +          CastEnvironment::MAIN, | 
| +          FROM_HERE, | 
| +          base::Bind(callback, scoped_refptr<VideoFrame>(NULL), false)); | 
| +    } | 
| + | 
| +    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( | 
| +        reinterpret_cast<uint8*>(string_as_array(&encoded_frame->data)), | 
| +        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); | 
| +}; | 
| + | 
| +VideoDecoder::VideoDecoder( | 
| +    const scoped_refptr<CastEnvironment>& cast_environment, | 
| +    const VideoReceiverConfig& video_config) | 
| +    : cast_environment_(cast_environment) { | 
| switch (video_config.codec) { | 
| case transport::kVp8: | 
| -      vp8_decoder_.reset(new Vp8Decoder(cast_environment)); | 
| +      impl_ = new Vp8Impl(cast_environment); | 
| break; | 
| case transport::kH264: | 
| +      // TODO(miu): Need implementation. | 
| NOTIMPLEMENTED(); | 
| break; | 
| +    default: | 
| +      NOTREACHED() << "Unknown or unspecified codec."; | 
| +      break; | 
| } | 
| } | 
|  | 
| VideoDecoder::~VideoDecoder() {} | 
|  | 
| -bool VideoDecoder::DecodeVideoFrame( | 
| -    const transport::EncodedVideoFrame* encoded_frame, | 
| -    const base::TimeTicks render_time, | 
| -    const VideoFrameDecodedCallback& frame_decoded_cb) { | 
| -  DCHECK(encoded_frame->codec == codec_) << "Invalid codec"; | 
| -  DCHECK_GT(encoded_frame->data.size(), UINT64_C(0)) << "Empty video frame"; | 
| -  return vp8_decoder_->Decode(encoded_frame, render_time, frame_decoded_cb); | 
| +CastInitializationStatus VideoDecoder::InitializationResult() const { | 
| +  if (impl_) | 
| +    return impl_->InitializationResult(); | 
| +  return STATUS_UNSUPPORTED_VIDEO_CODEC; | 
| +} | 
| + | 
| +void VideoDecoder::DecodeFrame( | 
| +    scoped_ptr<transport::EncodedVideoFrame> 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 | 
|  |