| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "media/filters/video_renderer_base.h" |
| 6 |
| 5 #include "base/bind.h" | 7 #include "base/bind.h" |
| 6 #include "base/callback.h" | 8 #include "base/callback.h" |
| 7 #include "base/callback_helpers.h" | 9 #include "base/callback_helpers.h" |
| 8 #include "base/threading/platform_thread.h" | 10 #include "base/threading/platform_thread.h" |
| 9 #include "media/base/buffers.h" | 11 #include "media/base/buffers.h" |
| 10 #include "media/base/filter_host.h" | |
| 11 #include "media/base/limits.h" | 12 #include "media/base/limits.h" |
| 12 #include "media/base/pipeline.h" | 13 #include "media/base/pipeline.h" |
| 13 #include "media/base/video_frame.h" | 14 #include "media/base/video_frame.h" |
| 14 #include "media/filters/video_renderer_base.h" | |
| 15 | 15 |
| 16 namespace media { | 16 namespace media { |
| 17 | 17 |
| 18 VideoRendererBase::VideoRendererBase(const base::Closure& paint_cb, | 18 VideoRendererBase::VideoRendererBase(const base::Closure& paint_cb, |
| 19 const SetOpaqueCB& set_opaque_cb, | 19 const SetOpaqueCB& set_opaque_cb, |
| 20 bool drop_frames) | 20 bool drop_frames) |
| 21 : host_(NULL), | 21 : frame_available_(&lock_), |
| 22 frame_available_(&lock_), | |
| 23 state_(kUninitialized), | 22 state_(kUninitialized), |
| 24 thread_(base::kNullThreadHandle), | 23 thread_(base::kNullThreadHandle), |
| 25 pending_read_(false), | 24 pending_read_(false), |
| 26 pending_paint_(false), | 25 pending_paint_(false), |
| 27 pending_paint_with_last_available_(false), | 26 pending_paint_with_last_available_(false), |
| 28 drop_frames_(drop_frames), | 27 drop_frames_(drop_frames), |
| 29 playback_rate_(0), | 28 playback_rate_(0), |
| 30 paint_cb_(paint_cb), | 29 paint_cb_(paint_cb), |
| 31 set_opaque_cb_(set_opaque_cb) { | 30 set_opaque_cb_(set_opaque_cb) { |
| 32 DCHECK(!paint_cb_.is_null()); | 31 DCHECK(!paint_cb_.is_null()); |
| 33 } | 32 } |
| 34 | 33 |
| 35 void VideoRendererBase::SetHost(FilterHost* host) { | |
| 36 DCHECK(host); | |
| 37 DCHECK(!host_); | |
| 38 host_ = host; | |
| 39 } | |
| 40 | |
| 41 void VideoRendererBase::Play(const base::Closure& callback) { | 34 void VideoRendererBase::Play(const base::Closure& callback) { |
| 42 base::AutoLock auto_lock(lock_); | 35 base::AutoLock auto_lock(lock_); |
| 43 DCHECK_EQ(kPrerolled, state_); | 36 DCHECK_EQ(kPrerolled, state_); |
| 44 state_ = kPlaying; | 37 state_ = kPlaying; |
| 45 callback.Run(); | 38 callback.Run(); |
| 46 } | 39 } |
| 47 | 40 |
| 48 void VideoRendererBase::Pause(const base::Closure& callback) { | 41 void VideoRendererBase::Pause(const base::Closure& callback) { |
| 49 base::AutoLock auto_lock(lock_); | 42 base::AutoLock auto_lock(lock_); |
| 50 DCHECK(state_ != kUninitialized || state_ == kError); | 43 DCHECK(state_ != kUninitialized || state_ == kError); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 107 DCHECK(!cb.is_null()); | 100 DCHECK(!cb.is_null()); |
| 108 DCHECK(seek_cb_.is_null()); | 101 DCHECK(seek_cb_.is_null()); |
| 109 | 102 |
| 110 state_ = kSeeking; | 103 state_ = kSeeking; |
| 111 seek_cb_ = cb; | 104 seek_cb_ = cb; |
| 112 seek_timestamp_ = time; | 105 seek_timestamp_ = time; |
| 113 AttemptRead_Locked(); | 106 AttemptRead_Locked(); |
| 114 } | 107 } |
| 115 | 108 |
| 116 void VideoRendererBase::Initialize(const scoped_refptr<VideoDecoder>& decoder, | 109 void VideoRendererBase::Initialize(const scoped_refptr<VideoDecoder>& decoder, |
| 117 const PipelineStatusCB& status_cb, | 110 const PipelineStatusCB& init_cb, |
| 118 const StatisticsCB& statistics_cb, | 111 const StatisticsCB& statistics_cb, |
| 119 const TimeCB& time_cb) { | 112 const TimeCB& time_cb, |
| 113 const NaturalSizeChangedCB& size_changed_cb, |
| 114 const base::Closure& ended_cb, |
| 115 const PipelineStatusCB& error_cb, |
| 116 const TimeDeltaCB& get_time_cb, |
| 117 const TimeDeltaCB& get_duration_cb) { |
| 120 base::AutoLock auto_lock(lock_); | 118 base::AutoLock auto_lock(lock_); |
| 121 DCHECK(decoder); | 119 DCHECK(decoder); |
| 122 DCHECK(!status_cb.is_null()); | 120 DCHECK(!init_cb.is_null()); |
| 123 DCHECK(!statistics_cb.is_null()); | 121 DCHECK(!statistics_cb.is_null()); |
| 124 DCHECK(!time_cb.is_null()); | 122 DCHECK(!time_cb.is_null()); |
| 123 DCHECK(!size_changed_cb.is_null()); |
| 124 DCHECK(!ended_cb.is_null()); |
| 125 DCHECK(!get_time_cb.is_null()); |
| 126 DCHECK(!get_duration_cb.is_null()); |
| 125 DCHECK_EQ(kUninitialized, state_); | 127 DCHECK_EQ(kUninitialized, state_); |
| 126 decoder_ = decoder; | 128 decoder_ = decoder; |
| 127 | 129 |
| 128 statistics_cb_ = statistics_cb; | 130 statistics_cb_ = statistics_cb; |
| 129 time_cb_ = time_cb; | 131 time_cb_ = time_cb; |
| 132 size_changed_cb_ = size_changed_cb; |
| 133 ended_cb_ = ended_cb; |
| 134 error_cb_ = error_cb; |
| 135 get_time_cb_ = get_time_cb; |
| 136 get_duration_cb_ = get_duration_cb; |
| 130 | 137 |
| 131 // Notify the pipeline of the video dimensions. | 138 // Notify the pipeline of the video dimensions. |
| 132 host_->SetNaturalVideoSize(decoder_->natural_size()); | 139 size_changed_cb_.Run(decoder_->natural_size()); |
| 133 | 140 |
| 134 // We're all good! Consider ourselves flushed. (ThreadMain() should never | 141 // We're all good! Consider ourselves flushed. (ThreadMain() should never |
| 135 // see us in the kUninitialized state). | 142 // see us in the kUninitialized state). |
| 136 // Since we had an initial Seek, we consider ourself flushed, because we | 143 // Since we had an initial Seek, we consider ourself flushed, because we |
| 137 // have not populated any buffers yet. | 144 // have not populated any buffers yet. |
| 138 state_ = kFlushed; | 145 state_ = kFlushed; |
| 139 | 146 |
| 140 set_opaque_cb_.Run(!decoder->HasAlpha()); | 147 set_opaque_cb_.Run(!decoder->HasAlpha()); |
| 141 set_opaque_cb_.Reset(); | 148 set_opaque_cb_.Reset(); |
| 142 | 149 |
| 143 // Create our video thread. | 150 // Create our video thread. |
| 144 if (!base::PlatformThread::Create(0, this, &thread_)) { | 151 if (!base::PlatformThread::Create(0, this, &thread_)) { |
| 145 NOTREACHED() << "Video thread creation failed"; | 152 NOTREACHED() << "Video thread creation failed"; |
| 146 state_ = kError; | 153 state_ = kError; |
| 147 status_cb.Run(PIPELINE_ERROR_INITIALIZATION_FAILED); | 154 init_cb.Run(PIPELINE_ERROR_INITIALIZATION_FAILED); |
| 148 return; | 155 return; |
| 149 } | 156 } |
| 150 | 157 |
| 151 #if defined(OS_WIN) | 158 #if defined(OS_WIN) |
| 152 // Bump up our priority so our sleeping is more accurate. | 159 // Bump up our priority so our sleeping is more accurate. |
| 153 // TODO(scherkus): find out if this is necessary, but it seems to help. | 160 // TODO(scherkus): find out if this is necessary, but it seems to help. |
| 154 ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); | 161 ::SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); |
| 155 #endif // defined(OS_WIN) | 162 #endif // defined(OS_WIN) |
| 156 status_cb.Run(PIPELINE_OK); | 163 init_cb.Run(PIPELINE_OK); |
| 157 } | 164 } |
| 158 | 165 |
| 159 bool VideoRendererBase::HasEnded() { | 166 bool VideoRendererBase::HasEnded() { |
| 160 base::AutoLock auto_lock(lock_); | 167 base::AutoLock auto_lock(lock_); |
| 161 return state_ == kEnded; | 168 return state_ == kEnded; |
| 162 } | 169 } |
| 163 | 170 |
| 164 // PlatformThread::Delegate implementation. | 171 // PlatformThread::Delegate implementation. |
| 165 void VideoRendererBase::ThreadMain() { | 172 void VideoRendererBase::ThreadMain() { |
| 166 base::PlatformThread::SetName("CrVideoRenderer"); | 173 base::PlatformThread::SetName("CrVideoRenderer"); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 if (ready_frames_.empty()) { | 208 if (ready_frames_.empty()) { |
| 202 frame_available_.TimedWait(kIdleTimeDelta); | 209 frame_available_.TimedWait(kIdleTimeDelta); |
| 203 continue; | 210 continue; |
| 204 } | 211 } |
| 205 | 212 |
| 206 // Remain idle until we've initialized |current_frame_| via prerolling. | 213 // Remain idle until we've initialized |current_frame_| via prerolling. |
| 207 if (!current_frame_) { | 214 if (!current_frame_) { |
| 208 // This can happen if our preroll only contains end of stream frames. | 215 // This can happen if our preroll only contains end of stream frames. |
| 209 if (ready_frames_.front()->IsEndOfStream()) { | 216 if (ready_frames_.front()->IsEndOfStream()) { |
| 210 state_ = kEnded; | 217 state_ = kEnded; |
| 211 host_->NotifyEnded(); | 218 ended_cb_.Run(); |
| 212 ready_frames_.clear(); | 219 ready_frames_.clear(); |
| 213 | 220 |
| 214 // No need to sleep here as we idle when |state_ != kPlaying|. | 221 // No need to sleep here as we idle when |state_ != kPlaying|. |
| 215 continue; | 222 continue; |
| 216 } | 223 } |
| 217 | 224 |
| 218 frame_available_.TimedWait(kIdleTimeDelta); | 225 frame_available_.TimedWait(kIdleTimeDelta); |
| 219 continue; | 226 continue; |
| 220 } | 227 } |
| 221 | 228 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 240 // of time and also have the next frame that ready for rendering. | 247 // of time and also have the next frame that ready for rendering. |
| 241 | 248 |
| 242 | 249 |
| 243 // If the next frame is end of stream then we are truly at the end of the | 250 // If the next frame is end of stream then we are truly at the end of the |
| 244 // video stream. | 251 // video stream. |
| 245 // | 252 // |
| 246 // TODO(scherkus): deduplicate this end of stream check after we get rid of | 253 // TODO(scherkus): deduplicate this end of stream check after we get rid of |
| 247 // |current_frame_|. | 254 // |current_frame_|. |
| 248 if (ready_frames_.front()->IsEndOfStream()) { | 255 if (ready_frames_.front()->IsEndOfStream()) { |
| 249 state_ = kEnded; | 256 state_ = kEnded; |
| 250 host_->NotifyEnded(); | 257 ended_cb_.Run(); |
| 251 ready_frames_.clear(); | 258 ready_frames_.clear(); |
| 252 | 259 |
| 253 // No need to sleep here as we idle when |state_ != kPlaying|. | 260 // No need to sleep here as we idle when |state_ != kPlaying|. |
| 254 continue; | 261 continue; |
| 255 } | 262 } |
| 256 | 263 |
| 257 // We cannot update |current_frame_| until we've completed the pending | 264 // We cannot update |current_frame_| until we've completed the pending |
| 258 // paint. Furthermore, the pending paint might be really slow: check to | 265 // paint. Furthermore, the pending paint might be really slow: check to |
| 259 // see if we have any ready frames that we can drop if they've already | 266 // see if we have any ready frames that we can drop if they've already |
| 260 // expired. | 267 // expired. |
| 261 if (pending_paint_) { | 268 if (pending_paint_) { |
| 262 while (!ready_frames_.empty()) { | 269 while (!ready_frames_.empty()) { |
| 263 // Can't drop anything if we're at the end. | 270 // Can't drop anything if we're at the end. |
| 264 if (ready_frames_.front()->IsEndOfStream()) | 271 if (ready_frames_.front()->IsEndOfStream()) |
| 265 break; | 272 break; |
| 266 | 273 |
| 267 base::TimeDelta remaining_time = | 274 base::TimeDelta remaining_time = |
| 268 ready_frames_.front()->GetTimestamp() - host_->GetTime(); | 275 ready_frames_.front()->GetTimestamp() - get_time_cb_.Run(); |
| 269 | 276 |
| 270 // Still a chance we can render the frame! | 277 // Still a chance we can render the frame! |
| 271 if (remaining_time.InMicroseconds() > 0) | 278 if (remaining_time.InMicroseconds() > 0) |
| 272 break; | 279 break; |
| 273 | 280 |
| 274 if (!drop_frames_) | 281 if (!drop_frames_) |
| 275 break; | 282 break; |
| 276 | 283 |
| 277 // Frame dropped: read again. | 284 // Frame dropped: read again. |
| 278 ++frames_dropped; | 285 ++frames_dropped; |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 377 DCHECK(!frame); | 384 DCHECK(!frame); |
| 378 PipelineStatus error = PIPELINE_ERROR_DECODE; | 385 PipelineStatus error = PIPELINE_ERROR_DECODE; |
| 379 if (status == VideoDecoder::kDecryptError) | 386 if (status == VideoDecoder::kDecryptError) |
| 380 error = PIPELINE_ERROR_DECRYPT; | 387 error = PIPELINE_ERROR_DECRYPT; |
| 381 | 388 |
| 382 if (!seek_cb_.is_null()) { | 389 if (!seek_cb_.is_null()) { |
| 383 base::ResetAndReturn(&seek_cb_).Run(error); | 390 base::ResetAndReturn(&seek_cb_).Run(error); |
| 384 return; | 391 return; |
| 385 } | 392 } |
| 386 | 393 |
| 387 host_->SetError(error); | 394 error_cb_.Run(error); |
| 388 return; | 395 return; |
| 389 } | 396 } |
| 390 | 397 |
| 391 // Already-queued Decoder ReadCB's can fire after various state transitions | 398 // Already-queued Decoder ReadCB's can fire after various state transitions |
| 392 // have happened; in that case just drop those frames immediately. | 399 // have happened; in that case just drop those frames immediately. |
| 393 if (state_ == kStopped || state_ == kError || state_ == kFlushed || | 400 if (state_ == kStopped || state_ == kError || state_ == kFlushed || |
| 394 state_ == kFlushingDecoder) | 401 state_ == kFlushingDecoder) |
| 395 return; | 402 return; |
| 396 | 403 |
| 397 if (state_ == kFlushing) { | 404 if (state_ == kFlushing) { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 417 AttemptRead_Locked(); | 424 AttemptRead_Locked(); |
| 418 return; | 425 return; |
| 419 } | 426 } |
| 420 | 427 |
| 421 // Adjust the incoming frame if its rendering stop time is past the duration | 428 // Adjust the incoming frame if its rendering stop time is past the duration |
| 422 // of the video itself. This is typically the last frame of the video and | 429 // of the video itself. This is typically the last frame of the video and |
| 423 // occurs if the container specifies a duration that isn't a multiple of the | 430 // occurs if the container specifies a duration that isn't a multiple of the |
| 424 // frame rate. Another way for this to happen is for the container to state a | 431 // frame rate. Another way for this to happen is for the container to state a |
| 425 // smaller duration than the largest packet timestamp. | 432 // smaller duration than the largest packet timestamp. |
| 426 if (!frame->IsEndOfStream()) { | 433 if (!frame->IsEndOfStream()) { |
| 427 if (frame->GetTimestamp() > host_->GetDuration()) | 434 base::TimeDelta duration = get_duration_cb_.Run(); |
| 428 frame->SetTimestamp(host_->GetDuration()); | 435 if (frame->GetTimestamp() > duration) |
| 429 if ((frame->GetTimestamp() + frame->GetDuration()) > host_->GetDuration()) | 436 frame->SetTimestamp(duration); |
| 430 frame->SetDuration(host_->GetDuration() - frame->GetTimestamp()); | 437 if ((frame->GetTimestamp() + frame->GetDuration()) > duration) |
| 438 frame->SetDuration(duration - frame->GetTimestamp()); |
| 431 } | 439 } |
| 432 | 440 |
| 433 // This one's a keeper! Place it in the ready queue. | 441 // This one's a keeper! Place it in the ready queue. |
| 434 ready_frames_.push_back(frame); | 442 ready_frames_.push_back(frame); |
| 435 DCHECK_LE(NumFrames_Locked(), limits::kMaxVideoFrames); | 443 DCHECK_LE(NumFrames_Locked(), limits::kMaxVideoFrames); |
| 436 if (!frame->IsEndOfStream()) | 444 if (!frame->IsEndOfStream()) |
| 437 time_cb_.Run(frame->GetTimestamp() + frame->GetDuration()); | 445 time_cb_.Run(frame->GetTimestamp() + frame->GetDuration()); |
| 438 frame_available_.Signal(); | 446 frame_available_.Signal(); |
| 439 | 447 |
| 440 PipelineStatistics statistics; | 448 PipelineStatistics statistics; |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 509 state_ = kFlushed; | 517 state_ = kFlushed; |
| 510 current_frame_ = NULL; | 518 current_frame_ = NULL; |
| 511 base::ResetAndReturn(&flush_cb_).Run(); | 519 base::ResetAndReturn(&flush_cb_).Run(); |
| 512 } | 520 } |
| 513 } | 521 } |
| 514 | 522 |
| 515 base::TimeDelta VideoRendererBase::CalculateSleepDuration( | 523 base::TimeDelta VideoRendererBase::CalculateSleepDuration( |
| 516 const scoped_refptr<VideoFrame>& next_frame, | 524 const scoped_refptr<VideoFrame>& next_frame, |
| 517 float playback_rate) { | 525 float playback_rate) { |
| 518 // Determine the current and next presentation timestamps. | 526 // Determine the current and next presentation timestamps. |
| 519 base::TimeDelta now = host_->GetTime(); | 527 base::TimeDelta now = get_time_cb_.Run(); |
| 520 base::TimeDelta this_pts = current_frame_->GetTimestamp(); | 528 base::TimeDelta this_pts = current_frame_->GetTimestamp(); |
| 521 base::TimeDelta next_pts; | 529 base::TimeDelta next_pts; |
| 522 if (!next_frame->IsEndOfStream()) { | 530 if (!next_frame->IsEndOfStream()) { |
| 523 next_pts = next_frame->GetTimestamp(); | 531 next_pts = next_frame->GetTimestamp(); |
| 524 } else { | 532 } else { |
| 525 next_pts = this_pts + current_frame_->GetDuration(); | 533 next_pts = this_pts + current_frame_->GetDuration(); |
| 526 } | 534 } |
| 527 | 535 |
| 528 // Scale our sleep based on the playback rate. | 536 // Scale our sleep based on the playback rate. |
| 529 base::TimeDelta sleep = next_pts - now; | 537 base::TimeDelta sleep = next_pts - now; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 542 | 550 |
| 543 int VideoRendererBase::NumFrames_Locked() const { | 551 int VideoRendererBase::NumFrames_Locked() const { |
| 544 lock_.AssertAcquired(); | 552 lock_.AssertAcquired(); |
| 545 int outstanding_frames = | 553 int outstanding_frames = |
| 546 (current_frame_ ? 1 : 0) + (last_available_frame_ ? 1 : 0) + | 554 (current_frame_ ? 1 : 0) + (last_available_frame_ ? 1 : 0) + |
| 547 (current_frame_ && (current_frame_ == last_available_frame_) ? -1 : 0); | 555 (current_frame_ && (current_frame_ == last_available_frame_) ? -1 : 0); |
| 548 return ready_frames_.size() + outstanding_frames; | 556 return ready_frames_.size() + outstanding_frames; |
| 549 } | 557 } |
| 550 | 558 |
| 551 } // namespace media | 559 } // namespace media |
| OLD | NEW |