1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "media/filters/video_frame_scheduler_impl.h" 6 7 #include <list> 8 9 #include "base/single_thread_task_runner.h" 10 #include "base/time/default_tick_clock.h" 11 #include "media/base/video_frame.h" 12 13 namespace media { 14 15 VideoFrameSchedulerImpl::VideoFrameSchedulerImpl( 16 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 17 const DisplayCB& display_cb) 18 : task_runner_(task_runner), 19 display_cb_(display_cb), 20 tick_clock_(new base::DefaultTickClock()) { 21 } 22 23 VideoFrameSchedulerImpl::~VideoFrameSchedulerImpl() { 24 } 25 26 void VideoFrameSchedulerImpl::ScheduleVideoFrame( 27 const scoped_refptr<VideoFrame>& frame, 28 base::TimeTicks wall_ticks, 29 const DoneCB& done_cb) { 30 DCHECK(task_runner_->BelongsToCurrentThread()); 31 DCHECK(!frame->end_of_stream()); 32 pending_frames_.push(PendingFrame(frame, wall_ticks, done_cb)); 33 ResetTimerIfNecessary(); 34 } 35 36 void VideoFrameSchedulerImpl::Reset() { 37 pending_frames_ = PendingFrameQueue(); 38 timer_.Stop(); 39 } 40 41 void VideoFrameSchedulerImpl::SetTickClockForTesting( 42 scoped_ptr<base::TickClock> tick_clock) { 43 tick_clock_.swap(tick_clock); 44 } 45 46 void VideoFrameSchedulerImpl::ResetTimerIfNecessary() { 47 if (pending_frames_.empty()) { 48 DCHECK(!timer_.IsRunning()); 49 return; 50 } 51 52 // Negative times will schedule the callback to run immediately. 53 timer_.Stop(); 54 timer_.Start(FROM_HERE, 55 pending_frames_.top().wall_ticks - tick_clock_->NowTicks(), 56 base::Bind(&VideoFrameSchedulerImpl::OnTimerFired, 57 base::Unretained(this))); 58 } 59 60 void VideoFrameSchedulerImpl::OnTimerFired() { 61 base::TimeTicks now = tick_clock_->NowTicks(); 62 63 // Move all frames that have reached their deadline into a separate queue. 64 std::list<PendingFrame> expired_frames; 65 while (!pending_frames_.empty() && pending_frames_.top().wall_ticks <= now) { 66 expired_frames.push_back(pending_frames_.top()); 67 pending_frames_.pop(); 68 } 69 70 // Signal that all frames except for the last one as dropped. 71 while (expired_frames.size() > 1) { 72 expired_frames.front().done_cb.Run(expired_frames.front().frame, DROPPED); 73 expired_frames.pop_front(); 74 } 75 76 // Display the last expired frame. 77 if (!expired_frames.empty()) { 78 display_cb_.Run(expired_frames.front().frame); 79 expired_frames.front().done_cb.Run(expired_frames.front().frame, DISPLAYED); 80 expired_frames.pop_front(); 81 } 82 83 ResetTimerIfNecessary(); 84 } 85 86 VideoFrameSchedulerImpl::PendingFrame::PendingFrame( 87 const scoped_refptr<VideoFrame>& frame, 88 base::TimeTicks wall_ticks, 89 const DoneCB& done_cb) 90 : frame(frame), wall_ticks(wall_ticks), done_cb(done_cb) { 91 } 92 93 VideoFrameSchedulerImpl::PendingFrame::~PendingFrame() { 94 } 95 96 bool VideoFrameSchedulerImpl::PendingFrame::operator<( 97 const PendingFrame& other) const { 98 // Flip the comparison as std::priority_queue<T>::top() returns the largest 99 // element. 100 // 101 // Assume video frames with identical timestamps contain identical content. 102 return wall_ticks > other.wall_ticks; 103 } 104 105 } // namespace media 106