qtdemux.c 105 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
  QTDEMUX_STATE_BUFFER_MDAT     /* Buffering the mdat atom */
108 109
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

209 210
}

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

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

220
  parent_class = g_type_class_peek_parent (klass);
221

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

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

240
  qtdemux->state = QTDEMUX_STATE_INITIAL;
241
  qtdemux->last_ts = GST_CLOCK_TIME_NONE;
242 243 244 245
  qtdemux->pullbased = FALSE;
  qtdemux->neededbytes = 16;
  qtdemux->todrop = 0;
  qtdemux->adapter = gst_adapter_new ();
246 247 248
  qtdemux->offset = 0;
  qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
  qtdemux->mdatbuffer = NULL;
Artyom Baginski's avatar
Artyom Baginski committed
249 250
}

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

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

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

  return res;
}
303
#endif
304 305

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

  return src_types;
}

static gboolean
318
gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
319
{
320 321
  gboolean res = FALSE;
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
322

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

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

        gst_query_set_duration (query, GST_FORMAT_TIME, duration);
339
        res = TRUE;
340 341 342 343 344 345 346
      }
      break;
    default:
      res = FALSE;
      break;
  }

347 348
  gst_object_unref (qtdemux);

349 350 351
  return res;
}

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
/* 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);
}

368
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
369
gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event)
370 371
{
  gboolean res = TRUE;
372
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
373 374

  switch (GST_EVENT_TYPE (event)) {
375 376 377 378
    case GST_EVENT_SEEK:{
      GstFormat format;
      GstSeekFlags flags;
      gint64 desired_offset;
379

380
      /* FIXME do seeking correctly */
381 382 383 384 385 386
      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:{
387 388
          gint i = 0, n;
          QtDemuxStream *stream = gst_pad_get_element_private (pad);
389 390 391

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

392 393 394 395 396
          if (!stream->n_samples) {
            res = FALSE;
            break;
          }

397
          /* unlock upstream pull_range */
398
          gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ());
399
          /* make sure out loop function exits */
400 401
          gst_qtdemux_send_event (qtdemux, gst_event_new_flush_start ());

402 403 404
          /* wait for streaming to finish */
          GST_PAD_STREAM_LOCK (qtdemux->sinkpad);

405 406 407 408 409
          /* 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++) {
410 411 412 413 414
              /* 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;
415
                break;
416
              }
417 418 419
            }
            str->sample_index = i;
          }
420
          /* prepare for streaming again */
421 422 423 424 425 426
          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));
427 428 429

          /* and restart */
          gst_pad_start_task (qtdemux->sinkpad,
430
              (GstTaskFunction) gst_qtdemux_loop, qtdemux->sinkpad);
431

432
          GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);
433
          break;
434 435 436 437
        }
        default:
          res = FALSE;
          break;
438
      }
439 440
      break;
    }
441 442 443 444 445
    default:
      res = FALSE;
      break;
  }

446 447
  gst_object_unref (qtdemux);

448 449 450 451 452
  gst_event_unref (event);

  return res;
}

453 454
GST_DEBUG_CATEGORY (qtdemux_debug);

