qtdemux.c 120 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
#include "qtdemux.h"
26

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

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

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

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

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

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

51 52
//typedef struct _QtDemuxStream QtDemuxStream;

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

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68 69
struct _QtDemuxSample
{
70 71 72
  gint sample_index;
  gint chunk;
  gint size;
73
  guint64 offset;
74 75
  guint64 timestamp;            /* In GstClockTime */
  guint32 duration;             /* in stream->timescale units */
76
  gboolean keyframe;            /* TRUE when this packet is a keyframe */
77 78
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
79 80
struct _QtDemuxStream
{
81 82
  guint32 subtype;
  GstCaps *caps;
83
  guint32 fourcc;
84
  GstPad *pad;
85
  gint n_samples;
86
  QtDemuxSample *samples;
87
  gint timescale;
88
  gboolean all_keyframe;        /* TRUE when all packets are keyframes (no stss) */
89

90
  gint sample_index;
91

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

98 99
  gdouble rate;
  gint n_channels;
100
  guint bytes_per_frame;
101
  guint compression;
102
  guint samples_per_packet;
103 104
  guint16 bits_per_sample;
  guint16 color_table_id;
105 106 107

  /* when a discontinuity is pending */
  gboolean discont;
108 109
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
110 111
enum QtDemuxState
{
112 113 114
  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 */
115
  QTDEMUX_STATE_BUFFER_MDAT     /* Buffering the mdat atom */
116 117
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
118 119
static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
120

121 122 123 124 125
static GstElementDetails gst_qtdemux_details =
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
126

David Schleef's avatar
David Schleef committed
127
static GstStaticPadTemplate gst_qtdemux_sink_template =
128
    GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
129
    GST_PAD_SINK,
130
    GST_PAD_ALWAYS,
131
    GST_STATIC_CAPS ("video/quicktime; audio/x-m4a; application/x-3gp")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
132
    );
David Schleef's avatar
David Schleef committed
133 134

static GstStaticPadTemplate gst_qtdemux_videosrc_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
135 136 137 138
GST_STATIC_PAD_TEMPLATE ("audio_%02d",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
David Schleef's avatar
David Schleef committed
139 140

static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
141 142 143 144
GST_STATIC_PAD_TEMPLATE ("video_%02d",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
Artyom Baginski's avatar
Artyom Baginski committed
145

146
static GstElementClass *parent_class = NULL;
Artyom Baginski's avatar
Artyom Baginski committed
147

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
/* 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
236 237 238
static void gst_qtdemux_class_init (GstQTDemuxClass * klass);
static void gst_qtdemux_base_init (GstQTDemuxClass * klass);
static void gst_qtdemux_init (GstQTDemux * quicktime_demux);
239
static void gst_qtdemux_dispose (GObject * object);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
240 241
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
    GstStateChange transition);
242
static void gst_qtdemux_loop (GstPad * pad);
243
static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
244 245
static gboolean qtdemux_sink_activate (GstPad * sinkpad);
static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
246 247
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
248 249 250 251 252 253 254

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);
255
static void qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta);
256
static void qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag,
257
    GNode * node);
258 259 260 261 262
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);

263 264
static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
    QtDemuxStream * stream, GNode * esds);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
265
static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux, guint32 fourcc,
266
    const guint8 * stsd_data, const gchar ** codec_name);
267 268 269
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
270 271 272

static GType
gst_qtdemux_get_type (void)
Artyom Baginski's avatar
Artyom Baginski committed
273 274 275 276 277
{
  static GType qtdemux_type = 0;

  if (!qtdemux_type) {
    static const GTypeInfo qtdemux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
278 279 280 281 282
      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
283
    };
284

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
285
    qtdemux_type =
286 287
        g_type_register_static (GST_TYPE_ELEMENT, "GstQTDemux", &qtdemux_info,
        0);
Artyom Baginski's avatar
Artyom Baginski committed
288 289 290 291
  }
  return qtdemux_type;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
292 293
static void
gst_qtdemux_base_init (GstQTDemuxClass * klass)
294 295 296 297
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
298 299 300 301 302
      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));
303
  gst_element_class_set_details (element_class, &gst_qtdemux_details);
David Schleef's avatar
David Schleef committed
304

305 306
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
307 308
static void
gst_qtdemux_class_init (GstQTDemuxClass * klass)
Artyom Baginski's avatar
Artyom Baginski committed
309 310 311 312
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
313 314
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Artyom Baginski's avatar
Artyom Baginski committed
315

316
  parent_class = g_type_class_peek_parent (klass);
317

318 319
  gobject_class->dispose = gst_qtdemux_dispose;

Artyom Baginski's avatar
Artyom Baginski committed
320 321 322
  gstelement_class->change_state = gst_qtdemux_change_state;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
323 324
static void
gst_qtdemux_init (GstQTDemux * qtdemux)
Artyom Baginski's avatar
Artyom Baginski committed
325
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
326
  qtdemux->sinkpad =
327
      gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
328 329 330
  gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
  gst_pad_set_activatepull_function (qtdemux->sinkpad,
      qtdemux_sink_activate_pull);
331 332 333 334
  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
335
  gst_element_add_pad (GST_ELEMENT (qtdemux), qtdemux->sinkpad);
336

337
  qtdemux->state = QTDEMUX_STATE_INITIAL;
338
  qtdemux->last_ts = GST_CLOCK_TIME_NONE;
339 340 341 342
  qtdemux->pullbased = FALSE;
  qtdemux->neededbytes = 16;
  qtdemux->todrop = 0;
  qtdemux->adapter = gst_adapter_new ();
343 344 345
  qtdemux->offset = 0;
  qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
  qtdemux->mdatbuffer = NULL;
346
  gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
Artyom Baginski's avatar
Artyom Baginski committed
347 348
}

349 350 351 352 353 354 355 356 357
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;
  }
358 359

  G_OBJECT_CLASS (parent_class)->dispose (object);
360 361
}

362
#if 0
363
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
364 365
gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
366 367
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
368
  QtDemuxStream *stream = gst_pad_get_element_private (pad);
369

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
370
  if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e') &&
371 372 373 374 375 376
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
    return FALSE;

  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
377 378 379 380 381 382 383 384 385
        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;
386 387 388 389
      }
      break;
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
390 391 392 393 394 395
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
396 397 398 399
      }
      break;
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
400 401 402 403 404 405
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
406 407 408 409 410 411 412 413
      }
      break;
    default:
      res = FALSE;
  }

  return res;
}
414
#endif
415 416

static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
417
gst_qtdemux_get_src_query_types (GstPad * pad)
418 419 420
{
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
Wim Taymans's avatar
Wim Taymans committed
421
    GST_QUERY_DURATION,
422 423 424 425 426 427 428
    0
  };

  return src_types;
}

static gboolean
429
gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
430
{
431 432
  gboolean res = FALSE;
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
433

434
  switch (GST_QUERY_TYPE (query)) {
435
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
436 437 438 439 440 441
      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:
442 443
      if (qtdemux->pullbased && qtdemux->duration != 0
          && qtdemux->timescale != 0) {
444 445 446 447 448 449
        gint64 duration;

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

        gst_query_set_duration (query, GST_FORMAT_TIME, duration);
450
        res = TRUE;
451 452 453 454 455 456 457
      }
      break;
    default:
      res = FALSE;
      break;
  }

458 459
  gst_object_unref (qtdemux);

460 461 462
  return res;
}

463
/* push event on all source pads; takes ownership of the event */
464
static void
465
gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
466 467 468 469 470 471 472
{
  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++) {
473
    gst_pad_push_event (qtdemux->streams[n]->pad, gst_event_ref (event));
474 475 476 477
  }
  gst_event_unref (event);
}

478
/* move all streams back on the keyframe before @offset.
479
 * 
480 481 482 483
 * If @end is FALSE, the search is started from the current 
 * sample_index position of each stream.
 * If @end is TRUE, the search is started from the last sample
 * of each stream.
484
 *
485
 * Returns: the minimum of the timestamps of the positions of all streams.
486
 */
487
/* FIXME, binary search would be nice here */
488 489 490 491 492 493 494 495 496 497
static guint64
gst_qtdemux_go_back (GstQTDemux * qtdemux, gboolean end, guint64 offset)
{
  gint n;
  guint64 min_time = G_MAXUINT64;

  /* resync to new time */
  for (n = 0; n < qtdemux->n_streams; n++) {
    QtDemuxStream *str;
    gboolean keyframe;
498
    gint search;
499 500 501 502

    str = qtdemux->streams[n];
    keyframe = str->all_keyframe;

503 504 505 506 507 508 509 510
    /* start from the last sample if @end == TRUE */
    if (end) {
      if (str->n_samples == 0)
        search = 0;
      else
        search = str->n_samples - 1;
    } else
      search = str->sample_index;
511

512
    for (; search > 0; search--) {
513 514
      guint64 timestamp;

515
      timestamp = str->samples[search].timestamp;
516 517 518 519 520

      /* Seek to the sample just before the desired offset and
       * let downstream throw away bits outside of the segment */
      if (timestamp <= offset) {
        /* update the keyframe flag */
521
        keyframe = keyframe | str->samples[search].keyframe;
522 523
        if (keyframe) {
          GST_DEBUG_OBJECT (qtdemux,
524
              "found keyframe at sample %d, %" GST_TIME_FORMAT, search,
525
              GST_TIME_ARGS (timestamp));
526
          /* update min_time */
527 528 529 530 531 532 533
          if (timestamp < min_time)
            min_time = timestamp;
          break;
        }
      }
    }

534 535 536
    /* did not find anything or we're at the beginning, position to beginning */
    if (search <= 0) {
      search = 0;
537 538
      min_time = 0;
    }
539 540 541 542 543 544
    /* and set stream to the index */
    if (search != str->sample_index) {
      str->sample_index = search;
      /* position changed, we have a discont */
      str->discont = TRUE;
    }
545 546 547 548 549 550 551
  }
  return min_time;
}

/* perform the seek.
 *
 * We always go to the keyframe before the desired seek position. If
552 553 554 555
 * the seek was to a keyframe, we update the last_stop and time with 
 * the position of the keyframe, else we leve the event as-is and it 
 * will be clipped automatically to the right segment boundaries by 
 * downstream elements.
556
 */
557
static gboolean
558
gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
559
{
560 561
  gint64 desired_offset;
  guint64 min;
562

563
  desired_offset = segment->last_stop;
564

565 566
  GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (desired_offset));
567

568 569 570
  /* position all streams to key unit before the desired time,
   * start searching from the last sample in the stream. */
  min = gst_qtdemux_go_back (qtdemux, TRUE, desired_offset);
571

572 573 574 575 576 577
  if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
    GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %" GST_TIME_FORMAT,
        GST_TIME_ARGS (min));
    /* key unit, we seek to min, so back off streams to this new
     * position. We start from our current position. */
    gst_qtdemux_go_back (qtdemux, FALSE, min);
578

579
    /* update the segment values to the position of the keyframes */
580 581 582
    segment->last_stop = min;
    segment->time = min;
  }
583 584 585 586 587

