qtdemux.c 143 KB
Newer Older
Artyom Baginski's avatar
Artyom Baginski committed
1
2
/* GStreamer
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3
 * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
4
 * Copyright (C) <2006> Wim Taymans <wim@fluendo.com>
Artyom Baginski's avatar
Artyom Baginski committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

22
23
24
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
25
26
27

#include "gst/gst-i18n-plugin.h"

28
#include "qtdemux.h"
29

30
#include <stdlib.h>
Artyom Baginski's avatar
Artyom Baginski committed
31
#include <string.h>
32
33
#include <zlib.h>

34
GST_DEBUG_CATEGORY_STATIC (qtdemux_debug);
35
#define GST_CAT_DEFAULT qtdemux_debug
36

37
38
39
40
41
42

#if 0
#define qtdemux_dump_mem(a,b)  gst_util_dump_mem(a,b)
#else
#define qtdemux_dump_mem(a,b)   /* */
#endif
43

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
44
45
46
#define QTDEMUX_GUINT32_GET(a)  (GST_READ_UINT32_BE(a))
#define QTDEMUX_GUINT24_GET(a)  (GST_READ_UINT32_BE(a) >> 8)
#define QTDEMUX_GUINT16_GET(a)  (GST_READ_UINT16_BE(a))
47
#define QTDEMUX_GUINT8_GET(a)   (GST_READ_UINT8(a))
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
48
49
50
#define QTDEMUX_FP32_GET(a)     ((GST_READ_UINT32_BE(a))/65536.0)
#define QTDEMUX_FP16_GET(a)     ((GST_READ_UINT16_BE(a))/256.0)
#define QTDEMUX_FOURCC_GET(a)   (GST_READ_UINT32_LE(a))
51
52

#define QTDEMUX_GUINT64_GET(a) ((((guint64)QTDEMUX_GUINT32_GET(a))<<32)|QTDEMUX_GUINT32_GET(((void *)a)+4))
Artyom Baginski's avatar
Artyom Baginski committed
53

54
55
typedef struct _QtNode QtNode;
typedef struct _QtNodeType QtNodeType;
56
typedef struct _QtDemuxSegment QtDemuxSegment;
57
typedef struct _QtDemuxSample QtDemuxSample;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
58

59
60
//typedef struct _QtDemuxStream QtDemuxStream;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
61
62
struct _QtNode
{
63
64
  guint32 type;
  gpointer data;
65
  gint len;
66
67
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68
69
struct _QtNodeType
{
70
  guint32 fourcc;
71
  const gchar *name;
72
  gint flags;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
73
  void (*dump) (GstQTDemux * qtdemux, void *buffer, int depth);
74
75
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
76
77
struct _QtDemuxSample
{
78
79
  guint32 chunk;
  guint32 size;
80
  guint64 offset;
81
  guint64 timestamp;            /* In GstClockTime */
82
  guint64 duration;             /* in GstClockTime */
83
  gboolean keyframe;            /* TRUE when this packet is a keyframe */
84
85
};

86
87
88
89
90
91
92
93
94
95
96
97
struct _QtDemuxSegment
{
  /* global time and duration, all gst time */
  guint64 time;
  guint64 stop_time;
  guint64 duration;
  /* media time of trak, all gst time */
  guint64 media_start;
  guint64 media_stop;
  gdouble rate;
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
98
99
struct _QtDemuxStream
{
100
101
102
  GstPad *pad;

  /* stream type */
103
104
  guint32 subtype;
  GstCaps *caps;
105
  guint32 fourcc;
106

107
108
109
110
111
112
113
114
  /* duration/scale */
  guint32 duration;             /* in timescale */
  guint32 timescale;

  /* our samples */
  guint32 n_samples;
  QtDemuxSample *samples;
  gboolean all_keyframe;        /* TRUE when all samples are keyframes (no stss) */
115
  guint32 min_duration;         /* duration in timescale of dirst sample, used for figuring out
116
                                   the framerate, in timescale units */
117

118
119
120
121
  /* if we use chunks or samples */
  gboolean sampled;

  /* video info */
122
123
  gint width;
  gint height;
124
125
126
  /* Numerator/denominator framerate */
  gint fps_n;
  gint fps_d;
127
128
  guint16 bits_per_sample;
  guint16 color_table_id;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
129

130
  /* audio info */
131
132
  gdouble rate;
  gint n_channels;
133
134
135
136
  guint samples_per_packet;
  guint samples_per_frame;
  guint bytes_per_packet;
  guint bytes_per_sample;
137
  guint bytes_per_frame;
138
  guint compression;
139
140
141

  /* when a discontinuity is pending */
  gboolean discont;
142
143
144
145
146
147

