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

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 * SECTION:element-qtdemux
 *
 * <refsect2>
 * <para>
 * Demuxes a .mov file into raw or compressed audio and/or video streams.
 * </para>
 * <para>
 * This element supports both push and pull-based scheduling, depending on the
 * capabilities of the upstream elements.
 * </para>
 * <title>Example launch line</title>
 * <para>
 * <programlisting>
 * gst-launch filesrc location=test.mov ! qtdemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
 * </programlisting>
 * Play (parse and decode) a .mov file and try to output it to
 * an automatically detected soundcard and videosink. If the MOV file contains
 * compressed audio or video data, this will only work if you have the
 * right decoder elements/plugins installed.
 * </para>
 * </refsect2>
 *
 * Last reviewed on 2006-12-29 (0.10.5)
 */

48
49
50
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
51
52
53

#include "gst/gst-i18n-plugin.h"

54
55
56
#include "qtdemux_types.h"
#include "qtdemux_dump.h"
#include "qtdemux_fourcc.h"
57
#include "qtdemux.h"
58
#include "qtpalette.h"
59

60
#include <stdlib.h>
Artyom Baginski's avatar
Artyom Baginski committed
61
#include <string.h>
62
63
64
65

#ifdef HAVE_ZLIB
# include <zlib.h>
#endif
66

67
GST_DEBUG_CATEGORY (qtdemux_debug);
68
69
70
71
72
73

#if 0
#define qtdemux_dump_mem(a,b)  gst_util_dump_mem(a,b)
#else
#define qtdemux_dump_mem(a,b)   /* */
#endif
74

75
typedef struct _QtNode QtNode;
76
typedef struct _QtDemuxSegment QtDemuxSegment;
77
typedef struct _QtDemuxSample QtDemuxSample;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
78
79
80

struct _QtNode
{
81
  guint32 type;
82
  guint8 *data;
83
  gint len;
84
85
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
86
87
struct _QtDemuxSample
{
88
89
  guint32 chunk;
  guint32 size;
90
  guint64 offset;
91
  GstClockTimeDiff pts_offset;  /* Add this value to timestamp to get the pts */
92
  guint64 timestamp;            /* In GstClockTime */
93
  guint64 duration;             /* in GstClockTime */
94
  gboolean keyframe;            /* TRUE when this packet is a keyframe */
95
96
};

97
98
99
100
101
102
103
104
105
106
107
108
struct _QtDemuxSegment
{
  /* global time and duration, all gst time */
  guint64 time;
  guint64 stop_time;
  guint64 duration;
  /* media time of trak, all gst time */
  guint64 media_start;
  guint64 media_stop;
  gdouble rate;
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
109
110
struct _QtDemuxStream
{
111
112
113
  GstPad *pad;

  /* stream type */
114
115
  guint32 subtype;
  GstCaps *caps;
116
  guint32 fourcc;
117

118
  /* duration/scale */
119
  guint64 duration;             /* in timescale */
120
  guint32 timescale;
121
122
123
124
125

  /* our samples */
  guint32 n_samples;
  QtDemuxSample *samples;
  gboolean all_keyframe;        /* TRUE when all samples are keyframes (no stss) */
126
  guint32 min_duration;         /* duration in timescale of first sample, used for figuring out
127
                                   the framerate, in timescale units */
128

129
130
131
132
  /* if we use chunks or samples */
  gboolean sampled;

  /* video info */
133
134
  gint width;
  gint height;
135
136
137
  /* Numerator/denominator framerate */
  gint fps_n;
  gint fps_d;
138
139
  guint16 bits_per_sample;
  guint16 color_table_id;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
140

141
  /* audio info */
142
143
  gdouble rate;
  gint n_channels;
144
145
146
147
  guint samples_per_packet;
  guint samples_per_frame;
  guint bytes_per_packet;
  guint bytes_per_sample;
148
  guint bytes_per_frame;
149
  guint compression;
150
151
152