  /* and we stop at the end */
  if (segment->stop == -1)
    segment->stop = segment->duration;

588 589
  return TRUE;
}
590

591
/* do a seek in pull based mode */
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
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;
  GstEvent *newsegment;

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

633
  flush = flags & GST_SEEK_FLAG_FLUSH;
634

635
  GST_DEBUG_OBJECT (qtdemux, "seek format %d", format);
636

637
  /* stop streaming, either by flushing or by pausing the task */
638 639 640 641 642 643
  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 {
644
    /* non flushing seek, pause the task */
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
    qtdemux->segment_running = FALSE;
    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);
  }

663
  /* now do the seek, this actually never returns FALSE */
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
  res = gst_qtdemux_perform_seek (qtdemux, &seeksegment);

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

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

684
  /* commit the new segment */
685 686 687 688 689 690 691 692
  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));
  }

693
  /* send the newsegment */
694 695 696
  GST_DEBUG_OBJECT (qtdemux, "Sending newsegment from %" GST_TIME_FORMAT
      " to %" GST_TIME_FORMAT, GST_TIME_ARGS (qtdemux->segment.start),
      GST_TIME_ARGS (qtdemux->segment.stop));
697 698 699 700 701 702 703 704

  newsegment =
      gst_event_new_new_segment (FALSE, qtdemux->segment.rate,
      qtdemux->segment.format, qtdemux->segment.last_stop,
      qtdemux->segment.stop, qtdemux->segment.time);

  gst_qtdemux_push_event (qtdemux, newsegment);

