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

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: rebase 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
« no previous file with comments | « media/filters/vpx_video_decoder.h ('k') | media/media.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 29 matching lines...) Expand all
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 weak_factory_(this), 62 weak_factory_(this),
62 state_(kUninitialized), 63 state_(kUninitialized),
63 vpx_codec_(NULL) { 64 vpx_codec_(NULL),
65 vpx_codec_alpha_(NULL) {
64 } 66 }
65 67
66 VpxVideoDecoder::~VpxVideoDecoder() { 68 VpxVideoDecoder::~VpxVideoDecoder() {
67 DCHECK_EQ(kUninitialized, state_); 69 DCHECK_EQ(kUninitialized, state_);
68 CloseDecoder(); 70 CloseDecoder();
69 } 71 }
70 72
71 void VpxVideoDecoder::Initialize( 73 void VpxVideoDecoder::Initialize(
72 const scoped_refptr<DemuxerStream>& stream, 74 const scoped_refptr<DemuxerStream>& stream,
73 const PipelineStatusCB& status_cb, 75 const PipelineStatusCB& status_cb,
(...skipping 13 matching lines...) Expand all
87 if (!ConfigureDecoder()) { 89 if (!ConfigureDecoder()) {
88 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); 90 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
89 return; 91 return;
90 } 92 }
91 93
92 // Success! 94 // Success!
93 state_ = kNormal; 95 state_ = kNormal;
94 status_cb.Run(PIPELINE_OK); 96 status_cb.Run(PIPELINE_OK);
95 } 97 }
96 98
99 static vpx_codec_ctx* InitializeVpxContext(vpx_codec_ctx* context,
100 const VideoDecoderConfig& config) {
101 context = new vpx_codec_ctx();
102 vpx_codec_dec_cfg_t vpx_config = {0};
103 vpx_config.w = config.coded_size().width();
104 vpx_config.h = config.coded_size().height();
105 vpx_config.threads = GetThreadCount();
106
107 vpx_codec_err_t status = vpx_codec_dec_init(context,
108 config.codec() == kCodecVP9 ?
109 vpx_codec_vp9_dx() :
110 vpx_codec_vp8_dx(),
111 &vpx_config,
112 0);
113 if (status != VPX_CODEC_OK) {
114 LOG(ERROR) << "vpx_codec_dec_init failed, status=" << status;
115 delete context;
116 return NULL;
117 }
118 return context;
119 }
120
97 bool VpxVideoDecoder::ConfigureDecoder() { 121 bool VpxVideoDecoder::ConfigureDecoder() {
98 const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config(); 122 const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config();
99 if (!config.IsValidConfig()) { 123 if (!config.IsValidConfig()) {
100 DLOG(ERROR) << "Invalid video stream config: " 124 DLOG(ERROR) << "Invalid video stream config: "
101 << config.AsHumanReadableString(); 125 << config.AsHumanReadableString();
102 return false; 126 return false;
103 } 127 }
104 128
105 if (config.codec() != kCodecVP9) 129 const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
130 bool can_handle = false;
131 if (cmd_line->HasSwitch(switches::kEnableVp9Playback) &&
132 config.codec() == kCodecVP9) {
133 can_handle = true;
134 }
135 if (cmd_line->HasSwitch(switches::kEnableVp8AlphaPlayback) &&
136 config.codec() == kCodecVP8 && config.format() == VideoFrame::YV12A) {
137 can_handle = true;
138 }
139 if (!can_handle)
106 return false; 140 return false;
107 141
108 CloseDecoder(); 142 CloseDecoder();
109 143
110 vpx_codec_ = new vpx_codec_ctx(); 144 vpx_codec_ = InitializeVpxContext(vpx_codec_, config);
111 vpx_codec_dec_cfg_t vpx_config = {0}; 145 if (!vpx_codec_)
112 vpx_config.w = config.coded_size().width(); 146 return false;
113 vpx_config.h = config.coded_size().height();
114 vpx_config.threads = GetThreadCount();
115 147
116 vpx_codec_err_t status = vpx_codec_dec_init(vpx_codec_, 148 if (config.format() == VideoFrame::YV12A) {
117 vpx_codec_vp9_dx(), 149 vpx_codec_alpha_ = InitializeVpxContext(vpx_codec_alpha_, config);
118 &vpx_config, 150 if (!vpx_codec_alpha_)
119 0); 151 return false;
120 if (status != VPX_CODEC_OK) {
121 LOG(ERROR) << "vpx_codec_dec_init failed, status=" << status;
122 delete vpx_codec_;
123 vpx_codec_ = NULL;
124 return false;
125 } 152 }
126 153
127 return true; 154 return true;
128 } 155 }
129 156
130 void VpxVideoDecoder::CloseDecoder() { 157 void VpxVideoDecoder::CloseDecoder() {
131 if (vpx_codec_) { 158 if (vpx_codec_) {
132 vpx_codec_destroy(vpx_codec_); 159 vpx_codec_destroy(vpx_codec_);
133 delete vpx_codec_; 160 delete vpx_codec_;
134 vpx_codec_ = NULL; 161 vpx_codec_ = NULL;
135 } 162 }
163 if (vpx_codec_alpha_) {
164 vpx_codec_destroy(vpx_codec_alpha_);
165 delete vpx_codec_alpha_;
166 vpx_codec_alpha_ = NULL;
167 }
136 } 168 }
137 169
138 void VpxVideoDecoder::Read(const ReadCB& read_cb) { 170 void VpxVideoDecoder::Read(const ReadCB& read_cb) {
139 DCHECK(message_loop_->BelongsToCurrentThread()); 171 DCHECK(message_loop_->BelongsToCurrentThread());
140 DCHECK(!read_cb.is_null()); 172 DCHECK(!read_cb.is_null());
141 CHECK_NE(state_, kUninitialized); 173 CHECK_NE(state_, kUninitialized);
142 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; 174 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported.";
143 read_cb_ = BindToCurrentLoop(read_cb); 175 read_cb_ = BindToCurrentLoop(read_cb);
144 176
145 // Return empty frames if decoding has finished. 177 // Return empty frames if decoding has finished.
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
241 } 273 }
242 274
243 scoped_refptr<VideoFrame> video_frame; 275 scoped_refptr<VideoFrame> video_frame;
244 if (!Decode(buffer, &video_frame)) { 276 if (!Decode(buffer, &video_frame)) {
245 state_ = kDecodeFinished; 277 state_ = kDecodeFinished;
246 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); 278 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
247 return; 279 return;
248 } 280 }
249 281
250 // Any successful decode counts! 282 // Any successful decode counts!
251 if (buffer->GetDataSize()) { 283 if (buffer->GetDataSize() && buffer->GetSideDataSize()) {
252 PipelineStatistics statistics; 284 PipelineStatistics statistics;
253 statistics.video_bytes_decoded = buffer->GetDataSize(); 285 statistics.video_bytes_decoded = buffer->GetDataSize();
254 statistics_cb_.Run(statistics); 286 statistics_cb_.Run(statistics);
255 } 287 }
256 288
257 // If we didn't get a frame we need more data. 289 // If we didn't get a frame we need more data.
258 if (!video_frame) { 290 if (!video_frame) {
259 ReadFromDemuxerStream(); 291 ReadFromDemuxerStream();
260 return; 292 return;
261 } 293 }
(...skipping 26 matching lines...) Expand all
288 if (!vpx_image) { 320 if (!vpx_image) {
289 *video_frame = NULL; 321 *video_frame = NULL;
290 return true; 322 return true;
291 } 323 }
292 324
293 if (vpx_image->user_priv != reinterpret_cast<void*>(&timestamp)) { 325 if (vpx_image->user_priv != reinterpret_cast<void*>(&timestamp)) {
294 LOG(ERROR) << "Invalid output timestamp."; 326 LOG(ERROR) << "Invalid output timestamp.";
295 return false; 327 return false;
296 } 328 }
297 329
298 CopyVpxImageTo(vpx_image, video_frame); 330 const vpx_image_t* vpx_image_alpha = NULL;
331 if (vpx_codec_alpha_ && buffer->GetSideDataSize() >= 8) {
332 // Pass alpha data to libvpx.
333 int64 timestamp_alpha = buffer->GetTimestamp().InMicroseconds();
334 void* user_priv_alpha = reinterpret_cast<void*>(&timestamp_alpha);
335
336 // First 8 bytes of side data is side_data_id in big endian.
337 const uint64 side_data_id = base::NetToHost64(
338 *(reinterpret_cast<const uint64*>(buffer->GetSideData())));
339 if (side_data_id == 1) {
340 status = vpx_codec_decode(vpx_codec_alpha_,
341 buffer->GetSideData() + 8,
342 buffer->GetSideDataSize() - 8,
343 user_priv_alpha,
344 0);
345
346 if (status != VPX_CODEC_OK) {
347 LOG(ERROR) << "vpx_codec_decode() failed on alpha, status=" << status;
348 return false;
349 }
350
351 // Gets pointer to decoded data.
352 vpx_codec_iter_t iter_alpha = NULL;
353 vpx_image_alpha = vpx_codec_get_frame(vpx_codec_alpha_, &iter_alpha);
354 if (!vpx_image_alpha) {
355 *video_frame = NULL;
356 return true;
357 }
358
359 if (vpx_image_alpha->user_priv !=
360 reinterpret_cast<void*>(&timestamp_alpha)) {
361 LOG(ERROR) << "Invalid output timestamp on alpha.";
362 return false;
363 }
364 }
365 }
366
367 CopyVpxImageTo(vpx_image, vpx_image_alpha, video_frame);
299 (*video_frame)->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp)); 368 (*video_frame)->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp));
300 return true; 369 return true;
301 } 370 }
302 371
303 void VpxVideoDecoder::DoReset() { 372 void VpxVideoDecoder::DoReset() {
304 DCHECK(read_cb_.is_null()); 373 DCHECK(read_cb_.is_null());
305 374
306 state_ = kNormal; 375 state_ = kNormal;
307 reset_cb_.Run(); 376 reset_cb_.Run();
308 reset_cb_.Reset(); 377 reset_cb_.Reset();
309 } 378 }
310 379
311 void VpxVideoDecoder::CopyVpxImageTo( 380 void VpxVideoDecoder::CopyVpxImageTo(
312 const vpx_image* vpx_image, 381 const vpx_image* vpx_image,
382 const struct vpx_image* vpx_image_alpha,
313 scoped_refptr<VideoFrame>* video_frame) { 383 scoped_refptr<VideoFrame>* video_frame) {
314 CHECK(vpx_image); 384 CHECK(vpx_image);
315 CHECK_EQ(vpx_image->d_w % 2, 0U); 385 CHECK_EQ(vpx_image->d_w % 2, 0U);
316 CHECK_EQ(vpx_image->d_h % 2, 0U); 386 CHECK_EQ(vpx_image->d_h % 2, 0U);
317 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 || 387 CHECK(vpx_image->fmt == VPX_IMG_FMT_I420 ||
318 vpx_image->fmt == VPX_IMG_FMT_YV12); 388 vpx_image->fmt == VPX_IMG_FMT_YV12);
319 389
320 gfx::Size size(vpx_image->d_w, vpx_image->d_h); 390 gfx::Size size(vpx_image->d_w, vpx_image->d_h);
321 gfx::Size natural_size = 391 gfx::Size natural_size =
322 demuxer_stream_->video_decoder_config().natural_size(); 392 demuxer_stream_->video_decoder_config().natural_size();
323 393
324 *video_frame = VideoFrame::CreateFrame(VideoFrame::YV12, 394 *video_frame = VideoFrame::CreateFrame(vpx_codec_alpha_ ?
395 VideoFrame::YV12A :
396 VideoFrame::YV12,
325 size, 397 size,
326 gfx::Rect(size), 398 gfx::Rect(size),
327 natural_size, 399 natural_size,
328 kNoTimestamp()); 400 kNoTimestamp());
401
329 CopyYPlane(vpx_image->planes[VPX_PLANE_Y], 402 CopyYPlane(vpx_image->planes[VPX_PLANE_Y],
330 vpx_image->stride[VPX_PLANE_Y], 403 vpx_image->stride[VPX_PLANE_Y],
331 vpx_image->d_h, 404 vpx_image->d_h,
332 *video_frame); 405 *video_frame);
333 CopyUPlane(vpx_image->planes[VPX_PLANE_U], 406 CopyUPlane(vpx_image->planes[VPX_PLANE_U],
334 vpx_image->stride[VPX_PLANE_U], 407 vpx_image->stride[VPX_PLANE_U],
335 vpx_image->d_h / 2, 408 vpx_image->d_h / 2,
336 *video_frame); 409 *video_frame);
337 CopyVPlane(vpx_image->planes[VPX_PLANE_V], 410 CopyVPlane(vpx_image->planes[VPX_PLANE_V],
338 vpx_image->stride[VPX_PLANE_V], 411 vpx_image->stride[VPX_PLANE_V],
339 vpx_image->d_h / 2, 412 vpx_image->d_h / 2,
340 *video_frame); 413 *video_frame);
414 if (!vpx_codec_alpha_)
415 return;
416 if (!vpx_image_alpha) {
417 MakeOpaqueAPlane(vpx_image->stride[VPX_PLANE_Y], vpx_image->d_h,
418 *video_frame);
419 return;
420 }
421 CopyAPlane(vpx_image_alpha->planes[VPX_PLANE_Y],
422 vpx_image->stride[VPX_PLANE_Y],
423 vpx_image->d_h,
424 *video_frame);
341 } 425 }
342 426
343 } // namespace media 427 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/vpx_video_decoder.h ('k') | media/media.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698