qtdemux.c 249 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>
5
 * Copyright (C) <2007> Julien Moutte <julien@fluendo.com>
6
 * Copyright (C) <2009> Tim-Philipp Müller <tim centricular net>
7
 * Copyright (C) <2009> STEricsson <benjamin.gaignard@stericsson.com>
Artyom Baginski's avatar
Artyom Baginski committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * 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.
 */

25
26
27
28
/**
 * SECTION:element-qtdemux
 *
 * Demuxes a .mov file into raw or compressed audio and/or video streams.
29
 *
30
31
 * This element supports both push and pull-based scheduling, depending on the
 * capabilities of the upstream elements.
32
33
 *
 * <refsect2>
34
 * <title>Example launch line</title>
35
 * |[
36
 * gst-launch filesrc location=test.mov ! qtdemux name=demux  demux.audio_00 ! decodebin ! audioconvert ! audioresample ! autoaudiosink   demux.video_00 ! queue ! decodebin ! ffmpegcolorspace ! videoscale ! autovideosink
37
 * ]| Play (parse and decode) a .mov file and try to output it to
38
39
40
41
42
43
44
45
 * 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.
 * </refsect2>
 *
 * Last reviewed on 2006-12-29 (0.10.5)
 */

46
47
48
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
49
50
51

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

52
#include <glib/gprintf.h>
53
54
#include <gst/tag/tag.h>

55
#include "qtatomparser.h"
56
57
58
#include "qtdemux_types.h"
#include "qtdemux_dump.h"
#include "qtdemux_fourcc.h"
59
#include "qtdemux_lang.h"
60
#include "qtdemux.h"
61
#include "qtpalette.h"
62

63
64
65
#include "gst/riff/riff-media.h"
#include "gst/riff/riff-read.h"

66
#include <stdio.h>
67
#include <stdlib.h>
Artyom Baginski's avatar
Artyom Baginski committed
68
#include <string.h>
69
70
71
72

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

74
75
76
/* max. size considered 'sane' for non-mdat atoms */
#define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)

77
78
79
/* if the sample index is larger than this, something is likely wrong */
#define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)

80
GST_DEBUG_CATEGORY (qtdemux_debug);
81

82
/*typedef struct _QtNode QtNode; */
83
typedef struct _QtDemuxSegment QtDemuxSegment;
84
typedef struct _QtDemuxSample QtDemuxSample;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
85

86
/*struct _QtNode
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
87
{
88
  guint32 type;
89
  guint8 *data;
90
  gint len;
91
};*/
92

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
93
94
struct _QtDemuxSample
{
95
  guint32 size;
96
  gint32 pts_offset;            /* Add this value to timestamp to get the pts */
97
  guint64 offset;
98
99
  guint64 timestamp;            /* DTS In mov time */
  guint32 duration;             /* In mov time */
100
  gboolean keyframe;            /* TRUE when this packet is a keyframe */
101
102
};

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* timestamp is the DTS */
#define QTSAMPLE_DTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp,\
    GST_SECOND, (stream)->timescale)
/* timestamp + offset is the PTS */
#define QTSAMPLE_PTS(stream,sample) gst_util_uint64_scale ((sample)->timestamp + \
    (sample)->pts_offset, GST_SECOND, (stream)->timescale)
/* timestamp + duration - dts is the duration */
#define QTSAMPLE_DUR_DTS(stream,sample,dts) (gst_util_uint64_scale ((sample)->timestamp + \
    (sample)->duration, GST_SECOND, (stream)->timescale) - (dts));
/* timestamp + offset + duration - pts is the duration */
#define QTSAMPLE_DUR_PTS(stream,sample,pts) (gst_util_uint64_scale ((sample)->timestamp + \
    (sample)->pts_offset + (sample)->duration, GST_SECOND, (stream)->timescale) - (pts));

