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

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

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

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

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
36 37 38
#define QTDEMUX_GUINT32_GET(a)  (GST_READ_UINT32_BE(a))
#define QTDEMUX_GUINT24_GET(a)  (GST_READ_UINT32_BE(a) >> 8)
#define QTDEMUX_GUINT16_GET(a)  (GST_READ_UINT16_BE(a))
39
#define QTDEMUX_GUINT8_GET(a) (*(guint8 *)(a))
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
40 41 42
#define QTDEMUX_FP32_GET(a)     ((GST_READ_UINT32_BE(a))/65536.0)
#define QTDEMUX_FP16_GET(a)     ((GST_READ_UINT16_BE(a))/256.0)
#define QTDEMUX_FOURCC_GET(a)   (GST_READ_UINT32_LE(a))
43 44

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

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

50 51
//typedef struct _QtDemuxStream QtDemuxStream;

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

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

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

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

  int sample_index;
88 89 90

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

95 96
  double rate;
  int n_channels;
97
  guint bytes_per_frame;
98
  guint compression;
99
  guint samples_per_packet;
100 101
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
102 103
enum QtDemuxState
{
104 105 106
  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 */
107 108
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
109 110
static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
111

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

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

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

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

138
static GstElementClass *parent_class = NULL;
Artyom Baginski's avatar
Artyom Baginski committed
139

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
140 141 142
static void gst_qtdemux_class_init (GstQTDemuxClass * klass);
static void gst_qtdemux_base_init (GstQTDemuxClass * klass);
static void gst_qtdemux_init (GstQTDemux * quicktime_demux);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
143 144
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
    GstStateChange transition);
145
static void gst_qtdemux_loop_header (GstPad * pad);
146
static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
147 148
static gboolean qtdemux_sink_activate (GstPad * sinkpad);
static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
149 150
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
151 152 153 154 155 156 157

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);
158
static void qtdemux_parse_udta (GstQTDemux * qtdemux, GNode * udta);
159
static void qtdemux_tag_add_str (GstQTDemux * qtdemux, const char *tag,
160
    GNode * node);
161 162 163 164 165
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);

166 167
static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
    QtDemuxStream * stream, GNode * esds);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
168
static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux, guint32 fourcc,
169
    const guint8 * stsd_data, const gchar ** codec_name);
170 171 172
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
173 174 175

static GType
gst_qtdemux_get_type (void)
Artyom Baginski's avatar
Artyom Baginski committed
176 177 178 179 180
{
  static GType qtdemux_type = 0;

  if (!qtdemux_type) {
    static const GTypeInfo qtdemux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
181 182 183 184 185
      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
186
    };
187

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
188
    qtdemux_type =
189 190
        g_type_register_static (GST_TYPE_ELEMENT, "GstQTDemux", &qtdemux_info,
        0);
Artyom Baginski's avatar
Artyom Baginski committed
191 192 193 194
  }
  return qtdemux_type;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
195 196
static void
gst_qtdemux_base_init (GstQTDemuxClass * klass)
197 198 199 200
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
201 202 203 204 205
      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));
206
  gst_element_class_set_details (element_class, &gst_qtdemux_details);
David Schleef's avatar
David Schleef committed
207

208 209
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
210 211
static void
gst_qtdemux_class_init (GstQTDemuxClass * klass)
Artyom Baginski's avatar
Artyom Baginski committed
212 213 214 215
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
216 217
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Artyom Baginski's avatar
Artyom Baginski committed
218

219
  parent_class = g_type_class_peek_parent (klass);
220

Artyom Baginski's avatar
Artyom Baginski committed
221 222 223
  gstelement_class->change_state = gst_qtdemux_change_state;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
224 225
static void
gst_qtdemux_init (GstQTDemux * qtdemux)
Artyom Baginski's avatar
Artyom Baginski committed
226
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
227 228 229
  qtdemux->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get
      (&gst_qtdemux_sink_template), "sink");
230 231 232
  gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
  gst_pad_set_activatepull_function (qtdemux->sinkpad,
      qtdemux_sink_activate_pull);
233 234 235 236
  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
237
  gst_element_add_pad (GST_ELEMENT (qtdemux), qtdemux->sinkpad);
238

