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/callback.h" | 9 #include "media/base/callback.h" |
10 #include "media/base/filter_host.h" | 10 #include "media/base/filter_host.h" |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
51 } | 51 } |
52 | 52 |
53 void VideoRendererBase::Pause(FilterCallback* callback) { | 53 void VideoRendererBase::Pause(FilterCallback* callback) { |
54 base::AutoLock auto_lock(lock_); | 54 base::AutoLock auto_lock(lock_); |
55 DCHECK(state_ != kUninitialized || state_ == kError); | 55 DCHECK(state_ != kUninitialized || state_ == kError); |
56 AutoCallbackRunner done_runner(callback); | 56 AutoCallbackRunner done_runner(callback); |
57 state_ = kPaused; | 57 state_ = kPaused; |
58 } | 58 } |
59 | 59 |
60 void VideoRendererBase::Flush(FilterCallback* callback) { | 60 void VideoRendererBase::Flush(FilterCallback* callback) { |
| 61 base::AutoLock auto_lock(lock_); |
61 DCHECK_EQ(state_, kPaused); | 62 DCHECK_EQ(state_, kPaused); |
62 | |
63 base::AutoLock auto_lock(lock_); | |
64 flush_callback_.reset(callback); | 63 flush_callback_.reset(callback); |
65 state_ = kFlushing; | 64 state_ = kFlushing; |
66 | 65 |
67 if (pending_paint_ == false) | 66 if (!pending_paint_) |
68 FlushBuffers(); | 67 FlushBuffers_Locked(); |
69 } | 68 } |
70 | 69 |
71 void VideoRendererBase::Stop(FilterCallback* callback) { | 70 void VideoRendererBase::Stop(FilterCallback* callback) { |
72 base::PlatformThreadHandle old_thread_handle = base::kNullThreadHandle; | 71 base::PlatformThreadHandle old_thread_handle = base::kNullThreadHandle; |
73 { | 72 { |
74 base::AutoLock auto_lock(lock_); | 73 base::AutoLock auto_lock(lock_); |
75 state_ = kStopped; | 74 state_ = kStopped; |
76 | 75 |
77 if (!pending_paint_ && !pending_paint_with_last_available_) | 76 if (!pending_paint_ && !pending_paint_with_last_available_) |
78 DoStopOrErrorFlush_Locked(); | 77 DoStopOrErrorFlush_Locked(); |
(...skipping 22 matching lines...) Expand all Loading... |
101 void VideoRendererBase::Seek(base::TimeDelta time, const FilterStatusCB& cb) { | 100 void VideoRendererBase::Seek(base::TimeDelta time, const FilterStatusCB& cb) { |
102 bool run_callback = false; | 101 bool run_callback = false; |
103 | 102 |
104 { | 103 { |
105 base::AutoLock auto_lock(lock_); | 104 base::AutoLock auto_lock(lock_); |
106 // There is a race condition between filters to receive SeekTask(). | 105 // There is a race condition between filters to receive SeekTask(). |
107 // It turns out we could receive buffer from decoder before seek() | 106 // It turns out we could receive buffer from decoder before seek() |
108 // is called on us. so we do the following: | 107 // is called on us. so we do the following: |
109 // kFlushed => ( Receive first buffer or Seek() ) => kSeeking and | 108 // kFlushed => ( Receive first buffer or Seek() ) => kSeeking and |
110 // kSeeking => ( Receive enough buffers) => kPrerolled. ) | 109 // kSeeking => ( Receive enough buffers) => kPrerolled. ) |
111 DCHECK(kPrerolled == state_ || kFlushed == state_ || kSeeking == state_); | 110 DCHECK(state_ == kPrerolled || state_ == kFlushed || state_ == kSeeking); |
112 DCHECK(!cb.is_null()); | 111 DCHECK(!cb.is_null()); |
113 DCHECK(seek_cb_.is_null()); | 112 DCHECK(seek_cb_.is_null()); |
114 | 113 |
115 if (state_ == kPrerolled) { | 114 if (state_ == kPrerolled) { |
116 // Already get enough buffers from decoder. | 115 // Already get enough buffers from decoder. |
117 run_callback = true; | 116 run_callback = true; |
118 | 117 |
119 } else { | 118 } else { |
120 // Otherwise we are either kFlushed or kSeeking, but without enough | 119 // Otherwise we are either kFlushed or kSeeking, but without enough |
121 // buffers we should save the callback function and call it later. | 120 // buffers we should save the callback function and call it later. |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
254 continue; | 253 continue; |
255 | 254 |
256 scoped_refptr<VideoFrame> next_frame = frames_queue_ready_.front(); | 255 scoped_refptr<VideoFrame> next_frame = frames_queue_ready_.front(); |
257 if (next_frame->IsEndOfStream()) { | 256 if (next_frame->IsEndOfStream()) { |
258 state_ = kEnded; | 257 state_ = kEnded; |
259 DVLOG(1) << "Video render gets EOS"; | 258 DVLOG(1) << "Video render gets EOS"; |
260 host()->NotifyEnded(); | 259 host()->NotifyEnded(); |
261 continue; | 260 continue; |
262 } | 261 } |
263 | 262 |
264 if (next_frame->GetTimestamp() <= host()->GetTime() + kIdleTimeDelta || | 263 // Normally we're ready to loop again at this point, but there are |
265 current_frame_.get() == NULL || | 264 // exceptions that cause us to drop a frame and/or consider painting a |
266 current_frame_->GetTimestamp() > host()->GetDuration()) { | 265 // "next" frame. |
267 // 1. either next frame's time-stamp is already current. | 266 if (next_frame->GetTimestamp() > host()->GetTime() + kIdleTimeDelta && |
268 // 2. or we do not have any current frame yet anyway. | 267 current_frame_ && |
269 // 3. or a special case when the stream is badly formatted that | 268 current_frame_->GetTimestamp() <= host()->GetDuration()) { |
270 // we get video frame with time-stamp greater than the duration. | 269 continue; |
271 // In this case we should proceed anyway and try to obtain the | 270 } |
272 // end-of-stream packet. | 271 |
273 scoped_refptr<VideoFrame> timeout_frame; | 272 // If we got here then: |
274 bool new_frame_available = false; | 273 // 1. next frame's timestamp is already current; or |
275 if (!pending_paint_) { | 274 // 2. we do not have any current frame yet anyway; or |
276 // In this case, current frame could be recycled. | 275 // 3. a special case when the stream is badly formatted and |
277 timeout_frame = current_frame_; | 276 // we got a frame with timestamp greater than overall duration. |
278 current_frame_ = frames_queue_ready_.front(); | 277 // In this case we should proceed anyway and try to obtain the |
| 278 // end-of-stream packet. |
| 279 scoped_refptr<VideoFrame> timeout_frame; |
| 280 bool new_frame_available = false; |
| 281 if (!pending_paint_) { |
| 282 // In this case, current frame could be recycled. |
| 283 timeout_frame = current_frame_; |
| 284 current_frame_ = frames_queue_ready_.front(); |
| 285 frames_queue_ready_.pop_front(); |
| 286 new_frame_available = true; |
| 287 } else if (pending_paint_ && frames_queue_ready_.size() >= 2 && |
| 288 !frames_queue_ready_[1]->IsEndOfStream()) { |
| 289 // Painter could be really slow, therefore we had to skip frames if |
| 290 // 1. We had not reached end of stream. |
| 291 // 2. The next frame of current frame is also out-dated. |
| 292 base::TimeDelta next_remaining_time = |
| 293 frames_queue_ready_[1]->GetTimestamp() - host()->GetTime(); |
| 294 if (next_remaining_time.InMicroseconds() <= 0) { |
| 295 // Since the current frame is still hold by painter/compositor, and |
| 296 // the next frame is already timed-out, we should skip the next frame |
| 297 // which is the first frame in the queue. |
| 298 timeout_frame = frames_queue_ready_.front(); |
279 frames_queue_ready_.pop_front(); | 299 frames_queue_ready_.pop_front(); |
280 new_frame_available = true; | 300 ++frames_dropped; |
281 } else if (pending_paint_ && frames_queue_ready_.size() >= 2 && | |
282 !frames_queue_ready_[1]->IsEndOfStream()) { | |
283 // Painter could be really slow, therefore we had to skip frames if | |
284 // 1. We had not reached end of stream. | |
285 // 2. The next frame of current frame is also out-dated. | |
286 base::TimeDelta next_remaining_time = | |
287 frames_queue_ready_[1]->GetTimestamp() - host()->GetTime(); | |
288 if (next_remaining_time.InMicroseconds() <= 0) { | |
289 // Since the current frame is still hold by painter/compositor, and | |
290 // the next frame is already timed-out, we should skip the next frame | |
291 // which is the first frame in the queue. | |
292 timeout_frame = frames_queue_ready_.front(); | |
293 frames_queue_ready_.pop_front(); | |
294 ++frames_dropped; | |
295 } | |
296 } | 301 } |
297 if (timeout_frame.get()) { | 302 } |
298 frames_queue_done_.push_back(timeout_frame); | 303 if (timeout_frame.get()) { |
299 ScheduleRead_Locked(); | 304 frames_queue_done_.push_back(timeout_frame); |
300 } | 305 ScheduleRead_Locked(); |
301 if (new_frame_available) { | 306 } |
302 base::AutoUnlock auto_unlock(lock_); | 307 if (new_frame_available) { |
303 OnFrameAvailable(); | 308 base::AutoUnlock auto_unlock(lock_); |
304 } | 309 OnFrameAvailable(); |
305 } | 310 } |
306 } | 311 } |
307 } | 312 } |
308 | 313 |
309 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { | 314 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { |
310 base::AutoLock auto_lock(lock_); | 315 base::AutoLock auto_lock(lock_); |
311 DCHECK(!pending_paint_ && !pending_paint_with_last_available_); | 316 DCHECK(!pending_paint_ && !pending_paint_with_last_available_); |
312 | 317 |
313 if ((!current_frame_.get() || current_frame_->IsEndOfStream()) && | 318 if ((!current_frame_.get() || current_frame_->IsEndOfStream()) && |
314 (!last_available_frame_.get() || | 319 (!last_available_frame_.get() || |
(...skipping 20 matching lines...) Expand all Loading... |
335 base::AutoLock auto_lock(lock_); | 340 base::AutoLock auto_lock(lock_); |
336 | 341 |
337 // Note that we do not claim |pending_paint_| when we return NULL frame, in | 342 // Note that we do not claim |pending_paint_| when we return NULL frame, in |
338 // that case, |current_frame_| could be changed before PutCurrentFrame. | 343 // that case, |current_frame_| could be changed before PutCurrentFrame. |
339 if (pending_paint_) { | 344 if (pending_paint_) { |
340 DCHECK(current_frame_.get() == frame.get()); | 345 DCHECK(current_frame_.get() == frame.get()); |
341 DCHECK(!pending_paint_with_last_available_); | 346 DCHECK(!pending_paint_with_last_available_); |
342 pending_paint_ = false; | 347 pending_paint_ = false; |
343 } else if (pending_paint_with_last_available_) { | 348 } else if (pending_paint_with_last_available_) { |
344 DCHECK(last_available_frame_.get() == frame.get()); | 349 DCHECK(last_available_frame_.get() == frame.get()); |
| 350 DCHECK(!pending_paint_); |
345 pending_paint_with_last_available_ = false; | 351 pending_paint_with_last_available_ = false; |
346 } else { | 352 } else { |
347 DCHECK(frame.get() == NULL); | 353 DCHECK(frame.get() == NULL); |
348 } | 354 } |
349 | 355 |
350 // 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 |
351 // 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 |
352 // frame when this is true. | 358 // frame when this is true. |
353 frame_available_.Signal(); | 359 frame_available_.Signal(); |
354 if (state_ == kFlushing) { | 360 if (state_ == kFlushing) { |
355 FlushBuffers(); | 361 FlushBuffers_Locked(); |
356 } else if (state_ == kError || state_ == kStopped) { | 362 } else if (state_ == kError || state_ == kStopped) { |
357 DoStopOrErrorFlush_Locked(); | 363 DoStopOrErrorFlush_Locked(); |
358 } | 364 } |
359 } | 365 } |
360 | 366 |
361 void VideoRendererBase::ConsumeVideoFrame(scoped_refptr<VideoFrame> frame) { | 367 void VideoRendererBase::ConsumeVideoFrame(scoped_refptr<VideoFrame> frame) { |
362 if (frame) { | 368 if (frame) { |
363 PipelineStatistics statistics; | 369 PipelineStatistics statistics; |
364 statistics.video_frames_decoded = 1; | 370 statistics.video_frames_decoded = 1; |
365 statistics_callback_->Run(statistics); | 371 statistics_callback_->Run(statistics); |
366 } | 372 } |
367 | 373 |
368 base::AutoLock auto_lock(lock_); | 374 base::AutoLock auto_lock(lock_); |
369 | 375 |
370 if (!frame) { | 376 if (!frame) { |
371 EnterErrorState_Locked(PIPELINE_ERROR_DECODE); | 377 EnterErrorState_Locked(PIPELINE_ERROR_DECODE); |
372 return; | 378 return; |
373 } | 379 } |
374 | 380 |
375 // Decoder could reach seek state before our Seek() get called. | 381 // Decoder could reach seek state before our Seek() get called. |
376 // We will enter kSeeking | 382 // We will enter kSeeking |
377 if (kFlushed == state_) | 383 if (state_ == kFlushed) |
378 state_ = kSeeking; | 384 state_ = kSeeking; |
379 | 385 |
380 // Synchronous flush between filters should prevent this from happening. | 386 // Synchronous flush between filters should prevent this from happening. |
381 DCHECK_NE(state_, kStopped); | 387 DCHECK_NE(state_, kStopped); |
382 if (frame.get() && !frame->IsEndOfStream()) | 388 if (frame.get() && !frame->IsEndOfStream()) |
383 --pending_reads_; | 389 --pending_reads_; |
384 | 390 |
385 DCHECK(state_ != kUninitialized && state_ != kStopped && state_ != kError); | 391 DCHECK(state_ != kUninitialized && state_ != kStopped && state_ != kError); |
386 | 392 |
387 if (state_ == kPaused || state_ == kFlushing) { | 393 if (state_ == kPaused || state_ == kFlushing) { |
388 // Decoder are flushing rubbish video frame, we will not display them. | 394 // Decoder are flushing rubbish video frame, we will not display them. |
389 if (frame.get() && !frame->IsEndOfStream()) | 395 if (frame.get() && !frame->IsEndOfStream()) |
390 frames_queue_done_.push_back(frame); | 396 frames_queue_done_.push_back(frame); |
391 DCHECK_LE(frames_queue_done_.size(), | 397 DCHECK_LE(frames_queue_done_.size(), |
392 static_cast<size_t>(Limits::kMaxVideoFrames)); | 398 static_cast<size_t>(Limits::kMaxVideoFrames)); |
393 | 399 |
394 // Excluding kPause here, because in pause state, we will never | 400 // Excluding kPause here, because in pause state, we will never |
395 // transfer out-bounding buffer. We do not flush buffer when Compositor | 401 // transfer out-bounding buffer. We do not flush buffer when Compositor |
396 // hold reference to our video frame either. | 402 // hold reference to our video frame either. |
397 if (state_ == kFlushing && pending_paint_ == false) | 403 if (state_ == kFlushing && pending_paint_ == false) |
398 FlushBuffers(); | 404 FlushBuffers_Locked(); |
399 | 405 |
400 return; | 406 return; |
401 } | 407 } |
402 | 408 |
403 // Discard frames until we reach our desired seek timestamp. | 409 // Discard frames until we reach our desired seek timestamp. |
404 if (state_ == kSeeking && !frame->IsEndOfStream() && | 410 if (state_ == kSeeking && !frame->IsEndOfStream() && |
405 (frame->GetTimestamp() + frame->GetDuration()) <= seek_timestamp_) { | 411 (frame->GetTimestamp() + frame->GetDuration()) <= seek_timestamp_) { |
406 frames_queue_done_.push_back(frame); | 412 frames_queue_done_.push_back(frame); |
407 ScheduleRead_Locked(); | 413 ScheduleRead_Locked(); |
408 } else { | 414 } else { |
(...skipping 24 matching lines...) Expand all Loading... |
433 new_frame_available = true; | 439 new_frame_available = true; |
434 | 440 |
435 // If we reach prerolled state before Seek() is called by pipeline, | 441 // If we reach prerolled state before Seek() is called by pipeline, |
436 // |seek_callback_| is not set, we will return immediately during | 442 // |seek_callback_| is not set, we will return immediately during |
437 // when Seek() is eventually called. | 443 // when Seek() is eventually called. |
438 if (!seek_cb_.is_null()) { | 444 if (!seek_cb_.is_null()) { |
439 ResetAndRunCB(&seek_cb_, PIPELINE_OK); | 445 ResetAndRunCB(&seek_cb_, PIPELINE_OK); |
440 } | 446 } |
441 } | 447 } |
442 } else if (state_ == kFlushing && pending_reads_ == 0 && !pending_paint_) { | 448 } else if (state_ == kFlushing && pending_reads_ == 0 && !pending_paint_) { |
443 OnFlushDone(); | 449 OnFlushDone_Locked(); |
444 } | 450 } |
445 | 451 |
446 if (new_frame_available) { | 452 if (new_frame_available) { |
447 base::AutoUnlock auto_unlock(lock_); | 453 base::AutoUnlock auto_unlock(lock_); |
448 OnFrameAvailable(); | 454 OnFrameAvailable(); |
449 } | 455 } |
450 } | 456 } |
451 | 457 |
452 void VideoRendererBase::ReadInput(scoped_refptr<VideoFrame> frame) { | 458 void VideoRendererBase::ReadInput(scoped_refptr<VideoFrame> frame) { |
453 // We should never return empty frames or EOS frame. | 459 // We should never return empty frames or EOS frame. |
454 DCHECK(frame.get() && !frame->IsEndOfStream()); | 460 DCHECK(frame.get() && !frame->IsEndOfStream()); |
455 | 461 |
456 decoder_->ProduceVideoFrame(frame); | 462 decoder_->ProduceVideoFrame(frame); |
457 ++pending_reads_; | 463 ++pending_reads_; |
458 } | 464 } |
459 | 465 |
460 void VideoRendererBase::ScheduleRead_Locked() { | 466 void VideoRendererBase::ScheduleRead_Locked() { |
461 lock_.AssertAcquired(); | 467 lock_.AssertAcquired(); |
462 DCHECK_NE(kEnded, state_); | 468 DCHECK_NE(kEnded, state_); |
463 // TODO(jiesun): We use dummy buffer to feed decoder to let decoder to | 469 // TODO(jiesun): We use dummy buffer to feed decoder to let decoder to |
464 // provide buffer pools. In the future, we may want to implement real | 470 // provide buffer pools. In the future, we may want to implement real |
465 // buffer pool to recycle buffers. | 471 // buffer pool to recycle buffers. |
466 while (!frames_queue_done_.empty()) { | 472 while (!frames_queue_done_.empty()) { |
467 scoped_refptr<VideoFrame> video_frame = frames_queue_done_.front(); | 473 scoped_refptr<VideoFrame> video_frame = frames_queue_done_.front(); |
468 frames_queue_done_.pop_front(); | 474 frames_queue_done_.pop_front(); |
469 ReadInput(video_frame); | 475 ReadInput(video_frame); |
470 } | 476 } |
471 } | 477 } |
472 | 478 |
473 void VideoRendererBase::FlushBuffers() { | 479 void VideoRendererBase::FlushBuffers_Locked() { |
| 480 lock_.AssertAcquired(); |
474 DCHECK(!pending_paint_); | 481 DCHECK(!pending_paint_); |
475 | 482 |
476 // We should never put EOF frame into "done queue". | 483 // We should never put EOF frame into "done queue". |
477 while (!frames_queue_ready_.empty()) { | 484 while (!frames_queue_ready_.empty()) { |
478 scoped_refptr<VideoFrame> video_frame = frames_queue_ready_.front(); | 485 scoped_refptr<VideoFrame> video_frame = frames_queue_ready_.front(); |
479 if (!video_frame->IsEndOfStream()) { | 486 if (!video_frame->IsEndOfStream()) { |
480 frames_queue_done_.push_back(video_frame); | 487 frames_queue_done_.push_back(video_frame); |
481 } | 488 } |
482 frames_queue_ready_.pop_front(); | 489 frames_queue_ready_.pop_front(); |
483 } | 490 } |
484 if (current_frame_.get() && !current_frame_->IsEndOfStream()) { | 491 if (current_frame_.get() && !current_frame_->IsEndOfStream()) { |
485 frames_queue_done_.push_back(current_frame_); | 492 frames_queue_done_.push_back(current_frame_); |
486 } | 493 } |
487 current_frame_ = NULL; | 494 current_frame_ = NULL; |
488 | 495 |
489 // Flush all buffers out to decoder. | 496 // Flush all buffers out to decoder. |
490 ScheduleRead_Locked(); | 497 ScheduleRead_Locked(); |
491 | 498 |
492 if (pending_reads_ == 0 && state_ == kFlushing) | 499 if (pending_reads_ == 0 && state_ == kFlushing) |
493 OnFlushDone(); | 500 OnFlushDone_Locked(); |
494 } | 501 } |
495 | 502 |
496 void VideoRendererBase::OnFlushDone() { | 503 void VideoRendererBase::OnFlushDone_Locked() { |
497 // Check all buffers are return to owners. | 504 lock_.AssertAcquired(); |
| 505 // Check all buffers are returned to owners. |
498 DCHECK_EQ(frames_queue_done_.size(), 0u); | 506 DCHECK_EQ(frames_queue_done_.size(), 0u); |
499 DCHECK(!current_frame_.get()); | 507 DCHECK(!current_frame_.get()); |
500 DCHECK(frames_queue_ready_.empty()); | 508 DCHECK(frames_queue_ready_.empty()); |
501 | 509 |
502 if (flush_callback_.get()) { // This ensures callback is invoked once. | 510 if (flush_callback_.get()) { // This ensures callback is invoked once. |
503 flush_callback_->Run(); | 511 flush_callback_->Run(); |
504 flush_callback_.reset(); | 512 flush_callback_.reset(); |
505 } | 513 } |
506 state_ = kFlushed; | 514 state_ = kFlushed; |
507 } | 515 } |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
578 host()->SetError(status); | 586 host()->SetError(status); |
579 | 587 |
580 if (callback.get()) | 588 if (callback.get()) |
581 callback->Run(); | 589 callback->Run(); |
582 } | 590 } |
583 | 591 |
584 void VideoRendererBase::DoStopOrErrorFlush_Locked() { | 592 void VideoRendererBase::DoStopOrErrorFlush_Locked() { |
585 DCHECK(!pending_paint_); | 593 DCHECK(!pending_paint_); |
586 DCHECK(!pending_paint_with_last_available_); | 594 DCHECK(!pending_paint_with_last_available_); |
587 lock_.AssertAcquired(); | 595 lock_.AssertAcquired(); |
588 FlushBuffers(); | 596 FlushBuffers_Locked(); |
589 last_available_frame_ = NULL; | 597 last_available_frame_ = NULL; |
590 DCHECK_EQ(pending_reads_, 0); | 598 DCHECK_EQ(pending_reads_, 0); |
591 } | 599 } |
592 | 600 |
593 } // namespace media | 601 } // namespace media |
OLD | NEW |