  /* current position */
  guint32 segment_index;
  guint32 sample_index;
  guint64 time_position;        /* in gst time */

148
149
150
  /* last GstFlowReturn */
  GstFlowReturn last_ret;

151
152
153
154
  /* quicktime segments */
  guint32 n_segments;
  QtDemuxSegment *segments;
  gboolean segment_pending;
155
156
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
157
158
enum QtDemuxState
{
159
160
161
  QTDEMUX_STATE_INITIAL,        /* Initial state (haven't got the header yet) */
  QTDEMUX_STATE_HEADER,         /* Parsing the header */
  QTDEMUX_STATE_MOVIE,          /* Parsing/Playing the media data */
162
  QTDEMUX_STATE_BUFFER_MDAT     /* Buffering the mdat atom */
163
164
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
165
166
static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
167

168
static const GstElementDetails gst_qtdemux_details =
169
170
171
172
GST_ELEMENT_DETAILS ("QuickTime demuxer",
    "Codec/Demuxer",
    "Demultiplex a QuickTime file into audio and video streams",
    "David Schleef <ds@schleef.org>");
Artyom Baginski's avatar
Artyom Baginski committed
173

David Schleef's avatar
David Schleef committed
174
static GstStaticPadTemplate gst_qtdemux_sink_template =
175
    GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
176
    GST_PAD_SINK,
177
    GST_PAD_ALWAYS,
178
    GST_STATIC_CAPS ("video/quicktime; audio/x-m4a; application/x-3gp")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
179
    );
David Schleef's avatar
David Schleef committed
180
181

static GstStaticPadTemplate gst_qtdemux_videosrc_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
182
183
184
185
GST_STATIC_PAD_TEMPLATE ("audio_%02d",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
David Schleef's avatar
David Schleef committed
186
187

static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188
189
190
191
GST_STATIC_PAD_TEMPLATE ("video_%02d",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
Artyom Baginski's avatar
Artyom Baginski committed
192

193
static GstElementClass *parent_class = NULL;
Artyom Baginski's avatar
Artyom Baginski committed
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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/* we could generate these programmatically, but the generation code
 * is only a few lines shorter than the tables, and much uglier */
static const guint32 ff_qt_default_palette_256[256] = {
  0xFFFFFF, 0xFFFFCC, 0xFFFF99, 0xFFFF66, 0xFFFF33, 0xFFFF00,
  0xFFCCFF, 0xFFCCCC, 0xFFCC99, 0xFFCC66, 0xFFCC33, 0xFFCC00,
  0xFF99FF, 0xFF99CC, 0xFF9999, 0xFF9966, 0xFF9933, 0xFF9900,
  0xFF66FF, 0xFF66CC, 0xFF6699, 0xFF6666, 0xFF6633, 0xFF6600,
  0xFF33FF, 0xFF33CC, 0xFF3399, 0xFF3366, 0xFF3333, 0xFF3300,
  0xFF00FF, 0xFF00CC, 0xFF0099, 0xFF0066, 0xFF0033, 0xFF0000,
  0xCCFFFF, 0xCCFFCC, 0xCCFF99, 0xCCFF66, 0xCCFF33, 0xCCFF00,
  0xCCCCFF, 0xCCCCCC, 0xCCCC99, 0xCCCC66, 0xCCCC33, 0xCCCC00,
  0xCC99FF, 0xCC99CC, 0xCC9999, 0xCC9966, 0xCC9933, 0xCC9900,
  0xCC66FF, 0xCC66CC, 0xCC6699, 0xCC6666, 0xCC6633, 0xCC6600,
  0xCC33FF, 0xCC33CC, 0xCC3399, 0xCC3366, 0xCC3333, 0xCC3300,
  0xCC00FF, 0xCC00CC, 0xCC0099, 0xCC0066, 0xCC0033, 0xCC0000,
  0x99FFFF, 0x99FFCC, 0x99FF99, 0x99FF66, 0x99FF33, 0x99FF00,
  0x99CCFF, 0x99CCCC, 0x99CC99, 0x99CC66, 0x99CC33, 0x99CC00,
  0x9999FF, 0x9999CC, 0x999999, 0x999966, 0x999933, 0x999900,
  0x9966FF, 0x9966CC, 0x996699, 0x996666, 0x996633, 0x996600,
  0x9933FF, 0x9933CC, 0x993399, 0x993366, 0x993333, 0x993300,
  0x9900FF, 0x9900CC, 0x990099, 0x990066, 0x990033, 0x990000,
  0x66FFFF, 0x66FFCC, 0x66FF99, 0x66FF66, 0x66FF33, 0x66FF00,
  0x66CCFF, 0x66CCCC, 0x66CC99, 0x66CC66, 0x66CC33, 0x66CC00,
  0x6699FF, 0x6699CC, 0x669999, 0x669966, 0x669933, 0x669900,
  0x6666FF, 0x6666CC, 0x666699, 0x666666, 0x666633, 0x666600,
  0x6633FF, 0x6633CC, 0x663399, 0x663366, 0x663333, 0x663300,
  0x6600FF, 0x6600CC, 0x660099, 0x660066, 0x660033, 0x660000,
  0x33FFFF, 0x33FFCC, 0x33FF99, 0x33FF66, 0x33FF33, 0x33FF00,
  0x33CCFF, 0x33CCCC, 0x33CC99, 0x33CC66, 0x33CC33, 0x33CC00,
  0x3399FF, 0x3399CC, 0x339999, 0x339966, 0x339933, 0x339900,
  0x3366FF, 0x3366CC, 0x336699, 0x336666, 0x336633, 0x336600,
  0x3333FF, 0x3333CC, 0x333399, 0x333366, 0x333333, 0x333300,
  0x3300FF, 0x3300CC, 0x330099, 0x330066, 0x330033, 0x330000,
  0x00FFFF, 0x00FFCC, 0x00FF99, 0x00FF66, 0x00FF33, 0x00FF00,
  0x00CCFF, 0x00CCCC, 0x00CC99, 0x00CC66, 0x00CC33, 0x00CC00,
  0x0099FF, 0x0099CC, 0x009999, 0x009966, 0x009933, 0x009900,
  0x0066FF, 0x0066CC, 0x006699, 0x006666, 0x006633, 0x006600,
  0x0033FF, 0x0033CC, 0x003399, 0x003366, 0x003333, 0x003300,
  0x0000FF, 0x0000CC, 0x000099, 0x000066, 0x000033, 0xEE0000,
  0xDD0000, 0xBB0000, 0xAA0000, 0x880000, 0x770000, 0x550000,
  0x440000, 0x220000, 0x110000, 0x00EE00, 0x00DD00, 0x00BB00,
  0x00AA00, 0x008800, 0x007700, 0x005500, 0x004400, 0x002200,
  0x001100, 0x0000EE, 0x0000DD, 0x0000BB, 0x0000AA, 0x000088,
  0x000077, 0x000055, 0x000044, 0x000022, 0x000011, 0xEEEEEE,
  0xDDDDDD, 0xBBBBBB, 0xAAAAAA, 0x888888, 0x777777, 0x555555,
  0x444444, 0x222222, 0x111111, 0x000000
};

static const guint32 ff_qt_grayscale_palette_256[256] = {
  0xffffff, 0xfefefe, 0xfdfdfd, 0xfcfcfc, 0xfbfbfb, 0xfafafa, 0xf9f9f9,
  0xf8f8f8, 0xf7f7f7, 0xf6f6f6, 0xf5f5f5, 0xf4f4f4, 0xf3f3f3, 0xf2f2f2,
  0xf1f1f1, 0xf0f0f0, 0xefefef, 0xeeeeee, 0xededed, 0xececec, 0xebebeb,
  0xeaeaea, 0xe9e9e9, 0xe8e8e8, 0xe7e7e7, 0xe6e6e6, 0xe5e5e5, 0xe4e4e4,
  0xe3e3e3, 0xe2e2e2, 0xe1e1e1, 0xe0e0e0, 0xdfdfdf, 0xdedede, 0xdddddd,
  0xdcdcdc, 0xdbdbdb, 0xdadada, 0xd9d9d9, 0xd8d8d8, 0xd7d7d7, 0xd6d6d6,
  0xd5d5d5, 0xd4d4d4, 0xd3d3d3, 0xd2d2d2, 0xd1d1d1, 0xd0d0d0, 0xcfcfcf,
  0xcecece, 0xcdcdcd, 0xcccccc, 0xcbcbcb, 0xcacaca, 0xc9c9c9, 0xc8c8c8,
  0xc7c7c7, 0xc6c6c6, 0xc5c5c5, 0xc4c4c4, 0xc3c3c3, 0xc2c2c2, 0xc1c1c1,
  0xc0c0c0, 0xbfbfbf, 0xbebebe, 0xbdbdbd, 0xbcbcbc, 0xbbbbbb, 0xbababa,
  0xb9b9b9, 0xb8b8b8, 0xb7b7b7, 0xb6b6b6, 0xb5b5b5, 0xb4b4b4, 0xb3b3b3,
  0xb2b2b2, 0xb1b1b1, 0xb0b0b0, 0xafafaf, 0xaeaeae, 0xadadad, 0xacacac,
  0xababab, 0xaaaaaa, 0xa9a9a9, 0xa8a8a8, 0xa7a7a7, 0xa6a6a6, 0xa5a5a5,
  0xa4a4a4, 0xa3a3a3, 0xa2a2a2, 0xa1a1a1, 0xa0a0a0, 0x9f9f9f, 0x9e9e9e,
  0x9d9d9d, 0x9c9c9c, 0x9b9b9b, 0x9a9a9a, 0x999999, 0x989898, 0x979797,
  0x969696, 0x959595, 0x949494, 0x939393, 0x929292, 0x919191, 0x909090,
  0x8f8f8f, 0x8e8e8e, 0x8d8d8d, 0x8c8c8c, 0x8b8b8b, 0x8a8a8a, 0x898989,
  0x888888, 0x878787, 0x868686, 0x858585, 0x848484, 0x838383, 0x828282,
  0x818181, 0x808080, 0x7f7f7f, 0x7e7e7e, 0x7d7d7d, 0x7c7c7c, 0x7b7b7b,
  0x7a7a7a, 0x797979, 0x787878, 0x777777, 0x767676, 0x757575, 0x747474,
  0x737373, 0x727272, 0x717171, 0x707070, 0x6f6f6f, 0x6e6e6e, 0x6d6d6d,
  0x6c6c6c, 0x6b6b6b, 0x6a6a6a, 0x696969, 0x686868, 0x676767, 0x666666,
  0x656565, 0x646464, 0x636363, 0x626262, 0x616161, 0x606060, 0x5f5f5f,
  0x5e5e5e, 0x5d5d5d, 0x5c5c5c, 0x5b5b5b, 0x5a5a5a, 0x595959, 0x585858,
  0x575757, 0x565656, 0x555555, 0x545454, 0x535353, 0x525252, 0x515151,
  0x505050, 0x4f4f4f, 0x4e4e4e, 0x4d4d4d, 0x4c4c4c, 0x4b4b4b, 0x4a4a4a,
  0x494949, 0x484848, 0x474747, 0x464646, 0x454545, 0x444444, 0x434343,
  0x424242, 0x414141, 0x404040, 0x3f3f3f, 0x3e3e3e, 0x3d3d3d, 0x3c3c3c,
  0x3b3b3b, 0x3a3a3a, 0x393939, 0x383838, 0x373737, 0x363636, 0x353535,
  0x343434, 0x333333, 0x323232, 0x313131, 0x303030, 0x2f2f2f, 0x2e2e2e,
  0x2d2d2d, 0x2c2c2c, 0x2b2b2b, 0x2a2a2a, 0x292929, 0x282828, 0x272727,
  0x262626, 0x252525, 0x242424, 0x232323, 0x222222, 0x212121, 0x202020,
  0x1f1f1f, 0x1e1e1e, 0x1d1d1d, 0x1c1c1c, 0x1b1b1b, 0x1a1a1a, 0x191919,
  0x181818, 0x171717, 0x161616, 0x151515, 0x141414, 0x131313, 0x121212,
  0x111111, 0x101010, 0x0f0f0f, 0x0e0e0e, 0x0d0d0d, 0x0c0c0c, 0x0b0b0b,
  0x0a0a0a, 0x090909, 0x080808, 0x070707, 0x060606, 0x050505, 0x040404,
  0x030303, 0x020202, 0x010101, 0x000000
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
283
284
285
static void gst_qtdemux_class_init (GstQTDemuxClass * klass);
static void gst_qtdemux_base_init (GstQTDemuxClass * klass);
static void gst_qtdemux_init (GstQTDemux * quicktime_demux);
286
static void gst_qtdemux_dispose (GObject * object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
287
288
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
    GstStateChange transition);
289
static void gst_qtdemux_loop (GstPad * pad);
290
static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
291
292
static gboolean qtdemux_sink_activate (GstPad * sinkpad);
static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
293
294
static gboolean qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active);
static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstEvent * event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
295
296
297
298

static void qtdemux_parse_moov (GstQTDemux * qtdemux, void *buffer, int length);
static void qtdemux_parse (GstQTDemux * qtdemux, GNode * node, void *buffer,
    int length);
299
static const QtNodeType *qtdemux_type_get (guint32 fourcc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
300
301
static void qtdemux_node_dump (GstQTDemux * qtdemux, GNode * node);
static void qtdemux_parse_tree (GstQTDemux * qtdemux);
302
static void qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta);
303
static void qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag,
304
    GNode * node);
305
306
307
308
static void qtdemux_tag_add_num (GstQTDemux * qtdemux, const char *tag1,
    const char *tag2, GNode * node);
static void qtdemux_tag_add_gnre (GstQTDemux * qtdemux, const char *tag,
    GNode * node);
309
310
static void qtdemux_tag_add_date (GstQTDemux * qtdemux, const char *tag,
    GNode * node);
311

312
static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
313
    QtDemuxStream * stream, GNode * esds, GstTagList * list);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
314
static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux, guint32 fourcc,
315
    const guint8 * stsd_data, const gchar ** codec_name);
316
317
318
static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint32 fourcc, const guint8 * data, int len,
    const gchar ** codec_name);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
319
320
321

static GType
gst_qtdemux_get_type (void)
Artyom Baginski's avatar
Artyom Baginski committed
322
323
324
325
326
{
  static GType qtdemux_type = 0;

  if (!qtdemux_type) {
    static const GTypeInfo qtdemux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
327
328
329
330
331
      sizeof (GstQTDemuxClass),
      (GBaseInitFunc) gst_qtdemux_base_init, NULL,
      (GClassInitFunc) gst_qtdemux_class_init,
      NULL, NULL, sizeof (GstQTDemux), 0,
      (GInstanceInitFunc) gst_qtdemux_init,
Artyom Baginski's avatar
Artyom Baginski committed
332
    };
333

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
334
    qtdemux_type =
335
336
        g_type_register_static (GST_TYPE_ELEMENT, "GstQTDemux", &qtdemux_info,
        0);
Artyom Baginski's avatar
Artyom Baginski committed
337
338
339
340
  }
  return qtdemux_type;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
341
342
static void
gst_qtdemux_base_init (GstQTDemuxClass * klass)
343
344
345
346
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
347
348
349
350
351
      gst_static_pad_template_get (&gst_qtdemux_sink_template));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_qtdemux_videosrc_template));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_qtdemux_audiosrc_template));
