summaryrefslogtreecommitdiffstats
path: root/media/filters/ffmpeg_demuxer.h
blob: d96622ca398da151f031d5340d967563395a3ca4 (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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
// Copyright (c) 2011 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.

// Implements the Demuxer interface using FFmpeg's libavformat.  At this time
// will support demuxing any audio/video format thrown at it.  The streams
// output mime types audio/x-ffmpeg and video/x-ffmpeg and include an integer
// key FFmpegCodecID which contains the CodecID enumeration value.  The CodecIDs
// can be used to create and initialize the corresponding FFmpeg decoder.
//
// FFmpegDemuxer sets the duration of pipeline during initialization by using
// the duration of the longest audio/video stream.
//
// NOTE: since FFmpegDemuxer reads packets sequentially without seeking, media
// files with very large drift between audio/video streams may result in
// excessive memory consumption.
//
// When stopped, FFmpegDemuxer and FFmpegDemuxerStream release all callbacks
// and buffered packets.  Reads from a stopped FFmpegDemuxerStream will not be
// replied to.

#ifndef MEDIA_FILTERS_FFMPEG_DEMUXER_H_
#define MEDIA_FILTERS_FFMPEG_DEMUXER_H_

#include <deque>
#include <vector>

#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/synchronization/waitable_event.h"
#include "media/base/buffers.h"
#include "media/base/filters.h"
#include "media/base/pipeline.h"
#include "media/base/media_format.h"
#include "media/filters/ffmpeg_glue.h"

// FFmpeg forward declarations.
struct AVFormatContext;
struct AVPacket;
struct AVRational;
struct AVStream;

namespace media {

class BitstreamConverter;
class FFmpegDemuxer;

// Forward declaration for scoped_ptr_malloc.
class ScopedPtrAVFree;

class FFmpegDemuxerStream : public DemuxerStream {
 public:
  // Keeps a copy of |demuxer| and initializes itself using information
  // inside |stream|.  Both parameters must outlive |this|.
  FFmpegDemuxerStream(FFmpegDemuxer* demuxer, AVStream* stream);

  // Returns true is this stream has pending reads, false otherwise.
  //
  // Safe to call on any thread.
  virtual bool HasPendingReads();

  // Enqueues and takes ownership over the given AVPacket.
  virtual void EnqueuePacket(AVPacket* packet);

  // Signals to empty the buffer queue and mark next packet as discontinuous.
  virtual void FlushBuffers();

  // Empties the queues and ignores any additional calls to Read().
  virtual void Stop();

  // Returns the duration of this stream.
  virtual base::TimeDelta duration();

  // DemuxerStream implementation.
  virtual Type type();
  virtual const MediaFormat& media_format();

  // If |buffer_queue_| is not empty will execute on caller's thread, otherwise
  // will post ReadTask to execute on demuxer's thread. Read will acquire
  // |lock_| for the life of the function so that means |read_callback| must
  // not make calls into FFmpegDemuxerStream directly or that may cause a
  // deadlock. |read_callback| should execute as quickly as possible because
  // |lock_| is held throughout the life of the callback.
  virtual void Read(const ReadCallback& read_callback);
  // Bitstream converter to convert input packet.
  virtual void EnableBitstreamConverter();
  virtual AVStream* GetAVStream();

 private:
  virtual ~FFmpegDemuxerStream();

  // Carries out enqueuing a pending read on the demuxer thread.
  void ReadTask(const ReadCallback& read_callback);

  // Attempts to fulfill a single pending read by dequeueing a buffer and read
  // callback pair and executing the callback. The calling function must
  // acquire |lock_| before calling this function.
  void FulfillPendingRead();

  // Converts an FFmpeg stream timestamp into a base::TimeDelta.
  static base::TimeDelta ConvertStreamTimestamp(const AVRational& time_base,
                                                int64 timestamp);

  FFmpegDemuxer* demuxer_;
  AVStream* stream_;
  Type type_;
  MediaFormat media_format_;
  base::TimeDelta duration_;
  bool discontinuous_;
  bool stopped_;

  typedef std::deque<scoped_refptr<Buffer> > BufferQueue;
  BufferQueue buffer_queue_;

  typedef std::deque<ReadCallback> ReadQueue;
  ReadQueue read_queue_;

  // Used to translate bitstream formats.
  scoped_ptr<BitstreamConverter> bitstream_converter_;

  // Used to synchronize access to |buffer_queue_|, |read_queue_|, and
  // |stopped_|. This is so other threads can get access to buffers that have
  // already been demuxed without having the demuxer thread sending the
  // buffers. |lock_| must be acquired before any access to |buffer_queue_|,
  // |read_queue_|, or |stopped_|.
  base::Lock lock_;

  DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxerStream);
};

class FFmpegDemuxer : public Demuxer,
                      public FFmpegURLProtocol {
 public:
  explicit FFmpegDemuxer(MessageLoop* message_loop);
  virtual ~FFmpegDemuxer();

  // Posts a task to perform additional demuxing.
  virtual void PostDemuxTask();

  void Initialize(
      DataSource* data_source, PipelineStatusCallback* callback);

  // Filter implementation.
  virtual void Stop(FilterCallback* callback);
  virtual void Seek(base::TimeDelta time, const FilterStatusCB& cb);
  virtual void OnAudioRendererDisabled();
  virtual void set_host(FilterHost* filter_host);
  virtual void SetPlaybackRate(float playback_rate);
  virtual void SetPreload(Preload preload);

  // Demuxer implementation.
  virtual scoped_refptr<DemuxerStream> GetStream(DemuxerStream::Type type);

  // FFmpegProtocol implementation.
  virtual int Read(int size, uint8* data);
  virtual bool GetPosition(int64* position_out);
  virtual bool SetPosition(int64 position);
  virtual bool GetSize(int64* size_out);
  virtual bool IsStreaming();

  // Provide access to FFmpegDemuxerStream.
  MessageLoop* message_loop();

 private:
  // Only allow a factory to create this class.
  friend class MockFFmpegDemuxer;
  FRIEND_TEST_ALL_PREFIXES(FFmpegDemuxerTest, ProtocolRead);

  // Carries out initialization on the demuxer thread.
  void InitializeTask(
      DataSource* data_source, PipelineStatusCallback* callback);

  // Carries out a seek on the demuxer thread.
  void SeekTask(base::TimeDelta time, const FilterStatusCB& cb);

  // Carries out demuxing and satisfying stream reads on the demuxer thread.
  void DemuxTask();

  // Carries out stopping the demuxer streams on the demuxer thread.
  void StopTask(FilterCallback* callback);

  // Carries out disabling the audio stream on the demuxer thread.
  void DisableAudioStreamTask();

  // Returns true if any of the streams have pending reads.  Since we lazily
  // post a DemuxTask() for every read, we use this method to quickly terminate
  // the tasks if there is no work to do.
  //
  // Must be called on the demuxer thread.
  bool StreamsHavePendingReads();

  // Signal all FFmpegDemuxerStream that the stream has ended.
  //
  // Must be called on the demuxer thread.
  void StreamHasEnded();

  // Read callback method to be passed to DataSource. When the asynchronous
  // read has completed, this method will be called from DataSource with
  // number of bytes read or kDataSource in case of error.
  void OnReadCompleted(size_t size);

  // Wait for asynchronous read to complete and return number of bytes read.
  virtual size_t WaitForRead();

  // Signal that read has completed, and |size| bytes have been read.
  virtual void SignalReadCompleted(size_t size);

  MessageLoop* message_loop_;

  // FFmpeg context handle.
  AVFormatContext* format_context_;

  // Two vector of streams:
  //   - |streams_| is indexed by type for the Demuxer interface GetStream(),
  //     and contains NULLs for types which aren't present.
  //   - |packet_streams_| is indexed to mirror AVFormatContext when dealing
  //     with AVPackets returned from av_read_frame() and contain NULL entries
  //     representing unsupported streams where we throw away the data.
  //
  // Ownership is handled via reference counting.
  //
  // Once initialized, operations on FFmpegDemuxerStreams should be carried out
  // on the demuxer thread.
  typedef std::vector< scoped_refptr<FFmpegDemuxerStream> > StreamVector;
  StreamVector streams_;
  StreamVector packet_streams_;

  // Reference to the data source. Asynchronous read requests are submitted to
  // this object.
  scoped_refptr<DataSource> data_source_;

  // This member is used to block on read method calls from FFmpeg and wait
  // until the asynchronous reads in the data source to complete. It is also
  // signaled when the demuxer is being stopped.
  base::WaitableEvent read_event_;

  // Flag to indicate if read has ever failed. Once set to true, it will
  // never be reset. This flag is set true and accessed in Read().
  bool read_has_failed_;

  size_t last_read_bytes_;
  int64 read_position_;

  // Initialization can happen before set_host() is called, in which case we
  // store these bits for deferred reporting to the FilterHost when we get one.
  base::TimeDelta max_duration_;
  PipelineStatus deferred_status_;

  DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxer);
};

}  // namespace media

#endif  // MEDIA_FILTERS_FFMPEG_DEMUXER_H_