705
  /* restart streaming */
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
  qtdemux->segment_running = TRUE;
  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);
731
      break;
732 733 734 735 736
    default:
      res = FALSE;
      break;
  }

737 738
  gst_object_unref (qtdemux);

739 740 741 742 743
  gst_event_unref (event);

  return res;
}

744 745
GST_DEBUG_CATEGORY (qtdemux_debug);

Artyom Baginski's avatar
Artyom Baginski committed
746
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
747
plugin_init (GstPlugin * plugin)
Artyom Baginski's avatar
Artyom Baginski committed
748
{
749
  GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
750

751
  return gst_element_register (plugin, "qtdemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
752
      GST_RANK_PRIMARY, GST_TYPE_QTDEMUX);
Artyom Baginski's avatar
Artyom Baginski committed
753 754
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
755 756 757 758
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "qtdemux",
    "Quicktime stream demuxer",
759
    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
760

761
static gboolean
762
gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
Artyom Baginski's avatar
Artyom Baginski committed
763
{
764 765 766 767 768 769
  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
770
    default:
771
      gst_pad_event_default (demux->sinkpad, event);
772
      return TRUE;
Artyom Baginski's avatar
Artyom Baginski committed
773
  }
774

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
775
  gst_event_unref (event);
776
  return res;
Artyom Baginski's avatar
Artyom Baginski committed
777 778
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
779 780
static GstStateChangeReturn
gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
781
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
782
  GstQTDemux *qtdemux = GST_QTDEMUX (element);
783 784
  GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;

785 786 787 788 789 790 791
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      break;
    default:
      break;
  }

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
794 795
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:{
796 797
      gint n;

798
      qtdemux->state = QTDEMUX_STATE_INITIAL;
799
      qtdemux->last_ts = GST_CLOCK_TIME_NONE;
800 801 802 803
      qtdemux->neededbytes = 16;
      qtdemux->todrop = 0;
      qtdemux->pullbased = FALSE;
      qtdemux->offset = 0;
804 805 806 807
      qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
      if (qtdemux->mdatbuffer)
        gst_buffer_unref (qtdemux->mdatbuffer);
      qtdemux->mdatbuffer = NULL;
808
      gst_adapter_clear (qtdemux->adapter);
809 810 811
      for (n = 0; n < qtdemux->n_streams; n++) {
        gst_element_remove_pad (element, qtdemux->streams[n]->pad);
        g_free (qtdemux->streams[n]->samples);
812 813
        if (qtdemux->streams[n]->caps)
          gst_caps_unref (qtdemux->streams[n]->caps);
814 815 816
        g_free (qtdemux->streams[n]);
      }
      qtdemux->n_streams = 0;
817
      gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
818
      break;
819
    }