352
  gst_element_class_set_details (element_class, &gst_qtdemux_details);
David Schleef's avatar
David Schleef committed
353

354
355
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
356
357
static void
gst_qtdemux_class_init (GstQTDemuxClass * klass)
Artyom Baginski's avatar
Artyom Baginski committed
358
359
360
361
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
362
363
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Artyom Baginski's avatar
Artyom Baginski committed
364

365
  parent_class = g_type_class_peek_parent (klass);
366

367
368
  gobject_class->dispose = gst_qtdemux_dispose;

Artyom Baginski's avatar
Artyom Baginski committed
369
370
371
  gstelement_class->change_state = gst_qtdemux_change_state;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
372
373
static void
gst_qtdemux_init (GstQTDemux * qtdemux)
Artyom Baginski's avatar
Artyom Baginski committed
374
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
375
  qtdemux->sinkpad =
376
      gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
377
378
379
  gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
  gst_pad_set_activatepull_function (qtdemux->sinkpad,
      qtdemux_sink_activate_pull);
380
381
382
383
  gst_pad_set_activatepush_function (qtdemux->sinkpad,
      qtdemux_sink_activate_push);
  gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
  gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
Artyom Baginski's avatar
Artyom Baginski committed
384
  gst_element_add_pad (GST_ELEMENT (qtdemux), qtdemux->sinkpad);
