qtdemux.c 148 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(((guint8 *)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
  guint32 type;
64
  guint8 *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;
73
  void (*dump) (GstQTDemux * qtdemux, guint8 * 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
};

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
#define QT_CONTAINER 1

#define FOURCC_moov     GST_MAKE_FOURCC('m','o','o','v')
#define FOURCC_mvhd     GST_MAKE_FOURCC('m','v','h','d')
#define FOURCC_clip     GST_MAKE_FOURCC('c','l','i','p')
#define FOURCC_trak     GST_MAKE_FOURCC('t','r','a','k')
#define FOURCC_udta     GST_MAKE_FOURCC('u','d','t','a')
#define FOURCC_ctab     GST_MAKE_FOURCC('c','t','a','b')
#define FOURCC_tkhd     GST_MAKE_FOURCC('t','k','h','d')
#define FOURCC_crgn     GST_MAKE_FOURCC('c','r','g','n')
#define FOURCC_matt     GST_MAKE_FOURCC('m','a','t','t')
#define FOURCC_kmat     GST_MAKE_FOURCC('k','m','a','t')
#define FOURCC_edts     GST_MAKE_FOURCC('e','d','t','s')
#define FOURCC_elst     GST_MAKE_FOURCC('e','l','s','t')
#define FOURCC_load     GST_MAKE_FOURCC('l','o','a','d')
#define FOURCC_tref     GST_MAKE_FOURCC('t','r','e','f')
#define FOURCC_imap     GST_MAKE_FOURCC('i','m','a','p')
#define FOURCC___in     GST_MAKE_FOURCC(' ',' ','i','n')
#define FOURCC___ty     GST_MAKE_FOURCC(' ',' ','t','y')
#define FOURCC_mdia     GST_MAKE_FOURCC('m','d','i','a')
#define FOURCC_mdhd     GST_MAKE_FOURCC('m','d','h','d')
#define FOURCC_hdlr     GST_MAKE_FOURCC('h','d','l','r')
#define FOURCC_minf     GST_MAKE_FOURCC('m','i','n','f')
#define FOURCC_vmhd     GST_MAKE_FOURCC('v','m','h','d')
#define FOURCC_smhd     GST_MAKE_FOURCC('s','m','h','d')
#define FOURCC_gmhd     GST_MAKE_FOURCC('g','m','h','d')
#define FOURCC_gmin     GST_MAKE_FOURCC('g','m','i','n')
#define FOURCC_dinf     GST_MAKE_FOURCC('d','i','n','f')
#define FOURCC_dref     GST_MAKE_FOURCC('d','r','e','f')
#define FOURCC_stbl     GST_MAKE_FOURCC('s','t','b','l')
#define FOURCC_stsd     GST_MAKE_FOURCC('s','t','s','d')
#define FOURCC_stts     GST_MAKE_FOURCC('s','t','t','s')
#define FOURCC_stss     GST_MAKE_FOURCC('s','t','s','s')
#define FOURCC_stsc     GST_MAKE_FOURCC('s','t','s','c')
#define FOURCC_stsz     GST_MAKE_FOURCC('s','t','s','z')
#define FOURCC_stco     GST_MAKE_FOURCC('s','t','c','o')
#define FOURCC_vide     GST_MAKE_FOURCC('v','i','d','e')
#define FOURCC_soun     GST_MAKE_FOURCC('s','o','u','n')
#define FOURCC_strm     GST_MAKE_FOURCC('s','t','r','m')
#define FOURCC_rtsp     GST_MAKE_FOURCC('r','t','s','p')
#define FOURCC_co64     GST_MAKE_FOURCC('c','o','6','4')
#define FOURCC_cmov     GST_MAKE_FOURCC('c','m','o','v')
#define FOURCC_dcom     GST_MAKE_FOURCC('d','c','o','m')
#define FOURCC_cmvd     GST_MAKE_FOURCC('c','m','v','d')
#define FOURCC_hint     GST_MAKE_FOURCC('h','i','n','t')
#define FOURCC_mp4a     GST_MAKE_FOURCC('m','p','4','a')
#define FOURCC_mp4v     GST_MAKE_FOURCC('m','p','4','v')
#define FOURCC_wave     GST_MAKE_FOURCC('w','a','v','e')
#define FOURCC_appl     GST_MAKE_FOURCC('a','p','p','l')
#define FOURCC_esds     GST_MAKE_FOURCC('e','s','d','s')
#define FOURCC_hnti     GST_MAKE_FOURCC('h','n','t','i')
#define FOURCC_rtp_     GST_MAKE_FOURCC('r','t','p',' ')
#define FOURCC_sdp_     GST_MAKE_FOURCC('s','d','p',' ')
#define FOURCC_meta     GST_MAKE_FOURCC('m','e','t','a')
#define FOURCC_ilst     GST_MAKE_FOURCC('i','l','s','t')
#define FOURCC__nam     GST_MAKE_FOURCC(0xa9,'n','a','m')
#define FOURCC__ART     GST_MAKE_FOURCC(0xa9,'A','R','T')
#define FOURCC__wrt     GST_MAKE_FOURCC(0xa9,'w','r','t')
#define FOURCC__grp     GST_MAKE_FOURCC(0xa9,'g','r','p')
#define FOURCC__alb     GST_MAKE_FOURCC(0xa9,'a','l','b')
#define FOURCC__day     GST_MAKE_FOURCC(0xa9,'d','a','y')
#define FOURCC_gnre     GST_MAKE_FOURCC('g','n','r','e')
#define FOURCC_disc     GST_MAKE_FOURCC('d','i','s','c')
#define FOURCC_disk     GST_MAKE_FOURCC('d','i','s','k')
#define FOURCC_trkn     GST_MAKE_FOURCC('t','r','k','n')
#define FOURCC_cpil     GST_MAKE_FOURCC('c','p','i','l')
#define FOURCC_tmpo     GST_MAKE_FOURCC('t','m','p','o')
#define FOURCC__too     GST_MAKE_FOURCC(0xa9,'t','o','o')
#define FOURCC_____     GST_MAKE_FOURCC('-','-','-','-')
#define FOURCC_free     GST_MAKE_FOURCC('f','r','e','e')
#define FOURCC_data     GST_MAKE_FOURCC('d','a','t','a')
#define FOURCC_SVQ3     GST_MAKE_FOURCC('S','V','Q','3')
#define FOURCC_rmra     GST_MAKE_FOURCC('r','m','r','a')
#define FOURCC_rmda     GST_MAKE_FOURCC('r','m','d','a')
#define FOURCC_rdrf     GST_MAKE_FOURCC('r','d','r','f')
#define FOURCC__gen     GST_MAKE_FOURCC(0xa9, 'g', 'e', 'n')
#define FOURCC_rmdr     GST_MAKE_FOURCC('r','m','d','r')
#define FOURCC_rmvc     GST_MAKE_FOURCC('r','m','v','c')
#define FOURCC_qtim     GST_MAKE_FOURCC('q','t','i','m')


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
364
365
366
static void gst_qtdemux_class_init (GstQTDemuxClass * klass);
static void gst_qtdemux_base_init (GstQTDemuxClass * klass);
static void gst_qtdemux_init (GstQTDemux * quicktime_demux);
367
static void gst_qtdemux_dispose (GObject * object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
368
369
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
    GstStateChange transition);
370
static void gst_qtdemux_loop (GstPad * pad);
371
static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
372
373
static gboolean qtdemux_sink_activate (GstPad * sinkpad);
static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
374
375
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
376

377
378
379
static void qtdemux_parse_moov (GstQTDemux * qtdemux, guint8 * buffer,
    int length);
static void qtdemux_parse (GstQTDemux * qtdemux, GNode * node, guint8 * buffer,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
380
    int length);
381
static const QtNodeType *qtdemux_type_get (guint32 fourcc);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
382
383
static void qtdemux_node_dump (GstQTDemux * qtdemux, GNode * node);
static void qtdemux_parse_tree (GstQTDemux * qtdemux);
384
static void qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta);
385
static void qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag,
386
    GNode * node);