  /* when a discontinuity is pending */
  gboolean discont;
153

154
155
156
157
  /* if we need to clip this buffer. This is only needed for uncompressed
   * data */
  gboolean need_clip;

158
159
160
161
162
  /* current position */
  guint32 segment_index;
  guint32 sample_index;
  guint64 time_position;        /* in gst time */

163
164
165
  /* the Gst segment we are processing out, used for clipping */
  GstSegment segment;

166
167
168
  /* last GstFlowReturn */
  GstFlowReturn last_ret;

169
170
171
172
  /* quicktime segments */
  guint32 n_segments;
  QtDemuxSegment *segments;
  gboolean segment_pending;
173
174
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
175
176
enum QtDemuxState
{
177
178
179
  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 */
180
  QTDEMUX_STATE_BUFFER_MDAT     /* Buffering the mdat atom */
181
182
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
183
184
static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
185

186
static const GstElementDetails gst_qtdemux_details =
187
188
189
GST_ELEMENT_DETAILS ("QuickTime demuxer",
    "Codec/Demuxer",
    "Demultiplex a QuickTime file into audio and video streams",
190
    "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
Artyom Baginski's avatar
Artyom Baginski committed
191

David Schleef's avatar
David Schleef committed
192
static GstStaticPadTemplate gst_qtdemux_sink_template =
193
    GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
194
    GST_PAD_SINK,
195
    GST_PAD_ALWAYS,
196
    GST_STATIC_CAPS ("video/quicktime; audio/x-m4a; application/x-3gp")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
197
    );
David Schleef's avatar
David Schleef committed
198
199

static GstStaticPadTemplate gst_qtdemux_videosrc_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
200
201
202
203
GST_STATIC_PAD_TEMPLATE ("audio_%02d",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
David Schleef's avatar
David Schleef committed
204
205

static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
206
207
208
209
GST_STATIC_PAD_TEMPLATE ("video_%02d",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
Artyom Baginski's avatar
Artyom Baginski committed
210

211
static GstElementClass *parent_class = NULL;
Artyom Baginski's avatar
Artyom Baginski committed
212

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
213
214
215
static void gst_qtdemux_class_init (GstQTDemuxClass * klass);
static void gst_qtdemux_base_init (GstQTDemuxClass * klass);
static void gst_qtdemux_init (GstQTDemux * quicktime_demux);
216
static void gst_qtdemux_dispose (GObject * object);
217

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
218
219
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
    GstStateChange transition);
220
221
static gboolean qtdemux_sink_activate (GstPad * sinkpad);
static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
222
static gboolean qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active);
223
224
225

static void gst_qtdemux_loop (GstPad * pad);
static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstBuffer * inbuf);
226
static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstEvent * event);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
227

228
static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux, guint8 * buffer,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
229
    int length);
230
231
232
static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
    guint8 * buffer, int length);
static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
233

234
static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
235
    QtDemuxStream * stream, GNode * esds, GstTagList * list);
236
237
238
static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
    const gchar ** codec_name);
239
240
241
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
242

243
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
244
gst_qtdemux_get_type (void)
Artyom Baginski's avatar
Artyom Baginski committed
245
246
247
248
249
{
  static GType qtdemux_type = 0;

  if (!qtdemux_type) {
    static const GTypeInfo qtdemux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
250
251
252
253
254
      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
255
    };
256

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
257
    qtdemux_type =
258
259
        g_type_register_static (GST_TYPE_ELEMENT, "GstQTDemux", &qtdemux_info,
        0);
Artyom Baginski's avatar
Artyom Baginski committed
260
261
262
263
  }
  return qtdemux_type;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
264
265
static void
gst_qtdemux_base_init (GstQTDemuxClass * klass)
266
267
268
269
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
270
271
272
273
274
      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));
275
  gst_element_class_set_details (element_class, &gst_qtdemux_details);
David Schleef's avatar
David Schleef committed
276

277
  GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
278
279
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
280
281
static void
gst_qtdemux_class_init (GstQTDemuxClass * klass)
Artyom Baginski's avatar
Artyom Baginski committed
282
283
284
285
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
286
287
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Artyom Baginski's avatar
Artyom Baginski committed
288

289
  parent_class = g_type_class_peek_parent (klass);
290

291
292
  gobject_class->dispose = gst_qtdemux_dispose;

Artyom Baginski's avatar
Artyom Baginski committed
293
294
295
  gstelement_class->change_state = gst_qtdemux_change_state;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
296
297
static void
gst_qtdemux_init (GstQTDemux * qtdemux)
Artyom Baginski's avatar
Artyom Baginski committed
298
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
299
  qtdemux->sinkpad =
300
      gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
301
302
303
  gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
  gst_pad_set_activatepull_function (qtdemux->sinkpad,
      qtdemux_sink_activate_pull);
304
305
306
307
  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);
308
  gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
309

310
  qtdemux->state = QTDEMUX_STATE_INITIAL;
311
  /* FIXME, use segment last_stop for this */
312
  qtdemux->last_ts = GST_CLOCK_TIME_NONE;
313
314
315
316
  qtdemux->pullbased = FALSE;
  qtdemux->neededbytes = 16;
  qtdemux->todrop = 0;
  qtdemux->adapter = gst_adapter_new ();
317
318
319
  qtdemux->offset = 0;
  qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
  qtdemux->mdatbuffer = NULL;
320
  gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
Artyom Baginski's avatar
Artyom Baginski committed
321
322
}

