Commit 353132b9 authored by scherkus@chromium.org's avatar scherkus@chromium.org

Introduce audio/video BufferingState to Pipeline.

This is a stepping stone towards having audio/video renderers accurately
report their buffering state. For now we use the Preroll() callback to
signal that enough data has been buffered.

Notable changes:
  - The kStarting/kStarted states have been merged into kPlaying
  - DoPlay() is now done implicitly after entering the kPlaying state
  - Transitioning from waiting to non-waiting states (or vice versa)
    now controls the starting and stopping of playback

BUG=144683

Review URL: https://codereview.chromium.org/276973004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269828 0039d316-1c4b-4281-b951-d872f2087c98
parent 987f8091
// Copyright 2014 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_BUFFERING_STATE_H_
#define MEDIA_BASE_BUFFERING_STATE_H_
#include "base/callback_forward.h"
namespace media {
enum BufferingState {
// Indicates that there is no data buffered.
//
// Typical reason is data underflow and hence playback should be paused.
BUFFERING_HAVE_NOTHING,
// Indicates that enough data has been buffered.
//
// Typical reason is enough data has been prerolled to start playback.
BUFFERING_HAVE_ENOUGH,
};
} // namespace media
#endif // MEDIA_BASE_BUFFERING_STATE_H_
...@@ -48,6 +48,8 @@ Pipeline::Pipeline( ...@@ -48,6 +48,8 @@ Pipeline::Pipeline(
audio_ended_(false), audio_ended_(false),
video_ended_(false), video_ended_(false),
text_ended_(false), text_ended_(false),
audio_buffering_state_(BUFFERING_HAVE_NOTHING),
video_buffering_state_(BUFFERING_HAVE_NOTHING),
demuxer_(NULL), demuxer_(NULL),
creation_time_(default_tick_clock_.NowTicks()) { creation_time_(default_tick_clock_.NowTicks()) {
media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
...@@ -188,7 +190,7 @@ void Pipeline::SetErrorForTesting(PipelineStatus status) { ...@@ -188,7 +190,7 @@ void Pipeline::SetErrorForTesting(PipelineStatus status) {
} }
void Pipeline::SetState(State next_state) { void Pipeline::SetState(State next_state) {
if (state_ != kStarted && next_state == kStarted && if (state_ != kPlaying && next_state == kPlaying &&
!creation_time_.is_null()) { !creation_time_.is_null()) {
UMA_HISTOGRAM_TIMES("Media.TimeToPipelineStarted", UMA_HISTOGRAM_TIMES("Media.TimeToPipelineStarted",
default_tick_clock_.NowTicks() - creation_time_); default_tick_clock_.NowTicks() - creation_time_);
...@@ -211,8 +213,7 @@ const char* Pipeline::GetStateString(State state) { ...@@ -211,8 +213,7 @@ const char* Pipeline::GetStateString(State state) {
RETURN_STRING(kInitVideoRenderer); RETURN_STRING(kInitVideoRenderer);
RETURN_STRING(kInitPrerolling); RETURN_STRING(kInitPrerolling);
RETURN_STRING(kSeeking); RETURN_STRING(kSeeking);
RETURN_STRING(kStarting); RETURN_STRING(kPlaying);
RETURN_STRING(kStarted);
RETURN_STRING(kStopping); RETURN_STRING(kStopping);
RETURN_STRING(kStopped); RETURN_STRING(kStopped);
} }
...@@ -249,15 +250,12 @@ Pipeline::State Pipeline::GetNextState() const { ...@@ -249,15 +250,12 @@ Pipeline::State Pipeline::GetNextState() const {
return kInitPrerolling; return kInitPrerolling;
case kInitPrerolling: case kInitPrerolling:
return kStarting; return kPlaying;
case kSeeking: case kSeeking:
return kStarting; return kPlaying;
case kStarting: case kPlaying:
return kStarted;
case kStarted:
case kStopping: case kStopping:
case kStopped: case kStopped:
break; break;
...@@ -364,11 +362,9 @@ void Pipeline::StateTransitionTask(PipelineStatus status) { ...@@ -364,11 +362,9 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
// Guard against accidentally clearing |pending_callbacks_| for states that // Guard against accidentally clearing |pending_callbacks_| for states that
// use it as well as states that should not be using it. // use it as well as states that should not be using it.
//
// TODO(scherkus): Make every state transition use |pending_callbacks_|.
DCHECK_EQ(pending_callbacks_.get() != NULL, DCHECK_EQ(pending_callbacks_.get() != NULL,
(state_ == kInitPrerolling || state_ == kStarting || (state_ == kInitPrerolling || state_ == kSeeking));
state_ == kSeeking));
pending_callbacks_.reset(); pending_callbacks_.reset();
PipelineStatusCB done_cb = base::Bind( PipelineStatusCB done_cb = base::Bind(
...@@ -412,29 +408,24 @@ void Pipeline::StateTransitionTask(PipelineStatus status) { ...@@ -412,29 +408,24 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
return DoInitialPreroll(done_cb); return DoInitialPreroll(done_cb);
case kStarting: case kPlaying:
return DoPlay(done_cb); PlaybackRateChangedTask(GetPlaybackRate());
VolumeChangedTask(GetVolume());
case kStarted:
{ // We enter this state from either kInitPrerolling or kSeeking. As of now
base::AutoLock l(lock_); // both those states call Preroll(), which means by time we enter this
// We use audio stream to update the clock. So if there is such a // state we've already buffered enough data. Forcefully update the
// stream, we pause the clock until we receive a valid timestamp. // buffering state, which start the clock and renderers and transition
waiting_for_clock_update_ = true; // into kPlaying state.
if (!audio_renderer_) { //
clock_->SetMaxTime(clock_->Duration()); // TODO(scherkus): Remove after renderers are taught to fire buffering
StartClockIfWaitingForTimeUpdate_Locked(); // state callbacks http://crbug.com/144683
} DCHECK(WaitingForEnoughData());
} if (audio_renderer_)
BufferingStateChanged(&audio_buffering_state_, BUFFERING_HAVE_ENOUGH);
DCHECK(!seek_cb_.is_null()); if (video_renderer_)
DCHECK_EQ(status_, PIPELINE_OK); BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH);
return;
// Fire canplaythrough immediately after playback begins because of
// crbug.com/106480.
// TODO(vrk): set ready state to HaveFutureData when bug above is fixed.
preroll_completed_cb_.Run();
return base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
case kStopping: case kStopping:
case kStopped: case kStopped:
...@@ -469,6 +460,16 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) { ...@@ -469,6 +460,16 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
bound_fns.Push(base::Bind( bound_fns.Push(base::Bind(
&VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
seek_timestamp)); seek_timestamp));
// TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
// state callbacks http://crbug.com/144683
bound_fns.Push(base::Bind(&VideoRenderer::Play,
base::Unretained(video_renderer_.get())));
}
if (text_renderer_) {
bound_fns.Push(base::Bind(
&TextRenderer::Play, base::Unretained(text_renderer_.get())));
} }
pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
...@@ -495,10 +496,24 @@ void Pipeline::DoSeek( ...@@ -495,10 +496,24 @@ void Pipeline::DoSeek(
if (audio_renderer_) { if (audio_renderer_) {
bound_fns.Push(base::Bind( bound_fns.Push(base::Bind(
&AudioRenderer::Flush, base::Unretained(audio_renderer_.get()))); &AudioRenderer::Flush, base::Unretained(audio_renderer_.get())));
// TODO(scherkus): Remove after AudioRenderer is taught to fire buffering
// state callbacks http://crbug.com/144683
bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged,
base::Unretained(this),
&audio_buffering_state_,
BUFFERING_HAVE_NOTHING));
} }
if (video_renderer_) { if (video_renderer_) {
bound_fns.Push(base::Bind( bound_fns.Push(base::Bind(
&VideoRenderer::Flush, base::Unretained(video_renderer_.get()))); &VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
// TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
// state callbacks http://crbug.com/144683
bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged,
base::Unretained(this),
&video_buffering_state_,
BUFFERING_HAVE_NOTHING));
} }
if (text_renderer_) { if (text_renderer_) {
bound_fns.Push(base::Bind( bound_fns.Push(base::Bind(
...@@ -520,27 +535,11 @@ void Pipeline::DoSeek( ...@@ -520,27 +535,11 @@ void Pipeline::DoSeek(
bound_fns.Push(base::Bind( bound_fns.Push(base::Bind(
&VideoRenderer::Preroll, base::Unretained(video_renderer_.get()), &VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
seek_timestamp)); seek_timestamp));
}
pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
}
void Pipeline::DoPlay(const PipelineStatusCB& done_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!pending_callbacks_.get());
SerialRunner::Queue bound_fns;
PlaybackRateChangedTask(GetPlaybackRate());
VolumeChangedTask(GetVolume());
if (audio_renderer_) {
bound_fns.Push(base::Bind(
&AudioRenderer::Play, base::Unretained(audio_renderer_.get())));
}
if (video_renderer_) { // TODO(scherkus): Remove after renderers are taught to fire buffering
bound_fns.Push(base::Bind( // state callbacks http://crbug.com/144683
&VideoRenderer::Play, base::Unretained(video_renderer_.get()))); bound_fns.Push(base::Bind(&VideoRenderer::Play,
base::Unretained(video_renderer_.get())));
} }
if (text_renderer_) { if (text_renderer_) {
...@@ -706,7 +705,7 @@ void Pipeline::PlaybackRateChangedTask(float playback_rate) { ...@@ -706,7 +705,7 @@ void Pipeline::PlaybackRateChangedTask(float playback_rate) {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
// Playback rate changes are only carried out while playing. // Playback rate changes are only carried out while playing.
if (state_ != kStarting && state_ != kStarted) if (state_ != kPlaying)
return; return;
{ {
...@@ -724,7 +723,7 @@ void Pipeline::VolumeChangedTask(float volume) { ...@@ -724,7 +723,7 @@ void Pipeline::VolumeChangedTask(float volume) {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
// Volume changes are only carried out while playing. // Volume changes are only carried out while playing.
if (state_ != kStarting && state_ != kStarted) if (state_ != kPlaying)
return; return;
if (audio_renderer_) if (audio_renderer_)
...@@ -736,7 +735,7 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { ...@@ -736,7 +735,7 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
DCHECK(stop_cb_.is_null()); DCHECK(stop_cb_.is_null());
// Suppress seeking if we're not fully started. // Suppress seeking if we're not fully started.
if (state_ != kStarted) { if (state_ != kPlaying) {
DCHECK(state_ == kStopping || state_ == kStopped) DCHECK(state_ == kStopping || state_ == kStopped)
<< "Receive extra seek in unexpected state: " << state_; << "Receive extra seek in unexpected state: " << state_;
...@@ -770,7 +769,7 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { ...@@ -770,7 +769,7 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
void Pipeline::DoAudioRendererEnded() { void Pipeline::DoAudioRendererEnded() {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
if (state_ != kStarted) if (state_ != kPlaying)
return; return;
DCHECK(!audio_ended_); DCHECK(!audio_ended_);
...@@ -789,7 +788,7 @@ void Pipeline::DoAudioRendererEnded() { ...@@ -789,7 +788,7 @@ void Pipeline::DoAudioRendererEnded() {
void Pipeline::DoVideoRendererEnded() { void Pipeline::DoVideoRendererEnded() {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
if (state_ != kStarted) if (state_ != kPlaying)
return; return;
DCHECK(!video_ended_); DCHECK(!video_ended_);
...@@ -801,7 +800,7 @@ void Pipeline::DoVideoRendererEnded() { ...@@ -801,7 +800,7 @@ void Pipeline::DoVideoRendererEnded() {
void Pipeline::DoTextRendererEnded() { void Pipeline::DoTextRendererEnded() {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
if (state_ != kStarted) if (state_ != kPlaying)
return; return;
DCHECK(!text_ended_); DCHECK(!text_ended_);
...@@ -888,13 +887,79 @@ void Pipeline::OnAudioUnderflow() { ...@@ -888,13 +887,79 @@ void Pipeline::OnAudioUnderflow() {
return; return;
} }
if (state_ != kStarted) if (state_ != kPlaying)
return; return;
if (audio_renderer_) if (audio_renderer_)
audio_renderer_->ResumeAfterUnderflow(); audio_renderer_->ResumeAfterUnderflow();
} }
void Pipeline::BufferingStateChanged(BufferingState* buffering_state,
BufferingState new_buffering_state) {
DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", "
<< " " << new_buffering_state << ") "
<< (buffering_state == &audio_buffering_state_ ? "audio" : "video");
DCHECK(task_runner_->BelongsToCurrentThread());
bool was_waiting_for_enough_data = WaitingForEnoughData();
*buffering_state = new_buffering_state;
// Renderer underflowed.
if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
StartWaitingForEnoughData();
return;
}
// Renderer prerolled.
if (was_waiting_for_enough_data && !WaitingForEnoughData()) {
StartPlayback();
return;
}
}
bool Pipeline::WaitingForEnoughData() const {
DCHECK(task_runner_->BelongsToCurrentThread());
if (state_ != kPlaying)
return false;
if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH)
return true;
if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH)
return true;
return false;
}
void Pipeline::StartWaitingForEnoughData() {
DCHECK_EQ(state_, kPlaying);
DCHECK(WaitingForEnoughData());
if (audio_renderer_)
audio_renderer_->Pause();
base::AutoLock auto_lock(lock_);
clock_->Pause();
}
void Pipeline::StartPlayback() {
DCHECK_EQ(state_, kPlaying);
DCHECK(!WaitingForEnoughData());
if (audio_renderer_) {
audio_renderer_->Play();
base::AutoLock auto_lock(lock_);
// We use audio stream to update the clock. So if there is such a
// stream, we pause the clock until we receive a valid timestamp.
waiting_for_clock_update_ = true;
} else {
base::AutoLock auto_lock(lock_);
clock_->SetMaxTime(clock_->Duration());
clock_->Play();
}
preroll_completed_cb_.Run();
if (!seek_cb_.is_null())
base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
}
void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
lock_.AssertAcquired(); lock_.AssertAcquired();
if (!waiting_for_clock_update_) if (!waiting_for_clock_update_)
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
#include "base/time/default_tick_clock.h" #include "base/time/default_tick_clock.h"
#include "media/base/audio_renderer.h" #include "media/base/audio_renderer.h"
#include "media/base/buffering_state.h"
#include "media/base/demuxer.h" #include "media/base/demuxer.h"
#include "media/base/media_export.h" #include "media/base/media_export.h"
#include "media/base/pipeline_status.h" #include "media/base/pipeline_status.h"
...@@ -58,14 +59,13 @@ typedef base::Callback<void(PipelineMetadata)> PipelineMetadataCB; ...@@ -58,14 +59,13 @@ typedef base::Callback<void(PipelineMetadata)> PipelineMetadataCB;
// [ InitXXX (for each filter) ] [ Stopping ] // [ InitXXX (for each filter) ] [ Stopping ]
// | | // | |
// V V // V V
// [ InitPreroll ] [ Stopped ] // [ InitPrerolling ] [ Stopped ]
// | // |
// V // V
// [ Starting ] <-- [ Seeking ] // [ Playing ] <-- [ Seeking ]
// | ^ // | ^
// V | // `---------------'
// [ Started ] ----------' // Seek()
// Seek()
// //
// Initialization is a series of state transitions from "Created" through each // Initialization is a series of state transitions from "Created" through each
// filter initialization state. When all filter initialization states have // filter initialization state. When all filter initialization states have
...@@ -194,8 +194,7 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost { ...@@ -194,8 +194,7 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
kInitVideoRenderer, kInitVideoRenderer,
kInitPrerolling, kInitPrerolling,
kSeeking, kSeeking,
kStarting, kPlaying,
kStarted,
kStopping, kStopping,
kStopped, kStopped,
}; };
...@@ -305,10 +304,6 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost { ...@@ -305,10 +304,6 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
// indepentent from seeking. // indepentent from seeking.
void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb); void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb);
// Updates playback rate and volume and initiates an asynchronous play call
// sequence executing |done_cb| with the final status when completed.
void DoPlay(const PipelineStatusCB& done_cb);
// Initiates an asynchronous pause-flush-stop call sequence executing // Initiates an asynchronous pause-flush-stop call sequence executing
// |done_cb| when completed. // |done_cb| when completed.
void DoStop(const PipelineStatusCB& done_cb); void DoStop(const PipelineStatusCB& done_cb);
...@@ -316,6 +311,21 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost { ...@@ -316,6 +311,21 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
void OnAudioUnderflow(); void OnAudioUnderflow();
// Collection of callback methods and helpers for tracking changes in
// buffering state and transition from paused/underflow states and playing
// states.
//
// While in the kPlaying state:
// - A waiting to non-waiting transition indicates preroll has completed
// and StartPlayback() should be called
// - A non-waiting to waiting transition indicates underflow has occurred
// and StartWaitingForEnoughData() should be called
void BufferingStateChanged(BufferingState* buffering_state,
BufferingState new_buffering_state);
bool WaitingForEnoughData() const;
void StartWaitingForEnoughData();
void StartPlayback();
void StartClockIfWaitingForTimeUpdate_Locked(); void StartClockIfWaitingForTimeUpdate_Locked();
// Task runner used to execute pipeline tasks. // Task runner used to execute pipeline tasks.
...@@ -377,6 +387,9 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost { ...@@ -377,6 +387,9 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
bool video_ended_; bool video_ended_;
bool text_ended_; bool text_ended_;
BufferingState audio_buffering_state_;
BufferingState video_buffering_state_;
// Temporary callback used for Start() and Seek(). // Temporary callback used for Start() and Seek().
PipelineStatusCB seek_cb_; PipelineStatusCB seek_cb_;
......
...@@ -860,7 +860,6 @@ class PipelineTeardownTest : public PipelineTest { ...@@ -860,7 +860,6 @@ class PipelineTeardownTest : public PipelineTest {
kFlushing, kFlushing,
kSeeking, kSeeking,
kPrerolling, kPrerolling,
kStarting,
kPlaying, kPlaying,
}; };
...@@ -885,7 +884,6 @@ class PipelineTeardownTest : public PipelineTest { ...@@ -885,7 +884,6 @@ class PipelineTeardownTest : public PipelineTest {
case kFlushing: case kFlushing:
case kSeeking: case kSeeking:
case kPrerolling: case kPrerolling:
case kStarting:
DoInitialize(state, stop_or_error); DoInitialize(state, stop_or_error);
DoSeek(state, stop_or_error); DoSeek(state, stop_or_error);
break; break;
...@@ -1107,18 +1105,6 @@ class PipelineTeardownTest : public PipelineTest { ...@@ -1107,18 +1105,6 @@ class PipelineTeardownTest : public PipelineTest {
EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f)); EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f));
EXPECT_CALL(*audio_renderer_, SetVolume(1.0f)); EXPECT_CALL(*audio_renderer_, SetVolume(1.0f));
if (state == kStarting) {
if (stop_or_error == kStop) {
EXPECT_CALL(*audio_renderer_, Play())
.WillOnce(Stop(pipeline_.get(), stop_cb));
} else {
status = PIPELINE_ERROR_READ;
EXPECT_CALL(*audio_renderer_, Play())
.WillOnce(SetError(pipeline_.get(), status));
}
return status;
}
NOTREACHED() << "State not supported: " << state; NOTREACHED() << "State not supported: " << state;
return status; return status;
} }
...@@ -1168,7 +1154,6 @@ INSTANTIATE_TEARDOWN_TEST(Stop, Pausing); ...@@ -1168,7 +1154,6 @@ INSTANTIATE_TEARDOWN_TEST(Stop, Pausing);
INSTANTIATE_TEARDOWN_TEST(Stop, Flushing); INSTANTIATE_TEARDOWN_TEST(Stop, Flushing);
INSTANTIATE_TEARDOWN_TEST(Stop, Seeking); INSTANTIATE_TEARDOWN_TEST(Stop, Seeking);
INSTANTIATE_TEARDOWN_TEST(Stop, Prerolling); INSTANTIATE_TEARDOWN_TEST(Stop, Prerolling);
INSTANTIATE_TEARDOWN_TEST(Stop, Starting);
INSTANTIATE_TEARDOWN_TEST(Stop, Playing); INSTANTIATE_TEARDOWN_TEST(Stop, Playing);
INSTANTIATE_TEARDOWN_TEST(Error, InitDemuxer); INSTANTIATE_TEARDOWN_TEST(Error, InitDemuxer);
...@@ -1178,7 +1163,6 @@ INSTANTIATE_TEARDOWN_TEST(Error, Pausing); ...@@ -1178,7 +1163,6 @@ INSTANTIATE_TEARDOWN_TEST(Error, Pausing);
INSTANTIATE_TEARDOWN_TEST(Error, Flushing); INSTANTIATE_TEARDOWN_TEST(Error, Flushing);
INSTANTIATE_TEARDOWN_TEST(Error, Seeking); INSTANTIATE_TEARDOWN_TEST(Error, Seeking);
INSTANTIATE_TEARDOWN_TEST(Error, Prerolling); INSTANTIATE_TEARDOWN_TEST(Error, Prerolling);
INSTANTIATE_TEARDOWN_TEST(Error, Starting);
INSTANTIATE_TEARDOWN_TEST(Error, Playing); INSTANTIATE_TEARDOWN_TEST(Error, Playing);
INSTANTIATE_TEARDOWN_TEST(ErrorAndStop, Playing); INSTANTIATE_TEARDOWN_TEST(ErrorAndStop, Playing);
......
...@@ -248,6 +248,7 @@ ...@@ -248,6 +248,7 @@
'base/bit_reader_core.cc', 'base/bit_reader_core.cc',
'base/bit_reader_core.h', 'base/bit_reader_core.h',
'base/bitstream_buffer.h', 'base/bitstream_buffer.h',
'base/buffering_state.h',
'base/buffers.h', 'base/buffers.h',
'base/byte_queue.cc', 'base/byte_queue.cc',
'base/byte_queue.h', 'base/byte_queue.h',
......
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