385

386
  qtdemux->state = QTDEMUX_STATE_INITIAL;
387
  /* FIXME, use segment last_stop for this */
388
  qtdemux->last_ts = GST_CLOCK_TIME_NONE;
389
390
391
392
  qtdemux->pullbased = FALSE;
  qtdemux->neededbytes = 16;
  qtdemux->todrop = 0;
  qtdemux->adapter = gst_adapter_new ();
393
394
395
  qtdemux->offset = 0;
  qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
  qtdemux->mdatbuffer = NULL;
396
  gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
Artyom Baginski's avatar
Artyom Baginski committed
397
398
}

399
400
401
402
403
404
405
406
407
static void
gst_qtdemux_dispose (GObject * object)
{
  GstQTDemux *qtdemux = GST_QTDEMUX (object);

  if (qtdemux->adapter) {
    g_object_unref (G_OBJECT (qtdemux->adapter));
    qtdemux->adapter = NULL;
  }
408
409

  G_OBJECT_CLASS (parent_class)->dispose (object);
410
411
}

412
#if 0
413
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
414
415
gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
416
417
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
418
  QtDemuxStream *stream = gst_pad_get_element_private (pad);
419

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
420
  if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e') &&
421
422
423
424
425
426
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
    return FALSE;

  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
427
428
429
430
431
432
433
434
435
        case GST_FORMAT_BYTES:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        case GST_FORMAT_DEFAULT:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
