Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "base/bind.h" | 5 #include "base/bind.h" |
| 6 #include "base/callback.h" | 6 #include "base/callback.h" |
| 7 #include "base/threading/platform_thread.h" | 7 #include "base/threading/platform_thread.h" |
| 8 #include "media/base/buffers.h" | 8 #include "media/base/buffers.h" |
| 9 #include "media/base/filter_host.h" | 9 #include "media/base/filter_host.h" |
| 10 #include "media/base/limits.h" | 10 #include "media/base/limits.h" |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 | 23 |
| 24 // The number of milliseconds to idle when we do not have anything to do. | 24 // The number of milliseconds to idle when we do not have anything to do. |
| 25 // Nothing special about the value, other than we're being more OS-friendly | 25 // Nothing special about the value, other than we're being more OS-friendly |
| 26 // than sleeping for 1 millisecond. | 26 // than sleeping for 1 millisecond. |
| 27 static const int kIdleMilliseconds = 10; | 27 static const int kIdleMilliseconds = 10; |
| 28 | 28 |
| 29 VideoRendererBase::VideoRendererBase() | 29 VideoRendererBase::VideoRendererBase() |
| 30 : frame_available_(&lock_), | 30 : frame_available_(&lock_), |
| 31 state_(kUninitialized), | 31 state_(kUninitialized), |
| 32 thread_(base::kNullThreadHandle), | 32 thread_(base::kNullThreadHandle), |
| 33 pending_reads_(0), | 33 pending_read_(false), |
| 34 pending_paint_(false), | 34 pending_paint_(false), |
| 35 pending_paint_with_last_available_(false), | 35 pending_paint_with_last_available_(false), |
| 36 playback_rate_(0) { | 36 playback_rate_(0), |
| 37 frame_ready_cb_(base::Bind(&VideoRendererBase::FrameReady, | |
| 38 base::Unretained(this))) { | |
| 37 } | 39 } |
| 38 | 40 |
| 39 VideoRendererBase::~VideoRendererBase() { | 41 VideoRendererBase::~VideoRendererBase() { |
| 40 base::AutoLock auto_lock(lock_); | 42 base::AutoLock auto_lock(lock_); |
| 41 DCHECK(state_ == kUninitialized || state_ == kStopped) << state_; | 43 DCHECK(state_ == kUninitialized || state_ == kStopped) << state_; |
| 42 } | 44 } |
| 43 | 45 |
| 44 void VideoRendererBase::Play(const base::Closure& callback) { | 46 void VideoRendererBase::Play(const base::Closure& callback) { |
| 45 base::AutoLock auto_lock(lock_); | 47 base::AutoLock auto_lock(lock_); |
| 46 DCHECK_EQ(kPrerolled, state_); | 48 DCHECK_EQ(kPrerolled, state_); |
| 47 state_ = kPlaying; | 49 state_ = kPlaying; |
| 48 callback.Run(); | 50 callback.Run(); |
| 49 } | 51 } |
| 50 | 52 |
| 51 void VideoRendererBase::Pause(const base::Closure& callback) { | 53 void VideoRendererBase::Pause(const base::Closure& callback) { |
| 52 base::AutoLock auto_lock(lock_); | 54 base::AutoLock auto_lock(lock_); |
| 53 DCHECK(state_ != kUninitialized || state_ == kError); | 55 DCHECK(state_ != kUninitialized || state_ == kError); |
| 54 state_ = kPaused; | 56 state_ = kPaused; |
| 55 callback.Run(); | 57 callback.Run(); |
| 56 } | 58 } |
| 57 | 59 |
| 58 void VideoRendererBase::Flush(const base::Closure& callback) { | 60 void VideoRendererBase::Flush(const base::Closure& callback) { |
| 59 base::AutoLock auto_lock(lock_); | 61 base::AutoLock auto_lock(lock_); |
| 60 DCHECK_EQ(state_, kPaused); | 62 DCHECK_EQ(state_, kPaused); |
| 61 flush_callback_ = callback; | 63 flush_callback_ = callback; |
| 62 state_ = kFlushing; | 64 state_ = kFlushing; |
| 63 | 65 |
| 64 if (!pending_paint_) | 66 AttemptFlush_Locked(); |
| 65 FlushBuffers_Locked(); | |
| 66 } | 67 } |
| 67 | 68 |
| 68 void VideoRendererBase::Stop(const base::Closure& callback) { | 69 void VideoRendererBase::Stop(const base::Closure& callback) { |
| 69 base::PlatformThreadHandle old_thread_handle = base::kNullThreadHandle; | 70 base::PlatformThreadHandle old_thread_handle = base::kNullThreadHandle; |
| 70 { | 71 { |
| 71 base::AutoLock auto_lock(lock_); | 72 base::AutoLock auto_lock(lock_); |
| 72 state_ = kStopped; | 73 state_ = kStopped; |
| 73 | 74 |
| 74 if (!pending_paint_ && !pending_paint_with_last_available_) | 75 if (!pending_paint_ && !pending_paint_with_last_available_) |
| 75 DoStopOrErrorFlush_Locked(); | 76 DoStopOrError_Locked(); |
| 76 | 77 |
| 77 // Clean up our thread if present. | 78 // Clean up our thread if present. |
| 78 if (thread_) { | 79 if (thread_) { |
|
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
Given the code below shouldn't this be (thread_ !=
scherkus (not reviewing)
2011/11/03 04:55:59
Done.
| |
| 79 // Signal the thread since it's possible to get stopped with the video | 80 // Signal the thread since it's possible to get stopped with the video |
| 80 // thread waiting for a read to complete. | 81 // thread waiting for a read to complete. |
| 81 frame_available_.Signal(); | 82 frame_available_.Signal(); |
| 82 old_thread_handle = thread_; | 83 old_thread_handle = thread_; |
| 83 thread_ = base::kNullThreadHandle; | 84 thread_ = base::kNullThreadHandle; |
| 84 } | 85 } |
| 85 } | 86 } |
| 86 if (old_thread_handle) | 87 if (old_thread_handle) |
|
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
ditto
scherkus (not reviewing)
2011/11/03 04:55:59
Done.
| |
| 87 base::PlatformThread::Join(old_thread_handle); | 88 base::PlatformThread::Join(old_thread_handle); |
| 88 | 89 |
| 89 // Signal the subclass we're stopping. | 90 // Signal the subclass we're stopping. |
| 90 OnStop(callback); | 91 OnStop(callback); |
| 91 } | 92 } |
| 92 | 93 |
| 93 void VideoRendererBase::SetPlaybackRate(float playback_rate) { | 94 void VideoRendererBase::SetPlaybackRate(float playback_rate) { |
| 94 base::AutoLock auto_lock(lock_); | 95 base::AutoLock auto_lock(lock_); |
| 95 playback_rate_ = playback_rate; | 96 playback_rate_ = playback_rate; |
| 96 } | 97 } |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 113 // Already get enough buffers from decoder. | 114 // Already get enough buffers from decoder. |
| 114 run_callback = true; | 115 run_callback = true; |
| 115 } else { | 116 } else { |
| 116 // Otherwise we are either kFlushed or kSeeking, but without enough | 117 // Otherwise we are either kFlushed or kSeeking, but without enough |
| 117 // buffers we should save the callback function and call it later. | 118 // buffers we should save the callback function and call it later. |
| 118 state_ = kSeeking; | 119 state_ = kSeeking; |
| 119 seek_cb_ = cb; | 120 seek_cb_ = cb; |
| 120 } | 121 } |
| 121 | 122 |
| 122 seek_timestamp_ = time; | 123 seek_timestamp_ = time; |
| 123 ScheduleRead_Locked(); | 124 AttemptRead_Locked(); |
| 124 } | 125 } |
| 125 | 126 |
| 126 if (run_callback) | 127 if (run_callback) |
| 127 cb.Run(PIPELINE_OK); | 128 cb.Run(PIPELINE_OK); |
| 128 } | 129 } |
| 129 | 130 |
| 130 void VideoRendererBase::Initialize(VideoDecoder* decoder, | 131 void VideoRendererBase::Initialize(VideoDecoder* decoder, |
| 131 const base::Closure& callback, | 132 const base::Closure& callback, |
| 132 const StatisticsCallback& stats_callback) { | 133 const StatisticsCallback& stats_callback) { |
| 133 base::AutoLock auto_lock(lock_); | 134 base::AutoLock auto_lock(lock_); |
| 134 DCHECK(decoder); | 135 DCHECK(decoder); |
| 135 DCHECK(!callback.is_null()); | 136 DCHECK(!callback.is_null()); |
| 136 DCHECK(!stats_callback.is_null()); | 137 DCHECK(!stats_callback.is_null()); |
| 137 DCHECK_EQ(kUninitialized, state_); | 138 DCHECK_EQ(kUninitialized, state_); |
| 138 decoder_ = decoder; | 139 decoder_ = decoder; |
| 139 | 140 |
| 140 statistics_callback_ = stats_callback; | 141 statistics_callback_ = stats_callback; |
| 141 | 142 |
| 142 decoder_->set_consume_video_frame_callback( | |
| 143 base::Bind(&VideoRendererBase::ConsumeVideoFrame, | |
| 144 base::Unretained(this))); | |
| 145 | |
| 146 // Notify the pipeline of the video dimensions. | 143 // Notify the pipeline of the video dimensions. |
| 147 host()->SetNaturalVideoSize(decoder_->natural_size()); | 144 host()->SetNaturalVideoSize(decoder_->natural_size()); |
| 148 | 145 |
| 149 // Initialize the subclass. | 146 // Initialize the subclass. |
| 150 // TODO(scherkus): do we trust subclasses not to do something silly while | 147 // TODO(scherkus): do we trust subclasses not to do something silly while |
| 151 // we're holding the lock? | 148 // we're holding the lock? |
| 152 if (!OnInitialize(decoder)) { | 149 if (!OnInitialize(decoder)) { |
| 153 EnterErrorState_Locked(PIPELINE_ERROR_INITIALIZATION_FAILED); | 150 EnterErrorState_Locked(PIPELINE_ERROR_INITIALIZATION_FAILED); |
| 154 callback.Run(); | 151 callback.Run(); |
| 155 return; | 152 return; |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 262 // exceptions that cause us to drop a frame and/or consider painting a | 259 // exceptions that cause us to drop a frame and/or consider painting a |
| 263 // "next" frame. | 260 // "next" frame. |
| 264 if (next_frame->GetTimestamp() > host()->GetTime() + kIdleTimeDelta && | 261 if (next_frame->GetTimestamp() > host()->GetTime() + kIdleTimeDelta && |
| 265 current_frame_ && | 262 current_frame_ && |
| 266 current_frame_->GetTimestamp() <= host()->GetDuration()) { | 263 current_frame_->GetTimestamp() <= host()->GetDuration()) { |
| 267 continue; | 264 continue; |
| 268 } | 265 } |
| 269 | 266 |
| 270 // If we got here then: | 267 // If we got here then: |
| 271 // 1. next frame's timestamp is already current; or | 268 // 1. next frame's timestamp is already current; or |
| 272 // 2. we do not have any current frame yet anyway; or | 269 // 2. we do not have a current frame yet; or |
| 273 // 3. a special case when the stream is badly formatted and | 270 // 3. a special case when the stream is badly formatted and |
| 274 // we got a frame with timestamp greater than overall duration. | 271 // we got a frame with timestamp greater than overall duration. |
| 275 // In this case we should proceed anyway and try to obtain the | 272 // In this case we should proceed anyway and try to obtain the |
| 276 // end-of-stream packet. | 273 // end-of-stream packet. |
| 277 | 274 |
| 278 if (pending_paint_) { | 275 if (pending_paint_) { |
| 279 // The pending paint might be really slow. Check if we have any frames | 276 // The pending paint might be really slow. Check if we have any frames |
| 280 // available that we can drop if they've already expired. | 277 // available that we can drop if they've already expired. |
| 281 while (!frames_queue_ready_.empty()) { | 278 while (!frames_queue_ready_.empty()) { |
| 282 // Can't drop anything if we're at the end. | 279 // Can't drop anything if we're at the end. |
| 283 if (frames_queue_ready_.front()->IsEndOfStream()) | 280 if (frames_queue_ready_.front()->IsEndOfStream()) |
| 284 break; | 281 break; |
| 285 | 282 |
| 286 base::TimeDelta remaining_time = | 283 base::TimeDelta remaining_time = |
| 287 frames_queue_ready_.front()->GetTimestamp() - host()->GetTime(); | 284 frames_queue_ready_.front()->GetTimestamp() - host()->GetTime(); |
| 288 | 285 |
| 289 // Still a chance we can render the frame! | 286 // Still a chance we can render the frame! |
| 290 if (remaining_time.InMicroseconds() > 0) | 287 if (remaining_time.InMicroseconds() > 0) |
| 291 break; | 288 break; |
| 292 | 289 |
| 293 // Frame dropped: transfer ready frame into done queue and read again. | 290 // Frame dropped: read again. |
| 294 frames_queue_done_.push_back(frames_queue_ready_.front()); | 291 ++frames_dropped; |
| 295 frames_queue_ready_.pop_front(); | 292 frames_queue_ready_.pop_front(); |
| 296 ScheduleRead_Locked(); | 293 AttemptRead_Locked(); |
| 297 ++frames_dropped; | |
| 298 } | 294 } |
| 299 | 295 |
| 300 // Continue waiting for the current paint to finish. | 296 // Continue waiting for the current paint to finish. |
| 301 continue; | 297 continue; |
| 302 } | 298 } |
| 303 | 299 |
| 304 // Congratulations! You've made it past the video frame timing gauntlet. | 300 // Congratulations! You've made it past the video frame timing gauntlet. |
| 305 // | 301 // |
| 306 // We can now safely update the current frame, request another frame, and | 302 // We can now safely update the current frame, request another frame, and |
| 307 // signal to the client that a new frame is available. | 303 // signal to the client that a new frame is available. |
| 308 frames_queue_done_.push_back(current_frame_); | |
| 309 current_frame_ = frames_queue_ready_.front(); | 304 current_frame_ = frames_queue_ready_.front(); |
| 310 frames_queue_ready_.pop_front(); | 305 frames_queue_ready_.pop_front(); |
| 311 ScheduleRead_Locked(); | 306 AttemptRead_Locked(); |
| 312 | 307 |
| 313 base::AutoUnlock auto_unlock(lock_); | 308 base::AutoUnlock auto_unlock(lock_); |
| 314 OnFrameAvailable(); | 309 OnFrameAvailable(); |
| 315 } | 310 } |
| 316 } | 311 } |
| 317 | 312 |
| 318 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { | 313 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { |
| 319 base::AutoLock auto_lock(lock_); | 314 base::AutoLock auto_lock(lock_); |
| 320 DCHECK(!pending_paint_ && !pending_paint_with_last_available_); | 315 DCHECK(!pending_paint_ && !pending_paint_with_last_available_); |
| 321 | 316 |
| 322 if ((!current_frame_ || current_frame_->IsEndOfStream()) && | 317 if ((!current_frame_ || current_frame_->IsEndOfStream()) && |
| 323 (!last_available_frame_ || | 318 (!last_available_frame_ || last_available_frame_->IsEndOfStream())) { |
| 324 last_available_frame_->IsEndOfStream())) { | |
| 325 *frame_out = NULL; | 319 *frame_out = NULL; |
| 326 return; | 320 return; |
| 327 } | 321 } |
| 328 | 322 |
| 329 // We should have initialized and have the current frame. | 323 // We should have initialized and have the current frame. |
| 330 DCHECK_NE(state_, kUninitialized); | 324 DCHECK_NE(state_, kUninitialized); |
| 331 DCHECK_NE(state_, kStopped); | 325 DCHECK_NE(state_, kStopped); |
| 332 DCHECK_NE(state_, kError); | 326 DCHECK_NE(state_, kError); |
| 333 | 327 |
| 334 if (current_frame_) { | 328 if (current_frame_) { |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 357 pending_paint_with_last_available_ = false; | 351 pending_paint_with_last_available_ = false; |
| 358 } else { | 352 } else { |
| 359 DCHECK(!frame); | 353 DCHECK(!frame); |
| 360 } | 354 } |
| 361 | 355 |
| 362 // We had cleared the |pending_paint_| flag, there are chances that current | 356 // We had cleared the |pending_paint_| flag, there are chances that current |
| 363 // frame is timed-out. We will wake up our main thread to advance the current | 357 // frame is timed-out. We will wake up our main thread to advance the current |
| 364 // frame when this is true. | 358 // frame when this is true. |
| 365 frame_available_.Signal(); | 359 frame_available_.Signal(); |
| 366 if (state_ == kFlushing) { | 360 if (state_ == kFlushing) { |
| 367 FlushBuffers_Locked(); | 361 AttemptFlush_Locked(); |
| 368 } else if (state_ == kError || state_ == kStopped) { | 362 } else if (state_ == kError || state_ == kStopped) { |
| 369 DoStopOrErrorFlush_Locked(); | 363 DoStopOrError_Locked(); |
| 370 } | 364 } |
| 371 } | 365 } |
| 372 | 366 |
| 373 void VideoRendererBase::ConsumeVideoFrame(scoped_refptr<VideoFrame> frame) { | 367 void VideoRendererBase::FrameReady(scoped_refptr<VideoFrame> frame) { |
| 374 if (frame) { | 368 DCHECK(frame); |
| 375 PipelineStatistics statistics; | |
| 376 statistics.video_frames_decoded = 1; | |
| 377 statistics_callback_.Run(statistics); | |
| 378 } | |
| 379 | 369 |
| 380 base::AutoLock auto_lock(lock_); | 370 base::AutoLock auto_lock(lock_); |
| 371 DCHECK_NE(state_, kUninitialized); | |
| 372 DCHECK_NE(state_, kStopped); | |
| 373 DCHECK_NE(state_, kError); | |
| 374 CHECK(pending_read_); | |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
DCHECK?
scherkus (not reviewing)
2011/11/03 04:55:59
Again this one of those things where a callback wa
| |
| 381 | 375 |
| 382 if (!frame) { | 376 pending_read_ = false; |
| 383 EnterErrorState_Locked(PIPELINE_ERROR_DECODE); | 377 |
|
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
Why not keep this?
| |
| 378 // Decoder could reach seek state before our Seek() get called. | |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
s/get/got/
| |
| 379 // We will enter kSeeking | |
| 380 if (state_ == kFlushed) { | |
| 381 CHECK(false) << "I don't think this is possible"; | |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
CHECK(false) is always better spelled LOG(FATAL)
B
scherkus (not reviewing)
2011/11/03 04:55:59
Left these CHECK()s in for local testing -- they w
| |
| 382 //state_ = kSeeking; | |
| 384 return; | 383 return; |
| 385 } | 384 } |
| 386 | 385 |
| 387 // Decoder could reach seek state before our Seek() get called. | 386 if (state_ == kPaused) { |
|
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
Can't this happen based on the "Prerolling while p
scherkus (not reviewing)
2011/11/03 04:55:59
I believe it can:
T1: VRB holds lock inside FrameR
| |
| 388 // We will enter kSeeking | 387 CHECK(false) << "I don't think this is possible"; |
| 389 if (state_ == kFlushed) | 388 //frame_queue_ready_.push_back(video_frame); |
| 390 state_ = kSeeking; | 389 return; |
| 390 } | |
| 391 | 391 |
| 392 // Synchronous flush between filters should prevent this from happening. | 392 if (state_ == kFlushing) { |
| 393 DCHECK_NE(state_, kStopped); | 393 AttemptFlush_Locked(); |
| 394 if (frame && !frame->IsEndOfStream()) | |
| 395 --pending_reads_; | |
| 396 | |
| 397 DCHECK_NE(state_, kUninitialized); | |
| 398 DCHECK_NE(state_, kStopped); | |
| 399 DCHECK_NE(state_, kError); | |
| 400 | |
| 401 if (state_ == kPaused || state_ == kFlushing) { | |
| 402 // Decoder are flushing rubbish video frame, we will not display them. | |
| 403 if (frame && !frame->IsEndOfStream()) | |
| 404 frames_queue_done_.push_back(frame); | |
| 405 DCHECK_LE(frames_queue_done_.size(), | |
| 406 static_cast<size_t>(Limits::kMaxVideoFrames)); | |
| 407 | |
| 408 // Excluding kPause here, because in pause state, we will never | |
| 409 // transfer out-bounding buffer. We do not flush buffer when Compositor | |
| 410 // hold reference to our video frame either. | |
| 411 if (state_ == kFlushing && pending_paint_ == false) | |
| 412 FlushBuffers_Locked(); | |
| 413 | |
| 414 return; | 394 return; |
| 415 } | 395 } |
| 416 | 396 |
| 417 // Discard frames until we reach our desired seek timestamp. | 397 // Discard frames until we reach our desired seek timestamp. |
| 418 if (state_ == kSeeking && !frame->IsEndOfStream() && | 398 if (state_ == kSeeking && !frame->IsEndOfStream() && |
| 419 (frame->GetTimestamp() + frame->GetDuration()) <= seek_timestamp_) { | 399 (frame->GetTimestamp() + frame->GetDuration()) <= seek_timestamp_) { |
| 420 frames_queue_done_.push_back(frame); | 400 AttemptRead_Locked(); |
| 421 ScheduleRead_Locked(); | 401 return; |
| 422 } else { | |
| 423 frames_queue_ready_.push_back(frame); | |
| 424 DCHECK_LE(frames_queue_ready_.size(), | |
| 425 static_cast<size_t>(Limits::kMaxVideoFrames)); | |
| 426 frame_available_.Signal(); | |
| 427 } | 402 } |
| 428 | 403 |
| 429 // Check for our preroll complete condition. | 404 // This one's a keeper! Place it in the ready queue. |
| 430 bool new_frame_available = false; | 405 frames_queue_ready_.push_back(frame); |
| 431 if (state_ == kSeeking) { | 406 DCHECK_LE(frames_queue_ready_.size(), |
| 432 if (frames_queue_ready_.size() == Limits::kMaxVideoFrames || | 407 static_cast<size_t>(Limits::kMaxVideoFrames)); |
| 433 frame->IsEndOfStream()) { | 408 frame_available_.Signal(); |
| 434 // We're paused, so make sure we update |current_frame_| to represent | |
| 435 // our new location. | |
| 436 state_ = kPrerolled; | |
| 437 | 409 |
| 438 // Because we might remain paused (i.e., we were not playing before we | 410 PipelineStatistics statistics; |
| 439 // received a seek), we can't rely on ThreadMain() to notify the subclass | 411 statistics.video_frames_decoded = 1; |
| 440 // the frame has been updated. | 412 statistics_callback_.Run(statistics); |
| 441 scoped_refptr<VideoFrame> first_frame; | |
| 442 first_frame = frames_queue_ready_.front(); | |
| 443 if (!first_frame->IsEndOfStream()) { | |
| 444 frames_queue_ready_.pop_front(); | |
| 445 current_frame_ = first_frame; | |
| 446 } | |
| 447 new_frame_available = true; | |
| 448 | 413 |
| 449 // If we reach prerolled state before Seek() is called by pipeline, | 414 // Always request more decoded video if we have capacity. This serves two |
| 450 // |seek_callback_| is not set, we will return immediately during | 415 // purposes: |
| 451 // when Seek() is eventually called. | 416 // 1) Prerolling while paused |
| 452 if (!seek_cb_.is_null()) { | 417 // 2) Keeps decoding going if video rendering thread starts falling behind |
| 453 ResetAndRunCB(&seek_cb_, PIPELINE_OK); | 418 if (frames_queue_ready_.size() < Limits::kMaxVideoFrames && |
| 454 } | 419 !frame->IsEndOfStream()) { |
| 455 } | 420 AttemptRead_Locked(); |
| 456 } else if (state_ == kFlushing && pending_reads_ == 0 && !pending_paint_) { | 421 return; |
| 457 OnFlushDone_Locked(); | |
| 458 } | 422 } |
| 459 | 423 |
| 460 if (new_frame_available) { | 424 // If we're at capacity or end of stream while seeking we need to transition |
| 461 base::AutoUnlock auto_unlock(lock_); | 425 // to prerolled. |
| 426 if (state_ == kSeeking) { | |
| 427 state_ = kPrerolled; | |
| 428 | |
| 429 // Because we might remain in the prerolled state for an undetermined amount | |
| 430 // of time (i.e., we were not playing before we received a seek), we'll | |
| 431 // manually update the current frame and notify the subclass below. | |
| 432 if (!frames_queue_ready_.front()->IsEndOfStream()) { | |
| 433 current_frame_ = frames_queue_ready_.front(); | |
| 434 frames_queue_ready_.pop_front(); | |
| 435 } | |
| 436 | |
| 437 // If we reach prerolled state before Seek() is called by pipeline, | |
| 438 // |seek_callback_| is not set, we will return immediately during | |
| 439 // when Seek() is eventually called. | |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
English.
scherkus (not reviewing)
2011/11/03 04:55:59
this and the comments up in Seek() are crazy bizar
| |
| 440 if (!seek_cb_.is_null()) { | |
| 441 ResetAndRunCB(&seek_cb_, PIPELINE_OK); | |
| 442 } | |
| 443 | |
| 444 base::AutoUnlock ul(lock_); | |
| 462 OnFrameAvailable(); | 445 OnFrameAvailable(); |
| 463 } | 446 } |
| 464 } | 447 } |
| 465 | 448 |
| 466 void VideoRendererBase::ReadInput(scoped_refptr<VideoFrame> frame) { | 449 void VideoRendererBase::AttemptRead_Locked() { |
| 467 // We should never return empty frames or EOS frame. | 450 lock_.AssertAcquired(); |
| 468 DCHECK(frame && !frame->IsEndOfStream()); | 451 DCHECK_NE(kEnded, state_); |
| 469 | 452 |
| 470 decoder_->ProduceVideoFrame(frame); | 453 if (pending_read_ || frames_queue_ready_.size() == Limits::kMaxVideoFrames) { |
| 471 ++pending_reads_; | 454 return; |
| 455 } | |
| 456 | |
| 457 pending_read_ = true; | |
| 458 decoder_->Read(frame_ready_cb_); | |
| 472 } | 459 } |
| 473 | 460 |
| 474 void VideoRendererBase::ScheduleRead_Locked() { | 461 void VideoRendererBase::AttemptFlush_Locked() { |
| 475 lock_.AssertAcquired(); | 462 lock_.AssertAcquired(); |
| 476 DCHECK_NE(kEnded, state_); | 463 DCHECK_EQ(kFlushing, state_); |
| 477 // TODO(jiesun): We use dummy buffer to feed decoder to let decoder to | 464 |
| 478 // provide buffer pools. In the future, we may want to implement real | 465 // Get rid of any ready frames. |
| 479 // buffer pool to recycle buffers. | 466 while (!frames_queue_ready_.empty()) { |
| 480 while (!frames_queue_done_.empty()) { | 467 frames_queue_ready_.pop_front(); |
| 481 scoped_refptr<VideoFrame> video_frame = frames_queue_done_.front(); | 468 } |
| 482 frames_queue_done_.pop_front(); | 469 |
| 483 ReadInput(video_frame); | 470 if (!pending_paint_ && !pending_read_) { |
|
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
I think this may be where you can get into trouble
scherkus (not reviewing)
2011/11/03 04:55:59
Correct -- fixed + updated unit tests
| |
| 471 state_ = kFlushed; | |
| 472 current_frame_ = NULL; | |
| 473 ResetAndRunCB(&flush_callback_); | |
| 484 } | 474 } |
| 485 } | 475 } |
| 486 | 476 |
| 487 void VideoRendererBase::FlushBuffers_Locked() { | |
| 488 lock_.AssertAcquired(); | |
| 489 DCHECK(!pending_paint_); | |
| 490 | |
| 491 // We should never put EOF frame into "done queue". | |
| 492 while (!frames_queue_ready_.empty()) { | |
| 493 scoped_refptr<VideoFrame> video_frame = frames_queue_ready_.front(); | |
| 494 if (!video_frame->IsEndOfStream()) { | |
| 495 frames_queue_done_.push_back(video_frame); | |
| 496 } | |
| 497 frames_queue_ready_.pop_front(); | |
| 498 } | |
| 499 if (current_frame_ && !current_frame_->IsEndOfStream()) { | |
| 500 frames_queue_done_.push_back(current_frame_); | |
| 501 } | |
| 502 current_frame_ = NULL; | |
| 503 | |
| 504 // Flush all buffers out to decoder. | |
| 505 ScheduleRead_Locked(); | |
| 506 | |
| 507 if (pending_reads_ == 0 && state_ == kFlushing) | |
| 508 OnFlushDone_Locked(); | |
| 509 } | |
| 510 | |
| 511 void VideoRendererBase::OnFlushDone_Locked() { | |
| 512 lock_.AssertAcquired(); | |
| 513 // Check all buffers are returned to owners. | |
| 514 DCHECK_EQ(frames_queue_done_.size(), 0u); | |
| 515 DCHECK(!current_frame_); | |
| 516 DCHECK(frames_queue_ready_.empty()); | |
| 517 | |
| 518 if (!flush_callback_.is_null()) // This ensures callback is invoked once. | |
| 519 ResetAndRunCB(&flush_callback_); | |
| 520 | |
| 521 state_ = kFlushed; | |
| 522 } | |
| 523 | |
| 524 base::TimeDelta VideoRendererBase::CalculateSleepDuration( | 477 base::TimeDelta VideoRendererBase::CalculateSleepDuration( |
| 525 VideoFrame* next_frame, float playback_rate) { | 478 VideoFrame* next_frame, float playback_rate) { |
| 526 // Determine the current and next presentation timestamps. | 479 // Determine the current and next presentation timestamps. |
| 527 base::TimeDelta now = host()->GetTime(); | 480 base::TimeDelta now = host()->GetTime(); |
| 528 base::TimeDelta this_pts = current_frame_->GetTimestamp(); | 481 base::TimeDelta this_pts = current_frame_->GetTimestamp(); |
| 529 base::TimeDelta next_pts; | 482 base::TimeDelta next_pts; |
| 530 if (next_frame) { | 483 if (next_frame) { |
| 531 next_pts = next_frame->GetTimestamp(); | 484 next_pts = next_frame->GetTimestamp(); |
| 532 } else { | 485 } else { |
| 533 next_pts = this_pts + current_frame_->GetDuration(); | 486 next_pts = this_pts + current_frame_->GetDuration(); |
| 534 } | 487 } |
| 535 | 488 |
| 536 // Determine our sleep duration based on whether time advanced. | 489 // Determine our sleep duration based on whether time advanced. |
| 537 base::TimeDelta sleep; | 490 base::TimeDelta sleep; |
| 538 if (now == previous_time_) { | 491 if (now == previous_time_) { |
| 539 // Time has not changed, assume we sleep for the frame's duration. | 492 // Time has not changed, assume we sleep for the frame's duration. |
| 540 sleep = next_pts - this_pts; | 493 sleep = next_pts - this_pts; |
| 541 } else { | 494 } else { |
| 542 // Time has changed, figure out real sleep duration. | 495 // Time has changed, figure out real sleep duration. |
| 543 sleep = next_pts - now; | 496 sleep = next_pts - now; |
| 544 previous_time_ = now; | 497 previous_time_ = now; |
| 545 } | 498 } |
| 546 | 499 |
| 547 // Scale our sleep based on the playback rate. | 500 // Scale our sleep based on the playback rate. |
| 548 // TODO(scherkus): floating point badness and degrade gracefully. | 501 // TODO(scherkus): floating point badness and degrade gracefully. |
| 549 return base::TimeDelta::FromMicroseconds( | 502 return base::TimeDelta::FromMicroseconds( |
| 550 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); | 503 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); |
| 551 } | 504 } |
| 552 | 505 |
| 553 void VideoRendererBase::EnterErrorState_Locked(PipelineStatus status) { | 506 void VideoRendererBase::EnterErrorState_Locked(PipelineStatus status) { |
|
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
assuming you don't reinstate the call in FrameRead
scherkus (not reviewing)
2011/11/03 04:55:59
Done.
| |
| 554 lock_.AssertAcquired(); | 507 lock_.AssertAcquired(); |
| 555 | 508 |
| 556 base::Closure callback; | 509 base::Closure callback; |
| 557 State old_state = state_; | 510 State old_state = state_; |
| 558 state_ = kError; | 511 state_ = kError; |
| 559 | 512 |
| 560 // Flush frames if we aren't in the middle of a paint. If we | 513 // Flush frames if we aren't in the middle of a paint. If we |
| 561 // are painting then flushing will happen when the paint completes. | 514 // are painting then flushing will happen when the paint completes. |
| 562 if (!pending_paint_ && !pending_paint_with_last_available_) | 515 if (!pending_paint_ && !pending_paint_with_last_available_) |
| 563 DoStopOrErrorFlush_Locked(); | 516 DoStopOrError_Locked(); |
| 564 | 517 |
| 565 switch (old_state) { | 518 switch (old_state) { |
| 566 case kUninitialized: | 519 case kUninitialized: |
| 567 case kPrerolled: | 520 case kPrerolled: |
| 568 case kPaused: | 521 case kPaused: |
| 569 case kFlushed: | 522 case kFlushed: |
| 570 case kEnded: | 523 case kEnded: |
| 571 case kPlaying: | 524 case kPlaying: |
| 572 break; | 525 break; |
| 573 | 526 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 589 case kError: | 542 case kError: |
| 590 return; | 543 return; |
| 591 } | 544 } |
| 592 | 545 |
| 593 host()->SetError(status); | 546 host()->SetError(status); |
| 594 | 547 |
| 595 if (!callback.is_null()) | 548 if (!callback.is_null()) |
| 596 callback.Run(); | 549 callback.Run(); |
| 597 } | 550 } |
| 598 | 551 |
| 599 void VideoRendererBase::DoStopOrErrorFlush_Locked() { | 552 void VideoRendererBase::DoStopOrError_Locked() { |
| 600 DCHECK(!pending_paint_); | 553 DCHECK(!pending_paint_); |
| 601 DCHECK(!pending_paint_with_last_available_); | 554 DCHECK(!pending_paint_with_last_available_); |
| 602 lock_.AssertAcquired(); | 555 lock_.AssertAcquired(); |
| 603 FlushBuffers_Locked(); | |
| 604 last_available_frame_ = NULL; | 556 last_available_frame_ = NULL; |
| 605 DCHECK_EQ(pending_reads_, 0); | 557 DCHECK(!pending_read_); |
|
acolwell GONE FROM CHROMIUM
2011/11/01 18:31:03
Looking at the code this doesn't seem like a safe
scherkus (not reviewing)
2011/11/03 04:55:59
I really don't know what this method does but the
| |
| 606 } | 558 } |
| 607 | 559 |
| 608 } // namespace media | 560 } // namespace media |
| OLD | NEW |