Index: content/common/gpu/media/rendering_helper.cc |
diff --git a/content/common/gpu/media/rendering_helper.cc b/content/common/gpu/media/rendering_helper.cc |
index 37e7d92b0ddc2c8f00b39d426e5d3254cc735121..5870ceef564638b48b1c5c9b19c132a95c6f80bb 100644 |
--- a/content/common/gpu/media/rendering_helper.cc |
+++ b/content/common/gpu/media/rendering_helper.cc |
@@ -78,7 +78,7 @@ VideoFrameTexture::~VideoFrameTexture() { |
} |
RenderingHelper::RenderedVideo::RenderedVideo() |
- : last_frame_rendered(false), is_flushing(false), frames_to_drop(0) { |
+ : is_flushing(false), frames_to_drop(0) { |
} |
RenderingHelper::RenderedVideo::~RenderedVideo() { |
@@ -307,7 +307,11 @@ void RenderingHelper::Initialize(const RenderingHelperParams& params, |
if (frame_duration_ != base::TimeDelta()) |
WarmUpRendering(params.warm_up_iterations); |
- done->Signal(); |
+ // It's safe to use Unretained here since |rendering_thread_| will be stopped |
+ // in VideoDecodeAcceleratorTest.TearDown(), while the |rendering_helper_| is |
+ // a member of that class. (See video_decode_accelerator_unittest.cc.) |
+ gl_surface_->GetVSyncProvider()->GetVSyncParameters(base::Bind( |
+ &RenderingHelper::UpdateVSyncParameters, base::Unretained(this), done)); |
} |
// The rendering for the first few frames is slow (e.g., 100ms on Peach Pit). |
@@ -425,28 +429,18 @@ void RenderingHelper::QueueVideoFrame( |
RenderedVideo* video = &videos_[window_id]; |
DCHECK(!video->is_flushing); |
- // Start the rendering task when getting the first frame. |
- if (scheduled_render_time_.is_null() && |
- (frame_duration_ != base::TimeDelta())) { |
- scheduled_render_time_ = base::TimeTicks::Now(); |
- message_loop_->PostTask(FROM_HERE, render_task_.callback()); |
- } |
+ video->pending_frames.push(video_frame); |
- if (video->frames_to_drop > 0) { |
+ if (video->frames_to_drop > 0 && video->pending_frames.size() > 1) { |
--video->frames_to_drop; |
- return; |
- } |
- |
- // Pop the last frame if it has been rendered. |
- if (video->last_frame_rendered) { |
- // When last_frame_rendered is true, we should have only one pending frame. |
- // Since we are going to have a new frame, we can release the pending one. |
- DCHECK(video->pending_frames.size() == 1); |
video->pending_frames.pop(); |
- video->last_frame_rendered = false; |
} |
- video->pending_frames.push(video_frame); |
+ // Schedules the first RenderContent() if need. |
+ if (scheduled_render_time_.is_null()) { |
+ scheduled_render_time_ = base::TimeTicks::Now(); |
+ message_loop_->PostTask(FROM_HERE, render_task_.callback()); |
+ } |
} |
void RenderingHelper::RenderTexture(uint32 texture_target, uint32 texture_id) { |
@@ -544,10 +538,15 @@ void RenderingHelper::Flush(size_t window_id) { |
void RenderingHelper::RenderContent() { |
CHECK_EQ(base::MessageLoop::current(), message_loop_); |
- scheduled_render_time_ += frame_duration_; |
- base::TimeDelta delay = scheduled_render_time_ - base::TimeTicks::Now(); |
- message_loop_->PostDelayedTask( |
- FROM_HERE, render_task_.callback(), std::max(delay, base::TimeDelta())); |
+ // Update the VSync params. |
+ // |
+ // It's safe to use Unretained here since |rendering_thread_| will be stopped |
+ // in VideoDecodeAcceleratorTest.TearDown(), while the |rendering_helper_| is |
+ // a member of that class. (See video_decode_accelerator_unittest.cc.) |
+ gl_surface_->GetVSyncProvider()->GetVSyncParameters( |
+ base::Bind(&RenderingHelper::UpdateVSyncParameters, |
+ base::Unretained(this), |
+ static_cast<base::WaitableEvent*>(NULL))); |
glUniform1i(glGetUniformLocation(program_, "tex_flip"), 1); |
@@ -555,35 +554,35 @@ void RenderingHelper::RenderContent() { |
// after this vector falls out of scope at the end of this method. We need |
// to keep references to them until after SwapBuffers() call below. |
std::vector<scoped_refptr<VideoFrameTexture> > frames_to_be_returned; |
- |
+ bool need_swap_buffer = false; |
if (render_as_thumbnails_) { |
// In render_as_thumbnails_ mode, we render the FBO content on the |
// screen instead of the decoded textures. |
GLSetViewPort(videos_[0].render_area); |
RenderTexture(GL_TEXTURE_2D, thumbnails_texture_id_); |
+ need_swap_buffer = true; |
} else { |
- for (size_t i = 0; i < videos_.size(); ++i) { |
- RenderedVideo* video = &videos_[i]; |
- if (video->pending_frames.empty()) |
+ for (RenderedVideo& video : videos_) { |
+ if (video.pending_frames.empty()) |
continue; |
- scoped_refptr<VideoFrameTexture> frame = video->pending_frames.front(); |
- GLSetViewPort(video->render_area); |
+ need_swap_buffer = true; |
+ scoped_refptr<VideoFrameTexture> frame = video.pending_frames.front(); |
+ GLSetViewPort(video.render_area); |
RenderTexture(frame->texture_target(), frame->texture_id()); |
- if (video->last_frame_rendered) |
- ++video->frames_to_drop; |
- |
- if (video->pending_frames.size() > 1 || video->is_flushing) { |
- frames_to_be_returned.push_back(video->pending_frames.front()); |
- video->pending_frames.pop(); |
- video->last_frame_rendered = false; |
+ if (video.pending_frames.size() > 1 || video.is_flushing) { |
+ frames_to_be_returned.push_back(video.pending_frames.front()); |
+ video.pending_frames.pop(); |
} else { |
- video->last_frame_rendered = true; |
+ ++video.frames_to_drop; |
} |
} |
} |
- gl_surface_->SwapBuffers(); |
+ if (need_swap_buffer) |
+ gl_surface_->SwapBuffers(); |
+ |
+ ScheduleNextRenderContent(); |
} |
// Helper function for the LayoutRenderingAreas(). The |lengths| are the |
@@ -639,4 +638,49 @@ void RenderingHelper::LayoutRenderingAreas( |
videos_[i].render_area = gfx::Rect(x, y, w, h); |
} |
} |
+ |
+void RenderingHelper::UpdateVSyncParameters(base::WaitableEvent* done, |
+ const base::TimeTicks timebase, |
+ const base::TimeDelta interval) { |
+ vsync_timebase_ = timebase; |
+ vsync_interval_ = interval; |
+ |
+ if (done) |
+ done->Signal(); |
+} |
+ |
+void RenderingHelper::DropOneFrameForAllVideos() { |
+ for (RenderedVideo& video : videos_) { |
+ if (video.pending_frames.empty()) |
+ continue; |
+ |
+ if (video.pending_frames.size() > 1 || video.is_flushing) { |
+ video.pending_frames.pop(); |
+ } else { |
+ ++video.frames_to_drop; |
+ } |
+ } |
+} |
+ |
+void RenderingHelper::ScheduleNextRenderContent() { |
+ scheduled_render_time_ += frame_duration_; |
+ |
+ // Schedules the next RenderContent() at latest VSYNC before the |
+ // |scheduled_render_time_|. |
+ base::TimeTicks now = base::TimeTicks::Now(); |
+ base::TimeTicks target = |
+ std::max(now + vsync_interval_, scheduled_render_time_); |
+ |
+ int64 intervals = (target - vsync_timebase_) / vsync_interval_; |
+ target = vsync_timebase_ + intervals * vsync_interval_; |
+ |
+ // When the rendering falls behind, drops frames. |
+ while (scheduled_render_time_ < target) { |
+ scheduled_render_time_ += frame_duration_; |
+ DropOneFrameForAllVideos(); |
+ } |
+ |
+ message_loop_->PostDelayedTask( |
+ FROM_HERE, render_task_.callback(), target - now); |
+} |
} // namespace content |