436
437
438
439
      }
      break;
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
440
441
442
443
444
445
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
446
447
448
449
      }
      break;
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
450
451
452
453
454
455
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
456
457
458
459
460
461
462
463
      }
      break;
    default:
      res = FALSE;
  }

  return res;
}
464
#endif
465
466

static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
467
gst_qtdemux_get_src_query_types (GstPad * pad)
468
469
470
{
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
Wim Taymans's avatar
Wim Taymans committed
471
    GST_QUERY_DURATION,
472
473
474
475
476
477
478
    0
  };

  return src_types;
}

static gboolean
479
gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
480
{
481
482
  gboolean res = FALSE;
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
483

484
  switch (GST_QUERY_TYPE (query)) {
485
    case GST_QUERY_POSITION:
486
487
488
      if (GST_CLOCK_TIME_IS_VALID (qtdemux->segment.last_stop)) {
        gst_query_set_position (query, GST_FORMAT_TIME,
            qtdemux->segment.last_stop);
Wim Taymans's avatar
Wim Taymans committed
489
490
491
492
        res = TRUE;
      }
      break;
    case GST_QUERY_DURATION:
493
      if (qtdemux->duration != 0 && qtdemux->timescale != 0) {
494
495
496
497
498
499
        gint64 duration;

        duration = gst_util_uint64_scale_int (qtdemux->duration,
            GST_SECOND, qtdemux->timescale);

        gst_query_set_duration (query, GST_FORMAT_TIME, duration);
500
        res = TRUE;
501
502
503
504
505
506
507
      }
      break;
    default:
      res = FALSE;
      break;
  }

508
509
  gst_object_unref (qtdemux);

510
511
512
  return res;
}