820 821 822
    default:
      break;
  }
Artyom Baginski's avatar
Artyom Baginski committed
823

824
  return result;
825
}
Artyom Baginski's avatar
Artyom Baginski committed
826

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
827
static void
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
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)
864 865 866
{
  guint32 length;
  guint32 fourcc;
867
  GstBuffer *buf = NULL;
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
  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;
891 892 893 894 895 896 897
      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;
      }
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
      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;
935
  guint64 offset;
936 937 938 939
  guint64 timestamp;
  gint size;
  gint index = -1;
  gint i;
940 941 942

  /* Figure out the next stream sample to output */
  min_time = G_MAXUINT64;
943

944 945
  for (i = 0; i < qtdemux->n_streams; i++) {
    stream = qtdemux->streams[i];
946
    if (stream->sample_index < stream->n_samples) {
947 948 949

      timestamp = stream->samples[stream->sample_index].timestamp;

950 951
      GST_LOG_OBJECT (qtdemux,
          "stream %d: sample_index %d, timestamp %" GST_TIME_FORMAT, i,
952
          stream->sample_index, GST_TIME_ARGS (timestamp));
953

954 955
      if (timestamp < min_time) {
        min_time = timestamp;
956 957
        index = i;
      }
958 959
    }
  }
960 961 962 963 964 965
  if (index == -1)
    goto eos;

  /* check for segment end */
  if (qtdemux->segment.stop != -1 && qtdemux->segment.stop < min_time)
    goto eos;
