Index: media/filters/video_renderer_impl.cc |
diff --git a/media/filters/video_renderer_impl.cc b/media/filters/video_renderer_impl.cc |
deleted file mode 100644 |
index 482d54e844632093692c93b6d828fd74fb6a050c..0000000000000000000000000000000000000000 |
--- a/media/filters/video_renderer_impl.cc |
+++ /dev/null |
@@ -1,452 +0,0 @@ |
-// Copyright 2013 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "media/filters/video_renderer_impl.h" |
- |
-#include "base/bind.h" |
-#include "base/callback.h" |
-#include "base/callback_helpers.h" |
-#include "base/location.h" |
-#include "base/single_thread_task_runner.h" |
-#include "base/threading/platform_thread.h" |
-#include "base/trace_event/trace_event.h" |
-#include "media/base/bind_to_current_loop.h" |
-#include "media/base/buffers.h" |
-#include "media/base/limits.h" |
-#include "media/base/pipeline.h" |
-#include "media/base/video_frame.h" |
- |
-namespace media { |
- |
-VideoRendererImpl::VideoRendererImpl( |
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
- ScopedVector<VideoDecoder> decoders, |
- bool drop_frames, |
- const scoped_refptr<MediaLog>& media_log) |
- : task_runner_(task_runner), |
- video_frame_stream_( |
- new VideoFrameStream(task_runner, decoders.Pass(), media_log)), |
- low_delay_(false), |
- received_end_of_stream_(false), |
- rendered_end_of_stream_(false), |
- frame_available_(&lock_), |
- state_(kUninitialized), |
- thread_(), |
- pending_read_(false), |
- drop_frames_(drop_frames), |
- buffering_state_(BUFFERING_HAVE_NOTHING), |
- last_timestamp_(kNoTimestamp()), |
- last_painted_timestamp_(kNoTimestamp()), |
- frames_decoded_(0), |
- frames_dropped_(0), |
- is_shutting_down_(false), |
- weak_factory_(this) { |
-} |
- |
-VideoRendererImpl::~VideoRendererImpl() { |
- DCHECK(task_runner_->BelongsToCurrentThread()); |
- |
- { |
- base::AutoLock auto_lock(lock_); |
- is_shutting_down_ = true; |
- frame_available_.Signal(); |
- } |
- |
- if (!thread_.is_null()) |
- base::PlatformThread::Join(thread_); |
- |
- if (!init_cb_.is_null()) |
- base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT); |
- |
- if (!flush_cb_.is_null()) |
- base::ResetAndReturn(&flush_cb_).Run(); |
-} |
- |
-void VideoRendererImpl::Flush(const base::Closure& callback) { |
- DVLOG(1) << __FUNCTION__; |
- DCHECK(task_runner_->BelongsToCurrentThread()); |
- base::AutoLock auto_lock(lock_); |
- DCHECK_EQ(state_, kPlaying); |
- flush_cb_ = callback; |
- state_ = kFlushing; |
- |
- // This is necessary if the |video_frame_stream_| has already seen an end of |
- // stream and needs to drain it before flushing it. |
- ready_frames_.clear(); |
- if (buffering_state_ != BUFFERING_HAVE_NOTHING) { |
- buffering_state_ = BUFFERING_HAVE_NOTHING; |
- buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING); |
- } |
- received_end_of_stream_ = false; |
- rendered_end_of_stream_ = false; |
- |
- video_frame_stream_->Reset( |
- base::Bind(&VideoRendererImpl::OnVideoFrameStreamResetDone, |
- weak_factory_.GetWeakPtr())); |
-} |
- |
-void VideoRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { |
- DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")"; |
- DCHECK(task_runner_->BelongsToCurrentThread()); |
- base::AutoLock auto_lock(lock_); |
- DCHECK_EQ(state_, kFlushed); |
- DCHECK(!pending_read_); |
- DCHECK(ready_frames_.empty()); |
- DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
- |
- state_ = kPlaying; |
- start_timestamp_ = timestamp; |
- AttemptRead_Locked(); |
-} |
- |
-void VideoRendererImpl::Initialize( |
- DemuxerStream* stream, |
- const PipelineStatusCB& init_cb, |
- const SetDecryptorReadyCB& set_decryptor_ready_cb, |
- const StatisticsCB& statistics_cb, |
- const BufferingStateCB& buffering_state_cb, |
- const PaintCB& paint_cb, |
- const base::Closure& ended_cb, |
- const PipelineStatusCB& error_cb, |
- const TimeDeltaCB& get_time_cb) { |
- DCHECK(task_runner_->BelongsToCurrentThread()); |
- base::AutoLock auto_lock(lock_); |
- DCHECK(stream); |
- DCHECK_EQ(stream->type(), DemuxerStream::VIDEO); |
- DCHECK(!init_cb.is_null()); |
- DCHECK(!statistics_cb.is_null()); |
- DCHECK(!buffering_state_cb.is_null()); |
- DCHECK(!paint_cb.is_null()); |
- DCHECK(!ended_cb.is_null()); |
- DCHECK(!get_time_cb.is_null()); |
- DCHECK_EQ(kUninitialized, state_); |
- |
- low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE); |
- |
- // Always post |init_cb_| because |this| could be destroyed if initialization |
- // failed. |
- init_cb_ = BindToCurrentLoop(init_cb); |
- |
- statistics_cb_ = statistics_cb; |
- buffering_state_cb_ = buffering_state_cb; |
- paint_cb_ = paint_cb, |
- ended_cb_ = ended_cb; |
- error_cb_ = error_cb; |
- get_time_cb_ = get_time_cb; |
- state_ = kInitializing; |
- |
- video_frame_stream_->Initialize( |
- stream, base::Bind(&VideoRendererImpl::OnVideoFrameStreamInitialized, |
- weak_factory_.GetWeakPtr()), |
- set_decryptor_ready_cb, statistics_cb); |
-} |
- |
-void VideoRendererImpl::CreateVideoThread() { |
- // This may fail and cause a crash if there are too many threads created in |
- // the current process. See http://crbug.com/443291 |
- CHECK(base::PlatformThread::Create(0, this, &thread_)); |
- |
-#if defined(OS_WIN) |
- // Bump up our priority so our sleeping is more accurate. |
- // TODO(scherkus): find out if this is necessary, but it seems to help. |
- ::SetThreadPriority(thread_.platform_handle(), THREAD_PRIORITY_ABOVE_NORMAL); |
-#endif // defined(OS_WIN) |
-} |
- |
-void VideoRendererImpl::OnVideoFrameStreamInitialized(bool success) { |
- DCHECK(task_runner_->BelongsToCurrentThread()); |
- base::AutoLock auto_lock(lock_); |
- DCHECK_EQ(state_, kInitializing); |
- |
- if (!success) { |
- state_ = kUninitialized; |
- base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); |
- return; |
- } |
- |
- // We're all good! Consider ourselves flushed. (ThreadMain() should never |
- // see us in the kUninitialized state). |
- // Since we had an initial Preroll(), we consider ourself flushed, because we |
- // have not populated any buffers yet. |
- state_ = kFlushed; |
- |
- CreateVideoThread(); |
- |
- base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); |
-} |
- |
-// PlatformThread::Delegate implementation. |
-void VideoRendererImpl::ThreadMain() { |
- base::PlatformThread::SetName("CrVideoRenderer"); |
- |
- // The number of milliseconds to idle when we do not have anything to do. |
- // Nothing special about the value, other than we're being more OS-friendly |
- // than sleeping for 1 millisecond. |
- // |
- // TODO(scherkus): switch to pure event-driven frame timing instead of this |
- // kIdleTimeDelta business http://crbug.com/106874 |
- const base::TimeDelta kIdleTimeDelta = |
- base::TimeDelta::FromMilliseconds(10); |
- |
- // If we have no frames and haven't painted any frame for certain amount of |
- // time, declare BUFFERING_HAVE_NOTHING. |
- const base::TimeDelta kTimeToDeclareHaveNothing = |
- base::TimeDelta::FromSeconds(3); |
- |
- for (;;) { |
- base::AutoLock auto_lock(lock_); |
- |
- // Thread exit condition. |
- if (is_shutting_down_) |
- return; |
- |
- // Remain idle as long as we're not playing. |
- if (state_ != kPlaying || buffering_state_ != BUFFERING_HAVE_ENOUGH) { |
- UpdateStatsAndWait_Locked(kIdleTimeDelta); |
- continue; |
- } |
- |
- base::TimeDelta now = get_time_cb_.Run(); |
- |
- // Remain idle until we have the next frame ready for rendering. |
- if (ready_frames_.empty()) { |
- if (received_end_of_stream_) { |
- if (!rendered_end_of_stream_) { |
- rendered_end_of_stream_ = true; |
- task_runner_->PostTask(FROM_HERE, ended_cb_); |
- } |
- } else if (last_painted_timestamp_ != kNoTimestamp() && |
- now - last_painted_timestamp_ >= kTimeToDeclareHaveNothing) { |
- buffering_state_ = BUFFERING_HAVE_NOTHING; |
- task_runner_->PostTask( |
- FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING)); |
- } |
- |
- UpdateStatsAndWait_Locked(kIdleTimeDelta); |
- continue; |
- } |
- |
- base::TimeDelta target_paint_timestamp = ready_frames_.front()->timestamp(); |
- base::TimeDelta latest_paint_timestamp; |
- |
- // Deadline is defined as the duration between this frame and the next |
- // frame, using the delta between this frame and the previous frame as the |
- // assumption for frame duration. |
- // |
- // TODO(scherkus): This can be vastly improved. Use a histogram to measure |
- // the accuracy of our frame timing code. http://crbug.com/149829 |
- if (last_timestamp_ == kNoTimestamp()) { |
- latest_paint_timestamp = base::TimeDelta::Max(); |
- } else { |
- base::TimeDelta duration = target_paint_timestamp - last_timestamp_; |
- latest_paint_timestamp = target_paint_timestamp + duration; |
- } |
- |
- // Remain idle until we've reached our target paint window. |
- if (now < target_paint_timestamp) { |
- UpdateStatsAndWait_Locked(kIdleTimeDelta); |
- continue; |
- } |
- |
- if (now > latest_paint_timestamp && drop_frames_) { |
- DropNextReadyFrame_Locked(); |
- continue; |
- } |
- |
- // Congratulations! You've made it past the video frame timing gauntlet. |
- // |
- // At this point enough time has passed that the next frame that ready for |
- // rendering. |
- PaintNextReadyFrame_Locked(); |
- } |
-} |
- |
-void VideoRendererImpl::PaintNextReadyFrame_Locked() { |
- lock_.AssertAcquired(); |
- |
- scoped_refptr<VideoFrame> next_frame = ready_frames_.front(); |
- ready_frames_.pop_front(); |
- frames_decoded_++; |
- |
- last_timestamp_ = next_frame->timestamp(); |
- last_painted_timestamp_ = next_frame->timestamp(); |
- |
- paint_cb_.Run(next_frame); |
- |
- task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); |
-} |
- |
-void VideoRendererImpl::DropNextReadyFrame_Locked() { |
- TRACE_EVENT0("media", "VideoRendererImpl:frameDropped"); |
- |
- lock_.AssertAcquired(); |
- |
- last_timestamp_ = ready_frames_.front()->timestamp(); |
- ready_frames_.pop_front(); |
- frames_decoded_++; |
- frames_dropped_++; |
- |
- task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind(&VideoRendererImpl::AttemptRead, weak_factory_.GetWeakPtr())); |
-} |
- |
-void VideoRendererImpl::FrameReady(VideoFrameStream::Status status, |
- const scoped_refptr<VideoFrame>& frame) { |
- DCHECK(task_runner_->BelongsToCurrentThread()); |
- base::AutoLock auto_lock(lock_); |
- DCHECK_NE(state_, kUninitialized); |
- DCHECK_NE(state_, kFlushed); |
- |
- CHECK(pending_read_); |
- pending_read_ = false; |
- |
- if (status == VideoFrameStream::DECODE_ERROR || |
- status == VideoFrameStream::DECRYPT_ERROR) { |
- DCHECK(!frame.get()); |
- PipelineStatus error = PIPELINE_ERROR_DECODE; |
- if (status == VideoFrameStream::DECRYPT_ERROR) |
- error = PIPELINE_ERROR_DECRYPT; |
- task_runner_->PostTask(FROM_HERE, base::Bind(error_cb_, error)); |
- return; |
- } |
- |
- // Already-queued VideoFrameStream ReadCB's can fire after various state |
- // transitions have happened; in that case just drop those frames immediately. |
- if (state_ == kFlushing) |
- return; |
- |
- DCHECK_EQ(state_, kPlaying); |
- |
- // Can happen when demuxers are preparing for a new Seek(). |
- if (!frame.get()) { |
- DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED); |
- return; |
- } |
- |
- if (frame->end_of_stream()) { |
- DCHECK(!received_end_of_stream_); |
- received_end_of_stream_ = true; |
- } else { |
- // Maintain the latest frame decoded so the correct frame is displayed after |
- // prerolling has completed. |
- if (frame->timestamp() <= start_timestamp_) |
- ready_frames_.clear(); |
- AddReadyFrame_Locked(frame); |
- } |
- |
- // Signal buffering state if we've met our conditions for having enough data. |
- if (buffering_state_ != BUFFERING_HAVE_ENOUGH && HaveEnoughData_Locked()) |
- TransitionToHaveEnough_Locked(); |
- |
- // Always request more decoded video if we have capacity. This serves two |
- // purposes: |
- // 1) Prerolling while paused |
- // 2) Keeps decoding going if video rendering thread starts falling behind |
- AttemptRead_Locked(); |
-} |
- |
-bool VideoRendererImpl::HaveEnoughData_Locked() { |
- DCHECK_EQ(state_, kPlaying); |
- return received_end_of_stream_ || |
- !video_frame_stream_->CanReadWithoutStalling() || |
- ready_frames_.size() >= static_cast<size_t>(limits::kMaxVideoFrames) || |
- (low_delay_ && ready_frames_.size() > 0); |
-} |
- |
-void VideoRendererImpl::TransitionToHaveEnough_Locked() { |
- DCHECK(task_runner_->BelongsToCurrentThread()); |
- DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
- |
- if (!ready_frames_.empty()) { |
- // Because the clock might remain paused in for an undetermined amount |
- // of time (e.g., seeking while paused), paint the first frame. |
- PaintNextReadyFrame_Locked(); |
- } |
- |
- buffering_state_ = BUFFERING_HAVE_ENOUGH; |
- buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH); |
-} |
- |
-void VideoRendererImpl::AddReadyFrame_Locked( |
- const scoped_refptr<VideoFrame>& frame) { |
- DCHECK(task_runner_->BelongsToCurrentThread()); |
- lock_.AssertAcquired(); |
- DCHECK(!frame->end_of_stream()); |
- |
- ready_frames_.push_back(frame); |
- DCHECK_LE(ready_frames_.size(), |
- static_cast<size_t>(limits::kMaxVideoFrames)); |
- |
- // Avoid needlessly waking up |thread_| unless playing. |
- if (state_ == kPlaying) |
- frame_available_.Signal(); |
-} |
- |
-void VideoRendererImpl::AttemptRead() { |
- base::AutoLock auto_lock(lock_); |
- AttemptRead_Locked(); |
-} |
- |
-void VideoRendererImpl::AttemptRead_Locked() { |
- DCHECK(task_runner_->BelongsToCurrentThread()); |
- lock_.AssertAcquired(); |
- |
- if (pending_read_ || received_end_of_stream_ || |
- ready_frames_.size() == static_cast<size_t>(limits::kMaxVideoFrames)) { |
- return; |
- } |
- |
- switch (state_) { |
- case kPlaying: |
- pending_read_ = true; |
- video_frame_stream_->Read(base::Bind(&VideoRendererImpl::FrameReady, |
- weak_factory_.GetWeakPtr())); |
- return; |
- |
- case kUninitialized: |
- case kInitializing: |
- case kFlushing: |
- case kFlushed: |
- return; |
- } |
-} |
- |
-void VideoRendererImpl::OnVideoFrameStreamResetDone() { |
- base::AutoLock auto_lock(lock_); |
- DCHECK_EQ(kFlushing, state_); |
- DCHECK(!pending_read_); |
- DCHECK(ready_frames_.empty()); |
- DCHECK(!received_end_of_stream_); |
- DCHECK(!rendered_end_of_stream_); |
- DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
- |
- state_ = kFlushed; |
- last_timestamp_ = kNoTimestamp(); |
- last_painted_timestamp_ = kNoTimestamp(); |
- base::ResetAndReturn(&flush_cb_).Run(); |
-} |
- |
-void VideoRendererImpl::UpdateStatsAndWait_Locked( |
- base::TimeDelta wait_duration) { |
- lock_.AssertAcquired(); |
- DCHECK_GE(frames_decoded_, 0); |
- DCHECK_LE(frames_dropped_, frames_decoded_); |
- |
- if (frames_decoded_) { |
- PipelineStatistics statistics; |
- statistics.video_frames_decoded = frames_decoded_; |
- statistics.video_frames_dropped = frames_dropped_; |
- task_runner_->PostTask(FROM_HERE, base::Bind(statistics_cb_, statistics)); |
- |
- frames_decoded_ = 0; |
- frames_dropped_ = 0; |
- } |
- |
- frame_available_.TimedWait(wait_duration); |
-} |
- |
-} // namespace media |