513
/* push event on all source pads; takes ownership of the event */
514
static void
515
gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
516
517
518
519
520
521
522
{
  guint n;

  GST_DEBUG_OBJECT (qtdemux, "pushing %s event on all source pads",
      GST_EVENT_TYPE_NAME (event));

  for (n = 0; n < qtdemux->n_streams; n++) {
523
    gst_pad_push_event (qtdemux->streams[n]->pad, gst_event_ref (event));
524
525
526
527
  }
  gst_event_unref (event);
}

528
/* find the index of the sample that includes the data for @media_time
529
 *
530
531
 * Returns the index of the sample or n_samples when the sample was not
 * found.
532
 */
533
/* FIXME, binary search would be nice here */
534
535
536
static guint32
gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint64 media_time)
537
{
538
  guint32 i;
539

540
541
  if (str->n_samples == 0)
    return 0;
542

543
544
545
546
  for (i = 0; i < str->n_samples; i++) {
    if (str->samples[i].timestamp > media_time) {
      /* first sample after media_time, we need the previous one */
      return (i == 0 ? 0 : i - 1);
547
    }
548
549
550
  }
  return str->n_samples - 1;
}
551

552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
/* find the index of the keyframe needed to decode the sample at @index
 * of stream @str.
 *
 * Returns the index of the keyframe.
 */
static guint32
gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint32 index)
{
  if (index >= str->n_samples)
    return str->n_samples;

  /* all keyframes, return index */
  if (str->all_keyframe)
    return index;

  /* else go back until we have a keyframe */
  while (TRUE) {
    if (str->samples[index].keyframe)
      break;

    if (index == 0)
      break;

    index--;
  }
  return index;
}

/* find the segment for @time_position for @stream
 *
 * Returns -1 if the segment cannot be found.
 */
static guint32
gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
    guint64 time_position)
{
  gint i;
  guint32 seg_idx;

  /* find segment corresponding to time_position if we are looking
   * for a segment. */
  seg_idx = -1;
  for (i = 0; i < stream->n_segments; i++) {
    QtDemuxSegment *segment = &stream->segments[i];

    if (segment->time <= time_position && time_position < segment->stop_time) {
      seg_idx = i;
      break;
601
    }
602
  }
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
  return seg_idx;
}

/* move the stream @str to the sample position @index.
 *
 * Updates @str->sample_index and marks discontinuity if needed.
 */
static void
gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint32 index)
{
  /* no change needed */
  if (index == str->sample_index)
    return;

  GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
      str->n_samples);

  /* position changed, we have a discont */
  str->sample_index = index;
  str->discont = TRUE;
624
625
626
627
}

/* perform the seek.
 *
628
629
630
631
632
633
634
635
636
637
638
 * We set all segment_indexes in the streams to unknown and
 * adjust the time_position to the desired position. this is enough
 * to trigger a segment switch in the streaming thread to start
 * streaming from the desired position.
 *
 * Keyframe seeking is a little more complicated when dealing with
 * segments. Ideally we want to move to the previous keyframe in 
 * the segment but there might not be a keyframe in the segment. In
 * fact, none of the segments could contain a keyframe. We take a
 * practical approach: seek to the previous keyframe in the segment,
 * if there is none, seek to the beginning of the segment.
639
640
 *
 * Called with STREAM_LOCK
641
 */
642
static gboolean
643
gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
644
{
645
  gint64 desired_offset;
646
  gint n;
647

648
  desired_offset = segment->last_stop;
649

650
651
  GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (desired_offset));
652

653
  if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
654
655
656
657
658
659
660
661
    guint64 min_offset;

    min_offset = desired_offset;

    /* for each stream, find the index of the sample in the segment
     * and move back to the previous keyframe. */
    for (n = 0; n < qtdemux->n_streams; n++) {
      QtDemuxStream *str;
662
      guint32 index, kindex;
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
      guint32 seg_idx;
      guint64 media_start;
      guint64 media_time;
      guint64 seg_time;
      QtDemuxSegment *seg;

      str = qtdemux->streams[n];

      seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_offset);
      GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);

      /* segment not found, continue with normal flow */
      if (seg_idx == -1)
        continue;

      /* get segment and time in the segment */
      seg = &str->segments[seg_idx];
      seg_time = desired_offset - seg->time;

      /* get the media time in the segment */
      media_start = seg->media_start + seg_time;

      /* get the index of the sample with media time */
      index = gst_qtdemux_find_index (qtdemux, str, media_start);
      GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u",
          GST_TIME_ARGS (media_start), index);