Artyom Baginski's avatar
Artyom Baginski committed
455
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
456
plugin_init (GstPlugin * plugin)
Artyom Baginski's avatar
Artyom Baginski committed
457
{
458
  GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
459

460
  return gst_element_register (plugin, "qtdemux",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
461
      GST_RANK_PRIMARY, GST_TYPE_QTDEMUX);
Artyom Baginski's avatar
Artyom Baginski committed
462 463
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
464 465 466 467
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "qtdemux",
    "Quicktime stream demuxer",
468
    plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
469

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
484
  gst_event_unref (event);
485
  return res;
Artyom Baginski's avatar
Artyom Baginski committed
486 487
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
488 489
static GstStateChangeReturn
gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
490
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
491
  GstQTDemux *qtdemux = GST_QTDEMUX (element);
492 493 494
  GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
496 497
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:{
498 499
      gint n;

500
      qtdemux->state = QTDEMUX_STATE_INITIAL;
501
      qtdemux->last_ts = GST_CLOCK_TIME_NONE;
502 503 504 505
      qtdemux->neededbytes = 16;
      qtdemux->todrop = 0;
      qtdemux->pullbased = FALSE;
      qtdemux->offset = 0;
506 507 508 509
      qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
      if (qtdemux->mdatbuffer)
        gst_buffer_unref (qtdemux->mdatbuffer);
      qtdemux->mdatbuffer = NULL;
510
      gst_adapter_clear (qtdemux->adapter);
511 512 513
      for (n = 0; n < qtdemux->n_streams; n++) {
        gst_element_remove_pad (element, qtdemux->streams[n]->pad);
        g_free (qtdemux->streams[n]->samples);
514 515
        if (qtdemux->streams[n]->caps)
          gst_caps_unref (qtdemux->streams[n]->caps);
516 517 518
        g_free (qtdemux->streams[n]);
      }
      qtdemux->n_streams = 0;
519
      break;
520
    }
521 522 523
    default:
      break;
  }
Artyom Baginski's avatar
Artyom Baginski committed
524

525
  return result;
526
}
Artyom Baginski's avatar
Artyom Baginski committed
527

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
528
static void
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 556 557 558 559 560 561 562 563 564
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)
565 566 567
{
  guint32 length;
  guint32 fourcc;
568
  GstBuffer *buf = NULL;
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
  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;
592 593 594 595 596 597 598
      if (length != GST_BUFFER_SIZE (moov)) {
        GST_WARNING_OBJECT (qtdemux,
            "We got less than expected (received %d, wanted %d)",
            GST_BUFFER_SIZE (moov), length);
        ret = GST_FLOW_ERROR;
        goto beach;
      }
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
      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;
636
  guint64 offset;
637
  int size;
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 722 723 724 725 726 727 728 729 730 731 732
  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
733
gst_qtdemux_loop (GstPad * pad)
734 735 736 737
{
  GstQTDemux *qtdemux = GST_QTDEMUX (GST_OBJECT_PARENT (pad));
  guint64 cur_offset;
  GstFlowReturn ret = GST_FLOW_ERROR;
738

739
  cur_offset = qtdemux->offset;
740
  GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
741
      cur_offset, qtdemux->state);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
742 743

  switch (qtdemux->state) {
744 745 746 747 748 749 750 751 752 753 754
    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
755

756
  if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_NOT_LINKED)) {
757 758 759 760 761 762 763 764 765 766
    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)));
    }
  }
}
767