#define QTSAMPLE_KEYFRAME(stream,sample) ((stream)->all_keyframe || (sample)->keyframe)

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
 * Quicktime has tracks and segments. A track is a continuous piece of
 * multimedia content. The track is not always played from start to finish but
 * instead, pieces of the track are 'cut out' and played in sequence. This is
 * what the segments do.
 *
 * Inside the track we have keyframes (K) and delta frames. The track has its
 * own timing, which starts from 0 and extends to end. The position in the track
 * is called the media_time.
 *
 * The segments now describe the pieces that should be played from this track
 * and are basically tupples of media_time/duration/rate entries. We can have
 * multiple segments and they are all played after one another. An example:
 *
 * segment 1: media_time: 1 second, duration: 1 second, rate 1
 * segment 2: media_time: 3 second, duration: 2 second, rate 2
 *
 * To correctly play back this track, one must play: 1 second of media starting
 * from media_time 1 followed by 2 seconds of media starting from media_time 3
 * at a rate of 2.
 *
 * Each of the segments will be played at a specific time, the first segment at
 * time 0, the second one after the duration of the first one, etc.. Note that
 * the time in resulting playback is not identical to the media_time of the
 * track anymore.
 *
 * Visually, assuming the track has 4 second of media_time:
 *
 *                (a)                   (b)          (c)              (d)
 *         .-----------------------------------------------------------.
 * track:  | K.....K.........K........K.......K.......K...........K... |
 *         '-----------------------------------------------------------'
 *         0              1              2              3              4    
 *           .------------^              ^   .----------^              ^
 *          /              .-------------'  /       .------------------'
 *         /              /          .-----'       /
 *         .--------------.         .--------------.
 *         | segment 1    |         | segment 2    |
 *         '--------------'         '--------------'
 *       
 * The challenge here is to cut out the right pieces of the track for each of
 * the playback segments. This fortunatly can easily be done with the SEGMENT
 * events of gstreamer.
 *
 * For playback of segment 1, we need to provide the decoder with the keyframe
 * (a), in the above figure, but we must instruct it only to output the decoded
 * data between second 1 and 2. We do this with a SEGMENT event for 1 to 2, time
 * position set to the time of the segment: 0.
 *
 * We then proceed to push data from keyframe (a) to frame (b). The decoder
 * decodes but clips all before media_time 1.
 * 
 * After finishing a segment, we push out a new SEGMENT event with the clipping
 * boundaries of the new data.
 *
 * This is a good usecase for the GStreamer accumulated SEGMENT events.
 */

176
177
178
179
180
181
182
183
184
185
186
187
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
188
189
struct _QtDemuxStream
{
190
191
192
  GstPad *pad;

  /* stream type */
193
194
  guint32 subtype;
  GstCaps *caps;
195
  guint32 fourcc;
196

197
198
199
  /* if the stream has a redirect URI in its headers, we store it here */
  gchar *redirect_uri;

200
  /* duration/scale */
201
  guint64 duration;             /* in timescale */
202
  guint32 timescale;
203

204
  /* language */
205
  gchar lang_id[4];             /* ISO 639-2T language code */
206

207
208
209
210
  /* our samples */
  guint32 n_samples;
  QtDemuxSample *samples;
  gboolean all_keyframe;        /* TRUE when all samples are keyframes (no stss) */
211
  guint32 min_duration;         /* duration in timescale of first sample, used for figuring out
212
                                   the framerate, in timescale units */
213

214
215
  /* if we use chunks or samples */
  gboolean sampled;
216
  guint padding;
217
218

  /* video info */
219
220
  gint width;
  gint height;
221
222
223
224
225
  /* aspect ratio */
  gint display_width;
  gint display_height;
  gint par_w;
  gint par_h;
226
227
228
  /* Numerator/denominator framerate */
  gint fps_n;
  gint fps_d;
229
230
  guint16 bits_per_sample;
  guint16 color_table_id;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
231

232
  /* audio info */
233
234
  gdouble rate;
  gint n_channels;
235
236
237
238
  guint samples_per_packet;
  guint samples_per_frame;
  guint bytes_per_packet;
  guint bytes_per_sample;
239
  guint bytes_per_frame;
240
  guint compression;
241
242
243

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

245
246
247
  /* list of buffers to push first */
  GSList *buffers;

248
249
250
251
  /* if we need to clip this buffer. This is only needed for uncompressed
   * data */
  gboolean need_clip;

252
253
254
  /* buffer needs some custom processing, e.g. subtitles */
  gboolean need_process;

255
256
257
258
259
  /* current position */
  guint32 segment_index;
  guint32 sample_index;
  guint64 time_position;        /* in gst time */

260
261
262
  /* the Gst segment we are processing out, used for clipping */
  GstSegment segment;

263
264
265
  /* last GstFlowReturn */
  GstFlowReturn last_ret;

266
267
268
  /* quicktime segments */
  guint32 n_segments;
  QtDemuxSegment *segments;
269
270
  guint32 from_sample;
  guint32 to_sample;
271
272

