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

Unified Diff: media/renderers/video_renderer_impl.cc

Issue 1996763002: Make painting a single frame a permanent API on VideoRendererSink. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Comments. Created 4 years, 7 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/renderers/video_renderer_impl.h ('k') | media/renderers/video_renderer_impl_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/renderers/video_renderer_impl.cc
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index e216f2a8f0816fbfe3e235662be454ce9345b4cd..cb92e45dcc62afa7f3b380afbbb0b15ac1166c02 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -57,8 +57,6 @@ VideoRendererImpl::VideoRendererImpl(
tick_clock_(new base::DefaultTickClock()),
was_background_rendering_(false),
time_progressing_(false),
- render_first_frame_and_stop_(false),
- posted_maybe_stop_after_first_paint_(false),
last_video_memory_usage_(0),
have_renderered_frames_(false),
last_frame_opaque_(false),
@@ -139,8 +137,6 @@ void VideoRendererImpl::Initialize(
DCHECK(!init_cb.is_null());
DCHECK(!wall_clock_time_cb.is_null());
DCHECK_EQ(kUninitialized, state_);
- DCHECK(!render_first_frame_and_stop_);
- DCHECK(!posted_maybe_stop_after_first_paint_);
DCHECK(!was_background_rendering_);
DCHECK(!time_progressing_);
DCHECK(!have_renderered_frames_);
@@ -182,40 +178,12 @@ scoped_refptr<VideoFrame> VideoRendererImpl::Render(
// we've had a proper startup sequence.
DCHECK(result);
- // Notify client of size and opacity changes if this is the first frame
- // or if those have changed from the last frame.
- if (!have_renderered_frames_ ||
- (last_frame_natural_size_ != result->natural_size())) {
- last_frame_natural_size_ = result->natural_size();
- task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&VideoRendererImpl::OnVideoNaturalSizeChange,
- weak_factory_.GetWeakPtr(), last_frame_natural_size_));
- }
- if (!have_renderered_frames_ ||
- (last_frame_opaque_ != IsOpaque(result->format()))) {
- last_frame_opaque_ = IsOpaque(result->format());
- task_runner_->PostTask(
- FROM_HERE, base::Bind(&VideoRendererImpl::OnVideoOpacityChange,
- weak_factory_.GetWeakPtr(), last_frame_opaque_));
- }
-
// Declare HAVE_NOTHING if we reach a state where we can't progress playback
// any further. We don't want to do this if we've already done so, reached
// end of stream, or have frames available. We also don't want to do this in
// background rendering mode unless this isn't the first background render
// tick and we haven't seen any decoded frames since the last one.
- //
- // We use the inverse of |render_first_frame_and_stop_| as a proxy for the
- // value of |time_progressing_| here since we can't access it from the
- // compositor thread. If we're here (in Render()) the sink must have been
- // started -- but if it was started only to render the first frame and stop,
- // then |time_progressing_| is likely false. If we're still in Render() when
- // |render_first_frame_and_stop_| is false, then |time_progressing_| is true.
- // If |time_progressing_| is actually true when |render_first_frame_and_stop_|
- // is also true, then the ended callback will be harmlessly delayed until
- // MaybeStopSinkAfterFirstPaint() runs and the next Render() call comes in.
- MaybeFireEndedCallback_Locked(!render_first_frame_and_stop_);
+ MaybeFireEndedCallback_Locked(true);
if (buffering_state_ == BUFFERING_HAVE_ENOUGH && !received_end_of_stream_ &&
!algorithm_->effective_frames_queued() &&
(!background_rendering ||
@@ -237,28 +205,15 @@ scoped_refptr<VideoFrame> VideoRendererImpl::Render(
UpdateStats_Locked();
was_background_rendering_ = background_rendering;
- // After painting the first frame, if playback hasn't started, we post a
- // delayed task to request that the sink be stopped. The task is delayed to
- // give videos with autoplay time to start.
- //
- // OnTimeStateChanged() will clear this flag if time starts before we get here
- // and MaybeStopSinkAfterFirstPaint() will ignore this request if time starts
- // before the call executes.
- if (render_first_frame_and_stop_ && !posted_maybe_stop_after_first_paint_) {
- posted_maybe_stop_after_first_paint_ = true;
- task_runner_->PostDelayedTask(
- FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint,
- weak_factory_.GetWeakPtr()),
- base::TimeDelta::FromMilliseconds(250));
- }
-
// Always post this task, it will acquire new frames if necessary and since it
// happens on another thread, even if we don't have room in the queue now, by
// the time it runs (may be delayed up to 50ms for complex decodes!) we might.
- task_runner_->PostTask(FROM_HERE, base::Bind(&VideoRendererImpl::AttemptRead,
- weak_factory_.GetWeakPtr()));
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoRendererImpl::AttemptReadAndCheckForMetadataChanges,
+ weak_factory_.GetWeakPtr(), result->format(),
+ result->natural_size()));
- have_renderered_frames_ = true;
return result;
}
@@ -316,16 +271,6 @@ void VideoRendererImpl::OnWaitingForDecryptionKey() {
client_->OnWaitingForDecryptionKey();
}
-void VideoRendererImpl::OnVideoNaturalSizeChange(const gfx::Size& size) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- client_->OnVideoNaturalSizeChange(size);
-}
-
-void VideoRendererImpl::OnVideoOpacityChange(bool opaque) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- client_->OnVideoOpacityChange(opaque);
-}
-
void VideoRendererImpl::SetTickClockForTesting(
std::unique_ptr<base::TickClock> tick_clock) {
tick_clock_.swap(tick_clock);
@@ -390,92 +335,86 @@ void VideoRendererImpl::FrameReady(uint32_t sequence_token,
VideoFrameStream::Status status,
const scoped_refptr<VideoFrame>& frame) {
DCHECK(task_runner_->BelongsToCurrentThread());
- bool start_sink = false;
- {
- base::AutoLock auto_lock(lock_);
- // Stream has been reset and this VideoFrame was decoded before the reset
- // but the async copy finished after.
- if (sequence_token != sequence_token_)
- return;
-
- DCHECK_NE(state_, kUninitialized);
- DCHECK_NE(state_, kFlushed);
-
- CHECK(pending_read_);
- pending_read_ = false;
+ base::AutoLock auto_lock(lock_);
- if (status == VideoFrameStream::DECODE_ERROR) {
- DCHECK(!frame);
- task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&VideoRendererImpl::OnPlaybackError,
- weak_factory_.GetWeakPtr(), PIPELINE_ERROR_DECODE));
- return;
- }
+ // Stream has been reset and this VideoFrame was decoded before the reset
+ // but the async copy finished after.
+ if (sequence_token != sequence_token_)
+ 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_NE(state_, kUninitialized);
+ DCHECK_NE(state_, kFlushed);
- DCHECK_EQ(state_, kPlaying);
+ CHECK(pending_read_);
+ pending_read_ = false;
- // Can happen when demuxers are preparing for a new Seek().
- if (!frame) {
- DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
- return;
- }
+ if (status == VideoFrameStream::DECODE_ERROR) {
+ DCHECK(!frame);
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoRendererImpl::OnPlaybackError,
+ weak_factory_.GetWeakPtr(), PIPELINE_ERROR_DECODE));
+ return;
+ }
- if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
- DCHECK(!received_end_of_stream_);
- received_end_of_stream_ = true;
-
- // See if we can fire EOS immediately instead of waiting for Render().
- MaybeFireEndedCallback_Locked(time_progressing_);
- } else if ((low_delay_ || !video_frame_stream_->CanReadWithoutStalling()) &&
- IsBeforeStartTime(frame->timestamp())) {
- // Don't accumulate frames that are earlier than the start time if we
- // won't have a chance for a better frame, otherwise we could declare
- // HAVE_ENOUGH_DATA and start playback prematurely.
- AttemptRead_Locked();
- return;
- } else {
- // If the sink hasn't been started, we still have time to release less
- // than ideal frames prior to startup. We don't use IsBeforeStartTime()
- // here since it's based on a duration estimate and we can be exact here.
- if (!sink_started_ && frame->timestamp() <= start_timestamp_)
- algorithm_->Reset();
-
- AddReadyFrame_Locked(frame);
- }
+ // 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;
- // Attempt to purge bad frames in case of underflow or backgrounding.
- RemoveFramesForUnderflowOrBackgroundRendering();
+ DCHECK_EQ(state_, kPlaying);
- // Signal buffering state if we've met our conditions.
- if (buffering_state_ == BUFFERING_HAVE_NOTHING && HaveEnoughData_Locked()) {
- TransitionToHaveEnough_Locked();
- if (!sink_started_ && !rendered_end_of_stream_) {
- start_sink = true;
- render_first_frame_and_stop_ = true;
- posted_maybe_stop_after_first_paint_ = false;
- }
- }
+ // Can happen when demuxers are preparing for a new Seek().
+ if (!frame) {
+ DCHECK_EQ(status, VideoFrameStream::DEMUXER_READ_ABORTED);
+ return;
+ }
- // 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
+ if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
+ DCHECK(!received_end_of_stream_);
+ received_end_of_stream_ = true;
+
+ // See if we can fire EOS immediately instead of waiting for Render().
+ MaybeFireEndedCallback_Locked(time_progressing_);
+ } else if ((low_delay_ || !video_frame_stream_->CanReadWithoutStalling()) &&
+ IsBeforeStartTime(frame->timestamp())) {
+ // Don't accumulate frames that are earlier than the start time if we
+ // won't have a chance for a better frame, otherwise we could declare
+ // HAVE_ENOUGH_DATA and start playback prematurely.
AttemptRead_Locked();
+ return;
+ } else {
+ // If the sink hasn't been started, we still have time to release less
+ // than ideal frames prior to startup. We don't use IsBeforeStartTime()
+ // here since it's based on a duration estimate and we can be exact here.
+ if (!sink_started_ && frame->timestamp() <= start_timestamp_)
+ algorithm_->Reset();
+
+ AddReadyFrame_Locked(frame);
}
- // If time is progressing, the sink has already been started; this may be true
- // if we have previously underflowed, yet weren't stopped because of audio.
- if (start_sink) {
- DCHECK(!sink_started_);
- StartSink();
+ // Attempt to purge bad frames in case of underflow or backgrounding.
+ RemoveFramesForUnderflowOrBackgroundRendering();
+
+ // Signal buffering state if we've met our conditions.
+ if (buffering_state_ == BUFFERING_HAVE_NOTHING && HaveEnoughData_Locked()) {
+ TransitionToHaveEnough_Locked();
+
+ // Paint the first frame if necessary.
+ if (!rendered_end_of_stream_ && !sink_started_) {
+ DCHECK(algorithm_->frames_queued());
+ scoped_refptr<VideoFrame> frame = algorithm_->first_frame();
+ CheckForMetadataChanges(frame->format(), frame->natural_size());
+ sink_->PaintSingleFrame(frame);
+ }
}
+
+ // 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() {
@@ -532,11 +471,6 @@ void VideoRendererImpl::AddReadyFrame_Locked(
algorithm_->EnqueueFrame(frame);
}
-void VideoRendererImpl::AttemptRead() {
- base::AutoLock auto_lock(lock_);
- AttemptRead_Locked();
-}
-
void VideoRendererImpl::AttemptRead_Locked() {
DCHECK(task_runner_->BelongsToCurrentThread());
lock_.AssertAcquired();
@@ -604,16 +538,6 @@ void VideoRendererImpl::UpdateStats_Locked() {
}
}
-void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
- DCHECK(task_runner_->BelongsToCurrentThread());
-
- if (!time_progressing_ && sink_started_)
- StopSink();
-
- base::AutoLock auto_lock(lock_);
- render_first_frame_and_stop_ = false;
-}
-
bool VideoRendererImpl::HaveReachedBufferingCap() {
DCHECK(task_runner_->BelongsToCurrentThread());
const size_t kMaxVideoFrames = limits::kMaxVideoFrames;
@@ -731,4 +655,32 @@ void VideoRendererImpl::RemoveFramesForUnderflowOrBackgroundRendering() {
}
}
+void VideoRendererImpl::CheckForMetadataChanges(VideoPixelFormat pixel_format,
+ const gfx::Size& natural_size) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ // Notify client of size and opacity changes if this is the first frame
+ // or if those have changed from the last frame.
+ if (!have_renderered_frames_ || last_frame_natural_size_ != natural_size) {
+ last_frame_natural_size_ = natural_size;
+ client_->OnVideoNaturalSizeChange(last_frame_natural_size_);
+ }
+
+ const bool is_opaque = IsOpaque(pixel_format);
+ if (!have_renderered_frames_ || last_frame_opaque_ != is_opaque) {
+ last_frame_opaque_ = is_opaque;
+ client_->OnVideoOpacityChange(last_frame_opaque_);
+ }
+
+ have_renderered_frames_ = true;
+}
+
+void VideoRendererImpl::AttemptReadAndCheckForMetadataChanges(
+ VideoPixelFormat pixel_format,
+ const gfx::Size& natural_size) {
+ base::AutoLock auto_lock(lock_);
+ CheckForMetadataChanges(pixel_format, natural_size);
+ AttemptRead_Locked();
+}
+
} // namespace media
« no previous file with comments | « media/renderers/video_renderer_impl.h ('k') | media/renderers/video_renderer_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698