239
  qtdemux->state = QTDEMUX_STATE_INITIAL;
240
  qtdemux->last_ts = GST_CLOCK_TIME_NONE;
241 242 243 244
  qtdemux->pullbased = FALSE;
  qtdemux->neededbytes = 16;
  qtdemux->todrop = 0;
  qtdemux->adapter = gst_adapter_new ();
Artyom Baginski's avatar
Artyom Baginski committed
245 246
}

247
#if 0
248
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
249 250
gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
251 252
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
253
  QtDemuxStream *stream = gst_pad_get_element_private (pad);
254

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
255
  if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e') &&
256 257 258 259 260 261
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
    return FALSE;

  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
262 263 264 265 266 267 268 269 270
        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;
271 272 273 274
      }
      break;
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
275 276 277 278 279 280
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
281 282 283 284
      }
      break;
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
285 286 287 288 289 290
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
291 292 293 294 295 296 297 298
      }
      break;
    default:
      res = FALSE;
  }

  return res;
}
299
#endif
300 301

static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
302
gst_qtdemux_get_src_query_types (GstPad * pad)
303 304 305
{
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
Wim Taymans's avatar
Wim Taymans committed
306
    GST_QUERY_DURATION,
307 308 309 310 311 312 313
    0
  };

  return src_types;
}

static gboolean
314
gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
315
{
316 317
  gboolean res = FALSE;
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
318

319
  switch (GST_QUERY_TYPE (query)) {
320
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
321 322 323 324 325 326 327
      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:
      if (qtdemux->duration != 0 && qtdemux->timescale != 0) {
328 329 330 331 332 333
        gint64 duration;

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

        gst_query_set_duration (query, GST_FORMAT_TIME, duration);
334
        res = TRUE;
335 336 337 338 339 340 341
      }
      break;
    default:
      res = FALSE;
      break;
  }

342 343
  gst_object_unref (qtdemux);

344 345 346
  return res;
}

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
/* sends event to all source pads; takes ownership of the event */
static void
gst_qtdemux_send_event (GstQTDemux * qtdemux, GstEvent * event)
{
  guint n;

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

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

363
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
364
gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event)
365 366
{
  gboolean res = TRUE;
367
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
368 369

  switch (GST_EVENT_TYPE (event)) {
370 371 372 373
    case GST_EVENT_SEEK:{
      GstFormat format;
      GstSeekFlags flags;
      gint64 desired_offset;
374

375
      /* FIXME do seeking correctly */
376 377 378 379 380 381
      gst_event_parse_seek (event, NULL, &format, &flags, NULL,
          &desired_offset, NULL, NULL);
      GST_DEBUG ("seek format %d", format);

      switch (format) {
        case GST_FORMAT_TIME:{
382 383
          gint i = 0, n;
          QtDemuxStream *stream = gst_pad_get_element_private (pad);
384 385 386

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

387 388 389 390 391
          if (!stream->n_samples) {
            res = FALSE;
            break;
          }

392
          /* unlock upstream pull_range */
393
          gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ());
394
          /* make sure out loop function exits */
395 396
          gst_qtdemux_send_event (qtdemux, gst_event_new_flush_start ());

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

400 401 402 403 404
          /* resync to new time */
          for (n = 0; n < qtdemux->n_streams; n++) {
            QtDemuxStream *str = qtdemux->streams[n];

            for (i = 0; i < str->n_samples; i++) {
405 406 407 408 409
              /* Seek to the sample just before the desired offset and
               * let downstream throw away bits outside of the segment */
              if (str->samples[i].timestamp > desired_offset) {
                if (i > 0)
                  --i;
410
                break;
411
              }
412 413 414
            }
            str->sample_index = i;
          }
415
          /* prepare for streaming again */
416 417 418 419 420 421
          gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_stop ());
          gst_qtdemux_send_event (qtdemux, gst_event_new_flush_stop ());

          gst_qtdemux_send_event (qtdemux,
              gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
                  desired_offset, GST_CLOCK_TIME_NONE, desired_offset));
422 423 424 425 426

          /* and restart */
          gst_pad_start_task (qtdemux->sinkpad,
              (GstTaskFunction) gst_qtdemux_loop_header, qtdemux->sinkpad);

427
          GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
428
          break;
429 430 431 432
        }
        default:
          res = FALSE;
          break;
433
      }
