OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/callback.h" | 5 #include "base/callback.h" |
6 #include "media/base/buffers.h" | 6 #include "media/base/buffers.h" |
7 #include "media/base/callback.h" | 7 #include "media/base/callback.h" |
8 #include "media/base/filter_host.h" | 8 #include "media/base/filter_host.h" |
| 9 #include "media/base/limits.h" |
9 #include "media/base/video_frame.h" | 10 #include "media/base/video_frame.h" |
10 #include "media/filters/video_renderer_base.h" | 11 #include "media/filters/video_renderer_base.h" |
11 | 12 |
12 namespace media { | 13 namespace media { |
13 | 14 |
14 // Limit our read ahead to at least 3 frames. One frame is typically in flux at | |
15 // all times, as in frame n is discarded at the top of ThreadMain() while frame | |
16 // (n + kMaxFrames) is being asynchronously fetched. The remaining two frames | |
17 // allow us to advance the current frame as well as read the timestamp of the | |
18 // following frame for more accurate timing. | |
19 // | |
20 // Increasing this number beyond 3 simply creates a larger buffer to work with | |
21 // at the expense of memory (~0.5MB and ~1.3MB per frame for 480p and 720p | |
22 // resolutions, respectively). This can help on lower-end systems if there are | |
23 // difficult sections in the movie and decoding slows down. | |
24 // | |
25 // Set to 4 because some vendor's driver doesn't allow buffer count to go below | |
26 // preset limit, e.g., EGLImage path. | |
27 static const size_t kMaxFrames = 4; | |
28 | |
29 // This equates to ~16.67 fps, which is just slow enough to be tolerable when | 15 // This equates to ~16.67 fps, which is just slow enough to be tolerable when |
30 // our video renderer is ahead of the audio playback. | 16 // our video renderer is ahead of the audio playback. |
31 // | 17 // |
32 // A higher value will be a slower frame rate, which looks worse but allows the | 18 // A higher value will be a slower frame rate, which looks worse but allows the |
33 // audio renderer to catch up faster. A lower value will be a smoother frame | 19 // audio renderer to catch up faster. A lower value will be a smoother frame |
34 // rate, but results in the video being out of sync for longer. | 20 // rate, but results in the video being out of sync for longer. |
35 static const int64 kMaxSleepMilliseconds = 60; | 21 static const int64 kMaxSleepMilliseconds = 60; |
36 | 22 |
37 // The number of milliseconds to idle when we do not have anything to do. | 23 // The number of milliseconds to idle when we do not have anything to do. |
38 // Nothing special about the value, other than we're being more OS-friendly | 24 // Nothing special about the value, other than we're being more OS-friendly |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 return false; | 70 return false; |
85 if (!media_format.GetAsInteger(MediaFormat::kHeight, &height)) | 71 if (!media_format.GetAsInteger(MediaFormat::kHeight, &height)) |
86 return false; | 72 return false; |
87 if (width_out) *width_out = width; | 73 if (width_out) *width_out = width; |
88 if (height_out) *height_out = height; | 74 if (height_out) *height_out = height; |
89 return true; | 75 return true; |
90 } | 76 } |
91 | 77 |
92 void VideoRendererBase::Play(FilterCallback* callback) { | 78 void VideoRendererBase::Play(FilterCallback* callback) { |
93 AutoLock auto_lock(lock_); | 79 AutoLock auto_lock(lock_); |
94 DCHECK(kPaused == state_ || kFlushing == state_); | 80 DCHECK_EQ(kPrerolled, state_); |
95 scoped_ptr<FilterCallback> c(callback); | 81 scoped_ptr<FilterCallback> c(callback); |
96 state_ = kPlaying; | 82 state_ = kPlaying; |
97 callback->Run(); | 83 callback->Run(); |
98 } | 84 } |
99 | 85 |
100 void VideoRendererBase::Pause(FilterCallback* callback) { | 86 void VideoRendererBase::Pause(FilterCallback* callback) { |
101 AutoLock auto_lock(lock_); | 87 AutoLock auto_lock(lock_); |
102 DCHECK(state_ == kPlaying || state_ == kEnded); | 88 DCHECK(state_ != kUninitialized || state_ == kError); |
103 AutoCallbackRunner done_runner(callback); | 89 AutoCallbackRunner done_runner(callback); |
104 state_ = kPaused; | 90 state_ = kPaused; |
105 } | 91 } |
106 | 92 |
107 void VideoRendererBase::Flush(FilterCallback* callback) { | 93 void VideoRendererBase::Flush(FilterCallback* callback) { |
108 DCHECK(state_ == kPaused); | 94 DCHECK_EQ(state_, kPaused); |
109 | 95 |
110 AutoLock auto_lock(lock_); | 96 AutoLock auto_lock(lock_); |
111 flush_callback_.reset(callback); | 97 flush_callback_.reset(callback); |
112 state_ = kFlushing; | 98 state_ = kFlushing; |
113 | 99 |
114 // Filter is considered paused when we've finished all pending reads, which | 100 if (pending_paint_ == false) |
115 // implies all buffers are returned to owner in Decoder/Renderer. Renderer | |
116 // is considered paused with one more contingency that |pending_paint_| is | |
117 // false, such that no client of us is holding any reference to VideoFrame. | |
118 if (pending_reads_ == 0 && pending_paint_ == false) { | |
119 flush_callback_->Run(); | |
120 flush_callback_.reset(); | |
121 FlushBuffers(); | 101 FlushBuffers(); |
122 } | |
123 } | 102 } |
124 | 103 |
125 void VideoRendererBase::Stop(FilterCallback* callback) { | 104 void VideoRendererBase::Stop(FilterCallback* callback) { |
| 105 DCHECK_EQ(pending_reads_, 0); |
| 106 |
126 { | 107 { |
127 AutoLock auto_lock(lock_); | 108 AutoLock auto_lock(lock_); |
128 state_ = kStopped; | 109 state_ = kStopped; |
129 | 110 |
130 // TODO(jiesun): move this to flush. | |
131 // TODO(jiesun): we should wait until pending_paint_ is false; | |
132 FlushBuffers(); | |
133 | |
134 // Clean up our thread if present. | 111 // Clean up our thread if present. |
135 if (thread_) { | 112 if (thread_) { |
136 // Signal the thread since it's possible to get stopped with the video | 113 // Signal the thread since it's possible to get stopped with the video |
137 // thread waiting for a read to complete. | 114 // thread waiting for a read to complete. |
138 frame_available_.Signal(); | 115 frame_available_.Signal(); |
139 { | 116 { |
140 AutoUnlock auto_unlock(lock_); | 117 AutoUnlock auto_unlock(lock_); |
141 PlatformThread::Join(thread_); | 118 PlatformThread::Join(thread_); |
142 } | 119 } |
143 thread_ = kNullThreadHandle; | 120 thread_ = kNullThreadHandle; |
144 } | 121 } |
145 | 122 |
146 } | 123 } |
147 // Signal the subclass we're stopping. | 124 // Signal the subclass we're stopping. |
148 OnStop(callback); | 125 OnStop(callback); |
149 } | 126 } |
150 | 127 |
151 void VideoRendererBase::SetPlaybackRate(float playback_rate) { | 128 void VideoRendererBase::SetPlaybackRate(float playback_rate) { |
152 AutoLock auto_lock(lock_); | 129 AutoLock auto_lock(lock_); |
153 playback_rate_ = playback_rate; | 130 playback_rate_ = playback_rate; |
154 } | 131 } |
155 | 132 |
156 void VideoRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) { | 133 void VideoRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) { |
157 AutoLock auto_lock(lock_); | 134 AutoLock auto_lock(lock_); |
158 DCHECK(kPaused == state_ || kFlushing == state_); | 135 // There is a race condition between filters to receive SeekTask(). |
159 DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; | 136 // It turns out we could receive buffer from decoder before seek() |
160 state_ = kSeeking; | 137 // is called on us. so we do the following: |
161 seek_callback_.reset(callback); | 138 // kFlushed => ( Receive first buffer or Seek() ) => kSeeking and |
162 seek_timestamp_ = time; | 139 // kSeeking => ( Receive enough buffers) => kPrerolled. ) |
| 140 DCHECK(kPrerolled == state_ || kFlushed == state_ || kSeeking == state_); |
163 | 141 |
164 // Throw away everything and schedule our reads. | 142 if (state_ == kPrerolled) { |
165 // TODO(jiesun): this should be guaranteed by pause/flush before seek happen. | 143 // Already get enough buffers from decoder. |
166 frames_queue_ready_.clear(); | 144 callback->Run(); |
167 frames_queue_done_.clear(); | 145 delete callback; |
168 for (size_t i = 0; i < kMaxFrames; ++i) { | 146 } else { |
169 // TODO(jiesun): this is dummy read for ffmpeg path until we truely recycle | 147 // Otherwise we are either kFlushed or kSeeking, but without enough buffers; |
170 // in that path. | 148 // we should save the callback function and call it later. |
171 scoped_refptr<VideoFrame> null_frame; | 149 state_ = kSeeking; |
172 frames_queue_done_.push_back(null_frame); | 150 seek_callback_.reset(callback); |
173 } | 151 } |
174 | 152 |
175 // TODO(jiesun): if EGL image path make sure those video frames are already in | 153 seek_timestamp_ = time; |
176 // frames_queue_done_, we could remove FillThisBuffer call from derived class. | |
177 // But currently that is trigger by first paint(), which is bad. | |
178 ScheduleRead_Locked(); | 154 ScheduleRead_Locked(); |
179 } | 155 } |
180 | 156 |
181 void VideoRendererBase::Initialize(VideoDecoder* decoder, | 157 void VideoRendererBase::Initialize(VideoDecoder* decoder, |
182 FilterCallback* callback) { | 158 FilterCallback* callback) { |
183 AutoLock auto_lock(lock_); | 159 AutoLock auto_lock(lock_); |
184 DCHECK(decoder); | 160 DCHECK(decoder); |
185 DCHECK(callback); | 161 DCHECK(callback); |
186 DCHECK_EQ(kUninitialized, state_); | 162 DCHECK_EQ(kUninitialized, state_); |
187 decoder_ = decoder; | 163 decoder_ = decoder; |
188 scoped_ptr<FilterCallback> c(callback); | 164 AutoCallbackRunner done_runner(callback); |
189 | 165 |
190 decoder_->set_fill_buffer_done_callback( | 166 decoder_->set_fill_buffer_done_callback( |
191 NewCallback(this, &VideoRendererBase::OnFillBufferDone)); | 167 NewCallback(this, &VideoRendererBase::OnFillBufferDone)); |
192 // Notify the pipeline of the video dimensions. | 168 // Notify the pipeline of the video dimensions. |
193 if (!ParseMediaFormat(decoder->media_format(), | 169 if (!ParseMediaFormat(decoder->media_format(), |
194 &surface_type_, | 170 &surface_type_, |
195 &surface_format_, | 171 &surface_format_, |
196 &width_, &height_)) { | 172 &width_, &height_)) { |
197 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); | 173 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); |
198 callback->Run(); | 174 state_ = kError; |
199 return; | 175 return; |
200 } | 176 } |
201 host()->SetVideoSize(width_, height_); | 177 host()->SetVideoSize(width_, height_); |
202 | 178 |
203 // Initialize the subclass. | 179 // Initialize the subclass. |
204 // TODO(scherkus): do we trust subclasses not to do something silly while | 180 // TODO(scherkus): do we trust subclasses not to do something silly while |
205 // we're holding the lock? | 181 // we're holding the lock? |
206 if (!OnInitialize(decoder)) { | 182 if (!OnInitialize(decoder)) { |
207 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); | 183 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); |
208 callback->Run(); | 184 state_ = kError; |
209 return; | 185 return; |
210 } | 186 } |
211 | 187 |
212 // We're all good! Consider ourselves paused (ThreadMain() should never | 188 // We're all good! Consider ourselves flushed. (ThreadMain() should never |
213 // see us in the kUninitialized state). | 189 // see us in the kUninitialized state). |
214 state_ = kPaused; | 190 // Since we had an initial Seek, we consider ourself flushed, because we |
| 191 // have not populated any buffers yet. |
| 192 state_ = kFlushed; |
215 | 193 |
216 // Create our video thread. | 194 // Create our video thread. |
217 if (!PlatformThread::Create(0, this, &thread_)) { | 195 if (!PlatformThread::Create(0, this, &thread_)) { |
218 NOTREACHED() << "Video thread creation failed"; | 196 NOTREACHED() << "Video thread creation failed"; |
219 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); | 197 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); |
220 callback->Run(); | 198 state_ = kError; |
221 return; | 199 return; |
222 } | 200 } |
223 | 201 |
224 #if defined(OS_WIN) | 202 #if defined(OS_WIN) |
225 // Bump up our priority so our sleeping is more accurate. | 203 // Bump up our priority so our sleeping is more accurate. |
226 // TODO(scherkus): find out if this is necessary, but it seems to help. | 204 // TODO(scherkus): find out if this is necessary, but it seems to help. |
227 ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); | 205 ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); |
228 #endif // defined(OS_WIN) | 206 #endif // defined(OS_WIN) |
229 | 207 |
230 // Finally, execute the start callback. | |
231 callback->Run(); | |
232 } | 208 } |
233 | 209 |
234 bool VideoRendererBase::HasEnded() { | 210 bool VideoRendererBase::HasEnded() { |
235 AutoLock auto_lock(lock_); | 211 AutoLock auto_lock(lock_); |
236 return state_ == kEnded; | 212 return state_ == kEnded; |
237 } | 213 } |
238 | 214 |
239 // PlatformThread::Delegate implementation. | 215 // PlatformThread::Delegate implementation. |
240 void VideoRendererBase::ThreadMain() { | 216 void VideoRendererBase::ThreadMain() { |
241 PlatformThread::SetName("CrVideoRenderer"); | 217 PlatformThread::SetName("CrVideoRenderer"); |
(...skipping 17 matching lines...) Expand all Loading... |
259 else | 235 else |
260 remaining_time = kIdleTimeDelta; | 236 remaining_time = kIdleTimeDelta; |
261 } else { | 237 } else { |
262 // Calculate how long until we should advance the frame, which is | 238 // Calculate how long until we should advance the frame, which is |
263 // typically negative but for playback rates < 1.0f may be long enough | 239 // typically negative but for playback rates < 1.0f may be long enough |
264 // that it makes more sense to idle and check again. | 240 // that it makes more sense to idle and check again. |
265 scoped_refptr<VideoFrame> next_frame = frames_queue_ready_.front(); | 241 scoped_refptr<VideoFrame> next_frame = frames_queue_ready_.front(); |
266 remaining_time = CalculateSleepDuration(next_frame, playback_rate_); | 242 remaining_time = CalculateSleepDuration(next_frame, playback_rate_); |
267 } | 243 } |
268 | 244 |
| 245 // TODO(jiesun): I do not think we should wake up every 10ms. |
| 246 // We should only wait up when following is true: |
| 247 // 1. frame arrival (use event); |
| 248 // 2. state_ change (use event); |
| 249 // 3. playback_rate_ change (use event); |
| 250 // 4. next frame's pts (use timeout); |
269 if (remaining_time > kIdleTimeDelta) | 251 if (remaining_time > kIdleTimeDelta) |
270 remaining_time = kIdleTimeDelta; | 252 remaining_time = kIdleTimeDelta; |
| 253 |
| 254 // We can not do anything about this until next frame arrival. |
| 255 // We do not want to spin in this case though. |
| 256 if (remaining_time.InMicroseconds() < 0 && frames_queue_ready_.empty()) |
| 257 remaining_time = kIdleTimeDelta; |
| 258 |
271 if (remaining_time.InMicroseconds() > 0) | 259 if (remaining_time.InMicroseconds() > 0) |
272 frame_available_.TimedWait(remaining_time); | 260 frame_available_.TimedWait(remaining_time); |
273 | 261 |
274 if (state_ != kPlaying || playback_rate_ == 0) | 262 if (state_ != kPlaying || playback_rate_ == 0) |
275 continue; | 263 continue; |
276 | 264 |
277 // Otherwise we're playing, so advance the frame and keep reading from the | 265 // Otherwise we're playing, so advance the frame and keep reading from the |
278 // decoder when following condition is satisfied: | 266 // decoder when following condition is satisfied: |
279 // 1. We had at least one backup frame. | 267 // 1. We had at least one backup frame. |
280 // 2. We had not reached end of stream. | 268 // 2. We had not reached end of stream. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
316 frames_queue_ready_[1]->GetTimestamp() - host()->GetTime(); | 304 frames_queue_ready_[1]->GetTimestamp() - host()->GetTime(); |
317 if (next_remaining_time.InMicroseconds() <= 0) { | 305 if (next_remaining_time.InMicroseconds() <= 0) { |
318 // Since the current frame is still hold by painter/compositor, and | 306 // Since the current frame is still hold by painter/compositor, and |
319 // the next frame is already timed-out, we should skip the next frame | 307 // the next frame is already timed-out, we should skip the next frame |
320 // which is the first frame in the queue. | 308 // which is the first frame in the queue. |
321 timeout_frame = frames_queue_ready_.front(); | 309 timeout_frame = frames_queue_ready_.front(); |
322 frames_queue_ready_.pop_front(); | 310 frames_queue_ready_.pop_front(); |
323 } | 311 } |
324 } | 312 } |
325 if (timeout_frame.get()) { | 313 if (timeout_frame.get()) { |
326 // TODO(jiesun): we should really merge the following branch. That way | 314 frames_queue_done_.push_back(timeout_frame); |
327 // we will remove the last EGLImage hack in this class. but the | 315 ScheduleRead_Locked(); |
328 // |pending_reads_| prevents use to do so (until we had implemented | |
329 // flush logic and get rid of pending reads.) | |
330 if (uses_egl_image() && | |
331 media::VideoFrame::TYPE_EGL_IMAGE == timeout_frame->type()) { | |
332 decoder_->FillThisBuffer(timeout_frame); | |
333 } else { | |
334 // TODO(jiesun): could this be merged with EGLimage path? | |
335 frames_queue_done_.push_back(timeout_frame); | |
336 ScheduleRead_Locked(); | |
337 } | |
338 } | 316 } |
339 if (new_frame_available) { | 317 if (new_frame_available) { |
340 AutoUnlock auto_unlock(lock_); | 318 AutoUnlock auto_unlock(lock_); |
341 // Notify subclass that |current_frame_| has been updated. | 319 // Notify subclass that |current_frame_| has been updated. |
342 OnFrameAvailable(); | 320 OnFrameAvailable(); |
343 } | 321 } |
344 } | 322 } |
345 } | 323 } |
346 } | 324 } |
347 | 325 |
348 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { | 326 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { |
349 AutoLock auto_lock(lock_); | 327 AutoLock auto_lock(lock_); |
350 DCHECK(!pending_paint_); | 328 DCHECK(!pending_paint_); |
351 | 329 |
352 if (state_ == kStopped || !current_frame_.get() || | 330 if (!current_frame_.get() || current_frame_->IsEndOfStream()) { |
353 current_frame_->IsEndOfStream()) { | |
354 *frame_out = NULL; | 331 *frame_out = NULL; |
355 return; | 332 return; |
356 } | 333 } |
357 | 334 |
358 // We should have initialized and have the current frame. | 335 // We should have initialized and have the current frame. |
359 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying || | 336 DCHECK(state_ != kUninitialized && state_ != kStopped && state_ != kError); |
360 state_ == kFlushing || state_ == kEnded); | |
361 *frame_out = current_frame_; | 337 *frame_out = current_frame_; |
362 pending_paint_ = true; | 338 pending_paint_ = true; |
363 } | 339 } |
364 | 340 |
365 void VideoRendererBase::PutCurrentFrame(scoped_refptr<VideoFrame> frame) { | 341 void VideoRendererBase::PutCurrentFrame(scoped_refptr<VideoFrame> frame) { |
366 AutoLock auto_lock(lock_); | 342 AutoLock auto_lock(lock_); |
367 | 343 |
368 // Note that we do not claim |pending_paint_| when we return NULL frame, in | 344 // Note that we do not claim |pending_paint_| when we return NULL frame, in |
369 // that case, |current_frame_| could be changed before PutCurrentFrame. | 345 // that case, |current_frame_| could be changed before PutCurrentFrame. |
370 DCHECK(pending_paint_ || frame.get() == NULL); | 346 DCHECK(pending_paint_ || frame.get() == NULL); |
371 DCHECK(current_frame_.get() == frame.get() || frame.get() == NULL); | 347 DCHECK(current_frame_.get() == frame.get() || frame.get() == NULL); |
372 | 348 |
373 pending_paint_ = false; | 349 pending_paint_ = false; |
374 // We had cleared the |pending_paint_| flag, there are chances that current | 350 // We had cleared the |pending_paint_| flag, there are chances that current |
375 // frame is timed-out. We will wake up our main thread to advance the current | 351 // frame is timed-out. We will wake up our main thread to advance the current |
376 // frame when this is true. | 352 // frame when this is true. |
377 frame_available_.Signal(); | 353 frame_available_.Signal(); |
378 if (state_ == kFlushing && pending_reads_ == 0 && flush_callback_.get()) { | 354 if (state_ == kFlushing) |
379 // No more pending reads! We're now officially "paused". | |
380 FlushBuffers(); | 355 FlushBuffers(); |
381 flush_callback_->Run(); | |
382 flush_callback_.reset(); | |
383 } | |
384 } | 356 } |
385 | 357 |
386 void VideoRendererBase::OnFillBufferDone(scoped_refptr<VideoFrame> frame) { | 358 void VideoRendererBase::OnFillBufferDone(scoped_refptr<VideoFrame> frame) { |
387 AutoLock auto_lock(lock_); | 359 AutoLock auto_lock(lock_); |
388 | 360 |
389 // TODO(ajwong): Work around cause we don't synchronize on stop. Correct | 361 // Decoder could reach seek state before our Seek() get called. |
390 // fix is to resolve http://crbug.com/16059. | 362 // We will enter kSeeking |
391 if (state_ == kStopped) { | 363 if (kFlushed == state_) |
392 // TODO(jiesun): Remove this when flush before stop landed! | 364 state_ = kSeeking; |
| 365 |
| 366 // Synchronous flush between filters should prevent this from happening. |
| 367 DCHECK_NE(state_, kStopped); |
| 368 if (frame.get() && !frame->IsEndOfStream()) |
| 369 --pending_reads_; |
| 370 |
| 371 DCHECK(state_ != kUninitialized && state_ != kStopped && state_ != kError); |
| 372 |
| 373 if (state_ == kPaused || state_ == kFlushing) { |
| 374 // Decoder are flushing rubbish video frame, we will not display them. |
| 375 if (frame.get() && !frame->IsEndOfStream()) |
| 376 frames_queue_done_.push_back(frame); |
| 377 DCHECK_LE(frames_queue_done_.size(), |
| 378 static_cast<size_t>(Limits::kMaxVideoFrames)); |
| 379 |
| 380 // Excluding kPause here, because in pause state, we will never |
| 381 // transfer out-bounding buffer. We do not flush buffer when Compositor |
| 382 // hold reference to our video frame either. |
| 383 if (state_ == kFlushing && pending_paint_ == false) |
| 384 FlushBuffers(); |
| 385 |
393 return; | 386 return; |
394 } | 387 } |
395 | 388 |
396 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying || | |
397 state_ == kFlushing || state_ == kEnded); | |
398 DCHECK_GT(pending_reads_, 0u); | |
399 --pending_reads_; | |
400 | |
401 // Discard frames until we reach our desired seek timestamp. | 389 // Discard frames until we reach our desired seek timestamp. |
402 if (state_ == kSeeking && !frame->IsEndOfStream() && | 390 if (state_ == kSeeking && !frame->IsEndOfStream() && |
403 (frame->GetTimestamp() + frame->GetDuration()) < seek_timestamp_) { | 391 (frame->GetTimestamp() + frame->GetDuration()) < seek_timestamp_) { |
404 frames_queue_done_.push_back(frame); | 392 frames_queue_done_.push_back(frame); |
405 ScheduleRead_Locked(); | 393 ScheduleRead_Locked(); |
406 } else { | 394 } else { |
407 frames_queue_ready_.push_back(frame); | 395 frames_queue_ready_.push_back(frame); |
408 DCHECK_LE(frames_queue_ready_.size(), kMaxFrames); | 396 DCHECK_LE(frames_queue_ready_.size(), |
| 397 static_cast<size_t>(Limits::kMaxVideoFrames)); |
409 frame_available_.Signal(); | 398 frame_available_.Signal(); |
410 } | 399 } |
411 | 400 |
412 // Check for our preroll complete condition. | 401 // Check for our preroll complete condition. |
413 if (state_ == kSeeking) { | 402 if (state_ == kSeeking) { |
414 DCHECK(seek_callback_.get()); | 403 if (frames_queue_ready_.size() == Limits::kMaxVideoFrames || |
415 if (frames_queue_ready_.size() == kMaxFrames || frame->IsEndOfStream()) { | 404 frame->IsEndOfStream()) { |
416 // We're paused, so make sure we update |current_frame_| to represent | 405 // We're paused, so make sure we update |current_frame_| to represent |
417 // our new location. | 406 // our new location. |
418 state_ = kPaused; | 407 state_ = kPrerolled; |
419 | 408 |
420 // Because we might remain paused (i.e., we were not playing before we | 409 // Because we might remain paused (i.e., we were not playing before we |
421 // received a seek), we can't rely on ThreadMain() to notify the subclass | 410 // received a seek), we can't rely on ThreadMain() to notify the subclass |
422 // the frame has been updated. | 411 // the frame has been updated. |
423 scoped_refptr<VideoFrame> first_frame; | 412 scoped_refptr<VideoFrame> first_frame; |
424 first_frame = frames_queue_ready_.front(); | 413 first_frame = frames_queue_ready_.front(); |
425 if (!first_frame->IsEndOfStream()) { | 414 if (!first_frame->IsEndOfStream()) { |
426 frames_queue_ready_.pop_front(); | 415 frames_queue_ready_.pop_front(); |
427 current_frame_ = first_frame; | 416 current_frame_ = first_frame; |
428 } | 417 } |
429 OnFrameAvailable(); | 418 OnFrameAvailable(); |
430 | 419 |
431 seek_callback_->Run(); | 420 // If we reach prerolled state before Seek() is called by pipeline, |
432 seek_callback_.reset(); | 421 // |seek_callback_| is not set, we will return immediately during |
| 422 // when Seek() is eventually called. |
| 423 if ((seek_callback_.get())) { |
| 424 seek_callback_->Run(); |
| 425 seek_callback_.reset(); |
| 426 } |
433 } | 427 } |
434 } else if (state_ == kFlushing && pending_reads_ == 0 && !pending_paint_) { | 428 } else if (state_ == kFlushing && pending_reads_ == 0 && !pending_paint_) { |
435 // No more pending reads! We're now officially "paused". | 429 OnFlushDone(); |
436 if (flush_callback_.get()) { | |
437 flush_callback_->Run(); | |
438 flush_callback_.reset(); | |
439 } | |
440 } | 430 } |
441 } | 431 } |
442 | 432 |
| 433 void VideoRendererBase::ReadInput(scoped_refptr<VideoFrame> frame) { |
| 434 // We should never return empty frames or EOS frame. |
| 435 DCHECK(frame.get() && !frame->IsEndOfStream()); |
| 436 |
| 437 decoder_->FillThisBuffer(frame); |
| 438 ++pending_reads_; |
| 439 } |
| 440 |
443 void VideoRendererBase::ScheduleRead_Locked() { | 441 void VideoRendererBase::ScheduleRead_Locked() { |
444 lock_.AssertAcquired(); | 442 lock_.AssertAcquired(); |
445 DCHECK_NE(kEnded, state_); | 443 DCHECK_NE(kEnded, state_); |
446 // TODO(jiesun): We use dummy buffer to feed decoder to let decoder to | 444 // TODO(jiesun): We use dummy buffer to feed decoder to let decoder to |
447 // provide buffer pools. In the future, we may want to implement real | 445 // provide buffer pools. In the future, we may want to implement real |
448 // buffer pool to recycle buffers. | 446 // buffer pool to recycle buffers. |
449 while (!frames_queue_done_.empty()) { | 447 while (!frames_queue_done_.empty()) { |
450 scoped_refptr<VideoFrame> video_frame = frames_queue_done_.front(); | 448 scoped_refptr<VideoFrame> video_frame = frames_queue_done_.front(); |
451 frames_queue_done_.pop_front(); | 449 frames_queue_done_.pop_front(); |
452 decoder_->FillThisBuffer(video_frame); | 450 ReadInput(video_frame); |
453 DCHECK_LT(pending_reads_, kMaxFrames); | |
454 ++pending_reads_; | |
455 } | 451 } |
456 } | 452 } |
457 | 453 |
458 void VideoRendererBase::FlushBuffers() { | 454 void VideoRendererBase::FlushBuffers() { |
459 DCHECK(!pending_paint_); | 455 DCHECK(!pending_paint_); |
460 | 456 |
461 // We should never put EOF frame into "done queue". | 457 // We should never put EOF frame into "done queue". |
462 while (!frames_queue_ready_.empty()) { | 458 while (!frames_queue_ready_.empty()) { |
463 scoped_refptr<VideoFrame> video_frame = frames_queue_ready_.front(); | 459 scoped_refptr<VideoFrame> video_frame = frames_queue_ready_.front(); |
464 if (!video_frame->IsEndOfStream()) { | 460 if (!video_frame->IsEndOfStream()) { |
465 frames_queue_done_.push_back(video_frame); | 461 frames_queue_done_.push_back(video_frame); |
466 } | 462 } |
467 frames_queue_ready_.pop_front(); | 463 frames_queue_ready_.pop_front(); |
468 } | 464 } |
469 if (current_frame_.get() && !current_frame_->IsEndOfStream()) { | 465 if (current_frame_.get() && !current_frame_->IsEndOfStream()) { |
470 frames_queue_done_.push_back(current_frame_); | 466 frames_queue_done_.push_back(current_frame_); |
471 } | 467 } |
472 current_frame_ = NULL; | 468 current_frame_ = NULL; |
| 469 |
| 470 if (decoder_->ProvidesBuffer()) { |
| 471 // Flush all buffers out to decoder; |
| 472 ScheduleRead_Locked(); |
| 473 } |
| 474 |
| 475 if (pending_reads_ == 0) |
| 476 OnFlushDone(); |
| 477 } |
| 478 |
| 479 void VideoRendererBase::OnFlushDone() { |
| 480 // Check all buffers are return to owners. |
| 481 if (decoder_->ProvidesBuffer()) { |
| 482 DCHECK_EQ(frames_queue_done_.size(), 0u); |
| 483 } else { |
| 484 DCHECK_EQ(frames_queue_done_.size(), |
| 485 static_cast<size_t>(Limits::kMaxVideoFrames)); |
| 486 } |
| 487 DCHECK(!current_frame_.get()); |
| 488 DCHECK(frames_queue_ready_.empty()); |
| 489 |
| 490 if (flush_callback_.get()) { // This ensures callback is invoked once. |
| 491 flush_callback_->Run(); |
| 492 flush_callback_.reset(); |
| 493 } |
| 494 state_ = kFlushed; |
473 } | 495 } |
474 | 496 |
475 base::TimeDelta VideoRendererBase::CalculateSleepDuration( | 497 base::TimeDelta VideoRendererBase::CalculateSleepDuration( |
476 VideoFrame* next_frame, float playback_rate) { | 498 VideoFrame* next_frame, float playback_rate) { |
477 // Determine the current and next presentation timestamps. | 499 // Determine the current and next presentation timestamps. |
478 base::TimeDelta now = host()->GetTime(); | 500 base::TimeDelta now = host()->GetTime(); |
479 base::TimeDelta this_pts = current_frame_->GetTimestamp(); | 501 base::TimeDelta this_pts = current_frame_->GetTimestamp(); |
480 base::TimeDelta next_pts; | 502 base::TimeDelta next_pts; |
481 if (next_frame) { | 503 if (next_frame) { |
482 next_pts = next_frame->GetTimestamp(); | 504 next_pts = next_frame->GetTimestamp(); |
(...skipping 12 matching lines...) Expand all Loading... |
495 previous_time_ = now; | 517 previous_time_ = now; |
496 } | 518 } |
497 | 519 |
498 // Scale our sleep based on the playback rate. | 520 // Scale our sleep based on the playback rate. |
499 // TODO(scherkus): floating point badness and degrade gracefully. | 521 // TODO(scherkus): floating point badness and degrade gracefully. |
500 return base::TimeDelta::FromMicroseconds( | 522 return base::TimeDelta::FromMicroseconds( |
501 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); | 523 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); |
502 } | 524 } |
503 | 525 |
504 } // namespace media | 526 } // namespace media |
OLD | NEW |