summaryrefslogtreecommitdiffstats
path: root/media/filters/video_renderer_base.h
blob: de9fc49d86ebe08ef0ee26ede90a1b21821de870 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// Copyright (c) 2010 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.

// VideoRendererBase creates its own thread for the sole purpose of timing frame
// presentation.  It handles reading from the decoder and stores the results in
// a queue of decoded frames, calling OnFrameAvailable() on subclasses to notify
// when a frame is ready to display.
//
// The media filter methods Initialize(), Stop(), SetPlaybackRate() and Seek()
// should be serialized, which they commonly are the pipeline thread.
// As long as VideoRendererBase is initialized, GetCurrentFrame() is safe to
// call from any thread, at any time, including inside of OnFrameAvailable().

#ifndef MEDIA_FILTERS_VIDEO_RENDERER_BASE_H_
#define MEDIA_FILTERS_VIDEO_RENDERER_BASE_H_

#include <deque>

#include "base/condition_variable.h"
#include "base/lock.h"
#include "base/platform_thread.h"
#include "base/scoped_ptr.h"
#include "media/base/filters.h"
#include "media/base/video_frame.h"

namespace media {

// TODO(scherkus): to avoid subclasses, consider using a peer/delegate interface
// and pass in a reference to the constructor.
class VideoRendererBase : public VideoRenderer,
                          public PlatformThread::Delegate {
 public:
  VideoRendererBase();
  virtual ~VideoRendererBase();

  // Helper method to parse out video-related information from a MediaFormat.
  // Returns true all the required parameters are existent in |media_format|.
  // |surface_type_out|, |surface_format_out|, |width_out|, |height_out| can
  // be NULL where the result is not needed.
  static bool ParseMediaFormat(
      const MediaFormat& media_format,
      VideoFrame::SurfaceType* surface_type_out,
      VideoFrame::Format* surface_format_out,
      int* width_out, int* height_out);

  // MediaFilter implementation.
  virtual void Play(FilterCallback* callback);
  virtual void Pause(FilterCallback* callback);
  virtual void Flush(FilterCallback* callback);
  virtual void Stop(FilterCallback* callback);
  virtual void SetPlaybackRate(float playback_rate);
  virtual void Seek(base::TimeDelta time, FilterCallback* callback);

  // VideoRenderer implementation.
  virtual void Initialize(VideoDecoder* decoder, FilterCallback* callback);
  virtual bool HasEnded();

  // PlatformThread::Delegate implementation.
  virtual void ThreadMain();

  // Clients of this class (painter/compositor) should use GetCurrentFrame()
  // obtain ownership of VideoFrame, it should always relinquish the ownership
  // by use PutCurrentFrame(). Current frame is not guaranteed to be non-NULL.
  // It expects clients to use color-fill the background if current frame
  // is NULL. This could happen when before pipeline is pre-rolled or during
  // pause/flush/seek.
  void GetCurrentFrame(scoped_refptr<VideoFrame>* frame_out);
  void PutCurrentFrame(scoped_refptr<VideoFrame> frame);

 protected:
  // Subclass interface.  Called before any other initialization in the base
  // class takes place.
  //
  // Implementors typically use the media format of |decoder| to create their
  // output surfaces.  Implementors should NOT call InitializationComplete().
  virtual bool OnInitialize(VideoDecoder* decoder) = 0;

  // Subclass interface.  Called after all other stopping actions take place.
  //
  // Implementors should perform any necessary cleanup before calling the
  // callback.
  virtual void OnStop(FilterCallback* callback) = 0;

  // Subclass interface.  Called when a new frame is ready for display, which
  // can be accessed via GetCurrentFrame().
  //
  // 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
  // down the video thread may result in losing synchronization with audio.
  //
  // IMPORTANT: This method is called on the video renderer thread, which is
  // different from the thread OnInitialize(), OnStop(), and the rest of the
  // class executes on.
  virtual void OnFrameAvailable() = 0;

  virtual VideoDecoder* GetDecoder() {
    return decoder_.get();
  }

  int width() { return width_; }
  int height() { return height_; }
  VideoFrame::Format surface_format() { return surface_format_; }
  VideoFrame::SurfaceType surface_type() { return surface_type_; }

  void ReadInput(scoped_refptr<VideoFrame> frame);

 private:
  // Callback from video decoder to deliver decoded video frames and decrements
  // |pending_reads_|.
  void ConsumeVideoFrame(scoped_refptr<VideoFrame> frame);

  // Helper method that schedules an asynchronous read from the decoder and
  // increments |pending_reads_|.
  //
  // Safe to call from any thread.
  void ScheduleRead_Locked();

  // Helper function to finished "flush" operation
  void OnFlushDone();

  // Helper method that flushes all video frame in "ready queue" including
  // current frame into "done queue".
  void FlushBuffers();

  // Calculates the duration to sleep for based on |current_frame_|'s timestamp,
  // the next frame timestamp (may be NULL), and the provided playback rate.
  //
  // We don't use |playback_rate_| to avoid locking.
  base::TimeDelta CalculateSleepDuration(VideoFrame* next_frame,
                                         float playback_rate);

  // Used for accessing data members.
  Lock lock_;

  scoped_refptr<VideoDecoder> decoder_;

  int width_;
  int height_;
  VideoFrame::Format surface_format_;
  VideoFrame::SurfaceType surface_type_;

  // Queue of incoming frames as well as the current frame since the last time
  // OnFrameAvailable() was called.
  typedef std::deque< scoped_refptr<VideoFrame> > VideoFrameQueue;
  VideoFrameQueue frames_queue_ready_;
  VideoFrameQueue frames_queue_done_;
  scoped_refptr<VideoFrame> current_frame_;

  // Used to signal |thread_| as frames are added to |frames_|.  Rule of thumb:
  // always check |state_| to see if it was set to STOPPED after waking up!
  ConditionVariable frame_available_;

  // State transition Diagram of this class:
  //       [kUninitialized] -------> [kError]
  //              |
  //              | Initialize()
  //              V        All frames returned
  //   +------[kFlushed]<----------------------[kFlushing]
  //   |          | Seek() or upon                  ^
  //   |          V got first frame                 |
  //   |      [kSeeking]                            | Flush()
  //   |          |                                 |
  //   |          V Got enough frames               |
  //   |      [kPrerolled]---------------------->[kPaused]
  //   |          |                Pause()          ^
  //   |          V Play()                          |
  //   |       [kPlaying]---------------------------|
  //   |          |                Pause()          ^
  //   |          V Receive EOF frame.              | Pause()
  //   |       [kEnded]-----------------------------+
  //   |                                            ^
  //   |                                            |
  //   +-----> [kStopped]                   [Any state other than]
  //                                        [kUninitialized/kError]

  // Simple state tracking variable.
  enum State {
    kUninitialized,
    kPrerolled,
    kPaused,
    kFlushing,
    kFlushed,
    kSeeking,
    kPlaying,
    kEnded,
    kStopped,
    kError,
  };
  State state_;

  // Video thread handle.
  PlatformThreadHandle thread_;

  // Previous time returned from the pipeline.
  base::TimeDelta previous_time_;

  // Keeps track of our pending buffers. We *must* have no pending reads
  // before executing the flush callback; We decrement it each time we receive
  // a buffer and increment it each time we send a buffer out. therefore if
  // decoder provides buffer, |pending_reads_| is always non-positive and if
  // renderer provides buffer, |pending_reads_| is always non-negative.
  int pending_reads_;
  bool pending_paint_;

  float playback_rate_;

  // Filter callbacks.
  scoped_ptr<FilterCallback> flush_callback_;
  scoped_ptr<FilterCallback> seek_callback_;

  base::TimeDelta seek_timestamp_;

  DISALLOW_COPY_AND_ASSIGN(VideoRendererBase);
};

}  // namespace media

#endif  // MEDIA_FILTERS_VIDEO_RENDERER_BASE_H_