689

690
      /* find previous keyframe */
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
      kindex = gst_qtdemux_find_keyframe (qtdemux, str, index);

      GST_DEBUG_OBJECT (qtdemux, "keyframe at %u", kindex);

      /* if the keyframe is at a different position, we need to update the
       * requiested seek time */
      if (index != kindex) {
        index = kindex;

        /* get timestamp of keyframe */
        media_time = str->samples[kindex].timestamp;
        GST_DEBUG_OBJECT (qtdemux, "keyframe at %u with time %" GST_TIME_FORMAT,
            kindex, GST_TIME_ARGS (media_time));

        /* keyframes in the segment get a chance to change the
         * desired_offset. keyframes out of the segment are
         * ignored. */
        if (media_time >= seg->media_start) {
          guint64 seg_time;

          /* this keyframe is inside the segment, convert back to
           * segment time */
          seg_time = (media_time - seg->media_start) + seg->time;
          if (seg_time < min_offset)
            min_offset = seg_time;
        }
717
718
719
720
      }
    }
    GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
        GST_TIME_FORMAT, GST_TIME_ARGS (desired_offset));
721
    desired_offset = min_offset;
722
723
724
725
726
  }

  /* and set all streams to the final position */
  for (n = 0; n < qtdemux->n_streams; n++) {
    QtDemuxStream *stream = qtdemux->streams[n];
727

728
729
730
    stream->time_position = desired_offset;
    stream->sample_index = 0;
    stream->segment_index = -1;
731
    stream->last_ret = GST_FLOW_OK;
732
  }
733
734
  segment->last_stop = desired_offset;
  segment->time = desired_offset;
735

736
  /* we stop at the end */
737
738
739
  if (segment->stop == -1)
    segment->stop = segment->duration;

740
741
  return TRUE;
}
742

743
/* do a seek in pull based mode */
744
745
746
747
748
749
750
751
752
753
754
755
static gboolean
gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
{
  gdouble rate;
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  gint64 cur, stop;
  gboolean flush;
  gboolean res;
  gboolean update;
  GstSegment seeksegment;
756
  int i;
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783

  if (event) {
    GST_DEBUG_OBJECT (qtdemux, "doing seek with event");

    gst_event_parse_seek (event, &rate, &format, &flags,
        &cur_type, &cur, &stop_type, &stop);

    /* we have to have a format as the segment format. Try to convert
     * if not. */
    if (format != GST_FORMAT_TIME) {
      GstFormat fmt;

      fmt = GST_FORMAT_TIME;
      res = TRUE;
      if (cur_type != GST_SEEK_TYPE_NONE)
        res = gst_pad_query_convert (pad, format, cur, &fmt, &cur);
      if (res && stop_type != GST_SEEK_TYPE_NONE)
        res = gst_pad_query_convert (pad, format, stop, &fmt, &stop);
      if (!res)
        goto no_format;

      format = fmt;
    }
  } else {
    GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
    flags = 0;
  }
784

785
  flush = flags & GST_SEEK_FLAG_FLUSH;
786

787
  GST_DEBUG_OBJECT (qtdemux, "seek format %d", format);
788

789
  /* stop streaming, either by flushing or by pausing the task */
790
791
792
793
794
795
  if (flush) {
    /* unlock upstream pull_range */
    gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ());
    /* make sure out loop function exits */
    gst_qtdemux_push_event (qtdemux, gst_event_new_flush_start ());
  } else {
796
    /* non flushing seek, pause the task */
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
    gst_pad_pause_task (qtdemux->sinkpad);
  }

  /* wait for streaming to finish */
  GST_PAD_STREAM_LOCK (qtdemux->sinkpad);

  /* copy segment, we need this because we still need the old
   * segment when we close the current segment. */
  memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));

  if (event) {
    /* configure the segment with the seek variables */
    GST_DEBUG_OBJECT (qtdemux, "configuring seek");
    gst_segment_set_seek (&seeksegment, rate, format, flags,
        cur_type, cur, stop_type, stop, &update);
  }

814
  /* now do the seek, this actually never returns FALSE */
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
  res = gst_qtdemux_perform_seek (qtdemux, &seeksegment);

  /* prepare for streaming again */
  if (flush) {
    gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_stop ());
    gst_qtdemux_push_event (qtdemux, gst_event_new_flush_stop ());
  } else if (qtdemux->segment_running) {
    /* we are running the current segment and doing a non-flushing seek,
     * close the segment first based on the last_stop. */
    GST_DEBUG_OBJECT (qtdemux, "closing running segment %" G_GINT64_FORMAT
        " to %" G_GINT64_FORMAT, qtdemux->segment.start,
        qtdemux->segment.last_stop);

    gst_qtdemux_push_event (qtdemux,
        gst_event_new_new_segment (TRUE,
            qtdemux->segment.rate, qtdemux->segment.format,
            qtdemux->segment.start, qtdemux->segment.last_stop,
            qtdemux->segment.time));
  }

