qtdemux.c 112 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>
Artyom Baginski's avatar
Artyom Baginski committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *
 * 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.
 */

21
22
23
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
24
#include "qtdemux.h"
25

26
#include <stdlib.h>
Artyom Baginski's avatar
Artyom Baginski committed
27
#include <string.h>
28
29
#include <zlib.h>

30
31
GST_DEBUG_CATEGORY_EXTERN (qtdemux_debug);
#define GST_CAT_DEFAULT qtdemux_debug
32

33
/* temporary hack */
34
#define gst_util_dump_mem(a,b)  /* */
35

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
36
37
38
#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))
39
#define QTDEMUX_GUINT8_GET(a) (*(guint8 *)(a))
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
40
41
42
#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))
43
44

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

46
47
48
typedef struct _QtNode QtNode;
typedef struct _QtNodeType QtNodeType;
typedef struct _QtDemuxSample QtDemuxSample;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
49

50
51
//typedef struct _QtDemuxStream QtDemuxStream;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
52
53
struct _QtNode
{
54
55
56
57
58
  guint32 type;
  gpointer data;
  int len;
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
59
60
struct _QtNodeType
{
61
62
63
  guint32 fourcc;
  char *name;
  int flags;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
64
  void (*dump) (GstQTDemux * qtdemux, void *buffer, int depth);
65
66
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
67
68
struct _QtDemuxSample
{
69
70
71
  int sample_index;
  int chunk;
  int size;
72
  guint64 offset;
73
74
  guint64 timestamp;            /* In GstClockTime */
  guint32 duration;             /* in stream->timescale units */
75
76
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
77
78
struct _QtDemuxStream
{
79
80
  guint32 subtype;
  GstCaps *caps;
81
  guint32 fourcc;
82
83
84
85
86
87
  GstPad *pad;
  int n_samples;
  QtDemuxSample *samples;
  int timescale;

  int sample_index;
88
89
90

