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), |
37 buffering_state_(BUFFERING_HAVE_NOTHING), | 38 buffering_state_(BUFFERING_HAVE_NOTHING), |
38 paint_cb_(paint_cb), | 39 paint_cb_(paint_cb), |
39 last_timestamp_(kNoTimestamp()), | 40 last_timestamp_(kNoTimestamp()), |
40 frames_decoded_(0), | 41 frames_decoded_(0), |
41 frames_dropped_(0), | 42 frames_dropped_(0), |
42 weak_factory_(this) { | 43 weak_factory_(this) { |
43 DCHECK(!paint_cb_.is_null()); | 44 DCHECK(!paint_cb_.is_null()); |
44 } | 45 } |
45 | 46 |
46 VideoRendererImpl::~VideoRendererImpl() { | 47 VideoRendererImpl::~VideoRendererImpl() { |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 } | 99 } |
99 | 100 |
100 if (!thread_to_join.is_null()) { | 101 if (!thread_to_join.is_null()) { |
101 base::AutoUnlock auto_unlock(lock_); | 102 base::AutoUnlock auto_unlock(lock_); |
102 base::PlatformThread::Join(thread_to_join); | 103 base::PlatformThread::Join(thread_to_join); |
103 } | 104 } |
104 | 105 |
105 video_frame_stream_.Stop(callback); | 106 video_frame_stream_.Stop(callback); |
106 } | 107 } |
107 | 108 |
| 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 |
108 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { | 115 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { |
109 DCHECK(task_runner_->BelongsToCurrentThread()); | 116 DCHECK(task_runner_->BelongsToCurrentThread()); |
110 base::AutoLock auto_lock(lock_); | 117 base::AutoLock auto_lock(lock_); |
111 DCHECK_EQ(state_, kFlushed); | 118 DCHECK_EQ(state_, kFlushed); |
112 DCHECK(!pending_read_); | 119 DCHECK(!pending_read_); |
113 DCHECK(ready_frames_.empty()); | 120 DCHECK(ready_frames_.empty()); |
114 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | 121 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
115 | 122 |
116 state_ = kPlaying; | 123 state_ = kPlaying; |
117 start_timestamp_ = timestamp; | 124 start_timestamp_ = timestamp; |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
207 base::TimeDelta::FromMilliseconds(10); | 214 base::TimeDelta::FromMilliseconds(10); |
208 | 215 |
209 for (;;) { | 216 for (;;) { |
210 base::AutoLock auto_lock(lock_); | 217 base::AutoLock auto_lock(lock_); |
211 | 218 |
212 // Thread exit condition. | 219 // Thread exit condition. |
213 if (state_ == kStopped) | 220 if (state_ == kStopped) |
214 return; | 221 return; |
215 | 222 |
216 // Remain idle as long as we're not playing. | 223 // Remain idle as long as we're not playing. |
217 if (state_ != kPlaying || buffering_state_ != BUFFERING_HAVE_ENOUGH) { | 224 if (state_ != kPlaying || playback_rate_ == 0 || |
| 225 buffering_state_ != BUFFERING_HAVE_ENOUGH) { |
218 UpdateStatsAndWait_Locked(kIdleTimeDelta); | 226 UpdateStatsAndWait_Locked(kIdleTimeDelta); |
219 continue; | 227 continue; |
220 } | 228 } |
221 | 229 |
222 // Remain idle until we have the next frame ready for rendering. | 230 // Remain idle until we have the next frame ready for rendering. |
223 if (ready_frames_.empty()) { | 231 if (ready_frames_.empty()) { |
224 if (received_end_of_stream_ && !rendered_end_of_stream_) { | 232 if (received_end_of_stream_ && !rendered_end_of_stream_) { |
225 rendered_end_of_stream_ = true; | 233 rendered_end_of_stream_ = true; |
226 ended_cb_.Run(); | 234 ended_cb_.Run(); |
227 } | 235 } |
228 | 236 |
229 UpdateStatsAndWait_Locked(kIdleTimeDelta); | 237 UpdateStatsAndWait_Locked(kIdleTimeDelta); |
230 continue; | 238 continue; |
231 } | 239 } |
232 | 240 |
233 base::TimeDelta now = get_time_cb_.Run(); | 241 base::TimeDelta remaining_time = |
234 base::TimeDelta target_timestamp = ready_frames_.front()->timestamp(); | 242 CalculateSleepDuration(ready_frames_.front(), playback_rate_); |
235 base::TimeDelta earliest_paint_timestamp; | 243 |
236 base::TimeDelta latest_paint_timestamp; | 244 // Sleep up to a maximum of our idle time until we're within the time to |
| 245 // render the next frame. |
| 246 if (remaining_time.InMicroseconds() > 0) { |
| 247 remaining_time = std::min(remaining_time, kIdleTimeDelta); |
| 248 UpdateStatsAndWait_Locked(remaining_time); |
| 249 continue; |
| 250 } |
237 | 251 |
238 // Deadline is defined as the midpoint between this frame and the next | 252 // Deadline is defined as the midpoint between this frame and the next |
239 // frame, using the delta between this frame and the previous frame as the | 253 // frame, using the delta between this frame and the previous frame as the |
240 // assumption for frame duration. | 254 // assumption for frame duration. |
241 // | 255 // |
242 // TODO(scherkus): An improvement over midpoint might be selecting the | 256 // TODO(scherkus): An improvement over midpoint might be selecting the |
243 // minimum and/or maximum between the midpoint and some constants. As a | 257 // minimum and/or maximum between the midpoint and some constants. As a |
244 // thought experiment, consider what would be better than the midpoint | 258 // thought experiment, consider what would be better than the midpoint |
245 // for both the 1fps case and 120fps case. | 259 // for both the 1fps case and 120fps case. |
246 // | 260 // |
247 // TODO(scherkus): This can be vastly improved. Use a histogram to measure | 261 // TODO(scherkus): This can be vastly improved. Use a histogram to measure |
248 // the accuracy of our frame timing code. http://crbug.com/149829 | 262 // the accuracy of our frame timing code. http://crbug.com/149829 |
249 if (last_timestamp_ == kNoTimestamp()) { | 263 if (drop_frames_ && last_timestamp_ != kNoTimestamp()) { |
250 earliest_paint_timestamp = target_timestamp; | 264 base::TimeDelta now = get_time_cb_.Run(); |
251 latest_paint_timestamp = base::TimeDelta::Max(); | 265 base::TimeDelta deadline = ready_frames_.front()->timestamp() + |
252 } else { | 266 (ready_frames_.front()->timestamp() - last_timestamp_) / 2; |
253 base::TimeDelta duration = target_timestamp - last_timestamp_; | |
254 earliest_paint_timestamp = target_timestamp - duration / 2; | |
255 latest_paint_timestamp = target_timestamp + duration / 2; | |
256 } | |
257 | 267 |
258 // Remain idle until we've reached our target paint window. | 268 if (now > deadline) { |
259 if (now < earliest_paint_timestamp) { | 269 DropNextReadyFrame_Locked(); |
260 UpdateStatsAndWait_Locked(kIdleTimeDelta); | 270 continue; |
261 continue; | 271 } |
262 } | |
263 | |
264 if (now > latest_paint_timestamp && drop_frames_) { | |
265 DropNextReadyFrame_Locked(); | |
266 continue; | |
267 } | 272 } |
268 | 273 |
269 // Congratulations! You've made it past the video frame timing gauntlet. | 274 // Congratulations! You've made it past the video frame timing gauntlet. |
270 // | 275 // |
271 // At this point enough time has passed that the next frame that ready for | 276 // At this point enough time has passed that the next frame that ready for |
272 // rendering. | 277 // rendering. |
273 PaintNextReadyFrame_Locked(); | 278 PaintNextReadyFrame_Locked(); |
274 } | 279 } |
275 } | 280 } |
276 | 281 |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
465 DCHECK(ready_frames_.empty()); | 470 DCHECK(ready_frames_.empty()); |
466 DCHECK(!received_end_of_stream_); | 471 DCHECK(!received_end_of_stream_); |
467 DCHECK(!rendered_end_of_stream_); | 472 DCHECK(!rendered_end_of_stream_); |
468 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | 473 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
469 | 474 |
470 state_ = kFlushed; | 475 state_ = kFlushed; |
471 last_timestamp_ = kNoTimestamp(); | 476 last_timestamp_ = kNoTimestamp(); |
472 base::ResetAndReturn(&flush_cb_).Run(); | 477 base::ResetAndReturn(&flush_cb_).Run(); |
473 } | 478 } |
474 | 479 |
| 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 |
475 void VideoRendererImpl::DoStopOrError_Locked() { | 493 void VideoRendererImpl::DoStopOrError_Locked() { |
476 lock_.AssertAcquired(); | 494 lock_.AssertAcquired(); |
477 last_timestamp_ = kNoTimestamp(); | 495 last_timestamp_ = kNoTimestamp(); |
478 ready_frames_.clear(); | 496 ready_frames_.clear(); |
479 } | 497 } |
480 | 498 |
481 void VideoRendererImpl::UpdateStatsAndWait_Locked( | 499 void VideoRendererImpl::UpdateStatsAndWait_Locked( |
482 base::TimeDelta wait_duration) { | 500 base::TimeDelta wait_duration) { |
483 lock_.AssertAcquired(); | 501 lock_.AssertAcquired(); |
484 DCHECK_GE(frames_decoded_, 0); | 502 DCHECK_GE(frames_decoded_, 0); |
485 DCHECK_LE(frames_dropped_, frames_decoded_); | 503 DCHECK_LE(frames_dropped_, frames_decoded_); |
486 | 504 |
487 if (frames_decoded_) { | 505 if (frames_decoded_) { |
488 PipelineStatistics statistics; | 506 PipelineStatistics statistics; |
489 statistics.video_frames_decoded = frames_decoded_; | 507 statistics.video_frames_decoded = frames_decoded_; |
490 statistics.video_frames_dropped = frames_dropped_; | 508 statistics.video_frames_dropped = frames_dropped_; |
491 statistics_cb_.Run(statistics); | 509 statistics_cb_.Run(statistics); |
492 | 510 |
493 frames_decoded_ = 0; | 511 frames_decoded_ = 0; |
494 frames_dropped_ = 0; | 512 frames_dropped_ = 0; |
495 } | 513 } |
496 | 514 |
497 frame_available_.TimedWait(wait_duration); | 515 frame_available_.TimedWait(wait_duration); |
498 } | 516 } |
499 | 517 |
500 } // namespace media | 518 } // namespace media |
OLD | NEW |