434 435
      break;
    }
436 437 438 439 440
    default:
      res = FALSE;
      break;
  }

441 442
  gst_object_unref (qtdemux);

443 444 445 446 447
  gst_event_unref (event);

  return res;
}

448 449
GST_DEBUG_CATEGORY (qtdemux_debug);

Artyom Baginski's avatar
Artyom Baginski committed
450
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
451
plugin_init (GstPlugin * plugin)
Artyom Baginski's avatar
Artyom Baginski committed
452
{
453
  GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
454

455
  return gst_element_register (plugin, "qtdemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
456
      GST_RANK_PRIMARY, GST_TYPE_QTDEMUX);
Artyom Baginski's avatar
Artyom Baginski committed
457 458
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
459 460 461 462
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "qtdemux",
    "Quicktime stream demuxer",
463
    plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
464

465
static gboolean
466
gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
Artyom Baginski's avatar
Artyom Baginski committed
467
{
468 469 470 471 472 473
  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
474
    default:
475
      gst_pad_event_default (demux->sinkpad, event);
476
      return TRUE;
Artyom Baginski's avatar
Artyom Baginski committed
477
  }
478

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
479
  gst_event_unref (event);
480
  return res;
Artyom Baginski's avatar
Artyom Baginski committed
481 482
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
483 484
static GstStateChangeReturn
gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
485
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
486
  GstQTDemux *qtdemux = GST_QTDEMUX (element);
487 488 489
  GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
491 492
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:{
493 494
      gint n;

495
      qtdemux->state = QTDEMUX_STATE_INITIAL;
496
      qtdemux->last_ts = GST_CLOCK_TIME_NONE;
497 498 499 500 501
      qtdemux->neededbytes = 16;
      qtdemux->todrop = 0;
      qtdemux->pullbased = FALSE;
      qtdemux->offset = 0;
      gst_adapter_clear (qtdemux->adapter);
502 503 504
      for (n = 0; n < qtdemux->n_streams; n++) {
        gst_element_remove_pad (element, qtdemux->streams[n]->pad);
        g_free (qtdemux->streams[n]->samples);
505 506
        if (qtdemux->streams[n]->caps)
          gst_caps_unref (qtdemux->streams[n]->caps);
507 508 509
        g_free (qtdemux->streams[n]);
      }
      qtdemux->n_streams = 0;
510
      break;
511
    }
512 513 514
    default:
      break;
  }
Artyom Baginski's avatar
Artyom Baginski committed
515

516
  return result;
517
}
Artyom Baginski's avatar
Artyom Baginski committed
518

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
519
static void
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
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)
556 557 558
{
  guint32 length;
  guint32 fourcc;
559
  GstBuffer *buf = NULL;
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
  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;
      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;
620
  guint64 offset;
621
  int size;
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 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 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
  int index = -1;
  int i;

  /* Figure out the next stream sample to output */
  min_time = G_MAXUINT64;
  for (i = 0; i < qtdemux->n_streams; i++) {
    stream = qtdemux->streams[i];
    GST_LOG_OBJECT (qtdemux,
        "stream %d: sample_index %d, timestamp %" GST_TIME_FORMAT, i,
        stream->sample_index,
        GST_TIME_ARGS (stream->samples[stream->sample_index].timestamp));
    if (stream->sample_index < stream->n_samples
        && stream->samples[stream->sample_index].timestamp < min_time) {
      min_time = stream->samples[stream->sample_index].timestamp;
      index = i;
    }
  }
  if (index == -1) {
    GST_DEBUG_OBJECT (qtdemux, "No samples left for any streams - EOS");
    gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ());
    goto beach;
  }

  stream = qtdemux->streams[index];

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

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

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

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

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

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

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

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

  stream->sample_index++;

beach:
  return ret;
}

static void
gst_qtdemux_loop_header (GstPad * pad)
{
  GstQTDemux *qtdemux = GST_QTDEMUX (GST_OBJECT_PARENT (pad));
  guint64 cur_offset;
  GstFlowReturn ret = GST_FLOW_ERROR;
722

723
  cur_offset = qtdemux->offset;
724
  GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
725
      cur_offset, qtdemux->state);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
