| OLD | NEW | 
|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "media/cast/video_receiver/video_decoder.h" | 5 #include "media/cast/video_receiver/video_decoder.h" | 
| 6 | 6 | 
| 7 #include <stdint.h> |  | 
| 8 |  | 
| 9 #include "base/bind.h" | 7 #include "base/bind.h" | 
|  | 8 #include "base/bind_helpers.h" | 
|  | 9 #include "base/location.h" | 
| 10 #include "base/logging.h" | 10 #include "base/logging.h" | 
| 11 #include "base/message_loop/message_loop.h" | 11 #include "base/stl_util.h" | 
| 12 #include "media/cast/video_receiver/codecs/vp8/vp8_decoder.h" | 12 #include "media/base/video_util.h" | 
|  | 13 #include "media/cast/cast_defines.h" | 
|  | 14 #include "media/cast/cast_environment.h" | 
|  | 15 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide | 
|  | 16 // backwards compatibility for legacy applications using the library. | 
|  | 17 #define VPX_CODEC_DISABLE_COMPAT 1 | 
|  | 18 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h" | 
|  | 19 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h" | 
|  | 20 #include "ui/gfx/size.h" | 
| 13 | 21 | 
| 14 namespace media { | 22 namespace media { | 
| 15 namespace cast { | 23 namespace cast { | 
| 16 | 24 | 
| 17 VideoDecoder::VideoDecoder(const VideoReceiverConfig& video_config, | 25 // Base class that handles the common problem of detecting dropped frames, and | 
| 18                            scoped_refptr<CastEnvironment> cast_environment) | 26 // then invoking the Decode() method implemented by the subclasses to convert | 
| 19     : codec_(video_config.codec), vp8_decoder_() { | 27 // the encoded payload data into a usable video frame. | 
|  | 28 class VideoDecoder::ImplBase | 
|  | 29     : public base::RefCountedThreadSafe<VideoDecoder::ImplBase> { | 
|  | 30  public: | 
|  | 31   ImplBase(const scoped_refptr<CastEnvironment>& cast_environment, | 
|  | 32            transport::VideoCodec codec) | 
|  | 33       : cast_environment_(cast_environment), | 
|  | 34         codec_(codec), | 
|  | 35         cast_initialization_status_(STATUS_VIDEO_UNINITIALIZED), | 
|  | 36         seen_first_frame_(false) {} | 
|  | 37 | 
|  | 38   CastInitializationStatus InitializationResult() const { | 
|  | 39     return cast_initialization_status_; | 
|  | 40   } | 
|  | 41 | 
|  | 42   void DecodeFrame(scoped_ptr<transport::EncodedVideoFrame> encoded_frame, | 
|  | 43                    const DecodeFrameCallback& callback) { | 
|  | 44     DCHECK_EQ(cast_initialization_status_, STATUS_VIDEO_INITIALIZED); | 
|  | 45 | 
|  | 46     if (encoded_frame->codec != codec_) { | 
|  | 47       NOTREACHED(); | 
|  | 48       cast_environment_->PostTask( | 
|  | 49           CastEnvironment::MAIN, | 
|  | 50           FROM_HERE, | 
|  | 51           base::Bind(callback, scoped_refptr<VideoFrame>(NULL), false)); | 
|  | 52     } | 
|  | 53 | 
|  | 54     COMPILE_ASSERT(sizeof(encoded_frame->frame_id) == sizeof(last_frame_id_), | 
|  | 55                    size_of_frame_id_types_do_not_match); | 
|  | 56     bool is_continuous = true; | 
|  | 57     if (seen_first_frame_) { | 
|  | 58       const uint32 frames_ahead = encoded_frame->frame_id - last_frame_id_; | 
|  | 59       if (frames_ahead > 1) { | 
|  | 60         RecoverBecauseFramesWereDropped(); | 
|  | 61         is_continuous = false; | 
|  | 62       } | 
|  | 63     } else { | 
|  | 64       seen_first_frame_ = true; | 
|  | 65     } | 
|  | 66     last_frame_id_ = encoded_frame->frame_id; | 
|  | 67 | 
|  | 68     const scoped_refptr<VideoFrame> decoded_frame = Decode( | 
|  | 69         reinterpret_cast<uint8*>(string_as_array(&encoded_frame->data)), | 
|  | 70         static_cast<int>(encoded_frame->data.size())); | 
|  | 71     cast_environment_->PostTask( | 
|  | 72         CastEnvironment::MAIN, | 
|  | 73         FROM_HERE, | 
|  | 74         base::Bind(callback, decoded_frame, is_continuous)); | 
|  | 75   } | 
|  | 76 | 
|  | 77  protected: | 
|  | 78   friend class base::RefCountedThreadSafe<ImplBase>; | 
|  | 79   virtual ~ImplBase() {} | 
|  | 80 | 
|  | 81   virtual void RecoverBecauseFramesWereDropped() {} | 
|  | 82 | 
|  | 83   // Note: Implementation of Decode() is allowed to mutate |data|. | 
|  | 84   virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) = 0; | 
|  | 85 | 
|  | 86   const scoped_refptr<CastEnvironment> cast_environment_; | 
|  | 87   const transport::VideoCodec codec_; | 
|  | 88 | 
|  | 89   // Subclass' ctor is expected to set this to STATUS_VIDEO_INITIALIZED. | 
|  | 90   CastInitializationStatus cast_initialization_status_; | 
|  | 91 | 
|  | 92  private: | 
|  | 93   bool seen_first_frame_; | 
|  | 94   uint32 last_frame_id_; | 
|  | 95 | 
|  | 96   DISALLOW_COPY_AND_ASSIGN(ImplBase); | 
|  | 97 }; | 
|  | 98 | 
|  | 99 class VideoDecoder::Vp8Impl : public VideoDecoder::ImplBase { | 
|  | 100  public: | 
|  | 101   explicit Vp8Impl(const scoped_refptr<CastEnvironment>& cast_environment) | 
|  | 102       : ImplBase(cast_environment, transport::kVp8) { | 
|  | 103     if (ImplBase::cast_initialization_status_ != STATUS_VIDEO_UNINITIALIZED) | 
|  | 104       return; | 
|  | 105 | 
|  | 106     vpx_codec_dec_cfg_t cfg = {0}; | 
|  | 107     // TODO(miu): Revisit this for typical multi-core desktop use case.  This | 
|  | 108     // feels like it should be 4 or 8. | 
|  | 109     cfg.threads = 1; | 
|  | 110 | 
|  | 111     DCHECK(vpx_codec_get_caps(vpx_codec_vp8_dx()) & VPX_CODEC_CAP_POSTPROC); | 
|  | 112     if (vpx_codec_dec_init(&context_, | 
|  | 113                            vpx_codec_vp8_dx(), | 
|  | 114                            &cfg, | 
|  | 115                            VPX_CODEC_USE_POSTPROC) != VPX_CODEC_OK) { | 
|  | 116       ImplBase::cast_initialization_status_ = | 
|  | 117           STATUS_INVALID_VIDEO_CONFIGURATION; | 
|  | 118       return; | 
|  | 119     } | 
|  | 120     ImplBase::cast_initialization_status_ = STATUS_VIDEO_INITIALIZED; | 
|  | 121   } | 
|  | 122 | 
|  | 123  private: | 
|  | 124   virtual ~Vp8Impl() { | 
|  | 125     if (ImplBase::cast_initialization_status_ == STATUS_VIDEO_INITIALIZED) | 
|  | 126       CHECK_EQ(VPX_CODEC_OK, vpx_codec_destroy(&context_)); | 
|  | 127   } | 
|  | 128 | 
|  | 129   virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) OVERRIDE { | 
|  | 130     if (len <= 0 || vpx_codec_decode(&context_, | 
|  | 131                                      data, | 
|  | 132                                      static_cast<unsigned int>(len), | 
|  | 133                                      NULL, | 
|  | 134                                      0) != VPX_CODEC_OK) { | 
|  | 135       return NULL; | 
|  | 136     } | 
|  | 137 | 
|  | 138     vpx_codec_iter_t iter = NULL; | 
|  | 139     vpx_image_t* const image = vpx_codec_get_frame(&context_, &iter); | 
|  | 140     if (!image) | 
|  | 141       return NULL; | 
|  | 142     if (image->fmt != VPX_IMG_FMT_I420 && image->fmt != VPX_IMG_FMT_YV12) { | 
|  | 143       NOTREACHED(); | 
|  | 144       return NULL; | 
|  | 145     } | 
|  | 146     DCHECK(vpx_codec_get_frame(&context_, &iter) == NULL) | 
|  | 147         << "Should have only decoded exactly one frame."; | 
|  | 148 | 
|  | 149     const gfx::Size frame_size(image->d_w, image->d_h); | 
|  | 150     // Note: Timestamp for the VideoFrame will be set in VideoReceiver. | 
|  | 151     const scoped_refptr<VideoFrame> decoded_frame = | 
|  | 152         VideoFrame::CreateFrame(VideoFrame::YV12, | 
|  | 153                                 frame_size, | 
|  | 154                                 gfx::Rect(frame_size), | 
|  | 155                                 frame_size, | 
|  | 156                                 base::TimeDelta()); | 
|  | 157     CopyYPlane(image->planes[VPX_PLANE_Y], | 
|  | 158                image->stride[VPX_PLANE_Y], | 
|  | 159                image->d_h, | 
|  | 160                decoded_frame); | 
|  | 161     CopyUPlane(image->planes[VPX_PLANE_U], | 
|  | 162                image->stride[VPX_PLANE_U], | 
|  | 163                (image->d_h + 1) / 2, | 
|  | 164                decoded_frame); | 
|  | 165     CopyVPlane(image->planes[VPX_PLANE_V], | 
|  | 166                image->stride[VPX_PLANE_V], | 
|  | 167                (image->d_h + 1) / 2, | 
|  | 168                decoded_frame); | 
|  | 169     return decoded_frame; | 
|  | 170   } | 
|  | 171 | 
|  | 172   // VPX decoder context (i.e., an instantiation). | 
|  | 173   vpx_codec_ctx_t context_; | 
|  | 174 | 
|  | 175   DISALLOW_COPY_AND_ASSIGN(Vp8Impl); | 
|  | 176 }; | 
|  | 177 | 
|  | 178 VideoDecoder::VideoDecoder( | 
|  | 179     const scoped_refptr<CastEnvironment>& cast_environment, | 
|  | 180     const VideoReceiverConfig& video_config) | 
|  | 181     : cast_environment_(cast_environment) { | 
| 20   switch (video_config.codec) { | 182   switch (video_config.codec) { | 
| 21     case transport::kVp8: | 183     case transport::kVp8: | 
| 22       vp8_decoder_.reset(new Vp8Decoder(cast_environment)); | 184       impl_ = new Vp8Impl(cast_environment); | 
| 23       break; | 185       break; | 
| 24     case transport::kH264: | 186     case transport::kH264: | 
|  | 187       // TODO(miu): Need implementation. | 
| 25       NOTIMPLEMENTED(); | 188       NOTIMPLEMENTED(); | 
| 26       break; | 189       break; | 
|  | 190     default: | 
|  | 191       NOTREACHED() << "Unknown or unspecified codec."; | 
|  | 192       break; | 
| 27   } | 193   } | 
| 28 } | 194 } | 
| 29 | 195 | 
| 30 VideoDecoder::~VideoDecoder() {} | 196 VideoDecoder::~VideoDecoder() {} | 
| 31 | 197 | 
| 32 bool VideoDecoder::DecodeVideoFrame( | 198 CastInitializationStatus VideoDecoder::InitializationResult() const { | 
| 33     const transport::EncodedVideoFrame* encoded_frame, | 199   if (impl_) | 
| 34     const base::TimeTicks render_time, | 200     return impl_->InitializationResult(); | 
| 35     const VideoFrameDecodedCallback& frame_decoded_cb) { | 201   return STATUS_UNSUPPORTED_VIDEO_CODEC; | 
| 36   DCHECK(encoded_frame->codec == codec_) << "Invalid codec"; |  | 
| 37   DCHECK_GT(encoded_frame->data.size(), UINT64_C(0)) << "Empty video frame"; |  | 
| 38   return vp8_decoder_->Decode(encoded_frame, render_time, frame_decoded_cb); |  | 
| 39 } | 202 } | 
| 40 | 203 | 
|  | 204 void VideoDecoder::DecodeFrame( | 
|  | 205     scoped_ptr<transport::EncodedVideoFrame> encoded_frame, | 
|  | 206     const DecodeFrameCallback& callback) { | 
|  | 207   DCHECK(encoded_frame.get()); | 
|  | 208   DCHECK(!callback.is_null()); | 
|  | 209   if (!impl_ || impl_->InitializationResult() != STATUS_VIDEO_INITIALIZED) { | 
|  | 210     callback.Run(make_scoped_refptr<VideoFrame>(NULL), false); | 
|  | 211     return; | 
|  | 212   } | 
|  | 213   cast_environment_->PostTask(CastEnvironment::VIDEO, | 
|  | 214                               FROM_HERE, | 
|  | 215                               base::Bind(&VideoDecoder::ImplBase::DecodeFrame, | 
|  | 216                                          impl_, | 
|  | 217                                          base::Passed(&encoded_frame), | 
|  | 218                                          callback)); | 
|  | 219 } | 
|  | 220 | 
| 41 }  // namespace cast | 221 }  // namespace cast | 
| 42 }  // namespace media | 222 }  // namespace media | 
| OLD | NEW | 
|---|