323
324
325
326
327
328
329
330
331
static void
gst_qtdemux_dispose (GObject * object)
{
  GstQTDemux *qtdemux = GST_QTDEMUX (object);

  if (qtdemux->adapter) {
    g_object_unref (G_OBJECT (qtdemux->adapter));
    qtdemux->adapter = NULL;
  }
332
333

  G_OBJECT_CLASS (parent_class)->dispose (object);
334
335
}

336
#if 0
337
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
338
339
gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
340
341
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
342
  QtDemuxStream *stream = gst_pad_get_element_private (pad);
343

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
344
  if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e') &&
345
346
347
348
349
350
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
    return FALSE;

  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
351
352
353
354
355
356
357
358
359
        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;
360
361
362
363
      }
      break;
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
364
365
366
367
368
369
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
370
371
372
373
      }
      break;
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
374
375
376
377
378
379
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
380
381
382
383
384
385
386
387
      }
      break;
    default:
      res = FALSE;
  }

  return res;
}
388
#endif
389
390

static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
391
gst_qtdemux_get_src_query_types (GstPad * pad)
392
393
394
{
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
Wim Taymans's avatar
Wim Taymans committed
395
    GST_QUERY_DURATION,
396
    GST_QUERY_SEEKING,
397
398
399
400
401
402
    0
  };

  return src_types;
}

403
404
405
406
407
408
409
410
411
static gboolean
gst_qtdemux_get_duration (GstQTDemux * qtdemux, gint64 * duration)
{
  gboolean res = TRUE;

  *duration = GST_CLOCK_TIME_NONE;

  if (qtdemux->duration != 0) {
    if (qtdemux->duration != G_MAXINT32 && qtdemux->timescale != 0) {
412
      *duration = gst_util_uint64_scale (qtdemux->duration,
413
414
415
416
417
418
          GST_SECOND, qtdemux->timescale);
    }
  }
  return res;
}

419
static gboolean
420
gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
421
{
422
423
  gboolean res = FALSE;
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
424

425
  switch (GST_QUERY_TYPE (query)) {
426
    case GST_QUERY_POSITION:
427
428
429
      if (GST_CLOCK_TIME_IS_VALID (qtdemux->segment.last_stop)) {
        gst_query_set_position (query, GST_FORMAT_TIME,
            qtdemux->segment.last_stop);
Wim Taymans's avatar
Wim Taymans committed
430
431
432
        res = TRUE;
      }
      break;
433
434
    case GST_QUERY_DURATION:{
      GstFormat fmt;
435

436
437
438
      gst_query_parse_duration (query, &fmt, NULL);
      if (fmt == GST_FORMAT_TIME) {
        gint64 duration = -1;
439

440
441
442
443
444
445
446
        gst_qtdemux_get_duration (qtdemux, &duration);
        if (duration > 0) {
          gst_query_set_duration (query, GST_FORMAT_TIME, duration);
          res = TRUE;
        }
      }
      break;
447
    }
448
449
450
451
452
453
454
455
456
457
458
459
    case GST_QUERY_SEEKING:{
      GstFormat fmt;

      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
      if (fmt == GST_FORMAT_TIME) {
        gint64 duration = -1;

        gst_qtdemux_get_duration (qtdemux, &duration);
        gst_query_set_seeking (query, GST_FORMAT_TIME, qtdemux->pullbased,
            0, duration);
        res = TRUE;
      }
460
      break;
461
    }
462
    default:
463
      res = gst_pad_query_default (pad, query);
464
465
466
      break;
  }

467
468
  gst_object_unref (qtdemux);

469
470
471
  return res;
}

472
/* push event on all source pads; takes ownership of the event */
473
static void
474
gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
475
476
477
478
479
480
481
{
  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++) {
482
483
484
485
    GstPad *pad;

    if ((pad = qtdemux->streams[n]->pad))
      gst_pad_push_event (pad, gst_event_ref (event));
486
487
488
489
  }
  gst_event_unref (event);
}

490
/* find the index of the sample that includes the data for @media_time
491
 *
492
493
 * Returns the index of the sample or n_samples when the sample was not
 * found.
494
 */
495
/* FIXME, binary search would be nice here */
496
497
498
static guint32
gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint64 media_time)
499
{
500
  guint32 i;
501

502
503
  if (str->n_samples == 0)
    return 0;
504

505
506
507
508
  for (i = 0; i < str->n_samples; i++) {
    if (str->samples[i].timestamp > media_time) {
      /* first sample after media_time, we need the previous one */
      return (i == 0 ? 0 : i - 1);
509
    }
510
511
512
  }
  return str->n_samples - 1;
}
513

514
515
516
517
518
519
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
556
557
558
559
560
561
562
/* find the index of the keyframe needed to decode the sample at @index
 * of stream @str.
 *
 * Returns the index of the keyframe.
 */