387
388
389
390
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);
391
392
static void qtdemux_tag_add_date (GstQTDemux * qtdemux, const char *tag,
    GNode * node);
393

394
static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
395
    QtDemuxStream * stream, GNode * esds, GstTagList * list);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
396
static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux, guint32 fourcc,
397
    const guint8 * stsd_data, const gchar ** codec_name);
398
399
400
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
401
402
403

static GType
gst_qtdemux_get_type (void)
Artyom Baginski's avatar
Artyom Baginski committed
404
405
406
407
408
{
  static GType qtdemux_type = 0;

  if (!qtdemux_type) {
    static const GTypeInfo qtdemux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
409
410
411
412
413
      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
414
    };
415

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
416
    qtdemux_type =
417
418
        g_type_register_static (GST_TYPE_ELEMENT, "GstQTDemux", &qtdemux_info,
        0);
Artyom Baginski's avatar
Artyom Baginski committed
419
420
421
422
  }
  return qtdemux_type;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
423
424
static void
gst_qtdemux_base_init (GstQTDemuxClass * klass)
425
426
427
428
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
429
430
431
432
433
      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));
434
  gst_element_class_set_details (element_class, &gst_qtdemux_details);
David Schleef's avatar
David Schleef committed
435

436
437
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
438
439
static void
gst_qtdemux_class_init (GstQTDemuxClass * klass)
Artyom Baginski's avatar
Artyom Baginski committed
440
441
442
443
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
444
445
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Artyom Baginski's avatar
Artyom Baginski committed
446

