| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/filters/video_renderer_impl.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback.h" | |
| 9 #include "base/callback_helpers.h" | |
| 10 #include "base/location.h" | |
| 11 #include "base/single_thread_task_runner.h" | |
| 12 #include "base/threading/platform_thread.h" | |
| 13 #include "base/trace_event/trace_event.h" | |
| 14 #include "media/base/bind_to_current_loop.h" | |
| 15 #include "media/base/buffers.h" | |
| 16 #include "media/base/limits.h" | |
| 17 #include "media/base/pipeline.h" | |
| 18 #include "media/base/video_frame.h" | |
| 19 | |
| 20 namespace media { | |
| 21 | |
| 22 VideoRendererImpl::VideoRendererImpl( | |
| 23 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | |
| 24 ScopedVector<VideoDecoder> decoders, | |
| 25 bool drop_frames, | |
| 26 const scoped_refptr<MediaLog>& media_log) | |
| 27 : task_runner_(task_runner), | |
| 28 video_frame_stream_( | |
| 29 new VideoFrameStream(task_runner, decoders.Pass(), media_log)), | |
| 30 low_delay_(false), | |
| 31 received_end_of_stream_(false), | |
| 32 rendered_end_of_stream_(false), | |
| 33 frame_available_(&lock_), | |
| 34 state_(kUninitialized), | |
| 35 thread_(), | |
| 36 pending_read_(false), | |
| 37 drop_frames_(drop_frames), | |
| 38 buffering_state_(BUFFERING_HAVE_NOTHING), | |
| 39 last_timestamp_(kNoTimestamp()), | |
| 40 last_painted_timestamp_(kNoTimestamp()), | |
| 41 frames_decoded_(0), | |
| 42 frames_dropped_(0), | |
| 43 is_shutting_down_(false), | |
| 44 weak_factory_(this) { | |
| 45 } | |
| 46 | |
| 47 VideoRendererImpl::~VideoRendererImpl() { | |
| 48 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 49 | |
| 50 { | |
| 51 base::AutoLock auto_lock(lock_); | |
| 52 is_shutting_down_ = true; | |
| 53 frame_available_.Signal(); | |
| 54 } | |
| 55 | |
| 56 if (!thread_.is_null()) | |
| 57 base::PlatformThread::Join(thread_); | |
| 58 | |
| 59 if (!init_cb_.is_null()) | |
| 60 base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT); | |
| 61 | |
| 62 if (!flush_cb_.is_null()) | |
| 63 base::ResetAndReturn(&flush_cb_).Run(); | |
| 64 } | |
| 65 | |
| 66 void VideoRendererImpl::Flush(const base::Closure& callback) { | |
| 67 DVLOG(1) << __FUNCTION__; | |
| 68 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 69 base::AutoLock auto_lock(lock_); | |
| 70 DCHECK_EQ(state_, kPlaying); | |
| 71 flush_cb_ = callback; | |
| 72 state_ = kFlushing; | |
| 73 | |
| 74 // This is necessary if the |video_frame_stream_| has already seen an end of | |
| 75 // stream and needs to drain it before flushing it. | |
| 76 ready_frames_.clear(); | |
| 77 if (buffering_state_ != BUFFERING_HAVE_NOTHING) { | |
| 78 buffering_state_ = BUFFERING_HAVE_NOTHING; | |
| 79 buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING); | |
| 80 } | |
| 81 received_end_of_stream_ = false; | |
| 82 rendered_end_of_stream_ = false; | |
| 83 | |
| 84 video_frame_stream_->Reset( | |
| 85 base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone, | |
| 86 weak_factory_.GetWeakPtr())); | |
| 87 } | |
| 88 | |
| 89 void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { | |
| 90 DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")"; | |
| 91 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 92 base::AutoLock auto_lock(lock_); | |
| 93 DCHECK_EQ(state_, kFlushed); | |
| 94 DCHECK(!pending_read_); | |
| 95 DCHECK(ready_frames_.empty()); | |
| 96 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | |
| 97 | |
| 98 state_ = kPlaying; | |
| 99 start_timestamp_ = timestamp; | |
| 100 AttemptRead_Locked(); | |
| 101 } | |
| 102 | |
| 103 void VideoRendererImpl::Initialize( | |
| 104 DemuxerStream* stream, | |
| 105 const PipelineStatusCB& init_cb, | |
| 106 const SetDecryptorReadyCB& set_decryptor_ready_cb, | |
| 107 const StatisticsCB& statistics_cb, | |
| 108 const BufferingStateCB& buffering_state_cb, | |
| 109 const PaintCB& paint_cb, | |
| 110 const base::Closure& ended_cb, | |
| 111 const PipelineStatusCB& error_cb, | |
| 112 const TimeDeltaCB& get_time_cb) { | |
| 113 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 114 base::AutoLock auto_lock(lock_); | |
| 115 DCHECK(stream); | |
| 116 DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); | |
| 117 DCHECK(!init_cb.is_null()); | |
| 118 DCHECK(!statistics_cb.is_null()); | |
| 119 DCHECK(!buffering_state_cb.is_null()); | |
| 120 DCHECK(!paint_cb.is_null()); | |
| 121 DCHECK(!ended_cb.is_null()); | |
| 122 DCHECK(!get_time_cb.is_null()); | |
| 123 DCHECK_EQ(kUninitialized, state_); | |
| 124 | |
| 125 low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE); | |
| 126 | |
| 127 // Always post |init_cb_| because |this| could be destroyed if initialization | |
| 128 // failed. | |
| 129 init_cb_ = BindToCurrentLoop(init_cb); | |
| 130 | |
| 131 statistics_cb_ = statistics_cb; | |
| 132 buffering_state_cb_ = buffering_state_cb; | |
| 133 paint_cb_ = paint_cb, | |
| 134 ended_cb_ = ended_cb; | |
| 135 error_cb_ = error_cb; | |
| 136 get_time_cb_ = get_time_cb; | |
| 137 state_ = kInitializing; | |
| 138 | |
| 139 video_frame_stream_->Initialize( | |
| 140 stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized, | |
| 141 weak_factory_.GetWeakPtr()), | |
| 142 set_decryptor_ready_cb, statistics_cb); | |
| 143 } | |
| 144 | |
| 145 void VideoRendererImpl::CreateVideoThread() { | |
| 146 // This may fail and cause a crash if there are too many threads created in | |
| 147 // the current process. See http://crbug.com/443291 | |
| 148 CHECK(base::PlatformThread::Create(0, this, &thread_)); | |
| 149 | |
| 150 #if defined(OS_WIN) | |
| 151 // Bump up our priority so our sleeping is more accurate. | |
| 152 // TODO(scherkus): find out if this is necessary, but it seems to help. | |
| 153 ::SetThreadPriority(thread_.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL); | |
| 154 #endif // defined(OS_WIN) | |
| 155 } | |
| 156 | |
| 157 void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { | |
| 158 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 159 base::AutoLock auto_lock(lock_); | |
| 160 DCHECK_EQ(state_, kInitializing); | |
| 161 | |
| 162 if (!success) { | |
| 163 state_ = kUninitialized; | |
| 164 base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); | |
| 165 return; | |
| 166 } | |
| 167 | |
| 168 // We're all good! Consider ourselves flushed. (ThreadMain() should never | |
| 169 // see us in the kUninitialized state). | |
| 170 // Since we had an initial Preroll(), we consider ourself flushed, because we | |
| 171 // have not populated any buffers yet. | |
| 172 state_ = kFlushed; | |
| 173 | |
| 174 CreateVideoThread(); | |
| 175 | |
| 176 base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); | |
| 177 } | |
| 178 | |
| 179 // PlatformThread::Delegate implementation. | |
| 180 void VideoRendererImpl::ThreadMain() { | |
| 181 base::PlatformThread::SetName("CrVideoRenderer"); | |
| 182 | |
| 183 // The number of milliseconds to idle when we do not have anything to do. | |
| 184 // Nothing special about the value, other than we're being more OS-friendly | |
| 185 // than sleeping for 1 millisecond. | |
| 186 // | |
| 187 // TODO(scherkus): switch to pure event-driven frame timing instead of this | |
| 188 // kIdleTimeDelta business http://crbug.com/106874 | |
| 189 const base::TimeDelta kIdleTimeDelta = | |
| 190 base::TimeDelta::FromMilliseconds(10); | |
| 191 | |
| 192 // If we have no frames and haven't painted any frame for certain amount of | |
| 193 // time, declare BUFFERING_HAVE_NOTHING. | |
| 194 const base::TimeDelta kTimeToDeclareHaveNothing = | |
| 195 base::TimeDelta::FromSeconds(3); | |
| 196 | |
| 197 for (;;) { | |
| 198 base::AutoLock auto_lock(lock_); | |
| 199 | |
| 200 // Thread exit condition. | |
| 201 if (is_shutting_down_) | |
| 202 return; | |
| 203 | |
| 204 // Remain idle as long as we're not playing. | |
| 205 if (state_ != kPlaying || buffering_state_ != BUFFERING_HAVE_ENOUGH) { | |
| 206 UpdateStatsAndWait_Locked(kIdleTimeDelta); | |
| 207 continue; | |
| 208 } | |
| 209 | |
| 210 base::TimeDelta now = get_time_cb_.Run(); | |
| 211 | |
| 212 // Remain idle until we have the next frame ready for rendering. | |
| 213 if (ready_frames_.empty()) { | |
| 214 if (received_end_of_stream_) { | |
| 215 if (!rendered_end_of_stream_) { | |
| 216 rendered_end_of_stream_ = true; | |
| 217 task_runner_->PostTask(FROM_HERE, ended_cb_); | |
| 218 } | |
| 219 } else if (last_painted_timestamp_ != kNoTimestamp() && | |
| 220 now - last_painted_timestamp_ >= kTimeToDeclareHaveNothing) { | |
| 221 buffering_state_ = BUFFERING_HAVE_NOTHING; | |
| 222 task_runner_->PostTask( | |
| 223 FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING)); | |
| 224 } | |
| 225 | |
| 226 UpdateStatsAndWait_Locked(kIdleTimeDelta); | |
| 227 continue; | |
| 228 } | |
| 229 | |
| 230 base::TimeDelta target_paint_timestamp = ready_frames_.front()->timestamp(); | |
| 231 base::TimeDelta latest_paint_timestamp; | |
| 232 | |
| 233 // Deadline is defined as the duration between this frame and the next | |
| 234 // frame, using the delta between this frame and the previous frame as the | |
| 235 // assumption for frame duration. | |
| 236 // | |
| 237 // TODO(scherkus): This can be vastly improved. Use a histogram to measure | |
| 238 // the accuracy of our frame timing code. http://crbug.com/149829 | |
| 239 if (last_timestamp_ == kNoTimestamp()) { | |
| 240 latest_paint_timestamp = base::TimeDelta::Max(); | |
| 241 } else { | |
| 242 base::TimeDelta duration = target_paint_timestamp - last_timestamp_; | |
| 243 latest_paint_timestamp = target_paint_timestamp + duration; | |
| 244 } | |
| 245 | |
| 246 // Remain idle until we've reached our target paint window. | |
| 247 if (now < target_paint_timestamp) { | |
| 248 UpdateStatsAndWait_Locked(kIdleTimeDelta); | |
| 249 continue; | |
| 250 } | |
| 251 | |
| 252 if (now > latest_paint_timestamp && drop_frames_) { | |
| 253 DropNextReadyFrame_Locked(); | |
| 254 continue; | |
| 255 } | |
| 256 | |
| 257 // Congratulations! You've made it past the video frame timing gauntlet. | |
| 258 // | |
| 259 // At this point enough time has passed that the next frame that ready for | |
| 260 // rendering. | |
| 261 PaintNextReadyFrame_Locked(); | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 void VideoRendererImpl::PaintNextReadyFrame_Locked() { | |
| 266 lock_.AssertAcquired(); | |
| 267 | |
| 268 scoped_refptr<VideoFrame> next_frame = ready_frames_.front(); | |
| 269 ready_frames_.pop_front(); | |
| 270 frames_decoded_++; | |
| 271 | |
| 272 last_timestamp_ = next_frame->timestamp(); | |
| 273 last_painted_timestamp_ = next_frame->timestamp(); | |
| 274 | |
| 275 paint_cb_.Run(next_frame); | |
| 276 | |
| 277 task_runner_->PostTask( | |
| 278 FROM_HERE, | |
| 279 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); | |
| 280 } | |
| 281 | |
| 282 void VideoRendererImpl::DropNextReadyFrame_Locked() { | |
| 283 TRACE_EVENT0("media", "VideoRendererImpl:frameDropped"); | |
| 284 | |
| 285 lock_.AssertAcquired(); | |
| 286 | |
| 287 last_timestamp_ = ready_frames_.front()->timestamp(); | |
| 288 ready_frames_.pop_front(); | |
| 289 frames_decoded_++; | |
| 290 frames_dropped_++; | |
| 291 | |
| 292 task_runner_->PostTask( | |
| 293 FROM_HERE, | |
| 294 base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); | |
| 295 } | |
| 296 | |
| 297 void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, | |
| 298 const scoped_refptr<VideoFrame>& frame) { | |
| 299 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 300 base::AutoLock auto_lock(lock_); | |
| 301 DCHECK_NE(state_, kUninitialized); | |
| 302 DCHECK_NE(state_, kFlushed); | |
| 303 | |
| 304 CHECK(pending_read_); | |
| 305 pending_read_ = false; | |
| 306 | |
| 307 if (status == VideoFrameStream::DECODE_ERROR || | |
| 308 status == VideoFrameStream::DECRYPT_ERROR) { | |
| 309 DCHECK(!frame.get()); | |
| 310 PipelineStatus error = PIPELINE_ERROR_DECODE; | |
| 311 if (status == VideoFrameStream::DECRYPT_ERROR) | |
| 312 error = PIPELINE_ERROR_DECRYPT; | |
| 313 task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error)); | |
| 314 return; | |
| 315 } | |
| 316 | |
| 317 // Already-queued VideoFrameStream ReadCB's can fire after various state | |
| 318 // transitions have happened; in that case just drop those frames immediately. | |
| 319 if (state_ == kFlushing) | |
| 320 return; | |
| 321 | |
| 322 DCHECK_EQ(state_, kPlaying); | |
| 323 | |
| 324 // Can happen when demuxers are preparing for a new Seek(). | |
| 325 if (!frame.get()) { | |
| 326 DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED); | |
| 327 return; | |
| 328 } | |
| 329 | |
| 330 if (frame->end_of_stream()) { | |
| 331 DCHECK(!received_end_of_stream_); | |
| 332 received_end_of_stream_ = true; | |
| 333 } else { | |
| 334 // Maintain the latest frame decoded so the correct frame is displayed after | |
| 335 // prerolling has completed. | |
| 336 if (frame->timestamp() <= start_timestamp_) | |
| 337 ready_frames_.clear(); | |
| 338 AddReadyFrame_Locked(frame); | |
| 339 } | |
| 340 | |
| 341 // Signal buffering state if we've met our conditions for having enough data. | |
| 342 if (buffering_state_ != BUFFERING_HAVE_ENOUGH && HaveEnoughData_Locked()) | |
| 343 TransitionToHaveEnough_Locked(); | |
| 344 | |
| 345 // Always request more decoded video if we have capacity. This serves two | |
| 346 // purposes: | |
| 347 // 1) Prerolling while paused | |
| 348 // 2) Keeps decoding going if video rendering thread starts falling behind | |
| 349 AttemptRead_Locked(); | |
| 350 } | |
| 351 | |
| 352 bool VideoRendererImpl::HaveEnoughData_Locked() { | |
| 353 DCHECK_EQ(state_, kPlaying); | |
| 354 return received_end_of_stream_ || | |
| 355 !video_frame_stream_->CanReadWithoutStalling() || | |
| 356 ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames) || | |
| 357 (low_delay_ && ready_frames_.size() > 0); | |
| 358 } | |
| 359 | |
| 360 void VideoRendererImpl::TransitionToHaveEnough_Locked() { | |
| 361 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 362 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | |
| 363 | |
| 364 if (!ready_frames_.empty()) { | |
| 365 // Because the clock might remain paused in for an undetermined amount | |
| 366 // of time (e.g., seeking while paused), paint the first frame. | |
| 367 PaintNextReadyFrame_Locked(); | |
| 368 } | |
| 369 | |
| 370 buffering_state_ = BUFFERING_HAVE_ENOUGH; | |
| 371 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH); | |
| 372 } | |
| 373 | |
| 374 void VideoRendererImpl::AddReadyFrame_Locked( | |
| 375 const scoped_refptr<VideoFrame>& frame) { | |
| 376 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 377 lock_.AssertAcquired(); | |
| 378 DCHECK(!frame->end_of_stream()); | |
| 379 | |
| 380 ready_frames_.push_back(frame); | |
| 381 DCHECK_LE(ready_frames_.size(), | |
| 382 static_cast<size_t>(limits::kMaxVideoFrames)); | |
| 383 | |
| 384 // Avoid needlessly waking up |thread_| unless playing. | |
| 385 if (state_ == kPlaying) | |
| 386 frame_available_.Signal(); | |
| 387 } | |
| 388 | |
| 389 void VideoRendererImpl::AttemptRead() { | |
| 390 base::AutoLock auto_lock(lock_); | |
| 391 AttemptRead_Locked(); | |
| 392 } | |
| 393 | |
| 394 void VideoRendererImpl::AttemptRead_Locked() { | |
| 395 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 396 lock_.AssertAcquired(); | |
| 397 | |
| 398 if (pending_read_ || received_end_of_stream_ || | |
| 399 ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { | |
| 400 return; | |
| 401 } | |
| 402 | |
| 403 switch (state_) { | |
| 404 case kPlaying: | |
| 405 pending_read_ = true; | |
| 406 video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady, | |
| 407 weak_factory_.GetWeakPtr())); | |
| 408 return; | |
| 409 | |
| 410 case kUninitialized: | |
| 411 case kInitializing: | |
| 412 case kFlushing: | |
| 413 case kFlushed: | |
| 414 return; | |
| 415 } | |
| 416 } | |
| 417 | |
| 418 void VideoRendererImpl::OnVideoFrameStreamResetDone() { | |
| 419 base::AutoLock auto_lock(lock_); | |
| 420 DCHECK_EQ(kFlushing, state_); | |
| 421 DCHECK(!pending_read_); | |
| 422 DCHECK(ready_frames_.empty()); | |
| 423 DCHECK(!received_end_of_stream_); | |
| 424 DCHECK(!rendered_end_of_stream_); | |
| 425 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | |
| 426 | |
| 427 state_ = kFlushed; | |
| 428 last_timestamp_ = kNoTimestamp(); | |
| 429 last_painted_timestamp_ = kNoTimestamp(); | |
| 430 base::ResetAndReturn(&flush_cb_).Run(); | |
| 431 } | |
| 432 | |
| 433 void VideoRendererImpl::UpdateStatsAndWait_Locked( | |
| 434 base::TimeDelta wait_duration) { | |
| 435 lock_.AssertAcquired(); | |
| 436 DCHECK_GE(frames_decoded_, 0); | |
| 437 DCHECK_LE(frames_dropped_, frames_decoded_); | |
| 438 | |
| 439 if (frames_decoded_) { | |
| 440 PipelineStatistics statistics; | |
| 441 statistics.video_frames_decoded = frames_decoded_; | |
| 442 statistics.video_frames_dropped = frames_dropped_; | |
| 443 task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics)); | |
| 444 | |
| 445 frames_decoded_ = 0; | |
| 446 frames_dropped_ = 0; | |
| 447 } | |
| 448 | |
| 449 frame_available_.TimedWait(wait_duration); | |
| 450 } | |
| 451 | |
| 452 } // namespace media | |
| OLD | NEW |