| 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 |