  int width;
  int height;
91
92
93
  /* Numerator/denominator framerate */
  gint fps_n;
  gint fps_d;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
94

95
96
  double rate;
  int n_channels;
97
  guint bytes_per_frame;
98
  guint compression;
99
  guint samples_per_packet;
100
101
  guint16 bits_per_sample;
  guint16 color_table_id;
102
103
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
104
105
enum QtDemuxState
{
106
107
108
  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 */
109
  QTDEMUX_STATE_BUFFER_MDAT     /* Buffering the mdat atom */
110
111
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
112
113
static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
114

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
115
static GstElementDetails gst_qtdemux_details = {
116
117
118
  "QuickTime Demuxer",
  "Codec/Demuxer",
  "Demultiplex a QuickTime file into audio and video streams",
119
  "David Schleef <ds@schleef.org>"
Artyom Baginski's avatar
Artyom Baginski committed
120
121
};

David Schleef's avatar
David Schleef committed
122
static GstStaticPadTemplate gst_qtdemux_sink_template =
123
    GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
124
    GST_PAD_SINK,
125
    GST_PAD_ALWAYS,
126
    GST_STATIC_CAPS ("video/quicktime; audio/x-m4a; application/x-3gp")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
127
    );
David Schleef's avatar
David Schleef committed
128
129

static GstStaticPadTemplate gst_qtdemux_videosrc_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
130
131
132
133
GST_STATIC_PAD_TEMPLATE ("audio_%02d",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
David Schleef's avatar
David Schleef committed
134
135

static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
136
137
138
139
GST_STATIC_PAD_TEMPLATE ("video_%02d",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
Artyom Baginski's avatar
Artyom Baginski committed
140

141
static GstElementClass *parent_class = NULL;
Artyom Baginski's avatar
Artyom Baginski committed
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
/* 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
231
232
233
static void gst_qtdemux_class_init (GstQTDemuxClass * klass);
static void gst_qtdemux_base_init (GstQTDemuxClass * klass);
static void gst_qtdemux_init (GstQTDemux * quicktime_demux);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
234
235
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
    GstStateChange transition);
236
static void gst_qtdemux_loop (GstPad * pad);
237
static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
238
239
static gboolean qtdemux_sink_activate (GstPad * sinkpad);
static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
240
241
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
242
243
244
245
246
247
248

static void qtdemux_parse_moov (GstQTDemux * qtdemux, void *buffer, int length);
static void qtdemux_parse (GstQTDemux * qtdemux, GNode * node, void *buffer,
    int length);
static QtNodeType *qtdemux_type_get (guint32 fourcc);
static void qtdemux_node_dump (GstQTDemux * qtdemux, GNode * node);
static void qtdemux_parse_tree (GstQTDemux * qtdemux);
249
static void qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta);
250
static void qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag,
251
    GNode * node);
252
253
254
255
256
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);

257
258
static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
    QtDemuxStream * stream, GNode * esds);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
259
static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux, guint32 fourcc,
260
    const guint8 * stsd_data, const gchar ** codec_name);
261
262
263
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
264
265
266

static GType
gst_qtdemux_get_type (void)
Artyom Baginski's avatar
Artyom Baginski committed
267
268
269
270
271
{
  static GType qtdemux_type = 0;

  if (!qtdemux_type) {
    static const GTypeInfo qtdemux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
272
273
274
275
276
      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
277
    };
278

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
279
    qtdemux_type =
280
281
        g_type_register_static (GST_TYPE_ELEMENT, "GstQTDemux", &qtdemux_info,
        0);
Artyom Baginski's avatar
Artyom Baginski committed
282
283
284
285
  }
  return qtdemux_type;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
286
287
static void
gst_qtdemux_base_init (GstQTDemuxClass * klass)
288
289
290
291
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
292
293
294
295
296
      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));
297
  gst_element_class_set_details (element_class, &gst_qtdemux_details);
David Schleef's avatar
David Schleef committed
298

299
300
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
301
302
static void
gst_qtdemux_class_init (GstQTDemuxClass * klass)
Artyom Baginski's avatar
Artyom Baginski committed
303
304
305
306
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
307
308
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Artyom Baginski's avatar
Artyom Baginski committed
309

310
  parent_class = g_type_class_peek_parent (klass);
311

Artyom Baginski's avatar
Artyom Baginski committed
312
313
314
  gstelement_class->change_state = gst_qtdemux_change_state;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
315
316
static void
gst_qtdemux_init (GstQTDemux * qtdemux)
Artyom Baginski's avatar
Artyom Baginski committed
317
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
318
319
320
  qtdemux->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get
      (&gst_qtdemux_sink_template), "sink");
321
322
323
  gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
  gst_pad_set_activatepull_function (qtdemux->sinkpad,
      qtdemux_sink_activate_pull);
324
325
326
327
  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
328
  gst_element_add_pad (GST_ELEMENT (qtdemux), qtdemux->sinkpad);
329

330
  qtdemux->state = QTDEMUX_STATE_INITIAL;
331
  qtdemux->last_ts = GST_CLOCK_TIME_NONE;
332
333
334
335
  qtdemux->pullbased = FALSE;
  qtdemux->neededbytes = 16;
  qtdemux->todrop = 0;
  qtdemux->adapter = gst_adapter_new ();
336
337
338
  qtdemux->offset = 0;
  qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
  qtdemux->mdatbuffer = NULL;
Artyom Baginski's avatar
Artyom Baginski committed
339
340
}

341
#if 0
342
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
343
344
gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
345
346
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
347
  QtDemuxStream *stream = gst_pad_get_element_private (pad);
348

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
349
  if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e') &&
350
351
352
353
354
355
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
    return FALSE;

  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
356
357
358
359
360
361
362
363
364
        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;
365
366
367
368
      }
      break;
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
369
370
371
372
373
374
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
375
376
377
378
      }
      break;
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
379
380
381
382
383
384
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
385
386
387
388
389
390
391
392
      }
      break;
    default:
      res = FALSE;
  }

  return res;
}
393
#endif
394
395

static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
396
gst_qtdemux_get_src_query_types (GstPad * pad)
397
398
399
{
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
Wim Taymans's avatar
Wim Taymans committed
400
    GST_QUERY_DURATION,
401
402
403
404
405
406
407
    0
  };

  return src_types;
}

static gboolean
408
gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
409
{
410
411
  gboolean res = FALSE;
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
412

413
  switch (GST_QUERY_TYPE (query)) {
414
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
415
416
417
418
419
420
      if (GST_CLOCK_TIME_IS_VALID (qtdemux->last_ts)) {
        gst_query_set_position (query, GST_FORMAT_TIME, qtdemux->last_ts);
        res = TRUE;
      }
      break;
    case GST_QUERY_DURATION:
421
422
      if (qtdemux->pullbased && qtdemux->duration != 0
          && qtdemux->timescale != 0) {
423
424
425
426
427
428
        gint64 duration;

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

        gst_query_set_duration (query, GST_FORMAT_TIME, duration);
429
        res = TRUE;
430
431
432
433
434
435
436
      }
      break;
    default:
      res = FALSE;
      break;
  }

437
438
  gst_object_unref (qtdemux);

439
440
441
  return res;
}

442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
/* sends event to all source pads; takes ownership of the event */
static void
gst_qtdemux_send_event (GstQTDemux * qtdemux, GstEvent * event)
{
  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++) {
    gst_event_ref (event);
    gst_pad_push_event (qtdemux->streams[n]->pad, event);
  }
  gst_event_unref (event);
}

458
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
459
gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event)
460
461
{
  gboolean res = TRUE;
462
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
463
464

  switch (GST_EVENT_TYPE (event)) {
465
466
467
468
    case GST_EVENT_SEEK:{
      GstFormat format;
      GstSeekFlags flags;
      gint64 desired_offset;
469

470
      /* FIXME do seeking correctly */
471
472
473
474
475
476
      gst_event_parse_seek (event, NULL, &format, &flags, NULL,
          &desired_offset, NULL, NULL);
      GST_DEBUG ("seek format %d", format);

      switch (format) {
        case GST_FORMAT_TIME:{
477
478
          gint i = 0, n;
          QtDemuxStream *stream = gst_pad_get_element_private (pad);
479
480
481

          GST_DEBUG ("seeking to %" G_GINT64_FORMAT, desired_offset);

482
483
484
485
486
          if (!stream->n_samples) {
            res = FALSE;
            break;
          }

487
          /* unlock upstream pull_range */
488
          gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ());
489
          /* make sure out loop function exits */
490
491
          gst_qtdemux_send_event (qtdemux, gst_event_new_flush_start ());

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

495
496
497
498
499
          /* resync to new time */
          for (n = 0; n < qtdemux->n_streams; n++) {
            QtDemuxStream *str = qtdemux->streams[n];

            for (i = 0; i < str->n_samples; i++) {
500
501
502
503
504
              /* Seek to the sample just before the desired offset and
               * let downstream throw away bits outside of the segment */
              if (str->samples[i].timestamp > desired_offset) {
                if (i > 0)
                  --i;
505
                break;
506
              }
507
508
509
            }
            str->sample_index = i;
          }
510
          /* prepare for streaming again */
511
512
513
514
515
516
          gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_stop ());
          gst_qtdemux_send_event (qtdemux, gst_event_new_flush_stop ());

          gst_qtdemux_send_event (qtdemux,
              gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
                  desired_offset, GST_CLOCK_TIME_NONE, desired_offset));
517
518
519

          /* and restart */
          gst_pad_start_task (qtdemux->sinkpad,
520
              (GstTaskFunction) gst_qtdemux_loop, qtdemux->sinkpad);
521

522
          GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
523
          break;
524
525
526
527
        }
        default:
          res = FALSE;
          break;
528
      }
529
530
      break;
    }
531
532
533
534
535
    default:
      res = FALSE;
      break;
  }

536
537
  gst_object_unref (qtdemux);

538
539
540
541
542
  gst_event_unref (event);

