Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: media/cast/video_receiver/video_decoder.cc

Issue 308043006: [Cast] Clean-up: Merge RtpReceiver+AudioReceiver+VideoReceiver-->FrameReceiver. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed hclam's comments. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "media/cast/video_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 transport::VideoCodec 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<transport::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 transport::VideoCodec 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, transport::kVp8) {
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);
154 CopyUPlane(image->planes[VPX_PLANE_U],
155 image->stride[VPX_PLANE_U],
156 (image->d_h + 1) / 2,
157 decoded_frame);
158 CopyVPlane(image->planes[VPX_PLANE_V],
159 image->stride[VPX_PLANE_V],
160 (image->d_h + 1) / 2,
161 decoded_frame);
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, transport::kFakeSoftwareVideo),
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 base::JSONReader reader;
188 scoped_ptr<base::Value> values(reader.Read(
189 base::StringPiece(reinterpret_cast<char*>(data), len)));
190 base::DictionaryValue* dict = NULL;
191 values->GetAsDictionary(&dict);
192
193 bool key = false;
194 int id = 0;
195 int ref = 0;
196 dict->GetBoolean("key", &key);
197 dict->GetInteger("id", &id);
198 dict->GetInteger("ref", &ref);
199 DCHECK(id == last_decoded_id_ + 1);
200 last_decoded_id_ = id;
201 return media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2));
202 }
203
204 int last_decoded_id_;
205
206 DISALLOW_COPY_AND_ASSIGN(FakeImpl);
207 };
208 #endif
209
210 VideoDecoder::VideoDecoder(
211 const scoped_refptr<CastEnvironment>& cast_environment,
212 const FrameReceiverConfig& video_config)
213 : cast_environment_(cast_environment) {
214 switch (video_config.codec.video) {
215 #ifndef OFFICIAL_BUILD
216 case transport::kFakeSoftwareVideo:
217 impl_ = new FakeImpl(cast_environment);
218 break;
219 #endif
220 case transport::kVp8:
221 impl_ = new Vp8Impl(cast_environment);
222 break;
223 case transport::kH264:
224 // TODO(miu): Need implementation.
225 NOTIMPLEMENTED();
226 break;
227 default:
228 NOTREACHED() << "Unknown or unspecified codec.";
229 break;
230 }
231 }
232
233 VideoDecoder::~VideoDecoder() {}
234
235 CastInitializationStatus VideoDecoder::InitializationResult() const {
236 if (impl_)
237 return impl_->InitializationResult();
238 return STATUS_UNSUPPORTED_VIDEO_CODEC;
239 }
240
241 void VideoDecoder::DecodeFrame(
242 scoped_ptr<transport::EncodedFrame> encoded_frame,
243 const DecodeFrameCallback& callback) {
244 DCHECK(encoded_frame.get());
245 DCHECK(!callback.is_null());
246 if (!impl_ || impl_->InitializationResult() != STATUS_VIDEO_INITIALIZED) {
247 callback.Run(make_scoped_refptr<VideoFrame>(NULL), false);
248 return;
249 }
250 cast_environment_->PostTask(CastEnvironment::VIDEO,
251 FROM_HERE,
252 base::Bind(&VideoDecoder::ImplBase::DecodeFrame,
253 impl_,
254 base::Passed(&encoded_frame),
255 callback));
256 }
257
258 } // namespace cast
259 } // namespace media
OLDNEW
« no previous file with comments | « media/cast/video_receiver/video_decoder.h ('k') | media/cast/video_receiver/video_decoder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698