blob: 85964c27b3fb15862ce6e08196d3b860bf9afc77 [file] [log] [blame]
// 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_audio_decoder.h"
#include <cstdlib>
#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/bind_to_loop.h"
#include "media/base/buffers.h"
#include "media/base/data_buffer.h"
#include "media/base/decryptor.h"
#include "media/base/demuxer_stream.h"
#include "media/base/pipeline.h"
#include "media/base/decoder_buffer.h"
namespace media {
#define BIND_TO_LOOP(function) \
media::BindToLoop(message_loop_, base::Bind(function, this))
static inline bool IsOutOfSync(const base::TimeDelta& timestamp_1,
const base::TimeDelta& timestamp_2) {
// Out of sync of 100ms would be pretty noticeable and we should keep any
// drift below that.
const int64 kOutOfSyncThresholdInMicroseconds = 100000;
return std::abs(timestamp_1.InMicroseconds() - timestamp_2.InMicroseconds()) >
kOutOfSyncThresholdInMicroseconds;
}
DecryptingAudioDecoder::DecryptingAudioDecoder(
const scoped_refptr<base::MessageLoopProxy>& message_loop,
const SetDecryptorReadyCB& set_decryptor_ready_cb)
: message_loop_(message_loop),
state_(kUninitialized),
set_decryptor_ready_cb_(set_decryptor_ready_cb),
decryptor_(NULL),
key_added_while_decode_pending_(false),
bits_per_channel_(0),
channel_layout_(CHANNEL_LAYOUT_NONE),
samples_per_second_(0),
bytes_per_sample_(0),
output_timestamp_base_(kNoTimestamp()),
total_samples_decoded_(0) {
}
void DecryptingAudioDecoder::Initialize(
const scoped_refptr<DemuxerStream>& stream,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) {
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(FROM_HERE, base::Bind(
&DecryptingAudioDecoder::DoInitialize, this,
stream, status_cb, statistics_cb));
return;
}
DoInitialize(stream, status_cb, statistics_cb);
}
void DecryptingAudioDecoder::Read(const ReadCB& read_cb) {
// Complete operation asynchronously on different stack of execution as per
// the API contract of AudioDecoder::Read()
message_loop_->PostTask(FROM_HERE, base::Bind(
&DecryptingAudioDecoder::DoRead, this, read_cb));
}
void DecryptingAudioDecoder::Reset(const base::Closure& closure) {
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(FROM_HERE, base::Bind(
&DecryptingAudioDecoder::Reset, this, closure));
return;
}
DVLOG(2) << "Reset() - state: " << state_;
DCHECK(state_ == kIdle ||
state_ == kPendingConfigChange ||
state_ == kPendingDemuxerRead ||
state_ == kPendingDecode ||
state_ == kWaitingForKey ||
state_ == kDecodeFinished) << state_;
DCHECK(init_cb_.is_null()); // No Reset() during pending initialization.
DCHECK(reset_cb_.is_null());
reset_cb_ = closure;
decryptor_->ResetDecoder(Decryptor::kAudio);
// 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 DoDecryptAndDecodeBuffer() and
// DoDeliverFrame().
if (state_ == kPendingConfigChange ||
state_ == kPendingDemuxerRead ||
state_ == kPendingDecode) {
DCHECK(!read_cb_.is_null());
return;
}
if (state_ == kWaitingForKey) {
DCHECK(!read_cb_.is_null());
pending_buffer_to_decode_ = NULL;
base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
}
DCHECK(read_cb_.is_null());
DoReset();
}
int DecryptingAudioDecoder::bits_per_channel() {
return bits_per_channel_;
}
ChannelLayout DecryptingAudioDecoder::channel_layout() {
return channel_layout_;
}
int DecryptingAudioDecoder::samples_per_second() {
return samples_per_second_;
}
DecryptingAudioDecoder::~DecryptingAudioDecoder() {
}
void DecryptingAudioDecoder::DoInitialize(
const scoped_refptr<DemuxerStream>& stream,
const PipelineStatusCB& status_cb,
const StatisticsCB& statistics_cb) {
DVLOG(2) << "DoInitialize()";
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, kUninitialized) << state_;
DCHECK(stream);
const AudioDecoderConfig& config = stream->audio_decoder_config();
if (!config.IsValidConfig()) {
DLOG(ERROR) << "Invalid audio stream config.";
status_cb.Run(PIPELINE_ERROR_DECODE);
return;
}
// DecryptingAudioDecoder only accepts potentially encrypted stream.
if (!config.is_encrypted()) {
status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
return;
}
DCHECK(!demuxer_stream_);
demuxer_stream_ = stream;
statistics_cb_ = statistics_cb;
init_cb_ = status_cb;
state_ = kDecryptorRequested;
set_decryptor_ready_cb_.Run(
BIND_TO_LOOP(&DecryptingAudioDecoder::SetDecryptor));
}
void DecryptingAudioDecoder::SetDecryptor(Decryptor* decryptor) {
DVLOG(2) << "SetDecryptor()";
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, kDecryptorRequested) << state_;
DCHECK(!init_cb_.is_null());
DCHECK(!set_decryptor_ready_cb_.is_null());
set_decryptor_ready_cb_.Reset();
if (!decryptor) {
base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
// TODO(xhwang): Add kError state. See http://crbug.com/251503
state_ = kDecodeFinished;
return;
}
decryptor_ = decryptor;
scoped_ptr<AudioDecoderConfig> scoped_config(new AudioDecoderConfig());
scoped_config->CopyFrom(demuxer_stream_->audio_decoder_config());
state_ = kPendingDecoderInit;
decryptor_->InitializeAudioDecoder(
scoped_config.Pass(),
BIND_TO_LOOP(&DecryptingAudioDecoder::FinishInitialization));
}
void DecryptingAudioDecoder::FinishInitialization(bool success) {
DVLOG(2) << "FinishInitialization()";
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, kPendingDecoderInit) << state_;
DCHECK(!init_cb_.is_null());
DCHECK(reset_cb_.is_null()); // No Reset() before initialization finished.
DCHECK(read_cb_.is_null()); // No Read() before initialization finished.
if (!success) {
base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
state_ = kDecodeFinished;
return;
}
// Success!
const AudioDecoderConfig& config = demuxer_stream_->audio_decoder_config();
bits_per_channel_ = config.bits_per_channel();
channel_layout_ = config.channel_layout();
samples_per_second_ = config.samples_per_second();
const int kBitsPerByte = 8;
bytes_per_sample_ = ChannelLayoutToChannelCount(channel_layout_) *
bits_per_channel_ / kBitsPerByte;
decryptor_->RegisterKeyAddedCB(
Decryptor::kAudio, BIND_TO_LOOP(&DecryptingAudioDecoder::OnKeyAdded));
state_ = kIdle;
base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
}
void DecryptingAudioDecoder::FinishConfigChange(bool success) {
DVLOG(2) << "FinishConfigChange()";
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, kPendingConfigChange) << state_;
DCHECK(!read_cb_.is_null());
if (!success) {
base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
state_ = kDecodeFinished;
if (!reset_cb_.is_null())
base::ResetAndReturn(&reset_cb_).Run();
return;
}
// Config change succeeded.
if (!reset_cb_.is_null()) {
base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
DoReset();
return;
}
state_ = kPendingDemuxerRead;
ReadFromDemuxerStream();
}
void DecryptingAudioDecoder::DoRead(const ReadCB& read_cb) {
DVLOG(3) << "DoRead()";
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK(state_ == kIdle || state_ == kDecodeFinished) << state_;
DCHECK(!read_cb.is_null());
CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported.";
// Return empty (end-of-stream) frames if decoding has finished.
if (state_ == kDecodeFinished) {
read_cb.Run(kOk, scoped_refptr<Buffer>(new DataBuffer(0)));
return;
}
if (!queued_audio_frames_.empty()) {
read_cb.Run(kOk, queued_audio_frames_.front());
queued_audio_frames_.pop_front();
return;
}
read_cb_ = read_cb;
state_ = kPendingDemuxerRead;
ReadFromDemuxerStream();
}
void DecryptingAudioDecoder::ReadFromDemuxerStream() {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, kPendingDemuxerRead) << state_;
DCHECK(!read_cb_.is_null());
demuxer_stream_->Read(
base::Bind(&DecryptingAudioDecoder::DoDecryptAndDecodeBuffer, this));
}
void DecryptingAudioDecoder::DoDecryptAndDecodeBuffer(
DemuxerStream::Status status,
const scoped_refptr<DecoderBuffer>& buffer) {
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(FROM_HERE, base::Bind(
&DecryptingAudioDecoder::DoDecryptAndDecodeBuffer, this,
status, buffer));
return;
}
DVLOG(3) << "DoDecryptAndDecodeBuffer()";
DCHECK_EQ(state_, kPendingDemuxerRead) << state_;
DCHECK(!read_cb_.is_null());
DCHECK_EQ(buffer != NULL, status == DemuxerStream::kOk) << status;
if (status == DemuxerStream::kConfigChanged) {
DVLOG(2) << "DoDecryptAndDecodeBuffer() - kConfigChanged";
scoped_ptr<AudioDecoderConfig> scoped_config(new AudioDecoderConfig());
scoped_config->CopyFrom(demuxer_stream_->audio_decoder_config());
state_ = kPendingConfigChange;
decryptor_->DeinitializeDecoder(Decryptor::kAudio);
decryptor_->InitializeAudioDecoder(
scoped_config.Pass(), BindToCurrentLoop(base::Bind(
&DecryptingAudioDecoder::FinishConfigChange, this)));
return;
}
if (!reset_cb_.is_null()) {
base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
DoReset();
return;
}
if (status == DemuxerStream::kAborted) {
DVLOG(2) << "DoDecryptAndDecodeBuffer() - kAborted";
state_ = kIdle;
base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
return;
}
DCHECK_EQ(status, DemuxerStream::kOk);
// Initialize the |next_output_timestamp_| to be the timestamp of the first
// non-EOS buffer.
if (output_timestamp_base_ == kNoTimestamp() && !buffer->IsEndOfStream()) {
DCHECK_EQ(total_samples_decoded_, 0);
output_timestamp_base_ = buffer->GetTimestamp();
}
pending_buffer_to_decode_ = buffer;
state_ = kPendingDecode;
DecodePendingBuffer();
}
void DecryptingAudioDecoder::DecodePendingBuffer() {
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, kPendingDecode) << state_;
decryptor_->DecryptAndDecodeAudio(
pending_buffer_to_decode_,
base::Bind(&DecryptingAudioDecoder::DeliverFrame, this,
pending_buffer_to_decode_->GetDataSize()));
}
void DecryptingAudioDecoder::DeliverFrame(
int buffer_size,
Decryptor::Status status,
const Decryptor::AudioBuffers& frames) {
// We need to force task post here because the AudioDecodeCB 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(
&DecryptingAudioDecoder::DoDeliverFrame, this,
buffer_size, status, frames));
}
void DecryptingAudioDecoder::DoDeliverFrame(
int buffer_size,
Decryptor::Status status,
const Decryptor::AudioBuffers& frames) {
DVLOG(3) << "DoDeliverFrame() - status: " << status;
DCHECK(message_loop_->BelongsToCurrentThread());
DCHECK_EQ(state_, kPendingDecode) << state_;
DCHECK(!read_cb_.is_null());
DCHECK(pending_buffer_to_decode_);
DCHECK(queued_audio_frames_.empty());
bool need_to_try_again_if_nokey_is_returned = key_added_while_decode_pending_;
key_added_while_decode_pending_ = false;
scoped_refptr<DecoderBuffer> scoped_pending_buffer_to_decode =
pending_buffer_to_decode_;
pending_buffer_to_decode_ = NULL;
if (!reset_cb_.is_null()) {
base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
DoReset();
return;
}
DCHECK_EQ(status == Decryptor::kSuccess, !frames.empty());
if (status == Decryptor::kError) {
DVLOG(2) << "DoDeliverFrame() - kError";
state_ = kDecodeFinished;
base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
return;
}
if (status == Decryptor::kNoKey) {
DVLOG(2) << "DoDeliverFrame() - kNoKey";
// Set |pending_buffer_to_decode_| back as we need to try decoding the
// pending buffer again when new key is added to the decryptor.
pending_buffer_to_decode_ = scoped_pending_buffer_to_decode;
if (need_to_try_again_if_nokey_is_returned) {
// The |state_| is still kPendingDecode.
DecodePendingBuffer();
return;
}
state_ = kWaitingForKey;
return;
}
// The buffer has been accepted by the decoder, let's report statistics.
if (buffer_size) {
PipelineStatistics statistics;
statistics.audio_bytes_decoded = buffer_size;
statistics_cb_.Run(statistics);
}
if (status == Decryptor::kNeedMoreData) {
DVLOG(2) << "DoDeliverFrame() - kNeedMoreData";
if (scoped_pending_buffer_to_decode->IsEndOfStream()) {
state_ = kDecodeFinished;
base::ResetAndReturn(&read_cb_).Run(
kOk, scoped_refptr<Buffer>(new DataBuffer(0)));
return;
}
state_ = kPendingDemuxerRead;
ReadFromDemuxerStream();
return;
}
DCHECK_EQ(status, Decryptor::kSuccess);
DCHECK(!frames.empty());
EnqueueFrames(frames);
state_ = kIdle;
base::ResetAndReturn(&read_cb_).Run(kOk, queued_audio_frames_.front());
queued_audio_frames_.pop_front();
}
void DecryptingAudioDecoder::OnKeyAdded() {
DCHECK(message_loop_->BelongsToCurrentThread());
if (state_ == kPendingDecode) {
key_added_while_decode_pending_ = true;
return;
}
if (state_ == kWaitingForKey) {
state_ = kPendingDecode;
DecodePendingBuffer();
}
}
void DecryptingAudioDecoder::DoReset() {
DCHECK(init_cb_.is_null());
DCHECK(read_cb_.is_null());
output_timestamp_base_ = kNoTimestamp();
total_samples_decoded_ = 0;
state_ = kIdle;
base::ResetAndReturn(&reset_cb_).Run();
}
void DecryptingAudioDecoder::EnqueueFrames(
const Decryptor::AudioBuffers& frames) {
queued_audio_frames_ = frames;
for (Decryptor::AudioBuffers::iterator iter = queued_audio_frames_.begin();
iter != queued_audio_frames_.end();
++iter) {
scoped_refptr<Buffer>& frame = *iter;
DCHECK(!frame->IsEndOfStream()) << "EOS frame returned.";
DCHECK_GT(frame->GetDataSize(), 0) << "Empty frame returned.";
base::TimeDelta cur_timestamp = output_timestamp_base_ +
NumberOfSamplesToDuration(total_samples_decoded_);
if (IsOutOfSync(cur_timestamp, frame->GetTimestamp())) {
DVLOG(1) << "Timestamp returned by the decoder ("
<< frame->GetTimestamp().InMilliseconds() << " ms)"
<< " does not match the input timestamp and number of samples"
<< " decoded (" << cur_timestamp.InMilliseconds() << " ms).";
}
frame->SetTimestamp(cur_timestamp);
int frame_size = frame->GetDataSize();
DCHECK_EQ(frame_size % bytes_per_sample_, 0) <<
"Decoder didn't output full samples";
int samples_decoded = frame_size / bytes_per_sample_;
total_samples_decoded_ += samples_decoded;
base::TimeDelta next_timestamp = output_timestamp_base_ +
NumberOfSamplesToDuration(total_samples_decoded_);
base::TimeDelta duration = next_timestamp - cur_timestamp;
frame->SetDuration(duration);
}
}
base::TimeDelta DecryptingAudioDecoder::NumberOfSamplesToDuration(
int number_of_samples) const {
DCHECK(samples_per_second_);
return base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond *
number_of_samples /
samples_per_second_);
}
} // namespace media