447
  parent_class = g_type_class_peek_parent (klass);
448

449
450
  gobject_class->dispose = gst_qtdemux_dispose;

Artyom Baginski's avatar
Artyom Baginski committed
451
452
453
  gstelement_class->change_state = gst_qtdemux_change_state;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
454
455
static void
gst_qtdemux_init (GstQTDemux * qtdemux)
Artyom Baginski's avatar
Artyom Baginski committed
456
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
457
  qtdemux->sinkpad =
458
      gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
459
460
461
  gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
  gst_pad_set_activatepull_function (qtdemux->sinkpad,
      qtdemux_sink_activate_pull);
462
463
464
465
  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);
466
  gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
467

468
  qtdemux->state = QTDEMUX_STATE_INITIAL;
469
  /* FIXME, use segment last_stop for this */
470
  qtdemux->last_ts = GST_CLOCK_TIME_NONE;
471
472
473
474
  qtdemux->pullbased = FALSE;
  qtdemux->neededbytes = 16;
  qtdemux->todrop = 0;
  qtdemux->adapter = gst_adapter_new ();
475
476
477
  qtdemux->offset = 0;
  qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
  qtdemux->mdatbuffer = NULL;
478
  gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
Artyom Baginski's avatar
Artyom Baginski committed
479
480
}

481
482
483
484
485
486
487
488
489
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;
  }
490
491

  G_OBJECT_CLASS (parent_class)->dispose (object);
492
493
}

494
#if 0
495
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
496
497
gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
498
499
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
500
  QtDemuxStream *stream = gst_pad_get_element_private (pad);
501

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
502
  if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e') &&
503
504
505
506
507
508
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
    return FALSE;

  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
509
510
511
512
513
514
515
516
517
        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;
518
519
520
521
      }
      break;
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
522
523
524
525
526
527
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
528
529
530
531
      }
      break;
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
532
533
534
535
536
537
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
538
539
540
541
542
543
544
545
      }
      break;
    default:
      res = FALSE;
  }

  return res;
}
546
#endif
547
548

static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
549
gst_qtdemux_get_src_query_types (GstPad * pad)
550
551
552
{
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
Wim Taymans's avatar
Wim Taymans committed
553
    GST_QUERY_DURATION,
554
555
556
557
558
559
    0
  };

  return src_types;
}

560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
static gboolean
gst_qtdemux_get_duration (GstQTDemux * qtdemux, gint64 * duration)
{
  gboolean res = TRUE;

  *duration = GST_CLOCK_TIME_NONE;

  if (qtdemux->duration != 0) {
    if (qtdemux->duration != G_MAXINT32 && qtdemux->timescale != 0) {
      *duration = gst_util_uint64_scale_int (qtdemux->duration,
          GST_SECOND, qtdemux->timescale);
    }
  }
  return res;
}

576
static gboolean
577
gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
578
{
579
580
  gboolean res = FALSE;
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
581

582
  switch (GST_QUERY_TYPE (query)) {
583
    case GST_QUERY_POSITION:
584
585
586
      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
587
588
589
590
        res = TRUE;
      }
      break;
    case GST_QUERY_DURATION:
591
592
    {
      gint64 duration;
593

594
      res = gst_qtdemux_get_duration (qtdemux, &duration);
595

596
597
      gst_query_set_duration (query, GST_FORMAT_TIME, duration);
    }
598
599
600
601
602
603
      break;
    default:
      res = FALSE;
      break;
  }

