Commit ff625610 authored by scherkus@chromium.org's avatar scherkus@chromium.org

Move AudioRenderer out of Filter heirarchy.

BUG=108341
TEST=none


Review URL: https://chromiumcodereview.appspot.com/10753021

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@147619 0039d316-1c4b-4281-b951-d872f2087c98
parent 35daebe0
......@@ -16,37 +16,78 @@ namespace media {
class AudioDecoder;
class MEDIA_EXPORT AudioRenderer : public Filter {
class MEDIA_EXPORT AudioRenderer
: public base::RefCountedThreadSafe<AudioRenderer> {
public:
// Used to update the pipeline's clock time. The first parameter is the
// current time, and the second parameter is the time that the clock must not
// exceed.
// First parameter is the current time that has been rendered.
// Second parameter is the maximum time value that the clock cannot exceed.
typedef base::Callback<void(base::TimeDelta, base::TimeDelta)> TimeCB;
// Initialize a AudioRenderer with the given AudioDecoder, executing the
// |init_cb| upon completion. |underflow_cb| is called when the
// renderer runs out of data to pass to the audio card during playback.
// If the |underflow_cb| is called ResumeAfterUnderflow() must be called
// |init_cb| upon completion.
//
// |underflow_cb| is executed when the renderer runs out of data to pass to
// the audio card during playback. ResumeAfterUnderflow() must be called
// to resume playback. Pause(), Seek(), or Stop() cancels the underflow
// condition.
//
// |time_cb| is executed whenever time has advanced by way of audio rendering.
//
// |ended_cb| is executed when audio rendering has reached the end of stream.
//
// |disabled_cb| is executed when audio rendering has been disabled due to
// external factors (i.e., device was removed). |time_cb| will no longer be
// executed.
//
// |error_cb| is executed if an error was encountered.
virtual void Initialize(const scoped_refptr<AudioDecoder>& decoder,
const PipelineStatusCB& init_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb) = 0;
const TimeCB& time_cb,
const base::Closure& ended_cb,
const base::Closure& disabled_cb,
const PipelineStatusCB& error_cb) = 0;
// Returns true if this filter has received and processed an end-of-stream
// buffer.
// Start audio decoding and rendering at the current playback rate, executing
// |callback| when playback is underway.
virtual void Play(const base::Closure& callback) = 0;
// Temporarily suspend decoding and rendering audio, executing |callback| when
// playback has been suspended.
virtual void Pause(const base::Closure& callback) = 0;
// Discard any audio data, executing |callback| when completed.
virtual void Flush(const base::Closure& callback) = 0;
// Start prerolling audio data for samples starting at |time|, executing
// |callback| when completed.
//
// Only valid to call after a successful Initialize() or Flush().
//
// TODO(scherkus): rename this to Preroll().
virtual void Seek(base::TimeDelta time, const PipelineStatusCB& callback) = 0;
// Stop all operations in preparation for being deleted, executing |callback|
// when complete.
virtual void Stop(const base::Closure& callback) = 0;
// Updates the current playback rate.
virtual void SetPlaybackRate(float playback_rate) = 0;
// Returns true if all audio data has been played back by the audio device.
virtual bool HasEnded() = 0;
// Sets the output volume.
virtual void SetVolume(float volume) = 0;
// Resumes playback after underflow occurs.
//
// |buffer_more_audio| is set to true if you want to increase the size of the
// decoded audio buffer.
virtual void ResumeAfterUnderflow(bool buffer_more_audio) = 0;
protected:
friend class base::RefCountedThreadSafe<AudioRenderer>;
virtual ~AudioRenderer() {}
};
......
// 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/base/callback_util.h"
#include "base/bind.h"
#include "base/synchronization/lock.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
namespace media {
// Executes the given closure if and only if the closure returned by
// GetClosure() has been executed exactly |count| times.
//
// |done_cb| will be executed on the same thread that created the CountingCB.
class CountingCB : public base::RefCountedThreadSafe<CountingCB> {
public:
CountingCB(int count, const base::Closure& done_cb)
: message_loop_(base::MessageLoopProxy::current()),
count_(count),
done_cb_(done_cb) {
}
// Returns a closure bound to this object.
base::Closure GetClosure() {
return base::Bind(&CountingCB::OnCallback, this);
}
protected:
friend class base::RefCountedThreadSafe<CountingCB>;
virtual ~CountingCB() {}
private:
void OnCallback() {
{
base::AutoLock l(lock_);
count_--;
DCHECK_GE(count_, 0) << "CountingCB executed too many times";
if (count_ != 0)
return;
}
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(FROM_HERE, done_cb_);
return;
}
done_cb_.Run();
}
scoped_refptr<base::MessageLoopProxy> message_loop_;
base::Lock lock_;
int count_;
base::Closure done_cb_;
DISALLOW_COPY_AND_ASSIGN(CountingCB);
};
static void OnSeriesCallback(
scoped_refptr<base::MessageLoopProxy> message_loop,
scoped_ptr<std::queue<ClosureFunc> > closures,
const base::Closure& done_cb) {
if (!message_loop->BelongsToCurrentThread()) {
message_loop->PostTask(FROM_HERE, base::Bind(
&OnSeriesCallback, message_loop, base::Passed(&closures), done_cb));
return;
}
if (closures->empty()) {
done_cb.Run();
return;
}
ClosureFunc cb = closures->front();
closures->pop();
cb.Run(base::Bind(
&OnSeriesCallback, message_loop, base::Passed(&closures), done_cb));
}
void RunInSeries(scoped_ptr<std::queue<ClosureFunc> > closures,
const base::Closure& done_cb) {
OnSeriesCallback(base::MessageLoopProxy::current(),
closures.Pass(), done_cb);
}
static void OnStatusCallback(
scoped_refptr<base::MessageLoopProxy> message_loop,
scoped_ptr<std::queue<PipelineStatusCBFunc> > status_cbs,
const PipelineStatusCB& done_cb,
PipelineStatus last_status) {
if (!message_loop->BelongsToCurrentThread()) {
message_loop->PostTask(FROM_HERE, base::Bind(
&OnStatusCallback, message_loop, base::Passed(&status_cbs), done_cb,
last_status));
return;
}
if (status_cbs->empty() || last_status != PIPELINE_OK) {
done_cb.Run(last_status);
return;
}
PipelineStatusCBFunc status_cb = status_cbs->front();
status_cbs->pop();
status_cb.Run(base::Bind(
&OnStatusCallback, message_loop, base::Passed(&status_cbs), done_cb));
}
void RunInSeriesWithStatus(
scoped_ptr<std::queue<PipelineStatusCBFunc> > status_cbs,
const PipelineStatusCB& done_cb) {
OnStatusCallback(base::MessageLoopProxy::current(),
status_cbs.Pass(), done_cb, PIPELINE_OK);
}
void RunInParallel(scoped_ptr<std::queue<ClosureFunc> > closures,
const base::Closure& done_cb) {
if (closures->empty()) {
done_cb.Run();
return;
}
scoped_refptr<CountingCB> counting_cb =
new CountingCB(closures->size(), done_cb);
while (!closures->empty()) {
closures->front().Run(counting_cb->GetClosure());
closures->pop();
}
}
} // namespace media
// 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.
#ifndef MEDIA_BASE_CALLBACK_UTIL_H_
#define MEDIA_BASE_CALLBACK_UTIL_H_
#include <queue>
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "media/base/pipeline_status.h"
namespace media {
typedef base::Callback<void(const base::Closure&)> ClosureFunc;
typedef base::Callback<void(const PipelineStatusCB&)> PipelineStatusCBFunc;
// Executes the closures in FIFO order, executing |done_cb| when the last
// closure has completed running.
//
// All closures (including |done_cb|) are executed on same thread as the
// calling thread.
void RunInSeries(scoped_ptr<std::queue<ClosureFunc> > closures,
const base::Closure& done_cb);
// Executes the closures in FIFO order, executing |done_cb| when the last
// closure has completed running, reporting the final status code.
//
// Closures will stop being executed if a previous closure in the series
// returned an error status and |done_cb| will be executed prematurely.
//
// All closures (including |done_cb|) are executed on same thread as the
// calling thread.
void RunInSeriesWithStatus(
scoped_ptr<std::queue<PipelineStatusCBFunc> > status_cbs,
const PipelineStatusCB& done_cb);
// Executes the closures in parallel, executing |done_cb| when all closures have
// completed running.
//
// No attempt is made to parallelize execution of the closures. In other words,
// this method will run all closures in FIFO order if said closures execute
// synchronously on the same call stack.
//
// All closures (including |done_cb|) are executed on same thread as the
// calling thread.
void RunInParallel(scoped_ptr<std::queue<ClosureFunc> > closures,
const base::Closure& done_cb);
} // namespace media
#endif // MEDIA_BASE_CALLBACK_UTIL_H_
......@@ -25,7 +25,6 @@ class CompositeFilter::FilterHostImpl : public FilterHost {
virtual base::TimeDelta GetDuration() const OVERRIDE;
virtual void SetNaturalVideoSize(const gfx::Size& size) OVERRIDE;
virtual void NotifyEnded() OVERRIDE;
virtual void DisableAudioRenderer() OVERRIDE;
private:
CompositeFilter* parent_;
......@@ -469,8 +468,4 @@ void CompositeFilter::FilterHostImpl::NotifyEnded() {
host_->NotifyEnded();
}
void CompositeFilter::FilterHostImpl::DisableAudioRenderer() {
host_->DisableAudioRenderer();
}
} // namespace media
......@@ -43,10 +43,6 @@ class MEDIA_EXPORT FilterHost {
// endpoints such as renderers.
virtual void NotifyEnded() = 0;
// Disable audio renderer by calling OnAudioRendererDisabled() on all
// filters.
virtual void DisableAudioRenderer() = 0;
protected:
virtual ~FilterHost() {}
};
......
......@@ -85,29 +85,6 @@ scoped_ptr<FilterCollection> MockFilterCollection::Create() {
return collection.Pass();
}
void RunPipelineStatusCB(const PipelineStatusCB& status_cb) {
status_cb.Run(PIPELINE_OK);
}
void RunPipelineStatusCB2(::testing::Unused,
const PipelineStatusCB& status_cb) {
status_cb.Run(PIPELINE_OK);
}
void RunPipelineStatusCB3(::testing::Unused, const PipelineStatusCB& status_cb,
::testing::Unused) {
status_cb.Run(PIPELINE_OK);
}
void RunPipelineStatusCB4(::testing::Unused, const PipelineStatusCB& status_cb,
::testing::Unused, ::testing::Unused) {
status_cb.Run(PIPELINE_OK);
}
void RunClosure(const base::Closure& closure) {
closure.Run();
}
MockFilter::MockFilter() : host_(NULL) {}
MockFilter::~MockFilter() {}
......
......@@ -199,23 +199,22 @@ class MockAudioRenderer : public AudioRenderer {
public:
MockAudioRenderer();
// Filter implementation.
MOCK_METHOD1(SetHost, void(FilterHost* host));
// AudioRenderer implementation.
MOCK_METHOD7(Initialize, void(const scoped_refptr<AudioDecoder>& decoder,
const PipelineStatusCB& init_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb,
const base::Closure& ended_cb,
const base::Closure& disabled_cb,
const PipelineStatusCB& error_cb));
MOCK_METHOD1(Play, void(const base::Closure& callback));
MOCK_METHOD1(Pause, void(const base::Closure& callback));
MOCK_METHOD1(Flush, void(const base::Closure& callback));
MOCK_METHOD1(Stop, void(const base::Closure& callback));
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
MOCK_METHOD2(Seek, void(base::TimeDelta time, const PipelineStatusCB& cb));
// AudioRenderer implementation.
MOCK_METHOD4(Initialize, void(const scoped_refptr<AudioDecoder>& decoder,
const PipelineStatusCB& init_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb));
MOCK_METHOD0(HasEnded, bool());
MOCK_METHOD1(SetVolume, void(float volume));
MOCK_METHOD1(ResumeAfterUnderflow, void(bool buffer_more_audio));
protected:
......@@ -310,19 +309,6 @@ class MockFilterCollection {
DISALLOW_COPY_AND_ASSIGN(MockFilterCollection);
};
// Helper gmock functions that immediately executes and destroys the
// Closure on behalf of the provided filter. Can be used when mocking
// the Initialize() and Seek() methods.
void RunPipelineStatusCB(const PipelineStatusCB& status_cb);
void RunPipelineStatusCB2(::testing::Unused, const PipelineStatusCB& status_cb);
void RunPipelineStatusCB3(::testing::Unused, const PipelineStatusCB& status_cb,
::testing::Unused);
void RunPipelineStatusCB4(::testing::Unused, const PipelineStatusCB& status_cb,
::testing::Unused, ::testing::Unused);
// Helper gmock function that immediately executes the Closure on behalf of the
// provided filter. Can be used when mocking the Stop() method.
void RunClosure(const base::Closure& closure);
// Helper gmock action that calls SetError() on behalf of the provided filter.
ACTION_P2(SetError, filter, error) {
filter->host()->SetError(error);
......@@ -334,6 +320,10 @@ ACTION_P2(SetDuration, filter, duration) {
filter->host()->SetDuration(duration);
}
ACTION(RunClosure) {
arg0.Run();
}
// Helper mock statistics callback.
class MockStatisticsCB {
public:
......
......@@ -17,6 +17,7 @@
#include "base/synchronization/condition_variable.h"
#include "media/base/audio_decoder.h"
#include "media/base/audio_renderer.h"
#include "media/base/callback_util.h"
#include "media/base/clock.h"
#include "media/base/composite_filter.h"
#include "media/base/filter_collection.h"
......@@ -64,7 +65,6 @@ media::PipelineStatus PipelineStatusNotification::status() {
struct Pipeline::PipelineInitState {
scoped_refptr<AudioDecoder> audio_decoder;
scoped_refptr<VideoDecoder> video_decoder;
scoped_refptr<AudioRenderer> audio_renderer;
scoped_refptr<VideoRenderer> video_renderer;
scoped_refptr<CompositeFilter> composite;
};
......@@ -317,6 +317,7 @@ bool Pipeline::IsPipelineSeeking() {
}
void Pipeline::ReportStatus(const PipelineStatusCB& cb, PipelineStatus status) {
DCHECK(message_loop_->BelongsToCurrentThread());
if (cb.is_null())
return;
cb.Run(status);
......@@ -390,6 +391,14 @@ TimeDelta Pipeline::GetDuration() const {
return GetMediaDuration();
}
void Pipeline::OnAudioDisabled() {
DCHECK(IsRunning());
message_loop_->PostTask(FROM_HERE, base::Bind(
&Pipeline::AudioDisabledTask, this));
media_log_->AddEvent(
media_log_->CreateEvent(MediaLogEvent::AUDIO_RENDERER_DISABLED));
}
void Pipeline::OnAudioTimeUpdate(TimeDelta time, TimeDelta max_time) {
DCHECK(time <= max_time);
DCHECK(IsRunning());
......@@ -460,6 +469,61 @@ TimeDelta Pipeline::TimeForByteOffset_Locked(int64 byte_offset) const {
return time_offset;
}
void Pipeline::DoPause(const base::Closure& done_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>);
if (audio_renderer_)
closures->push(base::Bind(&AudioRenderer::Pause, audio_renderer_));
if (pipeline_filter_)
closures->push(base::Bind(&Filter::Pause, pipeline_filter_));
RunInSeries(closures.Pass(), done_cb);
}
void Pipeline::DoFlush(const base::Closure& done_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>);
if (audio_renderer_)
closures->push(base::Bind(&AudioRenderer::Flush, audio_renderer_));
if (pipeline_filter_)
closures->push(base::Bind(&Filter::Flush, pipeline_filter_));
RunInParallel(closures.Pass(), done_cb);
}
void Pipeline::DoPlay(const base::Closure& done_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>);
if (audio_renderer_)
closures->push(base::Bind(&AudioRenderer::Play, audio_renderer_));
if (pipeline_filter_)
closures->push(base::Bind(&Filter::Play, pipeline_filter_));
RunInSeries(closures.Pass(), done_cb);
}
void Pipeline::DoStop(const base::Closure& done_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>);
if (demuxer_)
closures->push(base::Bind(&Demuxer::Stop, demuxer_));
if (audio_renderer_)
closures->push(base::Bind(&AudioRenderer::Stop, audio_renderer_));
if (pipeline_filter_)
closures->push(base::Bind(&Filter::Stop, pipeline_filter_));
RunInSeries(closures.Pass(), done_cb);
}
void Pipeline::AddBufferedByteRange(int64 start, int64 end) {
DCHECK(IsRunning());
base::AutoLock auto_lock(lock_);
......@@ -491,16 +555,6 @@ void Pipeline::NotifyEnded() {
media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED));
}
void Pipeline::DisableAudioRenderer() {
DCHECK(IsRunning());
// Disable renderer on the message loop.
message_loop_->PostTask(FROM_HERE, base::Bind(
&Pipeline::DisableAudioRendererTask, this));
media_log_->AddEvent(
media_log_->CreateEvent(MediaLogEvent::AUDIO_RENDERER_DISABLED));
}
// Called from any thread.
void Pipeline::OnFilterInitialize(PipelineStatus status) {
// Continue the initialize task by proceeding to the next stage.
......@@ -658,12 +712,13 @@ void Pipeline::InitializeTask(PipelineStatus last_stage_status) {
PlaybackRateChangedTask(GetPlaybackRate());
VolumeChangedTask(GetVolume());
// Fire a seek request to get the renderers to preroll. We don't need to
// tell the demuxer to seek since it should already be at the start.
// Fire a seek request to get the renderers to preroll. We can skip a seek
// here as the demuxer should be at the start of the stream.
seek_pending_ = true;
SetState(kSeeking);
seek_timestamp_ = demuxer_->GetStartTime();
OnDemuxerSeekDone(seek_timestamp_, PIPELINE_OK);
DoSeek(seek_timestamp_, true,
base::Bind(&Pipeline::OnFilterStateTransitionWithStatus, this));
}
}
......@@ -752,9 +807,11 @@ void Pipeline::PlaybackRateChangedTask(float playback_rate) {
// hasn't completed yet, the playback rate will be set when initialization
// completes.
if (pipeline_filter_) {
DCHECK(demuxer_);
demuxer_->SetPlaybackRate(playback_rate);
pipeline_filter_->SetPlaybackRate(playback_rate);
if (audio_renderer_)
audio_renderer_->SetPlaybackRate(playback_rate_);
}
}
......@@ -800,8 +857,7 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
if (clock_->IsPlaying())
clock_->Pause();
}
pipeline_filter_->Pause(
base::Bind(&Pipeline::OnFilterStateTransition, this));
DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this));
}
void Pipeline::NotifyEndedTask() {
......@@ -841,7 +897,7 @@ void Pipeline::NotifyEndedTask() {
ReportStatus(ended_cb_, status_);
}
void Pipeline::DisableAudioRendererTask() {
void Pipeline::AudioDisabledTask() {
DCHECK(message_loop_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
......@@ -888,16 +944,14 @@ void Pipeline::FilterStateTransitionTask() {
// Carry out the action for the current state.
if (TransientState(state_)) {
if (state_ == kPausing) {
pipeline_filter_->Pause(
base::Bind(&Pipeline::OnFilterStateTransition, this));
DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this));
} else if (state_ == kFlushing) {
pipeline_filter_->Flush(
base::Bind(&Pipeline::OnFilterStateTransition, this));
DoFlush(base::Bind(&Pipeline::OnFilterStateTransition, this));
} else if (state_ == kSeeking) {
DoSeek(seek_timestamp_);
DoSeek(seek_timestamp_, false,
base::Bind(&Pipeline::OnFilterStateTransitionWithStatus, this));
} else if (state_ == kStarting) {
pipeline_filter_->Play(
base::Bind(&Pipeline::OnFilterStateTransition, this));
DoPlay(base::Bind(&Pipeline::OnFilterStateTransition, this));
} else if (state_ == kStopping) {
DoStop(base::Bind(&Pipeline::OnFilterStateTransition, this));
} else {
......@@ -943,8 +997,7 @@ void Pipeline::TeardownStateTransitionTask() {
break;
case kPausing:
SetState(kFlushing);
pipeline_filter_->Flush(
base::Bind(&Pipeline::OnTeardownStateTransition, this));
DoFlush(base::Bind(&Pipeline::OnTeardownStateTransition, this));
break;
case kFlushing:
SetState(kStopping);
......@@ -1092,23 +1145,20 @@ bool Pipeline::InitializeAudioRenderer(
if (!decoder)
return false;
filter_collection_->SelectAudioRenderer(
&pipeline_init_state_->audio_renderer);
if (!pipeline_init_state_->audio_renderer) {
filter_collection_->SelectAudioRenderer(&audio_renderer_);
if (!audio_renderer_) {
SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING);
return false;
}
pipeline_init_state_->composite->AddFilter(
pipeline_init_state_->audio_renderer);
pipeline_init_state_->audio_renderer->Initialize(
audio_renderer_->Initialize(
decoder,
base::Bind(&Pipeline::OnFilterInitialize, this),
base::Bind(&Pipeline::OnAudioUnderflow, this),
base::Bind(&Pipeline::OnAudioTimeUpdate, this));
audio_renderer_ = pipeline_init_state_->audio_renderer;
base::Bind(&Pipeline::OnAudioTimeUpdate, this),
base::Bind(&Pipeline::NotifyEnded, this),
base::Bind(&Pipeline::OnAudioDisabled, this),
base::Bind(&Pipeline::SetError, this));
return true;
}
......@@ -1194,8 +1244,7 @@ void Pipeline::TearDownPipeline() {
case kStarted:
case kEnded:
SetState(kPausing);
pipeline_filter_->Pause(
base::Bind(&Pipeline::OnTeardownStateTransition, this));
DoPause(base::Bind(&Pipeline::OnTeardownStateTransition, this));
break;
case kStopping:
......@@ -1207,53 +1256,26 @@ void Pipeline::TearDownPipeline() {
};
}
void Pipeline::DoStop(const base::Closure& callback) {
if (demuxer_) {
demuxer_->Stop(base::Bind(
&Pipeline::OnDemuxerStopDone, this, callback));
return;
}
OnDemuxerStopDone(callback);
}
void Pipeline::OnDemuxerStopDone(const base::Closure& callback) {
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(FROM_HERE, base::Bind(
&Pipeline::OnDemuxerStopDone, this, callback));
return;
}
if (pipeline_filter_) {
pipeline_filter_->Stop(callback);
return;
}
callback.Run();
}
void Pipeline::DoSeek(TimeDelta seek_timestamp) {
demuxer_->Seek(seek_timestamp, base::Bind(
&Pipeline::OnDemuxerSeekDone, this, seek_timestamp));
}
void Pipeline::DoSeek(base::TimeDelta seek_timestamp,
bool skip_demuxer_seek,
const PipelineStatusCB& done_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
scoped_ptr<std::queue<PipelineStatusCBFunc> > status_cbs(
new std::queue<PipelineStatusCBFunc>());
void Pipeline::OnDemuxerSeekDone(TimeDelta seek_timestamp,
PipelineStatus status) {
if (!message_loop_->BelongsToCurrentThread()) {
message_loop_->PostTask(FROM_HERE, base::Bind(
&Pipeline::OnDemuxerSeekDone, this, seek_timestamp, status));
return;
}
if (!skip_demuxer_seek)
status_cbs->push(base::Bind(&Demuxer::Seek, demuxer_, seek_timestamp));
PipelineStatusCB done_cb =
base::Bind(&Pipeline::OnFilterStateTransitionWithStatus, this);
if (audio_renderer_)
status_cbs->push(base::Bind(
&AudioRenderer::Seek, audio_renderer_, seek_timestamp));
if (status == PIPELINE_OK && pipeline_filter_) {
pipeline_filter_->Seek(seek_timestamp, done_cb);
return;
}
if (pipeline_filter_)
status_cbs->push(base::Bind(
&Filter::Seek, pipeline_filter_, seek_timestamp));
ReportStatus(done_cb, status);
RunInSeriesWithStatus(status_cbs.Pass(), base::Bind(
&Pipeline::ReportStatus, this, done_cb));
}
void Pipeline::OnAudioUnderflow() {
......
......@@ -10,6 +10,7 @@
#include "base/gtest_prod_util.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "media/base/audio_renderer.h"
#include "media/base/demuxer.h"
#include "media/base/filter_host.h"
#include "media/base/media_export.h"
......@@ -27,7 +28,6 @@ class TimeDelta;
namespace media {
class AudioDecoder;
class AudioRenderer;
class Clock;
class Filter;
class FilterCollection;
......@@ -225,6 +225,7 @@ class MEDIA_EXPORT Pipeline
private:
FRIEND_TEST_ALL_PREFIXES(PipelineTest, GetBufferedTimeRanges);
FRIEND_TEST_ALL_PREFIXES(PipelineTest, DisableAudioRenderer);
friend class MediaLog;
// Only allow ourselves to be deleted by reference counting.
......@@ -301,7 +302,6 @@ class MEDIA_EXPORT Pipeline
virtual base::TimeDelta GetDuration() const OVERRIDE;
virtual void SetNaturalVideoSize(const gfx::Size& size) OVERRIDE;
virtual void NotifyEnded() OVERRIDE;
virtual void DisableAudioRenderer() OVERRIDE;
// Callbacks executed by filters upon completing initialization.
void OnFilterInitialize(PipelineStatus status);
......@@ -318,6 +318,9 @@ class MEDIA_EXPORT Pipeline
// Callback executed by filters to update statistics.
void OnUpdateStatistics(const PipelineStatistics& stats);
// Callback executed by audio renderer when it has been disabled.
void OnAudioDisabled();
// Callback executed by audio renderer to update clock time.
void OnAudioTimeUpdate(base::TimeDelta time, base::TimeDelta max_time);
......@@ -359,7 +362,7 @@ class MEDIA_EXPORT Pipeline
void NotifyEndedTask();
// Carries out disabling the audio renderer.
void DisableAudioRendererTask();
void AudioDisabledTask();
// Carries out advancing to the next filter during Play()/Pause()/Seek().
void FilterStateTransitionTask();
......@@ -410,21 +413,22 @@ class MEDIA_EXPORT Pipeline
// Compute the time corresponding to a byte offset.
base::TimeDelta TimeForByteOffset_Locked(int64 byte_offset) const;
// Initiates a Stop() on |demuxer_| & |pipeline_filter_|. |callback|
// is called once both objects have been stopped.
void DoStop(const base::Closure& callback);
// Called when |demuxer_| has stopped. This method calls Stop()
// on |pipeline_filter_|.
void OnDemuxerStopDone(const base::Closure& callback);
// Initiates a Seek() on the |demuxer_| & |pipeline_filter_|.
void DoSeek(base::TimeDelta seek_timestamp);
// Initiates an asynchronous Pause/Seek/Play/Stop() call sequence executing
// |done_cb| when completed.
void DoPause(const base::Closure& done_cb);
void DoFlush(const base::Closure& done_cb);
void DoPlay(const base::Closure& done_cb);
void DoStop(const base::Closure& done_cb);
// Called when |demuxer_| finishes seeking. If seeking was successful,
// then Seek() is called on |pipeline_filter_|.
void OnDemuxerSeekDone(base::TimeDelta seek_timestamp,
PipelineStatus status);
// Initiates an asynchronous Seek() and preroll call sequence executing
// |done_cb| with the final status when completed. If |skip_demuxer_seek| is
// true then only renderers will attempt to preroll.
//
// TODO(scherkus): Prerolling should be separate from seeking so we can report
// finer grained ready states (HAVE_CURRENT_DATA vs. HAVE_FUTURE_DATA)
// indepentent from seeking.
void DoSeek(base::TimeDelta seek_timestamp, bool skip_demuxer_seek,
const PipelineStatusCB& done_cb);
void OnAudioUnderflow();
......@@ -533,6 +537,10 @@ class MEDIA_EXPORT Pipeline
PipelineStatusCB error_cb_;
// Reference to the filter(s) that constitute the pipeline.
//
// TODO(scherkus): At this point in time this is a CompositeFilter that
// contains |video_renderer_|. Remove when CompositeFilter is gone, see
// http://crbug.com/126069
scoped_refptr<Filter> pipeline_filter_;
// Decoder reference used for signalling imminent shutdown.
......
......@@ -39,18 +39,17 @@ static const int kTotalBytes = 1024;
static const int kBufferedBytes = 1024;
static const int kBitrate = 1234;
ACTION_P(InitializeDemuxerWithError, error) {
arg1.Run(error);
}
ACTION_P(SetDemuxerProperties, duration) {
arg0->SetTotalBytes(kTotalBytes);
arg0->SetDuration(duration);
}
ACTION_P(DisableAudioRenderer, pipeline) {
FilterHost* host = pipeline;
host->DisableAudioRenderer();
ACTION(RunPipelineStatusCB1) {
arg1.Run(PIPELINE_OK);
}
ACTION_P(RunPipelineStatusCB1WithStatus, status) {
arg1.Run(status);
}
// Used for setting expectations on pipeline callbacks. Using a StrictMock
......@@ -100,24 +99,24 @@ class PipelineTest : public ::testing::Test {
// Shutdown sequence.
if (pipeline_->IsInitialized()) {
EXPECT_CALL(*mocks_->demuxer(), Stop(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
if (audio_stream_) {
EXPECT_CALL(*mocks_->audio_renderer(), Pause(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->audio_renderer(), Flush(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->audio_renderer(), Stop(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
}
if (video_stream_) {
EXPECT_CALL(*mocks_->video_renderer(), Pause(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->video_renderer(), Flush(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->video_renderer(), Stop(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
}
}
......@@ -138,7 +137,7 @@ class PipelineTest : public ::testing::Test {
const base::TimeDelta& duration) {
EXPECT_CALL(*mocks_->demuxer(), Initialize(_, _))
.WillOnce(DoAll(SetDemuxerProperties(duration),
Invoke(&RunPipelineStatusCB2)));
RunPipelineStatusCB1()));
EXPECT_CALL(*mocks_->demuxer(), SetPlaybackRate(0.0f));
// Demuxer properties.
......@@ -170,13 +169,13 @@ class PipelineTest : public ::testing::Test {
void InitializeVideoDecoder(const scoped_refptr<DemuxerStream>& stream) {
EXPECT_CALL(*mocks_->video_decoder(),
Initialize(stream, _, _))
.WillOnce(Invoke(&RunPipelineStatusCB3));
.WillOnce(RunPipelineStatusCB1());
}
// Sets up expectations to allow the audio decoder to initialize.
void InitializeAudioDecoder(const scoped_refptr<DemuxerStream>& stream) {
EXPECT_CALL(*mocks_->audio_decoder(), Initialize(stream, _, _))
.WillOnce(Invoke(&RunPipelineStatusCB3));
.WillOnce(RunPipelineStatusCB1());
}
// Sets up expectations to allow the video renderer to initialize.
......@@ -184,39 +183,40 @@ class PipelineTest : public ::testing::Test {
EXPECT_CALL(*mocks_->video_renderer(), SetHost(NotNull()));
EXPECT_CALL(*mocks_->video_renderer(), Initialize(
scoped_refptr<VideoDecoder>(mocks_->video_decoder()), _, _, _))
.WillOnce(Invoke(&RunPipelineStatusCB4));
.WillOnce(RunPipelineStatusCB1());
EXPECT_CALL(*mocks_->video_renderer(), SetPlaybackRate(0.0f));
// Startup sequence.
EXPECT_CALL(*mocks_->video_renderer(),
Seek(mocks_->demuxer()->GetStartTime(), _))
.WillOnce(Invoke(&RunPipelineStatusCB2));
.WillOnce(RunPipelineStatusCB1());
EXPECT_CALL(*mocks_->video_renderer(), Play(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
}
// Sets up expectations to allow the audio renderer to initialize.
void InitializeAudioRenderer(bool disable_after_init_cb = false) {
EXPECT_CALL(*mocks_->audio_renderer(), SetHost(NotNull()));
if (disable_after_init_cb) {
EXPECT_CALL(*mocks_->audio_renderer(), Initialize(
scoped_refptr<AudioDecoder>(mocks_->audio_decoder()), _, _, _))
.WillOnce(DoAll(Invoke(&RunPipelineStatusCB4),
DisableAudioRenderer(pipeline_)));
scoped_refptr<AudioDecoder>(mocks_->audio_decoder()),
_, _, _, _, _, _))
.WillOnce(DoAll(RunPipelineStatusCB1(),
WithArg<5>(RunClosure()))); // |disabled_cb|.
} else {
EXPECT_CALL(*mocks_->audio_renderer(), Initialize(
scoped_refptr<AudioDecoder>(mocks_->audio_decoder()), _, _, _))
scoped_refptr<AudioDecoder>(mocks_->audio_decoder()),
_, _, _, _, _, _))
.WillOnce(DoAll(SaveArg<3>(&audio_time_cb_),
Invoke(&RunPipelineStatusCB4)));
RunPipelineStatusCB1()));
}
EXPECT_CALL(*mocks_->audio_renderer(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->audio_renderer(), SetVolume(1.0f));
// Startup sequence.
EXPECT_CALL(*mocks_->audio_renderer(), Seek(base::TimeDelta(), _))
.WillOnce(Invoke(&RunPipelineStatusCB2));
.WillOnce(RunPipelineStatusCB1());
EXPECT_CALL(*mocks_->audio_renderer(), Play(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
}
// Sets up expectations on the callback and initializes the pipeline. Called
......@@ -251,28 +251,28 @@ class PipelineTest : public ::testing::Test {
void ExpectSeek(const base::TimeDelta& seek_time) {
// Every filter should receive a call to Seek().
EXPECT_CALL(*mocks_->demuxer(), Seek(seek_time, _))
.WillOnce(Invoke(&RunPipelineStatusCB2));
.WillOnce(RunPipelineStatusCB1());
if (audio_stream_) {
EXPECT_CALL(*mocks_->audio_renderer(), Pause(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->audio_renderer(), Flush(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->audio_renderer(), Seek(seek_time, _))
.WillOnce(Invoke(&RunPipelineStatusCB2));
.WillOnce(RunPipelineStatusCB1());
EXPECT_CALL(*mocks_->audio_renderer(), Play(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
}
if (video_stream_) {
EXPECT_CALL(*mocks_->video_renderer(), Pause(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->video_renderer(), Flush(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->video_renderer(), Seek(seek_time, _))
.WillOnce(Invoke(&RunPipelineStatusCB2));
.WillOnce(RunPipelineStatusCB1());
EXPECT_CALL(*mocks_->video_renderer(), Play(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
}
// We expect a successful seek callback.
......@@ -344,7 +344,7 @@ TEST_F(PipelineTest, NeverInitializes) {
// Don't execute the callback passed into Initialize().
EXPECT_CALL(*mocks_->demuxer(), Initialize(_, _));
EXPECT_CALL(*mocks_->demuxer(), Stop(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
// This test hangs during initialization by never calling
// InitializationComplete(). StrictMock<> will ensure that the callback is
......@@ -382,9 +382,9 @@ TEST_F(PipelineTest, RequiredFilterMissing) {
TEST_F(PipelineTest, URLNotFound) {
EXPECT_CALL(*mocks_->demuxer(), Initialize(_, _))
.WillOnce(InitializeDemuxerWithError(PIPELINE_ERROR_URL_NOT_FOUND));
.WillOnce(RunPipelineStatusCB1WithStatus(PIPELINE_ERROR_URL_NOT_FOUND));
EXPECT_CALL(*mocks_->demuxer(), Stop(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
InitializePipeline(PIPELINE_ERROR_URL_NOT_FOUND);
EXPECT_FALSE(pipeline_->IsInitialized());
......@@ -392,9 +392,9 @@ TEST_F(PipelineTest, URLNotFound) {
TEST_F(PipelineTest, NoStreams) {
EXPECT_CALL(*mocks_->demuxer(), Initialize(_, _))
.WillOnce(Invoke(&RunPipelineStatusCB2));
.WillOnce(RunPipelineStatusCB1());
EXPECT_CALL(*mocks_->demuxer(), Stop(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
InitializePipeline(PIPELINE_ERROR_COULD_NOT_RENDER);
EXPECT_FALSE(pipeline_->IsInitialized());
......@@ -578,12 +578,8 @@ TEST_F(PipelineTest, DisableAudioRenderer) {
EXPECT_TRUE(pipeline_->HasAudio());
EXPECT_TRUE(pipeline_->HasVideo());
EXPECT_CALL(*mocks_->audio_renderer(), SetPlaybackRate(1.0f))
.WillOnce(DisableAudioRenderer(pipeline_));
EXPECT_CALL(*mocks_->demuxer(),
OnAudioRendererDisabled());
mocks_->audio_renderer()->SetPlaybackRate(1.0f);
EXPECT_CALL(*mocks_->demuxer(), OnAudioRendererDisabled());
pipeline_->OnAudioDisabled();
// Verify that ended event is fired when video ends.
EXPECT_CALL(*mocks_->video_renderer(), HasEnded())
......@@ -729,10 +725,6 @@ TEST_F(PipelineTest, AudioStreamShorterThanVideo) {
host->NotifyEnded();
}
void SendReadErrorToCB(::testing::Unused, const PipelineStatusCB& cb) {
cb.Run(PIPELINE_ERROR_READ);
}
TEST_F(PipelineTest, ErrorDuringSeek) {
CreateAudioStream();
MockDemuxerStreamVector streams;
......@@ -753,16 +745,16 @@ TEST_F(PipelineTest, ErrorDuringSeek) {
// Seek() isn't called as the demuxer errors out first.
EXPECT_CALL(*mocks_->audio_renderer(), Pause(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->audio_renderer(), Flush(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->audio_renderer(), Stop(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->demuxer(), Seek(seek_time, _))
.WillOnce(Invoke(&SendReadErrorToCB));
.WillOnce(RunPipelineStatusCB1WithStatus(PIPELINE_ERROR_READ));
EXPECT_CALL(*mocks_->demuxer(), Stop(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
pipeline_->Seek(seek_time, base::Bind(&CallbackHelper::OnSeek,
base::Unretained(&callbacks_)));
......@@ -809,16 +801,16 @@ TEST_F(PipelineTest, NoMessageDuringTearDownFromError) {
// Seek() isn't called as the demuxer errors out first.
EXPECT_CALL(*mocks_->audio_renderer(), Pause(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->audio_renderer(), Flush(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->audio_renderer(), Stop(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->demuxer(), Seek(seek_time, _))
.WillOnce(Invoke(&SendReadErrorToCB));
.WillOnce(RunPipelineStatusCB1WithStatus(PIPELINE_ERROR_READ));
EXPECT_CALL(*mocks_->demuxer(), Stop(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
pipeline_->Seek(seek_time, base::Bind(&CallbackHelper::OnSeek,
base::Unretained(&callbacks_)));
......@@ -903,16 +895,16 @@ TEST_F(PipelineTest, AudioTimeUpdateDuringSeek) {
base::Closure closure = base::Bind(&RunTimeCB, audio_time_cb_, 300, 700);
EXPECT_CALL(*mocks_->demuxer(), Seek(seek_time, _))
.WillOnce(DoAll(InvokeWithoutArgs(&closure, &base::Closure::Run),
Invoke(&RunPipelineStatusCB2)));
RunPipelineStatusCB1()));
EXPECT_CALL(*mocks_->audio_renderer(), Pause(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->audio_renderer(), Flush(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(*mocks_->audio_renderer(), Seek(seek_time, _))
.WillOnce(Invoke(&RunPipelineStatusCB2));
.WillOnce(RunPipelineStatusCB1());
EXPECT_CALL(*mocks_->audio_renderer(), Play(_))
.WillOnce(Invoke(&RunClosure));
.WillOnce(RunClosure());
EXPECT_CALL(callbacks_, OnSeek(PIPELINE_OK));
DoSeek(seek_time);
......
......@@ -16,8 +16,7 @@
namespace media {
AudioRendererImpl::AudioRendererImpl(media::AudioRendererSink* sink)
: host_(NULL),
state_(kUninitialized),
: state_(kUninitialized),
pending_read_(false),
received_end_of_stream_(false),
rendered_end_of_stream_(false),
......@@ -33,12 +32,6 @@ AudioRendererImpl::AudioRendererImpl(media::AudioRendererSink* sink)
base::Unretained(this))) {
}
void AudioRendererImpl::SetHost(FilterHost* host) {
DCHECK(host);
DCHECK(!host_);
host_ = host;
}
void AudioRendererImpl::Play(const base::Closure& callback) {
{
base::AutoLock auto_lock(lock_);
......@@ -145,15 +138,24 @@ void AudioRendererImpl::DoSeek() {
void AudioRendererImpl::Initialize(const scoped_refptr<AudioDecoder>& decoder,
const PipelineStatusCB& init_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb) {
const TimeCB& time_cb,
const base::Closure& ended_cb,
const base::Closure& disabled_cb,
const PipelineStatusCB& error_cb) {
DCHECK(decoder);
DCHECK(!init_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_);
decoder_ = decoder;
underflow_cb_ = underflow_cb;
time_cb_ = time_cb;
ended_cb_ = ended_cb;
disabled_cb_ = disabled_cb;
error_cb_ = error_cb;
// Create a callback so our algorithm can request more reads.
base::Closure cb = base::Bind(&AudioRendererImpl::ScheduleRead_Locked, this);
......@@ -436,7 +438,7 @@ uint32 AudioRendererImpl::FillBuffer(uint8* dest,
if (!algorithm_->CanFillBuffer() && received_end_of_stream_ &&
!rendered_end_of_stream_ && base::Time::Now() >= earliest_end_time_) {
rendered_end_of_stream_ = true;
host_->NotifyEnded();
ended_cb_.Run();
} else if (!algorithm_->CanFillBuffer() && !received_end_of_stream_ &&
state_ == kPlaying && !underflow_disabled_) {
state_ = kUnderflow;
......@@ -519,7 +521,7 @@ base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) {
}
void AudioRendererImpl::OnRenderError() {
host_->DisableAudioRenderer();
disabled_cb_.Run();
}
void AudioRendererImpl::DisableUnderflowForTesting() {
......@@ -535,7 +537,7 @@ void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error) {
return;
case kPaused:
if (status != PIPELINE_OK)
host_->SetError(status);
error_cb_.Run(status);
base::ResetAndReturn(&pause_cb_).Run();
return;
case kSeeking:
......@@ -547,7 +549,7 @@ void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error) {
case kRebuffering:
case kStopped:
if (status != PIPELINE_OK)
host_->SetError(status);
error_cb_.Run(status);
return;
}
}
......
......@@ -40,20 +40,20 @@ class MEDIA_EXPORT AudioRendererImpl
explicit AudioRendererImpl(media::AudioRendererSink* sink);
// Methods called on pipeline thread ----------------------------------------
// Filter implementation.
virtual void SetHost(FilterHost* host) OVERRIDE;
// AudioRenderer implementation.
virtual void Initialize(const scoped_refptr<AudioDecoder>& decoder,
const PipelineStatusCB& init_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb,
const base::Closure& ended_cb,
const base::Closure& disabled_cb,
const PipelineStatusCB& error_cb) OVERRIDE;
virtual void Play(const base::Closure& callback) OVERRIDE;
virtual void Pause(const base::Closure& callback) OVERRIDE;
virtual void Flush(const base::Closure& callback) OVERRIDE;
virtual void Stop(const base::Closure& callback) OVERRIDE;
virtual void SetPlaybackRate(float rate) OVERRIDE;
virtual void Seek(base::TimeDelta time, const PipelineStatusCB& cb) OVERRIDE;
// AudioRenderer implementation.
virtual void Initialize(const scoped_refptr<AudioDecoder>& decoder,
const PipelineStatusCB& init_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb) OVERRIDE;
virtual bool HasEnded() OVERRIDE;
virtual void ResumeAfterUnderflow(bool buffer_more_audio) OVERRIDE;
virtual void SetVolume(float volume) OVERRIDE;
......@@ -136,8 +136,6 @@ class MEDIA_EXPORT AudioRendererImpl
// in the kSeeking state.
bool IsBeforeSeekTime(const scoped_refptr<Buffer>& buffer);
FilterHost* host_;
// Audio decoder.
scoped_refptr<AudioDecoder> decoder_;
......@@ -175,8 +173,10 @@ class MEDIA_EXPORT AudioRendererImpl
PipelineStatusCB seek_cb_;
base::Closure underflow_cb_;
TimeCB time_cb_;
base::Closure ended_cb_;
base::Closure disabled_cb_;
PipelineStatusCB error_cb_;
base::TimeDelta seek_timestamp_;
......
......@@ -34,7 +34,6 @@ class AudioRendererImplTest : public ::testing::Test {
AudioRendererImplTest()
: renderer_(new AudioRendererImpl(new NiceMock<MockAudioRendererSink>())),
decoder_(new MockAudioDecoder()) {
renderer_->SetHost(&host_);
// Queue all reads from the decoder by default.
ON_CALL(*decoder_, Read(_))
......@@ -48,28 +47,12 @@ class AudioRendererImplTest : public ::testing::Test {
.Times(AnyNumber());
EXPECT_CALL(*decoder_, samples_per_second())
.Times(AnyNumber());
// We'll pretend time never advances.
EXPECT_CALL(host_, GetTime())
.WillRepeatedly(Return(base::TimeDelta()));
}
virtual ~AudioRendererImplTest() {
renderer_->Stop(NewExpectedClosure());
}
MOCK_METHOD1(OnSeekComplete, void(PipelineStatus));
PipelineStatusCB NewSeekCB() {
return base::Bind(&AudioRendererImplTest::OnSeekComplete,
base::Unretained(this));
}
MOCK_METHOD0(OnUnderflow, void());
base::Closure NewUnderflowClosure() {
return base::Bind(&AudioRendererImplTest::OnUnderflow,
base::Unretained(this));
}
void SetSupportedAudioDecoderProperties() {
ON_CALL(*decoder_, bits_per_channel())
.WillByDefault(Return(16));
......@@ -88,20 +71,39 @@ class AudioRendererImplTest : public ::testing::Test {
.WillByDefault(Return(0));
}
MOCK_METHOD1(OnSeekComplete, void(PipelineStatus));
PipelineStatusCB NewSeekCB() {
return base::Bind(&AudioRendererImplTest::OnSeekComplete,
base::Unretained(this));
}
MOCK_METHOD0(OnUnderflow, void());
MOCK_METHOD0(OnEnded, void());
MOCK_METHOD0(OnDisabled, void());
MOCK_METHOD1(OnError, void(PipelineStatus));
void OnAudioTimeCallback(
base::TimeDelta current_time, base::TimeDelta max_time) {
CHECK(current_time <= max_time);
}
AudioRenderer::TimeCB NewAudioTimeClosure() {
return base::Bind(&AudioRendererImplTest::OnAudioTimeCallback,
base::Unretained(this));
void Initialize() {
InitializeWithStatus(PIPELINE_OK);
}
void Initialize() {
void InitializeWithStatus(PipelineStatus expected) {
renderer_->Initialize(
decoder_, NewExpectedStatusCB(PIPELINE_OK), NewUnderflowClosure(),
NewAudioTimeClosure());
decoder_, NewExpectedStatusCB(expected),
base::Bind(&AudioRendererImplTest::OnUnderflow,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnAudioTimeCallback,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnEnded,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnDisabled,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnError,
base::Unretained(this)));
}
void Preroll() {
......@@ -211,7 +213,6 @@ class AudioRendererImplTest : public ::testing::Test {
// Fixture members.
scoped_refptr<AudioRendererImpl> renderer_;
scoped_refptr<MockAudioDecoder> decoder_;
StrictMock<MockFilterHost> host_;
AudioDecoder::ReadCB read_cb_;
base::TimeDelta next_timestamp_;
......@@ -226,18 +227,14 @@ class AudioRendererImplTest : public ::testing::Test {
TEST_F(AudioRendererImplTest, Initialize_Failed) {
SetUnsupportedAudioDecoderProperties();
renderer_->Initialize(
decoder_,
NewExpectedStatusCB(PIPELINE_ERROR_INITIALIZATION_FAILED),
NewUnderflowClosure(), NewAudioTimeClosure());
InitializeWithStatus(PIPELINE_ERROR_INITIALIZATION_FAILED);
// We should have no reads.
EXPECT_TRUE(read_cb_.is_null());
}
TEST_F(AudioRendererImplTest, Initialize_Successful) {
renderer_->Initialize(decoder_, NewExpectedStatusCB(PIPELINE_OK),
NewUnderflowClosure(), NewAudioTimeClosure());
Initialize();
// We should have no reads.
EXPECT_TRUE(read_cb_.is_null());
......@@ -273,7 +270,7 @@ TEST_F(AudioRendererImplTest, EndOfStream) {
EXPECT_FALSE(renderer_->HasEnded());
// Drain internal buffer, now we should report ended.
EXPECT_CALL(host_, NotifyEnded());
EXPECT_CALL(*this, OnEnded());
EXPECT_TRUE(ConsumeBufferedData(bytes_buffered(), NULL));
EXPECT_TRUE(renderer_->HasEnded());
}
......@@ -351,9 +348,9 @@ TEST_F(AudioRendererImplTest, Underflow_EndOfStream) {
//
// TODO(scherkus): fix AudioRendererImpl and AudioRendererAlgorithmBase to
// stop reading after receiving an end of stream buffer. It should have also
// called NotifyEnded() http://crbug.com/106641
// fired the ended callback http://crbug.com/106641
DeliverEndOfStream();
EXPECT_CALL(host_, NotifyEnded());
EXPECT_CALL(*this, OnEnded());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_FALSE(muted);
......
......@@ -61,7 +61,7 @@ class VideoRendererBaseTest : public ::testing::Test {
EXPECT_CALL(*this, SetOpaqueCBWasCalled(_))
.WillRepeatedly(::testing::Return());
EXPECT_CALL(*decoder_, Stop(_))
.WillRepeatedly(Invoke(RunClosure));
.WillRepeatedly(RunClosure());
EXPECT_CALL(*this, TimeCBWasCalled(_))
.WillRepeatedly(::testing::Return());
}
......
......@@ -142,6 +142,8 @@
'base/buffers.h',
'base/byte_queue.cc',
'base/byte_queue.h',
'base/callback_util.cc',
'base/callback_util.h',
'base/channel_layout.cc',
'base/channel_layout.h',
'base/clock.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment