Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(820)

Side by Side Diff: media/filters/video_renderer_base.cc

Issue 8417019: Simplify VideoDecodeEngine interface by making everything synchronous. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix for CaptureVideoDecoder Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/filters/video_renderer_base.h ('k') | media/filters/video_renderer_base_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « media/filters/video_renderer_base.h ('k') | media/filters/video_renderer_base_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698