604
605
  gst_object_unref (qtdemux);

606
607
608
  return res;
}

609
/* push event on all source pads; takes ownership of the event */
610
static void
611
gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
612
613
614
615
616
617
618
{
  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++) {
619
620
621
622
    GstPad *pad;

    if ((pad = qtdemux->streams[n]->pad))
      gst_pad_push_event (pad, gst_event_ref (event));
623
624
625
626
  }
  gst_event_unref (event);
}

627
/* find the index of the sample that includes the data for @media_time
628
 *
629
630
 * Returns the index of the sample or n_samples when the sample was not
 * found.
631
 */
632
/* FIXME, binary search would be nice here */
633
634
635
static guint32
gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint64 media_time)
636
{
637
  guint32 i;
638

639
640
  if (str->n_samples == 0)
    return 0;
641

642
643
644
645
  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);
646
    }
647
648
649
  }
  return str->n_samples - 1;
}
650

651
652
653
654
655
656
657
658
659
660
661
662
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
689
690
691
692
693
694
695
696
697
698
699
/* 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;
700
    }
701
  }
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
  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;
723
724
725
726
}

/* perform the seek.
 *
727
728
729
730
731
732
733
734
735
736
737
 * 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.
738
739
 *
 * Called with STREAM_LOCK
740
 */
741
static gboolean
742
gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
743
{
744
  gint64 desired_offset;
745
  gint n;
746

747
  desired_offset = segment->last_stop;
748

749
750
  GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (desired_offset));
751

752
  if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
753
754
755
756
757
758
759
760
    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;
761
      guint32 index, kindex;
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
      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);
788

789
      /* find previous keyframe */
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
      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;
        }
816
817
818
819
      }
    }
    GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
        GST_TIME_FORMAT, GST_TIME_ARGS (desired_offset));
820
    desired_offset = min_offset;
821
822
823
824
825
  }

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

827
828
829
    stream->time_position = desired_offset;
    stream->sample_index = 0;
    stream->segment_index = -1;
830
    stream->last_ret = GST_FLOW_OK;
831
  }
832
833
  segment->last_stop = desired_offset;
  segment->time = desired_offset;
834

835
  /* we stop at the end */
836
837
838
  if (segment->stop == -1)
    segment->stop = segment->duration;

839
840
  return TRUE;
}
841

842
/* do a seek in pull based mode */
843
844
845
846
847
848
849
850
851
852
853
854
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;
855
  int i;
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882

  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;
  }
883

884
  flush = flags & GST_SEEK_FLAG_FLUSH;
885

886
  GST_DEBUG_OBJECT (qtdemux, "seek format %d", format);
887

888
  /* stop streaming, either by flushing or by pausing the task */
889
890
891
892
893
894
  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 {
895
    /* non flushing seek, pause the task */
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
    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);
  }

913
  /* now do the seek, this actually never returns FALSE */
914
915
916
917
918
919
920
921
922
923
924
925
926
  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);

927
928
    /* FIXME, needs to be done from the streaming thread. Also, the rate is the
     * product of the global rate and the (quicktime) segment rate. */
929
930
931
932
933
934
935
    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));
  }

936
  /* commit the new segment */
937
938
939
940
941
942
943
944
  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));
  }

945
946
  /* restart streaming, NEWSEGMENT will be sent from the streaming
   * thread. */
947
  qtdemux->segment_running = TRUE;
948
  for (i = 0; i < qtdemux->n_streams; i++)
949
    qtdemux->streams[i]->last_ret = GST_FLOW_OK;
950

951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
  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);
975
      break;
976
977
978
979
980
    default:
      res = FALSE;
      break;
  }

981
982
  gst_object_unref (qtdemux);

983
984
985
986
987
  gst_event_unref (event);

  return res;
}

988
static gboolean
989
gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
Artyom Baginski's avatar
Artyom Baginski committed
990
{
991
  GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
992
  gboolean res;
993
994
995
996

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_NEWSEGMENT:
      /* We need to convert it to a GST_FORMAT_TIME new segment */
997
998
999
      gst_event_unref (event);
      res = TRUE;
      break;
Artyom Baginski's avatar
Artyom Baginski committed
1000
    default:
1001
1002
      res = gst_pad_event_default (demux->sinkpad, event);
      break;
Artyom Baginski's avatar
Artyom Baginski committed
1003
  }
1004

1005
  return res;