  return res;
}

543
544
GST_DEBUG_CATEGORY (qtdemux_debug);

Artyom Baginski's avatar
Artyom Baginski committed
545
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
546
plugin_init (GstPlugin * plugin)
Artyom Baginski's avatar
Artyom Baginski committed
547
{
548
  GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
549

550
  return gst_element_register (plugin, "qtdemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
551
      GST_RANK_PRIMARY, GST_TYPE_QTDEMUX);
Artyom Baginski's avatar
Artyom Baginski committed
552
553
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
554
555
556
557
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "qtdemux",
    "Quicktime stream demuxer",
558
    plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
559

560
static gboolean
561
gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
Artyom Baginski's avatar
Artyom Baginski committed
562
{
563
564
565
566
567
568
  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
569
    default:
570
      gst_pad_event_default (demux->sinkpad, event);
571
      return TRUE;
Artyom Baginski's avatar
Artyom Baginski committed
572
  }
573

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
574
  gst_event_unref (event);
575
  return res;
Artyom Baginski's avatar
Artyom Baginski committed
576
577
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
578
579
static GstStateChangeReturn
gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
580
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
581
  GstQTDemux *qtdemux = GST_QTDEMUX (element);
582
583
584
  GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
586
587
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:{
588
589
      gint n;

590
      qtdemux->state = QTDEMUX_STATE_INITIAL;
591
      qtdemux->last_ts = GST_CLOCK_TIME_NONE;
592
593
594
595
      qtdemux->neededbytes = 16;
      qtdemux->todrop = 0;
      qtdemux->pullbased = FALSE;
      qtdemux->offset = 0;
596
597
598
599
      qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
      if (qtdemux->mdatbuffer)
        gst_buffer_unref (qtdemux->mdatbuffer);
      qtdemux->mdatbuffer = NULL;
600
      gst_adapter_clear (qtdemux->adapter);
601
602
603
      for (n = 0; n < qtdemux->n_streams; n++) {
        gst_element_remove_pad (element, qtdemux->streams[n]->pad);
        g_free (qtdemux->streams[n]->samples);
604
605
        if (qtdemux->streams[n]->caps)
          gst_caps_unref (qtdemux->streams[n]->caps);
606
607
608
        g_free (qtdemux->streams[n]);
      }
      qtdemux->n_streams = 0;
609
      break;
610
    }
611
612
613
    default:
      break;
  }
Artyom Baginski's avatar
Artyom Baginski committed
614

615
  return result;
616
}
Artyom Baginski's avatar
Artyom Baginski committed
617

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
618
static void
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
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)
655
656
657
{
  guint32 length;
  guint32 fourcc;
658
  GstBuffer *buf = NULL;
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
  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;
  extract_initial_length_and_fourcc (GST_BUFFER_DATA (buf), &length, &fourcc);
  gst_buffer_unref (buf);


  switch (fourcc) {
    case GST_MAKE_FOURCC ('m', 'd', 'a', 't'):
    case GST_MAKE_FOURCC ('f', 'r', 'e', 'e'):
    case GST_MAKE_FOURCC ('w', 'i', 'd', 'e'):
    case GST_MAKE_FOURCC ('P', 'I', 'C', 'T'):
    case GST_MAKE_FOURCC ('p', 'n', 'o', 't'):
      goto ed_edd_and_eddy;
    case GST_MAKE_FOURCC ('m', 'o', 'o', 'v'):{
      GstBuffer *moov;

      ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
      if (ret != GST_FLOW_OK)
        goto beach;
682
683
684
685
686
687
688
      if (length != GST_BUFFER_SIZE (moov)) {
        GST_WARNING_OBJECT (qtdemux,
            "We got less than expected (received %d, wanted %d)",
            GST_BUFFER_SIZE (moov), length);
        ret = GST_FLOW_ERROR;
        goto beach;
      }
689
690
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
717
718
719
720
721
722
723
724
725
      cur_offset += length;
      qtdemux->offset += length;

      qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length);
      if (1) {
        qtdemux_node_dump (qtdemux, qtdemux->moov_node);
      }
      qtdemux_parse_tree (qtdemux);
      g_node_destroy (qtdemux->moov_node);
      gst_buffer_unref (moov);
      qtdemux->moov_node = NULL;
      qtdemux->state = QTDEMUX_STATE_MOVIE;
      GST_DEBUG_OBJECT (qtdemux, "switching state to STATE_MOVIE (%d)",
          qtdemux->state);
    }
      break;
    ed_edd_and_eddy:
    default:{
      GST_LOG ("unknown %08x '%" GST_FOURCC_FORMAT "' at %d",
          fourcc, GST_FOURCC_ARGS (fourcc), cur_offset);
      cur_offset += length;
      qtdemux->offset += length;
      break;
    }
  }

