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

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

Issue 164403: Implemented end-of-stream callback for media::PipelineImpl. (Closed)
Patch Set: Baz Created 11 years, 4 months 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
« 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) 2009 The Chromium Authors. All rights reserved. Use of this 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
2 // source code is governed by a BSD-style license that can be found in the 2 // source code is governed by a BSD-style license that can be found in the
3 // LICENSE file. 3 // LICENSE file.
4 4
5 #include "media/base/buffers.h" 5 #include "media/base/buffers.h"
6 #include "media/base/filter_host.h" 6 #include "media/base/filter_host.h"
7 #include "media/base/video_frame_impl.h" 7 #include "media/base/video_frame_impl.h"
8 #include "media/filters/video_renderer_base.h" 8 #include "media/filters/video_renderer_base.h"
9 9
10 namespace media { 10 namespace media {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
67 void VideoRendererBase::Play(FilterCallback* callback) { 67 void VideoRendererBase::Play(FilterCallback* callback) {
68 AutoLock auto_lock(lock_); 68 AutoLock auto_lock(lock_);
69 DCHECK_EQ(kPaused, state_); 69 DCHECK_EQ(kPaused, state_);
70 scoped_ptr<FilterCallback> c(callback); 70 scoped_ptr<FilterCallback> c(callback);
71 state_ = kPlaying; 71 state_ = kPlaying;
72 callback->Run(); 72 callback->Run();
73 } 73 }
74 74
75 void VideoRendererBase::Pause(FilterCallback* callback) { 75 void VideoRendererBase::Pause(FilterCallback* callback) {
76 AutoLock auto_lock(lock_); 76 AutoLock auto_lock(lock_);
77 DCHECK_EQ(kPlaying, state_); 77 DCHECK(state_ == kPlaying || state_ == kEnded);
78 pause_callback_.reset(callback); 78 pause_callback_.reset(callback);
79 state_ = kPaused; 79 state_ = kPaused;
80 80
81 // We'll only pause when we've finished all pending reads. 81 // We'll only pause when we've finished all pending reads.
82 if (pending_reads_ == 0) { 82 if (pending_reads_ == 0) {
83 pause_callback_->Run(); 83 pause_callback_->Run();
84 pause_callback_.reset(); 84 pause_callback_.reset();
85 } else { 85 } else {
86 state_ = kPaused; 86 state_ = kPaused;
87 } 87 }
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
149 // TODO(scherkus): do we trust subclasses not to do something silly while 149 // TODO(scherkus): do we trust subclasses not to do something silly while
150 // we're holding the lock? 150 // we're holding the lock?
151 if (!OnInitialize(decoder)) { 151 if (!OnInitialize(decoder)) {
152 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); 152 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
153 callback->Run(); 153 callback->Run();
154 return; 154 return;
155 } 155 }
156 156
157 // Create a black frame so clients have something to render before we finish 157 // Create a black frame so clients have something to render before we finish
158 // prerolling. 158 // prerolling.
159 CreateBlackFrame(&current_frame_); 159 VideoFrameImpl::CreateBlackFrame(width_, height_, &current_frame_);
160 160
161 // We're all good! Consider ourselves paused (ThreadMain() should never 161 // We're all good! Consider ourselves paused (ThreadMain() should never
162 // see us in the kUninitialized state). 162 // see us in the kUninitialized state).
163 state_ = kPaused; 163 state_ = kPaused;
164 164
165 // Create our video thread. 165 // Create our video thread.
166 if (!PlatformThread::Create(0, this, &thread_)) { 166 if (!PlatformThread::Create(0, this, &thread_)) {
167 NOTREACHED() << "Video thread creation failed"; 167 NOTREACHED() << "Video thread creation failed";
168 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); 168 host()->SetError(PIPELINE_ERROR_INITIALIZATION_FAILED);
169 callback->Run(); 169 callback->Run();
170 return; 170 return;
171 } 171 }
172 172
173 #if defined(OS_WIN) 173 #if defined(OS_WIN)
174 // Bump up our priority so our sleeping is more accurate. 174 // Bump up our priority so our sleeping is more accurate.
175 // TODO(scherkus): find out if this is necessary, but it seems to help. 175 // TODO(scherkus): find out if this is necessary, but it seems to help.
176 ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); 176 ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL);
177 #endif // defined(OS_WIN) 177 #endif // defined(OS_WIN)
178 178
179 // Finally, execute the start callback. 179 // Finally, execute the start callback.
180 callback->Run(); 180 callback->Run();
181 } 181 }
182 182
183 bool VideoRendererBase::HasEnded() {
184 AutoLock auto_lock(lock_);
185 return state_ == kEnded;
186 }
187
183 // PlatformThread::Delegate implementation. 188 // PlatformThread::Delegate implementation.
184 void VideoRendererBase::ThreadMain() { 189 void VideoRendererBase::ThreadMain() {
185 PlatformThread::SetName("VideoThread"); 190 PlatformThread::SetName("VideoThread");
186 for (;;) { 191 for (;;) {
187 // State and playback rate to assume for this iteration of the loop. 192 // State and playback rate to assume for this iteration of the loop.
188 State state; 193 State state;
189 float playback_rate; 194 float playback_rate;
195 base::TimeDelta remaining_time;
190 { 196 {
191 AutoLock auto_lock(lock_); 197 AutoLock auto_lock(lock_);
192 state = state_; 198 state = state_;
193 playback_rate = playback_rate_; 199 playback_rate = playback_rate_;
200
201 // Calculate how long until we should advance the frame, which is
202 // typically negative but for playback rates < 1.0f may be long enough
203 // that it makes more sense to idle and check again.
204 remaining_time = current_frame_->GetTimestamp() - host()->GetTime();
194 } 205 }
195 if (state == kStopped) { 206 if (state == kStopped) {
196 return; 207 return;
197 } 208 }
198 209
199 // Sleep while paused or seeking. 210 // Idle if we shouldn't be playing or advancing the frame yet.
200 if (state == kPaused || state == kSeeking || playback_rate == 0) { 211 if (state == kPaused || state == kSeeking || state == kEnded ||
212 remaining_time.InMilliseconds() > kIdleMilliseconds ||
213 playback_rate == 0) {
201 PlatformThread::Sleep(kIdleMilliseconds); 214 PlatformThread::Sleep(kIdleMilliseconds);
202 continue; 215 continue;
203 } 216 }
204 217
205 // Advance |current_frame_| and try to determine |next_frame|. Note that 218 // Advance |current_frame_| and try to determine |next_frame|. Note that
206 // this loop executes our "playing" logic. 219 // this loop executes our "playing" logic.
207 DCHECK_EQ(kPlaying, state); 220 DCHECK_EQ(kPlaying, state);
208 scoped_refptr<VideoFrame> next_frame; 221 scoped_refptr<VideoFrame> next_frame;
209 { 222 {
210 AutoLock auto_lock(lock_); 223 AutoLock auto_lock(lock_);
211 // Check the actual state to see if we're trying to stop playing. 224 // Check the actual state to see if we're trying to stop playing.
212 if (state_ != kPlaying) { 225 if (state_ != kPlaying) {
213 continue; 226 continue;
214 } 227 }
215 228
216 // Idle if the next frame is too far ahead.
217 base::TimeDelta diff = current_frame_->GetTimestamp() - host()->GetTime();
218 if (diff.InMilliseconds() > kIdleMilliseconds) {
219 PlatformThread::Sleep(kIdleMilliseconds);
220 continue;
221 }
222
223 // Otherwise we're playing, so advance the frame and keep reading from the 229 // Otherwise we're playing, so advance the frame and keep reading from the
224 // decoder. |frames_| might be empty if we seeked to the very end of the 230 // decoder if we haven't reach end of stream.
225 // media where no frames were available. 231 if (!frames_.empty() && !frames_.front()->IsEndOfStream()) {
226 if (!frames_.empty()) {
227 DCHECK_EQ(current_frame_, frames_.front()); 232 DCHECK_EQ(current_frame_, frames_.front());
228 frames_.pop_front(); 233 frames_.pop_front();
229 ScheduleRead_Locked(); 234 ScheduleRead_Locked();
230 } 235 }
231 236
232 // While playing, we'll wait until a new frame arrives before updating 237 // While playing, we'll wait until a new frame arrives before updating
233 // |current_frame_|. 238 // |current_frame_|.
234 while (frames_.empty() && state_ == kPlaying) { 239 while (frames_.empty() && state_ == kPlaying) {
235 frame_available_.Wait(); 240 frame_available_.Wait();
236 } 241 }
237 242
238 // If we ended up transitioning out of playing while waiting for a new 243 // If we ended up transitioning out of playing while waiting for a new
239 // frame, restart the iteration. 244 // frame, restart the iteration.
240 if (state_ != kPlaying) { 245 if (state_ != kPlaying) {
241 continue; 246 continue;
242 } 247 }
243 248
249 // If the new front frame is end of stream, we've officially ended.
250 if (frames_.front()->IsEndOfStream()) {
251 state_ = kEnded;
252 host()->NotifyEnded();
253 continue;
254 }
255
244 // Update our current frame and attempt to grab the next frame. 256 // Update our current frame and attempt to grab the next frame.
245 current_frame_ = frames_.front(); 257 current_frame_ = frames_.front();
246 if (frames_.size() >= 2) { 258 if (frames_.size() >= 2 && !frames_[1]->IsEndOfStream()) {
247 next_frame = frames_[1]; 259 next_frame = frames_[1];
248 } 260 }
249 } 261 }
250 262
251 // Calculate our sleep duration. 263 // Calculate our sleep duration.
252 base::TimeDelta sleep = CalculateSleepDuration(next_frame, playback_rate); 264 base::TimeDelta sleep = CalculateSleepDuration(next_frame, playback_rate);
253 int sleep_ms = static_cast<int>(sleep.InMilliseconds()); 265 int sleep_ms = static_cast<int>(sleep.InMilliseconds());
254 266
255 // If we're too far behind to catch up, simply drop the frame. 267 // If we're too far behind to catch up, simply drop the frame.
256 // 268 //
(...skipping 12 matching lines...) Expand all
269 // Notify subclass that |current_frame_| has been updated. 281 // Notify subclass that |current_frame_| has been updated.
270 OnFrameAvailable(); 282 OnFrameAvailable();
271 283
272 PlatformThread::Sleep(sleep_ms); 284 PlatformThread::Sleep(sleep_ms);
273 } 285 }
274 } 286 }
275 287
276 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) { 288 void VideoRendererBase::GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out) {
277 AutoLock auto_lock(lock_); 289 AutoLock auto_lock(lock_);
278 // We should have initialized and have the current frame. 290 // We should have initialized and have the current frame.
279 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying); 291 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying ||
292 state_ == kEnded);
280 DCHECK(current_frame_); 293 DCHECK(current_frame_);
281 *frame_out = current_frame_; 294 *frame_out = current_frame_;
282 } 295 }
283 296
284 void VideoRendererBase::OnReadComplete(VideoFrame* frame) { 297 void VideoRendererBase::OnReadComplete(VideoFrame* frame) {
285 AutoLock auto_lock(lock_); 298 AutoLock auto_lock(lock_);
286 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying); 299 DCHECK(state_ == kPaused || state_ == kSeeking || state_ == kPlaying ||
300 state_ == kEnded);
287 DCHECK_GT(pending_reads_, 0u); 301 DCHECK_GT(pending_reads_, 0u);
288 --pending_reads_; 302 --pending_reads_;
289 303
290 // If this is an end of stream frame, don't enqueue it since it has no data. 304 // Enqueue the frame.
291 if (!frame->IsEndOfStream()) { 305 frames_.push_back(frame);
292 frames_.push_back(frame); 306 DCHECK_LE(frames_.size(), kMaxFrames);
293 DCHECK_LE(frames_.size(), kMaxFrames); 307 frame_available_.Signal();
294 frame_available_.Signal();
295 }
296 308
297 // Check for our preroll complete condition. 309 // Check for our preroll complete condition.
298 if (state_ == kSeeking) { 310 if (state_ == kSeeking) {
299 DCHECK(seek_callback_.get()); 311 DCHECK(seek_callback_.get());
300 if (frames_.size() == kMaxFrames || frame->IsEndOfStream()) { 312 if (frames_.size() == kMaxFrames) {
301 if (frames_.empty()) { 313 // We're paused, so make sure we update |current_frame_| to represent
302 // Eeep.. we seeked to somewhere where there's no video data (most 314 // our new location.
303 // likely the very end of the file). For user-friendliness, we'll 315 state_ = kPaused;
304 // create a black frame just in case |current_frame_| is old or garbage. 316 if (frames_.front()->IsEndOfStream()) {
305 CreateBlackFrame(&current_frame_); 317 VideoFrameImpl::CreateBlackFrame(width_, height_, &current_frame_);
306 } else { 318 } else {
307 // Update our current frame.
308 current_frame_ = frames_.front(); 319 current_frame_ = frames_.front();
309 } 320 }
310 // Because we might remain paused, we can't rely on ThreadMain() to 321
311 // notify the subclass the frame has been updated. 322 // Because we might remain paused (i.e., we were not playing before we
323 // received a seek), we can't rely on ThreadMain() to notify the subclass
324 // the frame has been updated.
312 DCHECK(current_frame_); 325 DCHECK(current_frame_);
313 state_ = kPaused;
314 OnFrameAvailable(); 326 OnFrameAvailable();
315 327
316 seek_callback_->Run(); 328 seek_callback_->Run();
317 seek_callback_.reset(); 329 seek_callback_.reset();
318 } 330 }
319 } else if (state_ == kPaused && pending_reads_ == 0) { 331 } else if (state_ == kPaused && pending_reads_ == 0) {
320 // No more pending reads! We're now officially "paused". 332 // No more pending reads! We're now officially "paused".
321 if (pause_callback_.get()) { 333 if (pause_callback_.get()) {
322 pause_callback_->Run(); 334 pause_callback_->Run();
323 pause_callback_.reset(); 335 pause_callback_.reset();
324 } 336 }
325 } 337 }
326 } 338 }
327 339
328 void VideoRendererBase::ScheduleRead_Locked() { 340 void VideoRendererBase::ScheduleRead_Locked() {
329 lock_.AssertAcquired(); 341 lock_.AssertAcquired();
342 DCHECK_NE(kEnded, state_);
330 DCHECK_LT(pending_reads_, kMaxFrames); 343 DCHECK_LT(pending_reads_, kMaxFrames);
331 ++pending_reads_; 344 ++pending_reads_;
332 decoder_->Read(NewCallback(this, &VideoRendererBase::OnReadComplete)); 345 decoder_->Read(NewCallback(this, &VideoRendererBase::OnReadComplete));
333 } 346 }
334 347
335 base::TimeDelta VideoRendererBase::CalculateSleepDuration( 348 base::TimeDelta VideoRendererBase::CalculateSleepDuration(
336 VideoFrame* next_frame, float playback_rate) { 349 VideoFrame* next_frame, float playback_rate) {
337 // Determine the current and next presentation timestamps. 350 // Determine the current and next presentation timestamps.
338 base::TimeDelta now = host()->GetTime(); 351 base::TimeDelta now = host()->GetTime();
339 base::TimeDelta this_pts = current_frame_->GetTimestamp(); 352 base::TimeDelta this_pts = current_frame_->GetTimestamp();
(...skipping 14 matching lines...) Expand all
354 sleep = next_pts - now; 367 sleep = next_pts - now;
355 previous_time_ = now; 368 previous_time_ = now;
356 } 369 }
357 370
358 // Scale our sleep based on the playback rate. 371 // Scale our sleep based on the playback rate.
359 // TODO(scherkus): floating point badness and degrade gracefully. 372 // TODO(scherkus): floating point badness and degrade gracefully.
360 return base::TimeDelta::FromMicroseconds( 373 return base::TimeDelta::FromMicroseconds(
361 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); 374 static_cast<int64>(sleep.InMicroseconds() / playback_rate));
362 } 375 }
363 376
364 void VideoRendererBase::CreateBlackFrame(scoped_refptr<VideoFrame>* frame_out) {
365 DCHECK_GT(width_, 0);
366 DCHECK_GT(height_, 0);
367 *frame_out = NULL;
368
369 // Create our frame.
370 scoped_refptr<VideoFrame> frame;
371 const base::TimeDelta kZero;
372 VideoFrameImpl::CreateFrame(VideoSurface::YV12, width_, height_, kZero, kZero,
373 &frame);
374 DCHECK(frame);
375
376 // Now set the data to YUV(0,128,128).
377 VideoSurface surface;
378 frame->Lock(&surface);
379 DCHECK_EQ(VideoSurface::YV12, surface.format) << "Expected YV12 surface";
380
381 // Fill the Y plane.
382 for (size_t i = 0; i < surface.height; ++i) {
383 memset(surface.data[VideoSurface::kYPlane], 0x00, surface.width);
384 surface.data[VideoSurface::kYPlane]
385 += surface.strides[VideoSurface::kYPlane];
386 }
387
388 // Fill the U and V planes.
389 for (size_t i = 0; i < (surface.height / 2); ++i) {
390 memset(surface.data[VideoSurface::kUPlane], 0x80, surface.width / 2);
391 memset(surface.data[VideoSurface::kVPlane], 0x80, surface.width / 2);
392 surface.data[VideoSurface::kUPlane]
393 += surface.strides[VideoSurface::kUPlane];
394 surface.data[VideoSurface::kVPlane]
395 += surface.strides[VideoSurface::kVPlane];
396 }
397 frame->Unlock();
398
399 // Success!
400 *frame_out = frame;
401 }
402
403 } // namespace media 377 } // 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