OLD | NEW |
---|---|
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" | 8 #include "base/callback.h" |
9 #include "base/callback_helpers.h" | 9 #include "base/callback_helpers.h" |
10 #include "base/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
(...skipping 16 matching lines...) Expand all Loading... | |
27 : task_runner_(task_runner), | 27 : task_runner_(task_runner), |
28 video_frame_stream_(task_runner, decoders.Pass(), set_decryptor_ready_cb), | 28 video_frame_stream_(task_runner, decoders.Pass(), set_decryptor_ready_cb), |
29 low_delay_(false), | 29 low_delay_(false), |
30 received_end_of_stream_(false), | 30 received_end_of_stream_(false), |
31 rendered_end_of_stream_(false), | 31 rendered_end_of_stream_(false), |
32 frame_available_(&lock_), | 32 frame_available_(&lock_), |
33 state_(kUninitialized), | 33 state_(kUninitialized), |
34 thread_(), | 34 thread_(), |
35 pending_read_(false), | 35 pending_read_(false), |
36 drop_frames_(drop_frames), | 36 drop_frames_(drop_frames), |
37 playback_rate_(0), | |
38 buffering_state_(BUFFERING_HAVE_NOTHING), | 37 buffering_state_(BUFFERING_HAVE_NOTHING), |
39 paint_cb_(paint_cb), | 38 paint_cb_(paint_cb), |
40 last_timestamp_(kNoTimestamp()), | 39 last_timestamp_(kNoTimestamp()), |
41 frames_decoded_(0), | 40 frames_decoded_(0), |
42 frames_dropped_(0), | 41 frames_dropped_(0), |
43 weak_factory_(this) { | 42 weak_factory_(this) { |
44 DCHECK(!paint_cb_.is_null()); | 43 DCHECK(!paint_cb_.is_null()); |
45 } | 44 } |
46 | 45 |
47 VideoRendererImpl::~VideoRendererImpl() { | 46 VideoRendererImpl::~VideoRendererImpl() { |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
99 } | 98 } |
100 | 99 |
101 if (!thread_to_join.is_null()) { | 100 if (!thread_to_join.is_null()) { |
102 base::AutoUnlock auto_unlock(lock_); | 101 base::AutoUnlock auto_unlock(lock_); |
103 base::PlatformThread::Join(thread_to_join); | 102 base::PlatformThread::Join(thread_to_join); |
104 } | 103 } |
105 | 104 |
106 video_frame_stream_.Stop(callback); | 105 video_frame_stream_.Stop(callback); |
107 } | 106 } |
108 | 107 |
109 void VideoRendererImpl::SetPlaybackRate(float playback_rate) { | |
110 DCHECK(task_runner_->BelongsToCurrentThread()); | |
111 base::AutoLock auto_lock(lock_); | |
112 playback_rate_ = playback_rate; | |
113 } | |
114 | |
115 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { | 108 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { |
116 DCHECK(task_runner_->BelongsToCurrentThread()); | 109 DCHECK(task_runner_->BelongsToCurrentThread()); |
117 base::AutoLock auto_lock(lock_); | 110 base::AutoLock auto_lock(lock_); |
118 DCHECK_EQ(state_, kFlushed); | 111 DCHECK_EQ(state_, kFlushed); |
119 DCHECK(!pending_read_); | 112 DCHECK(!pending_read_); |
120 DCHECK(ready_frames_.empty()); | 113 DCHECK(ready_frames_.empty()); |
121 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | 114 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
122 | 115 |
123 state_ = kPlaying; | 116 state_ = kPlaying; |
124 start_timestamp_ = timestamp; | 117 start_timestamp_ = timestamp; |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
214 base::TimeDelta::FromMilliseconds(10); | 207 base::TimeDelta::FromMilliseconds(10); |
215 | 208 |
216 for (;;) { | 209 for (;;) { |
217 base::AutoLock auto_lock(lock_); | 210 base::AutoLock auto_lock(lock_); |
218 | 211 |
219 // Thread exit condition. | 212 // Thread exit condition. |
220 if (state_ == kStopped) | 213 if (state_ == kStopped) |
221 return; | 214 return; |
222 | 215 |
223 // Remain idle as long as we're not playing. | 216 // Remain idle as long as we're not playing. |
224 if (state_ != kPlaying || playback_rate_ == 0 || | 217 if (state_ != kPlaying || buffering_state_ != BUFFERING_HAVE_ENOUGH) { |
225 buffering_state_ != BUFFERING_HAVE_ENOUGH) { | |
226 UpdateStatsAndWait_Locked(kIdleTimeDelta); | 218 UpdateStatsAndWait_Locked(kIdleTimeDelta); |
227 continue; | 219 continue; |
228 } | 220 } |
229 | 221 |
230 // Remain idle until we have the next frame ready for rendering. | 222 // Remain idle until we have the next frame ready for rendering. |
231 if (ready_frames_.empty()) { | 223 if (ready_frames_.empty()) { |
232 if (received_end_of_stream_ && !rendered_end_of_stream_) { | 224 if (received_end_of_stream_ && !rendered_end_of_stream_) { |
233 rendered_end_of_stream_ = true; | 225 rendered_end_of_stream_ = true; |
234 ended_cb_.Run(); | 226 ended_cb_.Run(); |
235 } | 227 } |
236 | 228 |
237 UpdateStatsAndWait_Locked(kIdleTimeDelta); | 229 UpdateStatsAndWait_Locked(kIdleTimeDelta); |
238 continue; | 230 continue; |
239 } | 231 } |
240 | 232 |
241 base::TimeDelta remaining_time = | 233 // Determine the current and next presentation timestamps. |
242 CalculateSleepDuration(ready_frames_.front(), playback_rate_); | 234 base::TimeDelta now = get_time_cb_.Run(); |
235 base::TimeDelta remaining_time = ready_frames_.front()->timestamp() - now; | |
243 | 236 |
244 // Sleep up to a maximum of our idle time until we're within the time to | 237 // Sleep up to a maximum of our idle time until we're within the time to |
245 // render the next frame. | 238 // render the next frame. |
246 if (remaining_time.InMicroseconds() > 0) { | 239 if (remaining_time.InMicroseconds() > 0) { |
247 remaining_time = std::min(remaining_time, kIdleTimeDelta); | 240 remaining_time = std::min(remaining_time, kIdleTimeDelta); |
xhwang
2014/07/10 23:54:05
Does it make a big difference if you always use kI
scherkus (not reviewing)
2014/07/11 00:01:03
I thought about it and we can certainly play aroun
| |
248 UpdateStatsAndWait_Locked(remaining_time); | 241 UpdateStatsAndWait_Locked(remaining_time); |
249 continue; | 242 continue; |
250 } | 243 } |
251 | 244 |
252 // Deadline is defined as the midpoint between this frame and the next | 245 // Deadline is defined as the midpoint between this frame and the next |
253 // frame, using the delta between this frame and the previous frame as the | 246 // frame, using the delta between this frame and the previous frame as the |
254 // assumption for frame duration. | 247 // assumption for frame duration. |
255 // | 248 // |
256 // TODO(scherkus): An improvement over midpoint might be selecting the | 249 // TODO(scherkus): An improvement over midpoint might be selecting the |
257 // minimum and/or maximum between the midpoint and some constants. As a | 250 // minimum and/or maximum between the midpoint and some constants. As a |
258 // thought experiment, consider what would be better than the midpoint | 251 // thought experiment, consider what would be better than the midpoint |
259 // for both the 1fps case and 120fps case. | 252 // for both the 1fps case and 120fps case. |
260 // | 253 // |
261 // TODO(scherkus): This can be vastly improved. Use a histogram to measure | 254 // TODO(scherkus): This can be vastly improved. Use a histogram to measure |
262 // the accuracy of our frame timing code. http://crbug.com/149829 | 255 // the accuracy of our frame timing code. http://crbug.com/149829 |
263 if (drop_frames_ && last_timestamp_ != kNoTimestamp()) { | 256 if (drop_frames_ && last_timestamp_ != kNoTimestamp()) { |
264 base::TimeDelta now = get_time_cb_.Run(); | |
265 base::TimeDelta deadline = ready_frames_.front()->timestamp() + | 257 base::TimeDelta deadline = ready_frames_.front()->timestamp() + |
266 (ready_frames_.front()->timestamp() - last_timestamp_) / 2; | 258 (ready_frames_.front()->timestamp() - last_timestamp_) / 2; |
267 | 259 |
268 if (now > deadline) { | 260 if (now > deadline) { |
269 DropNextReadyFrame_Locked(); | 261 DropNextReadyFrame_Locked(); |
270 continue; | 262 continue; |
271 } | 263 } |
272 } | 264 } |
273 | 265 |
274 // Congratulations! You've made it past the video frame timing gauntlet. | 266 // Congratulations! You've made it past the video frame timing gauntlet. |
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
470 DCHECK(ready_frames_.empty()); | 462 DCHECK(ready_frames_.empty()); |
471 DCHECK(!received_end_of_stream_); | 463 DCHECK(!received_end_of_stream_); |
472 DCHECK(!rendered_end_of_stream_); | 464 DCHECK(!rendered_end_of_stream_); |
473 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | 465 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
474 | 466 |
475 state_ = kFlushed; | 467 state_ = kFlushed; |
476 last_timestamp_ = kNoTimestamp(); | 468 last_timestamp_ = kNoTimestamp(); |
477 base::ResetAndReturn(&flush_cb_).Run(); | 469 base::ResetAndReturn(&flush_cb_).Run(); |
478 } | 470 } |
479 | 471 |
480 base::TimeDelta VideoRendererImpl::CalculateSleepDuration( | |
481 const scoped_refptr<VideoFrame>& next_frame, | |
482 float playback_rate) { | |
483 // Determine the current and next presentation timestamps. | |
484 base::TimeDelta now = get_time_cb_.Run(); | |
485 base::TimeDelta next_pts = next_frame->timestamp(); | |
486 | |
487 // Scale our sleep based on the playback rate. | |
488 base::TimeDelta sleep = next_pts - now; | |
489 return base::TimeDelta::FromMicroseconds( | |
490 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); | |
491 } | |
492 | |
493 void VideoRendererImpl::DoStopOrError_Locked() { | 472 void VideoRendererImpl::DoStopOrError_Locked() { |
494 lock_.AssertAcquired(); | 473 lock_.AssertAcquired(); |
495 last_timestamp_ = kNoTimestamp(); | 474 last_timestamp_ = kNoTimestamp(); |
496 ready_frames_.clear(); | 475 ready_frames_.clear(); |
497 } | 476 } |
498 | 477 |
499 void VideoRendererImpl::UpdateStatsAndWait_Locked( | 478 void VideoRendererImpl::UpdateStatsAndWait_Locked( |
500 base::TimeDelta wait_duration) { | 479 base::TimeDelta wait_duration) { |
501 lock_.AssertAcquired(); | 480 lock_.AssertAcquired(); |
502 DCHECK_GE(frames_decoded_, 0); | 481 DCHECK_GE(frames_decoded_, 0); |
503 DCHECK_LE(frames_dropped_, frames_decoded_); | 482 DCHECK_LE(frames_dropped_, frames_decoded_); |
504 | 483 |
505 if (frames_decoded_) { | 484 if (frames_decoded_) { |
506 PipelineStatistics statistics; | 485 PipelineStatistics statistics; |
507 statistics.video_frames_decoded = frames_decoded_; | 486 statistics.video_frames_decoded = frames_decoded_; |
508 statistics.video_frames_dropped = frames_dropped_; | 487 statistics.video_frames_dropped = frames_dropped_; |
509 statistics_cb_.Run(statistics); | 488 statistics_cb_.Run(statistics); |
510 | 489 |
511 frames_decoded_ = 0; | 490 frames_decoded_ = 0; |
512 frames_dropped_ = 0; | 491 frames_dropped_ = 0; |
513 } | 492 } |
514 | 493 |
515 frame_available_.TimedWait(wait_duration); | 494 frame_available_.TimedWait(wait_duration); |
516 } | 495 } |
517 | 496 |
518 } // namespace media | 497 } // namespace media |
OLD | NEW |