beach:
  return ret;
}

static GstFlowReturn
gst_qtdemux_loop_state_movie (GstQTDemux * qtdemux)
{
  GstFlowReturn ret = GST_FLOW_OK;
  GstBuffer *buf = NULL;
  QtDemuxStream *stream;
  guint64 min_time;
726
  guint64 offset;
727
  int size;
728
729
730
731
732
733
734
  int index = -1;
  int i;

  /* Figure out the next stream sample to output */
  min_time = G_MAXUINT64;
  for (i = 0; i < qtdemux->n_streams; i++) {
    stream = qtdemux->streams[i];
735
736
737
738
739
740
741
742
743
744
    if (stream->sample_index < stream->n_samples) {
      GST_LOG_OBJECT (qtdemux,
          "stream %d: sample_index %d, timestamp %" GST_TIME_FORMAT, i,
          stream->sample_index,
          GST_TIME_ARGS (stream->samples[stream->sample_index].timestamp));

      if (stream->samples[stream->sample_index].timestamp < min_time) {
        min_time = stream->samples[stream->sample_index].timestamp;
        index = i;
      }
745
746
747
748
749
750
751
752
753
754
755
756
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
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
    }
  }
  if (index == -1) {
    GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
    gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ());
    goto beach;
  }

  stream = qtdemux->streams[index];

  offset = stream->samples[stream->sample_index].offset;
  size = stream->samples[stream->sample_index].size;

  GST_LOG_OBJECT (qtdemux,
      "pushing from stream %d, sample_index=%d offset=%" G_GUINT64_FORMAT
      ",size=%d timestamp=%" GST_TIME_FORMAT,
      index, stream->sample_index, offset, size,
      GST_TIME_ARGS (stream->samples[stream->sample_index].timestamp));

  buf = NULL;
  if (size > 0) {
    GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
        offset);

    ret = gst_pad_pull_range (qtdemux->sinkpad, offset, size, &buf);
    if (ret != GST_FLOW_OK)
      goto beach;
  }

  if (buf) {
    /* hum... FIXME changing framerate breaks horribly, better set
     * an average framerate, or get rid of the framerate property. */
    if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e')) {
      //float fps =
      //   1. * GST_SECOND / stream->samples[stream->sample_index].duration;
      /*
         if (fps != stream->fps) {
         gst_caps_set_simple (stream->caps, "framerate", G_TYPE_DOUBLE, fps,
         NULL);
         stream->fps = fps;
         gst_pad_set_explicit_caps (stream->pad, stream->caps);
         }
       */
    }

    /* first buffer? */
    if (qtdemux->last_ts == GST_CLOCK_TIME_NONE) {
      gst_qtdemux_send_event (qtdemux,
          gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
              0, GST_CLOCK_TIME_NONE, 0));
    }

    /* timestamps of AMR aren't known... */
    if (stream->fourcc != GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
      GST_BUFFER_TIMESTAMP (buf) =
          stream->samples[stream->sample_index].timestamp;
      qtdemux->last_ts = GST_BUFFER_TIMESTAMP (buf);
      GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int
          (stream->samples[stream->sample_index].duration, GST_SECOND,
          stream->timescale);
    } else {
      if (stream->sample_index == 0) {
        GST_BUFFER_TIMESTAMP (buf) = 0;
      }
    }

    GST_LOG_OBJECT (qtdemux,
        "Pushing buffer with time %" GST_TIME_FORMAT " on pad %p",
        GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), stream->pad);
    gst_buffer_set_caps (buf, stream->caps);
    ret = gst_pad_push (stream->pad, buf);
  }

  stream->sample_index++;

