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 |