Commit 4beec40b authored by xhwang@chromium.org's avatar xhwang@chromium.org

Fold DecoderStream::Stop() into the dtor.

BUG=349211
TEST=Existing tests pass.

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283713 0039d316-1c4b-4281-b951-d872f2087c98
parent 317b66eb
......@@ -48,9 +48,9 @@ AudioRendererImpl::AudioRendererImpl(
AudioHardwareConfig* hardware_config)
: task_runner_(task_runner),
sink_(sink),
audio_buffer_stream_(task_runner,
decoders.Pass(),
set_decryptor_ready_cb),
audio_buffer_stream_(new AudioBufferStream(task_runner,
decoders.Pass(),
set_decryptor_ready_cb)),
hardware_config_(hardware_config),
state_(kUninitialized),
buffering_state_(BUFFERING_HAVE_NOTHING),
......@@ -60,9 +60,9 @@ AudioRendererImpl::AudioRendererImpl(
received_end_of_stream_(false),
rendered_end_of_stream_(false),
weak_factory_(this) {
audio_buffer_stream_.set_splice_observer(base::Bind(
audio_buffer_stream_->set_splice_observer(base::Bind(
&AudioRendererImpl::OnNewSpliceBuffer, weak_factory_.GetWeakPtr()));
audio_buffer_stream_.set_config_change_observer(base::Bind(
audio_buffer_stream_->set_config_change_observer(base::Bind(
&AudioRendererImpl::OnConfigChange, weak_factory_.GetWeakPtr()));
}
......@@ -155,8 +155,8 @@ void AudioRendererImpl::DoFlush_Locked() {
DCHECK(!pending_read_);
DCHECK_EQ(state_, kFlushed);
audio_buffer_stream_.Reset(base::Bind(&AudioRendererImpl::ResetDecoderDone,
weak_factory_.GetWeakPtr()));
audio_buffer_stream_->Reset(base::Bind(&AudioRendererImpl::ResetDecoderDone,
weak_factory_.GetWeakPtr()));
}
void AudioRendererImpl::ResetDecoderDone() {
......@@ -215,7 +215,8 @@ void AudioRendererImpl::Stop(const base::Closure& callback) {
sink_ = NULL;
}
audio_buffer_stream_.Stop(callback);
audio_buffer_stream_.reset();
task_runner_->PostTask(FROM_HERE, callback);
}
void AudioRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) {
......@@ -296,7 +297,7 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream,
audio_clock_.reset(new AudioClock(audio_parameters_.sample_rate()));
audio_buffer_stream_.Initialize(
audio_buffer_stream_->Initialize(
stream,
false,
statistics_cb,
......@@ -486,8 +487,8 @@ void AudioRendererImpl::AttemptRead_Locked() {
return;
pending_read_ = true;
audio_buffer_stream_.Read(base::Bind(&AudioRendererImpl::DecodedAudioReady,
weak_factory_.GetWeakPtr()));
audio_buffer_stream_->Read(base::Bind(&AudioRendererImpl::DecodedAudioReady,
weak_factory_.GetWeakPtr()));
}
bool AudioRendererImpl::CanRead_Locked() {
......
......@@ -22,6 +22,7 @@
#include <deque>
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "media/base/audio_decoder.h"
......@@ -197,7 +198,7 @@ class MEDIA_EXPORT AudioRendererImpl
// may deadlock between |task_runner_| and the audio callback thread.
scoped_refptr<media::AudioRendererSink> sink_;
AudioBufferStream audio_buffer_stream_;
scoped_ptr<AudioBufferStream> audio_buffer_stream_;
// Interface to the hardware audio params.
const AudioHardwareConfig* const hardware_config_;
......
......@@ -56,7 +56,30 @@ DecoderStream<StreamType>::DecoderStream(
template <DemuxerStream::Type StreamType>
DecoderStream<StreamType>::~DecoderStream() {
DCHECK(state_ == STATE_UNINITIALIZED || state_ == STATE_STOPPED) << state_;
FUNCTION_DVLOG(2);
DCHECK(task_runner_->BelongsToCurrentThread());
// TODO(xhwang): Fold DecoderSelector::Abort() into the dtor.
if (state_ == STATE_INITIALIZING)
decoder_selector_->Abort();
if (!init_cb_.is_null()) {
task_runner_->PostTask(FROM_HERE,
base::Bind(base::ResetAndReturn(&init_cb_), false));
}
if (!read_cb_.is_null()) {
task_runner_->PostTask(FROM_HERE, base::Bind(
base::ResetAndReturn(&read_cb_), ABORTED, scoped_refptr<Output>()));
}
if (!reset_cb_.is_null())
task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&reset_cb_));
if (decrypting_demuxer_stream_)
decrypting_demuxer_stream_->Stop();
stream_ = NULL;
decoder_.reset();
decrypting_demuxer_stream_.reset();
}
template <DemuxerStream::Type StreamType>
......@@ -89,13 +112,12 @@ template <DemuxerStream::Type StreamType>
void DecoderStream<StreamType>::Read(const ReadCB& read_cb) {
FUNCTION_DVLOG(2);
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_INITIALIZING &&
state_ != STATE_STOPPED) << state_;
DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_INITIALIZING)
<< state_;
// No two reads in the flight at any time.
DCHECK(read_cb_.is_null());
// No read during resetting or stopping process.
DCHECK(reset_cb_.is_null());
DCHECK(stop_cb_.is_null());
if (state_ == STATE_ERROR) {
task_runner_->PostTask(
......@@ -125,9 +147,8 @@ template <DemuxerStream::Type StreamType>
void DecoderStream<StreamType>::Reset(const base::Closure& closure) {
FUNCTION_DVLOG(2);
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_STOPPED) << state_;
DCHECK(state_ != STATE_UNINITIALIZED)<< state_;
DCHECK(reset_cb_.is_null());
DCHECK(stop_cb_.is_null());
reset_cb_ = closure;
......@@ -159,45 +180,6 @@ void DecoderStream<StreamType>::Reset(const base::Closure& closure) {
ResetDecoder();
}
template <DemuxerStream::Type StreamType>
void DecoderStream<StreamType>::Stop(const base::Closure& closure) {
FUNCTION_DVLOG(2);
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK_NE(state_, STATE_STOPPED) << state_;
DCHECK(stop_cb_.is_null());
// TODO(xhwang): This is the only asynchronousness in DecoderStream::Stop().
// Fix this so that we can merge the stopping code into the dtor.
if (state_ == STATE_INITIALIZING) {
stop_cb_ = closure;
decoder_selector_->Abort();
return;
}
DCHECK(init_cb_.is_null());
// All pending callbacks will be dropped.
weak_factory_.InvalidateWeakPtrs();
// Post callbacks to prevent reentrance into this object.
if (!read_cb_.is_null()) {
task_runner_->PostTask(FROM_HERE, base::Bind(
base::ResetAndReturn(&read_cb_), ABORTED, scoped_refptr<Output>()));
}
if (!reset_cb_.is_null())
task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&reset_cb_));
if (decrypting_demuxer_stream_)
decrypting_demuxer_stream_->Stop();
stream_ = NULL;
decoder_.reset();
decrypting_demuxer_stream_.reset();
state_ = STATE_STOPPED;
task_runner_->PostTask(FROM_HERE, closure);
}
template <DemuxerStream::Type StreamType>
bool DecoderStream<StreamType>::CanReadWithoutStalling() const {
DCHECK(task_runner_->BelongsToCurrentThread());
......@@ -251,19 +233,14 @@ void DecoderStream<StreamType>::OnDecoderSelected(
state_ = STATE_UNINITIALIZED;
StreamTraits::FinishInitialization(
base::ResetAndReturn(&init_cb_), selected_decoder.get(), stream_);
} else {
state_ = STATE_NORMAL;
decoder_ = selected_decoder.Pass();
decrypting_demuxer_stream_ = decrypting_demuxer_stream.Pass();
StreamTraits::FinishInitialization(
base::ResetAndReturn(&init_cb_), decoder_.get(), stream_);
}
// Stop() called during initialization.
if (!stop_cb_.is_null()) {
Stop(base::ResetAndReturn(&stop_cb_));
return;
}
state_ = STATE_NORMAL;
decoder_ = selected_decoder.Pass();
decrypting_demuxer_stream_ = decrypting_demuxer_stream.Pass();
StreamTraits::FinishInitialization(
base::ResetAndReturn(&init_cb_), decoder_.get(), stream_);
}
template <DemuxerStream::Type StreamType>
......@@ -281,7 +258,6 @@ void DecoderStream<StreamType>::Decode(
DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_;
DCHECK_LT(pending_decode_requests_, GetMaxDecodeRequests());
DCHECK(reset_cb_.is_null());
DCHECK(stop_cb_.is_null());
DCHECK(buffer);
int buffer_size = buffer->end_of_stream() ? 0 : buffer->data_size();
......@@ -308,7 +284,6 @@ void DecoderStream<StreamType>::OnDecodeDone(int buffer_size,
DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER ||
state_ == STATE_PENDING_DEMUXER_READ || state_ == STATE_ERROR)
<< state_;
DCHECK(stop_cb_.is_null());
DCHECK_GT(pending_decode_requests_, 0);
--pending_decode_requests_;
......@@ -403,7 +378,6 @@ void DecoderStream<StreamType>::ReadFromDemuxerStream() {
DCHECK_EQ(state_, STATE_NORMAL) << state_;
DCHECK(CanDecodeMore());
DCHECK(reset_cb_.is_null());
DCHECK(stop_cb_.is_null());
state_ = STATE_PENDING_DEMUXER_READ;
stream_->Read(base::Bind(&DecoderStream<StreamType>::OnBufferReady,
......@@ -418,15 +392,13 @@ void DecoderStream<StreamType>::OnBufferReady(
<< (buffer ? buffer->AsHumanReadableString() : "NULL");
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(state_ == STATE_PENDING_DEMUXER_READ || state_ == STATE_ERROR ||
state_ == STATE_STOPPED)
DCHECK(state_ == STATE_PENDING_DEMUXER_READ || state_ == STATE_ERROR)
<< state_;
DCHECK_EQ(buffer.get() != NULL, status == DemuxerStream::kOk) << status;
DCHECK(stop_cb_.is_null());
// Decoding has been stopped (e.g due to an error).
if (state_ != STATE_PENDING_DEMUXER_READ) {
DCHECK(state_ == STATE_ERROR || state_ == STATE_STOPPED);
DCHECK(state_ == STATE_ERROR);
DCHECK(read_cb_.is_null());
return;
}
......@@ -507,7 +479,6 @@ void DecoderStream<StreamType>::OnDecoderReinitialized(PipelineStatus status) {
FUNCTION_DVLOG(2);
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK_EQ(state_, STATE_REINITIALIZING_DECODER) << state_;
DCHECK(stop_cb_.is_null());
// ReinitializeDecoder() can be called in two cases:
// 1, Flushing decoder finished (see OnDecodeOutputReady()).
......@@ -555,7 +526,6 @@ void DecoderStream<StreamType>::OnDecoderReset() {
// before the reset callback is fired.
DCHECK(read_cb_.is_null());
DCHECK(!reset_cb_.is_null());
DCHECK(stop_cb_.is_null());
if (state_ != STATE_FLUSHING_DECODER) {
state_ = STATE_NORMAL;
......
......@@ -65,23 +65,16 @@ class MEDIA_EXPORT DecoderStream {
// Reads a decoded Output and returns it via the |read_cb|. Note that
// |read_cb| is always called asynchronously. This method should only be
// called after initialization has succeeded and must not be called during
// any pending Reset() and/or Stop().
// pending Reset().
void Read(const ReadCB& read_cb);
// Resets the decoder, flushes all decoded outputs and/or internal buffers,
// fires any existing pending read callback and calls |closure| on completion.
// Note that |closure| is always called asynchronously. This method should
// only be called after initialization has succeeded and must not be called
// during any pending Reset() and/or Stop().
// during pending Reset().
void Reset(const base::Closure& closure);
// Stops the decoder, fires any existing pending read callback or reset
// callback and calls |closure| on completion. Note that |closure| is always
// called asynchronously. The DecoderStream cannot be used anymore after
// it is stopped. This method can be called at any time but not during another
// pending Stop().
void Stop(const base::Closure& closure);
// Returns true if the decoder currently has the ability to decode and return
// an Output.
// TODO(rileya): Remove the need for this by refactoring Decoder queueing
......@@ -117,12 +110,11 @@ class MEDIA_EXPORT DecoderStream {
enum State {
STATE_UNINITIALIZED,
STATE_INITIALIZING,
STATE_NORMAL, // Includes idle, pending decoder decode/reset/stop.
STATE_NORMAL, // Includes idle, pending decoder decode/reset.
STATE_FLUSHING_DECODER,
STATE_PENDING_DEMUXER_READ,
STATE_REINITIALIZING_DECODER,
STATE_END_OF_STREAM, // End of stream reached; returns EOS on all reads.
STATE_STOPPED,
STATE_ERROR
};
......@@ -174,7 +166,6 @@ class MEDIA_EXPORT DecoderStream {
ReadCB read_cb_;
base::Closure reset_cb_;
base::Closure stop_cb_;
DemuxerStream* stream_;
bool low_delay_;
......
......@@ -76,14 +76,19 @@ class VideoFrameStreamTest
}
~VideoFrameStreamTest() {
// Check that the pipeline statistics callback was fired correctly.
if (decoder_)
EXPECT_EQ(decoder_->total_bytes_decoded(), total_bytes_decoded_);
is_initialized_ = false;
decoder_ = NULL;
video_frame_stream_.reset();
message_loop_.RunUntilIdle();
DCHECK(!pending_initialize_);
DCHECK(!pending_read_);
DCHECK(!pending_reset_);
DCHECK(!pending_stop_);
if (is_initialized_)
Stop();
EXPECT_FALSE(is_initialized_);
}
MOCK_METHOD1(OnNewSpliceBuffer, void(base::TimeDelta));
......@@ -157,16 +162,6 @@ class VideoFrameStreamTest
pending_reset_ = false;
}
void OnStopped() {
DCHECK(!pending_initialize_);
DCHECK(!pending_read_);
DCHECK(!pending_reset_);
DCHECK(pending_stop_);
pending_stop_ = false;
is_initialized_ = false;
decoder_ = NULL;
}
void ReadOneFrame() {
frame_read_ = NULL;
pending_read_ = true;
......@@ -270,7 +265,7 @@ class VideoFrameStreamTest
break;
// These two cases are only interesting to test during
// VideoFrameStream::Stop(). There's no need to satisfy a callback.
// VideoFrameStream destruction. There's no need to satisfy a callback.
case SET_DECRYPTOR:
case DECRYPTOR_NO_KEY:
NOTREACHED();
......@@ -315,15 +310,6 @@ class VideoFrameStreamTest
SatisfyPendingCallback(DECODER_RESET);
}
void Stop() {
// Check that the pipeline statistics callback was fired correctly.
EXPECT_EQ(decoder_->total_bytes_decoded(), total_bytes_decoded_);
pending_stop_ = true;
video_frame_stream_->Stop(base::Bind(&VideoFrameStreamTest::OnStopped,
base::Unretained(this)));
message_loop_.RunUntilIdle();
}
base::MessageLoop message_loop_;
scoped_ptr<VideoFrameStream> video_frame_stream_;
......@@ -577,118 +563,98 @@ TEST_P(VideoFrameStreamTest, Reset_DuringNoKeyRead) {
Reset();
}
TEST_P(VideoFrameStreamTest, Stop_BeforeInitialization) {
pending_stop_ = true;
video_frame_stream_->Stop(
base::Bind(&VideoFrameStreamTest::OnStopped, base::Unretained(this)));
message_loop_.RunUntilIdle();
// In the following Destroy_* tests, |video_frame_stream_| is destroyed in
// VideoFrameStreamTest dtor.
TEST_P(VideoFrameStreamTest, Destroy_BeforeInitialization) {
}
TEST_P(VideoFrameStreamTest, Stop_DuringSetDecryptor) {
TEST_P(VideoFrameStreamTest, Destroy_DuringSetDecryptor) {
if (!GetParam().is_encrypted) {
DVLOG(1) << "SetDecryptor test only runs when the stream is encrytped.";
return;
}
EnterPendingState(SET_DECRYPTOR);
pending_stop_ = true;
video_frame_stream_->Stop(
base::Bind(&VideoFrameStreamTest::OnStopped, base::Unretained(this)));
message_loop_.RunUntilIdle();
}
TEST_P(VideoFrameStreamTest, Stop_DuringInitialization) {
TEST_P(VideoFrameStreamTest, Destroy_DuringInitialization) {
EnterPendingState(DECODER_INIT);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterInitialization) {
TEST_P(VideoFrameStreamTest, Destroy_AfterInitialization) {
Initialize();
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringReinitialization) {
TEST_P(VideoFrameStreamTest, Destroy_DuringReinitialization) {
Initialize();
EnterPendingState(DECODER_REINIT);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterReinitialization) {
TEST_P(VideoFrameStreamTest, Destroy_AfterReinitialization) {
Initialize();
EnterPendingState(DECODER_REINIT);
SatisfyPendingCallback(DECODER_REINIT);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringDemuxerRead_Normal) {
TEST_P(VideoFrameStreamTest, Destroy_DuringDemuxerRead_Normal) {
Initialize();
EnterPendingState(DEMUXER_READ_NORMAL);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringDemuxerRead_ConfigChange) {
TEST_P(VideoFrameStreamTest, Destroy_DuringDemuxerRead_ConfigChange) {
Initialize();
EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringNormalDecoderDecode) {
TEST_P(VideoFrameStreamTest, Destroy_DuringNormalDecoderDecode) {
Initialize();
EnterPendingState(DECODER_DECODE);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterNormalRead) {
TEST_P(VideoFrameStreamTest, Destroy_AfterNormalRead) {
Initialize();
Read();
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterConfigChangeRead) {
TEST_P(VideoFrameStreamTest, Destroy_AfterConfigChangeRead) {
Initialize();
EnterPendingState(DEMUXER_READ_CONFIG_CHANGE);
SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringNoKeyRead) {
TEST_P(VideoFrameStreamTest, Destroy_DuringNoKeyRead) {
Initialize();
EnterPendingState(DECRYPTOR_NO_KEY);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringReset) {
TEST_P(VideoFrameStreamTest, Destroy_DuringReset) {
Initialize();
EnterPendingState(DECODER_RESET);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterReset) {
TEST_P(VideoFrameStreamTest, Destroy_AfterReset) {
Initialize();
Reset();
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_DuringRead_DuringReset) {
TEST_P(VideoFrameStreamTest, Destroy_DuringRead_DuringReset) {
Initialize();
EnterPendingState(DECODER_DECODE);
EnterPendingState(DECODER_RESET);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterRead_DuringReset) {
TEST_P(VideoFrameStreamTest, Destroy_AfterRead_DuringReset) {
Initialize();
EnterPendingState(DECODER_DECODE);
EnterPendingState(DECODER_RESET);
SatisfyPendingCallback(DECODER_DECODE);
Stop();
}
TEST_P(VideoFrameStreamTest, Stop_AfterRead_AfterReset) {
TEST_P(VideoFrameStreamTest, Destroy_AfterRead_AfterReset) {
Initialize();
Read();
Reset();
Stop();
}
TEST_P(VideoFrameStreamTest, DecoderErrorWhenReading) {
......
......@@ -25,7 +25,9 @@ VideoRendererImpl::VideoRendererImpl(
const PaintCB& paint_cb,
bool drop_frames)
: task_runner_(task_runner),
video_frame_stream_(task_runner, decoders.Pass(), set_decryptor_ready_cb),
video_frame_stream_(new VideoFrameStream(task_runner,
decoders.Pass(),
set_decryptor_ready_cb)),
low_delay_(false),
received_end_of_stream_(false),
rendered_end_of_stream_(false),
......@@ -66,7 +68,7 @@ void VideoRendererImpl::Flush(const base::Closure& callback) {
received_end_of_stream_ = false;
rendered_end_of_stream_ = false;
video_frame_stream_.Reset(
video_frame_stream_->Reset(
base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
weak_factory_.GetWeakPtr()));
}
......@@ -75,7 +77,7 @@ void VideoRendererImpl::Stop(const base::Closure& callback) {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
if (state_ == kUninitialized || state_ == kStopped) {
callback.Run();
task_runner_->PostTask(FROM_HERE, callback);
return;
}
......@@ -102,7 +104,8 @@ void VideoRendererImpl::Stop(const base::Closure& callback) {
base::PlatformThread::Join(thread_to_join);
}
video_frame_stream_.Stop(callback);
video_frame_stream_.reset();
task_runner_->PostTask(FROM_HERE, callback);
}
void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) {
......@@ -153,7 +156,7 @@ void VideoRendererImpl::Initialize(DemuxerStream* stream,
get_duration_cb_ = get_duration_cb;
state_ = kInitializing;
video_frame_stream_.Initialize(
video_frame_stream_->Initialize(
stream,
low_delay,
statistics_cb,
......@@ -360,7 +363,7 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
bool VideoRendererImpl::HaveEnoughData_Locked() {
DCHECK_EQ(state_, kPlaying);
return received_end_of_stream_ ||
!video_frame_stream_.CanReadWithoutStalling() ||
!video_frame_stream_->CanReadWithoutStalling() ||
ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames) ||
(low_delay_ && ready_frames_.size() > 0);
}
......@@ -440,8 +443,8 @@ void VideoRendererImpl::AttemptRead_Locked() {
switch (state_) {
case kPlaying:
pending_read_ = true;
video_frame_stream_.Read(base::Bind(&VideoRendererImpl::FrameReady,
weak_factory_.GetWeakPtr()));
video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady,
weak_factory_.GetWeakPtr()));
return;
case kUninitialized:
......
......@@ -8,6 +8,7 @@
#include <deque>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/condition_variable.h"
......@@ -123,7 +124,7 @@ class MEDIA_EXPORT VideoRendererImpl
base::Lock lock_;
// Provides video frames to VideoRendererImpl.
VideoFrameStream video_frame_stream_;
scoped_ptr<VideoFrameStream> video_frame_stream_;
// Flag indicating low-delay mode.
bool low_delay_;
......
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