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

Unified Diff: media/filters/video_renderer_impl.cc

Issue 941633004: Moved renderer implementation from media/filters/ to media/renderers/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed android build Created 5 years, 10 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/video_renderer_impl.h ('k') | media/filters/video_renderer_impl_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « media/filters/video_renderer_impl.h ('k') | media/filters/video_renderer_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698