qtdemux.c 104 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 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
  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;
629
  guint64 offset;
630
  int size;
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 722 723 724 725
  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
726
gst_qtdemux_loop (GstPad * pad)
727 728 729 730
{
  GstQTDemux *qtdemux = GST_QTDEMUX (GST_OBJECT_PARENT (pad));
  guint64 cur_offset;
  GstFlowReturn ret = GST_FLOW_ERROR;
731

732
  cur_offset = qtdemux->offset;
733
  GST_LOG_OBJECT (qtdemux, "loop at position %" G_GUINT64_FORMAT ", state %d",
734
      cur_offset, qtdemux->state);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
735 736

  switch (qtdemux->state) {
737 738 739 740 741 742 743 744 745 746 747
    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
748

749
  if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_NOT_LINKED)) {
750 751 752 753 754 755 756 757 758 759
    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)));
    }
  }
}
760

761 762 763 764 765 766
/*
  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
767

768 769 770 771 772
static guint64
next_entry_size (GstQTDemux * demux)
{
  QtDemuxStream *stream;
  int i;
773
  int smallidx = -1;
774 775 776 777 778 779 780 781 782 783 784 785 786
  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);

787 788 789
    if (((smalloffs == -1)
            || (stream->samples[stream->sample_index].offset < smalloffs))
        && (stream->samples[stream->sample_index].size)) {
790 791
      smallidx = i;
      smalloffs = stream->samples[stream->sample_index].offset;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
792
    }
793 794 795 796 797
  }

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

798 799
  if (smallidx == -1)
    return -1;
800 801 802 803 804 805 806 807 808 809 810 811 812
  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;
}

813 814 815 816 817 818 819 820 821 822 823 824
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)));
}

825 826 827 828 829 830 831 832 833 834 835 836
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) &&
837
      (ret == GST_FLOW_OK)) {
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852

    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);
853 854 855 856
        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'))) {
857 858 859 860 861 862 863
          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;
864
          }
865 866 867
        } else {
          demux->neededbytes = size;
          demux->state = QTDEMUX_STATE_HEADER;
868
        }
869
      }
870
        break;
871

872 873 874
      case QTDEMUX_STATE_HEADER:{
        guint8 *data;
        guint32 fourcc;
875

876
        GST_DEBUG_OBJECT (demux, "In header");
877

878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
        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 */
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
        }

        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");
914 915 916 917
          demux->offset += demux->neededbytes;
          demux->neededbytes = 16;
          demux->state = QTDEMUX_STATE_INITIAL;
        }
918
      }
919 920
        break;

921 922 923 924 925 926 927 928 929 930 931 932 933 934
      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;
935
        gst_qtdemux_post_buffering (demux, 1, 1);
936 937 938
      }
        break;

939 940 941 942 943 944 945 946
      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
947

948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
        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;
971 972
        }

973
        /* first buffer? */
974 975 976
        /* FIXME : this should be handled in sink_event */
        if (demux->last_ts == GST_CLOCK_TIME_NONE) {
          gst_qtdemux_send_event (demux,
977 978 979 980
              gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
                  0, GST_CLOCK_TIME_NONE, 0));
        }

981 982 983 984 985 986 987 988 989
        /* 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));

990
        if (stream->fourcc != GST_MAKE_FOURCC ('s', 'a', 'm', 'r')) {
991
          GST_BUFFER_TIMESTAMP (outbuf) =
992
              stream->samples[stream->sample_index].timestamp;
993 994
          demux->last_ts = GST_BUFFER_TIMESTAMP (outbuf);
          GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int
995 996
              (stream->samples[stream->sample_index].duration, GST_SECOND,
              stream->timescale);
997

998
        } else {
999 1000
          if (stream->sample_index == 0)
            GST_BUFFER_TIMESTAMP (outbuf) = 0;
1001
        }
1002

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

1028 1029 1030 1031 1032 1033
  /* 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);
  }
1034

1035
  return ret;
1036 1037 1038 1039 1040 1041 1042
}

static gboolean
qtdemux_sink_activate (GstPad * sinkpad)
{
  if (gst_pad_check_pull_range (sinkpad))
    return gst_pad_activate_pull (sinkpad, TRUE);
1043 1044
  else
    return gst_pad_activate_push (sinkpad, TRUE);
1045 1046 1047 1048 1049
}

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

1052 1053
  if (active) {
    /* if we have a scheduler we can start the task */
1054
    demux->pullbased = TRUE;
1055
    gst_pad_start_task (sinkpad, (GstTaskFunction) gst_qtdemux_loop, sinkpad);
1056 1057 1058 1059 1060
  } else {
    gst_pad_stop_task (sinkpad);
  }

  return TRUE;
Artyom Baginski's avatar
Artyom Baginski committed
1061 1062
}

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

1081 1082 1083
    templ = gst_static_pad_template_get (&gst_qtdemux_videosrc_template);
    stream->pad = gst_pad_new_from_template (templ, name);
    gst_object_unref (templ);
1084
    g_free (name);
1085
    if ((stream->n_samples == 1) && (stream->samples[0].duration == 0)) {
1086 1087 1088 1089
      stream->fps_n = 0;
      stream->fps_d = 1;
    } else {
      stream->fps_n = stream->timescale;
1090 1091 1092 1093
      if (stream->samples[0].duration == 0)
        stream->fps_d = 1;
      else
        stream->fps_d = stream->samples[0].duration;
1094
    }
1095

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1096 1097
    if (stream->caps) {
      gst_caps_set_simple (stream->caps,
1098 1099
          "width", G_TYPE_INT, stream->width,
          "height", G_TYPE_INT, stream->height,
1100
          "framerate", GST_TYPE_FRACTION, stream->fps_n, stream->fps_d, NULL);
1101 1102
    }
    qtdemux->n_video_streams++;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1103
  } else {
1104
    gchar *name = g_strdup_printf ("audio_%02d", qtdemux->n_audio_streams);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1105 1106

    stream->pad =
1107 1108
        gst_pad_new_from_template (gst_static_pad_template_get
        (&gst_qtdemux_audiosrc_template), name);
1109
    g_free (name);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1110 1111
    if (stream->caps) {
      gst_caps_set_simple (stream->caps,
1112 1113
          "rate", G_TYPE_INT, (int) stream->rate,
          "channels", G_TYPE_INT, stream->n_channels, NULL);
Artyom Baginski's avatar
Artyom Baginski committed
1114
    }
1115 1116
    qtdemux->n_audio_streams++;
  }
1117

1118
  gst_pad_use_fixed_caps (stream->pad);
1119

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1120
  GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream;
1121 1122
  qtdemux->streams[qtdemux->n_streams] = stream;
  qtdemux->n_streams++;
1123
  GST_DEBUG ("n_streams is now %d", qtdemux->n_streams);
1124