beach:
  return ret;
}

static void
825
gst_qtdemux_loop (GstPad * pad)
826
827
828
829
{
  GstQTDemux *qtdemux = GST_QTDEMUX (GST_OBJECT_PARENT (pad));
  guint64 cur_offset;
  GstFlowReturn ret = GST_FLOW_ERROR;
830

831
  cur_offset = qtdemux->offset;
832
  GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
833
      cur_offset, qtdemux->state);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
834
835

  switch (qtdemux->state) {
836
837
838
839
840
841
842
843
844
845
846
    case QTDEMUX_STATE_INITIAL:
    case QTDEMUX_STATE_HEADER:
      ret = gst_qtdemux_loop_state_header (qtdemux);
      break;
    case QTDEMUX_STATE_MOVIE:
      ret = gst_qtdemux_loop_state_movie (qtdemux);
      break;
    default:
      /* oh crap */
      g_error ("State=%d", qtdemux->state);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
847

848
  if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_NOT_LINKED)) {
849
850
851
852
853
854
855
856
857
858
    GST_LOG_OBJECT (qtdemux, "pausing task, reason %s",
        gst_flow_get_name (ret));
    gst_pad_pause_task (qtdemux->sinkpad);
    if (GST_FLOW_IS_FATAL (ret)) {
      gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ());
      GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
          (NULL), ("stream stopped, reason %s", gst_flow_get_name (ret)));
    }
  }
}
859