Artyom Baginski's avatar
Artyom Baginski committed
1006
1007
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1008
1009
static GstStateChangeReturn
gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
1010
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1011
  GstQTDemux *qtdemux = GST_QTDEMUX (element);
1012
1013
  GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;

1014
1015
1016
1017
1018
1019
1020
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      break;
    default:
      break;
  }

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1023
1024
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:{
1025
1026
      gint n;

1027
      qtdemux->state = QTDEMUX_STATE_INITIAL;
1028
      qtdemux->last_ts = GST_CLOCK_TIME_NONE;
1029
1030
1031
1032
      qtdemux->neededbytes = 16;
      qtdemux->todrop = 0;
      qtdemux->pullbased = FALSE;
      qtdemux->offset = 0;
1033
1034
1035
1036
      qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
      if (qtdemux->mdatbuffer)
        gst_buffer_unref (qtdemux->mdatbuffer);
      qtdemux->mdatbuffer = NULL;
1037
      gst_adapter_clear (qtdemux->adapter);
1038
      for (n = 0; n < qtdemux->n_streams; n++) {
1039
1040
1041
1042
1043
1044
1045
1046
1047
        QtDemuxStream *stream = qtdemux->streams[n];

        if (stream->pad)
          gst_element_remove_pad (element, stream->pad);
        g_free (stream->samples);
        if (stream->caps)
          gst_caps_unref (stream->caps);
        g_free (stream->segments);
        g_free (stream);
1048
1049
      }
      qtdemux->n_streams = 0;
1050
1051
      qtdemux->n_video_streams = 0;
      qtdemux->n_audio_streams = 0;
1052
      gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
1053
      break;
1054
    }
1055
1056
1057
    default:
      break;
  }
Artyom Baginski's avatar
Artyom Baginski committed
1058

1059
  return result;
1060
}
Artyom Baginski's avatar
Artyom Baginski committed
1061

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1062
static void
1063
extract_initial_length_and_fourcc (guint8 * data, guint64 * plength,
1064
1065
    guint32 * pfourcc)
{
1066
  guint64 length;
1067
1068
1069
  guint32 fourcc;

  length = GST_READ_UINT32_BE (data);
1070
  GST_DEBUG ("length %08" G_GINT64_MODIFIER "x", length);
1071
1072
1073
1074
1075
  fourcc = GST_READ_UINT32_LE (data + 4);
  GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));

  if (length == 0) {
    length = G_MAXUINT32;
1076
  } else if (length == 1) {
1077
1078
    /* this means we have an extended size, which is the 64 bit value of
     * the next 8 bytes */
1079
1080
    length = GST_READ_UINT64_BE (data + 8);
    GST_DEBUG ("length %08llx", length);
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
  }

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

static GstFlowReturn
gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
1091
{
1092
  guint64 length;
1093
  guint32 fourcc;
1094
  GstBuffer *buf = NULL;
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
  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'):
1110
1111
1112
1113
1114
      GST_LOG ("skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
          GST_FOURCC_ARGS (fourcc), cur_offset);
      cur_offset += length;
      qtdemux->offset += length;
      break;
1115
1116
1117
1118
1119
1120
    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;
1121
1122
      if (length != GST_BUFFER_SIZE (moov)) {
        GST_WARNING_OBJECT (qtdemux,
1123
1124
            "We got less than expected (received %u, wanted %u)",
            GST_BUFFER_SIZE (moov), (guint) length);
1125
1126
1127
        ret = GST_FLOW_ERROR;
        goto beach;
      }
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
      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;
1143
    }
1144
    default:{
1145
      GST_LOG ("unknown %08x '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
          fourcc, GST_FOURCC_ARGS (fourcc), cur_offset);
      cur_offset += length;
      qtdemux->offset += length;
      break;
    }
  }

beach:
  return ret;
}

1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
/* activate the given segment number @seg_idx of @stream at time @offset.
 * @offset is an absolute global position over all the segments.
 *
 * This will push out a NEWSEGMENT event with the right values and
 * position the stream index to the first decodable sample before 
 * @offset.
 */
