[go: nahoru, domu]

Pass |paint_cb| through media::Renderer::Initialize().

This provides a common path for a media::Renderer implementation to paint video
frames through the render process' CC code. There are several options how this
callback could be used (or not used):
- Call |paint_cb| periodically to render normal video frames.
- Call |paint_cb| with a HOLE_FRAME during initialization and during frame size
  change to support hold punching.
- Ignore |paint_cb| if there is no video stream, or if video renderering is
  handled by the media::Renderer implementation entirely.

BUG=432796

Review URL: https://codereview.chromium.org/725573003

Cr-Commit-Position: refs/heads/master@{#304074}
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index d2b9bd6c..0c9dba45 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -119,11 +119,12 @@
   virtual ~MockVideoRenderer();
 
   // VideoRenderer implementation.
-  MOCK_METHOD8(Initialize, void(DemuxerStream* stream,
+  MOCK_METHOD9(Initialize, void(DemuxerStream* stream,
                                 bool low_delay,
                                 const PipelineStatusCB& init_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));
@@ -161,12 +162,13 @@
   virtual ~MockRenderer();
 
   // Renderer implementation.
-  MOCK_METHOD6(Initialize, void(DemuxerStreamProvider* demuxer_stream_provider,
+  MOCK_METHOD7(Initialize, void(DemuxerStreamProvider* demuxer_stream_provider,
                                 const base::Closure& init_cb,
                                 const StatisticsCB& statistics_cb,
+                                const BufferingStateCB& buffering_state_cb,
+                                const PaintCB& paint_cb,
                                 const base::Closure& ended_cb,
-                                const PipelineStatusCB& error_cb,
-                                const BufferingStateCB& buffering_state_cb));
+                                const PipelineStatusCB& error_cb));
   MOCK_METHOD1(Flush, void(const base::Closure& flush_cb));
   MOCK_METHOD1(StartPlayingFrom, void(base::TimeDelta timestamp));
   MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc
index 0597105..7534688 100644
--- a/media/base/pipeline.cc
+++ b/media/base/pipeline.cc
@@ -68,6 +68,7 @@
                      const PipelineStatusCB& seek_cb,
                      const PipelineMetadataCB& metadata_cb,
                      const BufferingStateCB& buffering_state_cb,
+                     const PaintCB& paint_cb,
                      const base::Closure& duration_change_cb,
                      const AddTextTrackCB& add_text_track_cb) {
   DCHECK(!ended_cb.is_null());
@@ -75,6 +76,7 @@
   DCHECK(!seek_cb.is_null());
   DCHECK(!metadata_cb.is_null());
   DCHECK(!buffering_state_cb.is_null());
+  DCHECK(!paint_cb.is_null());
 
   base::AutoLock auto_lock(lock_);
   CHECK(!running_) << "Media pipeline is already running";
@@ -87,6 +89,7 @@
   seek_cb_ = seek_cb;
   metadata_cb_ = metadata_cb;
   buffering_state_cb_ = buffering_state_cb;
+  paint_cb_ = paint_cb;
   duration_change_cb_ = duration_change_cb;
   add_text_track_cb_ = add_text_track_cb;
 
@@ -694,9 +697,10 @@
       demuxer_,
       done_cb,
       base::Bind(&Pipeline::OnUpdateStatistics, weak_this),
+      base::Bind(&Pipeline::BufferingStateChanged, weak_this),
+      base::ResetAndReturn(&paint_cb_),
       base::Bind(&Pipeline::OnRendererEnded, weak_this),
-      base::Bind(&Pipeline::OnError, weak_this),
-      base::Bind(&Pipeline::BufferingStateChanged, weak_this));
+      base::Bind(&Pipeline::OnError, weak_this));
 }
 
 void Pipeline::ReportMetadata() {
diff --git a/media/base/pipeline.h b/media/base/pipeline.h
index 71c16e1..2fef5d0 100644
--- a/media/base/pipeline.h
+++ b/media/base/pipeline.h
@@ -6,6 +6,7 @@
 #define MEDIA_BASE_PIPELINE_H_
 
 #include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
@@ -32,6 +33,7 @@
 class TextRenderer;
 class TextTrackConfig;
 class TimeDeltaInterpolator;
+class VideoFrame;
 
 // Metadata describing a pipeline once it has been initialized.
 struct PipelineMetadata {
@@ -76,6 +78,9 @@
 // "Stopped" state.
 class MEDIA_EXPORT Pipeline : public DemuxerHost {
  public:
+  // Used to paint VideoFrame.
+  typedef base::Callback<void(const scoped_refptr<VideoFrame>&)> PaintCB;
+
   // Constructs a media pipeline that will execute on |task_runner|.
   Pipeline(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
            MediaLog* media_log);
@@ -94,6 +99,8 @@
   //                 video in supported formats are known.
   //   |buffering_state_cb| will be executed whenever there are changes in the
   //                        overall buffering state of the pipeline.
+  //   |paint_cb| will be executed whenever there is a VideoFrame to be painted.
+  //              It's safe to call this callback from any thread.
   //   |duration_change_cb| optional callback that will be executed whenever the
   //                        presentation duration changes.
   //   |add_text_track_cb| will be executed whenever a text track is added.
@@ -105,6 +112,7 @@
              const PipelineStatusCB& seek_cb,
              const PipelineMetadataCB& metadata_cb,
              const BufferingStateCB& buffering_state_cb,
+             const PaintCB& paint_cb,
              const base::Closure& duration_change_cb,
              const AddTextTrackCB& add_text_track_cb);
 
@@ -342,6 +350,7 @@
   PipelineStatusCB error_cb_;
   PipelineMetadataCB metadata_cb_;
   BufferingStateCB buffering_state_cb_;
+  PaintCB paint_cb_;
   base::Closure duration_change_cb_;
   AddTextTrackCB add_text_track_cb_;
 
diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc
index 5bb48ad..2f6cdf4 100644
--- a/media/base/pipeline_unittest.cc
+++ b/media/base/pipeline_unittest.cc
@@ -77,6 +77,7 @@
     MOCK_METHOD1(OnError, void(PipelineStatus));
     MOCK_METHOD1(OnMetadata, void(PipelineMetadata));
     MOCK_METHOD1(OnBufferingStateChange, void(BufferingState));
+    MOCK_METHOD1(OnVideoFramePaint, void(const scoped_refptr<VideoFrame>&));
     MOCK_METHOD0(OnDurationChange, void());
 
    private:
@@ -165,9 +166,9 @@
 
   // Sets up expectations to allow the video renderer to initialize.
   void SetRendererExpectations() {
-    EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _))
-        .WillOnce(DoAll(SaveArg<3>(&ended_cb_),
-                        SaveArg<5>(&buffering_state_cb_),
+    EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _, _))
+        .WillOnce(DoAll(SaveArg<3>(&buffering_state_cb_),
+                        SaveArg<5>(&ended_cb_),
                         RunCallback<1>()));
     EXPECT_CALL(*renderer_, HasAudio()).WillRepeatedly(Return(audio_stream()));
     EXPECT_CALL(*renderer_, HasVideo()).WillRepeatedly(Return(video_stream()));
@@ -183,14 +184,15 @@
 
   void StartPipeline() {
     pipeline_->Start(
-        demuxer_.get(),
-        scoped_renderer_.Pass(),
+        demuxer_.get(), scoped_renderer_.Pass(),
         base::Bind(&CallbackHelper::OnEnded, base::Unretained(&callbacks_)),
         base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)),
         base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_)),
         base::Bind(&CallbackHelper::OnMetadata, base::Unretained(&callbacks_)),
         base::Bind(&CallbackHelper::OnBufferingStateChange,
                    base::Unretained(&callbacks_)),