966 967 968 969 970

  stream = qtdemux->streams[index];

  offset = stream->samples[stream->sample_index].offset;
  size = stream->samples[stream->sample_index].size;
971
  timestamp = stream->samples[stream->sample_index].timestamp;
972 973 974 975

  GST_LOG_OBJECT (qtdemux,
      "pushing from stream %d, sample_index=%d offset=%" G_GUINT64_FORMAT
      ",size=%d timestamp=%" GST_TIME_FORMAT,
976
      index, stream->sample_index, offset, size, GST_TIME_ARGS (timestamp));
977

978 979
  if (G_UNLIKELY (size <= 0))
    goto beach;
980

981 982
  GST_LOG_OBJECT (qtdemux, "reading %d bytes @ %" G_GUINT64_FORMAT, size,
      offset);
983

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

988
  buf = gst_buffer_make_metadata_writable (buf);
989

990 991 992 993 994 995 996 997 998 999
#if 0
  /* 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);
1000
    }
1001 1002
  }
#endif
1003

1004 1005 1006 1007 1008 1009
  /* first buffer? */
  if (qtdemux->last_ts == GST_CLOCK_TIME_NONE) {
    gst_qtdemux_push_event (qtdemux,
        gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
            0, GST_CLOCK_TIME_NONE, 0));
  }
1010

1011 1012 1013 1014
  if (stream->discont) {
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
    stream->discont = FALSE;
  }
1015

1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
  /* timestamps of AMR aren't known... */
  if (stream->fourcc == GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
    if (stream->sample_index == 0)
      GST_BUFFER_TIMESTAMP (buf) = 0;
  } else {
    GST_BUFFER_TIMESTAMP (buf) = 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);
1026
  }
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
  gst_segment_set_last_stop (&qtdemux->segment, GST_FORMAT_TIME,
      qtdemux->last_ts);

  if (!(stream->all_keyframe || stream->samples[stream->sample_index].keyframe))
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);

  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);
1039 1040 1041 1042 1043

  stream->sample_index++;

beach:
  return ret;
1044 1045 1046 1047 1048 1049 1050 1051

  /* special cases */
eos:
  {
    GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
    ret = GST_FLOW_UNEXPECTED;
    goto beach;
  }
1052 1053 1054
}

static void
1055
gst_qtdemux_loop (GstPad * pad)
1056
{
1057
  GstQTDemux *qtdemux;
1058
  guint64 cur_offset;
1059 1060 1061
  GstFlowReturn ret;

  qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
1062

1063
  cur_offset = qtdemux->offset;
1064
  GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
1065
      cur_offset, qtdemux->state);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1066 1067

  switch (qtdemux->state) {
1068 1069 1070 1071 1072 1073 1074 1075
    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:
1076 1077
      /* ouch */
      goto invalid_state;
1078
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1079

1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
  /* if all is fine, continue */
  if (G_LIKELY (ret == GST_FLOW_OK))
    goto done;

  /* we don't care about unlinked pads */
  if (ret == GST_FLOW_NOT_LINKED)
    goto done;

  /* other errors make us stop */
  GST_LOG_OBJECT (qtdemux, "pausing task, reason %s", gst_flow_get_name (ret));

  qtdemux->segment_running = FALSE;
1092
  gst_pad_pause_task (pad);
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107

  /* fatal errors need special actions */
  if (GST_FLOW_IS_FATAL (ret)) {
    /* check EOS */
    if (ret == GST_FLOW_UNEXPECTED) {
      if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
        GST_LOG_OBJECT (qtdemux, "Sending segment done, at end of segment");
        gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
            gst_message_new_segment_done (GST_OBJECT_CAST (qtdemux),
                GST_FORMAT_TIME, qtdemux->last_ts));
      } else {
        GST_LOG_OBJECT (qtdemux, "Sending EOS at end of segment");
        gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());
      }
    } else {
1108
      gst_qtdemux_push_event (qtdemux, gst_event_new_eos ());