726 727

  switch (qtdemux->state) {
728 729 730 731 732 733 734 735 736 737 738
    case QTDEMUX_STATE_INITIAL:
    case QTDEMUX_STATE_HEADER:
      ret = gst_qtdemux_loop_state_header (qtdemux);
      break;
    case QTDEMUX_STATE_MOVIE:
      ret = gst_qtdemux_loop_state_movie (qtdemux);
      break;
    default:
      /* oh crap */
      g_error ("State=%d", qtdemux->state);
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
739

740 741 742 743 744 745 746 747 748 749 750
  if (ret != GST_FLOW_OK) {
    GST_LOG_OBJECT (qtdemux, "pausing task, reason %s",
        gst_flow_get_name (ret));
    gst_pad_pause_task (qtdemux->sinkpad);
    if (GST_FLOW_IS_FATAL (ret)) {
      gst_pad_event_default (qtdemux->sinkpad, gst_event_new_eos ());
      GST_ELEMENT_ERROR (qtdemux, STREAM, FAILED,
          (NULL), ("stream stopped, reason %s", gst_flow_get_name (ret)));
    }
  }
}
751

752 753 754 755 756 757
/*
  next_entry_size
  
  Returns the size of the first entry at the current offset.
  If -1, there are none (which means EOS or empty file).
*/
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
758

759 760 761 762 763
static guint64
next_entry_size (GstQTDemux * demux)
{
  QtDemuxStream *stream;
  int i;
764
  int smallidx = -1;
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
  guint64 smalloffs = -1;

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

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

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

    if ((smalloffs == -1)
        || (stream->samples[stream->sample_index].offset < smalloffs)) {
      smallidx = i;
      smalloffs = stream->samples[stream->sample_index].offset;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
782
    }
783 784 785 786 787
  }

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

788 789
  if (smallidx == -1)
    return -1;
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
  stream = demux->streams[smallidx];

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

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

static GstFlowReturn
gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf)
{
  GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
  GstFlowReturn ret = GST_FLOW_OK;

  gst_adapter_push (demux->adapter, inbuf);

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

  while (((gst_adapter_available (demux->adapter)) >= demux->neededbytes) &&
      ret == GST_FLOW_OK) {

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

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

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

        /* get fourcc/length, set neededbytes */
        extract_initial_length_and_fourcc ((guint8 *) data, &size, &fourcc);
831 832 833 834 835 836 837 838 839 840
        GST_DEBUG_OBJECT (demux,
            "Peeking found [%" GST_FOURCC_FORMAT "] size:%ld",
            GST_FOURCC_ARGS (fourcc), size);
        if ((fourcc == GST_MAKE_FOURCC ('m', 'd', 'a', 't'))) {
          if (demux->n_streams <= 0) {
            GST_ELEMENT_ERROR (demux, STREAM, FAILED,
                (NULL),
                ("Can't handled files with header after data in push-mode!"));
            ret = GST_FLOW_ERROR;
          }
841 842 843 844 845 846 847
          demux->state = QTDEMUX_STATE_MOVIE;
          demux->offset += 24;
          gst_adapter_flush (demux->adapter, 24);
          demux->neededbytes = next_entry_size (demux);
        } else {
          demux->neededbytes = size;
          demux->state = QTDEMUX_STATE_HEADER;
848
        }
849
      }
850
        break;
851

852 853 854
      case QTDEMUX_STATE_HEADER:{
        guint8 *data;
        guint32 fourcc;
855

856
        GST_DEBUG_OBJECT (demux, "In header");
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 883 884
        data = gst_adapter_take (demux->adapter, demux->neededbytes);

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

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

          g_node_destroy (demux->moov_node);
          g_free (data);
          demux->moov_node = NULL;

          demux->neededbytes = 16;
          demux->state = QTDEMUX_STATE_INITIAL;
        } else {
          GST_WARNING_OBJECT (demux,
              "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
              GST_FOURCC_ARGS (fourcc));
          /* Let's jump that one and go back to initial state */
          demux->offset += demux->neededbytes;
          demux->neededbytes = 16;
          demux->state = QTDEMUX_STATE_INITIAL;
        }
885
      }