835
  /* commit the new segment */
836
837
838
839
840
841
842
843
  memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));

  if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
    gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
        gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
            qtdemux->segment.format, qtdemux->segment.last_stop));
  }

844
845
  /* restart streaming, NEWSEGMENT will be sent from the streaming
   * thread. */
846
  qtdemux->segment_running = TRUE;
847
848
849
  for (i = 0; i < qtdemux->n_streams; i++) {
    qtdemux->streams[i]->last_ret = GST_FLOW_OK;
  }
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
  gst_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
      qtdemux->sinkpad);

  GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);

  return TRUE;

  /* ERRORS */
no_format:
  {
    GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
    return FALSE;
  }
}

static gboolean
gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event)
{
  gboolean res = TRUE;
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
      res = gst_qtdemux_do_seek (qtdemux, pad, event);
874
      break;
875
876
877
878
879
    default:
      res = FALSE;
      break;
  }

880
881
  gst_object_unref (qtdemux);

882
883
884
885
886
  gst_event_unref (event);

  return res;
}

887
static gboolean
888
gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
Artyom Baginski's avatar
Artyom Baginski committed
889
{
890
891
892
893
894
895
  GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
  gboolean res = FALSE;

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_NEWSEGMENT:
      /* We need to convert it to a GST_FORMAT_TIME new segment */
Artyom Baginski's avatar
Artyom Baginski committed
896
    default:
897
      gst_pad_event_default (demux->sinkpad, event);
898
      return TRUE;
Artyom Baginski's avatar
Artyom Baginski committed
899
  }
900

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
901
  gst_event_unref (event);
902
  return res;
Artyom Baginski's avatar
Artyom Baginski committed
903
904
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
905
906
static GstStateChangeReturn
gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
907
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
908
  GstQTDemux *qtdemux = GST_QTDEMUX (element);
909
910
  GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;

911
912
913
914
915
916
917
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      break;
    default:
      break;
  }

918
  result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
Artyom Baginski's avatar
Artyom Baginski committed
919

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
920
921
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:{
922
923
      gint n;

924
      qtdemux->state = QTDEMUX_STATE_INITIAL;
925
      qtdemux->last_ts = GST_CLOCK_TIME_NONE;
926
927
928
929
      qtdemux->neededbytes = 16;
      qtdemux->todrop = 0;
      qtdemux->pullbased = FALSE;
      qtdemux->offset = 0;
930
931
932
933
      qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
      if (qtdemux->mdatbuffer)
        gst_buffer_unref (qtdemux->mdatbuffer);
      qtdemux->mdatbuffer = NULL;
934
      gst_adapter_clear (qtdemux->adapter);
935
936
937
      for (n = 0; n < qtdemux->n_streams; n++) {
        gst_element_remove_pad (element, qtdemux->streams[n]->pad);
        g_free (qtdemux->streams[n]->samples);
938
939
        if (qtdemux->streams[n]->caps)
          gst_caps_unref (qtdemux->streams[n]->caps);
940
        g_free (qtdemux->streams[n]->segments);
941
        g_free (qtdemux->streams[n]);
942
943
      }
      qtdemux->n_streams = 0;
944
      gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
945
      break;
946
    }
947
948
949
    default:
      break;
  }
Artyom Baginski's avatar
Artyom Baginski committed
950

951
  return result;
952
}
Artyom Baginski's avatar
Artyom Baginski committed
953

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
954
static void
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
extract_initial_length_and_fourcc (guint8 * data, guint32 * plength,
    guint32 * pfourcc)
{
  guint32 length;
  guint32 fourcc;

  length = GST_READ_UINT32_BE (data);
  GST_DEBUG ("length %08x", length);
  fourcc = GST_READ_UINT32_LE (data + 4);
  GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));

  if (length == 0) {
    length = G_MAXUINT32;
  }
  if (length == 1) {
    /* this means we have an extended size, which is the 64 bit value of
     * the next 8 bytes */
    guint32 length1, length2;

    length1 = GST_READ_UINT32_BE (data + 8);
    GST_DEBUG ("length1 %08x", length1);
    length2 = GST_READ_UINT32_BE (data + 12);
    GST_DEBUG ("length2 %08x", length2);

    /* FIXME: I guess someone didn't want to make 64 bit size work :) */
    length = length2;
  }

  if (plength)
    *plength = length;
  if (pfourcc)
    *pfourcc = fourcc;
}

static GstFlowReturn
gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
991
992
993
{
  guint32 length;
  guint32 fourcc;
994
  GstBuffer *buf = NULL;
995
996
997
998
999
1000
  GstFlowReturn ret = GST_FLOW_OK;
  guint64 cur_offset = qtdemux->offset;

  ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, 16, &buf);
  if (ret != GST_FLOW_OK)
    goto beach;