768 769 770 771 772 773
/*
  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
774

775 776 777 778 779
static guint64
next_entry_size (GstQTDemux * demux)
{
  QtDemuxStream *stream;
  int i;
780
  int smallidx = -1;
781 782 783 784 785 786 787 788 789 790 791 792 793
  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);

794 795 796
    if (((smalloffs == -1)
            || (stream->samples[stream->sample_index].offset < smalloffs))
        && (stream->samples[stream->sample_index].size)) {
797 798
      smallidx = i;
      smalloffs = stream->samples[stream->sample_index].offset;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
799
    }
800 801 802 803 804
  }

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

805 806
  if (smallidx == -1)
    return -1;
807 808 809 810 811 812 813 814 815 816 817 818 819
  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;
}

820 821 822 823 824 825 826 827 828 829 830 831
static void
gst_qtdemux_post_buffering (GstQTDemux * demux, gint num, gint denom)
{
  gint perc = (gint) ((gdouble) num * 100.0 / (gdouble) denom);

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

832 833 834 835 836 837 838 839 840 841 842 843
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) &&
844
      (ret == GST_FLOW_OK)) {
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859

    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);
860 861 862 863
        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'))) {
864 865 866 867 868 869 870
          if (demux->n_streams > 0) {
            demux->state = QTDEMUX_STATE_MOVIE;
            demux->neededbytes = next_entry_size (demux);
          } else {
            demux->state = QTDEMUX_STATE_BUFFER_MDAT;
            demux->neededbytes = size;
            demux->mdatoffset = demux->offset;
871
          }
872 873 874
        } else {
          demux->neededbytes = size;
          demux->state = QTDEMUX_STATE_HEADER;
875
        }
876
      }
877
        break;
878

879 880 881
      case QTDEMUX_STATE_HEADER:{
        guint8 *data;
        guint32 fourcc;
882

883
        GST_DEBUG_OBJECT (demux, "In header");
884

885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
        data = gst_adapter_take (demux->adapter, demux->neededbytes);

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

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

          g_node_destroy (demux->moov_node);
          g_free (data);
          demux->moov_node = NULL;
        } else {
          GST_WARNING_OBJECT (demux,
              "Unknown fourcc while parsing header : %" GST_FOURCC_FORMAT,
              GST_FOURCC_ARGS (fourcc));
          /* Let's jump that one and go back to initial state */
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
        }

        GST_DEBUG_OBJECT (demux, "Finished parsing the header");
        if (demux->mdatbuffer && demux->n_streams) {
          /* the mdat was before the header */
          GST_DEBUG_OBJECT (demux, "We have n_streams:%d and mdatbuffer:%p",
              demux->n_streams, demux->mdatbuffer);
          gst_adapter_clear (demux->adapter);
          GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
              GST_FOURCC_ARGS (GST_READ_UINT32_BE (demux->mdatbuffer)));
          gst_adapter_push (demux->adapter, demux->mdatbuffer);
          demux->mdatbuffer = NULL;
          demux->offset = demux->mdatoffset;
          demux->neededbytes = next_entry_size (demux);
          demux->state = QTDEMUX_STATE_MOVIE;
        } else {
          GST_DEBUG_OBJECT (demux, "Carrying on normally");
921 922 923 924
          demux->offset += demux->neededbytes;
          demux->neededbytes = 16;
          demux->state = QTDEMUX_STATE_INITIAL;
        }
925
      }
926 927
        break;

928 929 930 931 932 933 934 935 936 937 938 939 940 941
      case QTDEMUX_STATE_BUFFER_MDAT:{
        GST_DEBUG_OBJECT (demux, "Got our buffer at offset %lld",
            demux->mdatoffset);
        if (demux->mdatbuffer)
          gst_buffer_unref (demux->mdatbuffer);
        demux->mdatbuffer = gst_buffer_new ();
        gst_buffer_set_data (demux->mdatbuffer,
            gst_adapter_take (demux->adapter, demux->neededbytes),
            demux->neededbytes);
        GST_DEBUG_OBJECT (demux, "mdatbuffer starts with %" GST_FOURCC_FORMAT,
            GST_FOURCC_ARGS (GST_READ_UINT32_BE (demux->mdatbuffer)));
        demux->offset += demux->neededbytes;
        demux->neededbytes = 16;
        demux->state = QTDEMUX_STATE_INITIAL;
942
        gst_qtdemux_post_buffering (demux, 1, 1);
943 944 945
      }
        break;

946 947 948 949 950 951 952 953
      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
954

955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
        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;
978 979
        }

980
        /* first buffer? */
981 982 983
        /* FIXME : this should be handled in sink_event */
        if (demux->last_ts == GST_CLOCK_TIME_NONE) {
          gst_qtdemux_send_event (demux,
984 985 986 987
              gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
                  0, GST_CLOCK_TIME_NONE, 0));
        }

988 989 990 991 992 993 994 995 996
        /* 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));

997
        if (stream->fourcc != GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
998
          GST_BUFFER_TIMESTAMP (outbuf) =
999
              stream->samples[stream->sample_index].timestamp;
1000 1001
          demux->last_ts = GST_BUFFER_TIMESTAMP (outbuf);
          GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int
1002 1003
              (stream->samples[stream->sample_index].duration, GST_SECOND,
              stream->timescale);
1004

1005
        } else {
1006 1007
          if (stream->sample_index == 0)
            GST_BUFFER_TIMESTAMP (outbuf) = 0;
1008
        }
1009

1010 1011
        /* send buffer */
        GST_LOG_OBJECT (demux,
1012
            "Pushing buffer with time %" GST_TIME_FORMAT " on pad %p",
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
            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
1027
      }