886 887 888 889 890 891 892 893 894 895
        break;

      case QTDEMUX_STATE_MOVIE:{
        guint8 *data;
        GstBuffer *outbuf;
        QtDemuxStream *stream = NULL;
        int i = -1;

        GST_DEBUG_OBJECT (demux, "BEGIN // in MOVIE for offset %lld",
            demux->offset);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
896

897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
        if (demux->todrop) {
          gst_adapter_flush (demux->adapter, demux->todrop);
          demux->neededbytes -= demux->todrop;
          demux->offset += demux->todrop;
        }

        /* Figure out which stream this is packet belongs to */
        for (i = 0; i < demux->n_streams; i++) {
          stream = demux->streams[i];
          GST_LOG_OBJECT (demux,
              "Checking stream %d (sample_index:%d / offset:%lld / size:%d / chunk:%d)",
              i, stream->sample_index,
              stream->samples[stream->sample_index].offset,
              stream->samples[stream->sample_index].size,
              stream->samples[stream->sample_index].chunk);
          if (stream->samples[stream->sample_index].offset == demux->offset)
            break;
        }

        if (stream == NULL) {
          GST_WARNING_OBJECT (demux, "WHAT THE FUCK ?");
          ret = GST_FLOW_ERROR;
          break;
920 921
        }

922
        /* first buffer? */
923 924 925
        /* FIXME : this should be handled in sink_event */
        if (demux->last_ts == GST_CLOCK_TIME_NONE) {
          gst_qtdemux_send_event (demux,
926 927 928 929
              gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
                  0, GST_CLOCK_TIME_NONE, 0));
        }

930 931 932 933 934 935 936 937 938
        /* get data */
        data = gst_adapter_take (demux->adapter, demux->neededbytes);

        /* Put data in a buffer, set timestamps, caps, ... */
        outbuf = gst_buffer_new ();
        gst_buffer_set_data (outbuf, data, demux->neededbytes);
        GST_DEBUG_OBJECT (demux, "stream : %" GST_FOURCC_FORMAT,
            GST_FOURCC_ARGS (stream->fourcc));

939
        if (stream->fourcc != GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
940
          GST_BUFFER_TIMESTAMP (outbuf) =
941
              stream->samples[stream->sample_index].timestamp;
942 943
          demux->last_ts = GST_BUFFER_TIMESTAMP (outbuf);
          GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int
944 945
              (stream->samples[stream->sample_index].duration, GST_SECOND,
              stream->timescale);
946

947
        } else {
948 949
          if (stream->sample_index == 0)
            GST_BUFFER_TIMESTAMP (outbuf) = 0;
950
        }
951

952 953
        /* send buffer */
        GST_LOG_OBJECT (demux,
954
            "Pushing buffer with time %" GST_TIME_FORMAT " on pad %p",
955 956 957 958 959 960 961 962 963 964 965 966 967 968
            GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), stream->pad);
        gst_buffer_set_caps (outbuf, stream->caps);
        ret = gst_pad_push (stream->pad, outbuf);

        stream->sample_index++;

        /* update current offset and figure out size of next buffer */
        GST_LOG_OBJECT (demux, "bumping offset:%lld up by %lld",
            demux->offset, demux->neededbytes);
        demux->offset += demux->neededbytes;
        GST_LOG_OBJECT (demux, "offset is now %lld", demux->offset);

        if ((demux->neededbytes = next_entry_size (demux)) == -1)
          ret = GST_FLOW_ERROR;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
969
      }
970 971 972 973
        break;
      default:
        g_warning ("This line should never be reached\n");
        ret = GST_FLOW_ERROR;
Artyom Baginski's avatar
Artyom Baginski committed
974
    }
975
  }
976 977


978
  return ret;
979 980 981 982 983 984 985
}

static gboolean
qtdemux_sink_activate (GstPad * sinkpad)
{
  if (gst_pad_check_pull_range (sinkpad))
    return gst_pad_activate_pull (sinkpad, TRUE);
986 987
  else
    return gst_pad_activate_push (sinkpad, TRUE);
988 989 990 991 992
}

static gboolean
qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
{
993 994
  GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));

