| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/callback.h" | 5 #include "base/callback.h" |
| 6 #include "media/base/buffers.h" | 6 #include "media/base/buffers.h" |
| 7 #include "media/base/callback.h" |
| 7 #include "media/base/filter_host.h" | 8 #include "media/base/filter_host.h" |
| 8 #include "media/base/video_frame.h" | 9 #include "media/base/video_frame.h" |
| 9 #include "media/filters/video_renderer_base.h" | 10 #include "media/filters/video_renderer_base.h" |
| 10 | 11 |
| 11 namespace media { | 12 namespace media { |
| 12 | 13 |
| 13 // Limit our read ahead to at least 3 frames. One frame is typically in flux at | 14 // Limit our read ahead to at least 3 frames. One frame is typically in flux at |
| 14 // all times, as in frame n is discarded at the top of ThreadMain() while frame | 15 // all times, as in frame n is discarded at the top of ThreadMain() while frame |
| 15 // (n + kMaxFrames) is being asynchronously fetched. The remaining two frames | 16 // (n + kMaxFrames) is being asynchronously fetched. The remaining two frames |
| 16 // allow us to advance the current frame as well as read the timestamp of the | 17 // allow us to advance the current frame as well as read the timestamp of the |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 83 return false; | 84 return false; |
| 84 if (!media_format.GetAsInteger(MediaFormat::kHeight, &height)) | 85 if (!media_format.GetAsInteger(MediaFormat::kHeight, &height)) |
| 85 return false; | 86 return false; |
| 86 if (width_out) *width_out = width; | 87 if (width_out) *width_out = width; |
| 87 if (height_out) *height_out = height; | 88 if (height_out) *height_out = height; |
| 88 return true; | 89 return true; |
| 89 } | 90 } |
| 90 | 91 |
| 91 void VideoRendererBase::Play(FilterCallback* callback) { | 92 void VideoRendererBase::Play(FilterCallback* callback) { |
| 92 AutoLock auto_lock(lock_); | 93 AutoLock auto_lock(lock_); |
| 93 DCHECK_EQ(kPaused, state_); | 94 DCHECK(kPaused == state_ || kFlushing == state_); |
| 94 scoped_ptr<FilterCallback> c(callback); | 95 scoped_ptr<FilterCallback> c(callback); |
| 95 state_ = kPlaying; | 96 state_ = kPlaying; |
| 96 callback->Run(); | 97 callback->Run(); |
| 97 } | 98 } |
| 98 | 99 |
| 99 void VideoRendererBase::Pause(FilterCallback* callback) { | 100 void VideoRendererBase::Pause(FilterCallback* callback) { |
| 100 AutoLock auto_lock(lock_); | 101 AutoLock auto_lock(lock_); |
| 101 DCHECK(state_ == kPlaying || state_ == kEnded); | 102 DCHECK(state_ == kPlaying || state_ == kEnded); |
| 102 pause_callback_.reset(callback); | 103 AutoCallbackRunner done_runner(callback); |
| 103 state_ = kPaused; | 104 state_ = kPaused; |
| 105 } |
| 104 | 106 |
| 105 // TODO(jiesun): currently we use Pause() to fulfill Flush(). | 107 void VideoRendererBase::Flush(FilterCallback* callback) { |
| 108 DCHECK(state_ == kPaused); |
| 109 |
| 110 AutoLock auto_lock(lock_); |
| 111 flush_callback_.reset(callback); |
| 112 state_ = kFlushing; |
| 113 |
| 106 // Filter is considered paused when we've finished all pending reads, which | 114 // Filter is considered paused when we've finished all pending reads, which |
| 107 // implies all buffers are returned to owner in Decoder/Renderer. Renderer | 115 // implies all buffers are returned to owner in Decoder/Renderer. Renderer |
| 108 // is considered paused with one more contingency that |pending_paint_| is | 116 // is considered paused with one more contingency that |pending_paint_| is |
| 109 // false, such that no client of us is holding any reference to VideoFrame. | 117 // false, such that no client of us is holding any reference to VideoFrame. |
| 110 if (pending_reads_ == 0 && pending_paint_ == false) { | 118 if (pending_reads_ == 0 && pending_paint_ == false) { |
| 111 pause_callback_->Run(); | 119 flush_callback_->Run(); |
| 112 pause_callback_.reset(); | 120 flush_callback_.reset(); |
| 113 FlushBuffers(); | 121 FlushBuffers(); |
| 114 } | 122 } |
| 115 } | 123 } |
| 116 | 124 |
| 117 void VideoRendererBase::Stop(FilterCallback* callback) { | 125 void VideoRendererBase::Stop(FilterCallback* callback) { |
| 118 { | 126 { |
| 119 AutoLock auto_lock(lock_); | 127 AutoLock auto_lock(lock_); |
| 120 state_ = kStopped; | 128 state_ = kStopped; |
| 121 | 129 |
| 122 // TODO(jiesun): move this to flush. | 130 // TODO(jiesun): move this to flush. |
| (...skipping 17 matching lines...) Expand all Loading... |
| 140 OnStop(callback); | 148 OnStop(callback); |
| 141 } | 149 } |
| 142 | 150 |
| 143 void VideoRendererBase::SetPlaybackRate(float playback_rate) { | 151 void VideoRendererBase::SetPlaybackRate(float playback_rate) { |
| 144 AutoLock auto_lock(lock_); | 152 AutoLock auto_lock(lock_); |
| 145 playback_rate_ = playback_rate; | 153 playback_rate_ = playback_rate; |
| 146 } | 154 } |
| 147 | 155 |
| 148 void VideoRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) { | 156 void VideoRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) { |
| 149 AutoLock auto_lock(lock_); | 157 AutoLock auto_lock(lock_); |
| 150 DCHECK_EQ(kPaused, state_); | 158 DCHECK(kPaused == state_ || kFlushing == state_); |
| 151 DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; | 159 DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; |
| 152 state_ = kSeeking; | 160 state_ = kSeeking; |
| 153 seek_callback_.reset(callback); | 161 seek_callback_.reset(callback); |
| 154 seek_timestamp_ = time; | 162 seek_timestamp_ = time; |
| 155 | 163 |
| 156 // Throw away everything and schedule our reads. | 164 // Throw away everything and schedule our reads. |
| 157 // TODO(jiesun): this should be guaranteed by pause/flush before seek happen. | 165 // TODO(jiesun): this should be guaranteed by pause/flush before seek happen. |
| 158 frames_queue_ready_.clear(); | 166 frames_queue_ready_.clear(); |
| 159 frames_queue_done_.clear(); | 167 frames_queue_done_.clear(); |
| 160 for (size_t i = 0; i < kMaxFrames; ++i) { | 168 for (size_t i = 0; i < kMaxFrames; ++i) { |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 | 243 |
| 236 for (;;) { | 244 for (;;) { |
| 237 AutoLock auto_lock(lock_); | 245 AutoLock auto_lock(lock_); |
| 238 | 246 |
| 239 const base::TimeDelta kIdleTimeDelta = | 247 const base::TimeDelta kIdleTimeDelta = |
| 240 base::TimeDelta::FromMilliseconds(kIdleMilliseconds); | 248 base::TimeDelta::FromMilliseconds(kIdleMilliseconds); |
| 241 | 249 |
| 242 if (state_ == kStopped) | 250 if (state_ == kStopped) |
| 243 return; | 251 return; |
| 244 | 252 |
| 245 if (state_ == kPaused || state_ == kSeeking || state_ == kEnded || | 253 if (state_ != kPlaying || playback_rate_ == 0) { |
| 246 playback_rate_ == 0) { | |
| 247 remaining_time = kIdleTimeDelta; | 254 remaining_time = kIdleTimeDelta; |
| 248 } else if (frames_queue_ready_.empty() || | 255 } else if (frames_queue_ready_.empty() || |
| 249 frames_queue_ready_.front()->IsEndOfStream()) { | 256 frames_queue_ready_.front()->IsEndOfStream()) { |
| 250 if (current_frame_.get()) | 257 if (current_frame_.get()) |
| 251 remaining_time = CalculateSleepDuration(NULL, playback_rate_); | 258 remaining_time = CalculateSleepDuration(NULL, playback_rate_); |
| 252 else | 259 else |
| 253 remaining_time = kIdleTimeDelta; | 260 remaining_time = kIdleTimeDelta; |
| 254 } else { | 261 } else { |
| 255 // Calculate how long until we should advance the frame, which is | 262 // Calculate how long until we should advance the frame, which is |
| 256 // typically negative but for playback rates < 1.0f may be long enough | 263 // typically negative but for playback rates < 1.0f may be long enough |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 343 DCHECK(!pending_paint_); | 350 DCHECK(!pending_paint_); |
| 344 | 351 |
| 345 if (state_ == kStopped || !current_frame_.get() || | 352 if (state_ == kStopped || !current_frame_.get() || |
| 346 current_frame_->IsEndOfStream()) { | 353 current_frame_->IsEndOfStream()) { |
| 347 *frame_out = NULL; | 354 *frame_out = NULL; |
| 348 return; | 355 return; |
| 349 } | 356 } |
| 350 | 357 |
| 351 // We should have initialized and have the current frame. | 358 // We should have initialized and have the current frame. |
| 352 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying || | 359 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying || |
| 353 state_ == kEnded); | 360 state_ == kFlushing || state_ == kEnded); |
| 354 *frame_out = current_frame_; | 361 *frame_out = current_frame_; |
| 355 pending_paint_ = true; | 362 pending_paint_ = true; |
| 356 } | 363 } |
| 357 | 364 |
| 358 void VideoRendererBase::PutCurrentFrame(scoped_refptr<VideoFrame> frame) { | 365 void VideoRendererBase::PutCurrentFrame(scoped_refptr<VideoFrame> frame) { |
| 359 AutoLock auto_lock(lock_); | 366 AutoLock auto_lock(lock_); |
| 360 | 367 |
| 361 // Note that we do not claim |pending_paint_| when we return NULL frame, in | 368 // Note that we do not claim |pending_paint_| when we return NULL frame, in |
| 362 // that case, |current_frame_| could be changed before PutCurrentFrame. | 369 // that case, |current_frame_| could be changed before PutCurrentFrame. |
| 363 DCHECK(pending_paint_ || frame.get() == NULL); | 370 DCHECK(pending_paint_ || frame.get() == NULL); |
| 364 DCHECK(current_frame_.get() == frame.get() || frame.get() == NULL); | 371 DCHECK(current_frame_.get() == frame.get() || frame.get() == NULL); |
| 365 | 372 |
| 366 pending_paint_ = false; | 373 pending_paint_ = false; |
| 367 // We had cleared the |pending_paint_| flag, there are chances that current | 374 // We had cleared the |pending_paint_| flag, there are chances that current |
| 368 // frame is timed-out. We will wake up our main thread to advance the current | 375 // frame is timed-out. We will wake up our main thread to advance the current |
| 369 // frame when this is true. | 376 // frame when this is true. |
| 370 frame_available_.Signal(); | 377 frame_available_.Signal(); |
| 371 if (state_ == kPaused && pending_reads_ == 0 && pause_callback_.get()) { | 378 if (state_ == kFlushing && pending_reads_ == 0 && flush_callback_.get()) { |
| 372 // No more pending reads! We're now officially "paused". | 379 // No more pending reads! We're now officially "paused". |
| 373 FlushBuffers(); | 380 FlushBuffers(); |
| 374 pause_callback_->Run(); | 381 flush_callback_->Run(); |
| 375 pause_callback_.reset(); | 382 flush_callback_.reset(); |
| 376 } | 383 } |
| 377 } | 384 } |
| 378 | 385 |
| 379 void VideoRendererBase::OnFillBufferDone(scoped_refptr<VideoFrame> frame) { | 386 void VideoRendererBase::OnFillBufferDone(scoped_refptr<VideoFrame> frame) { |
| 380 AutoLock auto_lock(lock_); | 387 AutoLock auto_lock(lock_); |
| 381 | 388 |
| 382 // TODO(ajwong): Work around cause we don't synchronize on stop. Correct | 389 // TODO(ajwong): Work around cause we don't synchronize on stop. Correct |
| 383 // fix is to resolve http://crbug.com/16059. | 390 // fix is to resolve http://crbug.com/16059. |
| 384 if (state_ == kStopped) { | 391 if (state_ == kStopped) { |
| 385 // TODO(jiesun): Remove this when flush before stop landed! | 392 // TODO(jiesun): Remove this when flush before stop landed! |
| 386 return; | 393 return; |
| 387 } | 394 } |
| 388 | 395 |
| 389 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying || | 396 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying || |
| 390 state_ == kEnded); | 397 state_ == kFlushing || state_ == kEnded); |
| 391 DCHECK_GT(pending_reads_, 0u); | 398 DCHECK_GT(pending_reads_, 0u); |
| 392 --pending_reads_; | 399 --pending_reads_; |
| 393 | 400 |
| 394 // Discard frames until we reach our desired seek timestamp. | 401 // Discard frames until we reach our desired seek timestamp. |
| 395 if (state_ == kSeeking && !frame->IsEndOfStream() && | 402 if (state_ == kSeeking && !frame->IsEndOfStream() && |
| 396 (frame->GetTimestamp() + frame->GetDuration()) < seek_timestamp_) { | 403 (frame->GetTimestamp() + frame->GetDuration()) < seek_timestamp_) { |
| 397 frames_queue_done_.push_back(frame); | 404 frames_queue_done_.push_back(frame); |
| 398 ScheduleRead_Locked(); | 405 ScheduleRead_Locked(); |
| 399 } else { | 406 } else { |
| 400 frames_queue_ready_.push_back(frame); | 407 frames_queue_ready_.push_back(frame); |
| 401 DCHECK_LE(frames_queue_ready_.size(), kMaxFrames); | 408 DCHECK_LE(frames_queue_ready_.size(), kMaxFrames); |
| 402 frame_available_.Signal(); | 409 frame_available_.Signal(); |
| 403 } | 410 } |
| 404 | 411 |
| 405 // Check for our preroll complete condition. | 412 // Check for our preroll complete condition. |
| 406 if (state_ == kSeeking) { | 413 if (state_ == kSeeking) { |
| 407 DCHECK(seek_callback_.get()); | 414 DCHECK(seek_callback_.get()); |
| 408 if (frames_queue_ready_.size() == kMaxFrames) { | 415 if (frames_queue_ready_.size() == kMaxFrames || frame->IsEndOfStream()) { |
| 409 // We're paused, so make sure we update |current_frame_| to represent | 416 // We're paused, so make sure we update |current_frame_| to represent |
| 410 // our new location. | 417 // our new location. |
| 411 state_ = kPaused; | 418 state_ = kPaused; |
| 412 | 419 |
| 413 // Because we might remain paused (i.e., we were not playing before we | 420 // Because we might remain paused (i.e., we were not playing before we |
| 414 // received a seek), we can't rely on ThreadMain() to notify the subclass | 421 // received a seek), we can't rely on ThreadMain() to notify the subclass |
| 415 // the frame has been updated. | 422 // the frame has been updated. |
| 416 current_frame_ = frames_queue_ready_.front(); | 423 scoped_refptr<VideoFrame> first_frame; |
| 417 frames_queue_ready_.pop_front(); | 424 first_frame = frames_queue_ready_.front(); |
| 425 if (!first_frame->IsEndOfStream()) { |
| 426 frames_queue_ready_.pop_front(); |
| 427 current_frame_ = first_frame; |
| 428 } |
| 418 OnFrameAvailable(); | 429 OnFrameAvailable(); |
| 419 | 430 |
| 420 seek_callback_->Run(); | 431 seek_callback_->Run(); |
| 421 seek_callback_.reset(); | 432 seek_callback_.reset(); |
| 422 } | 433 } |
| 423 } else if (state_ == kPaused && pending_reads_ == 0 && !pending_paint_) { | 434 } else if (state_ == kFlushing && pending_reads_ == 0 && !pending_paint_) { |
| 424 // No more pending reads! We're now officially "paused". | 435 // No more pending reads! We're now officially "paused". |
| 425 if (pause_callback_.get()) { | 436 if (flush_callback_.get()) { |
| 426 pause_callback_->Run(); | 437 flush_callback_->Run(); |
| 427 pause_callback_.reset(); | 438 flush_callback_.reset(); |
| 428 } | 439 } |
| 429 } | 440 } |
| 430 } | 441 } |
| 431 | 442 |
| 432 void VideoRendererBase::ScheduleRead_Locked() { | 443 void VideoRendererBase::ScheduleRead_Locked() { |
| 433 lock_.AssertAcquired(); | 444 lock_.AssertAcquired(); |
| 434 DCHECK_NE(kEnded, state_); | 445 DCHECK_NE(kEnded, state_); |
| 435 // TODO(jiesun): We use dummy buffer to feed decoder to let decoder to | 446 // TODO(jiesun): We use dummy buffer to feed decoder to let decoder to |
| 436 // provide buffer pools. In the future, we may want to implement real | 447 // provide buffer pools. In the future, we may want to implement real |
| 437 // buffer pool to recycle buffers. | 448 // buffer pool to recycle buffers. |
| 438 while (!frames_queue_done_.empty()) { | 449 while (!frames_queue_done_.empty()) { |
| 439 scoped_refptr<VideoFrame> video_frame = frames_queue_done_.front(); | 450 scoped_refptr<VideoFrame> video_frame = frames_queue_done_.front(); |
| 440 frames_queue_done_.pop_front(); | 451 frames_queue_done_.pop_front(); |
| 441 decoder_->FillThisBuffer(video_frame); | 452 decoder_->FillThisBuffer(video_frame); |
| 442 DCHECK_LT(pending_reads_, kMaxFrames); | 453 DCHECK_LT(pending_reads_, kMaxFrames); |
| 443 ++pending_reads_; | 454 ++pending_reads_; |
| 444 } | 455 } |
| 445 } | 456 } |
| 446 | 457 |
| 447 void VideoRendererBase::FlushBuffers() { | 458 void VideoRendererBase::FlushBuffers() { |
| 448 DCHECK(!pending_paint_); | 459 DCHECK(!pending_paint_); |
| 449 | 460 |
| 450 // We should never put EOF frame into "done queue". | 461 // We should never put EOF frame into "done queue". |
| 451 while (!frames_queue_ready_.empty()) { | 462 while (!frames_queue_ready_.empty()) { |
| 452 scoped_refptr<VideoFrame> video_frame = frames_queue_ready_.front(); | 463 scoped_refptr<VideoFrame> video_frame = frames_queue_ready_.front(); |
| 453 if (video_frame->IsEndOfStream()) { | 464 if (!video_frame->IsEndOfStream()) { |
| 454 frames_queue_done_.push_back(video_frame); | 465 frames_queue_done_.push_back(video_frame); |
| 455 } | 466 } |
| 456 frames_queue_ready_.pop_front(); | 467 frames_queue_ready_.pop_front(); |
| 457 } | 468 } |
| 458 if (current_frame_.get() && !current_frame_->IsEndOfStream()) { | 469 if (current_frame_.get() && !current_frame_->IsEndOfStream()) { |
| 459 frames_queue_done_.push_back(current_frame_); | 470 frames_queue_done_.push_back(current_frame_); |
| 460 } | 471 } |
| 461 current_frame_ = NULL; | 472 current_frame_ = NULL; |
| 462 } | 473 } |
| 463 | 474 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 484 previous_time_ = now; | 495 previous_time_ = now; |
| 485 } | 496 } |
| 486 | 497 |
| 487 // Scale our sleep based on the playback rate. | 498 // Scale our sleep based on the playback rate. |
| 488 // TODO(scherkus): floating point badness and degrade gracefully. | 499 // TODO(scherkus): floating point badness and degrade gracefully. |
| 489 return base::TimeDelta::FromMicroseconds( | 500 return base::TimeDelta::FromMicroseconds( |
| 490 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); | 501 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); |
| 491 } | 502 } |
| 492 | 503 |
| 493 } // namespace media | 504 } // namespace media |
| OLD | NEW |