static guint32
gst_qtdemux_find_keyframe (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint32 index)
{
  if (index >= str->n_samples)
    return str->n_samples;

  /* all keyframes, return index */
  if (str->all_keyframe)
    return index;

  /* else go back until we have a keyframe */
  while (TRUE) {
    if (str->samples[index].keyframe)
      break;

    if (index == 0)
      break;

    index--;
  }
  return index;
}

/* find the segment for @time_position for @stream
 *
 * Returns -1 if the segment cannot be found.
 */
static guint32
gst_qtdemux_find_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
    guint64 time_position)
{
  gint i;
  guint32 seg_idx;

  /* find segment corresponding to time_position if we are looking
   * for a segment. */
  seg_idx = -1;
  for (i = 0; i < stream->n_segments; i++) {
    QtDemuxSegment *segment = &stream->segments[i];

    if (segment->time <= time_position && time_position < segment->stop_time) {
      seg_idx = i;
      break;
563
    }
564
  }
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
  return seg_idx;
}

/* move the stream @str to the sample position @index.
 *
 * Updates @str->sample_index and marks discontinuity if needed.
 */
static void
gst_qtdemux_move_stream (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint32 index)
{
  /* no change needed */
  if (index == str->sample_index)
    return;

  GST_DEBUG_OBJECT (qtdemux, "moving to sample %u of %u", index,
      str->n_samples);

  /* position changed, we have a discont */
  str->sample_index = index;
  str->discont = TRUE;
586
587
588
589
}

/* perform the seek.
 *
590
591
592
593
594
595
 * We set all segment_indexes in the streams to unknown and
 * adjust the time_position to the desired position. this is enough
 * to trigger a segment switch in the streaming thread to start
 * streaming from the desired position.
 *
 * Keyframe seeking is a little more complicated when dealing with
596
 * segments. Ideally we want to move to the previous keyframe in
597
598
599
600
 * the segment but there might not be a keyframe in the segment. In
 * fact, none of the segments could contain a keyframe. We take a
 * practical approach: seek to the previous keyframe in the segment,
 * if there is none, seek to the beginning of the segment.
601
602
 *
 * Called with STREAM_LOCK
603
 */
604
static gboolean
605
gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
606
{
607
  gint64 desired_offset;
608
  gint n;
609

610
  desired_offset = segment->last_stop;
611

612
613
  GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (desired_offset));
614

615
  if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
616
617
618
619
620
621
622
623
    guint64 min_offset;

    min_offset = desired_offset;

    /* for each stream, find the index of the sample in the segment
     * and move back to the previous keyframe. */
    for (n = 0; n < qtdemux->n_streams; n++) {
      QtDemuxStream *str;
624
      guint32 index, kindex;
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
      guint32 seg_idx;
      guint64 media_start;
      guint64 media_time;
      guint64 seg_time;
      QtDemuxSegment *seg;

      str = qtdemux->streams[n];

      seg_idx = gst_qtdemux_find_segment (qtdemux, str, desired_offset);
      GST_DEBUG_OBJECT (qtdemux, "align segment %d", seg_idx);

      /* segment not found, continue with normal flow */
      if (seg_idx == -1)
        continue;

      /* get segment and time in the segment */
      seg = &str->segments[seg_idx];
      seg_time = desired_offset - seg->time;

      /* get the media time in the segment */
      media_start = seg->media_start + seg_time;

      /* get the index of the sample with media time */
      index = gst_qtdemux_find_index (qtdemux, str, media_start);
      GST_DEBUG_OBJECT (qtdemux, "sample for %" GST_TIME_FORMAT " at %u",
          GST_TIME_ARGS (media_start), index);
651

652
      /* find previous keyframe */
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
      kindex = gst_qtdemux_find_keyframe (qtdemux, str, index);

      GST_DEBUG_OBJECT (qtdemux, "keyframe at %u", kindex);

      /* if the keyframe is at a different position, we need to update the
       * requiested seek time */
      if (index != kindex) {
        index = kindex;

        /* get timestamp of keyframe */
        media_time = str->samples[kindex].timestamp;
        GST_DEBUG_OBJECT (qtdemux, "keyframe at %u with time %" GST_TIME_FORMAT,
            kindex, GST_TIME_ARGS (media_time));

        /* keyframes in the segment get a chance to change the
         * desired_offset. keyframes out of the segment are
         * ignored. */
        if (media_time >= seg->media_start) {
          guint64 seg_time;

          /* this keyframe is inside the segment, convert back to
           * segment time */
          seg_time = (media_time - seg->media_start) + seg->time;
          if (seg_time < min_offset)
            min_offset = seg_time;
        }
679
680
681
682
      }
    }
    GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
        GST_TIME_FORMAT, GST_TIME_ARGS (desired_offset));
683
    desired_offset = min_offset;