995 996
  if (active) {
    /* if we have a scheduler we can start the task */
997
    demux->pullbased = TRUE;
998 999 1000 1001 1002 1003 1004
    gst_pad_start_task (sinkpad,
        (GstTaskFunction) gst_qtdemux_loop_header, sinkpad);
  } else {
    gst_pad_stop_task (sinkpad);
  }

  return TRUE;
Artyom Baginski's avatar
Artyom Baginski committed
1005 1006
}

1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
static gboolean
qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active)
{
  GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));

  demux->pullbased = FALSE;

  return TRUE;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1017
void
1018 1019
gst_qtdemux_add_stream (GstQTDemux * qtdemux,
    QtDemuxStream * stream, GstTagList * list)
Artyom Baginski's avatar
Artyom Baginski committed
1020
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1021
  if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e')) {
1022
    GstPadTemplate *templ;
1023
    gchar *name = g_strdup_printf ("video_%02d", qtdemux->n_video_streams);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1024

1025 1026 1027
    templ = gst_static_pad_template_get (&gst_qtdemux_videosrc_template);
    stream->pad = gst_pad_new_from_template (templ, name);
    gst_object_unref (templ);
1028
    g_free (name);
1029
    if ((stream->n_samples == 1) && (stream->samples[0].duration == 0)) {
1030 1031 1032 1033
      stream->fps_n = 0;
      stream->fps_d = 1;
    } else {
      stream->fps_n = stream->timescale;
1034 1035 1036 1037
      if (stream->samples[0].duration == 0)
        stream->fps_d = 1;
      else
        stream->fps_d = stream->samples[0].duration;
1038
    }
1039

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1040 1041
    if (stream->caps) {
      gst_caps_set_simple (stream->caps,
1042 1043
          "width", G_TYPE_INT, stream->width,
          "height", G_TYPE_INT, stream->height,
1044
          "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL);
1045 1046
    }
    qtdemux->n_video_streams++;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1047
  } else {
1048
    gchar *name = g_strdup_printf ("audio_%02d", qtdemux->n_audio_streams);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1049 1050

    stream->pad =
1051 1052
        gst_pad_new_from_template (gst_static_pad_template_get
        (&gst_qtdemux_audiosrc_template), name);
1053
    g_free (name);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1054 1055
    if (stream->caps) {
      gst_caps_set_simple (stream->caps,
1056 1057
          "rate", G_TYPE_INT, (int) stream->rate,
          "channels", G_TYPE_INT, stream->n_channels, NULL);
Artyom Baginski's avatar
Artyom Baginski committed
1058
    }
1059 1060
    qtdemux->n_audio_streams++;
  }
1061

1062
  gst_pad_use_fixed_caps (stream->pad);
1063

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1064
  GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
1065 1066
  qtdemux->streams[qtdemux->n_streams] = stream;
  qtdemux->n_streams++;
1067
  GST_DEBUG ("n_streams is now %d", qtdemux->n_streams);
1068

1069
  gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1070 1071
  gst_pad_set_query_type_function (stream->pad,
      gst_qtdemux_get_src_query_types);
1072 1073
  gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);

1074
  GST_DEBUG ("setting caps %" GST_PTR_FORMAT, stream->caps);
1075
  gst_pad_set_caps (stream->pad, stream->caps);
1076

1077
  GST_DEBUG ("adding pad %s %p to qtdemux %p",
1078
      GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1079
  gst_element_add_pad (GST_ELEMENT (qtdemux), stream->pad);
1080
  if (list) {
1081
    gst_element_found_tags_for_pad (GST_ELEMENT (qtdemux), stream->pad, list);
1082
  }
Artyom Baginski's avatar
Artyom Baginski committed
1083 1084
}

1085 1086 1087

#define QT_CONTAINER 1

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
#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_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_gnre     GST_MAKE_FOURCC('g','n','r','e')
#define FOURCC_disc     GST_MAKE_FOURCC('d','i','s','c')
#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')
1158

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174
static void qtdemux_dump_mvhd (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_tkhd (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_elst (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_mdhd (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_hdlr (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_vmhd (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_dref (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_stsd (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_stts (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_stss (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_stsc (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_stsz (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_stco (GstQTDemux * qtdemux, void *buffer, int