Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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_base.h" | 5 #include "media/filters/video_renderer_base.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/message_loop.h" | 10 #include "base/message_loop.h" |
| 11 #include "base/threading/platform_thread.h" | 11 #include "base/threading/platform_thread.h" |
| 12 #include "media/base/buffers.h" | 12 #include "media/base/buffers.h" |
| 13 #include "media/base/limits.h" | 13 #include "media/base/limits.h" |
| 14 #include "media/base/pipeline.h" | 14 #include "media/base/pipeline.h" |
| 15 #include "media/base/video_frame.h" | 15 #include "media/base/video_frame.h" |
| 16 #include "media/filters/decrypting_demuxer_stream.h" | 16 #include "media/filters/decrypting_demuxer_stream.h" |
| 17 #include "media/filters/video_decoder_selector.h" | 17 #include "media/filters/video_decoder_selector.h" |
| 18 | 18 |
| 19 namespace media { | 19 namespace media { |
| 20 | 20 |
| 21 base::TimeDelta VideoRendererBase::kMaxLastFrameDuration() { | 21 base::TimeDelta VideoRendererBase::kMaxLastFrameDuration() { |
| 22 return base::TimeDelta::FromMilliseconds(250); | 22 return base::TimeDelta::FromMilliseconds(250); |
| 23 } | 23 } |
| 24 | 24 |
| 25 VideoRendererBase::VideoRendererBase( | 25 VideoRendererBase::VideoRendererBase( |
| 26 const scoped_refptr<base::MessageLoopProxy>& message_loop, | 26 const scoped_refptr<base::MessageLoopProxy>& message_loop, |
| 27 const SetDecryptorReadyCB& set_decryptor_ready_cb, | 27 const SetDecryptorReadyCB& set_decryptor_ready_cb, |
| 28 const base::Closure& paint_cb, | 28 const PaintCB& paint_cb, |
| 29 const SetOpaqueCB& set_opaque_cb, | 29 const SetOpaqueCB& set_opaque_cb, |
| 30 bool drop_frames) | 30 bool drop_frames) |
| 31 : message_loop_(message_loop), | 31 : message_loop_(message_loop), |
| 32 set_decryptor_ready_cb_(set_decryptor_ready_cb), | 32 set_decryptor_ready_cb_(set_decryptor_ready_cb), |
| 33 frame_available_(&lock_), | 33 frame_available_(&lock_), |
| 34 state_(kUninitialized), | 34 state_(kUninitialized), |
| 35 thread_(base::kNullThreadHandle), | 35 thread_(base::kNullThreadHandle), |
| 36 pending_read_(false), | 36 pending_read_(false), |
| 37 pending_paint_(false), | |
| 38 pending_paint_with_last_available_(false), | |
| 39 drop_frames_(drop_frames), | 37 drop_frames_(drop_frames), |
| 40 playback_rate_(0), | 38 playback_rate_(0), |
| 41 paint_cb_(paint_cb), | 39 paint_cb_(paint_cb), |
| 42 set_opaque_cb_(set_opaque_cb) { | 40 set_opaque_cb_(set_opaque_cb), |
| 41 last_timestamp_(kNoTimestamp()) { | |
| 43 DCHECK(!paint_cb_.is_null()); | 42 DCHECK(!paint_cb_.is_null()); |
| 44 } | 43 } |
| 45 | 44 |
| 46 void VideoRendererBase::Play(const base::Closure& callback) { | 45 void VideoRendererBase::Play(const base::Closure& callback) { |
| 47 DCHECK(message_loop_->BelongsToCurrentThread()); | 46 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 48 base::AutoLock auto_lock(lock_); | 47 base::AutoLock auto_lock(lock_); |
| 49 DCHECK_EQ(kPrerolled, state_); | 48 DCHECK_EQ(kPrerolled, state_); |
| 50 state_ = kPlaying; | 49 state_ = kPlaying; |
| 51 callback.Run(); | 50 callback.Run(); |
| 52 } | 51 } |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 76 } | 75 } |
| 77 | 76 |
| 78 void VideoRendererBase::ResetDecoder() { | 77 void VideoRendererBase::ResetDecoder() { |
| 79 DCHECK(message_loop_->BelongsToCurrentThread()); | 78 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 80 base::AutoLock auto_lock(lock_); | 79 base::AutoLock auto_lock(lock_); |
| 81 decoder_->Reset(base::Bind(&VideoRendererBase::OnDecoderResetDone, this)); | 80 decoder_->Reset(base::Bind(&VideoRendererBase::OnDecoderResetDone, this)); |
| 82 } | 81 } |
| 83 | 82 |
| 84 void VideoRendererBase::Stop(const base::Closure& callback) { | 83 void VideoRendererBase::Stop(const base::Closure& callback) { |
| 85 DCHECK(message_loop_->BelongsToCurrentThread()); | 84 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 85 base::AutoLock auto_lock(lock_); | |
| 86 if (state_ == kUninitialized || state_ == kStopped) { | 86 if (state_ == kUninitialized || state_ == kStopped) { |
| 87 callback.Run(); | 87 callback.Run(); |
| 88 return; | 88 return; |
| 89 } | 89 } |
| 90 | 90 |
| 91 state_ = kStopped; | |
| 92 | |
| 93 statistics_cb_.Reset(); | |
| 94 max_time_cb_.Reset(); | |
| 95 DoStopOrError_Locked(); | |
| 96 | |
| 97 // Clean up our thread if present. | |
| 91 base::PlatformThreadHandle thread_to_join = base::kNullThreadHandle; | 98 base::PlatformThreadHandle thread_to_join = base::kNullThreadHandle; |
| 92 { | 99 if (thread_ != base::kNullThreadHandle) { |
| 93 base::AutoLock auto_lock(lock_); | 100 // Signal the thread since it's possible to get stopped with the video |
| 94 state_ = kStopped; | 101 // thread waiting for a read to complete. |
| 102 frame_available_.Signal(); | |
| 103 std::swap(thread_, thread_to_join); | |
| 104 } | |
| 95 | 105 |
| 96 statistics_cb_.Reset(); | 106 if (thread_to_join != base::kNullThreadHandle) { |
| 97 max_time_cb_.Reset(); | 107 base::AutoUnlock auto_unlock(lock_); |
| 98 if (!pending_paint_ && !pending_paint_with_last_available_) | 108 base::PlatformThread::Join(thread_to_join); |
| 99 DoStopOrError_Locked(); | |
| 100 | |
| 101 // Clean up our thread if present. | |
| 102 if (thread_ != base::kNullThreadHandle) { | |
| 103 // Signal the thread since it's possible to get stopped with the video | |
| 104 // thread waiting for a read to complete. | |
| 105 frame_available_.Signal(); | |
| 106 thread_to_join = thread_; | |
| 107 thread_ = base::kNullThreadHandle; | |
| 108 } | |
| 109 } | 109 } |
| 110 if (thread_to_join != base::kNullThreadHandle) | |
| 111 base::PlatformThread::Join(thread_to_join); | |
| 112 | 110 |
| 113 if (decrypting_demuxer_stream_) { | 111 if (decrypting_demuxer_stream_) { |
| 114 decrypting_demuxer_stream_->Reset(base::Bind( | 112 decrypting_demuxer_stream_->Reset(base::Bind( |
| 115 &VideoRendererBase::StopDecoder, this, callback)); | 113 &VideoRendererBase::StopDecoder, this, callback)); |
| 116 return; | 114 return; |
| 117 } | 115 } |
| 118 | 116 |
| 119 decoder_->Stop(callback); | 117 decoder_->Stop(callback); |
| 120 } | 118 } |
| 121 | 119 |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 245 | 243 |
| 246 // The number of milliseconds to idle when we do not have anything to do. | 244 // The number of milliseconds to idle when we do not have anything to do. |
| 247 // Nothing special about the value, other than we're being more OS-friendly | 245 // Nothing special about the value, other than we're being more OS-friendly |
| 248 // than sleeping for 1 millisecond. | 246 // than sleeping for 1 millisecond. |
| 249 // | 247 // |
| 250 // TOOD(scherkus): switch to pure event-driven frame timing instead of this | 248 // TOOD(scherkus): switch to pure event-driven frame timing instead of this |
| 251 // kIdleTimeDelta business http://crbug.com/106874 | 249 // kIdleTimeDelta business http://crbug.com/106874 |
| 252 const base::TimeDelta kIdleTimeDelta = | 250 const base::TimeDelta kIdleTimeDelta = |
| 253 base::TimeDelta::FromMilliseconds(10); | 251 base::TimeDelta::FromMilliseconds(10); |
| 254 | 252 |
| 255 uint32 frames_dropped = 0; | |
| 256 | |
| 257 for (;;) { | 253 for (;;) { |
| 258 base::AutoLock auto_lock(lock_); | 254 base::AutoLock auto_lock(lock_); |
| 259 | 255 |
| 260 // Thread exit condition. | 256 // Thread exit condition. |
| 261 if (state_ == kStopped) | 257 if (state_ == kStopped) |
| 262 return; | 258 return; |
| 263 | 259 |
| 264 if (frames_dropped > 0) { | |
| 265 PipelineStatistics statistics; | |
| 266 statistics.video_frames_dropped = frames_dropped; | |
| 267 statistics_cb_.Run(statistics); | |
| 268 | |
| 269 frames_dropped = 0; | |
| 270 } | |
| 271 | |
| 272 // Remain idle as long as we're not playing. | 260 // Remain idle as long as we're not playing. |
| 273 if (state_ != kPlaying || playback_rate_ == 0) { | 261 if (state_ != kPlaying || playback_rate_ == 0) { |
| 274 frame_available_.TimedWait(kIdleTimeDelta); | 262 frame_available_.TimedWait(kIdleTimeDelta); |
| 275 continue; | 263 continue; |
| 276 } | 264 } |
| 277 | 265 |
| 278 // Remain idle until we have the next frame ready for rendering. | 266 // Remain idle until we have the next frame ready for rendering. |
| 279 if (ready_frames_.empty()) { | 267 if (ready_frames_.empty()) { |
| 280 frame_available_.TimedWait(kIdleTimeDelta); | 268 frame_available_.TimedWait(kIdleTimeDelta); |
| 281 continue; | 269 continue; |
| 282 } | 270 } |
| 283 | 271 |
| 284 // Remain idle until we've initialized |current_frame_| via prerolling. | 272 // We've reached the end! |
| 285 if (!current_frame_) { | 273 if (ready_frames_.front()->IsEndOfStream()) { |
| 286 // This can happen if our preroll only contains end of stream frames. | 274 state_ = kEnded; |
| 287 if (ready_frames_.front()->IsEndOfStream()) { | 275 ended_cb_.Run(); |
| 288 state_ = kEnded; | |
| 289 ended_cb_.Run(); | |
| 290 ready_frames_.clear(); | |
|
acolwell GONE FROM CHROMIUM
2013/02/04 17:35:15
Shouldn't we keep this line?
scherkus (not reviewing)
2013/02/05 00:29:39
Done.
| |
| 291 | 276 |
| 292 // No need to sleep here as we idle when |state_ != kPlaying|. | 277 // No need to sleep here as we idle when |state_ != kPlaying|. |
| 293 continue; | |
| 294 } | |
| 295 | |
| 296 frame_available_.TimedWait(kIdleTimeDelta); | |
| 297 continue; | 278 continue; |
| 298 } | 279 } |
| 299 | 280 |
| 300 // Calculate how long until we should advance the frame, which is | |
| 301 // typically negative but for playback rates < 1.0f may be long enough | |
| 302 // that it makes more sense to idle and check again. | |
| 303 base::TimeDelta remaining_time = | 281 base::TimeDelta remaining_time = |
| 304 CalculateSleepDuration(ready_frames_.front(), playback_rate_); | 282 CalculateSleepDuration(ready_frames_.front(), playback_rate_); |
| 305 | 283 |
| 306 // Sleep up to a maximum of our idle time until we're within the time to | 284 // Sleep up to a maximum of our idle time until we're within the time to |
| 307 // render the next frame. | 285 // render the next frame. |
| 308 if (remaining_time.InMicroseconds() > 0) { | 286 if (remaining_time.InMicroseconds() > 0) { |
| 309 remaining_time = std::min(remaining_time, kIdleTimeDelta); | 287 remaining_time = std::min(remaining_time, kIdleTimeDelta); |
| 310 frame_available_.TimedWait(remaining_time); | 288 frame_available_.TimedWait(remaining_time); |
| 311 continue; | 289 continue; |
| 312 } | 290 } |
| 313 | 291 |
| 292 // Deadline is defined as the midpoint between this frame and the next | |
|
scherkus (not reviewing)
2013/02/01 22:45:25
The old code's deadline was defined as:
remaining_
scherkus (not reviewing)
2013/02/05 00:29:39
Expanded this comment w/ more ideas.
| |
| 293 // frame, using the delta between this frame and the previous frame as the | |
| 294 // assumption for frame duration. | |
| 295 // | |
| 296 // TODO(scherkus): This can be vastly improved. Use a histogram to measure | |
| 297 // the accuracy of our frame timing code. http://crbug.com/149829 | |
| 298 if (drop_frames_ && last_timestamp_ != kNoTimestamp()) { | |
| 299 base::TimeDelta now = get_time_cb_.Run(); | |
| 300 base::TimeDelta deadline = ready_frames_.front()->GetTimestamp() + | |
| 301 (ready_frames_.front()->GetTimestamp() - last_timestamp_) / 2; | |
| 314 | 302 |
| 315 // We're almost there! | 303 if (now > deadline) { |
| 316 // | 304 DropNextReadyFrame_Locked(); |
| 317 // At this point we've rendered |current_frame_| for the proper amount | 305 continue; |
| 318 // of time and also have the next frame that ready for rendering. | 306 } |
| 319 | |
| 320 | |
| 321 // If the next frame is end of stream then we are truly at the end of the | |
| 322 // video stream. | |
| 323 // | |
| 324 // TODO(scherkus): deduplicate this end of stream check after we get rid of | |
| 325 // |current_frame_|. | |
| 326 if (ready_frames_.front()->IsEndOfStream()) { | |
| 327 state_ = kEnded; | |
| 328 ended_cb_.Run(); | |
| 329 ready_frames_.clear(); | |
| 330 | |
| 331 // No need to sleep here as we idle when |state_ != kPlaying|. | |
| 332 continue; | |
| 333 } | 307 } |
| 334 | 308 |
| 335 // We cannot update |current_frame_| until we've completed the pending | |
| 336 // paint. Furthermore, the pending paint might be really slow: check to | |
| 337 // see if we have any ready frames that we can drop if they've already | |
| 338 // expired. | |
| 339 if (pending_paint_) { | |
| 340 while (!ready_frames_.empty()) { | |
| 341 // Can't drop anything if we're at the end. | |
| 342 if (ready_frames_.front()->IsEndOfStream()) | |
| 343 break; | |
| 344 | |
| 345 base::TimeDelta remaining_time = | |
| 346 ready_frames_.front()->GetTimestamp() - get_time_cb_.Run(); | |
| 347 | |
| 348 // Still a chance we can render the frame! | |
| 349 if (remaining_time.InMicroseconds() > 0) | |
| 350 break; | |
| 351 | |
| 352 if (!drop_frames_) | |
| 353 break; | |
| 354 | |
| 355 // Frame dropped: read again. | |
| 356 ++frames_dropped; | |
| 357 ready_frames_.pop_front(); | |
| 358 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 359 &VideoRendererBase::AttemptRead, this)); | |
| 360 } | |
| 361 // Continue waiting for the current paint to finish. | |
| 362 frame_available_.TimedWait(kIdleTimeDelta); | |
| 363 continue; | |
| 364 } | |
| 365 | |
| 366 | |
| 367 // Congratulations! You've made it past the video frame timing gauntlet. | 309 // Congratulations! You've made it past the video frame timing gauntlet. |
| 368 // | 310 // |
| 369 // We can now safely update the current frame, request another frame, and | 311 // At this point enough time has passed that the next frame that ready for |
| 370 // signal to the client that a new frame is available. | 312 // rendering. |
| 371 DCHECK(!pending_paint_); | 313 PaintNextReadyFrame_Locked(); |
| 372 DCHECK(!ready_frames_.empty()); | |
| 373 SetCurrentFrameToNextReadyFrame(); | |
| 374 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 375 &VideoRendererBase::AttemptRead, this)); | |
| 376 | |
| 377 base::AutoUnlock auto_unlock(lock_); | |
| 378 paint_cb_.Run(); | |
| 379 } | 314 } |
| 380 } | 315 } |
| 381 | 316 |
| 382 void VideoRendererBase::SetCurrentFrameToNextReadyFrame() { | 317 void VideoRendererBase::PaintNextReadyFrame_Locked() { |
| 383 current_frame_ = ready_frames_.front(); | 318 lock_.AssertAcquired(); |
| 319 | |
| 320 scoped_refptr<VideoFrame> next_frame = ready_frames_.front(); | |
| 384 ready_frames_.pop_front(); | 321 ready_frames_.pop_front(); |
| 385 | 322 |
| 386 // Notify the pipeline of natural_size() changes. | 323 last_timestamp_ = next_frame->GetTimestamp(); |
| 387 const gfx::Size& natural_size = current_frame_->natural_size(); | 324 |
| 325 const gfx::Size& natural_size = next_frame->natural_size(); | |
| 388 if (natural_size != last_natural_size_) { | 326 if (natural_size != last_natural_size_) { |
| 327 last_natural_size_ = natural_size; | |
| 389 size_changed_cb_.Run(natural_size); | 328 size_changed_cb_.Run(natural_size); |
| 390 last_natural_size_ = natural_size; | |
| 391 } | 329 } |
| 330 | |
| 331 paint_cb_.Run(next_frame); | |
| 332 | |
| 333 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 334 &VideoRendererBase::AttemptRead, this)); | |
| 392 } | 335 } |
| 393 | 336 |
| 394 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { | 337 void VideoRendererBase::DropNextReadyFrame_Locked() { |
| 395 base::AutoLock auto_lock(lock_); | 338 lock_.AssertAcquired(); |
| 396 DCHECK(!pending_paint_ && !pending_paint_with_last_available_); | |
| 397 | 339 |
| 398 if ((!current_frame_ || current_frame_->IsEndOfStream()) && | 340 last_timestamp_ = ready_frames_.front()->GetTimestamp(); |
| 399 (!last_available_frame_ || last_available_frame_->IsEndOfStream())) { | 341 ready_frames_.pop_front(); |
| 400 *frame_out = NULL; | |
| 401 return; | |
| 402 } | |
| 403 | 342 |
| 404 // We should have initialized and have the current frame. | 343 PipelineStatistics statistics; |
| 405 DCHECK_NE(state_, kUninitialized); | 344 statistics.video_frames_dropped = 1; |
| 406 DCHECK_NE(state_, kStopped); | 345 statistics_cb_.Run(statistics); |
| 407 DCHECK_NE(state_, kError); | |
| 408 | 346 |
| 409 if (current_frame_) { | 347 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 410 *frame_out = current_frame_; | 348 &VideoRendererBase::AttemptRead, this)); |
| 411 last_available_frame_ = current_frame_; | |
| 412 pending_paint_ = true; | |
| 413 } else { | |
| 414 DCHECK(last_available_frame_); | |
| 415 *frame_out = last_available_frame_; | |
| 416 pending_paint_with_last_available_ = true; | |
| 417 } | |
| 418 } | |
| 419 | |
| 420 void VideoRendererBase::PutCurrentFrame(scoped_refptr<VideoFrame> frame) { | |
| 421 base::AutoLock auto_lock(lock_); | |
| 422 | |
| 423 // Note that we do not claim |pending_paint_| when we return NULL frame, in | |
| 424 // that case, |current_frame_| could be changed before PutCurrentFrame. | |
| 425 if (pending_paint_) { | |
| 426 DCHECK_EQ(current_frame_, frame); | |
| 427 DCHECK(!pending_paint_with_last_available_); | |
| 428 pending_paint_ = false; | |
| 429 } else if (pending_paint_with_last_available_) { | |
| 430 DCHECK_EQ(last_available_frame_, frame); | |
| 431 DCHECK(!pending_paint_); | |
| 432 pending_paint_with_last_available_ = false; | |
| 433 } else { | |
| 434 DCHECK(!frame); | |
| 435 } | |
| 436 | |
| 437 // We had cleared the |pending_paint_| flag, there are chances that current | |
| 438 // frame is timed-out. We will wake up our main thread to advance the current | |
| 439 // frame when this is true. | |
| 440 frame_available_.Signal(); | |
| 441 if (state_ == kFlushingDecoder) | |
| 442 return; | |
| 443 | |
| 444 if (state_ == kFlushing) { | |
| 445 AttemptFlush_Locked(); | |
| 446 return; | |
| 447 } | |
| 448 | |
| 449 if (state_ == kError || state_ == kStopped) { | |
| 450 DoStopOrError_Locked(); | |
| 451 } | |
| 452 } | 349 } |
| 453 | 350 |
| 454 VideoRendererBase::~VideoRendererBase() { | 351 VideoRendererBase::~VideoRendererBase() { |
| 455 base::AutoLock auto_lock(lock_); | 352 base::AutoLock auto_lock(lock_); |
| 456 DCHECK(state_ == kUninitialized || state_ == kStopped) << state_; | 353 DCHECK(state_ == kUninitialized || state_ == kStopped) << state_; |
| 457 } | 354 } |
| 458 | 355 |
| 459 void VideoRendererBase::FrameReady(VideoDecoder::Status status, | 356 void VideoRendererBase::FrameReady(VideoDecoder::Status status, |
| 460 const scoped_refptr<VideoFrame>& frame) { | 357 const scoped_refptr<VideoFrame>& frame) { |
| 461 base::AutoLock auto_lock(lock_); | 358 base::AutoLock auto_lock(lock_); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 525 // purposes: | 422 // purposes: |
| 526 // 1) Prerolling while paused | 423 // 1) Prerolling while paused |
| 527 // 2) Keeps decoding going if video rendering thread starts falling behind | 424 // 2) Keeps decoding going if video rendering thread starts falling behind |
| 528 if (NumFrames_Locked() < limits::kMaxVideoFrames && !frame->IsEndOfStream()) { | 425 if (NumFrames_Locked() < limits::kMaxVideoFrames && !frame->IsEndOfStream()) { |
| 529 AttemptRead_Locked(); | 426 AttemptRead_Locked(); |
| 530 return; | 427 return; |
| 531 } | 428 } |
| 532 | 429 |
| 533 // If we're at capacity or end of stream while prerolling we need to | 430 // If we're at capacity or end of stream while prerolling we need to |
| 534 // transition to prerolled. | 431 // transition to prerolled. |
| 535 if (state_ == kPrerolling) { | 432 if (state_ != kPrerolling) |
| 536 DCHECK(!current_frame_); | 433 return; |
| 537 state_ = kPrerolled; | |
| 538 | 434 |
| 539 // Because we might remain in the prerolled state for an undetermined amount | 435 state_ = kPrerolled; |
| 540 // of time (i.e., we were not playing before we started prerolling), we'll | |
| 541 // manually update the current frame and notify the subclass below. | |
| 542 if (!ready_frames_.front()->IsEndOfStream()) | |
| 543 SetCurrentFrameToNextReadyFrame(); | |
| 544 | 436 |
| 545 // ...and we're done prerolling! | 437 // Because we might remain in the prerolled state for an undetermined amount |
| 546 DCHECK(!preroll_cb_.is_null()); | 438 // of time (e.g., we seeked while paused), we'll paint the first prerolled |
| 547 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); | 439 // frame. |
| 440 if (!ready_frames_.front()->IsEndOfStream()) | |
| 441 PaintNextReadyFrame_Locked(); | |
| 548 | 442 |
| 549 base::AutoUnlock ul(lock_); | 443 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); |
| 550 paint_cb_.Run(); | |
| 551 } | |
| 552 } | 444 } |
| 553 | 445 |
| 554 void VideoRendererBase::AddReadyFrame(const scoped_refptr<VideoFrame>& frame) { | 446 void VideoRendererBase::AddReadyFrame(const scoped_refptr<VideoFrame>& frame) { |
| 555 // Adjust the incoming frame if its rendering stop time is past the duration | 447 // Adjust the incoming frame if its rendering stop time is past the duration |
| 556 // of the video itself. This is typically the last frame of the video and | 448 // of the video itself. This is typically the last frame of the video and |
| 557 // occurs if the container specifies a duration that isn't a multiple of the | 449 // occurs if the container specifies a duration that isn't a multiple of the |
| 558 // frame rate. Another way for this to happen is for the container to state a | 450 // frame rate. Another way for this to happen is for the container to state a |
| 559 // smaller duration than the largest packet timestamp. | 451 // smaller duration than the largest packet timestamp. |
| 560 base::TimeDelta duration = get_duration_cb_.Run(); | 452 base::TimeDelta duration = get_duration_cb_.Run(); |
| 561 if (frame->IsEndOfStream()) { | 453 if (frame->IsEndOfStream()) { |
| 562 base::TimeDelta end_timestamp = kNoTimestamp(); | 454 base::TimeDelta end_timestamp = kNoTimestamp(); |
| 563 if (!ready_frames_.empty()) { | 455 if (!ready_frames_.empty()) { |
| 564 end_timestamp = std::min( | 456 end_timestamp = std::min( |
| 565 duration, | 457 duration, |
| 566 ready_frames_.back()->GetTimestamp() + kMaxLastFrameDuration()); | 458 ready_frames_.back()->GetTimestamp() + kMaxLastFrameDuration()); |
| 567 } else if (current_frame_) { | 459 } else if (last_timestamp_ != kNoTimestamp()) { |
| 568 end_timestamp = | 460 end_timestamp = |
| 569 std::min(duration, | 461 std::min(duration, last_timestamp_ + kMaxLastFrameDuration()); |
| 570 current_frame_->GetTimestamp() + kMaxLastFrameDuration()); | |
| 571 } | 462 } |
| 572 frame->SetTimestamp(end_timestamp); | 463 frame->SetTimestamp(end_timestamp); |
| 573 } else if (frame->GetTimestamp() > duration) { | 464 } else if (frame->GetTimestamp() > duration) { |
| 574 frame->SetTimestamp(duration); | 465 frame->SetTimestamp(duration); |
| 575 } | 466 } |
| 576 | 467 |
| 577 ready_frames_.push_back(frame); | 468 ready_frames_.push_back(frame); |
| 578 DCHECK_LE(NumFrames_Locked(), limits::kMaxVideoFrames); | 469 DCHECK_LE(NumFrames_Locked(), limits::kMaxVideoFrames); |
| 579 | 470 |
| 580 base::TimeDelta max_clock_time = | 471 base::TimeDelta max_clock_time = |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 629 AttemptFlush_Locked(); | 520 AttemptFlush_Locked(); |
| 630 } | 521 } |
| 631 | 522 |
| 632 void VideoRendererBase::AttemptFlush_Locked() { | 523 void VideoRendererBase::AttemptFlush_Locked() { |
| 633 lock_.AssertAcquired(); | 524 lock_.AssertAcquired(); |
| 634 DCHECK_EQ(kFlushing, state_); | 525 DCHECK_EQ(kFlushing, state_); |
| 635 | 526 |
| 636 prerolling_delayed_frame_ = NULL; | 527 prerolling_delayed_frame_ = NULL; |
| 637 ready_frames_.clear(); | 528 ready_frames_.clear(); |
| 638 | 529 |
| 639 if (!pending_paint_ && !pending_read_) { | 530 if (pending_read_) |
| 640 state_ = kFlushed; | 531 return; |
| 641 current_frame_ = NULL; | 532 |
| 642 base::ResetAndReturn(&flush_cb_).Run(); | 533 state_ = kFlushed; |
| 643 } | 534 last_timestamp_ = kNoTimestamp(); |
| 535 base::ResetAndReturn(&flush_cb_).Run(); | |
| 644 } | 536 } |
| 645 | 537 |
| 646 base::TimeDelta VideoRendererBase::CalculateSleepDuration( | 538 base::TimeDelta VideoRendererBase::CalculateSleepDuration( |
| 647 const scoped_refptr<VideoFrame>& next_frame, | 539 const scoped_refptr<VideoFrame>& next_frame, |
| 648 float playback_rate) { | 540 float playback_rate) { |
| 649 // Determine the current and next presentation timestamps. | 541 // Determine the current and next presentation timestamps. |
| 650 base::TimeDelta now = get_time_cb_.Run(); | 542 base::TimeDelta now = get_time_cb_.Run(); |
| 651 base::TimeDelta next_pts = next_frame->GetTimestamp(); | 543 base::TimeDelta next_pts = next_frame->GetTimestamp(); |
| 652 | 544 |
| 653 // Scale our sleep based on the playback rate. | 545 // Scale our sleep based on the playback rate. |
| 654 base::TimeDelta sleep = next_pts - now; | 546 base::TimeDelta sleep = next_pts - now; |
| 655 return base::TimeDelta::FromMicroseconds( | 547 return base::TimeDelta::FromMicroseconds( |
| 656 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); | 548 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); |
| 657 } | 549 } |
| 658 | 550 |
| 659 void VideoRendererBase::DoStopOrError_Locked() { | 551 void VideoRendererBase::DoStopOrError_Locked() { |
| 660 DCHECK(!pending_paint_); | |
| 661 DCHECK(!pending_paint_with_last_available_); | |
| 662 lock_.AssertAcquired(); | 552 lock_.AssertAcquired(); |
| 663 current_frame_ = NULL; | 553 last_timestamp_ = kNoTimestamp(); |
| 664 last_available_frame_ = NULL; | |
| 665 ready_frames_.clear(); | 554 ready_frames_.clear(); |
| 666 } | 555 } |
| 667 | 556 |
| 668 int VideoRendererBase::NumFrames_Locked() const { | 557 int VideoRendererBase::NumFrames_Locked() const { |
| 669 lock_.AssertAcquired(); | 558 lock_.AssertAcquired(); |
| 670 int outstanding_frames = | 559 return ready_frames_.size(); |
| 671 (current_frame_ ? 1 : 0) + (last_available_frame_ ? 1 : 0) + | |
| 672 (current_frame_ && (current_frame_ == last_available_frame_) ? -1 : 0); | |
| 673 return ready_frames_.size() + outstanding_frames; | |
| 674 } | 560 } |
| 675 | 561 |
| 676 } // namespace media | 562 } // namespace media |
| OLD | NEW |