Chromium Code Reviews| 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 base::TimeDelta now = get_time_cb_.Run(); |
| 242 CalculateSleepDuration(ready_frames_.front(), playback_rate_); | 234 base::TimeDelta earliest_paint_timestamp; |
| 243 | 235 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 } | |
| 251 | 236 |
| 252 // Deadline is defined as the midpoint between this frame and the next | 237 // 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 | 238 // frame, using the delta between this frame and the previous frame as the |
| 254 // assumption for frame duration. | 239 // assumption for frame duration. |
| 255 // | 240 // |
| 256 // TODO(scherkus): An improvement over midpoint might be selecting the | 241 // TODO(scherkus): An improvement over midpoint might be selecting the |
| 257 // minimum and/or maximum between the midpoint and some constants. As a | 242 // minimum and/or maximum between the midpoint and some constants. As a |
| 258 // thought experiment, consider what would be better than the midpoint | 243 // thought experiment, consider what would be better than the midpoint |
| 259 // for both the 1fps case and 120fps case. | 244 // for both the 1fps case and 120fps case. |
| 260 // | 245 // |
| 261 // TODO(scherkus): This can be vastly improved. Use a histogram to measure | 246 // TODO(scherkus): This can be vastly improved. Use a histogram to measure |
| 262 // the accuracy of our frame timing code. http://crbug.com/149829 | 247 // the accuracy of our frame timing code. http://crbug.com/149829 |
| 263 if (drop_frames_ && last_timestamp_ != kNoTimestamp()) { | 248 if (last_timestamp_ == kNoTimestamp()) { |
| 264 base::TimeDelta now = get_time_cb_.Run(); | 249 earliest_paint_timestamp = ready_frames_.front()->timestamp(); |
| 265 base::TimeDelta deadline = ready_frames_.front()->timestamp() + | 250 latest_paint_timestamp = base::TimeDelta::Max(); |
| 266 (ready_frames_.front()->timestamp() - last_timestamp_) / 2; | 251 } else { |
| 252 base::TimeDelta duration = | |
| 253 ready_frames_.front()->timestamp() - last_timestamp_; | |
|
xhwang
2014/07/11 20:49:49
nit: ready_frames_.front()->timestamp() is used 4
scherkus (not reviewing)
2014/07/11 20:54:02
Done.
| |
| 254 earliest_paint_timestamp = | |
| 255 ready_frames_.front()->timestamp() - duration / 2; | |
| 256 latest_paint_timestamp = | |
| 257 ready_frames_.front()->timestamp() + duration / 2; | |
| 258 } | |
| 267 | 259 |
| 268 if (now > deadline) { | 260 // Remain idle until we've reached our target paint window. |
| 269 DropNextReadyFrame_Locked(); | 261 if (now < earliest_paint_timestamp) { |
| 270 continue; | 262 UpdateStatsAndWait_Locked(kIdleTimeDelta); |
| 271 } | 263 continue; |
| 264 } | |
| 265 | |
| 266 if (now > latest_paint_timestamp && drop_frames_) { | |
| 267 DropNextReadyFrame_Locked(); | |
| 268 continue; | |
| 272 } | 269 } |
| 273 | 270 |
| 274 // Congratulations! You've made it past the video frame timing gauntlet. | 271 // Congratulations! You've made it past the video frame timing gauntlet. |
| 275 // | 272 // |
| 276 // At this point enough time has passed that the next frame that ready for | 273 // At this point enough time has passed that the next frame that ready for |
| 277 // rendering. | 274 // rendering. |
| 278 PaintNextReadyFrame_Locked(); | 275 PaintNextReadyFrame_Locked(); |
| 279 } | 276 } |
| 280 } | 277 } |
| 281 | 278 |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 470 DCHECK(ready_frames_.empty()); | 467 DCHECK(ready_frames_.empty()); |
| 471 DCHECK(!received_end_of_stream_); | 468 DCHECK(!received_end_of_stream_); |
| 472 DCHECK(!rendered_end_of_stream_); | 469 DCHECK(!rendered_end_of_stream_); |
| 473 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | 470 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
| 474 | 471 |
| 475 state_ = kFlushed; | 472 state_ = kFlushed; |
| 476 last_timestamp_ = kNoTimestamp(); | 473 last_timestamp_ = kNoTimestamp(); |
| 477 base::ResetAndReturn(&flush_cb_).Run(); | 474 base::ResetAndReturn(&flush_cb_).Run(); |
| 478 } | 475 } |
| 479 | 476 |
| 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() { | 477 void VideoRendererImpl::DoStopOrError_Locked() { |
| 494 lock_.AssertAcquired(); | 478 lock_.AssertAcquired(); |
| 495 last_timestamp_ = kNoTimestamp(); | 479 last_timestamp_ = kNoTimestamp(); |
| 496 ready_frames_.clear(); | 480 ready_frames_.clear(); |
| 497 } | 481 } |
| 498 | 482 |
| 499 void VideoRendererImpl::UpdateStatsAndWait_Locked( | 483 void VideoRendererImpl::UpdateStatsAndWait_Locked( |
| 500 base::TimeDelta wait_duration) { | 484 base::TimeDelta wait_duration) { |
| 501 lock_.AssertAcquired(); | 485 lock_.AssertAcquired(); |
| 502 DCHECK_GE(frames_decoded_, 0); | 486 DCHECK_GE(frames_decoded_, 0); |
| 503 DCHECK_LE(frames_dropped_, frames_decoded_); | 487 DCHECK_LE(frames_dropped_, frames_decoded_); |
| 504 | 488 |
| 505 if (frames_decoded_) { | 489 if (frames_decoded_) { |
| 506 PipelineStatistics statistics; | 490 PipelineStatistics statistics; |
| 507 statistics.video_frames_decoded = frames_decoded_; | 491 statistics.video_frames_decoded = frames_decoded_; |
| 508 statistics.video_frames_dropped = frames_dropped_; | 492 statistics.video_frames_dropped = frames_dropped_; |
| 509 statistics_cb_.Run(statistics); | 493 statistics_cb_.Run(statistics); |
| 510 | 494 |
| 511 frames_decoded_ = 0; | 495 frames_decoded_ = 0; |
| 512 frames_dropped_ = 0; | 496 frames_dropped_ = 0; |
| 513 } | 497 } |
| 514 | 498 |
| 515 frame_available_.TimedWait(wait_duration); | 499 frame_available_.TimedWait(wait_duration); |
| 516 } | 500 } |
| 517 | 501 |
| 518 } // namespace media | 502 } // namespace media |
| OLD | NEW |