860
861
862
863
864
865
/*
  next_entry_size
  
  Returns the size of the first entry at the current offset.
  If -1, there are none (which means EOS or empty file).
*/
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
866

867
868
869
870
871
static guint64
next_entry_size (GstQTDemux * demux)
{
  QtDemuxStream *stream;
  int i;
872
  int smallidx = -1;
873
874
875
876
877
878
879
880
881
882
883
884
885
  guint64 smalloffs = -1;

  GST_LOG_OBJECT (demux, "Finding entry at offset %lld", demux->offset);

  for (i = 0; i < demux->n_streams; i++) {
    stream = demux->streams[i];

    GST_LOG_OBJECT (demux,
        "Checking Stream %d (sample_index:%d / offset:%lld / size:%d / chunk:%d)",
        i, stream->sample_index, stream->samples[stream->sample_index].offset,
        stream->samples[stream->sample_index].size,
        stream->samples[stream->sample_index].chunk);

886
887
888
    if (((smalloffs == -1)
            || (stream->samples[stream->sample_index].offset < smalloffs))
        && (stream->samples[stream->sample_index].size)) {
889
890
      smallidx = i;
      smalloffs = stream->samples[stream->sample_index].offset;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
891
    }
892
893
894
895
896
  }

  GST_LOG_OBJECT (demux, "stream %d offset %lld demux->offset :%lld",
      smallidx, smalloffs, demux->offset);

897
898
  if (smallidx == -1)
    return -1;
899
900
901
902
903
904
905
906
907
908
909
910
911
  stream = demux->streams[smallidx];

  if (stream->samples[stream->sample_index].offset >= demux->offset) {
    demux->todrop =
        stream->samples[stream->sample_index].offset - demux->offset;
    return stream->samples[stream->sample_index].size + demux->todrop;
  }

  GST_DEBUG_OBJECT (demux, "There wasn't any entry at offset %lld",
      demux->offset);
  return -1;
}

