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..a3589accfc796205eca59d4a20cade9c000c240c 100644 |
| --- a/media/filters/video_renderer_impl.cc |
| +++ b/media/filters/video_renderer_impl.cc |
| @@ -5,74 +5,73 @@ |
| #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), |
| + next_scheduled_frame_id_(0), |
| 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; |
| + |
| + ScheduleAllFrames(); |
| + 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); |
| + |
| state_ = kPaused; |
| + ResetScheduler(); |
| + |
| callback.Run(); |
| } |
| 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. |
| - ready_frames_.clear(); |
| + unscheduled_frames_.clear(); |
| + |
| received_end_of_stream_ = false; |
| video_frame_stream_.Reset( |
| base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone, |
| @@ -81,7 +80,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 +92,40 @@ 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); |
| - } |
| + unscheduled_frames_.clear(); |
| + scheduled_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; |
| + ResetScheduler(); |
| + |
| + if (state_ == kPlaying) |
| + ScheduleAllFrames(); |
| } |
| 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. |
| DCHECK(time == kNoTimestamp()); |
| } |
| @@ -139,12 +133,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 +150,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 +178,6 @@ void VideoRendererImpl::Initialize(DemuxerStream* stream, |
| void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { |
| DCHECK(task_runner_->BelongsToCurrentThread()); |
| - base::AutoLock auto_lock(lock_); |
| if (state_ == kStopped) |
| return; |
| @@ -198,143 +190,14 @@ void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { |
| return; |
| } |
| - // We're all good! Consider ourselves flushed. (ThreadMain() should never |
| - // see us in the kUninitialized state). |
| - // Since we had an initial Preroll(), we consider ourself flushed, because we |
| - // have not populated any buffers yet. |
| + // Consider ourselves flushed as no frames have been received 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 +230,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 +241,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 +253,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,28 +266,49 @@ 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(); |
| + ScheduleAllFrames(); |
| + |
| + 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); |
| +} |
| + |
| +bool VideoRendererImpl::ShouldTransitionToEnded() { |
| + return state_ == kPlaying && unscheduled_frames_.empty() && |
| + scheduled_frames_.empty() && received_end_of_stream_; |
| } |
| -void VideoRendererImpl::AttemptRead_Locked() { |
| +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; |
| } |
| @@ -470,41 +334,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 +355,113 @@ 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::ResetScheduler() { |
| + for (VideoFrameIDMap::iterator iter = scheduled_frames_.begin(); |
| + iter != scheduled_frames_.end(); |
| + ++iter) { |
| + unscheduled_frames_.push_back(iter->second); |
| + } |
|
xhwang
2014/04/25 05:44:47
With all the asyncness, it's not a surprise that a
scherkus (not reviewing)
2014/04/25 18:24:25
It's very possible we'll need to introduce more wa
|
| + scheduled_frames_.clear(); |
| + scheduler_->Reset(); |
| +} |
| + |
| +void VideoRendererImpl::ScheduleAllFrames() { |
| + DCHECK_EQ(state_, kPlaying); |
| + |
| + if (playback_rate_ == 0) |
| + return; |
| + |
| + base::TimeDelta current_media_time = get_time_cb_.Run(); |
| + base::TimeTicks current_wall_ticks = tick_clock_->NowTicks(); |
| + |
| + while (!unscheduled_frames_.empty()) { |
| + // Negative deltas are permitted as it's up to the scheduler to decide |
| + // what it wants to do with late frames. |
| + base::TimeDelta delta = |
| + unscheduled_frames_.front()->timestamp() - current_media_time; |
| + base::TimeDelta scaled_delta = base::TimeDelta::FromMicroseconds( |
| + static_cast<int64>(delta.InMicroseconds() / playback_rate_)); |
| + base::TimeTicks target_wall_ticks = current_wall_ticks + scaled_delta; |
| + |
| + scoped_refptr<VideoFrame> frame = unscheduled_frames_.front(); |
| + int frame_id = next_scheduled_frame_id_++; |
| + scheduled_frames_[frame_id] = frame; |
| + unscheduled_frames_.pop_front(); |
| + |
| + scheduler_->ScheduleVideoFrame( |
| + frame, |
| + target_wall_ticks, |
| + base::Bind(&VideoRendererImpl::OnVideoFrameFinished, |
| + weak_factory_.GetWeakPtr(), |
| + frame_id)); |
| + } |
| +} |
| + |
| +void VideoRendererImpl::ScheduleFirstFrameForImmediateDisplay() { |
| + DCHECK_EQ(state_, kPrerolled); |
| + |
| + scoped_refptr<VideoFrame> frame = unscheduled_frames_.front(); |
| + int frame_id = next_scheduled_frame_id_++; |
| + scheduled_frames_[frame_id] = frame; |
| + unscheduled_frames_.pop_front(); |
| + |
| + scheduler_->ScheduleVideoFrame( |
| + frame, |
| + tick_clock_->NowTicks(), |
| + base::Bind(&VideoRendererImpl::OnVideoFrameFinished, |
| + weak_factory_.GetWeakPtr(), |
| + frame_id)); |
| +} |
| + |
| +void VideoRendererImpl::OnVideoFrameFinished( |
| + int frame_id, |
| + const scoped_refptr<VideoFrame>& frame, |
| + VideoFrameScheduler::Reason reason) { |
| + VideoFrameIDMap::iterator iter = scheduled_frames_.find(frame_id); |
| + if (iter == scheduled_frames_.end()) { |
| + LOG(ERROR) << "Ignoring frame_id=" << frame_id |
|
scherkus (not reviewing)
2014/04/25 02:04:47
note to self: remove
|
| + << ", timestamp=" << frame->timestamp().InMicroseconds(); |
| + return; |
| + } |
| + |
| + CHECK_EQ(iter->second, frame); |
| + scheduled_frames_.erase(iter); |
| - if (frames_decoded_) { |
| - PipelineStatistics statistics; |
| - statistics.video_frames_decoded = frames_decoded_; |
| - statistics.video_frames_dropped = frames_dropped_; |
| - statistics_cb_.Run(statistics); |
| + switch (reason) { |
| + case VideoFrameScheduler::DISPLAYED: |
| + ++frames_decoded_; |
| + break; |
| - frames_decoded_ = 0; |
| - frames_dropped_ = 0; |
| + case VideoFrameScheduler::DROPPED: |
| + ++frames_decoded_; |
| + ++frames_dropped_; |
| + break; |
| } |
| - frame_available_.TimedWait(wait_duration); |
| + PipelineStatistics stats; |
| + stats.video_frames_decoded = frames_decoded_; |
| + stats.video_frames_dropped = frames_dropped_; |
| + statistics_cb_.Run(stats); |
| + |
| + if (ShouldTransitionToEnded()) { |
| + TransitionToEnded(); |
| + return; |
| + } |
| + |
| + AttemptRead(); |
| +} |
| + |
| +void VideoRendererImpl::SetTickClockForTesting( |
| + scoped_ptr<base::TickClock> tick_clock) { |
| + tick_clock_.swap(tick_clock); |
| } |
| } // namespace media |