| 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 read_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 thread_to_join = 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_ != base::kNullThreadHandle) { |
| 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 thread_to_join = thread_; |
| 83 thread_ = base::kNullThreadHandle; | 84 thread_ = base::kNullThreadHandle; |
| 84 } | 85 } |
| 85 } | 86 } |
| 86 if (old_thread_handle) | 87 if (thread_to_join != base::kNullThreadHandle) |
| 87 base::PlatformThread::Join(old_thread_handle); | 88 base::PlatformThread::Join(thread_to_join); |
| 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 } |
| 97 | 98 |
| 98 void VideoRendererBase::Seek(base::TimeDelta time, const FilterStatusCB& cb) { | 99 void VideoRendererBase::Seek(base::TimeDelta time, const FilterStatusCB& cb) { |
| 99 bool run_callback = false; | |
| 100 | |
| 101 { | 100 { |
| 102 base::AutoLock auto_lock(lock_); | 101 base::AutoLock auto_lock(lock_); |
| 103 // There is a race condition between filters to receive SeekTask(). | 102 DCHECK_EQ(state_, kFlushed) << "Must flush prior to seeking."; |
| 104 // It turns out we could receive buffer from decoder before seek() | |
| 105 // is called on us. so we do the following: | |
| 106 // kFlushed => ( Receive first buffer or Seek() ) => kSeeking and | |
| 107 // kSeeking => ( Receive enough buffers) => kPrerolled. ) | |
| 108 DCHECK(state_ == kPrerolled || state_ == kFlushed || state_ == kSeeking); | |
| 109 DCHECK(!cb.is_null()); | 103 DCHECK(!cb.is_null()); |
| 110 DCHECK(seek_cb_.is_null()); | 104 DCHECK(seek_cb_.is_null()); |
| 111 | 105 |
| 112 if (state_ == kPrerolled) { | 106 state_ = kSeeking; |
| 113 // Already get enough buffers from decoder. | 107 seek_cb_ = cb; |
| 114 run_callback = true; | |
| 115 } else { | |
| 116 // Otherwise we are either kFlushed or kSeeking, but without enough | |
| 117 // buffers we should save the callback function and call it later. | |
| 118 state_ = kSeeking; | |
| 119 seek_cb_ = cb; | |
| 120 } | |
| 121 | |
| 122 seek_timestamp_ = time; | 108 seek_timestamp_ = time; |
| 123 ScheduleRead_Locked(); | 109 AttemptRead_Locked(); |
| 124 } | 110 } |
| 125 | |
| 126 if (run_callback) | |
| 127 cb.Run(PIPELINE_OK); | |
| 128 } | 111 } |
| 129 | 112 |
| 130 void VideoRendererBase::Initialize(VideoDecoder* decoder, | 113 void VideoRendererBase::Initialize(VideoDecoder* decoder, |
| 131 const base::Closure& callback, | 114 const base::Closure& callback, |
| 132 const StatisticsCallback& stats_callback) { | 115 const StatisticsCallback& stats_callback) { |
| 133 base::AutoLock auto_lock(lock_); | 116 base::AutoLock auto_lock(lock_); |
| 134 DCHECK(decoder); | 117 DCHECK(decoder); |
| 135 DCHECK(!callback.is_null()); | 118 DCHECK(!callback.is_null()); |
| 136 DCHECK(!stats_callback.is_null()); | 119 DCHECK(!stats_callback.is_null()); |
| 137 DCHECK_EQ(kUninitialized, state_); | 120 DCHECK_EQ(kUninitialized, state_); |
| 138 decoder_ = decoder; | 121 decoder_ = decoder; |
| 139 | 122 |
| 140 statistics_callback_ = stats_callback; | 123 statistics_callback_ = stats_callback; |
| 141 | 124 |
| 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. | 125 // Notify the pipeline of the video dimensions. |
| 147 host()->SetNaturalVideoSize(decoder_->natural_size()); | 126 host()->SetNaturalVideoSize(decoder_->natural_size()); |
| 148 | 127 |
| 149 // Initialize the subclass. | 128 // Initialize the subclass. |
| 150 // TODO(scherkus): do we trust subclasses not to do something silly while | 129 // TODO(scherkus): do we trust subclasses not to do something silly while |
| 151 // we're holding the lock? | 130 // we're holding the lock? |
| 152 if (!OnInitialize(decoder)) { | 131 if (!OnInitialize(decoder)) { |
| 153 EnterErrorState_Locked(PIPELINE_ERROR_INITIALIZATION_FAILED); | 132 state_ = kError; |
| 133 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); |
| 154 callback.Run(); | 134 callback.Run(); |
| 155 return; | 135 return; |
| 156 } | 136 } |
| 157 | 137 |
| 158 // We're all good! Consider ourselves flushed. (ThreadMain() should never | 138 // We're all good! Consider ourselves flushed. (ThreadMain() should never |
| 159 // see us in the kUninitialized state). | 139 // see us in the kUninitialized state). |
| 160 // Since we had an initial Seek, we consider ourself flushed, because we | 140 // Since we had an initial Seek, we consider ourself flushed, because we |
| 161 // have not populated any buffers yet. | 141 // have not populated any buffers yet. |
| 162 state_ = kFlushed; | 142 state_ = kFlushed; |
| 163 | 143 |
| 164 // Create our video thread. | 144 // Create our video thread. |
| 165 if (!base::PlatformThread::Create(0, this, &thread_)) { | 145 if (!base::PlatformThread::Create(0, this, &thread_)) { |
| 166 NOTREACHED() << "Video thread creation failed"; | 146 NOTREACHED() << "Video thread creation failed"; |
| 167 EnterErrorState_Locked(PIPELINE_ERROR_INITIALIZATION_FAILED); | 147 state_ = kError; |
| 148 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); |
| 168 callback.Run(); | 149 callback.Run(); |
| 169 return; | 150 return; |
| 170 } | 151 } |
| 171 | 152 |
| 172 #if defined(OS_WIN) | 153 #if defined(OS_WIN) |
| 173 // Bump up our priority so our sleeping is more accurate. | 154 // Bump up our priority so our sleeping is more accurate. |
| 174 // TODO(scherkus): find out if this is necessary, but it seems to help. | 155 // TODO(scherkus): find out if this is necessary, but it seems to help. |
| 175 ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); | 156 ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); |
| 176 #endif // defined(OS_WIN) | 157 #endif // defined(OS_WIN) |
| 177 callback.Run(); | 158 callback.Run(); |
| (...skipping 84 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 | 243 // exceptions that cause us to drop a frame and/or consider painting a |
| 263 // "next" frame. | 244 // "next" frame. |
| 264 if (next_frame->GetTimestamp() > host()->GetTime() + kIdleTimeDelta && | 245 if (next_frame->GetTimestamp() > host()->GetTime() + kIdleTimeDelta && |
| 265 current_frame_ && | 246 current_frame_ && |
| 266 current_frame_->GetTimestamp() <= host()->GetDuration()) { | 247 current_frame_->GetTimestamp() <= host()->GetDuration()) { |
| 267 continue; | 248 continue; |
| 268 } | 249 } |
| 269 | 250 |
| 270 // If we got here then: | 251 // If we got here then: |
| 271 // 1. next frame's timestamp is already current; or | 252 // 1. next frame's timestamp is already current; or |
| 272 // 2. we do not have any current frame yet anyway; or | 253 // 2. we do not have a current frame yet; or |
| 273 // 3. a special case when the stream is badly formatted and | 254 // 3. a special case when the stream is badly formatted and |
| 274 // we got a frame with timestamp greater than overall duration. | 255 // we got a frame with timestamp greater than overall duration. |
| 275 // In this case we should proceed anyway and try to obtain the | 256 // In this case we should proceed anyway and try to obtain the |
| 276 // end-of-stream packet. | 257 // end-of-stream packet. |
| 277 | 258 |
| 278 if (pending_paint_) { | 259 if (pending_paint_) { |
| 279 // The pending paint might be really slow. Check if we have any frames | 260 // The pending paint might be really slow. Check if we have any frames |
| 280 // available that we can drop if they've already expired. | 261 // available that we can drop if they've already expired. |
| 281 while (!frames_queue_ready_.empty()) { | 262 while (!frames_queue_ready_.empty()) { |
| 282 // Can't drop anything if we're at the end. | 263 // Can't drop anything if we're at the end. |
| 283 if (frames_queue_ready_.front()->IsEndOfStream()) | 264 if (frames_queue_ready_.front()->IsEndOfStream()) |
| 284 break; | 265 break; |
| 285 | 266 |
| 286 base::TimeDelta remaining_time = | 267 base::TimeDelta remaining_time = |
| 287 frames_queue_ready_.front()->GetTimestamp() - host()->GetTime(); | 268 frames_queue_ready_.front()->GetTimestamp() - host()->GetTime(); |
| 288 | 269 |
| 289 // Still a chance we can render the frame! | 270 // Still a chance we can render the frame! |
| 290 if (remaining_time.InMicroseconds() > 0) | 271 if (remaining_time.InMicroseconds() > 0) |
| 291 break; | 272 break; |
| 292 | 273 |
| 293 // Frame dropped: transfer ready frame into done queue and read again. | 274 // Frame dropped: read again. |
| 294 frames_queue_done_.push_back(frames_queue_ready_.front()); | 275 ++frames_dropped; |
| 295 frames_queue_ready_.pop_front(); | 276 frames_queue_ready_.pop_front(); |
| 296 ScheduleRead_Locked(); | 277 AttemptRead_Locked(); |
| 297 ++frames_dropped; | |
| 298 } | 278 } |
| 299 | 279 |
| 300 // Continue waiting for the current paint to finish. | 280 // Continue waiting for the current paint to finish. |
| 301 continue; | 281 continue; |
| 302 } | 282 } |
| 303 | 283 |
| 304 // Congratulations! You've made it past the video frame timing gauntlet. | 284 // Congratulations! You've made it past the video frame timing gauntlet. |
| 305 // | 285 // |
| 306 // We can now safely update the current frame, request another frame, and | 286 // We can now safely update the current frame, request another frame, and |
| 307 // signal to the client that a new frame is available. | 287 // 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(); | 288 current_frame_ = frames_queue_ready_.front(); |
| 310 frames_queue_ready_.pop_front(); | 289 frames_queue_ready_.pop_front(); |
| 311 ScheduleRead_Locked(); | 290 AttemptRead_Locked(); |
| 312 | 291 |
| 313 base::AutoUnlock auto_unlock(lock_); | 292 base::AutoUnlock auto_unlock(lock_); |
| 314 OnFrameAvailable(); | 293 OnFrameAvailable(); |
| 315 } | 294 } |
| 316 } | 295 } |
| 317 | 296 |
| 318 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { | 297 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { |
| 319 base::AutoLock auto_lock(lock_); | 298 base::AutoLock auto_lock(lock_); |
| 320 DCHECK(!pending_paint_ && !pending_paint_with_last_available_); | 299 DCHECK(!pending_paint_ && !pending_paint_with_last_available_); |
| 321 | 300 |
| 322 if ((!current_frame_ || current_frame_->IsEndOfStream()) && | 301 if ((!current_frame_ || current_frame_->IsEndOfStream()) && |
| 323 (!last_available_frame_ || | 302 (!last_available_frame_ || last_available_frame_->IsEndOfStream())) { |
| 324 last_available_frame_->IsEndOfStream())) { | |
| 325 *frame_out = NULL; | 303 *frame_out = NULL; |
| 326 return; | 304 return; |
| 327 } | 305 } |
| 328 | 306 |
| 329 // We should have initialized and have the current frame. | 307 // We should have initialized and have the current frame. |
| 330 DCHECK_NE(state_, kUninitialized); | 308 DCHECK_NE(state_, kUninitialized); |
| 331 DCHECK_NE(state_, kStopped); | 309 DCHECK_NE(state_, kStopped); |
| 332 DCHECK_NE(state_, kError); | 310 DCHECK_NE(state_, kError); |
| 333 | 311 |
| 334 if (current_frame_) { | 312 if (current_frame_) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 357 pending_paint_with_last_available_ = false; | 335 pending_paint_with_last_available_ = false; |
| 358 } else { | 336 } else { |
| 359 DCHECK(!frame); | 337 DCHECK(!frame); |
| 360 } | 338 } |
| 361 | 339 |
| 362 // We had cleared the |pending_paint_| flag, there are chances that current | 340 // 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 | 341 // frame is timed-out. We will wake up our main thread to advance the current |
| 364 // frame when this is true. | 342 // frame when this is true. |
| 365 frame_available_.Signal(); | 343 frame_available_.Signal(); |
| 366 if (state_ == kFlushing) { | 344 if (state_ == kFlushing) { |
| 367 FlushBuffers_Locked(); | 345 AttemptFlush_Locked(); |
| 368 } else if (state_ == kError || state_ == kStopped) { | 346 } else if (state_ == kError || state_ == kStopped) { |
| 369 DoStopOrErrorFlush_Locked(); | 347 DoStopOrError_Locked(); |
| 370 } | 348 } |
| 371 } | 349 } |
| 372 | 350 |
| 373 void VideoRendererBase::ConsumeVideoFrame(scoped_refptr<VideoFrame> frame) { | 351 void VideoRendererBase::FrameReady(scoped_refptr<VideoFrame> frame) { |
| 374 if (frame) { | 352 DCHECK(frame); |
| 375 PipelineStatistics statistics; | |
| 376 statistics.video_frames_decoded = 1; | |
| 377 statistics_callback_.Run(statistics); | |
| 378 } | |
| 379 | 353 |
| 380 base::AutoLock auto_lock(lock_); | 354 base::AutoLock auto_lock(lock_); |
| 381 | |
| 382 if (!frame) { | |
| 383 EnterErrorState_Locked(PIPELINE_ERROR_DECODE); | |
| 384 return; | |
| 385 } | |
| 386 | |
| 387 // Decoder could reach seek state before our Seek() get called. | |
| 388 // We will enter kSeeking | |
| 389 if (state_ == kFlushed) | |
| 390 state_ = kSeeking; | |
| 391 | |
| 392 // Synchronous flush between filters should prevent this from happening. | |
| 393 DCHECK_NE(state_, kStopped); | |
| 394 if (frame && !frame->IsEndOfStream()) | |
| 395 --pending_reads_; | |
| 396 | |
| 397 DCHECK_NE(state_, kUninitialized); | 355 DCHECK_NE(state_, kUninitialized); |
| 398 DCHECK_NE(state_, kStopped); | 356 DCHECK_NE(state_, kStopped); |
| 399 DCHECK_NE(state_, kError); | 357 DCHECK_NE(state_, kError); |
| 358 DCHECK_NE(state_, kFlushed); |
| 359 CHECK(pending_read_); |
| 400 | 360 |
| 401 if (state_ == kPaused || state_ == kFlushing) { | 361 pending_read_ = false; |
| 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 | 362 |
| 408 // Excluding kPause here, because in pause state, we will never | 363 if (state_ == kFlushing) { |
| 409 // transfer out-bounding buffer. We do not flush buffer when Compositor | 364 AttemptFlush_Locked(); |
| 410 // hold reference to our video frame either. | |
| 411 if (state_ == kFlushing && pending_paint_ == false) | |
| 412 FlushBuffers_Locked(); | |
| 413 | |
| 414 return; | 365 return; |
| 415 } | 366 } |
| 416 | 367 |
| 417 // Discard frames until we reach our desired seek timestamp. | 368 // Discard frames until we reach our desired seek timestamp. |
| 418 if (state_ == kSeeking && !frame->IsEndOfStream() && | 369 if (state_ == kSeeking && !frame->IsEndOfStream() && |
| 419 (frame->GetTimestamp() + frame->GetDuration()) <= seek_timestamp_) { | 370 (frame->GetTimestamp() + frame->GetDuration()) <= seek_timestamp_) { |
| 420 frames_queue_done_.push_back(frame); | 371 AttemptRead_Locked(); |
| 421 ScheduleRead_Locked(); | 372 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 } | 373 } |
| 428 | 374 |
| 429 // Check for our preroll complete condition. | 375 // This one's a keeper! Place it in the ready queue. |
| 430 bool new_frame_available = false; | 376 frames_queue_ready_.push_back(frame); |
| 431 if (state_ == kSeeking) { | 377 DCHECK_LE(frames_queue_ready_.size(), |
| 432 if (frames_queue_ready_.size() == Limits::kMaxVideoFrames || | 378 static_cast<size_t>(Limits::kMaxVideoFrames)); |
| 433 frame->IsEndOfStream()) { | 379 frame_available_.Signal(); |
| 434 // We're paused, so make sure we update |current_frame_| to represent | |
| 435 // our new location. | |
| 436 state_ = kPrerolled; | |
| 437 | 380 |
| 438 // Because we might remain paused (i.e., we were not playing before we | 381 PipelineStatistics statistics; |
| 439 // received a seek), we can't rely on ThreadMain() to notify the subclass | 382 statistics.video_frames_decoded = 1; |
| 440 // the frame has been updated. | 383 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 | 384 |
| 449 // If we reach prerolled state before Seek() is called by pipeline, | 385 // Always request more decoded video if we have capacity. This serves two |
| 450 // |seek_callback_| is not set, we will return immediately during | 386 // purposes: |
| 451 // when Seek() is eventually called. | 387 // 1) Prerolling while paused |
| 452 if (!seek_cb_.is_null()) { | 388 // 2) Keeps decoding going if video rendering thread starts falling behind |
| 453 ResetAndRunCB(&seek_cb_, PIPELINE_OK); | 389 if (frames_queue_ready_.size() < Limits::kMaxVideoFrames && |
| 454 } | 390 !frame->IsEndOfStream()) { |
| 455 } | 391 AttemptRead_Locked(); |
| 456 } else if (state_ == kFlushing && pending_reads_ == 0 && !pending_paint_) { | 392 return; |
| 457 OnFlushDone_Locked(); | |
| 458 } | 393 } |
| 459 | 394 |
| 460 if (new_frame_available) { | 395 // If we're at capacity or end of stream while seeking we need to transition |
| 461 base::AutoUnlock auto_unlock(lock_); | 396 // to prerolled. |
| 397 if (state_ == kSeeking) { |
| 398 state_ = kPrerolled; |
| 399 |
| 400 // Because we might remain in the prerolled state for an undetermined amount |
| 401 // of time (i.e., we were not playing before we received a seek), we'll |
| 402 // manually update the current frame and notify the subclass below. |
| 403 if (!frames_queue_ready_.front()->IsEndOfStream()) { |
| 404 current_frame_ = frames_queue_ready_.front(); |
| 405 frames_queue_ready_.pop_front(); |
| 406 } |
| 407 |
| 408 // ...and we're done seeking! |
| 409 DCHECK(!seek_cb_.is_null()); |
| 410 ResetAndRunCB(&seek_cb_, PIPELINE_OK); |
| 411 |
| 412 base::AutoUnlock ul(lock_); |
| 462 OnFrameAvailable(); | 413 OnFrameAvailable(); |
| 463 } | 414 } |
| 464 } | 415 } |
| 465 | 416 |
| 466 void VideoRendererBase::ReadInput(scoped_refptr<VideoFrame> frame) { | 417 void VideoRendererBase::AttemptRead_Locked() { |
| 467 // We should never return empty frames or EOS frame. | 418 lock_.AssertAcquired(); |
| 468 DCHECK(frame && !frame->IsEndOfStream()); | 419 DCHECK_NE(kEnded, state_); |
| 469 | 420 |
| 470 decoder_->ProduceVideoFrame(frame); | 421 if (pending_read_ || frames_queue_ready_.size() == Limits::kMaxVideoFrames) { |
| 471 ++pending_reads_; | 422 return; |
| 423 } |
| 424 |
| 425 pending_read_ = true; |
| 426 decoder_->Read(read_cb_); |
| 472 } | 427 } |
| 473 | 428 |
| 474 void VideoRendererBase::ScheduleRead_Locked() { | 429 void VideoRendererBase::AttemptFlush_Locked() { |
| 475 lock_.AssertAcquired(); | 430 lock_.AssertAcquired(); |
| 476 DCHECK_NE(kEnded, state_); | 431 DCHECK_EQ(kFlushing, state_); |
| 477 // TODO(jiesun): We use dummy buffer to feed decoder to let decoder to | 432 |
| 478 // provide buffer pools. In the future, we may want to implement real | 433 // Get rid of any ready frames. |
| 479 // buffer pool to recycle buffers. | 434 while (!frames_queue_ready_.empty()) { |
| 480 while (!frames_queue_done_.empty()) { | 435 frames_queue_ready_.pop_front(); |
| 481 scoped_refptr<VideoFrame> video_frame = frames_queue_done_.front(); | 436 } |
| 482 frames_queue_done_.pop_front(); | 437 |
| 483 ReadInput(video_frame); | 438 if (!pending_paint_ && !pending_read_) { |
| 439 state_ = kFlushed; |
| 440 current_frame_ = NULL; |
| 441 ResetAndRunCB(&flush_callback_); |
| 484 } | 442 } |
| 485 } | 443 } |
| 486 | 444 |
| 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( | 445 base::TimeDelta VideoRendererBase::CalculateSleepDuration( |
| 525 VideoFrame* next_frame, float playback_rate) { | 446 VideoFrame* next_frame, float playback_rate) { |
| 526 // Determine the current and next presentation timestamps. | 447 // Determine the current and next presentation timestamps. |
| 527 base::TimeDelta now = host()->GetTime(); | 448 base::TimeDelta now = host()->GetTime(); |
| 528 base::TimeDelta this_pts = current_frame_->GetTimestamp(); | 449 base::TimeDelta this_pts = current_frame_->GetTimestamp(); |
| 529 base::TimeDelta next_pts; | 450 base::TimeDelta next_pts; |
| 530 if (next_frame) { | 451 if (next_frame) { |
| 531 next_pts = next_frame->GetTimestamp(); | 452 next_pts = next_frame->GetTimestamp(); |
| 532 } else { | 453 } else { |
| 533 next_pts = this_pts + current_frame_->GetDuration(); | 454 next_pts = this_pts + current_frame_->GetDuration(); |
| 534 } | 455 } |
| 535 | 456 |
| 536 // Determine our sleep duration based on whether time advanced. | 457 // Determine our sleep duration based on whether time advanced. |
| 537 base::TimeDelta sleep; | 458 base::TimeDelta sleep; |
| 538 if (now == previous_time_) { | 459 if (now == previous_time_) { |
| 539 // Time has not changed, assume we sleep for the frame's duration. | 460 // Time has not changed, assume we sleep for the frame's duration. |
| 540 sleep = next_pts - this_pts; | 461 sleep = next_pts - this_pts; |
| 541 } else { | 462 } else { |
| 542 // Time has changed, figure out real sleep duration. | 463 // Time has changed, figure out real sleep duration. |
| 543 sleep = next_pts - now; | 464 sleep = next_pts - now; |
| 544 previous_time_ = now; | 465 previous_time_ = now; |
| 545 } | 466 } |
| 546 | 467 |
| 547 // Scale our sleep based on the playback rate. | 468 // Scale our sleep based on the playback rate. |
| 548 // TODO(scherkus): floating point badness and degrade gracefully. | 469 // TODO(scherkus): floating point badness and degrade gracefully. |
| 549 return base::TimeDelta::FromMicroseconds( | 470 return base::TimeDelta::FromMicroseconds( |
| 550 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); | 471 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); |
| 551 } | 472 } |
| 552 | 473 |
| 553 void VideoRendererBase::EnterErrorState_Locked(PipelineStatus status) { | 474 void VideoRendererBase::DoStopOrError_Locked() { |
| 554 lock_.AssertAcquired(); | |
| 555 | |
| 556 base::Closure callback; | |
| 557 State old_state = state_; | |
| 558 state_ = kError; | |
| 559 | |
| 560 // 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. | |
| 562 if (!pending_paint_ && !pending_paint_with_last_available_) | |
| 563 DoStopOrErrorFlush_Locked(); | |
| 564 | |
| 565 switch (old_state) { | |
| 566 case kUninitialized: | |
| 567 case kPrerolled: | |
| 568 case kPaused: | |
| 569 case kFlushed: | |
| 570 case kEnded: | |
| 571 case kPlaying: | |
| 572 break; | |
| 573 | |
| 574 case kFlushing: | |
| 575 CHECK(!flush_callback_.is_null()); | |
| 576 std::swap(callback, flush_callback_); | |
| 577 break; | |
| 578 | |
| 579 case kSeeking: | |
| 580 CHECK(!seek_cb_.is_null()); | |
| 581 ResetAndRunCB(&seek_cb_, status); | |
| 582 return; | |
| 583 break; | |
| 584 | |
| 585 case kStopped: | |
| 586 NOTREACHED() << "Should not error if stopped."; | |
| 587 return; | |
| 588 | |
| 589 case kError: | |
| 590 return; | |
| 591 } | |
| 592 | |
| 593 host()->SetError(status); | |
| 594 | |
| 595 if (!callback.is_null()) | |
| 596 callback.Run(); | |
| 597 } | |
| 598 | |
| 599 void VideoRendererBase::DoStopOrErrorFlush_Locked() { | |
| 600 DCHECK(!pending_paint_); | 475 DCHECK(!pending_paint_); |
| 601 DCHECK(!pending_paint_with_last_available_); | 476 DCHECK(!pending_paint_with_last_available_); |
| 602 lock_.AssertAcquired(); | 477 lock_.AssertAcquired(); |
| 603 FlushBuffers_Locked(); | |
| 604 last_available_frame_ = NULL; | 478 last_available_frame_ = NULL; |
| 605 DCHECK_EQ(pending_reads_, 0); | 479 DCHECK(!pending_read_); |
| 606 } | 480 } |
| 607 | 481 |
| 608 } // namespace media | 482 } // namespace media |
| OLD | NEW |