  gboolean sent_eos;
273
274
  GstTagList *pending_tags;
  gboolean send_global_tags;
275
276

  GstEvent *pending_event;
277
278
279
280
281
282
283
284
285
286

  GstByteReader stco;
  GstByteReader stsz;
  GstByteReader stsc;
  GstByteReader stts;
  GstByteReader stss;
  GstByteReader stps;
  GstByteReader ctts;

  gboolean chunks_are_chunks;
287
  gint64 stbl_index;
288
289
  /* stco */
  guint co_size;
290
291
292
293
294
295
  GstByteReader co_chunk;
  guint32 first_chunk;
  guint32 current_chunk;
  guint32 last_chunk;
  guint32 samples_per_chunk;
  guint32 stco_sample_index;
296
297
298
  /* stsz */
  guint32 sample_size;          /* 0 means variable sizes are stored in stsz */
  /* stsc */
299
  guint32 stsc_index;
300
  guint32 n_samples_per_chunk;
301
302
303
  guint32 stsc_chunk_index;
  guint32 stsc_sample_index;
  guint64 chunk_offset;
304
  /* stts */
305
306
  guint32 stts_index;
  guint32 stts_samples;
307
  guint32 n_sample_times;
308
  guint32 stts_sample_index;
309
310
  guint32 stts_time;
  guint32 stts_duration;
311
  /* stss */
312
  gboolean stss_present;
313
  guint32 n_sample_syncs;
314
  guint32 stss_index;
315
  /* stps */
316
  gboolean stps_present;
317
  guint32 n_sample_partial_syncs;
318
  guint32 stps_index;
319
320
321
  /* ctts */
  gboolean ctts_present;
  guint32 n_composition_times;
322
323
324
325
  guint32 ctts_index;
  guint32 ctts_sample_index;
  guint32 ctts_count;
  gint32 ctts_soffset;
326
327
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
328
329
enum QtDemuxState
{
330
331
332
  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 */
333
  QTDEMUX_STATE_BUFFER_MDAT     /* Buffering the mdat atom */
334
335
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
336
static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
337
static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
338
    guint32 fourcc, GstByteReader * parser);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
339
static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
340

David Schleef's avatar
David Schleef committed
341
static GstStaticPadTemplate gst_qtdemux_sink_template =
342
    GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
343
    GST_PAD_SINK,
344
    GST_PAD_ALWAYS,
345
346
    GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
        "application/x-3gp")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
347
    );
David Schleef's avatar
David Schleef committed
348
349

static GstStaticPadTemplate gst_qtdemux_videosrc_template =
350
GST_STATIC_PAD_TEMPLATE ("video_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
351
352
353
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
David Schleef's avatar
David Schleef committed
354
355

static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
356
GST_STATIC_PAD_TEMPLATE ("audio_%02d",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
357
358
359
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
Artyom Baginski's avatar
Artyom Baginski committed
360

361
362
static GstStaticPadTemplate gst_qtdemux_subsrc_template =
GST_STATIC_PAD_TEMPLATE ("subtitle_%02d",
363
364
365
366
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

367
static GstElementClass *parent_class = NULL;
Artyom Baginski's avatar
Artyom Baginski committed
368

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
369
370
371
static void gst_qtdemux_class_init (GstQTDemuxClass * klass);
static void gst_qtdemux_base_init (GstQTDemuxClass * klass);
static void gst_qtdemux_init (GstQTDemux * quicktime_demux);
372
static void gst_qtdemux_dispose (GObject * object);
373

374
375
static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
static GstIndex *gst_qtdemux_get_index (GstElement * element);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
376
377
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
    GstStateChange transition);
378
379
static gboolean qtdemux_sink_activate (GstPad * sinkpad);
static gboolean qtdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
380
static gboolean qtdemux_sink_activate_push (GstPad * sinkpad, gboolean active);
381
382
383

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

386
static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux, const guint8 * buffer,
387
    guint length);
388
static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
389
    const guint8 * buffer, guint length);