684
685
686
687
688
  }

  /* and set all streams to the final position */
  for (n = 0; n < qtdemux->n_streams; n++) {
    QtDemuxStream *stream = qtdemux->streams[n];
689

690
691
692
    stream->time_position = desired_offset;
    stream->sample_index = 0;
    stream->segment_index = -1;
693
    stream->last_ret = GST_FLOW_OK;
694
  }
695
696
  segment->last_stop = desired_offset;
  segment->time = desired_offset;
697

698
  /* we stop at the end */
699
700
701
  if (segment->stop == -1)
    segment->stop = segment->duration;

702
703
  return TRUE;
}
704

705
/* do a seek in pull based mode */
706
707
708
709
710
711
712
713
714
715
716
717
static gboolean
gst_qtdemux_do_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
{
  gdouble rate;
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  gint64 cur, stop;
  gboolean flush;
  gboolean res;
  gboolean update;
  GstSegment seeksegment;
718
  int i;
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745

  if (event) {
    GST_DEBUG_OBJECT (qtdemux, "doing seek with event");

    gst_event_parse_seek (event, &rate, &format, &flags,
        &cur_type, &cur, &stop_type, &stop);

    /* we have to have a format as the segment format. Try to convert
     * if not. */
    if (format != GST_FORMAT_TIME) {
      GstFormat fmt;

      fmt = GST_FORMAT_TIME;
      res = TRUE;
      if (cur_type != GST_SEEK_TYPE_NONE)
        res = gst_pad_query_convert (pad, format, cur, &fmt, &cur);
      if (res && stop_type != GST_SEEK_TYPE_NONE)
        res = gst_pad_query_convert (pad, format, stop, &fmt, &stop);
      if (!res)
        goto no_format;

      format = fmt;
    }
  } else {
    GST_DEBUG_OBJECT (qtdemux, "doing seek without event");
    flags = 0;
  }
746

747
  flush = flags & GST_SEEK_FLAG_FLUSH;
748

749
  GST_DEBUG_OBJECT (qtdemux, "seek format %d", format);
750

751
  /* stop streaming, either by flushing or by pausing the task */
752
753
754
755
756
757
  if (flush) {
    /* unlock upstream pull_range */
    gst_pad_push_event (qtdemux->sinkpad, gst_event_new_flush_start ());
    /* make sure out loop function exits */
    gst_qtdemux_push_event (qtdemux, gst_event_new_flush_start ());
  } else {
758
    /* non flushing seek, pause the task */
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
    gst_pad_pause_task (qtdemux->sinkpad);
  }

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

  /* copy segment, we need this because we still need the old
   * segment when we close the current segment. */
  memcpy (&seeksegment, &qtdemux->segment, sizeof (GstSegment));

  if (event) {
    /* configure the segment with the seek variables */
    GST_DEBUG_OBJECT (qtdemux, "configuring seek");
    gst_segment_set_seek (&seeksegment, rate, format, flags,
        cur_type, cur, stop_type, stop, &update);
  }

776
  /* now do the seek, this actually never returns FALSE */
777
778
779
780
781
782
783
784
785
786
787
788
789
  res = gst_qtdemux_perform_seek (qtdemux, &seeksegment);

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

790
791
    /* FIXME, needs to be done from the streaming thread. Also, the rate is the
     * product of the global rate and the (quicktime) segment rate. */
792
793
794
795
796
797
798
    gst_qtdemux_push_event (qtdemux,
        gst_event_new_new_segment (TRUE,
            qtdemux->segment.rate, qtdemux->segment.format,
            qtdemux->segment.start, qtdemux->segment.last_stop,
            qtdemux->segment.time));
  }

799
  /* commit the new segment */
800
801
802
803
804
805
806
807
  memcpy (&qtdemux->segment, &seeksegment, sizeof (GstSegment));

  if (qtdemux->segment.flags & GST_SEEK_FLAG_SEGMENT) {
    gst_element_post_message (GST_ELEMENT_CAST (qtdemux),
        gst_message_new_segment_start (GST_OBJECT_CAST (qtdemux),
            qtdemux->segment.format, qtdemux->segment.last_stop));
  }

808
809
  /* restart streaming, NEWSEGMENT will be sent from the streaming
   * thread. */
810
  qtdemux->segment_running = TRUE;
811
  for (i = 0; i < qtdemux->n_streams; i++)
812
    qtdemux->streams[i]->last_ret = GST_FLOW_OK;
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_pad_start_task (qtdemux->sinkpad, (GstTaskFunction) gst_qtdemux_loop,
      qtdemux->sinkpad);

  GST_PAD_STREAM_UNLOCK (qtdemux->sinkpad);

  return TRUE;

  /* ERRORS */
no_format:
  {
    GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
    return FALSE;
  }
}

