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

Side by Side Diff: media/filters/vpx_video_decoder.cc

Issue 13886011: media: Add support for playback of VP8 Alpha video streams (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixing buildbot breakage Created 7 years, 8 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/filters/vpx_video_decoder.h" 5 #include "media/filters/vpx_video_decoder.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback_helpers.h" 8 #include "base/callback_helpers.h"
9 #include "base/command_line.h" 9 #include "base/command_line.h"
10 #include "base/location.h" 10 #include "base/location.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/message_loop_proxy.h" 12 #include "base/message_loop_proxy.h"
13 #include "base/string_number_conversions.h" 13 #include "base/string_number_conversions.h"
14 #include "base/sys_byteorder.h"
14 #include "media/base/bind_to_loop.h" 15 #include "media/base/bind_to_loop.h"
15 #include "media/base/decoder_buffer.h" 16 #include "media/base/decoder_buffer.h"
16 #include "media/base/demuxer_stream.h" 17 #include "media/base/demuxer_stream.h"
17 #include "media/base/media_switches.h" 18 #include "media/base/media_switches.h"
18 #include "media/base/pipeline.h" 19 #include "media/base/pipeline.h"
19 #include "media/base/video_decoder_config.h" 20 #include "media/base/video_decoder_config.h"
20 #include "media/base/video_frame.h" 21 #include "media/base/video_frame.h"
21 #include "media/base/video_util.h" 22 #include "media/base/video_util.h"
22 23
23 // Include libvpx header files. 24 // Include libvpx header files.
(...skipping 28 matching lines...) Expand all
52 53
53 decode_threads = std::max(decode_threads, 0); 54 decode_threads = std::max(decode_threads, 0);
54 decode_threads = std::min(decode_threads, kMaxDecodeThreads); 55 decode_threads = std::min(decode_threads, kMaxDecodeThreads);
55 return decode_threads; 56 return decode_threads;
56 } 57 }
57 58
58 VpxVideoDecoder::VpxVideoDecoder( 59 VpxVideoDecoder::VpxVideoDecoder(
59 const scoped_refptr<base::MessageLoopProxy>& message_loop) 60 const scoped_refptr<base::MessageLoopProxy>& message_loop)
60 : message_loop_(message_loop), 61 : message_loop_(message_loop),
61 state_(kUninitialized), 62 state_(kUninitialized),
62 vpx_codec_(NULL) { 63 vpx_codec_(NULL),
64 vpx_codec_alpha_(NULL) {
63 } 65 }
64 66
65 VpxVideoDecoder::~VpxVideoDecoder() { 67 VpxVideoDecoder::~VpxVideoDecoder() {
66 DCHECK_EQ(kUninitialized, state_); 68 DCHECK_EQ(kUninitialized, state_);
67 CloseDecoder(); 69 CloseDecoder();
68 } 70 }
69 71
70 void VpxVideoDecoder::Initialize( 72 void VpxVideoDecoder::Initialize(
71 const scoped_refptr<DemuxerStream>& stream, 73 const scoped_refptr<DemuxerStream>& stream,
72 const PipelineStatusCB& status_cb, 74 const PipelineStatusCB& status_cb,
(...skipping 12 matching lines...) Expand all
85 if (!ConfigureDecoder()) { 87 if (!ConfigureDecoder()) {
86 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); 88 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
87 return; 89 return;
88 } 90 }
89 91
90 // Success! 92 // Success!
91 state_ = kNormal; 93 state_ = kNormal;
92 status_cb.Run(PIPELINE_OK); 94 status_cb.Run(PIPELINE_OK);
93 } 95 }
94 96
97 static vpx_codec_ctx* InitializeVpxContext(vpx_codec_ctx* context,
98 const VideoDecoderConfig& config) {
99 context = new vpx_codec_ctx();
100 vpx_codec_dec_cfg_t vpx_config = {0};
101 vpx_config.w = config.coded_size().width();
102 vpx_config.h = config.coded_size().height();
103 vpx_config.threads = GetThreadCount();
104
105 vpx_codec_err_t status = vpx_codec_dec_init(context,
106 config.codec() == kCodecVP9 ?
107 vpx_codec_vp9_dx() :
108 vpx_codec_vp8_dx(),
109 &vpx_config,
110 0);
111 if (status != VPX_CODEC_OK) {
112 LOG(ERROR) << "vpx_codec_dec_init failed, status=" << status;
113 context = NULL;
scherkus (not reviewing) 2013/04/17 19:15:37 nit: delete context and return NULL instead of set
vignesh 2013/04/17 20:14:23 Done.
114 }
115 return context;
116 }
117
95 bool VpxVideoDecoder::ConfigureDecoder() { 118 bool VpxVideoDecoder::ConfigureDecoder() {
96 const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config(); 119 const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config();
97 if (!config.IsValidConfig()) { 120 if (!config.IsValidConfig()) {
98 DLOG(ERROR) << "Invalid video stream config: " 121 DLOG(ERROR) << "Invalid video stream config: "
99 << config.AsHumanReadableString(); 122 << config.AsHumanReadableString();
100 return false; 123 return false;
101 } 124 }
102 125
103 if (config.codec() != kCodecVP9) 126 const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
127 bool can_handle = false;
128 if (cmd_line->HasSwitch(switches::kEnableVp9Playback) &&
129 config.codec() == kCodecVP9) {
130 can_handle = true;
131 }
132 if (cmd_line->HasSwitch(switches::kEnableVp8AlphaPlayback) &&
133 config.codec() == kCodecVP8 && config.format() == VideoFrame::YV12A) {
134 can_handle = true;
135 }
136 if (!can_handle)
104 return false; 137 return false;
105 138
106 CloseDecoder(); 139 CloseDecoder();
107 140
108 vpx_codec_ = new vpx_codec_ctx(); 141 vpx_codec_ = InitializeVpxContext(vpx_codec_, config);
109 vpx_codec_dec_cfg_t vpx_config = {0}; 142 if (!vpx_codec_)
110 vpx_config.w = config.coded_size().width(); 143 return false;
111 vpx_config.h = config.coded_size().height();
112 vpx_config.threads = GetThreadCount();
113 144
114 vpx_codec_err_t status = vpx_codec_dec_init(vpx_codec_, 145 if (config.format() == VideoFrame::YV12A) {
115 vpx_codec_vp9_dx(), 146 vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config);
116 &vpx_config, 147 if (!vpx_codec_alpha_)
117 0); 148 return false;
118 if (status != VPX_CODEC_OK) {
119 LOG(ERROR) << "vpx_codec_dec_init failed, status=" << status;
120 delete vpx_codec_;
121 vpx_codec_ = NULL;
122 return false;
123 } 149 }
124 150
125 return true; 151 return true;
126 } 152 }
127 153
128 void VpxVideoDecoder::CloseDecoder() { 154 void VpxVideoDecoder::CloseDecoder() {
129 if (vpx_codec_) { 155 if (vpx_codec_) {
130 vpx_codec_destroy(vpx_codec_); 156 vpx_codec_destroy(vpx_codec_);
131 delete vpx_codec_; 157 delete vpx_codec_;
132 vpx_codec_ = NULL; 158 vpx_codec_ = NULL;
133 } 159 }
160 if (vpx_codec_alpha_) {
161 vpx_codec_destroy(vpx_codec_alpha_);
162 delete vpx_codec_alpha_;
163 vpx_codec_alpha_ = NULL;
164 }
134 } 165 }
135 166
136 void VpxVideoDecoder::Read(const ReadCB& read_cb) { 167 void VpxVideoDecoder::Read(const ReadCB& read_cb) {
137 DCHECK(message_loop_->BelongsToCurrentThread()); 168 DCHECK(message_loop_->BelongsToCurrentThread());
138 DCHECK(!read_cb.is_null()); 169 DCHECK(!read_cb.is_null());
139 CHECK_NE(state_, kUninitialized); 170 CHECK_NE(state_, kUninitialized);
140 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; 171 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported.";
141 read_cb_ = BindToCurrentLoop(read_cb); 172 read_cb_ = BindToCurrentLoop(read_cb);
142 173
143 // Return empty frames if decoding has finished. 174 // Return empty frames if decoding has finished.
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
239 } 270 }
240 271
241 scoped_refptr<VideoFrame> video_frame; 272 scoped_refptr<VideoFrame> video_frame;
242 if (!Decode(buffer, &video_frame)) { 273 if (!Decode(buffer, &video_frame)) {
243 state_ = kDecodeFinished; 274 state_ = kDecodeFinished;
244 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); 275 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
245 return; 276 return;
246 } 277 }
247 278
248 // Any successful decode counts! 279 // Any successful decode counts!
249 if (buffer->GetDataSize()) { 280 if (buffer->GetDataSize() && buffer->GetSideDataSize()) {
250 PipelineStatistics statistics; 281 PipelineStatistics statistics;
251 statistics.video_bytes_decoded = buffer->GetDataSize(); 282 statistics.video_bytes_decoded = buffer->GetDataSize();
252 statistics_cb_.Run(statistics); 283 statistics_cb_.Run(statistics);
253 } 284 }
254 285
255 // If we didn't get a frame we need more data. 286 // If we didn't get a frame we need more data.
256 if (!video_frame) { 287 if (!video_frame) {
257 ReadFromDemuxerStream(); 288 ReadFromDemuxerStream();
258 return; 289 return;
259 } 290 }
(...skipping 26 matching lines...) Expand all
286 if (!vpx_image) { 317 if (!vpx_image) {
287 *video_frame = NULL; 318 *video_frame = NULL;
288 return true; 319 return true;
289 } 320 }
290 321
291 if (vpx_image->user_priv != reinterpret_cast<void*>(&timestamp)) { 322 if (vpx_image->user_priv != reinterpret_cast<void*>(&timestamp)) {
292 LOG(ERROR) << "Invalid output timestamp."; 323 LOG(ERROR) << "Invalid output timestamp.";
293 return false; 324 return false;
294 } 325 }
295 326
296 CopyVpxImageTo(vpx_image, video_frame); 327 const vpx_image_t* vpx_image_alpha = NULL;
328 if (vpx_codec_alpha_ && buffer->GetSideDataSize() >= 8) {
329 // Pass alpha data to libvpx.
330 int64 timestamp_alpha = buffer->GetTimestamp().InMicroseconds();
331 void* user_priv_alpha = reinterpret_cast<void*>(&timestamp_alpha);
332
333 // First 8 bytes of side data is side_data_id in big endian.
334 const uint64 side_data_id = base::NetToHost64(
335 *(reinterpret_cast<const uint64*>(buffer->GetSideData())));
336 if (side_data_id == 1) {
337 status = vpx_codec_decode(vpx_codec_alpha_,
338 buffer->GetSideData() + 8,
339 buffer->GetSideDataSize() - 8,
340 user_priv_alpha,
341 0);
342
343 if (status != VPX_CODEC_OK) {
344 LOG(ERROR) << "vpx_codec_decode() failed on alpha, status=" << status;
345 return false;
346 }
347
348 // Gets pointer to decoded data.
349 vpx_codec_iter_t iter_alpha = NULL;
350 vpx_image_alpha = vpx_codec_get_frame(vpx_codec_alpha_,
scherkus (not reviewing) 2013/04/17 19:15:37 this fits on one line
vignesh 2013/04/17 20:14:23 Done.
351 &iter_alpha);
352 if (!vpx_image_alpha) {
353 *video_frame = NULL;
354 return true;
355 }
356
357 if (vpx_image_alpha->user_priv !=
358 reinterpret_cast<void*>(&timestamp_alpha)) {
359 LOG(ERROR) << "Invalid output timestamp on alpha.";
360 return false;
361 }
362 }
363 }
364
365 CopyVpxImageTo(vpx_image, vpx_image_alpha, video_frame);
297 (*video_frame)->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp)); 366 (*video_frame)->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp));
298 return true; 367 return true;
299 } 368 }
300 369
301 void VpxVideoDecoder::DoReset() { 370 void VpxVideoDecoder::DoReset() {
302 DCHECK(read_cb_.is_null()); 371 DCHECK(read_cb_.is_null());
303 372
304 state_ = kNormal; 373 state_ = kNormal;
305 reset_cb_.Run(); 374 reset_cb_.Run();
306 reset_cb_.Reset(); 375 reset_cb_.Reset();
307 } 376 }
308 377
309 void VpxVideoDecoder::CopyVpxImageTo( 378 void VpxVideoDecoder::CopyVpxImageTo(
310 const vpx_image* vpx_image, 379 const struct vpx_image* vpx_image,
scherkus (not reviewing) 2013/04/17 19:15:37 nit: C++ doesn't need struct keyword
vignesh 2013/04/17 20:14:23 Done.
380 const struct vpx_image* vpx_image_alpha,
311 scoped_refptr<VideoFrame>* video_frame) { 381 scoped_refptr<VideoFrame>* video_frame) {
312 CHECK(vpx_image); 382 CHECK(vpx_image);
313 CHECK_EQ(vpx_image->d_w % 2, 0U); 383 CHECK_EQ(vpx_image->d_w % 2, 0U);
314 CHECK_EQ(vpx_image->d_h % 2, 0U); 384 CHECK_EQ(vpx_image->d_h % 2, 0U);
315 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 || 385 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 ||
316 vpx_image->fmt == VPX_IMG_FMT_YV12); 386 vpx_image->fmt == VPX_IMG_FMT_YV12);
317 387
318 gfx::Size size(vpx_image->d_w, vpx_image->d_h); 388 gfx::Size size(vpx_image->d_w, vpx_image->d_h);
319 gfx::Size natural_size = 389 gfx::Size natural_size =
320 demuxer_stream_->video_decoder_config().natural_size(); 390 demuxer_stream_->video_decoder_config().natural_size();
321 391
322 *video_frame = VideoFrame::CreateFrame(VideoFrame::YV12, 392 *video_frame = VideoFrame::CreateFrame(vpx_codec_alpha_ ?
393 VideoFrame::YV12A :
394 VideoFrame::YV12,
323 size, 395 size,
324 gfx::Rect(size), 396 gfx::Rect(size),
325 natural_size, 397 natural_size,
326 kNoTimestamp()); 398 kNoTimestamp());
399
327 CopyYPlane(vpx_image->planes[VPX_PLANE_Y], 400 CopyYPlane(vpx_image->planes[VPX_PLANE_Y],
328 vpx_image->stride[VPX_PLANE_Y], 401 vpx_image->stride[VPX_PLANE_Y],
329 vpx_image->d_h, 402 vpx_image->d_h,
330 *video_frame); 403 *video_frame);
331 CopyUPlane(vpx_image->planes[VPX_PLANE_U], 404 CopyUPlane(vpx_image->planes[VPX_PLANE_U],
332 vpx_image->stride[VPX_PLANE_U], 405 vpx_image->stride[VPX_PLANE_U],
333 vpx_image->d_h / 2, 406 vpx_image->d_h / 2,
334 *video_frame); 407 *video_frame);
335 CopyVPlane(vpx_image->planes[VPX_PLANE_V], 408 CopyVPlane(vpx_image->planes[VPX_PLANE_V],
336 vpx_image->stride[VPX_PLANE_V], 409 vpx_image->stride[VPX_PLANE_V],
337 vpx_image->d_h / 2, 410 vpx_image->d_h / 2,
338 *video_frame); 411 *video_frame);
412 if (!vpx_codec_alpha_)
413 return;
414 if (!vpx_image_alpha) {
415 MakeOpaqueAPlane(vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h,
416 *video_frame);
417 return;
418 }
419 CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y],
420 vpx_image->stride[VPX_PLANE_Y],
421 vpx_image->d_h,
422 *video_frame);
339 } 423 }
340 424
341 } // namespace media 425 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698