+        base::Bind(&CallbackHelper::OnVideoFramePaint,
+                   base::Unretained(&callbacks_)),
         base::Bind(&CallbackHelper::OnDurationChange,
                    base::Unretained(&callbacks_)),
         base::Bind(&PipelineTest::OnAddTextTrack, base::Unretained(this)));
@@ -847,22 +849,22 @@
 
     if (state == kInitRenderer) {
       if (stop_or_error == kStop) {
-        EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _))
+        EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _, _))
             .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
                             RunCallback<1>()));
         ExpectPipelineStopAndDestroyPipeline();
       } else {
         status = PIPELINE_ERROR_INITIALIZATION_FAILED;
-        EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _))
-            .WillOnce(DoAll(RunCallback<4>(status), RunCallback<1>()));
+        EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _, _))
+            .WillOnce(DoAll(RunCallback<6>(status), RunCallback<1>()));
       }
 
       EXPECT_CALL(*demuxer_, Stop());
       return status;
     }
 
-    EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _))
-        .WillOnce(DoAll(SaveArg<5>(&buffering_state_cb_),
+    EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _, _))
+        .WillOnce(DoAll(SaveArg<3>(&buffering_state_cb_),
                         RunCallback<1>()));
 
     EXPECT_CALL(callbacks_, OnMetadata(_));