390
static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
391

392
static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
393
    QtDemuxStream * stream, GNode * esds, GstTagList * list);
394
395
static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint32 fourcc, const guint8 * stsd_data,
396
    gchar ** codec_name);
397
398
static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint32 fourcc, const guint8 * data, int len,
399
    gchar ** codec_name);
400
static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux,
401
402
    QtDemuxStream * stream, guint32 fourcc, const guint8 * data,
    gchar ** codec_name);
403
404
static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint32 n);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
405

406
GType
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
407
gst_qtdemux_get_type (void)
Artyom Baginski's avatar
Artyom Baginski committed
408
409
410
{
  static GType qtdemux_type = 0;

411
  if (G_UNLIKELY (!qtdemux_type)) {
Artyom Baginski's avatar
Artyom Baginski committed
412
    static const GTypeInfo qtdemux_info = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
413
414
415
416
417
      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
418
    };
419

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
420
    qtdemux_type =
421
422
        g_type_register_static (GST_TYPE_ELEMENT, "GstQTDemux", &qtdemux_info,
        0);
Artyom Baginski's avatar
Artyom Baginski committed
423
424
425
426
  }
  return qtdemux_type;
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
427
428
static void
gst_qtdemux_base_init (GstQTDemuxClass * klass)
429
430
431
432
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);

  gst_element_class_add_pad_template (element_class,
David Schleef's avatar
David Schleef committed
433
434
435
436
437
      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));
438
439
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&gst_qtdemux_subsrc_template));
440
441
442
443
  gst_element_class_set_details_simple (element_class, "QuickTime demuxer",
      "Codec/Demuxer",
      "Demultiplex a QuickTime file into audio and video streams",
      "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
David Schleef's avatar
David Schleef committed
444

445
  GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");
446
447
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
448
449
static void
gst_qtdemux_class_init (GstQTDemuxClass * klass)
Artyom Baginski's avatar
Artyom Baginski committed
450
451
452
453
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
454
455
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Artyom Baginski's avatar
Artyom Baginski committed
456

457
  parent_class = g_type_class_peek_parent (klass);
458

459
460
  gobject_class->dispose = gst_qtdemux_dispose;

461
462
463
464
  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);

  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
  gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
Artyom Baginski's avatar
Artyom Baginski committed
465
466
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
467
468
static void
gst_qtdemux_init (GstQTDemux * qtdemux)
Artyom Baginski's avatar
Artyom Baginski committed
469
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
470
  qtdemux->sinkpad =
471
      gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
472
473
474
  gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
  gst_pad_set_activatepull_function (qtdemux->sinkpad,
      qtdemux_sink_activate_pull);
475
476
477
478
  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);
479
  gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
480

481
482
  qtdemux->state = QTDEMUX_STATE_INITIAL;
  qtdemux->pullbased = FALSE;
483
  qtdemux->posted_redirect = FALSE;
484
485
486
  qtdemux->neededbytes = 16;
  qtdemux->todrop = 0;
  qtdemux->adapter = gst_adapter_new ();
487
  qtdemux->offset = 0;
488
  qtdemux->first_mdat = -1;
489
  qtdemux->got_moov = FALSE;
490
491
  qtdemux->mdatoffset = GST_CLOCK_TIME_NONE;
  qtdemux->mdatbuffer = NULL;
492
  gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
Artyom Baginski's avatar
Artyom Baginski committed
493
494
}

495
496
497
498
499
500
501
502
503
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;
  }
504
505

  G_OBJECT_CLASS (parent_class)->dispose (object);
506
507
}

508
509
510
511
static void
gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
{
  if (qtdemux->posted_redirect) {
512
    GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
513
514
515
        (_("This file contains no playable streams.")),
        ("no known streams found, a redirect message has been posted"));
  } else {
516
    GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
517
518
519
520
521
        (_("This file contains no playable streams.")),
        ("no known streams found"));
  }
}

