Chromium Code Reviews| Index: media/filters/decrypting_video_decoder.cc |
| diff --git a/media/filters/decrypting_video_decoder.cc b/media/filters/decrypting_video_decoder.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..dd1829d7ce07e95b0b29dcbd557da744865a9931 |
| --- /dev/null |
| +++ b/media/filters/decrypting_video_decoder.cc |
| @@ -0,0 +1,279 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "media/filters/decrypting_video_decoder.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/callback_helpers.h" |
| +#include "base/location.h" |
| +#include "base/message_loop_proxy.h" |
| +#include "media/base/decoder_buffer.h" |
| +#include "media/base/decryptor.h" |
| +#include "media/base/demuxer_stream.h" |
| +#include "media/base/pipeline.h" |
| +#include "media/base/video_decoder_config.h" |
| +#include "media/base/video_frame.h" |
| + |
| +namespace media { |
| + |
| +DecryptingVideoDecoder::DecryptingVideoDecoder( |
| + const MessageLoopFactoryCB& message_loop_factory_cb, |
| + Decryptor* decryptor) |
| + : message_loop_factory_cb_(message_loop_factory_cb), |
| + message_loop_(NULL), |
| + state_(kUninitialized), |
| + decryptor_(decryptor) { |
| +} |
| + |
| +void DecryptingVideoDecoder::Initialize( |
| + const scoped_refptr<DemuxerStream>& stream, |
| + const PipelineStatusCB& status_cb, |
| + const StatisticsCB& statistics_cb) { |
| + if (!message_loop_) { |
|
ddorwin
2012/09/28 17:36:40
Initialize can be called multiple times? Or is thi
xhwang
2012/09/30 19:58:51
This is the same code we have in FFVD. But it's a
|
| + message_loop_ = base::ResetAndReturn(&message_loop_factory_cb_).Run(); |
| + message_loop_->PostTask(FROM_HERE, base::Bind( |
| + &DecryptingVideoDecoder::Initialize, this, |
| + stream, status_cb, statistics_cb)); |
| + return; |
| + } |
| + |
| + DCHECK(!demuxer_stream_); |
| + DCHECK(stream); |
| + demuxer_stream_ = stream; |
|
ddorwin
2012/09/28 17:36:40
Can this class be reused after one of the followin
xhwang
2012/09/30 19:58:51
Done.
|
| + |
| + const VideoDecoderConfig& config = demuxer_stream_->video_decoder_config(); |
| + if (!config.IsValidConfig()) { |
| + DLOG(ERROR) << "Invalid video stream - " << config.AsHumanReadableString(); |
| + status_cb.Run(PIPELINE_ERROR_DECODE); |
| + return; |
| + } |
| + // DecryptingVideoDecoder only accepts potentially encrypted stream. |
|
ddorwin
2012/09/28 17:36:40
Newline
xhwang
2012/09/30 19:58:51
Done.
|
| + if (!config.is_encrypted()) { |
| + status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
| + return; |
| + } |
| + |
| + status_cb_ = status_cb; |
| + statistics_cb_ = statistics_cb; |
| + |
| + DCHECK(decryptor_); |
| + decryptor_->InitializeVideoDecoder( |
| + config, |
| + base::Bind(&DecryptingVideoDecoder::OnDecoderInitialized, this)); |
| +} |
| + |
| +void DecryptingVideoDecoder::Read(const ReadCB& read_cb) { |
| + // Complete operation asynchronously on different stack of execution as per |
| + // the API contract of VideoDecoder::Read() |
| + message_loop_->PostTask(FROM_HERE, base::Bind( |
| + &DecryptingVideoDecoder::DoRead, this, read_cb)); |
| +} |
| + |
| +void DecryptingVideoDecoder::Reset(const base::Closure& closure) { |
| + if (!message_loop_->BelongsToCurrentThread()) { |
| + message_loop_->PostTask(FROM_HERE, base::Bind( |
| + &DecryptingVideoDecoder::Reset, this, closure)); |
| + return; |
| + } |
| + |
| + DCHECK(reset_cb_.is_null()); |
| + reset_cb_ = closure; |
| + |
| + decryptor_->CancelDecryptAndDecodeVideo(); |
| + |
| + // Defer the reset if a read is pending. |
|
ddorwin
2012/09/28 17:36:40
Why? When will it be called then?
xhwang
2012/09/30 19:58:51
We can't finish the reset process if we have pendi
|
| + if (!read_cb_.is_null()) |
| + return; |
| + |
| + DoReset(); |
| +} |
| + |
| +void DecryptingVideoDecoder::Stop(const base::Closure& closure) { |
| + if (!message_loop_->BelongsToCurrentThread()) { |
| + message_loop_->PostTask(FROM_HERE, base::Bind( |
| + &DecryptingVideoDecoder::Stop, this, closure)); |
| + return; |
| + } |
| + |
| + DCHECK(stop_cb_.is_null()); |
| + stop_cb_ = closure; |
| + |
| + decryptor_->StopVideoDecoder(); |
| + |
| + // Defer stopping if a read is pending. |
|
ddorwin
2012/09/28 17:36:40
Same.
xhwang
2012/09/30 19:58:51
See above answer.
|
| + if (!read_cb_.is_null()) |
| + return; |
| + |
| + DoStop(); |
| +} |
| + |
| +DecryptingVideoDecoder::~DecryptingVideoDecoder() { |
|
ddorwin
2012/09/28 17:36:40
Do we need this? Presumably the parent is already
xhwang
2012/09/30 19:58:51
Correct. I added a DCHECK in the dtor so now we ne
|
| +} |
| + |
| +void DecryptingVideoDecoder::OnDecoderInitialized(bool success) { |
| + if (!message_loop_->BelongsToCurrentThread()) { |
| + message_loop_->PostTask(FROM_HERE, base::Bind( |
| + &DecryptingVideoDecoder::OnDecoderInitialized, this, success)); |
| + return; |
| + } |
| + |
| + DCHECK(!status_cb_.is_null()); |
| + if (!success) { |
|
ddorwin
2012/09/28 17:36:40
nit: newline since the above DCHECK is applicable
xhwang
2012/09/30 19:58:51
Done.
|
| + base::ResetAndReturn(&status_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); |
| + return; |
| + } |
| + |
| + // Success! |
| + state_ = kNormal; |
| + base::ResetAndReturn(&status_cb_).Run(PIPELINE_OK); |
| +} |
| + |
| +void DecryptingVideoDecoder::DoRead(const ReadCB& read_cb) { |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| + DCHECK(!read_cb.is_null()); |
| + CHECK_NE(state_, kUninitialized); |
| + CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; |
| + |
| + // Return empty frames if decoding has finished. |
| + if (state_ == kDecodeFinished) { |
| + read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); |
| + return; |
| + } |
| + |
| + read_cb_ = read_cb; |
| + ReadFromDemuxerStream(); |
| +} |
| + |
| +void DecryptingVideoDecoder::ReadFromDemuxerStream() { |
| + DCHECK_NE(state_, kUninitialized); |
| + DCHECK_NE(state_, kDecodeFinished); |
| + DCHECK(!read_cb_.is_null()); |
| + |
| + demuxer_stream_->Read( |
| + base::Bind(&DecryptingVideoDecoder::DecryptAndDecodeBuffer, this)); |
| +} |
| + |
| +void DecryptingVideoDecoder::DecryptAndDecodeBuffer( |
| + DemuxerStream::Status status, |
| + const scoped_refptr<DecoderBuffer>& buffer) { |
| + DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; |
| + // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read |
| + // callback on the same execution stack so we can get rid of forced task post. |
| + message_loop_->PostTask(FROM_HERE, base::Bind( |
| + &DecryptingVideoDecoder::DoDecryptAndDecodeBuffer, this, status, buffer)); |
| +} |
| + |
| +void DecryptingVideoDecoder::DoDecryptAndDecodeBuffer( |
| + DemuxerStream::Status status, |
| + const scoped_refptr<DecoderBuffer>& buffer) { |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| + DCHECK_NE(state_, kUninitialized); |
|
ddorwin
2012/09/28 17:36:40
These two can be replaced with EQ kNormal.
xhwang
2012/09/30 19:58:51
Done.
|
| + DCHECK_NE(state_, kDecodeFinished); |
| + DCHECK(!read_cb_.is_null()); |
| + |
| + if (!stop_cb_.is_null()) { |
| + base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
|
ddorwin
2012/09/28 17:36:40
Not related to this CL, but odd that we use the da
xhwang
2012/09/30 19:58:51
I agree. +scherkus/fischman: How about we add kAbo
|
| + DoStop(); |
| + return; |
| + } |
| + |
| + if (!reset_cb_.is_null()) { |
| + base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
| + DoReset(); |
| + return; |
| + } |
| + |
| + if (status == DemuxerStream::kAborted) { |
| + base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
| + return; |
| + } |
| + |
| + if (status == DemuxerStream::kConfigChanged) { |
| + // TODO(xhwang): Add config change support. |
| + base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
| + return; |
| + } |
| + |
| + DCHECK_EQ(DemuxerStream::kOk, status); |
|
ddorwin
2012/09/28 17:36:40
nit: Did we decide the other order for non-unittes
xhwang
2012/09/30 19:58:51
Yeah, as summarized by scherkus in another thread:
|
| + DCHECK(buffer); |
| + |
| + decryptor_->DecryptAndDecodeVideo( |
| + buffer, base::Bind(&DecryptingVideoDecoder::DeliverFrame, this, |
| + buffer->GetDataSize())); |
| +} |
| + |
| +void DecryptingVideoDecoder::DeliverFrame( |
| + int buffer_size, |
| + Decryptor::Status status, |
| + const scoped_refptr<VideoFrame>& frame) { |
| + // We need to force task post here because the VideoDecodeCB can be executed |
| + // synchronously or asynchronously by the decryptor. |
|
ddorwin
2012/09/28 17:36:40
Might be worth explaining why or pointing to an ex
xhwang
2012/09/30 19:58:51
Done.
|
| + message_loop_->PostTask(FROM_HERE, base::Bind( |
| + &DecryptingVideoDecoder::DoDeliverFrame, this, |
| + buffer_size, status, frame)); |
| +} |
| + |
| +void DecryptingVideoDecoder::DoDeliverFrame( |
| + int buffer_size, |
| + Decryptor::Status status, |
| + const scoped_refptr<VideoFrame>& frame) { |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| + |
| + DCHECK_NE(state_, kUninitialized); |
| + DCHECK_NE(state_, kDecodeFinished); |
| + DCHECK(!read_cb_.is_null()); |
| + |
| + if (!stop_cb_.is_null()) { |
| + base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
| + DoStop(); |
| + return; |
| + } |
| + |
| + if (!reset_cb_.is_null()) { |
| + base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
| + DoReset(); |
| + return; |
| + } |
| + |
| + if (status == Decryptor::kNoKey || status == Decryptor::kError) { |
| + DCHECK(!frame); |
| + state_ = kDecodeFinished; |
| + base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
|
ddorwin
2012/09/28 17:36:40
Why is kNoKey a decode error? Where is the wait an
xhwang
2012/09/30 19:58:51
The wait and retry logic is in ProxyDecryptor so i
ddorwin
2012/10/01 18:43:20
But wouldn't this class always wrap the real Decry
|
| + return; |
| + } |
| + |
| + // The buffer has been accepted by the decoder, let's report statistics. |
| + if (buffer_size) { |
| + PipelineStatistics statistics; |
| + statistics.video_bytes_decoded = buffer_size; |
| + statistics_cb_.Run(statistics); |
| + } |
| + |
| + if (status == Decryptor::kNeedMoreData) { |
| + DCHECK(!frame); |
| + ReadFromDemuxerStream(); |
| + return; |
| + } |
| + |
| + DCHECK_EQ(Decryptor::kSuccess, status); |
|
ddorwin
2012/09/28 17:36:40
same
xhwang
2012/09/30 19:58:51
Done.
|
| + DCHECK(frame); |
| + if (frame->IsEndOfStream()) |
| + state_ = kDecodeFinished; |
| + |
| + base::ResetAndReturn(&read_cb_).Run(kOk, frame); |
| +} |
| + |
| +void DecryptingVideoDecoder::DoReset() { |
| + DCHECK(read_cb_.is_null()); |
| + state_ = kNormal; |
|
ddorwin
2012/09/28 17:36:40
DCHECK(state_ != kUninitialized).
xhwang
2012/09/30 19:58:51
Done.
|
| + base::ResetAndReturn(&reset_cb_).Run(); |
| +} |
| + |
| +void DecryptingVideoDecoder::DoStop() { |
| + DCHECK(read_cb_.is_null()); |
| + state_ = kUninitialized; |
| + base::ResetAndReturn(&stop_cb_).Run(); |
| +} |
| + |
| +} // namespace media |