912
913
914
915
916
917
918
919
920
921
922
923
static void
gst_qtdemux_post_buffering (GstQTDemux * demux, gint num, gint denom)
{
  gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);

  gst_element_post_message (GST_ELEMENT (demux),
      gst_message_new_custom (GST_MESSAGE_BUFFERING,
          GST_OBJECT (demux),
          gst_structure_new ("GstMessageBuffering",
              "buffer-percent", G_TYPE_INT, perc, NULL)));
}

924
925
926
927
928
929
930
931
932
933
934
935
static GstFlowReturn
gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
{
  GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
  GstFlowReturn ret = GST_FLOW_OK;

  gst_adapter_push (demux->adapter, inbuf);

  GST_DEBUG_OBJECT (demux, "pushing in inbuf %p, neededbytes:%u, available:%u",
      inbuf, demux->neededbytes, gst_adapter_available (demux->adapter));

  while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
936
      (ret == GST_FLOW_OK)) {
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951

    GST_DEBUG_OBJECT (demux,
        "state:%d , demux->neededbytes:%d, demux->offset:%lld", demux->state,
        demux->neededbytes, demux->offset);

    switch (demux->state) {
      case QTDEMUX_STATE_INITIAL:{
        const guint8 *data;
        guint32 fourcc;
        guint32 size;

        data = gst_adapter_peek (demux->adapter, demux->neededbytes);

        /* get fourcc/length, set neededbytes */
        extract_initial_length_and_fourcc ((guint8 *) data, &size, &fourcc);
952
953
954
955
        GST_DEBUG_OBJECT (demux,
            "Peeking found [%" GST_FOURCC_FORMAT "] size:%ld",
            GST_FOURCC_ARGS (fourcc), size);
        if ((fourcc == GST_MAKE_FOURCC ('m', 'd', 'a', 't'))) {
956
957
958
959
960
961
962
          if (demux->n_streams > 0) {
            demux->state = QTDEMUX_STATE_MOVIE;
            demux->neededbytes = next_entry_size (demux);
          } else {
            demux->state = QTDEMUX_STATE_BUFFER_MDAT;
            demux->neededbytes = size;
            demux->mdatoffset = demux->offset;
963
          }
964
965
966
        } else {
          demux->neededbytes = size;
          demux->state = QTDEMUX_STATE_HEADER;
967
        }
968
      }
969
        break;
970

971
972
973
      case QTDEMUX_STATE_HEADER:{
        guint8 *data;
        guint32 fourcc;
974

975
        GST_DEBUG_OBJECT (demux, "In header");
976

977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
        data = gst_adapter_take (demux->adapter, demux->neededbytes);

        /* parse the header */
        extract_initial_length_and_fourcc (data, NULL, &fourcc);
        if (fourcc == GST_MAKE_FOURCC ('m', 'o', 'o', 'v')) {
          GST_DEBUG_OBJECT (demux, "Parsing [moov]");

          qtdemux_parse_moov (demux, data, demux->neededbytes);
          qtdemux_node_dump (demux, demux->moov_node);
          qtdemux_parse_tree (demux);

          g_node_destroy (demux->moov_node);
          g_free (data);
          demux->moov_node = NULL;
        } else {
          GST_WARNING_OBJECT (demux,
              "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
              GST_FOURCC_ARGS (fourcc));
          /* Let's jump that one and go back to initial state */
996
997
998
999
1000
        }

        GST_DEBUG_OBJECT (demux, "Finished parsing the header");
        if (demux->mdatbuffer && demux->n_streams) {
          /* the mdat was before the header */
For faster browsing, not all history is shown. View entire blame