static gboolean
gst_qtdemux_handle_src_event (GstPad * pad, GstEvent * event)
{
  gboolean res = TRUE;
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
837
838
839
840
841
842
      if (qtdemux->pullbased) {
        res = gst_qtdemux_do_seek (qtdemux, pad, event);
      } else {
        GST_DEBUG_OBJECT (qtdemux, "cannot seek in streaming mode");
        res = FALSE;
      }
843
      break;
844
845
846
847
848
    default:
      res = FALSE;
      break;
  }

849
850
  gst_object_unref (qtdemux);

851
852
853
854
855
  gst_event_unref (event);

  return res;
}

856
static gboolean
857
gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstEvent * event)
Artyom Baginski's avatar
Artyom Baginski committed
858
{
859
  GstQTDemux *demux = GST_QTDEMUX (GST_PAD_PARENT (sinkpad));
860
  gboolean res;
861
862
863
864

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_NEWSEGMENT:
      /* We need to convert it to a GST_FORMAT_TIME new segment */
865
866
867
      gst_event_unref (event);
      res = TRUE;
      break;
Artyom Baginski's avatar
Artyom Baginski committed
868
    default:
869
870
      res = gst_pad_event_default (demux->sinkpad, event);
      break;
Artyom Baginski's avatar
Artyom Baginski committed
871
  }
872

873
  return res;
Artyom Baginski's avatar
Artyom Baginski committed
874
875
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
876
877
static GstStateChangeReturn
gst_qtdemux_change_state (GstElement * element, GstStateChange transition)
878
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
879
  GstQTDemux *qtdemux = GST_QTDEMUX (element);
880
881
  GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;

882
883
884
885
886
887
888
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:
      break;
    default:
      break;
  }

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
891
892
  switch (transition) {
    case GST_STATE_CHANGE_PAUSED_TO_READY:{
893
894
      gint n;

895
      qtdemux->state = QTDEMUX_STATE_INITIAL;
896
      qtdemux->last_ts = GST_CLOCK_TIME_NONE;
897
898
899
900
      qtdemux->neededbytes = 16;
      qtdemux->todrop = 0;
      qtdemux->pullbased = FALSE;
      qtdemux->offset = 0;
901
902
903
904
      qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
      if (qtdemux->mdatbuffer)
        gst_buffer_unref (qtdemux->mdatbuffer);
      qtdemux->mdatbuffer = NULL;
905
      gst_adapter_clear (qtdemux->adapter);
906
      for (n = 0; n < qtdemux->n_streams; n++) {
907
908
909
910
911
912
913
914
915
        QtDemuxStream *stream = qtdemux->streams[n];

        if (stream->pad)
          gst_element_remove_pad (element, stream->pad);
        g_free (stream->samples);
        if (stream->caps)
          gst_caps_unref (stream->caps);
        g_free (stream->segments);
        g_free (stream);
916
917
      }
      qtdemux->n_streams = 0;
918
919
      qtdemux->n_video_streams = 0;
      qtdemux->n_audio_streams = 0;
920
      gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
921
      break;
922
    }
923
924
925
    default:
      break;
  }
Artyom Baginski's avatar
Artyom Baginski committed
926

927
  return result;
928
}
Artyom Baginski's avatar
Artyom Baginski committed
929

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
930
static void
931
extract_initial_length_and_fourcc (guint8 * data, guint64 * plength,
932
933
    guint32 * pfourcc)
{
934
  guint64 length;
935
936
  guint32 fourcc;

937
  length = QT_UINT32 (data);
938
  GST_DEBUG ("length %08" G_GINT64_MODIFIER "x", length);
939
  fourcc = QT_FOURCC (data + 4);
940
941
942
943
  GST_DEBUG ("atom type %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));

  if (length == 0) {
    length = G_MAXUINT32;
944
  } else if (length == 1) {
945
946
    /* this means we have an extended size, which is the 64 bit value of
     * the next 8 bytes */
947
    length = QT_UINT64 (data + 8);
948
    GST_DEBUG ("length %08llx", length);
949
950
951
952
953
954
955
956
957
958
  }

  if (plength)
    *plength = length;
  if (pfourcc)
    *pfourcc = fourcc;
}

static GstFlowReturn
gst_qtdemux_loop_state_header (GstQTDemux * qtdemux)
959
{
960
  guint64 length;
961
  guint32 fourcc;
962
  GstBuffer *buf = NULL;
963
964
965
966
967
968
969
970
971
972
  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) {
973
974
975
976
977
978
979
980
    case FOURCC_mdat:
    case FOURCC_free:
    case FOURCC_wide:
    case FOURCC_PICT:
    case FOURCC_pnot:
    {
      GST_LOG_OBJECT (qtdemux,
          "skipping atom '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT,
981
982
983
984
          GST_FOURCC_ARGS (fourcc), cur_offset);
      cur_offset += length;
      qtdemux->offset += length;
      break;
985
986
987
    }
    case FOURCC_moov:
    {
988
989
990
991
992
      GstBuffer *moov;

      ret = gst_pad_pull_range (qtdemux->sinkpad, cur_offset, length, &moov);
      if (ret != GST_FLOW_OK)
        goto beach;
993
      if (length != GST_BUFFER_SIZE (moov)) {
994
995
996
997
        GST_ELEMENT_ERROR (qtdemux, STREAM, DECODE,
            (_("This file is incomplete and cannot be played.")),
            ("We got less than expected (received %u, wanted %u)",
                GST_BUFFER_SIZE (moov), (guint) length));
998
999
1000
        ret = GST_FLOW_ERROR;
        goto beach;
      }
1001
1002
1003
1004
      cur_offset += length;
      qtdemux->offset += length;

      qtdemux_parse_moov (qtdemux, GST_BUFFER_DATA (moov), length);
1005
1006
      qtdemux_node_dump (qtdemux, qtdemux->moov_node);

1007
1008
1009
1010
1011
1012
1013
1014
      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;
1015
    }
1016
1017
1018
1019
1020
    default:
    {
      GST_LOG_OBJECT (qtdemux,
          "unknown %08x '%" GST_FOURCC_FORMAT "' at %" G_GUINT64_FORMAT, fourcc,
          GST_FOURCC_ARGS (fourcc), cur_offset);
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
      cur_offset += length;
      qtdemux->offset += length;
      break;
    }
  }

beach:
  return ret;
}