static gboolean
gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
    guint32 seg_idx, guint64 offset)
{
  GstEvent *event;
  QtDemuxSegment *segment;
  guint32 index, kf_index;
  guint64 seg_time;
  guint64 start, stop;
1173
  gdouble rate;
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190

  /* update the current segment */
  stream->segment_index = seg_idx;

  /* get the segment */
  segment = &stream->segments[seg_idx];

  if (offset < segment->time)
    return FALSE;

  /* get time in this segment */
  seg_time = offset - segment->time;

  if (seg_time >= segment->duration)
    return FALSE;

  /* calc media start/stop */
1191
1192
1193
1194
  if (qtdemux->segment.stop == -1)
    stop = segment->media_stop;
  else
    stop = MIN (segment->media_stop, qtdemux->segment.stop);
1195
  start = MIN (segment->media_start + seg_time, stop);
1196
1197
1198
1199
1200

  GST_DEBUG_OBJECT (qtdemux, "newsegment %d from %" GST_TIME_FORMAT
      " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
      GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (offset));

1201
1202
1203
  /* combine global rate with that of the segment */
  rate = segment->rate * qtdemux->segment.rate;
  event = gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME,
1204
1205
      start, stop, offset);

1206
1207
  if (stream->pad)
    gst_pad_push_event (stream->pad, event);
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251

  /* and move to the keyframe before the indicated media time of the
   * segment */
  index = gst_qtdemux_find_index (qtdemux, stream, start);

  GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
      ", index: %u", GST_TIME_ARGS (start), index);

  /* we're at the right spot */
  if (index == stream->sample_index)
    return TRUE;

  /* find keyframe of the target index */
  kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index);

  /* if we move forwards, we don't have to go back to the previous
   * keyframe since we already sent that. We can also just jump to
   * the keyframe right before the target index if there is one. */
  if (index > stream->sample_index) {
    /* moving forwards check if we move past a keyframe */
    if (kf_index > stream->sample_index) {
      GST_DEBUG_OBJECT (qtdemux, "moving forwards to keyframe at %u", kf_index);
      gst_qtdemux_move_stream (qtdemux, stream, kf_index);
    } else {
      GST_DEBUG_OBJECT (qtdemux, "moving forwards, keyframe at %u already sent",
          kf_index);
    }
  } else {
    GST_DEBUG_OBJECT (qtdemux, "moving backwards to keyframe at %u", kf_index);
    gst_qtdemux_move_stream (qtdemux, stream, kf_index);
  }

  return TRUE;
}

/* prepare to get the current sample of @stream, getting essential values.
 * 
 * This function will also prepare and send the segment when needed.
 *
 * Return FALSE if the stream is EOS.
 */
static gboolean
gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp,
1252
    guint64 * duration, gboolean * keyframe)
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
{
  QtDemuxSample *sample;
  guint64 time_position;
  guint32 seg_idx;

  g_return_val_if_fail (stream != NULL, FALSE);

  time_position = stream->time_position;
  if (time_position == -1)
    goto eos;

  seg_idx = stream->segment_index;
  if (seg_idx == -1) {
    /* find segment corresponding to time_position if we are looking
     * for a segment. */
    seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);

    /* nothing found, we're really eos */
    if (seg_idx == -1)
      goto eos;
  }

  /* different segment, activate it, sample_index will be set. */
  if (stream->segment_index != seg_idx)
    gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);

1279
1280
1281
  if (stream->sample_index >= stream->n_samples)
    goto eos;

1282
1283
1284
1285
1286
  /* now get the info for the sample we're at */
  sample = &stream->samples[stream->sample_index];

  *offset = sample->offset;
  *size = sample->size;
1287
1288
  *timestamp = sample->timestamp;
  *duration = sample->duration;
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
  *keyframe = stream->all_keyframe || sample->keyframe;

  return TRUE;

  /* special cases */
eos:
  {
    stream->time_position = -1;
    return FALSE;
  }
}

/* move to the next sample in @stream.
 *
 * Moves to the next segment when needed.
 */
static void
gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
{
  QtDemuxSample *sample;
  QtDemuxSegment *segment;

  /* move to next sample */
  stream->sample_index++;

  /* get current segment */
  segment = &stream->segments[stream->segment_index];

  /* reached the last sample, we need the next segment */
1318
  if (stream->sample_index >= stream->n_samples)
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
    goto next_segment;

  /* get next sample */
  sample = &stream->samples[stream->sample_index];

  /* see if we are past the segment */
  if (sample->timestamp >= segment->media_stop)
    goto next_segment;

  if (sample->timestamp >= segment->media_start) {
    /* inside the segment, update time_position, looks very familiar to
     * GStreamer segments, doesn't it? */