Chromium Code Reviews| Index: media/filters/video_renderer_impl.cc |
| diff --git a/media/filters/video_renderer_impl.cc b/media/filters/video_renderer_impl.cc |
| index 7d99dbf14a4bbfda6a7b5c68b8e253ddb46dd4f8..76e4e13654ae5fe14c123cb94198e4f37195c48b 100644 |
| --- a/media/filters/video_renderer_impl.cc |
| +++ b/media/filters/video_renderer_impl.cc |
| @@ -5,74 +5,74 @@ |
| #include "media/filters/video_renderer_impl.h" |
| #include "base/bind.h" |
| -#include "base/callback.h" |
| #include "base/callback_helpers.h" |
| -#include "base/debug/trace_event.h" |
| -#include "base/location.h" |
| #include "base/single_thread_task_runner.h" |
| -#include "base/threading/platform_thread.h" |
| -#include "media/base/buffers.h" |
| +#include "base/time/default_tick_clock.h" |
| #include "media/base/limits.h" |
| -#include "media/base/pipeline.h" |
| #include "media/base/video_frame.h" |
| namespace media { |
| VideoRendererImpl::VideoRendererImpl( |
| const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| + scoped_ptr<VideoFrameScheduler> scheduler, |
| ScopedVector<VideoDecoder> decoders, |
| - const SetDecryptorReadyCB& set_decryptor_ready_cb, |
| - const PaintCB& paint_cb, |
| - bool drop_frames) |
| + const SetDecryptorReadyCB& set_decryptor_ready_cb) |
| : task_runner_(task_runner), |
| + scheduler_(scheduler.Pass()), |
| + tick_clock_(new base::DefaultTickClock()), |
| video_frame_stream_(task_runner, decoders.Pass(), set_decryptor_ready_cb), |
| received_end_of_stream_(false), |
| - frame_available_(&lock_), |
| state_(kUninitialized), |
| - thread_(), |
| pending_read_(false), |
| - drop_frames_(drop_frames), |
| playback_rate_(0), |
| - paint_cb_(paint_cb), |
| - last_timestamp_(kNoTimestamp()), |
| frames_decoded_(0), |
| frames_dropped_(0), |
| weak_factory_(this) { |
| - DCHECK(!paint_cb_.is_null()); |
| } |
| VideoRendererImpl::~VideoRendererImpl() { |
| - base::AutoLock auto_lock(lock_); |
| CHECK(state_ == kStopped || state_ == kUninitialized) << state_; |
| - CHECK(thread_.is_null()); |
| } |
| void VideoRendererImpl::Play(const base::Closure& callback) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| - base::AutoLock auto_lock(lock_); |
| DCHECK_EQ(kPrerolled, state_); |
| state_ = kPlaying; |
| + |
| + ScheduleVideoFrames(); |
| + AttemptRead(); |
| + |
| + // Changing from non-playing to playing state can trigger ended. |
| + if (ShouldTransitionToEnded()) |
| + TransitionToEnded(); |
| + |
| callback.Run(); |
| } |
| void VideoRendererImpl::Pause(const base::Closure& callback) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| - base::AutoLock auto_lock(lock_); |
| DCHECK(state_ != kUninitialized || state_ == kError); |
|
acolwell GONE FROM CHROMIUM
2014/04/24 16:43:59
nit: Add != kPausing ?
scherkus (not reviewing)
2014/04/25 02:04:47
kPausing is no more!
|
| - state_ = kPaused; |
| - callback.Run(); |
| + |
| + state_ = kPausing; |
| + pause_cb_ = callback; |
| + scheduler_->Reset(); |
| + |
| + if (ShouldTransitionToPaused()) |
| + TransitionToPaused(); |
| } |
| void VideoRendererImpl::Flush(const base::Closure& callback) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| - base::AutoLock auto_lock(lock_); |
| DCHECK_EQ(state_, kPaused); |
| + DCHECK_EQ(scheduled_frames_.size(), 0u); |
| flush_cb_ = callback; |
| state_ = kFlushing; |
| // This is necessary if the |video_frame_stream_| has already seen an end of |
| // stream and needs to drain it before flushing it. |
|
acolwell GONE FROM CHROMIUM
2014/04/24 16:43:59
nit: Is this comment still correct? It confuses me
scherkus (not reviewing)
2014/04/25 02:04:47
That's my understanding.
I feel the comment might
xhwang
2014/04/25 05:44:47
If you look at the history, it existed even before
scherkus (not reviewing)
2014/04/25 18:24:25
Removed.
|
| - ready_frames_.clear(); |
| + unscheduled_frames_.clear(); |
| + |
| received_end_of_stream_ = false; |
| video_frame_stream_.Reset( |
| base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone, |
| @@ -81,7 +81,6 @@ void VideoRendererImpl::Flush(const base::Closure& callback) { |
| void VideoRendererImpl::Stop(const base::Closure& callback) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| - base::AutoLock auto_lock(lock_); |
| if (state_ == kUninitialized || state_ == kStopped) { |
| callback.Run(); |
| return; |
| @@ -94,44 +93,42 @@ void VideoRendererImpl::Stop(const base::Closure& callback) { |
| statistics_cb_.Reset(); |
| max_time_cb_.Reset(); |
| - DoStopOrError_Locked(); |
| - |
| - // Clean up our thread if present. |
| - base::PlatformThreadHandle thread_to_join = base::PlatformThreadHandle(); |
| - if (!thread_.is_null()) { |
| - // Signal the thread since it's possible to get stopped with the video |
| - // thread waiting for a read to complete. |
| - frame_available_.Signal(); |
| - std::swap(thread_, thread_to_join); |
| - } |
| - if (!thread_to_join.is_null()) { |
| - base::AutoUnlock auto_unlock(lock_); |
| - base::PlatformThread::Join(thread_to_join); |
| - } |
| + // Leave |scheduled_frames_| untouched. If we get the frames back -- great -- |
| + // but it's more likely we'll be destroyed before that. |
| + unscheduled_frames_.clear(); |
| + scheduler_->Reset(); |
| video_frame_stream_.Stop(callback); |
| } |
| void VideoRendererImpl::SetPlaybackRate(float playback_rate) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| - base::AutoLock auto_lock(lock_); |
| + if (playback_rate_ == playback_rate) |
| + return; |
| + |
| playback_rate_ = playback_rate; |
| + |
| + scheduler_->Reset(); |
| + if (state_ == kPlaying) |
| + ScheduleVideoFrames(); |
| } |
| void VideoRendererImpl::Preroll(base::TimeDelta time, |
| const PipelineStatusCB& cb) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| - base::AutoLock auto_lock(lock_); |
| DCHECK(!cb.is_null()); |
| DCHECK(preroll_cb_.is_null()); |
| - DCHECK(state_ == kFlushed || state_== kPaused) << "state_ " << state_; |
| + DCHECK(state_ == kFlushed || state_ == kPaused) << "state_ " << state_; |
| if (state_ == kFlushed) { |
| DCHECK(time != kNoTimestamp()); |
| DCHECK(!pending_read_); |
| - DCHECK(ready_frames_.empty()); |
| + DCHECK(unscheduled_frames_.empty()); |
| + DCHECK(scheduled_frames_.empty()); |
| } else { |
| + // Prerolling without a timestamp is used for rebuffering. No painting |
| + // is required so skip ahead one state. |
| DCHECK(time == kNoTimestamp()); |
| } |
| @@ -139,12 +136,12 @@ void VideoRendererImpl::Preroll(base::TimeDelta time, |
| preroll_cb_ = cb; |
| preroll_timestamp_ = time; |
| - if (ShouldTransitionToPrerolled_Locked()) { |
| - TransitionToPrerolled_Locked(); |
| + if (ShouldTransitionToPrerolled()) { |
| + TransitionToPrerolled(); |
| return; |
| } |
| - AttemptRead_Locked(); |
| + AttemptRead(); |
| } |
| void VideoRendererImpl::Initialize(DemuxerStream* stream, |
| @@ -156,7 +153,6 @@ void VideoRendererImpl::Initialize(DemuxerStream* stream, |
| const TimeDeltaCB& get_time_cb, |
| const TimeDeltaCB& get_duration_cb) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| - base::AutoLock auto_lock(lock_); |
| DCHECK(stream); |
| DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); |
| DCHECK(!init_cb.is_null()); |
| @@ -185,7 +181,6 @@ void VideoRendererImpl::Initialize(DemuxerStream* stream, |
| void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| - base::AutoLock auto_lock(lock_); |
| if (state_ == kStopped) |
| return; |
| @@ -204,137 +199,11 @@ void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { |
| // have not populated any buffers yet. |
| state_ = kFlushed; |
| - // Create our video thread. |
| - if (!base::PlatformThread::Create(0, this, &thread_)) { |
| - NOTREACHED() << "Video thread creation failed"; |
| - state_ = kError; |
| - base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED); |
| - return; |
| - } |
| - |
| -#if defined(OS_WIN) |
| - // Bump up our priority so our sleeping is more accurate. |
| - // TODO(scherkus): find out if this is necessary, but it seems to help. |
| - ::SetThreadPriority(thread_.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL); |
| -#endif // defined(OS_WIN) |
| base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); |
| } |
| -// PlatformThread::Delegate implementation. |
| -void VideoRendererImpl::ThreadMain() { |
| - base::PlatformThread::SetName("CrVideoRenderer"); |
| - |
| - // The number of milliseconds to idle when we do not have anything to do. |
| - // Nothing special about the value, other than we're being more OS-friendly |
| - // than sleeping for 1 millisecond. |
| - // |
| - // TODO(scherkus): switch to pure event-driven frame timing instead of this |
| - // kIdleTimeDelta business http://crbug.com/106874 |
| - const base::TimeDelta kIdleTimeDelta = |
| - base::TimeDelta::FromMilliseconds(10); |
| - |
| - for (;;) { |
| - base::AutoLock auto_lock(lock_); |
| - |
| - // Thread exit condition. |
| - if (state_ == kStopped) |
| - return; |
| - |
| - // Remain idle as long as we're not playing. |
| - if (state_ != kPlaying || playback_rate_ == 0) { |
| - UpdateStatsAndWait_Locked(kIdleTimeDelta); |
| - continue; |
| - } |
| - |
| - // Remain idle until we have the next frame ready for rendering. |
| - if (ready_frames_.empty()) { |
| - if (received_end_of_stream_) { |
| - state_ = kEnded; |
| - ended_cb_.Run(); |
| - |
| - // No need to sleep here as we idle when |state_ != kPlaying|. |
| - continue; |
| - } |
| - |
| - UpdateStatsAndWait_Locked(kIdleTimeDelta); |
| - continue; |
| - } |
| - |
| - base::TimeDelta remaining_time = |
| - CalculateSleepDuration(ready_frames_.front(), playback_rate_); |
| - |
| - // Sleep up to a maximum of our idle time until we're within the time to |
| - // render the next frame. |
| - if (remaining_time.InMicroseconds() > 0) { |
| - remaining_time = std::min(remaining_time, kIdleTimeDelta); |
| - UpdateStatsAndWait_Locked(remaining_time); |
| - continue; |
| - } |
| - |
| - // Deadline is defined as the midpoint between this frame and the next |
| - // frame, using the delta between this frame and the previous frame as the |
| - // assumption for frame duration. |
| - // |
| - // TODO(scherkus): An improvement over midpoint might be selecting the |
| - // minimum and/or maximum between the midpoint and some constants. As a |
| - // thought experiment, consider what would be better than the midpoint |
| - // for both the 1fps case and 120fps case. |
| - // |
| - // TODO(scherkus): This can be vastly improved. Use a histogram to measure |
| - // the accuracy of our frame timing code. http://crbug.com/149829 |
| - if (drop_frames_ && last_timestamp_ != kNoTimestamp()) { |
| - base::TimeDelta now = get_time_cb_.Run(); |
| - base::TimeDelta deadline = ready_frames_.front()->timestamp() + |
| - (ready_frames_.front()->timestamp() - last_timestamp_) / 2; |
| - |
| - if (now > deadline) { |
| - DropNextReadyFrame_Locked(); |
| - continue; |
| - } |
| - } |
| - |
| - // Congratulations! You've made it past the video frame timing gauntlet. |
| - // |
| - // At this point enough time has passed that the next frame that ready for |
| - // rendering. |
| - PaintNextReadyFrame_Locked(); |
| - } |
| -} |
| - |
| -void VideoRendererImpl::PaintNextReadyFrame_Locked() { |
| - lock_.AssertAcquired(); |
| - |
| - scoped_refptr<VideoFrame> next_frame = ready_frames_.front(); |
| - ready_frames_.pop_front(); |
| - frames_decoded_++; |
| - |
| - last_timestamp_ = next_frame->timestamp(); |
| - |
| - paint_cb_.Run(next_frame); |
| - |
| - task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); |
| -} |
| - |
| -void VideoRendererImpl::DropNextReadyFrame_Locked() { |
| - TRACE_EVENT0("media", "VideoRendererImpl:frameDropped"); |
| - |
| - lock_.AssertAcquired(); |
| - |
| - last_timestamp_ = ready_frames_.front()->timestamp(); |
| - ready_frames_.pop_front(); |
| - frames_decoded_++; |
| - frames_dropped_++; |
| - |
| - task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); |
| -} |
| - |
| void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, |
| const scoped_refptr<VideoFrame>& frame) { |
| - base::AutoLock auto_lock(lock_); |
| DCHECK_NE(state_, kUninitialized); |
| DCHECK_NE(state_, kFlushed); |
| @@ -367,7 +236,7 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, |
| // A new preroll will be requested after this one completes so there is no |
| // point trying to collect more frames. |
| if (state_ == kPrerolling) |
| - TransitionToPrerolled_Locked(); |
| + TransitionToPrerolled(); |
| return; |
| } |
| @@ -378,7 +247,10 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, |
| max_time_cb_.Run(get_duration_cb_.Run()); |
| if (state_ == kPrerolling) |
| - TransitionToPrerolled_Locked(); |
| + TransitionToPrerolled(); |
| + |
| + if (ShouldTransitionToEnded()) |
| + TransitionToEnded(); |
| return; |
| } |
| @@ -387,32 +259,9 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, |
| // prerolling has completed. |
| if (state_ == kPrerolling && preroll_timestamp_ != kNoTimestamp() && |
| frame->timestamp() <= preroll_timestamp_) { |
| - ready_frames_.clear(); |
| + unscheduled_frames_.clear(); |
| } |
| - AddReadyFrame_Locked(frame); |
| - |
| - if (ShouldTransitionToPrerolled_Locked()) |
| - TransitionToPrerolled_Locked(); |
| - |
| - // Always request more decoded video if we have capacity. This serves two |
| - // purposes: |
| - // 1) Prerolling while paused |
| - // 2) Keeps decoding going if video rendering thread starts falling behind |
| - AttemptRead_Locked(); |
| -} |
| - |
| -bool VideoRendererImpl::ShouldTransitionToPrerolled_Locked() { |
| - return state_ == kPrerolling && |
| - (!video_frame_stream_.CanReadWithoutStalling() || |
| - ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames)); |
| -} |
| - |
| -void VideoRendererImpl::AddReadyFrame_Locked( |
| - const scoped_refptr<VideoFrame>& frame) { |
| - lock_.AssertAcquired(); |
| - DCHECK(!frame->end_of_stream()); |
| - |
| // Adjust the incoming frame if its rendering stop time is past the duration |
| // of the video itself. This is typically the last frame of the video and |
| // occurs if the container specifies a duration that isn't a multiple of the |
| @@ -423,32 +272,64 @@ void VideoRendererImpl::AddReadyFrame_Locked( |
| frame->set_timestamp(duration); |
| } |
| - ready_frames_.push_back(frame); |
| - DCHECK_LE(ready_frames_.size(), |
| - static_cast<size_t>(limits::kMaxVideoFrames)); |
| + unscheduled_frames_.push_back(frame); |
| + DCHECK_LE(video_frame_count(), limits::kMaxVideoFrames); |
| max_time_cb_.Run(frame->timestamp()); |
| - // Avoid needlessly waking up |thread_| unless playing. |
| + if (ShouldTransitionToPrerolled()) |
| + TransitionToPrerolled(); |
| + |
| if (state_ == kPlaying) |
| - frame_available_.Signal(); |
| + ScheduleVideoFrames(); |
| + |
| + if (ShouldTransitionToEnded()) |
| + TransitionToEnded(); |
| + |
| + // Always request more decoded video if we have capacity. This serves two |
| + // purposes: |
| + // 1) Prerolling while paused |
| + // 2) Keeps decoding going if video rendering thread starts falling behind |
| + AttemptRead(); |
| } |
| -void VideoRendererImpl::AttemptRead() { |
| - base::AutoLock auto_lock(lock_); |
| - AttemptRead_Locked(); |
| +bool VideoRendererImpl::ShouldTransitionToPrerolled() { |
| + return state_ == kPrerolling && |
| + (!video_frame_stream_.CanReadWithoutStalling() || |
| + video_frame_count() >= limits::kMaxVideoFrames); |
| } |
| -void VideoRendererImpl::AttemptRead_Locked() { |
| +bool VideoRendererImpl::ShouldTransitionToPaused() { |
| + return state_ == kPausing && scheduled_frames_.empty(); |
| +} |
| + |
| +void VideoRendererImpl::TransitionToPaused() { |
| + DCHECK(ShouldTransitionToPaused()); |
| + state_ = kPaused; |
| + base::ResetAndReturn(&pause_cb_).Run(); |
| +} |
|
xhwang
2014/04/24 18:48:44
nit: I found it's easier to understand if we just
scherkus (not reviewing)
2014/04/25 02:04:47
yeah ... the biggest thing I wanted out of these t
|
| + |
| +bool VideoRendererImpl::ShouldTransitionToEnded() { |
| + return state_ == kPlaying && unscheduled_frames_.empty() && |
| + scheduled_frames_.empty() && received_end_of_stream_; |
| +} |
| + |
| +void VideoRendererImpl::TransitionToEnded() { |
| + DCHECK(ShouldTransitionToEnded()); |
| + state_ = kEnded; |
| + ended_cb_.Run(); |
| +} |
| + |
| +void VideoRendererImpl::AttemptRead() { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| - lock_.AssertAcquired(); |
| if (pending_read_ || received_end_of_stream_ || |
| - ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { |
| + video_frame_count() == limits::kMaxVideoFrames) { |
| return; |
| } |
| switch (state_) { |
| + case kPausing: |
| case kPaused: |
| case kPrerolling: |
| case kPlaying: |
| @@ -470,41 +351,20 @@ void VideoRendererImpl::AttemptRead_Locked() { |
| } |
| void VideoRendererImpl::OnVideoFrameStreamResetDone() { |
| - base::AutoLock auto_lock(lock_); |
| if (state_ == kStopped) |
| return; |
| DCHECK_EQ(kFlushing, state_); |
| DCHECK(!pending_read_); |
| - DCHECK(ready_frames_.empty()); |
| + DCHECK(unscheduled_frames_.empty()); |
| + DCHECK(scheduled_frames_.empty()); |
| DCHECK(!received_end_of_stream_); |
| state_ = kFlushed; |
| - last_timestamp_ = kNoTimestamp(); |
| base::ResetAndReturn(&flush_cb_).Run(); |
| } |
| -base::TimeDelta VideoRendererImpl::CalculateSleepDuration( |
| - const scoped_refptr<VideoFrame>& next_frame, |
| - float playback_rate) { |
| - // Determine the current and next presentation timestamps. |
| - base::TimeDelta now = get_time_cb_.Run(); |
| - base::TimeDelta next_pts = next_frame->timestamp(); |
| - |
| - // Scale our sleep based on the playback rate. |
| - base::TimeDelta sleep = next_pts - now; |
| - return base::TimeDelta::FromMicroseconds( |
| - static_cast<int64>(sleep.InMicroseconds() / playback_rate)); |
| -} |
| - |
| -void VideoRendererImpl::DoStopOrError_Locked() { |
| - lock_.AssertAcquired(); |
| - last_timestamp_ = kNoTimestamp(); |
| - ready_frames_.clear(); |
| -} |
| - |
| -void VideoRendererImpl::TransitionToPrerolled_Locked() { |
| - lock_.AssertAcquired(); |
| +void VideoRendererImpl::TransitionToPrerolled() { |
| DCHECK_EQ(state_, kPrerolling); |
| state_ = kPrerolled; |
| @@ -512,29 +372,105 @@ void VideoRendererImpl::TransitionToPrerolled_Locked() { |
| // Because we might remain in the prerolled state for an undetermined amount |
| // of time (e.g., we seeked while paused), we'll paint the first prerolled |
| // frame. |
| - if (!ready_frames_.empty()) |
| - PaintNextReadyFrame_Locked(); |
| + if (!unscheduled_frames_.empty()) { |
| + ScheduleFirstFrameForImmediateDisplay(); |
| + AttemptRead(); |
| + } |
| base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); |
| } |
| -void VideoRendererImpl::UpdateStatsAndWait_Locked( |
| - base::TimeDelta wait_duration) { |
| - lock_.AssertAcquired(); |
| - DCHECK_GE(frames_decoded_, 0); |
| - DCHECK_LE(frames_dropped_, frames_decoded_); |
| +void VideoRendererImpl::ScheduleVideoFrames() { |
| + DCHECK_EQ(state_, kPlaying); |
| + |
| + if (playback_rate_ == 0) |
| + return; |
| - if (frames_decoded_) { |
| - PipelineStatistics statistics; |
| - statistics.video_frames_decoded = frames_decoded_; |
| - statistics.video_frames_dropped = frames_dropped_; |
| - statistics_cb_.Run(statistics); |
| + base::TimeDelta current_media_time = get_time_cb_.Run(); |
| + base::TimeTicks current_wall_ticks = tick_clock_->NowTicks(); |
|
acolwell GONE FROM CHROMIUM
2014/04/24 16:43:59
hmm.. ISTM that pipeline's Clock should be the sol
scherkus (not reviewing)
2014/04/25 02:04:47
I agree -- we're not quite done with how we want c
|
| - frames_decoded_ = 0; |
| - frames_dropped_ = 0; |
| + while (!unscheduled_frames_.empty()) { |
| + base::TimeDelta delta = |
| + (unscheduled_frames_.front()->timestamp() - current_media_time) / |
| + playback_rate_; |
|
xhwang
2014/04/24 18:48:44
So even if delta < 0 we still ScheduleVideoFrame()
scherkus (not reviewing)
2014/04/25 02:04:47
Done.
It's up to the scheduler to decide whether
|
| + base::TimeTicks target_wall_ticks = current_wall_ticks + delta; |
| + |
| + scheduled_frames_.push_back(unscheduled_frames_.front()); |
| + unscheduled_frames_.pop_front(); |
| + |
| + scheduler_->ScheduleVideoFrame( |
| + scheduled_frames_.back(), |
| + target_wall_ticks, |
| + base::Bind(&VideoRendererImpl::OnVideoFrameFinished, |
| + weak_factory_.GetWeakPtr())); |
| } |
| +} |
| + |
| +void VideoRendererImpl::ScheduleFirstFrameForImmediateDisplay() { |
|
xhwang
2014/04/24 18:48:44
DCHECK_EQ(state_, kPrerolled);
scherkus (not reviewing)
2014/04/25 02:04:47
Done.
|
| + scheduled_frames_.push_back(unscheduled_frames_.front()); |
| + unscheduled_frames_.pop_front(); |
| + |
| + scheduler_->ScheduleVideoFrame( |
| + scheduled_frames_.back(), |
| + tick_clock_->NowTicks(), |
| + base::Bind(&VideoRendererImpl::OnVideoFrameFinished, |
| + weak_factory_.GetWeakPtr())); |
| +} |
|
xhwang
2014/04/24 18:48:44
Probably we can have a helper function like
Sche
scherkus (not reviewing)
2014/04/25 02:04:47
I tried it out but didn't like how ScheduleAllFram
|
| + |
| +void VideoRendererImpl::OnVideoFrameFinished( |
| + const scoped_refptr<VideoFrame>& frame, |
| + VideoFrameScheduler::Reason reason) { |
| + VideoFrameQueue::iterator iter = |
| + std::find(scheduled_frames_.begin(), scheduled_frames_.end(), frame); |
| + CHECK(iter != scheduled_frames_.end()); |
| + |
| + switch (reason) { |
| + case VideoFrameScheduler::DISPLAYED: { |
| + ++frames_decoded_; |
| + scheduled_frames_.erase(iter); |
|
acolwell GONE FROM CHROMIUM
2014/04/24 16:43:59
nit: An reason not to do this before the switch si
scherkus (not reviewing)
2014/04/25 02:04:47
Done.
|
| + |
| + PipelineStatistics stats; |
| + stats.video_frames_decoded = frames_decoded_; |
| + stats.video_frames_dropped = frames_dropped_; |
| + statistics_cb_.Run(stats); |
| + break; |
| + } |
| + |
| + case VideoFrameScheduler::DROPPED: { |
| + ++frames_decoded_; |
| + ++frames_dropped_; |
| + scheduled_frames_.erase(iter); |
| + |
| + PipelineStatistics stats; |
|
acolwell GONE FROM CHROMIUM
2014/04/24 16:43:59
nit: Move this below w/ a bool guard so we don't h
scherkus (not reviewing)
2014/04/25 02:04:47
With change in Reset() behaviour this function cle
|
| + stats.video_frames_decoded = frames_decoded_; |
| + stats.video_frames_dropped = frames_dropped_; |
| + statistics_cb_.Run(stats); |
| + break; |
| + } |
| + |
| + case VideoFrameScheduler::RESET: { |
| + unscheduled_frames_.push_back(*iter); |
|
xhwang
2014/04/24 18:48:44
ha, It surprised me a bit that the "reset" frames
scherkus (not reviewing)
2014/04/25 02:04:47
out of curiosity, what did you think "reset" was f
|
| + scheduled_frames_.erase(iter); |
| + break; |
| + } |
| + } |
| + |
| + if (ShouldTransitionToPaused()) { |
| + TransitionToPaused(); |
| + return; |
| + } |
| + |
| + if (ShouldTransitionToEnded()) { |
| + TransitionToEnded(); |
| + return; |
| + } |
| + |
| + AttemptRead(); |
| +} |
| - frame_available_.TimedWait(wait_duration); |
| +void VideoRendererImpl::SetTickClockForTesting( |
| + scoped_ptr<base::TickClock> tick_clock) { |
| + tick_clock_.swap(tick_clock); |
| } |
| } // namespace media |