| Index: media/blink/video_frame_compositor.cc | 
| diff --git a/media/blink/video_frame_compositor.cc b/media/blink/video_frame_compositor.cc | 
| index 90af145c7ed71fb269f1f35731f372d5756d9512..a5b23dfec88fe5b2f7e781ae8adbd043f961d2b0 100644 | 
| --- a/media/blink/video_frame_compositor.cc | 
| +++ b/media/blink/video_frame_compositor.cc | 
| @@ -6,10 +6,17 @@ | 
|  | 
| #include "base/bind.h" | 
| #include "base/message_loop/message_loop.h" | 
| +#include "base/time/default_tick_clock.h" | 
| +#include "base/trace_event/trace_event.h" | 
| #include "media/base/video_frame.h" | 
|  | 
| namespace media { | 
|  | 
| +// The maximum time we'll allow to elapse between Render() callbacks if there is | 
| +// an external caller requesting frames via GetCurrentFrame(); i.e. there is a | 
| +// canvas which frames are being copied into. | 
| +static const int kStaleFrameThresholdMs = 250; | 
| + | 
| static bool IsOpaque(const scoped_refptr<VideoFrame>& frame) { | 
| switch (frame->format()) { | 
| case VideoFrame::UNKNOWN: | 
| @@ -38,10 +45,14 @@ VideoFrameCompositor::VideoFrameCompositor( | 
| const base::Callback<void(gfx::Size)>& natural_size_changed_cb, | 
| const base::Callback<void(bool)>& opacity_changed_cb) | 
| : compositor_task_runner_(compositor_task_runner), | 
| +      tick_clock_(new base::DefaultTickClock()), | 
| natural_size_changed_cb_(natural_size_changed_cb), | 
| opacity_changed_cb_(opacity_changed_cb), | 
| client_(nullptr), | 
| +      stale_frame_threshold_( | 
| +          base::TimeDelta::FromMilliseconds(kStaleFrameThresholdMs)), | 
| rendering_(false), | 
| +      rendered_last_frame_(false), | 
| callback_(nullptr) { | 
| } | 
|  | 
| @@ -53,7 +64,7 @@ VideoFrameCompositor::~VideoFrameCompositor() { | 
| client_->StopUsingProvider(); | 
| } | 
|  | 
| -void VideoFrameCompositor::OnRendererStateUpdate() { | 
| +void VideoFrameCompositor::OnRendererStateUpdate(bool stop_if_not_rendering) { | 
| DCHECK(compositor_task_runner_->BelongsToCurrentThread()); | 
| if (!client_) | 
| return; | 
| @@ -62,18 +73,18 @@ void VideoFrameCompositor::OnRendererStateUpdate() { | 
| if (callback_) { | 
| if (rendering_) | 
| client_->StartRendering(); | 
| - | 
| -    // TODO(dalecurtis): This will need to request the first frame so we have | 
| -    // something to show, even if playback hasn't started yet. | 
| -  } else if (rendering_) { | 
| +  } else if (!rendering_ && stop_if_not_rendering) { | 
| client_->StopRendering(); | 
| } | 
| } | 
|  | 
| scoped_refptr<VideoFrame> | 
| VideoFrameCompositor::GetCurrentFrameAndUpdateIfStale() { | 
| -  // TODO(dalecurtis): Implement frame refresh when stale. | 
| DCHECK(compositor_task_runner_->BelongsToCurrentThread()); | 
| +  const base::TimeTicks now = tick_clock_->NowTicks(); | 
| +  if (now - last_frame_update_time_ > stale_frame_threshold_) | 
| +    UpdateCurrentFrame(now, now + last_interval_); | 
| + | 
| return GetCurrentFrame(); | 
| } | 
|  | 
| @@ -83,7 +94,7 @@ void VideoFrameCompositor::SetVideoFrameProviderClient( | 
| if (client_) | 
| client_->StopUsingProvider(); | 
| client_ = client; | 
| -  OnRendererStateUpdate(); | 
| +  OnRendererStateUpdate(false); | 
| } | 
|  | 
| scoped_refptr<VideoFrame> VideoFrameCompositor::GetCurrentFrame() { | 
| @@ -93,41 +104,77 @@ scoped_refptr<VideoFrame> VideoFrameCompositor::GetCurrentFrame() { | 
|  | 
| void VideoFrameCompositor::PutCurrentFrame() { | 
| DCHECK(compositor_task_runner_->BelongsToCurrentThread()); | 
| -  // TODO(dalecurtis): Wire up a flag for RenderCallback::OnFrameDropped(). | 
| +  rendered_last_frame_ = true; | 
| } | 
|  | 
| bool VideoFrameCompositor::UpdateCurrentFrame(base::TimeTicks deadline_min, | 
| base::TimeTicks deadline_max) { | 
| -  // TODO(dalecurtis): Wire this up to RenderCallback::Render(). | 
| +  DCHECK(compositor_task_runner_->BelongsToCurrentThread()); | 
| base::AutoLock lock(lock_); | 
| -  return false; | 
| +  if (!callback_) | 
| +    return false; | 
| + | 
| +  // If the previous frame was never rendered, let the client know. | 
| +  if (!rendered_last_frame_ && current_frame_) | 
| +    callback_->OnFrameDropped(); | 
| + | 
| +  last_frame_update_time_ = tick_clock_->NowTicks(); | 
| +  last_interval_ = deadline_max - deadline_min; | 
| +  scoped_refptr<VideoFrame> frame = | 
| +      callback_->Render(deadline_min, deadline_max); | 
| + | 
| +  // Do nothing if the current frame has already been rendered. | 
| +  if (current_frame_ == frame) | 
| +    return false; | 
| + | 
| +  // Set the flag indicating that the current frame is unrendered, if we get a | 
| +  // subsequent PutCurrentFrame() call it will mark it as rendered. | 
| +  rendered_last_frame_ = false; | 
| + | 
| +  if (current_frame_.get() && | 
| +      current_frame_->natural_size() != frame->natural_size()) { | 
| +    natural_size_changed_cb_.Run(frame->natural_size()); | 
| +  } | 
| + | 
| +  if (!current_frame_.get() || IsOpaque(current_frame_) != IsOpaque(frame)) { | 
| +    opacity_changed_cb_.Run(IsOpaque(frame)); | 
| +  } | 
| + | 
| +  current_frame_ = frame; | 
| +  return true; | 
| } | 
|  | 
| void VideoFrameCompositor::Start(RenderCallback* callback) { | 
| -  NOTREACHED(); | 
| - | 
| +  TRACE_EVENT0("media", "VideoFrameCompositor::Start"); | 
| // Called from the media thread, so acquire the callback under lock before | 
| // returning in case a Stop() call comes in before the PostTask is processed. | 
| base::AutoLock lock(lock_); | 
| +  DCHECK(!rendering_); | 
| +  DCHECK(!callback_); | 
| + | 
| callback_ = callback; | 
| rendering_ = true; | 
| +  current_frame_ = nullptr; | 
| compositor_task_runner_->PostTask( | 
| FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate, | 
| -                            base::Unretained(this))); | 
| +                            base::Unretained(this), true)); | 
| } | 
|  | 
| void VideoFrameCompositor::Stop() { | 
| -  NOTREACHED(); | 
| - | 
| +  TRACE_EVENT0("media", "VideoFrameCompositor::Stop"); | 
| // Called from the media thread, so release the callback under lock before | 
| // returning to avoid a pending UpdateCurrentFrame() call occurring before | 
| // the PostTask is processed. | 
| base::AutoLock lock(lock_); | 
| +  DCHECK(rendering_); | 
| +  DCHECK(callback_); | 
| + | 
| callback_ = nullptr; | 
| rendering_ = false; | 
| +  current_frame_ = nullptr; | 
| compositor_task_runner_->PostTask( | 
| FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate, | 
| -                            base::Unretained(this))); | 
| +                            base::Unretained(this), true)); | 
| } | 
|  | 
| void VideoFrameCompositor::PaintFrameUsingOldRenderingPath( | 
|  |