| Index: media/filters/decrypting_demuxer_stream.cc
|
| diff --git a/media/filters/decrypting_demuxer_stream.cc b/media/filters/decrypting_demuxer_stream.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..02ee0c501d11e78bc2820007f91b847758940d1d
|
| --- /dev/null
|
| +++ b/media/filters/decrypting_demuxer_stream.cc
|
| @@ -0,0 +1,350 @@
|
| +// 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_demuxer_stream.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback_helpers.h"
|
| +#include "base/location.h"
|
| +#include "base/logging.h"
|
| +#include "base/message_loop_proxy.h"
|
| +#include "media/base/audio_decoder_config.h"
|
| +#include "media/base/video_decoder_config.h"
|
| +#include "media/base/bind_to_loop.h"
|
| +#include "media/base/decoder_buffer.h"
|
| +#include "media/base/decryptor.h"
|
| +#include "media/base/demuxer_stream.h"
|
| +#include "media/base/pipeline.h"
|
| +
|
| +namespace media {
|
| +
|
| +#define BIND_TO_LOOP(function) \
|
| + media::BindToLoop(message_loop_, base::Bind(function, this))
|
| +
|
| +DecryptingDemuxerStream::DecryptingDemuxerStream(
|
| + const MessageLoopFactoryCB& message_loop_factory_cb,
|
| + const RequestDecryptorNotificationCB& request_decryptor_notification_cb)
|
| + : message_loop_factory_cb_(message_loop_factory_cb),
|
| + state_(kUninitialized),
|
| + stream_type_(UNKNOWN),
|
| + request_decryptor_notification_cb_(request_decryptor_notification_cb),
|
| + decryptor_(NULL),
|
| + key_added_while_decrypt_pending_(false) {
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::Initialize(
|
| + const scoped_refptr<DemuxerStream>& stream,
|
| + const PipelineStatusCB& status_cb) {
|
| + DCHECK(!message_loop_);
|
| + message_loop_ = base::ResetAndReturn(&message_loop_factory_cb_).Run();
|
| + message_loop_->PostTask(FROM_HERE, base::Bind(
|
| + &DecryptingDemuxerStream::DoInitialize, this,
|
| + stream, status_cb));
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::Read(const ReadCB& read_cb) {
|
| + DVLOG(3) << "Read()";
|
| + DCHECK(message_loop_->BelongsToCurrentThread());
|
| + DCHECK_EQ(state_, kIdle) << state_;
|
| + DCHECK(!read_cb.is_null());
|
| + CHECK(read_cb_.is_null()) << "Overlapping reads are not supported.";
|
| +
|
| + read_cb_ = read_cb;
|
| + state_ = kPendingDemuxerRead;
|
| + demuxer_stream_->Read(
|
| + base::Bind(&DecryptingDemuxerStream::DecryptBuffer, this));
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::Reset(const base::Closure& closure) {
|
| + if (!message_loop_->BelongsToCurrentThread()) {
|
| + message_loop_->PostTask(FROM_HERE, base::Bind(
|
| + &DecryptingDemuxerStream::Reset, this, closure));
|
| + return;
|
| + }
|
| +
|
| + DVLOG(2) << "Reset() - state: " << state_;
|
| + DCHECK(state_ != kUninitialized && state_ != kDecryptorRequested) << state_;
|
| + DCHECK(init_cb_.is_null()); // No Reset() during pending initialization.
|
| + DCHECK(reset_cb_.is_null());
|
| +
|
| + reset_cb_ = closure;
|
| +
|
| + decryptor_->CancelDecrypt(GetDecryptorStreamType());
|
| +
|
| + // Reset() cannot complete if the read callback is still pending.
|
| + // Defer the resetting process in this case. The |reset_cb_| will be fired
|
| + // after the read callback is fired - see DoDecryptBuffer() and
|
| + // DoDeliverBuffer().
|
| + if (state_ == kPendingDemuxerRead || state_ == kPendingDecrypt) {
|
| + DCHECK(!read_cb_.is_null());
|
| + return;
|
| + }
|
| +
|
| + if (state_ == kWaitingForKey) {
|
| + DCHECK(!read_cb_.is_null());
|
| + pending_buffer_to_decrypt_ = NULL;
|
| + base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
|
| + }
|
| +
|
| + DCHECK(read_cb_.is_null());
|
| + DoReset();
|
| +}
|
| +
|
| +const AudioDecoderConfig& DecryptingDemuxerStream::audio_decoder_config() {
|
| + DCHECK(state_ != kUninitialized && state_ != kDecryptorRequested) << state_;
|
| + CHECK_EQ(stream_type_, AUDIO);
|
| + return *audio_config_;
|
| +}
|
| +
|
| +const VideoDecoderConfig& DecryptingDemuxerStream::video_decoder_config() {
|
| + DCHECK(state_ != kUninitialized && state_ != kDecryptorRequested) << state_;
|
| + CHECK_EQ(stream_type_, VIDEO);
|
| + return *video_config_;
|
| +}
|
| +
|
| +DemuxerStream::Type DecryptingDemuxerStream::type() {
|
| + DCHECK(state_ != kUninitialized && state_ != kDecryptorRequested) << state_;
|
| + return stream_type_;
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::EnableBitstreamConverter() {
|
| + demuxer_stream_->EnableBitstreamConverter();
|
| +}
|
| +
|
| +DecryptingDemuxerStream::~DecryptingDemuxerStream() {}
|
| +
|
| +void DecryptingDemuxerStream::DoInitialize(
|
| + const scoped_refptr<DemuxerStream>& stream,
|
| + const PipelineStatusCB& status_cb) {
|
| + DVLOG(2) << "DoInitialize()";
|
| + DCHECK(message_loop_->BelongsToCurrentThread());
|
| + DCHECK_EQ(state_, kUninitialized) << state_;
|
| +
|
| + // Only valid potentially encrypted audio or video stream is accepted.
|
| + if (!((stream->type() == AUDIO &&
|
| + stream->audio_decoder_config().IsValidConfig() &&
|
| + stream->audio_decoder_config().is_encrypted()) ||
|
| + (stream->type() == VIDEO &&
|
| + stream->video_decoder_config().IsValidConfig() &&
|
| + stream->video_decoder_config().is_encrypted()))) {
|
| + status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
|
| + return;
|
| + }
|
| +
|
| + DCHECK(!demuxer_stream_);
|
| + demuxer_stream_ = stream;
|
| + stream_type_ = stream->type();
|
| + init_cb_ = status_cb;
|
| +
|
| + state_ = kDecryptorRequested;
|
| + request_decryptor_notification_cb_.Run(
|
| + BIND_TO_LOOP(&DecryptingDemuxerStream::SetDecryptor));
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::SetDecryptor(Decryptor* decryptor) {
|
| + DVLOG(2) << "SetDecryptor()";
|
| + DCHECK(message_loop_->BelongsToCurrentThread());
|
| + DCHECK_EQ(state_, kDecryptorRequested) << state_;
|
| + DCHECK(!init_cb_.is_null());
|
| + DCHECK(!request_decryptor_notification_cb_.is_null());
|
| +
|
| + request_decryptor_notification_cb_.Reset();
|
| + decryptor_ = decryptor;
|
| +
|
| + switch (stream_type_) {
|
| + case AUDIO: {
|
| + const AudioDecoderConfig& input_audio_config =
|
| + demuxer_stream_->audio_decoder_config();
|
| + audio_config_.reset(new AudioDecoderConfig());
|
| + audio_config_->Initialize(input_audio_config.codec(),
|
| + input_audio_config.bits_per_channel(),
|
| + input_audio_config.channel_layout(),
|
| + input_audio_config.samples_per_second(),
|
| + input_audio_config.extra_data(),
|
| + input_audio_config.extra_data_size(),
|
| + false, // Output audio is not encrypted.
|
| + false);
|
| + break;
|
| + }
|
| +
|
| + case VIDEO: {
|
| + const VideoDecoderConfig& input_video_config =
|
| + demuxer_stream_->video_decoder_config();
|
| + video_config_.reset(new VideoDecoderConfig());
|
| + video_config_->Initialize(input_video_config.codec(),
|
| + input_video_config.profile(),
|
| + input_video_config.format(),
|
| + input_video_config.coded_size(),
|
| + input_video_config.visible_rect(),
|
| + input_video_config.natural_size(),
|
| + input_video_config.extra_data(),
|
| + input_video_config.extra_data_size(),
|
| + false, // Output video is not encrypted.
|
| + false);
|
| + break;
|
| + }
|
| +
|
| + default:
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + decryptor_->RegisterKeyAddedCB(
|
| + GetDecryptorStreamType(),
|
| + BIND_TO_LOOP(&DecryptingDemuxerStream::OnKeyAdded));
|
| +
|
| + state_ = kIdle;
|
| + base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::DecryptBuffer(
|
| + DemuxerStream::Status status,
|
| + const scoped_refptr<DecoderBuffer>& buffer) {
|
| + // In theory, we don't need to force post the task here, because we do a
|
| + // force task post in DeliverBuffer(). Therefore, even if
|
| + // demuxer_stream_->Read() execute the read callback on the same execution
|
| + // stack we are still fine. But it looks like a force post task makes the
|
| + // logic more understandable and manageable, so why not?
|
| + message_loop_->PostTask(FROM_HERE, base::Bind(
|
| + &DecryptingDemuxerStream::DoDecryptBuffer, this, status, buffer));
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::DoDecryptBuffer(
|
| + DemuxerStream::Status status,
|
| + const scoped_refptr<DecoderBuffer>& buffer) {
|
| + DVLOG(3) << "DoDecryptBuffer()";
|
| + DCHECK(message_loop_->BelongsToCurrentThread());
|
| + DCHECK_EQ(state_, kPendingDemuxerRead) << state_;
|
| + DCHECK(!read_cb_.is_null());
|
| + DCHECK_EQ(buffer != NULL, status == kOk) << status;
|
| +
|
| + if (!reset_cb_.is_null()) {
|
| + base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
|
| + DoReset();
|
| + return;
|
| + }
|
| +
|
| + if (status == kAborted) {
|
| + DVLOG(2) << "DoDecryptBuffer() - kAborted.";
|
| + state_ = kIdle;
|
| + base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
|
| + return;
|
| + }
|
| +
|
| + if (status == kConfigChanged) {
|
| + DVLOG(2) << "DoDecryptBuffer() - kConfigChanged.";
|
| + state_ = kIdle;
|
| + // TODO(xhwang): Support kConfigChanged!
|
| + base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
|
| + return;
|
| + }
|
| +
|
| + if (buffer->IsEndOfStream()) {
|
| + DVLOG(2) << "DoDecryptBuffer() - EOS buffer.";
|
| + state_ = kIdle;
|
| + base::ResetAndReturn(&read_cb_).Run(status, buffer);
|
| + return;
|
| + }
|
| +
|
| + pending_buffer_to_decrypt_ = buffer;
|
| + state_ = kPendingDecrypt;
|
| + DecryptPendingBuffer();
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::DecryptPendingBuffer() {
|
| + DCHECK(message_loop_->BelongsToCurrentThread());
|
| + DCHECK_EQ(state_, kPendingDecrypt) << state_;
|
| + decryptor_->Decrypt(
|
| + GetDecryptorStreamType(),
|
| + pending_buffer_to_decrypt_,
|
| + base::Bind(&DecryptingDemuxerStream::DeliverBuffer, this));
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::DeliverBuffer(
|
| + Decryptor::Status status,
|
| + const scoped_refptr<DecoderBuffer>& decrypted_buffer) {
|
| + // We need to force task post here because the DecryptCB can be executed
|
| + // synchronously in Reset(). Instead of using more complicated logic in
|
| + // those function to fix it, why not force task post here to make everything
|
| + // simple and clear?
|
| + message_loop_->PostTask(FROM_HERE, base::Bind(
|
| + &DecryptingDemuxerStream::DoDeliverBuffer, this,
|
| + status, decrypted_buffer));
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::DoDeliverBuffer(
|
| + Decryptor::Status status,
|
| + const scoped_refptr<DecoderBuffer>& decrypted_buffer) {
|
| + DVLOG(3) << "DoDeliverBuffer() - status: " << status;
|
| + DCHECK(message_loop_->BelongsToCurrentThread());
|
| + DCHECK_EQ(state_, kPendingDecrypt) << state_;
|
| + DCHECK_NE(status, Decryptor::kNeedMoreData);
|
| + DCHECK(!read_cb_.is_null());
|
| + DCHECK(pending_buffer_to_decrypt_);
|
| +
|
| + bool need_to_try_again_if_nokey = key_added_while_decrypt_pending_;
|
| + key_added_while_decrypt_pending_ = false;
|
| +
|
| + if (!reset_cb_.is_null()) {
|
| + pending_buffer_to_decrypt_ = NULL;
|
| + base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
|
| + DoReset();
|
| + return;
|
| + }
|
| +
|
| + DCHECK_EQ(status == Decryptor::kSuccess, decrypted_buffer.get() != NULL);
|
| +
|
| + if (status == Decryptor::kError) {
|
| + DVLOG(2) << "DoDeliverBuffer() - kError";
|
| + pending_buffer_to_decrypt_ = NULL;
|
| + state_ = kIdle;
|
| + base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
|
| + return;
|
| + }
|
| +
|
| + if (status == Decryptor::kNoKey) {
|
| + DVLOG(2) << "DoDeliverBuffer() - kNoKey";
|
| + if (need_to_try_again_if_nokey) {
|
| + // The |state_| is still kPendingDecrypt.
|
| + DecryptPendingBuffer();
|
| + return;
|
| + }
|
| +
|
| + state_ = kWaitingForKey;
|
| + return;
|
| + }
|
| +
|
| + DCHECK_EQ(status, Decryptor::kSuccess);
|
| + pending_buffer_to_decrypt_ = NULL;
|
| + state_ = kIdle;
|
| + base::ResetAndReturn(&read_cb_).Run(kOk, decrypted_buffer);
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::OnKeyAdded() {
|
| + DCHECK(message_loop_->BelongsToCurrentThread());
|
| +
|
| + if (state_ == kPendingDecrypt) {
|
| + key_added_while_decrypt_pending_ = true;
|
| + return;
|
| + }
|
| +
|
| + if (state_ == kWaitingForKey) {
|
| + state_ = kPendingDecrypt;
|
| + DecryptPendingBuffer();
|
| + }
|
| +}
|
| +
|
| +void DecryptingDemuxerStream::DoReset() {
|
| + DCHECK(init_cb_.is_null());
|
| + DCHECK(read_cb_.is_null());
|
| + state_ = kIdle;
|
| + base::ResetAndReturn(&reset_cb_).Run();
|
| +}
|
| +
|
| +Decryptor::StreamType DecryptingDemuxerStream::GetDecryptorStreamType() const {
|
| + DCHECK(stream_type_ == AUDIO || stream_type_ == VIDEO);
|
| + return stream_type_ == AUDIO ? Decryptor::kAudio : Decryptor::kVideo;
|
| +}
|
| +
|
| +} // namespace media
|
|
|