diff --git a/media/base/renderer.h b/media/base/renderer.h
index 78ae043..d6cb74a 100644
--- a/media/base/renderer.h
+++ b/media/base/renderer.h
@@ -6,6 +6,7 @@
 #define MEDIA_BASE_RENDERER_H_
 
 #include "base/callback.h"
+#include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "media/base/buffering_state.h"
 #include "media/base/media_export.h"
@@ -13,11 +14,13 @@
 
 namespace media {
 
-class MediaKeys;
 class DemuxerStreamProvider;
+class MediaKeys;
+class VideoFrame;
 
 class MEDIA_EXPORT Renderer {
  public:
+  typedef base::Callback<void(const scoped_refptr<VideoFrame>&)> PaintCB;
   typedef base::Callback<base::TimeDelta()> TimeDeltaCB;
 
   Renderer();
@@ -32,15 +35,19 @@
   //
   // Permanent callbacks:
   // - |statistics_cb|: Executed periodically with rendering statistics.
-  // - |time_cb|: Executed whenever time has advanced through rendering.
+  // - |buffering_state_cb|: Executed when buffering state is changed.
+  // - |paint_cb|: Executed when there is a VideoFrame ready to paint.  Can be
+  //               ignored if the Renderer handles the painting by itself. Can
+  //               be called from any thread.
   // - |ended_cb|: Executed when rendering has reached the end of stream.
   // - |error_cb|: Executed if any error was encountered during rendering.
   virtual void Initialize(DemuxerStreamProvider* demuxer_stream_provider,
                           const base::Closure& init_cb,
                           const StatisticsCB& statistics_cb,
+                          const BufferingStateCB& buffering_state_cb,
+                          const PaintCB& paint_cb,
                           const base::Closure& ended_cb,
-                          const PipelineStatusCB& error_cb,
-                          const BufferingStateCB& buffering_state_cb) = 0;
+                          const PipelineStatusCB& error_cb) = 0;
 
   // The following functions must be called after Initialize().
 
diff --git a/media/base/video_renderer.h b/media/base/video_renderer.h
index fc544bc2..714a25a 100644
--- a/media/base/video_renderer.h
+++ b/media/base/video_renderer.h
@@ -16,9 +16,13 @@
 
 class DemuxerStream;
 class VideoDecoder;
+class VideoFrame;
 
 class MEDIA_EXPORT VideoRenderer {
  public:
+  // Used to paint VideoFrame.
+  typedef base::Callback<void(const scoped_refptr<VideoFrame>&)> PaintCB;
+
   // Used to query the current time or duration of the media.
   typedef base::Callback<base::TimeDelta()> TimeDeltaCB;
 
@@ -36,6 +40,9 @@
   // |buffering_state_cb| is executed when video rendering has either run out of
   // data or has enough data to continue playback.
   //
+  // |paint_cb| is executed on the video frame timing thread whenever a new
+  // frame is available for painting. Can be called from any thread.
+  //
   // |ended_cb| is executed when video rendering has reached the end of stream.
   //
   // |error_cb| is executed if an error was encountered.
@@ -46,6 +53,7 @@
                           const PipelineStatusCB& init_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) = 0;
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index d5a411f..0aaf1f0 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -875,7 +875,6 @@
       media_task_runner_,
       video_decoders.Pass(),
       set_decryptor_ready_cb,
-      base::Bind(&WebMediaPlayerImpl::FrameReady, base::Unretained(this)),
       true,
       media_log_));
 
@@ -932,6 +931,7 @@
       BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnPipelineSeeked, false),
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineMetadata),
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingStateChanged),
+      base::Bind(&WebMediaPlayerImpl::FrameReady, base::Unretained(this)),
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged),
       BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnAddTextTrack));
 }
diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc
index bd026da..57a2858 100644
--- a/media/filters/pipeline_integration_test.cc
+++ b/media/filters/pipeline_integration_test.cc
@@ -568,6 +568,8 @@
                    base::Unretained(this)),
         base::Bind(&PipelineIntegrationTest::OnBufferingStateChanged,
                    base::Unretained(this)),