1031
1032
1033
1034
/* activate the given segment number @seg_idx of @stream at time @offset.
 * @offset is an absolute global position over all the segments.
 *
 * This will push out a NEWSEGMENT event with the right values and
1035
 * position the stream index to the first decodable sample before
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
 * @offset.
 */
static gboolean
gst_qtdemux_activate_segment (GstQTDemux * qtdemux, QtDemuxStream * stream,
    guint32 seg_idx, guint64 offset)
{
  GstEvent *event;
  QtDemuxSegment *segment;
  guint32 index, kf_index;
  guint64 seg_time;
  guint64 start, stop;
1047
  gdouble rate;
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064

  /* update the current segment */
  stream->segment_index = seg_idx;

  /* get the segment */
  segment = &stream->segments[seg_idx];

  if (offset < segment->time)
    return FALSE;

  /* get time in this segment */
  seg_time = offset - segment->time;

  if (seg_time >= segment->duration)
    return FALSE;

  /* calc media start/stop */
1065
1066
1067
1068
  if (qtdemux->segment.stop == -1)
    stop = segment->media_stop;
  else
    stop = MIN (segment->media_stop, qtdemux->segment.stop);
1069
  start = MIN (segment->media_start + seg_time, stop);
1070
1071
1072
1073
1074

  GST_DEBUG_OBJECT (qtdemux, "newsegment %d from %" GST_TIME_FORMAT
      " to %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, seg_idx,
      GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (offset));

1075
1076
  /* combine global rate with that of the segment */
  rate = segment->rate * qtdemux->segment.rate;
1077
1078
1079
1080

  /* update the segment values used for clipping */
  gst_segment_init (&stream->segment, GST_FORMAT_TIME);
  gst_segment_set_newsegment (&stream->segment, FALSE, rate, GST_FORMAT_TIME,
1081
1082
      start, stop, offset);

1083
1084
1085
1086
  /* now prepare and send the segment */
  if (stream->pad) {
    event = gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME,
        start, stop, offset);
1087
    gst_pad_push_event (stream->pad, event);
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

  /* and move to the keyframe before the indicated media time of the
   * segment */
  index = gst_qtdemux_find_index (qtdemux, stream, start);

  GST_DEBUG_OBJECT (qtdemux, "moving data pointer to %" GST_TIME_FORMAT
      ", index: %u", GST_TIME_ARGS (start), index);

  /* we're at the right spot */
  if (index == stream->sample_index)
    return TRUE;

  /* find keyframe of the target index */
  kf_index = gst_qtdemux_find_keyframe (qtdemux, stream, index);

  /* if we move forwards, we don't have to go back to the previous
   * keyframe since we already sent that. We can also just jump to
   * the keyframe right before the target index if there is one. */
  if (index > stream->sample_index) {
    /* moving forwards check if we move past a keyframe */
    if (kf_index > stream->sample_index) {
      GST_DEBUG_OBJECT (qtdemux, "moving forwards to keyframe at %u", kf_index);
      gst_qtdemux_move_stream (qtdemux, stream, kf_index);
    } else {
      GST_DEBUG_OBJECT (qtdemux, "moving forwards, keyframe at %u already sent",
          kf_index);
    }
  } else {
    GST_DEBUG_OBJECT (qtdemux, "moving backwards to keyframe at %u", kf_index);
    gst_qtdemux_move_stream (qtdemux, stream, kf_index);
  }

  return TRUE;
}