522
523
524
525
526
527
528
529
static GstFlowReturn
gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
    GstBuffer ** buf)
{
  GstFlowReturn flow;

  /* Sanity check: catch bogus sizes (fuzzed/broken files) */
  if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
530
    GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
        (_("This file is invalid and cannot be played.")),
        ("atom has bogus size %" G_GUINT64_FORMAT, size));
    return GST_FLOW_ERROR;
  }

  flow = gst_pad_pull_range (qtdemux->sinkpad, offset, size, buf);

  if (G_UNLIKELY (flow != GST_FLOW_OK))
    return flow;

  /* Catch short reads - we don't want any partial atoms */
  if (G_UNLIKELY (GST_BUFFER_SIZE (*buf) < size)) {
    GST_WARNING_OBJECT (qtdemux, "short read: %u < %" G_GUINT64_FORMAT,
        GST_BUFFER_SIZE (*buf), size);
    gst_buffer_unref (*buf);
    *buf = NULL;
    return GST_FLOW_UNEXPECTED;
  }

  return flow;
}

553
#if 0
554
static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
555
556
gst_qtdemux_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
    GstFormat * dest_format, gint64 * dest_value)
557
558
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
559
  QtDemuxStream *stream = gst_pad_get_element_private (pad);
560

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
561
  if (stream->subtype == GST_MAKE_FOURCC ('v', 'i', 'd', 'e') &&
562
563
564
565
566
567
      (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES))
    return FALSE;

  switch (src_format) {
    case GST_FORMAT_TIME:
      switch (*dest_format) {
568
569
570
571
572
573
574
575
576
        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;
577
578
579
580
      }
      break;
    case GST_FORMAT_BYTES:
      switch (*dest_format) {
581
582
583
584
585
586
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
587
588
589
590
      }
      break;
    case GST_FORMAT_DEFAULT:
      switch (*dest_format) {
591
592
593
594
595
596
        case GST_FORMAT_TIME:
          *dest_value = src_value * 1;  /* FIXME */
          break;
        default:
          res = FALSE;
          break;
597
598
599
600
601
602
603
604
      }
      break;
    default:
      res = FALSE;
  }

  return res;
}
605
#endif
606
607

static const GstQueryType *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
608
gst_qtdemux_get_src_query_types (GstPad * pad)
609
610
611
{
  static const GstQueryType src_types[] = {
    GST_QUERY_POSITION,
Wim Taymans's avatar
Wim Taymans committed
612
    GST_QUERY_DURATION,
613
    GST_QUERY_SEEKING,
614
615
616
617
618
619
    0
  };

  return src_types;
}

620
621
622
623
624
625
626
627
628
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) {
629
      *duration = gst_util_uint64_scale (qtdemux->duration,
630
631
632
633
634
635
          GST_SECOND, qtdemux->timescale);
    }
  }
  return res;
}

636
static gboolean
637
gst_qtdemux_handle_src_query (GstPad * pad, GstQuery * query)
638
{
639
640
  gboolean res = FALSE;
  GstQTDemux *qtdemux = GST_QTDEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
641

642
643
  GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));

644
  switch (GST_QUERY_TYPE (query)) {
645
    case GST_QUERY_POSITION:
646
647
648
      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
649
650
651
        res = TRUE;
      }
      break;
652
653
    case GST_QUERY_DURATION:{
      GstFormat fmt;
654

655
656
657
      gst_query_parse_duration (query, &fmt, NULL);
      if (fmt == GST_FORMAT_TIME) {
        gint64 duration = -1;
658

659
660
661
662
663
664
665
        gst_qtdemux_get_duration (qtdemux, &duration);
        if (duration > 0) {
          gst_query_set_duration (query, GST_FORMAT_TIME, duration);
          res = TRUE;
        }
      }
      break;
666
    }
667
668
    case GST_QUERY_SEEKING:{
      GstFormat fmt;
669
      gboolean seekable;
670
671
672
673
674
675

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

        gst_qtdemux_get_duration (qtdemux, &duration);
676
677
678
679
680
681
682
        seekable = TRUE;
        if (!qtdemux->pullbased) {
          GstQuery *q;

          /* we might be able with help from upstream */
          seekable = FALSE;
          q = gst_query_new_seeking (GST_FORMAT_BYTES);
683
          if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
684
685
686
687
688
689
            gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
            GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
          }
          gst_query_unref (q);
        }
        gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