+        base::Bind(&PipelineIntegrationTest::OnVideoFramePaint,
+                   base::Unretained(this)),
         base::Closure(),
         base::Bind(&PipelineIntegrationTest::OnAddTextTrack,
                    base::Unretained(this)));
@@ -599,6 +601,8 @@
                    base::Unretained(this)),
         base::Bind(&PipelineIntegrationTest::OnBufferingStateChanged,
                    base::Unretained(this)),
+        base::Bind(&PipelineIntegrationTest::OnVideoFramePaint,
+                   base::Unretained(this)),
         base::Closure(),
         base::Bind(&PipelineIntegrationTest::OnAddTextTrack,
                    base::Unretained(this)));
diff --git a/media/filters/pipeline_integration_test_base.cc b/media/filters/pipeline_integration_test_base.cc
index dcd5065..7b861f5 100644
--- a/media/filters/pipeline_integration_test_base.cc
+++ b/media/filters/pipeline_integration_test_base.cc
@@ -116,8 +116,7 @@
       .Times(AtMost(1));
   CreateDemuxer(file_path);
   pipeline_->Start(
-      demuxer_.get(),
-      CreateRenderer(NULL),
+      demuxer_.get(), CreateRenderer(NULL),
       base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)),
       base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)),
       QuitOnStatusCB(expected_status),
@@ -125,9 +124,10 @@
                  base::Unretained(this)),
       base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged,
                  base::Unretained(this)),
-      base::Closure(),
-      base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
-                 base::Unretained(this)));
+      base::Bind(&PipelineIntegrationTestBase::OnVideoFramePaint,
+                 base::Unretained(this)),
+      base::Closure(), base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
+                                  base::Unretained(this)));
   message_loop_.Run();
   return (pipeline_status_ == PIPELINE_OK);
 }
@@ -164,6 +164,8 @@
                  base::Unretained(this)),
       base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged,
                  base::Unretained(this)),
+      base::Bind(&PipelineIntegrationTestBase::OnVideoFramePaint,
+                 base::Unretained(this)),
       base::Closure(),
       base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack,
                  base::Unretained(this)));
@@ -261,8 +263,6 @@
       base::Bind(&PipelineIntegrationTestBase::SetDecryptor,
                  base::Unretained(this),
                  decryptor),
-      base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint,
-                 base::Unretained(this)),
       false,
       new MediaLog()));
 
@@ -324,7 +324,7 @@
   EXPECT_CALL(*this, DecryptorAttached(true));
 }
 
