qtdemux.c 101 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 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
static guint64
next_entry_size (GstQTDemux * demux)
{
  QtDemuxStream *stream;
  int i;
  int smallidx = 0;
  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 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
  }

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

  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);
        if (fourcc == GST_MAKE_FOURCC ('m', 'd', 'a', 't')) {
          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;
837
        }
838
      }
839
        break;
840

841 842 843
      case QTDEMUX_STATE_HEADER:{
        guint8 *data;
        guint32 fourcc;
844

845
        GST_DEBUG_OBJECT (demux, "In header");
846

847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
        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;
        }
874
      }
875 876 877 878 879 880 881 882 883 884
        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
885

886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
        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;
909 910
        }

911
        /* first buffer? */
912 913 914
        /* FIXME : this should be handled in sink_event */
        if (demux->last_ts == GST_CLOCK_TIME_NONE) {
          gst_qtdemux_send_event (demux,
915 916 917 918
              gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
                  0, GST_CLOCK_TIME_NONE, 0));
        }

919 920 921 922 923 924 925 926 927
        /* 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));

928
        if (stream->fourcc != GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
929
          GST_BUFFER_TIMESTAMP (outbuf) =
930
              stream->samples[stream->sample_index].timestamp;
931 932
          demux->last_ts = GST_BUFFER_TIMESTAMP (outbuf);
          GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int
933 934
              (stream->samples[stream->sample_index].duration, GST_SECOND,
              stream->timescale);
935

936
        } else {
937 938
          if (stream->sample_index == 0)
            GST_BUFFER_TIMESTAMP (outbuf) = 0;
939
        }
940

941 942
        /* send buffer */
        GST_LOG_OBJECT (demux,
943
            "Pushing buffer with time %" GST_TIME_FORMAT " on pad %p",
944 945 946 947 948 949 950 951 952 953 954 955 956 957
            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
958
      }
959 960 961 962
        break;
      default:
        g_warning ("This line should never be reached\n");
        ret = GST_FLOW_ERROR;
Artyom Baginski's avatar
Artyom Baginski committed
963
    }
964
  }
965 966


967
  return ret;
968 969 970 971 972 973 974
}

static gboolean
qtdemux_sink_activate (GstPad * sinkpad)
{
  if (gst_pad_check_pull_range (sinkpad))
    return gst_pad_activate_pull (sinkpad, TRUE);
975 976
  else
    return gst_pad_activate_push (sinkpad, TRUE);
977 978 979 980 981
}

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

984 985
  if (active) {
    /* if we have a scheduler we can start the task */
986
    demux->pullbased = TRUE;
987 988 989 990 991 992 993
    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
994 995
}

996 997 998 999 1000 1001 1002 1003 1004 1005
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
1006
void
1007 1008
gst_qtdemux_add_stream (GstQTDemux * qtdemux,
    QtDemuxStream * stream, GstTagList * list)
Artyom Baginski's avatar
Artyom Baginski committed
1009
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1010
  if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e')) {
1011
    GstPadTemplate *templ;
1012
    gchar *name = g_strdup_printf ("video_%02d", qtdemux->n_video_streams);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1013

1014 1015 1016
    templ = gst_static_pad_template_get (&gst_qtdemux_videosrc_template);
    stream->pad = gst_pad_new_from_template (templ, name);
    gst_object_unref (templ);
1017
    g_free (name);
1018
    if ((stream->n_samples == 1) && (stream->samples[0].duration == 0)) {
1019 1020 1021 1022
      stream->fps_n = 0;
      stream->fps_d = 1;
    } else {
      stream->fps_n = stream->timescale;
1023 1024 1025 1026
      if (stream->samples[0].duration == 0)
        stream->fps_d = 1;
      else
        stream->fps_d = stream->samples[0].duration;
1027
    }
1028

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1029 1030
    if (stream->caps) {
      gst_caps_set_simple (stream->caps,
1031 1032
          "width", G_TYPE_INT, stream->width,
          "height", G_TYPE_INT, stream->height,
1033
          "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL);
1034 1035
    }
    qtdemux->n_video_streams++;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1036
  } else {
1037
    gchar *name = g_strdup_printf ("audio_%02d", qtdemux->n_audio_streams);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1038 1039

    stream->pad =
1040 1041
        gst_pad_new_from_template (gst_static_pad_template_get
        (&gst_qtdemux_audiosrc_template), name);
1042
    g_free (name);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1043 1044
    if (stream->caps) {
      gst_caps_set_simple (stream->caps,
1045 1046
          "rate", G_TYPE_INT, (int) stream->rate,
          "channels", G_TYPE_INT, stream->n_channels, NULL);
Artyom Baginski's avatar
Artyom Baginski committed
1047
    }
1048 1049
    qtdemux->n_audio_streams++;
  }
1050

1051
  gst_pad_use_fixed_caps (stream->pad);
1052

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1053
  GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
1054 1055
  qtdemux->streams[qtdemux->n_streams] = stream;
  qtdemux->n_streams++;
1056
  GST_DEBUG ("n_streams is now %d", qtdemux->n_streams);
1057

1058
  gst_pad_set_event_function (stream->pad, gst_qtdemux_handle_src_event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1059 1060
  gst_pad_set_query_type_function (stream->pad,
      gst_qtdemux_get_src_query_types);
1061 1062
  gst_pad_set_query_function (stream->pad, gst_qtdemux_handle_src_query);

1063
  GST_DEBUG ("setting caps %" GST_PTR_FORMAT, stream->caps);
1064
  gst_pad_set_caps (stream->pad, stream->caps);
1065

1066
  GST_DEBUG ("adding pad %s %p to qtdemux %p",
1067
      GST_OBJECT_NAME (stream->pad), stream->pad, qtdemux);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1068
  gst_element_add_pad (GST_ELEMENT (qtdemux), stream->pad);
1069
  if (list) {
1070
    gst_element_found_tags_for_pad (GST_ELEMENT (qtdemux), stream->pad, list);
1071
  }
Artyom Baginski's avatar
Artyom Baginski committed
1072 1073
}

1074 1075 1076

#define QT_CONTAINER 1

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 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
#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')
1147

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
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 depth);
static void qtdemux_dump_co64 (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_dcom (GstQTDemux * qtdemux, void *buffer, int depth);
static void qtdemux_dump_cmvd (GstQTDemux * qtdemux, void *buffer, int depth);
1164 1165
static void qtdemux_dump_unknown (GstQTDemux * qtdemux, void *buffer,
    int depth);
1166 1167

QtNodeType qt_node_types[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1168 1169 1170 1171 1172
  {FOURCC_moov, "movie", QT_CONTAINER,},
  {FOURCC_mvhd, "movie header", 0,
      qtdemux_dump_mvhd},
  {FOURCC_clip, "clipping", QT_CONTAINER,},
  {FOURCC_trak, "track", QT_CONTAINER,},
1173
  {FOURCC_udta, "user data", QT_CONTAINER,},    /* special container */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1174 1175