Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Side by Side Diff: media/filters/video_renderer_impl.cc

Issue 237353007: Refactor VideoRendererImpl to use VideoFrameScheduler. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/filters/video_renderer_impl.h" 5 #include "media/filters/video_renderer_impl.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/callback_helpers.h" 8 #include "base/callback_helpers.h"
10 #include "base/debug/trace_event.h"
11 #include "base/location.h"
12 #include "base/single_thread_task_runner.h" 9 #include "base/single_thread_task_runner.h"
13 #include "base/threading/platform_thread.h" 10 #include "base/time/default_tick_clock.h"
14 #include "media/base/buffers.h"
15 #include "media/base/limits.h" 11 #include "media/base/limits.h"
16 #include "media/base/pipeline.h"
17 #include "media/base/video_frame.h" 12 #include "media/base/video_frame.h"
18 13
19 namespace media { 14 namespace media {
20 15
21 VideoRendererImpl::VideoRendererImpl( 16 VideoRendererImpl::VideoRendererImpl(
22 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 17 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
18 scoped_ptr<VideoFrameScheduler> scheduler,
23 ScopedVector<VideoDecoder> decoders, 19 ScopedVector<VideoDecoder> decoders,
24 const SetDecryptorReadyCB& set_decryptor_ready_cb, 20 const SetDecryptorReadyCB& set_decryptor_ready_cb)
25 const PaintCB& paint_cb,
26 bool drop_frames)
27 : task_runner_(task_runner), 21 : task_runner_(task_runner),
22 scheduler_(scheduler.Pass()),
23 tick_clock_(new base::DefaultTickClock()),
28 video_frame_stream_(task_runner, decoders.Pass(), set_decryptor_ready_cb), 24 video_frame_stream_(task_runner, decoders.Pass(), set_decryptor_ready_cb),
25 next_scheduled_frame_id_(0),
29 received_end_of_stream_(false), 26 received_end_of_stream_(false),
30 frame_available_(&lock_),
31 state_(kUninitialized), 27 state_(kUninitialized),
32 thread_(),
33 pending_read_(false), 28 pending_read_(false),
34 drop_frames_(drop_frames),
35 playback_rate_(0), 29 playback_rate_(0),
36 paint_cb_(paint_cb),
37 last_timestamp_(kNoTimestamp()),
38 frames_decoded_(0), 30 frames_decoded_(0),
39 frames_dropped_(0), 31 frames_dropped_(0),
40 weak_factory_(this) { 32 weak_factory_(this) {
41 DCHECK(!paint_cb_.is_null());
42 } 33 }
43 34
44 VideoRendererImpl::~VideoRendererImpl() { 35 VideoRendererImpl::~VideoRendererImpl() {
45 base::AutoLock auto_lock(lock_);
46 CHECK(state_ == kStopped || state_ == kUninitialized) << state_; 36 CHECK(state_ == kStopped || state_ == kUninitialized) << state_;
47 CHECK(thread_.is_null());
48 } 37 }
49 38
50 void VideoRendererImpl::Play(const base::Closure& callback) { 39 void VideoRendererImpl::Play(const base::Closure& callback) {
51 DCHECK(task_runner_->BelongsToCurrentThread()); 40 DCHECK(task_runner_->BelongsToCurrentThread());
52 base::AutoLock auto_lock(lock_);
53 DCHECK_EQ(kPrerolled, state_); 41 DCHECK_EQ(kPrerolled, state_);
54 state_ = kPlaying; 42 state_ = kPlaying;
43
44 ScheduleAllFrames();
45 AttemptRead();
46
47 // Changing from non-playing to playing state can trigger ended.
48 if (ShouldTransitionToEnded())
49 TransitionToEnded();
50
55 callback.Run(); 51 callback.Run();
56 } 52 }
57 53
58 void VideoRendererImpl::Pause(const base::Closure& callback) { 54 void VideoRendererImpl::Pause(const base::Closure& callback) {
59 DCHECK(task_runner_->BelongsToCurrentThread()); 55 DCHECK(task_runner_->BelongsToCurrentThread());
60 base::AutoLock auto_lock(lock_);
61 DCHECK(state_ != kUninitialized || state_ == kError); 56 DCHECK(state_ != kUninitialized || state_ == kError);
57
62 state_ = kPaused; 58 state_ = kPaused;
59 ResetScheduler();
60
63 callback.Run(); 61 callback.Run();
64 } 62 }
65 63
66 void VideoRendererImpl::Flush(const base::Closure& callback) { 64 void VideoRendererImpl::Flush(const base::Closure& callback) {
67 DCHECK(task_runner_->BelongsToCurrentThread()); 65 DCHECK(task_runner_->BelongsToCurrentThread());
68 base::AutoLock auto_lock(lock_);
69 DCHECK_EQ(state_, kPaused); 66 DCHECK_EQ(state_, kPaused);
67 DCHECK_EQ(scheduled_frames_.size(), 0u);
70 flush_cb_ = callback; 68 flush_cb_ = callback;
71 state_ = kFlushing; 69 state_ = kFlushing;
72 70
73 // This is necessary if the |video_frame_stream_| has already seen an end of 71 // This is necessary if the |video_frame_stream_| has already seen an end of
74 // stream and needs to drain it before flushing it. 72 // stream and needs to drain it before flushing it.
75 ready_frames_.clear(); 73 unscheduled_frames_.clear();
74
76 received_end_of_stream_ = false; 75 received_end_of_stream_ = false;
77 video_frame_stream_.Reset( 76 video_frame_stream_.Reset(
78 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone, 77 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone,
79 weak_factory_.GetWeakPtr())); 78 weak_factory_.GetWeakPtr()));
80 } 79 }
81 80
82 void VideoRendererImpl::Stop(const base::Closure& callback) { 81 void VideoRendererImpl::Stop(const base::Closure& callback) {
83 DCHECK(task_runner_->BelongsToCurrentThread()); 82 DCHECK(task_runner_->BelongsToCurrentThread());
84 base::AutoLock auto_lock(lock_);
85 if (state_ == kUninitialized || state_ == kStopped) { 83 if (state_ == kUninitialized || state_ == kStopped) {
86 callback.Run(); 84 callback.Run();
87 return; 85 return;
88 } 86 }
89 87
90 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing 88 // TODO(scherkus): Consider invalidating |weak_factory_| and replacing
91 // task-running guards that check |state_| with DCHECK(). 89 // task-running guards that check |state_| with DCHECK().
92 90
93 state_ = kStopped; 91 state_ = kStopped;
94 92
95 statistics_cb_.Reset(); 93 statistics_cb_.Reset();
96 max_time_cb_.Reset(); 94 max_time_cb_.Reset();
97 DoStopOrError_Locked();
98 95
99 // Clean up our thread if present. 96 unscheduled_frames_.clear();
100 base::PlatformThreadHandle thread_to_join = base::PlatformThreadHandle(); 97 scheduled_frames_.clear();
101 if (!thread_.is_null()) { 98 scheduler_->Reset();
102 // Signal the thread since it's possible to get stopped with the video
103 // thread waiting for a read to complete.
104 frame_available_.Signal();
105 std::swap(thread_, thread_to_join);
106 }
107
108 if (!thread_to_join.is_null()) {
109 base::AutoUnlock auto_unlock(lock_);
110 base::PlatformThread::Join(thread_to_join);
111 }
112 99
113 video_frame_stream_.Stop(callback); 100 video_frame_stream_.Stop(callback);
114 } 101 }
115 102
116 void VideoRendererImpl::SetPlaybackRate(float playback_rate) { 103 void VideoRendererImpl::SetPlaybackRate(float playback_rate) {
117 DCHECK(task_runner_->BelongsToCurrentThread()); 104 DCHECK(task_runner_->BelongsToCurrentThread());
118 base::AutoLock auto_lock(lock_); 105 if (playback_rate_ == playback_rate)
106 return;
107
119 playback_rate_ = playback_rate; 108 playback_rate_ = playback_rate;
109 ResetScheduler();
110
111 if (state_ == kPlaying)
112 ScheduleAllFrames();
120 } 113 }
121 114
122 void VideoRendererImpl::Preroll(base::TimeDelta time, 115 void VideoRendererImpl::Preroll(base::TimeDelta time,
123 const PipelineStatusCB& cb) { 116 const PipelineStatusCB& cb) {
124 DCHECK(task_runner_->BelongsToCurrentThread()); 117 DCHECK(task_runner_->BelongsToCurrentThread());
125 base::AutoLock auto_lock(lock_);
126 DCHECK(!cb.is_null()); 118 DCHECK(!cb.is_null());
127 DCHECK(preroll_cb_.is_null()); 119 DCHECK(preroll_cb_.is_null());
128 DCHECK(state_ == kFlushed || state_== kPaused) << "state_ " << state_; 120 DCHECK(state_ == kFlushed || state_ == kPaused) << "state_ " << state_;
129 121
130 if (state_ == kFlushed) { 122 if (state_ == kFlushed) {
131 DCHECK(time != kNoTimestamp()); 123 DCHECK(time != kNoTimestamp());
132 DCHECK(!pending_read_); 124 DCHECK(!pending_read_);
133 DCHECK(ready_frames_.empty()); 125 DCHECK(unscheduled_frames_.empty());
126 DCHECK(scheduled_frames_.empty());
134 } else { 127 } else {
128 // Prerolling without a timestamp is used for rebuffering.
135 DCHECK(time == kNoTimestamp()); 129 DCHECK(time == kNoTimestamp());
136 } 130 }
137 131
138 state_ = kPrerolling; 132 state_ = kPrerolling;
139 preroll_cb_ = cb; 133 preroll_cb_ = cb;
140 preroll_timestamp_ = time; 134 preroll_timestamp_ = time;
141 135
142 if (ShouldTransitionToPrerolled_Locked()) { 136 if (ShouldTransitionToPrerolled()) {
143 TransitionToPrerolled_Locked(); 137 TransitionToPrerolled();
144 return; 138 return;
145 } 139 }
146 140
147 AttemptRead_Locked(); 141 AttemptRead();
148 } 142 }
149 143
150 void VideoRendererImpl::Initialize(DemuxerStream* stream, 144 void VideoRendererImpl::Initialize(DemuxerStream* stream,
151 const PipelineStatusCB& init_cb, 145 const PipelineStatusCB& init_cb,
152 const StatisticsCB& statistics_cb, 146 const StatisticsCB& statistics_cb,
153 const TimeCB& max_time_cb, 147 const TimeCB& max_time_cb,
154 const base::Closure& ended_cb, 148 const base::Closure& ended_cb,
155 const PipelineStatusCB& error_cb, 149 const PipelineStatusCB& error_cb,
156 const TimeDeltaCB& get_time_cb, 150 const TimeDeltaCB& get_time_cb,
157 const TimeDeltaCB& get_duration_cb) { 151 const TimeDeltaCB& get_duration_cb) {
158 DCHECK(task_runner_->BelongsToCurrentThread()); 152 DCHECK(task_runner_->BelongsToCurrentThread());
159 base::AutoLock auto_lock(lock_);
160 DCHECK(stream); 153 DCHECK(stream);
161 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); 154 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
162 DCHECK(!init_cb.is_null()); 155 DCHECK(!init_cb.is_null());
163 DCHECK(!statistics_cb.is_null()); 156 DCHECK(!statistics_cb.is_null());
164 DCHECK(!max_time_cb.is_null()); 157 DCHECK(!max_time_cb.is_null());
165 DCHECK(!ended_cb.is_null()); 158 DCHECK(!ended_cb.is_null());
166 DCHECK(!get_time_cb.is_null()); 159 DCHECK(!get_time_cb.is_null());
167 DCHECK(!get_duration_cb.is_null()); 160 DCHECK(!get_duration_cb.is_null());
168 DCHECK_EQ(kUninitialized, state_); 161 DCHECK_EQ(kUninitialized, state_);
169 162
170 init_cb_ = init_cb; 163 init_cb_ = init_cb;
171 statistics_cb_ = statistics_cb; 164 statistics_cb_ = statistics_cb;
172 max_time_cb_ = max_time_cb; 165 max_time_cb_ = max_time_cb;
173 ended_cb_ = ended_cb; 166 ended_cb_ = ended_cb;
174 error_cb_ = error_cb; 167 error_cb_ = error_cb;
175 get_time_cb_ = get_time_cb; 168 get_time_cb_ = get_time_cb;
176 get_duration_cb_ = get_duration_cb; 169 get_duration_cb_ = get_duration_cb;
177 state_ = kInitializing; 170 state_ = kInitializing;
178 171
179 video_frame_stream_.Initialize( 172 video_frame_stream_.Initialize(
180 stream, 173 stream,
181 statistics_cb, 174 statistics_cb,
182 base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized, 175 base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized,
183 weak_factory_.GetWeakPtr())); 176 weak_factory_.GetWeakPtr()));
184 } 177 }
185 178
186 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { 179 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) {
187 DCHECK(task_runner_->BelongsToCurrentThread()); 180 DCHECK(task_runner_->BelongsToCurrentThread());
188 base::AutoLock auto_lock(lock_);
189 181
190 if (state_ == kStopped) 182 if (state_ == kStopped)
191 return; 183 return;
192 184
193 DCHECK_EQ(state_, kInitializing); 185 DCHECK_EQ(state_, kInitializing);
194 186
195 if (!success) { 187 if (!success) {
196 state_ = kUninitialized; 188 state_ = kUninitialized;
197 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); 189 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED);
198 return; 190 return;
199 } 191 }
200 192
201 // We're all good! Consider ourselves flushed. (ThreadMain() should never 193 // Consider ourselves flushed as no frames have been received yet.
202 // see us in the kUninitialized state).
203 // Since we had an initial Preroll(), we consider ourself flushed, because we
204 // have not populated any buffers yet.
205 state_ = kFlushed; 194 state_ = kFlushed;
206 195
207 // Create our video thread.
208 if (!base::PlatformThread::Create(0, this, &thread_)) {
209 NOTREACHED() << "Video thread creation failed";
210 state_ = kError;
211 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
212 return;
213 }
214
215 #if defined(OS_WIN)
216 // Bump up our priority so our sleeping is more accurate.
217 // TODO(scherkus): find out if this is necessary, but it seems to help.
218 ::SetThreadPriority(thread_.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL);
219 #endif // defined(OS_WIN)
220 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); 196 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
221 } 197 }
222 198
223 // PlatformThread::Delegate implementation.
224 void VideoRendererImpl::ThreadMain() {
225 base::PlatformThread::SetName("CrVideoRenderer");
226
227 // The number of milliseconds to idle when we do not have anything to do.
228 // Nothing special about the value, other than we're being more OS-friendly
229 // than sleeping for 1 millisecond.
230 //
231 // TODO(scherkus): switch to pure event-driven frame timing instead of this
232 // kIdleTimeDelta business http://crbug.com/106874
233 const base::TimeDelta kIdleTimeDelta =
234 base::TimeDelta::FromMilliseconds(10);
235
236 for (;;) {
237 base::AutoLock auto_lock(lock_);
238
239 // Thread exit condition.
240 if (state_ == kStopped)
241 return;
242
243 // Remain idle as long as we're not playing.
244 if (state_ != kPlaying || playback_rate_ == 0) {
245 UpdateStatsAndWait_Locked(kIdleTimeDelta);
246 continue;
247 }
248
249 // Remain idle until we have the next frame ready for rendering.
250 if (ready_frames_.empty()) {
251 if (received_end_of_stream_) {
252 state_ = kEnded;
253 ended_cb_.Run();
254
255 // No need to sleep here as we idle when |state_ != kPlaying|.
256 continue;
257 }
258
259 UpdateStatsAndWait_Locked(kIdleTimeDelta);
260 continue;
261 }
262
263 base::TimeDelta remaining_time =
264 CalculateSleepDuration(ready_frames_.front(), playback_rate_);
265
266 // Sleep up to a maximum of our idle time until we're within the time to
267 // render the next frame.
268 if (remaining_time.InMicroseconds() > 0) {
269 remaining_time = std::min(remaining_time, kIdleTimeDelta);
270 UpdateStatsAndWait_Locked(remaining_time);
271 continue;
272 }
273
274 // Deadline is defined as the midpoint between this frame and the next
275 // frame, using the delta between this frame and the previous frame as the
276 // assumption for frame duration.
277 //
278 // TODO(scherkus): An improvement over midpoint might be selecting the
279 // minimum and/or maximum between the midpoint and some constants. As a
280 // thought experiment, consider what would be better than the midpoint
281 // for both the 1fps case and 120fps case.
282 //
283 // TODO(scherkus): This can be vastly improved. Use a histogram to measure
284 // the accuracy of our frame timing code. http://crbug.com/149829
285 if (drop_frames_ && last_timestamp_ != kNoTimestamp()) {
286 base::TimeDelta now = get_time_cb_.Run();
287 base::TimeDelta deadline = ready_frames_.front()->timestamp() +
288 (ready_frames_.front()->timestamp() - last_timestamp_) / 2;
289
290 if (now > deadline) {
291 DropNextReadyFrame_Locked();
292 continue;
293 }
294 }
295
296 // Congratulations! You've made it past the video frame timing gauntlet.
297 //
298 // At this point enough time has passed that the next frame that ready for
299 // rendering.
300 PaintNextReadyFrame_Locked();
301 }
302 }
303
304 void VideoRendererImpl::PaintNextReadyFrame_Locked() {
305 lock_.AssertAcquired();
306
307 scoped_refptr<VideoFrame> next_frame = ready_frames_.front();
308 ready_frames_.pop_front();
309 frames_decoded_++;
310
311 last_timestamp_ = next_frame->timestamp();
312
313 paint_cb_.Run(next_frame);
314
315 task_runner_->PostTask(
316 FROM_HERE,
317 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
318 }
319
320 void VideoRendererImpl::DropNextReadyFrame_Locked() {
321 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped");
322
323 lock_.AssertAcquired();
324
325 last_timestamp_ = ready_frames_.front()->timestamp();
326 ready_frames_.pop_front();
327 frames_decoded_++;
328 frames_dropped_++;
329
330 task_runner_->PostTask(
331 FROM_HERE,
332 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr()));
333 }
334
335 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, 199 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
336 const scoped_refptr<VideoFrame>& frame) { 200 const scoped_refptr<VideoFrame>& frame) {
337 base::AutoLock auto_lock(lock_);
338 DCHECK_NE(state_, kUninitialized); 201 DCHECK_NE(state_, kUninitialized);
339 DCHECK_NE(state_, kFlushed); 202 DCHECK_NE(state_, kFlushed);
340 203
341 CHECK(pending_read_); 204 CHECK(pending_read_);
342 pending_read_ = false; 205 pending_read_ = false;
343 206
344 if (status == VideoFrameStream::DECODE_ERROR || 207 if (status == VideoFrameStream::DECODE_ERROR ||
345 status == VideoFrameStream::DECRYPT_ERROR) { 208 status == VideoFrameStream::DECRYPT_ERROR) {
346 DCHECK(!frame.get()); 209 DCHECK(!frame.get());
347 PipelineStatus error = PIPELINE_ERROR_DECODE; 210 PipelineStatus error = PIPELINE_ERROR_DECODE;
(...skipping 12 matching lines...) Expand all
360 // Already-queued VideoFrameStream ReadCB's can fire after various state 223 // Already-queued VideoFrameStream ReadCB's can fire after various state
361 // transitions have happened; in that case just drop those frames immediately. 224 // transitions have happened; in that case just drop those frames immediately.
362 if (state_ == kStopped || state_ == kError || state_ == kFlushing) 225 if (state_ == kStopped || state_ == kError || state_ == kFlushing)
363 return; 226 return;
364 227
365 if (!frame.get()) { 228 if (!frame.get()) {
366 // Abort preroll early for a NULL frame because we won't get more frames. 229 // Abort preroll early for a NULL frame because we won't get more frames.
367 // A new preroll will be requested after this one completes so there is no 230 // A new preroll will be requested after this one completes so there is no
368 // point trying to collect more frames. 231 // point trying to collect more frames.
369 if (state_ == kPrerolling) 232 if (state_ == kPrerolling)
370 TransitionToPrerolled_Locked(); 233 TransitionToPrerolled();
371 234
372 return; 235 return;
373 } 236 }
374 237
375 if (frame->end_of_stream()) { 238 if (frame->end_of_stream()) {
376 DCHECK(!received_end_of_stream_); 239 DCHECK(!received_end_of_stream_);
377 received_end_of_stream_ = true; 240 received_end_of_stream_ = true;
378 max_time_cb_.Run(get_duration_cb_.Run()); 241 max_time_cb_.Run(get_duration_cb_.Run());
379 242
380 if (state_ == kPrerolling) 243 if (state_ == kPrerolling)
381 TransitionToPrerolled_Locked(); 244 TransitionToPrerolled();
245
246 if (ShouldTransitionToEnded())
247 TransitionToEnded();
382 248
383 return; 249 return;
384 } 250 }
385 251
386 // Maintain the latest frame decoded so the correct frame is displayed after 252 // Maintain the latest frame decoded so the correct frame is displayed after
387 // prerolling has completed. 253 // prerolling has completed.
388 if (state_ == kPrerolling && preroll_timestamp_ != kNoTimestamp() && 254 if (state_ == kPrerolling && preroll_timestamp_ != kNoTimestamp() &&
389 frame->timestamp() <= preroll_timestamp_) { 255 frame->timestamp() <= preroll_timestamp_) {
390 ready_frames_.clear(); 256 unscheduled_frames_.clear();
391 } 257 }
392 258
393 AddReadyFrame_Locked(frame);
394
395 if (ShouldTransitionToPrerolled_Locked())
396 TransitionToPrerolled_Locked();
397
398 // Always request more decoded video if we have capacity. This serves two
399 // purposes:
400 // 1) Prerolling while paused
401 // 2) Keeps decoding going if video rendering thread starts falling behind
402 AttemptRead_Locked();
403 }
404
405 bool VideoRendererImpl::ShouldTransitionToPrerolled_Locked() {
406 return state_ == kPrerolling &&
407 (!video_frame_stream_.CanReadWithoutStalling() ||
408 ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames));
409 }
410
411 void VideoRendererImpl::AddReadyFrame_Locked(
412 const scoped_refptr<VideoFrame>& frame) {
413 lock_.AssertAcquired();
414 DCHECK(!frame->end_of_stream());
415
416 // Adjust the incoming frame if its rendering stop time is past the duration 259 // Adjust the incoming frame if its rendering stop time is past the duration
417 // of the video itself. This is typically the last frame of the video and 260 // of the video itself. This is typically the last frame of the video and
418 // occurs if the container specifies a duration that isn't a multiple of the 261 // occurs if the container specifies a duration that isn't a multiple of the
419 // frame rate. Another way for this to happen is for the container to state 262 // frame rate. Another way for this to happen is for the container to state
420 // a smaller duration than the largest packet timestamp. 263 // a smaller duration than the largest packet timestamp.
421 base::TimeDelta duration = get_duration_cb_.Run(); 264 base::TimeDelta duration = get_duration_cb_.Run();
422 if (frame->timestamp() > duration) { 265 if (frame->timestamp() > duration) {
423 frame->set_timestamp(duration); 266 frame->set_timestamp(duration);
424 } 267 }
425 268
426 ready_frames_.push_back(frame); 269 unscheduled_frames_.push_back(frame);
427 DCHECK_LE(ready_frames_.size(), 270 DCHECK_LE(video_frame_count(), limits::kMaxVideoFrames);
428 static_cast<size_t>(limits::kMaxVideoFrames));
429 271
430 max_time_cb_.Run(frame->timestamp()); 272 max_time_cb_.Run(frame->timestamp());
431 273
432 // Avoid needlessly waking up |thread_| unless playing. 274 if (ShouldTransitionToPrerolled())
275 TransitionToPrerolled();
276
433 if (state_ == kPlaying) 277 if (state_ == kPlaying)
434 frame_available_.Signal(); 278 ScheduleAllFrames();
279
280 if (ShouldTransitionToEnded())
281 TransitionToEnded();
282
283 // Always request more decoded video if we have capacity. This serves two
284 // purposes:
285 // 1) Prerolling while paused
286 // 2) Keeps decoding going if video rendering thread starts falling behind
287 AttemptRead();
288 }
289
290 bool VideoRendererImpl::ShouldTransitionToPrerolled() {
291 return state_ == kPrerolling &&
292 (!video_frame_stream_.CanReadWithoutStalling() ||
293 video_frame_count() >= limits::kMaxVideoFrames);
294 }
295
296 bool VideoRendererImpl::ShouldTransitionToEnded() {
297 return state_ == kPlaying && unscheduled_frames_.empty() &&
298 scheduled_frames_.empty() && received_end_of_stream_;
299 }
300
301 void VideoRendererImpl::TransitionToEnded() {
302 DCHECK(ShouldTransitionToEnded());
303 state_ = kEnded;
304 ended_cb_.Run();
435 } 305 }
436 306
437 void VideoRendererImpl::AttemptRead() { 307 void VideoRendererImpl::AttemptRead() {
438 base::AutoLock auto_lock(lock_);
439 AttemptRead_Locked();
440 }
441
442 void VideoRendererImpl::AttemptRead_Locked() {
443 DCHECK(task_runner_->BelongsToCurrentThread()); 308 DCHECK(task_runner_->BelongsToCurrentThread());
444 lock_.AssertAcquired();
445 309
446 if (pending_read_ || received_end_of_stream_ || 310 if (pending_read_ || received_end_of_stream_ ||
447 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { 311 video_frame_count() == limits::kMaxVideoFrames) {
448 return; 312 return;
449 } 313 }
450 314
451 switch (state_) { 315 switch (state_) {
452 case kPaused: 316 case kPaused:
453 case kPrerolling: 317 case kPrerolling:
454 case kPlaying: 318 case kPlaying:
455 pending_read_ = true; 319 pending_read_ = true;
456 video_frame_stream_.Read(base::Bind(&VideoRendererImpl::FrameReady, 320 video_frame_stream_.Read(base::Bind(&VideoRendererImpl::FrameReady,
457 weak_factory_.GetWeakPtr())); 321 weak_factory_.GetWeakPtr()));
458 return; 322 return;
459 323
460 case kUninitialized: 324 case kUninitialized:
461 case kInitializing: 325 case kInitializing:
462 case kPrerolled: 326 case kPrerolled:
463 case kFlushing: 327 case kFlushing:
464 case kFlushed: 328 case kFlushed:
465 case kEnded: 329 case kEnded:
466 case kStopped: 330 case kStopped:
467 case kError: 331 case kError:
468 return; 332 return;
469 } 333 }
470 } 334 }
471 335
472 void VideoRendererImpl::OnVideoFrameStreamResetDone() { 336 void VideoRendererImpl::OnVideoFrameStreamResetDone() {
473 base::AutoLock auto_lock(lock_);
474 if (state_ == kStopped) 337 if (state_ == kStopped)
475 return; 338 return;
476 339
477 DCHECK_EQ(kFlushing, state_); 340 DCHECK_EQ(kFlushing, state_);
478 DCHECK(!pending_read_); 341 DCHECK(!pending_read_);
479 DCHECK(ready_frames_.empty()); 342 DCHECK(unscheduled_frames_.empty());
343 DCHECK(scheduled_frames_.empty());
480 DCHECK(!received_end_of_stream_); 344 DCHECK(!received_end_of_stream_);
481 345
482 state_ = kFlushed; 346 state_ = kFlushed;
483 last_timestamp_ = kNoTimestamp();
484 base::ResetAndReturn(&flush_cb_).Run(); 347 base::ResetAndReturn(&flush_cb_).Run();
485 } 348 }
486 349
487 base::TimeDelta VideoRendererImpl::CalculateSleepDuration( 350 void VideoRendererImpl::TransitionToPrerolled() {
488 const scoped_refptr<VideoFrame>& next_frame,
489 float playback_rate) {
490 // Determine the current and next presentation timestamps.
491 base::TimeDelta now = get_time_cb_.Run();
492 base::TimeDelta next_pts = next_frame->timestamp();
493
494 // Scale our sleep based on the playback rate.
495 base::TimeDelta sleep = next_pts - now;
496 return base::TimeDelta::FromMicroseconds(
497 static_cast<int64>(sleep.InMicroseconds() / playback_rate));
498 }
499
500 void VideoRendererImpl::DoStopOrError_Locked() {
501 lock_.AssertAcquired();
502 last_timestamp_ = kNoTimestamp();
503 ready_frames_.clear();
504 }
505
506 void VideoRendererImpl::TransitionToPrerolled_Locked() {
507 lock_.AssertAcquired();
508 DCHECK_EQ(state_, kPrerolling); 351 DCHECK_EQ(state_, kPrerolling);
509 352
510 state_ = kPrerolled; 353 state_ = kPrerolled;
511 354
512 // Because we might remain in the prerolled state for an undetermined amount 355 // Because we might remain in the prerolled state for an undetermined amount
513 // of time (e.g., we seeked while paused), we'll paint the first prerolled 356 // of time (e.g., we seeked while paused), we'll paint the first prerolled
514 // frame. 357 // frame.
515 if (!ready_frames_.empty()) 358 if (!unscheduled_frames_.empty()) {
516 PaintNextReadyFrame_Locked(); 359 ScheduleFirstFrameForImmediateDisplay();
360 AttemptRead();
361 }
517 362
518 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); 363 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK);
519 } 364 }
520 365
521 void VideoRendererImpl::UpdateStatsAndWait_Locked( 366 void VideoRendererImpl::ResetScheduler() {
522 base::TimeDelta wait_duration) { 367 for (VideoFrameIDMap::iterator iter = scheduled_frames_.begin();
523 lock_.AssertAcquired(); 368 iter != scheduled_frames_.end();
524 DCHECK_GE(frames_decoded_, 0); 369 ++iter) {
525 DCHECK_LE(frames_dropped_, frames_decoded_); 370 unscheduled_frames_.push_back(iter->second);
371 }
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
372 scheduled_frames_.clear();
373 scheduler_->Reset();
374 }
526 375
527 if (frames_decoded_) { 376 void VideoRendererImpl::ScheduleAllFrames() {
528 PipelineStatistics statistics; 377 DCHECK_EQ(state_, kPlaying);
529 statistics.video_frames_decoded = frames_decoded_;
530 statistics.video_frames_dropped = frames_dropped_;
531 statistics_cb_.Run(statistics);
532 378
533 frames_decoded_ = 0; 379 if (playback_rate_ == 0)
534 frames_dropped_ = 0; 380 return;
381
382 base::TimeDelta current_media_time = get_time_cb_.Run();
383 base::TimeTicks current_wall_ticks = tick_clock_->NowTicks();
384
385 while (!unscheduled_frames_.empty()) {
386 // Negative deltas are permitted as it's up to the scheduler to decide
387 // what it wants to do with late frames.
388 base::TimeDelta delta =
389 unscheduled_frames_.front()->timestamp() - current_media_time;
390 base::TimeDelta scaled_delta = base::TimeDelta::FromMicroseconds(
391 static_cast<int64>(delta.InMicroseconds() / playback_rate_));
392 base::TimeTicks target_wall_ticks = current_wall_ticks + scaled_delta;
393
394 scoped_refptr<VideoFrame> frame = unscheduled_frames_.front();
395 int frame_id = next_scheduled_frame_id_++;
396 scheduled_frames_[frame_id] = frame;
397 unscheduled_frames_.pop_front();
398
399 scheduler_->ScheduleVideoFrame(
400 frame,
401 target_wall_ticks,
402 base::Bind(&VideoRendererImpl::OnVideoFrameFinished,
403 weak_factory_.GetWeakPtr(),
404 frame_id));
405 }
406 }
407
408 void VideoRendererImpl::ScheduleFirstFrameForImmediateDisplay() {
409 DCHECK_EQ(state_, kPrerolled);
410
411 scoped_refptr<VideoFrame> frame = unscheduled_frames_.front();
412 int frame_id = next_scheduled_frame_id_++;
413 scheduled_frames_[frame_id] = frame;
414 unscheduled_frames_.pop_front();
415
416 scheduler_->ScheduleVideoFrame(
417 frame,
418 tick_clock_->NowTicks(),
419 base::Bind(&VideoRendererImpl::OnVideoFrameFinished,
420 weak_factory_.GetWeakPtr(),
421 frame_id));
422 }
423
424 void VideoRendererImpl::OnVideoFrameFinished(
425 int frame_id,
426 const scoped_refptr<VideoFrame>& frame,
427 VideoFrameScheduler::Reason reason) {
428 VideoFrameIDMap::iterator iter = scheduled_frames_.find(frame_id);
429 if (iter == scheduled_frames_.end()) {
430 LOG(ERROR) << "Ignoring frame_id=" << frame_id
scherkus (not reviewing) 2014/04/25 02:04:47 note to self: remove
431 << ", timestamp=" << frame->timestamp().InMicroseconds();
432 return;
535 } 433 }
536 434
537 frame_available_.TimedWait(wait_duration); 435 CHECK_EQ(iter->second, frame);
436 scheduled_frames_.erase(iter);
437
438 switch (reason) {
439 case VideoFrameScheduler::DISPLAYED:
440 ++frames_decoded_;
441 break;
442
443 case VideoFrameScheduler::DROPPED:
444 ++frames_decoded_;
445 ++frames_dropped_;
446 break;
447 }
448
449 PipelineStatistics stats;
450 stats.video_frames_decoded = frames_decoded_;
451 stats.video_frames_dropped = frames_dropped_;
452 statistics_cb_.Run(stats);
453
454 if (ShouldTransitionToEnded()) {
455 TransitionToEnded();
456 return;
457 }
458
459 AttemptRead();
460 }
461
462 void VideoRendererImpl::SetTickClockForTesting(
463 scoped_ptr<base::TickClock> tick_clock) {
464 tick_clock_.swap(tick_clock);
538 } 465 }
539 466
540 } // namespace media 467 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698