1028 1029 1030 1031
        break;
      default:
        g_warning ("This line should never be reached\n");
        ret = GST_FLOW_ERROR;
Artyom Baginski's avatar
Artyom Baginski committed
1032
    }
1033
  }
1034

1035 1036 1037 1038 1039 1040
  /* when buffering movie data, at least show user something is happening */
  if (ret == GST_FLOW_OK && demux->state == QTDEMUX_STATE_BUFFER_MDAT &&
      gst_adapter_available (demux->adapter) <= demux->neededbytes) {
    gst_qtdemux_post_buffering (demux, gst_adapter_available (demux->adapter),
        demux->neededbytes);
  }
1041

1042
  return ret;
1043 1044 1045 1046 1047 1048 1049
}

static gboolean
qtdemux_sink_activate (GstPad * sinkpad)
{
  if (gst_pad_check_pull_range (sinkpad))
    return gst_pad_activate_pull (sinkpad, TRUE);
1050 1051
  else
    return gst_pad_activate_push (sinkpad, TRUE);
1052 1053 1054 1055 1056
}

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

1059 1060
  if (active) {
    /* if we have a scheduler we can start the task */
1061
    demux->pullbased = TRUE;
1062
    gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop, sinkpad);
1063 1064 1065 1066 1067
  } else {
    gst_pad_stop_task (sinkpad);
  }

  return TRUE;
Artyom Baginski's avatar
Artyom Baginski committed
1068 1069
}

1070 1071 1072 1073 1074 1075 1076 1077 1078 1079
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
1080
void
1081 1082
gst_qtdemux_add_stream (GstQTDemux * qtdemux,
    QtDemuxStream * stream, GstTagList * list)
Artyom Baginski's avatar
Artyom Baginski committed
1083
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1084
  if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e')) {
1085
    GstPadTemplate *templ;
1086
    gchar *name = g_strdup_printf ("video_%02d", qtdemux->n_video_streams);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1087

1088 1089 1090
    templ = gst_static_pad_template_get (&gst_qtdemux_videosrc_template);
    stream->pad = gst_pad_new_from_template (templ, name);
    gst_object_unref (templ);
1091
    g_free (name);
1092
    if ((stream->n_samples == 1) && (stream->samples[0].duration == 0)) {
1093 1094 1095 1096
      stream->fps_n = 0;
      stream->fps_d = 1;
    } else {
      stream->fps_n = stream->timescale;
1097 1098 1099 1100
      if (stream->samples[0].duration == 0)
        stream->fps_d = 1;
      else
        stream->fps_d = stream->samples[0].duration;
1101
    }
1102

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1103 1104
    if (stream->caps) {
      gst_caps_set_simple (stream->caps,
1105 1106
          "width", G_TYPE_INT, stream->width,
          "height", G_TYPE_INT, stream->height,
1107
          "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL);
1108 1109
    }
    qtdemux->n_video_streams++;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1110
  } else {
1111
    gchar *name = g_strdup_printf ("audio_%02d", qtdemux->n_audio_streams);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1112 1113

    stream->pad =
1114 1115
        gst_pad_new_from_template (gst_static_pad_template_get
        (&gst_qtdemux_audiosrc_template), name);
1116
    g_free (name);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1117 1118
    if (stream->caps) {
      gst_caps_set_simple (stream->caps,
1119 1120
          "rate", G_TYPE_INT, (int) stream->rate,
          "channels", G_TYPE_INT, stream->n_channels, NULL);
Artyom Baginski's avatar
Artyom Baginski committed
1121
    }
1122 1123
    qtdemux->n_audio_streams++;
  }
1124

1125
  gst_pad_use_fixed_caps (stream->pad);
1126

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1127
  GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
1128 1129
  qt