690
691
        res = TRUE;
      }
692
      break;
693
    }
694
    default:
695
      res = gst_pad_query_default (pad, query);
696
697
698
      break;
  }

699
700
  gst_object_unref (qtdemux);

701
702
703
  return res;
}

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
static void
gst_qtdemux_push_tags (GstQTDemux * qtdemux, QtDemuxStream * stream)
{
  if (G_LIKELY (stream->pad)) {
    GST_DEBUG_OBJECT (qtdemux, "Checking pad %s:%s for tags",
        GST_DEBUG_PAD_NAME (stream->pad));

    if (G_UNLIKELY (stream->pending_tags)) {
      GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
          stream->pending_tags);
      gst_pad_push_event (stream->pad,
          gst_event_new_tag (stream->pending_tags));
      stream->pending_tags = NULL;
    }

    if (G_UNLIKELY (stream->send_global_tags && qtdemux->tag_list)) {
      GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
          qtdemux->tag_list);
      gst_pad_push_event (stream->pad,
          gst_event_new_tag (gst_tag_list_copy (qtdemux->tag_list)));
      stream->send_global_tags = FALSE;
    }
  }
}

729
/* push event on all source pads; takes ownership of the event */
730
static void
731
gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
732
733
{
  guint n;
734
735
  gboolean pushed_sucessfully = FALSE;
  GstEventType etype = GST_EVENT_TYPE (event);
736
737
738
739
740

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

  for (n = 0; n < qtdemux->n_streams; n++) {
741
742
    GstPad *pad;

743
744
745
746
    if ((pad = qtdemux->streams[n]->pad)) {
      if (gst_pad_push_event (pad, gst_event_ref (event)))
        pushed_sucessfully = TRUE;
    }
747
748
  }
  gst_event_unref (event);
749
750
751
752
753

  /* if it is EOS and nothing is pushed, post an error */
  if (!pushed_sucessfully && etype == GST_EVENT_EOS) {
    gst_qtdemux_post_no_playable_stream_error (qtdemux);
  }
754
755
}

756
757
758
759
760
761
762
763
764
765
/* push a pending newsegment event, if any from the streaming thread */
static void
gst_qtdemux_push_pending_newsegment (GstQTDemux * qtdemux)
{
  if (qtdemux->pending_newsegment) {
    gst_qtdemux_push_event (qtdemux, qtdemux->pending_newsegment);
    qtdemux->pending_newsegment = NULL;
  }
}

766
767
768
769
770
771
772
773
774
775
776
777
778
779
typedef struct
{
  guint64 media_time;
} FindData;

static gint
find_func (QtDemuxSample * s1, guint64 * media_time, gpointer user_data)
{
  if (s1->timestamp > *media_time)
    return 1;

  return -1;
}

780
781
/* find the index of the sample that includes the data for @media_time using a
 * binary search
782
 *
783
 * Returns the index of the sample.
784
 */
785
786
787
static guint32
gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint64 media_time)
788
{
789
790
  QtDemuxSample *result;
  guint32 index;
791

792
793
794
  /* convert media_time to mov format */
  media_time = gst_util_uint64_scale (media_time, str->timescale, GST_SECOND);

795
796
797
  result = gst_util_array_binary_search (str->samples, str->n_samples,
      sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
      GST_SEARCH_MODE_BEFORE, &media_time, NULL);
798

799
800
801
802
803
804
  if (G_LIKELY (result))
    index = result - str->samples;
  else
    index = 0;

  return index;
805
}
806

807
808
809
810
811
812
813
814
815
816
817
818
/* find the index of the sample that includes the data for @media_time using a
 * linear search
 *
 * Returns the index of the sample.
 */
static guint32
gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint64 media_time)
{
  QtDemuxSample *result = str->samples;
  guint32 index = 0;

819
820
821
  /* convert media_time to mov format */
  media_time = gst_util_uint64_scale (media_time, str->timescale, GST_SECOND);

822
823
824
825
826
  if (media_time == result->timestamp)
    return index;

  result++;
  while (index < str->n_samples - 1) {
827
    if (!qtdemux_parse_samples (qtdemux, str, index + 1))
828
829
      goto parse_failed;

830
831
    if (media_time < result->timestamp)
      break;
832

833
834
835
836
    index++;
    result++;
  }
  return index;
837
838
839
840
841
842
843

  /* ERRORS */
parse_failed:
  {
    GST_LOG_OBJECT (qtdemux, "Parsing of index %u failed!", index + 1);
    return -1;
  }
844
845
}

