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 |