Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / media / cast / receiver / video_decoder.cc
1 // Copyright 2014 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.
4
5 #include "media/cast/receiver/video_decoder.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/json/json_reader.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/values.h"
13 #include "media/base/video_util.h"
14 #include "media/cast/cast_defines.h"
15 #include "media/cast/cast_environment.h"
16 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide
17 // backwards compatibility for legacy applications using the library.
18 #define VPX_CODEC_DISABLE_COMPAT 1
19 #include "third_party/libvpx/source/libvpx/vpx/vp8dx.h"
20 #include "third_party/libvpx/source/libvpx/vpx/vpx_decoder.h"
21 #include "ui/gfx/size.h"
22
23 namespace media {
24 namespace cast {
25
26 // Base class that handles the common problem of detecting dropped frames, and
27 // then invoking the Decode() method implemented by the subclasses to convert
28 // the encoded payload data into a usable video frame.
29 class VideoDecoder::ImplBase
30     : public base::RefCountedThreadSafe<VideoDecoder::ImplBase> {
31  public:
32   ImplBase(const scoped_refptr<CastEnvironment>& cast_environment,
33            Codec codec)
34       : cast_environment_(cast_environment),
35         codec_(codec),
36         cast_initialization_status_(STATUS_VIDEO_UNINITIALIZED),
37         seen_first_frame_(false) {}
38
39   CastInitializationStatus InitializationResult() const {
40     return cast_initialization_status_;
41   }
42
43   void DecodeFrame(scoped_ptr<EncodedFrame> encoded_frame,
44                    const DecodeFrameCallback& callback) {
45     DCHECK_EQ(cast_initialization_status_, STATUS_VIDEO_INITIALIZED);
46
47     COMPILE_ASSERT(sizeof(encoded_frame->frame_id) == sizeof(last_frame_id_),
48                    size_of_frame_id_types_do_not_match);
49     bool is_continuous = true;
50     if (seen_first_frame_) {
51       const uint32 frames_ahead = encoded_frame->frame_id - last_frame_id_;
52       if (frames_ahead > 1) {
53         RecoverBecauseFramesWereDropped();
54         is_continuous = false;
55       }
56     } else {
57       seen_first_frame_ = true;
58     }
59     last_frame_id_ = encoded_frame->frame_id;
60
61     const scoped_refptr<VideoFrame> decoded_frame = Decode(
62         encoded_frame->mutable_bytes(),
63         static_cast<int>(encoded_frame->data.size()));
64     cast_environment_->PostTask(
65         CastEnvironment::MAIN,
66         FROM_HERE,
67         base::Bind(callback, decoded_frame, is_continuous));
68   }
69
70  protected:
71   friend class base::RefCountedThreadSafe<ImplBase>;
72   virtual ~ImplBase() {}
73
74   virtual void RecoverBecauseFramesWereDropped() {}
75
76   // Note: Implementation of Decode() is allowed to mutate |data|.
77   virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) = 0;
78
79   const scoped_refptr<CastEnvironment> cast_environment_;
80   const Codec codec_;
81
82   // Subclass' ctor is expected to set this to STATUS_VIDEO_INITIALIZED.
83   CastInitializationStatus cast_initialization_status_;
84
85  private:
86   bool seen_first_frame_;
87   uint32 last_frame_id_;
88
89   DISALLOW_COPY_AND_ASSIGN(ImplBase);
90 };
91
92 class VideoDecoder::Vp8Impl : public VideoDecoder::ImplBase {
93  public:
94   explicit Vp8Impl(const scoped_refptr<CastEnvironment>& cast_environment)
95       : ImplBase(cast_environment, CODEC_VIDEO_VP8) {
96     if (ImplBase::cast_initialization_status_ != STATUS_VIDEO_UNINITIALIZED)
97       return;
98
99     vpx_codec_dec_cfg_t cfg = {0};
100     // TODO(miu): Revisit this for typical multi-core desktop use case.  This
101     // feels like it should be 4 or 8.
102     cfg.threads = 1;
103
104     DCHECK(vpx_codec_get_caps(vpx_codec_vp8_dx()) & VPX_CODEC_CAP_POSTPROC);
105     if (vpx_codec_dec_init(&context_,
106                            vpx_codec_vp8_dx(),
107                            &cfg,
108                            VPX_CODEC_USE_POSTPROC) != VPX_CODEC_OK) {
109       ImplBase::cast_initialization_status_ =
110           STATUS_INVALID_VIDEO_CONFIGURATION;
111       return;
112     }
113     ImplBase::cast_initialization_status_ = STATUS_VIDEO_INITIALIZED;
114   }
115
116  private:
117   virtual ~Vp8Impl() {
118     if (ImplBase::cast_initialization_status_ == STATUS_VIDEO_INITIALIZED)
119       CHECK_EQ(VPX_CODEC_OK, vpx_codec_destroy(&context_));
120   }
121
122   virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) OVERRIDE {
123     if (len <= 0 || vpx_codec_decode(&context_,
124                                      data,
125                                      static_cast<unsigned int>(len),
126                                      NULL,
127                                      0) != VPX_CODEC_OK) {
128       return NULL;
129     }
130
131     vpx_codec_iter_t iter = NULL;
132     vpx_image_t* const image = vpx_codec_get_frame(&context_, &iter);
133     if (!image)
134       return NULL;
135     if (image->fmt != VPX_IMG_FMT_I420 && image->fmt != VPX_IMG_FMT_YV12) {
136       NOTREACHED();
137       return NULL;
138     }
139     DCHECK(vpx_codec_get_frame(&context_, &iter) == NULL)
140         << "Should have only decoded exactly one frame.";
141
142     const gfx::Size frame_size(image->d_w, image->d_h);
143     // Note: Timestamp for the VideoFrame will be set in VideoReceiver.
144     const scoped_refptr<VideoFrame> decoded_frame =
145         VideoFrame::CreateFrame(VideoFrame::YV12,
146                                 frame_size,
147                                 gfx::Rect(frame_size),
148                                 frame_size,
149                                 base::TimeDelta());
150     CopyYPlane(image->planes[VPX_PLANE_Y],
151                image->stride[VPX_PLANE_Y],
152                image->d_h,
153                decoded_frame.get());
154     CopyUPlane(image->planes[VPX_PLANE_U],
155                image->stride[VPX_PLANE_U],
156                (image->d_h + 1) / 2,
157                decoded_frame.get());
158     CopyVPlane(image->planes[VPX_PLANE_V],
159                image->stride[VPX_PLANE_V],
160                (image->d_h + 1) / 2,
161                decoded_frame.get());
162     return decoded_frame;
163   }
164
165   // VPX decoder context (i.e., an instantiation).
166   vpx_codec_ctx_t context_;
167
168   DISALLOW_COPY_AND_ASSIGN(Vp8Impl);
169 };
170
171 #ifndef OFFICIAL_BUILD
172 // A fake video decoder that always output 2x2 black frames.
173 class VideoDecoder::FakeImpl : public VideoDecoder::ImplBase {
174  public:
175   explicit FakeImpl(const scoped_refptr<CastEnvironment>& cast_environment)
176       : ImplBase(cast_environment, CODEC_VIDEO_FAKE),
177         last_decoded_id_(-1) {
178     if (ImplBase::cast_initialization_status_ != STATUS_VIDEO_UNINITIALIZED)
179       return;
180     ImplBase::cast_initialization_status_ = STATUS_VIDEO_INITIALIZED;
181   }
182
183  private:
184   virtual ~FakeImpl() {}
185
186   virtual scoped_refptr<VideoFrame> Decode(uint8* data, int len) OVERRIDE {
187     // Make sure this is a JSON string.
188     if (!len || data[0] != '{')
189       return NULL;
190     base::JSONReader reader;
191     scoped_ptr<base::Value> values(
192         reader.Read(base::StringPiece(reinterpret_cast<char*>(data), len)));
193     if (!values)
194       return NULL;
195     base::DictionaryValue* dict = NULL;
196     values->GetAsDictionary(&dict);
197
198     bool key = false;
199     int id = 0;
200     int ref = 0;
201     dict->GetBoolean("key", &key);
202     dict->GetInteger("id", &id);
203     dict->GetInteger("ref", &ref);
204     DCHECK(id == last_decoded_id_ + 1);
205     last_decoded_id_ = id;
206     return media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2));
207   }
208
209   int last_decoded_id_;
210
211   DISALLOW_COPY_AND_ASSIGN(FakeImpl);
212 };
213 #endif
214
215 VideoDecoder::VideoDecoder(
216     const scoped_refptr<CastEnvironment>& cast_environment,
217     Codec codec)
218     : cast_environment_(cast_environment) {
219   switch (codec) {
220 #ifndef OFFICIAL_BUILD
221     case CODEC_VIDEO_FAKE:
222       impl_ = new FakeImpl(cast_environment);
223       break;
224 #endif
225     case CODEC_VIDEO_VP8:
226       impl_ = new Vp8Impl(cast_environment);
227       break;
228     case CODEC_VIDEO_H264:
229       // TODO(miu): Need implementation.
230       NOTIMPLEMENTED();
231       break;
232     default:
233       NOTREACHED() << "Unknown or unspecified codec.";
234       break;
235   }
236 }
237
238 VideoDecoder::~VideoDecoder() {}
239
240 CastInitializationStatus VideoDecoder::InitializationResult() const {
241   if (impl_.get())
242     return impl_->InitializationResult();
243   return STATUS_UNSUPPORTED_VIDEO_CODEC;
244 }
245
246 void VideoDecoder::DecodeFrame(
247     scoped_ptr<EncodedFrame> encoded_frame,
248     const DecodeFrameCallback& callback) {
249   DCHECK(encoded_frame.get());
250   DCHECK(!callback.is_null());
251   if (!impl_.get() ||
252       impl_->InitializationResult() != STATUS_VIDEO_INITIALIZED) {
253     callback.Run(make_scoped_refptr<VideoFrame>(NULL), false);
254     return;
255   }
256   cast_environment_->PostTask(CastEnvironment::VIDEO,
257                               FROM_HERE,
258                               base::Bind(&VideoDecoder::ImplBase::DecodeFrame,
259                                          impl_,
260                                          base::Passed(&encoded_frame),
261                                          callback));
262 }
263
264 }  // namespace cast
265 }  // namespace media