846
847
848
849
850
851
852
853
854
/* 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)
{
855
856
857
858
859
860
  guint32 new_index = index;

  if (index >= str->n_samples) {
    new_index = str->n_samples;
    goto beach;
  }
861
862

  /* all keyframes, return index */
863
864
865
866
  if (str->all_keyframe) {
    new_index = index;
    goto beach;
  }
867
868
869

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

873
    if (new_index == 0)
874
875
      break;

876
    new_index--;
877
  }
878
879
880
881
882
883

beach:
  GST_DEBUG_OBJECT (qtdemux, "searching for keyframe index before index %u "
      "gave %u", index, new_index);

  return new_index;
884
885
886
887
888
889
890
891
892
893
894
895
896
}

/* 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;

897
898
899
  GST_LOG_OBJECT (qtdemux, "finding segment for %" GST_TIME_FORMAT,
      GST_TIME_ARGS (time_position));

900
901
902
903
904
905
  /* 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];

906
907
908
909
    GST_LOG_OBJECT (qtdemux,
        "looking at segment %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
        GST_TIME_ARGS (segment->time), GST_TIME_ARGS (segment->stop_time));

910
911
912
913
914
915
916
917
918
919
920
921
922
    /* For the last segment we include stop_time in the last segment */
    if (i < stream->n_segments - 1) {
      if (segment->time <= time_position && time_position < segment->stop_time) {
        GST_LOG_OBJECT (qtdemux, "segment %d matches", i);
        seg_idx = i;
        break;
      }
    } else {
      if (segment->time <= time_position && time_position <= segment->stop_time) {
        GST_LOG_OBJECT (qtdemux, "segment %d matches", i);
        seg_idx = i;
        break;
      }
923
    }
924
  }
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
  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;
945
946
947
  /* Each time we move in the stream we store the position where we are 
   * starting from */
  str->from_sample = index;
948
  str->discont = TRUE;
949
950
}

951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
static void
gst_qtdemux_adjust_seek (GstQTDemux * qtdemux, gint64 desired_time,
    gint64 * key_time, gint64 * key_offset)
{
  guint64 min_offset;
  gint64 min_byte_offset = -1;
  gint n;

  min_offset = desired_time;

  /* 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;
    guint32 index, kindex;
    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_time);
    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_time - 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);

    /* find previous keyframe */
    kindex = gst_qtdemux_find_keyframe (qtdemux, str, index);

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

      /* get timestamp of keyframe */
1002
1003
1004
      media_time =
          gst_util_uint64_scale (str->samples[kindex].timestamp, GST_SECOND,
          str->timescale);
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
      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;
      }
    }

    if (min_byte_offset < 0 || str->samples[index].offset < min_byte_offset)
      min_byte_offset = str->samples[index].offset;
  }

  if (key_time)
    *key_time = min_offset;
  if (key_offset)
    *key_offset = min_byte_offset;
}

static gboolean
gst_qtdemux_convert_seek (GstPad * pad, GstFormat * format,
    GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop)
{
  gboolean res;
  GstFormat fmt;

  g_return_val_if_fail (format != NULL, FALSE);
  g_return_val_if_fail (cur != NULL, FALSE);
  g_return_val_if_fail (stop != NULL, FALSE);

  if (*format == GST_FORMAT_TIME)
    return TRUE;

  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)
    *format = GST_FORMAT_TIME;

  return res;
}