-void PipelineIntegrationTestBase::OnVideoRendererPaint(
+void PipelineIntegrationTestBase::OnVideoFramePaint(
     const scoped_refptr<VideoFrame>& frame) {
   last_video_frame_format_ = frame->format();
   if (!hashing_enabled_)
diff --git a/media/filters/pipeline_integration_test_base.h b/media/filters/pipeline_integration_test_base.h
index a0f25c3..3db6efa 100644
--- a/media/filters/pipeline_integration_test_base.h
+++ b/media/filters/pipeline_integration_test_base.h
@@ -136,7 +136,7 @@
 
   void SetDecryptor(Decryptor* decryptor,
                     const DecryptorReadyCB& decryptor_ready_cb);
-  void OnVideoRendererPaint(const scoped_refptr<VideoFrame>& frame);
+  void OnVideoFramePaint(const scoped_refptr<VideoFrame>& frame);
 
   MOCK_METHOD1(OnMetadata, void(PipelineMetadata));
   MOCK_METHOD1(OnBufferingStateChanged, void(BufferingState));
diff --git a/media/filters/renderer_impl.cc b/media/filters/renderer_impl.cc
index 6416a3d..12674d1 100644
--- a/media/filters/renderer_impl.cc
+++ b/media/filters/renderer_impl.cc
@@ -54,25 +54,28 @@
 void RendererImpl::Initialize(DemuxerStreamProvider* demuxer_stream_provider,
                               const base::Closure& init_cb,
                               const StatisticsCB& statistics_cb,
+                              const BufferingStateCB& buffering_state_cb,
+                              const PaintCB& paint_cb,
                               const base::Closure& ended_cb,
-                              const PipelineStatusCB& error_cb,
-                              const BufferingStateCB& buffering_state_cb) {
+                              const PipelineStatusCB& error_cb) {
   DVLOG(1) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK_EQ(state_, STATE_UNINITIALIZED) << state_;
   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(!error_cb.is_null());
-  DCHECK(!buffering_state_cb.is_null());
   DCHECK(demuxer_stream_provider->GetStream(DemuxerStream::AUDIO) ||
          demuxer_stream_provider->GetStream(DemuxerStream::VIDEO));
 
   demuxer_stream_provider_ = demuxer_stream_provider;
   statistics_cb_ = statistics_cb;
+  buffering_state_cb_ = buffering_state_cb;
+  paint_cb_ = paint_cb;
   ended_cb_ = ended_cb;
   error_cb_ = error_cb;
-  buffering_state_cb_ = buffering_state_cb;
 
   init_cb_ = init_cb;
   state_ = STATE_INITIALIZING;
@@ -244,6 +247,7 @@
       base::Bind(&RendererImpl::OnBufferingStateChanged,
                  weak_this_,
                  &video_buffering_state_),
+      base::ResetAndReturn(&paint_cb_),
       base::Bind(&RendererImpl::OnVideoRendererEnded, weak_this_),
       base::Bind(&RendererImpl::OnError, weak_this_),
       base::Bind(&RendererImpl::GetMediaTimeForSyncingVideo,
diff --git a/media/filters/renderer_impl.h b/media/filters/renderer_impl.h
index 67902d0..734113b8 100644
--- a/media/filters/renderer_impl.h
+++ b/media/filters/renderer_impl.h
@@ -45,9 +45,10 @@
   void Initialize(DemuxerStreamProvider* demuxer_stream_provider,
                   const base::Closure& init_cb,
                   const StatisticsCB& statistics_cb,
+                  const BufferingStateCB& buffering_state_cb,
+                  const PaintCB& paint_cb,
                   const base::Closure& ended_cb,
-                  const PipelineStatusCB& error_cb,
-                  const BufferingStateCB& buffering_state_cb) override;
+                  const PipelineStatusCB& error_cb) override;
   void Flush(const base::Closure& flush_cb) override;
   void StartPlayingFrom(base::TimeDelta time) override;
   void SetPlaybackRate(float playback_rate) override;
@@ -125,6 +126,7 @@
   base::Closure ended_cb_;
   PipelineStatusCB error_cb_;
   BufferingStateCB buffering_state_cb_;
+  PaintCB paint_cb_;
 
   // Temporary callback used for Initialize() and Flush().
   base::Closure init_cb_;
diff --git a/media/filters/renderer_impl_unittest.cc b/media/filters/renderer_impl_unittest.cc
index 5db6205..8cd5675b 100644
--- a/media/filters/renderer_impl_unittest.cc
+++ b/media/filters/renderer_impl_unittest.cc
@@ -49,6 +49,7 @@
     MOCK_METHOD1(OnError, void(PipelineStatus));
     MOCK_METHOD1(OnUpdateStatistics, void(const PipelineStatistics&));
     MOCK_METHOD1(OnBufferingStateChange, void(BufferingState));
+    MOCK_METHOD1(OnVideoFramePaint, void(const scoped_refptr<VideoFrame>&));
 
    private:
     DISALLOW_COPY_AND_ASSIGN(CallbackHelper);
@@ -99,9 +100,9 @@
   // Sets up expectations to allow the video renderer to initialize.
   void SetVideoRendererInitializeExpectations(PipelineStatus status) {
     EXPECT_CALL(*video_renderer_,
-                Initialize(video_stream_.get(), _, _, _, _, _, _, _))
+                Initialize(video_stream_.get(), _, _, _, _, _, _, _, _))
         .WillOnce(DoAll(SaveArg<4>(&video_buffering_state_cb_),
-                        SaveArg<5>(&video_ended_cb_),
+                        SaveArg<6>(&video_ended_cb_),
                         RunCallback<2>(status)));
   }
 
@@ -122,10 +123,12 @@
                    base::Unretained(&callbacks_)),
         base::Bind(&CallbackHelper::OnUpdateStatistics,
                    base::Unretained(&callbacks_)),
-        base::Bind(&CallbackHelper::OnEnded, base::Unretained(&callbacks_)),
-        base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)),
         base::Bind(&CallbackHelper::OnBufferingStateChange,
-                   base::Unretained(&callbacks_)));
+                   base::Unretained(&callbacks_)),
+        base::Bind(&CallbackHelper::OnVideoFramePaint,
+                   base::Unretained(&callbacks_)),
+        base::Bind(&CallbackHelper::OnEnded, base::Unretained(&callbacks_)),
+        base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)));
     base::RunLoop().RunUntilIdle();
   }
 