/* prepare to get the current sample of @stream, getting essential values.
1125
 *
1126
1127
1128
1129
1130
1131
1132
 * This function will also prepare and send the segment when needed.
 *
 * Return FALSE if the stream is EOS.
 */
static gboolean
gst_qtdemux_prepare_current_sample (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint64 * offset, guint * size, guint64 * timestamp,
1133
    guint64 * duration, gboolean * keyframe)
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
{
  QtDemuxSample *sample;
  guint64 time_position;
  guint32 seg_idx;

  g_return_val_if_fail (stream != NULL, FALSE);

  time_position = stream->time_position;
  if (time_position == -1)
    goto eos;

  seg_idx = stream->segment_index;
  if (seg_idx == -1) {
    /* find segment corresponding to time_position if we are looking
     * for a segment. */
    seg_idx = gst_qtdemux_find_segment (qtdemux, stream, time_position);

    /* nothing found, we're really eos */
    if (seg_idx == -1)
      goto eos;
  }

  /* different segment, activate it, sample_index will be set. */
  if (stream->segment_index != seg_idx)
    gst_qtdemux_activate_segment (qtdemux, stream, seg_idx, time_position);

1160
  GST_LOG_OBJECT (qtdemux, "segment active, index = %u of %u",
1161
1162
      stream->sample_index, stream->n_samples);

1163
1164
1165
  if (stream->sample_index >= stream->n_samples)
    goto eos;

1166
1167
1168
  /* now get the info for the sample we're at */
  sample = &stream->samples[stream->sample_index];

1169
  *timestamp = sample->timestamp + sample->pts_offset;
1170
1171
  *offset = sample->offset;
  *size = sample->size;
1172
  *duration = sample->duration;
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
  *keyframe = stream->all_keyframe || sample->keyframe;

  return TRUE;

  /* special cases */
eos:
  {
    stream->time_position = -1;
    return FALSE;
  }
}

/* move to the next sample in @stream.
 *
 * Moves to the next segment when needed.
 */
static void
gst_qtdemux_advance_sample (GstQTDemux * qtdemux, QtDemuxStream * stream)
{
  QtDemuxSample *sample;
  QtDemuxSegment *segment;

  /* move to next sample */
  stream->sample_index++;

  /* get current segment */
  segment = &stream->segments[stream->segment_index];

  /* reached the last sample, we need the next segment */
1202
  if (stream->sample_index >= stream->n_samples)
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
    goto next_segment;

  /* get next sample */
  sample = &stream->samples[stream->sample_index];

  /* see if we are past the segment */
  if (sample->timestamp >= segment->media_stop)
    goto next_segment;

  if (sample->timestamp >= segment->media_start) {
    /* inside the segment, update time_position, looks very familiar to
     * GStreamer segments, doesn't it? */
    stream->time_position =
        (sample->timestamp - segment->media_start) + segment->time;
  } else {
    /* not yet in segment, time does not yet increment. This means
     * that we are still prerolling keyframes to the decoder so it can
     * decode the first sample of the segment. */
    stream->time_position = segment->time;
  }
  return;

  /* move to the next segment */
next_segment:
  {
    GST_DEBUG_OBJECT (qtdemux, "segment %d ended ", stream->segment_index);

    if (stream->segment_index == stream->n_segments - 1) {
      /* are we at the end of the last segment, we're EOS */
      stream->time_position = -1;
    } else {
      /* else we're only at the end of the current segment */
      stream->time_position = segment->stop_time;
    }
    /* make sure we select a new segment */
    stream->segment_index = -1;
  }
}

1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
static GstFlowReturn
gst_qtdemux_combine_flows (GstQTDemux * demux, QtDemuxStream * stream,
    GstFlowReturn ret)
{
  gint i;

  /* store the value */
  stream->last_ret = ret;

  /* any other error that is not-linked can be returned right
   * away */
  if (ret != GST_FLOW_NOT_LINKED)
    goto done;

  /* only return NOT_LINKED if all other pads returned NOT_LINKED */
  for (i = 0; i < demux->n_streams; i++) {
    QtDemuxStream *ostream = demux->streams[i];

    ret = ostream->last_ret;
    /* some other return value (must be SUCCESS but we can return
     * other values as well) */
    if (ret != GST_FLOW_NOT_LINKED)
      goto done;
  }
  /* if we get here, all other pads were unlinked and we return
   * NOT_LINKED then */
done:
1269
  GST_LOG_OBJECT (demux, "combined flow return: %s", gst_flow_get_name (ret));
1270
1271
1272
  return ret;
}

1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304 </