This source file includes following definitions.
- HistogramRendererEvent
- weak_factory_
- Play
- DoPlay_Locked
- Pause
- DoPause_Locked
- Flush
- DoFlush_Locked
- ResetDecoderDone
- Stop
- Preroll
- Initialize
- OnAudioBufferStreamInitialized
- ResumeAfterUnderflow
- SetVolume
- DecodedAudioReady
- HandleSplicerBuffer
- AttemptRead
- AttemptRead_Locked
- CanRead_Locked
- SetPlaybackRate
- IsBeforePrerollTime
- UpdateEarliestEndTime_Locked
- OnRenderError
- DisableUnderflowForTesting
- HandleAbortedReadOrDecodeError
- ChangeState_Locked
- OnNewSpliceBuffer
- OnConfigChange
#include "media/filters/audio_renderer_impl.h"
#include <math.h>
#include <algorithm>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/single_thread_task_runner.h"
#include "media/base/audio_buffer.h"
#include "media/base/audio_buffer_converter.h"
#include "media/base/audio_hardware_config.h"
#include "media/base/audio_splicer.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/demuxer_stream.h"
#include "media/filters/decrypting_demuxer_stream.h"
namespace media {
namespace {
enum AudioRendererEvent {
INITIALIZED,
RENDER_ERROR,
RENDER_EVENT_MAX = RENDER_ERROR,
};
void HistogramRendererEvent(AudioRendererEvent event) {
UMA_HISTOGRAM_ENUMERATION(
"Media.AudioRendererEvents", event, RENDER_EVENT_MAX + 1);
}
}
AudioRendererImpl::AudioRendererImpl(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
media::AudioRendererSink* sink,
ScopedVector<AudioDecoder> decoders,
const SetDecryptorReadyCB& set_decryptor_ready_cb,
AudioHardwareConfig* hardware_config)
: task_runner_(task_runner),
sink_(sink),
audio_buffer_stream_(task_runner,
decoders.Pass(),
set_decryptor_ready_cb),
hardware_config_(hardware_config),
now_cb_(base::Bind(&base::TimeTicks::Now)),
state_(kUninitialized),
sink_playing_(false),
pending_read_(false),
received_end_of_stream_(false),
rendered_end_of_stream_(false),
audio_time_buffered_(kNoTimestamp()),
current_time_(kNoTimestamp()),
underflow_disabled_(false),
preroll_aborted_(false),
weak_factory_(this) {
audio_buffer_stream_.set_splice_observer(base::Bind(
&AudioRendererImpl::OnNewSpliceBuffer, weak_factory_.GetWeakPtr()));
audio_buffer_stream_.set_config_change_observer(base::Bind(
&AudioRendererImpl::OnConfigChange, weak_factory_.GetWeakPtr()));
}
AudioRendererImpl::~AudioRendererImpl() {
DCHECK(state_ == kUninitialized || state_ == kStopped);
DCHECK(!algorithm_.get());
}
void AudioRendererImpl::Play(const base::Closure& callback) {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
DCHECK_EQ(state_, kPaused);
ChangeState_Locked(kPlaying);
callback.Run();
earliest_end_time_ = now_cb_.Run();
if (algorithm_->playback_rate() != 0)
DoPlay_Locked();
else
DCHECK(!sink_playing_);
}
void AudioRendererImpl::DoPlay_Locked() {
DCHECK(task_runner_->BelongsToCurrentThread());
lock_.AssertAcquired();
earliest_end_time_ = now_cb_.Run();
if ((state_ == kPlaying || state_ == kRebuffering || state_ == kUnderflow) &&
!sink_playing_) {
{
base::AutoUnlock auto_unlock(lock_);
sink_->Play();
}
sink_playing_ = true;
}
}
void AudioRendererImpl::Pause(const base::Closure& callback) {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
DCHECK(state_ == kPlaying || state_ == kUnderflow ||
state_ == kRebuffering) << "state_ == " << state_;
ChangeState_Locked(kPaused);
DoPause_Locked();
callback.Run();
}
void AudioRendererImpl::DoPause_Locked() {
DCHECK(task_runner_->BelongsToCurrentThread());
lock_.AssertAcquired();
if (sink_playing_) {
{
base::AutoUnlock auto_unlock(lock_);
sink_->Pause();
}
sink_playing_ = false;
}
}
void AudioRendererImpl::Flush(const base::Closure& callback) {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
DCHECK_EQ(state_, kPaused);
DCHECK(flush_cb_.is_null());
flush_cb_ = callback;
if (pending_read_) {
ChangeState_Locked(kFlushing);
return;
}
DoFlush_Locked();
}
void AudioRendererImpl::DoFlush_Locked() {
DCHECK(task_runner_->BelongsToCurrentThread());
lock_.AssertAcquired();
DCHECK(!pending_read_);
DCHECK_EQ(state_, kPaused);
audio_buffer_stream_.Reset(base::Bind(&AudioRendererImpl::ResetDecoderDone,
weak_factory_.GetWeakPtr()));
}
void AudioRendererImpl::ResetDecoderDone() {
DCHECK(task_runner_->BelongsToCurrentThread());
{
base::AutoLock auto_lock(lock_);
if (state_ == kStopped)
return;
DCHECK_EQ(state_, kPaused);
DCHECK(!flush_cb_.is_null());
audio_time_buffered_ = kNoTimestamp();
current_time_ = kNoTimestamp();
received_end_of_stream_ = false;
rendered_end_of_stream_ = false;
preroll_aborted_ = false;
earliest_end_time_ = now_cb_.Run();
splicer_->Reset();
if (buffer_converter_)
buffer_converter_->Reset();
algorithm_->FlushBuffers();
}
base::ResetAndReturn(&flush_cb_).Run();
}
void AudioRendererImpl::Stop(const base::Closure& callback) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!callback.is_null());
{
base::AutoLock auto_lock(lock_);
if (state_ == kStopped) {
task_runner_->PostTask(FROM_HERE, callback);
return;
}
ChangeState_Locked(kStopped);
algorithm_.reset();
underflow_cb_.Reset();
time_cb_.Reset();
flush_cb_.Reset();
}
if (sink_) {
sink_->Stop();
sink_ = NULL;
}
audio_buffer_stream_.Stop(callback);
}
void AudioRendererImpl::Preroll(base::TimeDelta time,
const PipelineStatusCB& cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
DCHECK(!sink_playing_);
DCHECK_EQ(state_, kPaused);
DCHECK(!pending_read_) << "Pending read must complete before seeking";
DCHECK(preroll_cb_.is_null());
ChangeState_Locked(kPrerolling);
preroll_cb_ = cb;
preroll_timestamp_ = time;
AttemptRead_Locked();
}
void AudioRendererImpl::Initialize(DemuxerStream* stream,
const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb,
const base::Closure& ended_cb,
const base::Closure& disabled_cb,
const PipelineStatusCB& error_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(stream);
DCHECK_EQ(stream->type(), DemuxerStream::AUDIO);
DCHECK(!init_cb.is_null());
DCHECK(!statistics_cb.is_null());
DCHECK(!underflow_cb.is_null());
DCHECK(!time_cb.is_null());
DCHECK(!ended_cb.is_null());
DCHECK(!disabled_cb.is_null());
DCHECK(!error_cb.is_null());
DCHECK_EQ(kUninitialized, state_);
DCHECK(sink_);
state_ = kInitializing;
init_cb_ = init_cb;
underflow_cb_ = underflow_cb;
time_cb_ = time_cb;
ended_cb_ = ended_cb;
disabled_cb_ = disabled_cb;
error_cb_ = error_cb;
expecting_config_changes_ = stream->SupportsConfigChanges();
if (!expecting_config_changes_) {
int buffer_size = stream->audio_decoder_config().samples_per_second() / 100;
audio_parameters_.Reset(
AudioParameters::AUDIO_PCM_LOW_LATENCY,
stream->audio_decoder_config().channel_layout(),
ChannelLayoutToChannelCount(
stream->audio_decoder_config().channel_layout()),
0,
stream->audio_decoder_config().samples_per_second(),
stream->audio_decoder_config().bits_per_channel(),
buffer_size);
buffer_converter_.reset();
} else {
audio_parameters_ = hardware_config_->GetOutputConfig();
}
audio_buffer_stream_.Initialize(
stream,
statistics_cb,
base::Bind(&AudioRendererImpl::OnAudioBufferStreamInitialized,
weak_factory_.GetWeakPtr()));
}
void AudioRendererImpl::OnAudioBufferStreamInitialized(bool success) {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
if (state_ == kStopped) {
base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT);
return;
}
if (!success) {
state_ = kUninitialized;
base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
return;
}
if (!audio_parameters_.IsValid()) {
ChangeState_Locked(kUninitialized);
base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
return;
}
if (expecting_config_changes_)
buffer_converter_.reset(new AudioBufferConverter(audio_parameters_));
splicer_.reset(new AudioSplicer(audio_parameters_.sample_rate()));
algorithm_.reset(new AudioRendererAlgorithm());
algorithm_->Initialize(0, audio_parameters_);
ChangeState_Locked(kPaused);
HistogramRendererEvent(INITIALIZED);
{
base::AutoUnlock auto_unlock(lock_);
sink_->Initialize(audio_parameters_, this);
sink_->Start();
sink_->Pause();
}
DCHECK(!sink_playing_);
base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
}
void AudioRendererImpl::ResumeAfterUnderflow() {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
if (state_ == kUnderflow) {
if (!preroll_aborted_)
algorithm_->IncreaseQueueCapacity();
ChangeState_Locked(kRebuffering);
}
}
void AudioRendererImpl::SetVolume(float volume) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(sink_);
sink_->SetVolume(volume);
}
void AudioRendererImpl::DecodedAudioReady(
AudioBufferStream::Status status,
const scoped_refptr<AudioBuffer>& buffer) {
DVLOG(1) << __FUNCTION__ << "(" << status << ")";
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
DCHECK(state_ != kUninitialized);
CHECK(pending_read_);
pending_read_ = false;
if (status == AudioBufferStream::ABORTED ||
status == AudioBufferStream::DEMUXER_READ_ABORTED) {
HandleAbortedReadOrDecodeError(false);
return;
}
if (status == AudioBufferStream::DECODE_ERROR) {
HandleAbortedReadOrDecodeError(true);
return;
}
DCHECK_EQ(status, AudioBufferStream::OK);
DCHECK(buffer.get());
if (state_ == kFlushing) {
ChangeState_Locked(kPaused);
DoFlush_Locked();
return;
}
if (expecting_config_changes_) {
DCHECK(buffer_converter_);
buffer_converter_->AddInput(buffer);
while (buffer_converter_->HasNextBuffer()) {
if (!splicer_->AddInput(buffer_converter_->GetNextBuffer())) {
HandleAbortedReadOrDecodeError(true);
return;
}
}
} else {
if (!splicer_->AddInput(buffer)) {
HandleAbortedReadOrDecodeError(true);
return;
}
}
if (!splicer_->HasNextBuffer()) {
AttemptRead_Locked();
return;
}
bool need_another_buffer = false;
while (splicer_->HasNextBuffer())
need_another_buffer = HandleSplicerBuffer(splicer_->GetNextBuffer());
if (!need_another_buffer && !CanRead_Locked())
return;
AttemptRead_Locked();
}
bool AudioRendererImpl::HandleSplicerBuffer(
const scoped_refptr<AudioBuffer>& buffer) {
if (buffer->end_of_stream()) {
received_end_of_stream_ = true;
if (state_ == kUnderflow || state_ == kRebuffering)
ChangeState_Locked(kPlaying);
} else {
if (state_ == kPrerolling) {
if (IsBeforePrerollTime(buffer))
return true;
const base::TimeDelta trim_time =
preroll_timestamp_ - buffer->timestamp();
if (trim_time > base::TimeDelta()) {
buffer->TrimStart(buffer->frame_count() *
(static_cast<double>(trim_time.InMicroseconds()) /
buffer->duration().InMicroseconds()));
}
if (!buffer->frame_count())
return true;
}
if (state_ != kUninitialized && state_ != kStopped)
algorithm_->EnqueueBuffer(buffer);
}
switch (state_) {
case kUninitialized:
case kInitializing:
case kFlushing:
NOTREACHED();
return false;
case kPaused:
DCHECK(!pending_read_);
return false;
case kPrerolling:
if (!buffer->end_of_stream() && !algorithm_->IsQueueFull())
return true;
ChangeState_Locked(kPaused);
base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK);
return false;
case kPlaying:
case kUnderflow:
return false;
case kRebuffering:
if (!algorithm_->IsQueueFull())
return true;
ChangeState_Locked(kPlaying);
return false;
case kStopped:
return false;
}
return false;
}
void AudioRendererImpl::AttemptRead() {
base::AutoLock auto_lock(lock_);
AttemptRead_Locked();
}
void AudioRendererImpl::AttemptRead_Locked() {
DCHECK(task_runner_->BelongsToCurrentThread());
lock_.AssertAcquired();
if (!CanRead_Locked())
return;
pending_read_ = true;
audio_buffer_stream_.Read(base::Bind(&AudioRendererImpl::DecodedAudioReady,
weak_factory_.GetWeakPtr()));
}
bool AudioRendererImpl::CanRead_Locked() {
lock_.AssertAcquired();
switch (state_) {
case kUninitialized:
case kInitializing:
case kPaused:
case kFlushing:
case kStopped:
return false;
case kPrerolling:
case kPlaying:
case kUnderflow:
case kRebuffering:
break;
}
return !pending_read_ && !received_end_of_stream_ &&
!algorithm_->IsQueueFull();
}
void AudioRendererImpl::SetPlaybackRate(float playback_rate) {
DVLOG(1) << __FUNCTION__ << "(" << playback_rate << ")";
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK_GE(playback_rate, 0);
DCHECK(sink_);
base::AutoLock auto_lock(lock_);
float current_playback_rate = algorithm_->playback_rate();
if (current_playback_rate == 0 && playback_rate != 0)
DoPlay_Locked();
else if (current_playback_rate != 0 && playback_rate == 0)
DoPause_Locked();
algorithm_->SetPlaybackRate(playback_rate);
}
bool AudioRendererImpl::IsBeforePrerollTime(
const scoped_refptr<AudioBuffer>& buffer) {
DCHECK_EQ(state_, kPrerolling);
return buffer && !buffer->end_of_stream() &&
(buffer->timestamp() + buffer->duration()) < preroll_timestamp_;
}
int AudioRendererImpl::Render(AudioBus* audio_bus,
int audio_delay_milliseconds) {
const int requested_frames = audio_bus->frames();
base::TimeDelta current_time = kNoTimestamp();
base::TimeDelta max_time = kNoTimestamp();
base::TimeDelta playback_delay = base::TimeDelta::FromMilliseconds(
audio_delay_milliseconds);
int frames_written = 0;
base::Closure underflow_cb;
{
base::AutoLock auto_lock(lock_);
if (!algorithm_)
return 0;
float playback_rate = algorithm_->playback_rate();
if (playback_rate == 0)
return 0;
if (state_ != kPlaying)
return 0;
frames_written = algorithm_->FillBuffer(audio_bus, requested_frames);
if (frames_written == 0) {
const base::TimeTicks now = now_cb_.Run();
if (received_end_of_stream_ && !rendered_end_of_stream_ &&
now >= earliest_end_time_) {
rendered_end_of_stream_ = true;
ended_cb_.Run();
} else if (!received_end_of_stream_ && state_ == kPlaying &&
!underflow_disabled_) {
ChangeState_Locked(kUnderflow);
underflow_cb = underflow_cb_;
} else {
}
}
if (CanRead_Locked()) {
task_runner_->PostTask(FROM_HERE,
base::Bind(&AudioRendererImpl::AttemptRead,
weak_factory_.GetWeakPtr()));
}
if (audio_time_buffered_ != kNoTimestamp()) {
base::TimeDelta adjusted_playback_delay =
base::TimeDelta::FromMicroseconds(ceil(
playback_delay.InMicroseconds() * playback_rate));
base::TimeDelta previous_time = current_time_;
current_time_ = audio_time_buffered_ - adjusted_playback_delay;
if (current_time_ > previous_time && !rendered_end_of_stream_) {
current_time = current_time_;
}
}
max_time = algorithm_->GetTime();
audio_time_buffered_ = max_time;
if (frames_written > 0) {
UpdateEarliestEndTime_Locked(
frames_written, playback_delay, now_cb_.Run());
}
}
if (current_time != kNoTimestamp() && max_time != kNoTimestamp())
time_cb_.Run(current_time, max_time);
if (!underflow_cb.is_null())
underflow_cb.Run();
DCHECK_LE(frames_written, requested_frames);
return frames_written;
}
void AudioRendererImpl::UpdateEarliestEndTime_Locked(
int frames_filled, const base::TimeDelta& playback_delay,
const base::TimeTicks& time_now) {
DCHECK_GT(frames_filled, 0);
base::TimeDelta predicted_play_time = base::TimeDelta::FromMicroseconds(
static_cast<float>(frames_filled) * base::Time::kMicrosecondsPerSecond /
audio_parameters_.sample_rate());
lock_.AssertAcquired();
earliest_end_time_ = std::max(
earliest_end_time_, time_now + playback_delay + predicted_play_time);
}
void AudioRendererImpl::OnRenderError() {
HistogramRendererEvent(RENDER_ERROR);
disabled_cb_.Run();
}
void AudioRendererImpl::DisableUnderflowForTesting() {
underflow_disabled_ = true;
}
void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error) {
lock_.AssertAcquired();
PipelineStatus status = is_decode_error ? PIPELINE_ERROR_DECODE : PIPELINE_OK;
switch (state_) {
case kUninitialized:
case kInitializing:
NOTREACHED();
return;
case kPaused:
if (status != PIPELINE_OK)
error_cb_.Run(status);
return;
case kFlushing:
ChangeState_Locked(kPaused);
if (status == PIPELINE_OK) {
DoFlush_Locked();
return;
}
error_cb_.Run(status);
base::ResetAndReturn(&flush_cb_).Run();
return;
case kPrerolling:
preroll_aborted_ = !is_decode_error;
ChangeState_Locked(kPaused);
base::ResetAndReturn(&preroll_cb_).Run(status);
return;
case kPlaying:
case kUnderflow:
case kRebuffering:
case kStopped:
if (status != PIPELINE_OK)
error_cb_.Run(status);
return;
}
}
void AudioRendererImpl::ChangeState_Locked(State new_state) {
DVLOG(1) << __FUNCTION__ << " : " << state_ << " -> " << new_state;
lock_.AssertAcquired();
state_ = new_state;
}
void AudioRendererImpl::OnNewSpliceBuffer(base::TimeDelta splice_timestamp) {
DCHECK(task_runner_->BelongsToCurrentThread());
splicer_->SetSpliceTimestamp(splice_timestamp);
}
void AudioRendererImpl::OnConfigChange() {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(expecting_config_changes_);
buffer_converter_->ResetTimestampState();
}
}