diff --git a/media/filters/video_renderer_impl.cc b/media/filters/video_renderer_impl.cc
index 1bd3f270..6cd1607 100644
--- a/media/filters/video_renderer_impl.cc
+++ b/media/filters/video_renderer_impl.cc
@@ -23,7 +23,6 @@
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
     ScopedVector<VideoDecoder> decoders,
     const SetDecryptorReadyCB& set_decryptor_ready_cb,
-    const PaintCB& paint_cb,
     bool drop_frames,
     const scoped_refptr<MediaLog>& media_log)
     : task_runner_(task_runner),
@@ -40,14 +39,12 @@
       pending_read_(false),
       drop_frames_(drop_frames),
       buffering_state_(BUFFERING_HAVE_NOTHING),
-      paint_cb_(paint_cb),
       last_timestamp_(kNoTimestamp()),
       last_painted_timestamp_(kNoTimestamp()),
       frames_decoded_(0),
       frames_dropped_(0),
       is_shutting_down_(false),
       weak_factory_(this) {
-  DCHECK(!paint_cb_.is_null());
 }
 
 VideoRendererImpl::~VideoRendererImpl() {
@@ -111,6 +108,7 @@
                                    const PipelineStatusCB& init_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) {
@@ -121,6 +119,7 @@
   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_);
@@ -133,6 +132,7 @@
 
   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;
diff --git a/media/filters/video_renderer_impl.h b/media/filters/video_renderer_impl.h
index 557a884..f319c6e2 100644
--- a/media/filters/video_renderer_impl.h
+++ b/media/filters/video_renderer_impl.h
@@ -37,12 +37,8 @@
     : public VideoRenderer,
       public base::PlatformThread::Delegate {
  public:
-  typedef base::Callback<void(const scoped_refptr<VideoFrame>&)> PaintCB;
-
   // |decoders| contains the VideoDecoders to use when initializing.
   //
-  // |paint_cb| is executed on the video frame timing thread whenever a new
-  // frame is available for painting.
   //
   // Implementors should avoid doing any sort of heavy work in this method and
   // instead post a task to a common/worker thread to handle rendering.  Slowing
@@ -53,7 +49,6 @@
       const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
       ScopedVector<VideoDecoder> decoders,
       const SetDecryptorReadyCB& set_decryptor_ready_cb,
-      const PaintCB& paint_cb,
       bool drop_frames,
       const scoped_refptr<MediaLog>& media_log);
   ~VideoRendererImpl() override;
@@ -64,6 +59,7 @@
                   const PipelineStatusCB& init_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) override;
diff --git a/media/filters/video_renderer_impl_unittest.cc b/media/filters/video_renderer_impl_unittest.cc
index 1a26f0c..feae22b 100644
--- a/media/filters/video_renderer_impl_unittest.cc
+++ b/media/filters/video_renderer_impl_unittest.cc
@@ -54,7 +54,6 @@
         message_loop_.message_loop_proxy(),
         decoders.Pass(),
         media::SetDecryptorReadyCB(),
-        base::Bind(&StrictMock<MockCB>::Display, base::Unretained(&mock_cb_)),
         true,
         new MediaLog()));
 
@@ -104,6 +103,7 @@
                    base::Unretained(this)),
         base::Bind(&StrictMock<MockCB>::BufferingStateChange,
                    base::Unretained(&mock_cb_)),
+        base::Bind(&StrictMock<MockCB>::Display, base::Unretained(&mock_cb_)),
         ended_event_.GetClosure(),
         error_event_.GetPipelineStatusCB(),
         base::Bind(&VideoRendererImplTest::GetTime, base::Unretained(this)));
diff --git a/media/mojo/services/media_renderer_apptest.cc b/media/mojo/services/media_renderer_apptest.cc
index 7c88ad87..60d3bd3b 100644
--- a/media/mojo/services/media_renderer_apptest.cc
+++ b/media/mojo/services/media_renderer_apptest.cc
@@ -150,8 +150,9 @@
   PipelineStatus expected_error(PIPELINE_OK);
   mojo_renderer_impl.Initialize(
       stream_provider(), base::MessageLoop::current()->QuitClosure(),
-      media::StatisticsCB(), base::Closure(),
-      base::Bind(&ErrorCallback, &expected_error), media::BufferingStateCB());
+      media::StatisticsCB(), media::BufferingStateCB(),
+      media::Renderer::PaintCB(), base::Closure(),
+      base::Bind(&ErrorCallback, &expected_error));
   base::MessageLoop::current()->Run();
 
   // We expect an error during initialization because MojoRendererService