/* perform seek in push based mode:
   find BYTE position to move to based on time and delegate to upstream
*/
static gboolean
gst_qtdemux_do_push_seek (GstQTDemux * qtdemux, GstPad * pad, GstEvent * event)
{
  gdouble rate;
  GstFormat format;
  GstSeekFlags flags;
  GstSeekType cur_type, stop_type;
  gint64 cur, stop;
  gboolean res;
  gint64 byte_cur;

  GST_DEBUG_OBJECT (qtdemux, "doing push-based seek");

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

1078
  /* FIXME, always play to the end */
1079
1080
  stop = -1;

1081
1082
1083
1084
  /* only forward streaming and seeking is possible */
  if (rate <= 0)
    goto unsupported_seek;

1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
  /* convert to TIME if needed and possible */
  if (!gst_qtdemux_convert_seek (pad, &format, cur_type, &cur,
          stop_type, &stop))
    goto no_format;

  /* find reasonable corresponding BYTE position,
   * also try to mind about keyframes, since we can not go back a bit for them
   * later on */
  gst_qtdemux_adjust_seek (qtdemux, cur, NULL, &byte_cur);

  if (byte_cur == -1)
    goto abort_seek;

  GST_DEBUG_OBJECT (qtdemux, "Pushing BYTE seek rate %g, "
      "start %" G_GINT64_FORMAT ", stop %" G_GINT64_FORMAT, rate, byte_cur,
      stop);
1101

1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
  if (!(flags & GST_SEEK_FLAG_KEY_UNIT)) {
    GST_DEBUG_OBJECT (qtdemux,
        "Requested seek time: %" GST_TIME_FORMAT ", calculated seek offset: %"
        G_GUINT64_FORMAT, GST_TIME_ARGS (cur), byte_cur);
    GST_OBJECT_LOCK (qtdemux);
    qtdemux->requested_seek_time = cur;
    qtdemux->seek_offset = byte_cur;
    GST_OBJECT_UNLOCK (qtdemux);
  }

1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
  /* BYTE seek event */
  event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, byte_cur,
      stop_type, stop);
  res = gst_pad_push_event (qtdemux->sinkpad, event);

  return res;

  /* ERRORS */
abort_seek:
  {
    GST_DEBUG_OBJECT (qtdemux, "could not determine byte position to seek to, "
        "seek aborted.");
    return FALSE;
  }
unsupported_seek:
  {
    GST_DEBUG_OBJECT (qtdemux, "unsupported seek, seek aborted.");
    return FALSE;
  }
no_format:
  {
    GST_DEBUG_OBJECT (qtdemux, "unsupported format given, seek aborted.");
    return FALSE;
  }
}

1138
1139
/* perform the seek.
 *
1140
1141
1142
1143
1144
1145
 * 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
1146
 * segments. Ideally we want to move to the previous keyframe in
1147
1148
1149
1150
 * 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.
1151
1152
 *
 * Called with STREAM_LOCK
1153
 */
1154
static gboolean
1155
gst_qtdemux_perform_seek (GstQTDemux * qtdemux, GstSegment * segment)
1156
{
1157
  gint64 desired_offset;
1158
  gint n;
1159

1160
  desired_offset = segment->last_stop;
1161

1162
1163
  GST_DEBUG_OBJECT (qtdemux, "seeking to %" GST_TIME_FORMAT,
      GST_TIME_ARGS (desired_offset));
1164

1165
  if (segment->flags & GST_SEEK_FLAG_KEY_UNIT) {
1166
1167
1168
    gint64 min_offset;

    gst_qtdemux_adjust_seek (qtdemux, desired_offset, &min_offset, NULL);
1169
    GST_DEBUG_OBJECT (qtdemux, "keyframe seek, align to %"
1170
        GST_TIME_FORMAT, GST_TIME_ARGS (min_offset));
1171
    desired_offset = min_offset;
1172
1173
1174
1175
1176
  }

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

1178
    stream->time_position = desired_offset;
1179
    stream->sample_index = -1;
1180
    stream->segment_index = -1;
1181
    stream->last_ret = GST_FLOW_OK;
1182
    stream->sent_eos = FALSE;
1183
  }
1184
1185
  segment->last_stop = desired_offset;
  segment->time = desired_offset;
1186

1187
  /* we stop at the end */
1188
1189
1190
  if (segment->stop == -1)
    segment->stop = segment->duration;

1191
1192
  return TRUE;
}
1193

1194
/* do a seek in pull based mode */
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
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 update;
  GstSegment seeksegment;
1206
  int i;
1207
1208
1209
1210
1211
1212
1213
1214
1215

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

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