diff --git a/media/mojo/services/mojo_renderer_impl.cc b/media/mojo/services/mojo_renderer_impl.cc
index 9ed903f..f93bfe1 100644
--- a/media/mojo/services/mojo_renderer_impl.cc
+++ b/media/mojo/services/mojo_renderer_impl.cc
@@ -35,13 +35,15 @@
   // Connection to |remote_audio_renderer_| will error-out here.
 }
 
+// TODO(xhwang): Support |paint_cb| if needed.
 void MojoRendererImpl::Initialize(
     DemuxerStreamProvider* demuxer_stream_provider,
     const base::Closure& init_cb,
     const StatisticsCB& statistics_cb,
+    const BufferingStateCB& buffering_state_cb,
+    const PaintCB& /* paint_cb */,
     const base::Closure& ended_cb,
-    const PipelineStatusCB& error_cb,
-    const BufferingStateCB& buffering_state_cb) {
+    const PipelineStatusCB& error_cb) {
   DVLOG(1) << __FUNCTION__;
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(demuxer_stream_provider);
diff --git a/media/mojo/services/mojo_renderer_impl.h b/media/mojo/services/mojo_renderer_impl.h
index 24a319b..4b033d8 100644
--- a/media/mojo/services/mojo_renderer_impl.h
+++ b/media/mojo/services/mojo_renderer_impl.h
@@ -43,9 +43,10 @@
   void Initialize(DemuxerStreamProvider* demuxer_stream_provider,
                   const base::Closure& init_cb,
                   const StatisticsCB& statistics_cb,
+                  const BufferingStateCB& buffering_state_cb,
+                  const PaintCB& paint_cb,
                   const base::Closure& ended_cb,
-                  const PipelineStatusCB& error_cb,
-                  const BufferingStateCB& buffering_state_cb) override;
+                  const PipelineStatusCB& error_cb) override;
   void Flush(const base::Closure& flush_cb) override;
   void StartPlayingFrom(base::TimeDelta time) override;
   void SetPlaybackRate(float playback_rate) override;
diff --git a/media/mojo/services/mojo_renderer_service.cc b/media/mojo/services/mojo_renderer_service.cc
index 60266c4..25a812b 100644
--- a/media/mojo/services/mojo_renderer_service.cc
+++ b/media/mojo/services/mojo_renderer_service.cc
@@ -89,7 +89,6 @@
                            task_runner,
                            base::Bind(&LogMediaSourceError, media_log)).Pass(),
       SetDecryptorReadyCB(),
-      base::Bind(&PaintNothing),
       true,
       media_log));
 
@@ -149,9 +148,10 @@
       base::Bind(
           &MojoRendererService::OnRendererInitializeDone, weak_this_, callback),
       base::Bind(&MojoRendererService::OnUpdateStatistics, weak_this_),
+      base::Bind(&MojoRendererService::OnBufferingStateChanged, weak_this_),
+      base::Bind(&PaintNothing),
       base::Bind(&MojoRendererService::OnRendererEnded, weak_this_),
-      base::Bind(&MojoRendererService::OnError, weak_this_),
-      base::Bind(&MojoRendererService::OnBufferingStateChanged, weak_this_));
+      base::Bind(&MojoRendererService::OnError, weak_this_));
 }
 
 void MojoRendererService::OnRendererInitializeDone(
diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc
index ded465c..9dfe601c 100644
--- a/media/tools/player_x11/player_x11.cc
+++ b/media/tools/player_x11/player_x11.cc
@@ -120,7 +120,6 @@
       new media::VideoRendererImpl(task_runner,
                                    video_decoders.Pass(),
                                    media::SetDecryptorReadyCB(),
-                                   paint_cb,
                                    true,
                                    new media::MediaLog()));
 
@@ -156,6 +155,7 @@
                   base::Bind(&SaveStatusAndSignal, &event, &status),
                   base::Bind(&OnMetadata),
                   base::Bind(&OnBufferingStateChanged),
+                  paint_cb,
                   base::Bind(&DoNothing),
                   base::Bind(&OnAddTextTrack));