qtdemux.c 462 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>
8
 * Copyright (C) <2013> Sreerenj Balachandran <sreerenj.balachandran@intel.com>
9
10
 * Copyright (C) <2013> Intel Corporation
 * Copyright (C) <2014> Centricular Ltd
11
 * Copyright (C) <2015> YouView TV Ltd.
12
 * Copyright (C) <2016> British Broadcasting Corporation
13
 *
Artyom Baginski's avatar
Artyom Baginski committed
14
15
16
17
18
19
20
21
22
23
24
25
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
26
27
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Artyom Baginski's avatar
Artyom Baginski committed
28
29
 */

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

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

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

55
#include <glib/gprintf.h>
56
#include <gst/tag/tag.h>
Wim Taymans's avatar
Wim Taymans committed
57
#include <gst/audio/audio.h>
58
#include <gst/video/video.h>
59

60
#include "qtatomparser.h"
61
62
#include "qtdemux_types.h"
#include "qtdemux_dump.h"
63
#include "fourcc.h"
64
#include "descriptors.h"
65
#include "qtdemux_lang.h"
66
#include "qtdemux.h"
67
#include "qtpalette.h"
68

69
70
71
#include "gst/riff/riff-media.h"
#include "gst/riff/riff-read.h"

72
73
#include <gst/pbutils/pbutils.h>

74
#include <stdio.h>
75
#include <stdlib.h>
Artyom Baginski's avatar
Artyom Baginski committed
76
#include <string.h>
77

78
79
80
#include <math.h>
#include <gst/math-compat.h>

81
82
83
#ifdef HAVE_ZLIB
# include <zlib.h>
#endif
84

85
86
87
/* max. size considered 'sane' for non-mdat atoms */
#define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)

88
89
90
/* if the sample index is larger than this, something is likely wrong */
#define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (50*1024*1024)

91
92
93
94
95
96
/* For converting qt creation times to unix epoch times */
#define QTDEMUX_SECONDS_PER_DAY (60 * 60 * 24)
#define QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970 17
#define QTDEMUX_SECONDS_FROM_1904_TO_1970 (((1970 - 1904) * (guint64) 365 + \
    QTDEMUX_LEAP_YEARS_FROM_1904_TO_1970) * QTDEMUX_SECONDS_PER_DAY)

97
98
#define QTDEMUX_TREE_NODE_FOURCC(n) (QT_FOURCC(((guint8 *) (n)->data) + 4))

99
#define STREAM_IS_EOS(s) (s->time_position == GST_CLOCK_TIME_NONE)
100

101
102
#define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) )

103
GST_DEBUG_CATEGORY (qtdemux_debug);
104

105
typedef struct _QtDemuxSegment QtDemuxSegment;
106
typedef struct _QtDemuxSample QtDemuxSample;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
107

108
109
typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
110
111
struct _QtDemuxSample
{
112
  guint32 size;
113
  gint32 pts_offset;            /* Add this value to timestamp to get the pts */
114
  guint64 offset;
115
116
  guint64 timestamp;            /* DTS In mov time */
  guint32 duration;             /* In mov time */
117
  gboolean keyframe;            /* TRUE when this packet is a keyframe */
118
119
};

120
121
122
123
124
125
126
/* Macros for converting to/from timescale */
#define QTSTREAMTIME_TO_GSTTIME(stream, value) (gst_util_uint64_scale((value), GST_SECOND, (stream)->timescale))
#define GSTTIME_TO_QTSTREAMTIME(stream, value) (gst_util_uint64_scale((value), (stream)->timescale, GST_SECOND))

#define QTTIME_TO_GSTTIME(qtdemux, value) (gst_util_uint64_scale((value), GST_SECOND, (qtdemux)->timescale))
#define GSTTIME_TO_QTTIME(qtdemux, value) (gst_util_uint64_scale((value), (qtdemux)->timescale, GST_SECOND))

127
/* timestamp is the DTS */
128
#define QTSAMPLE_DTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp))
129
/* timestamp + offset + cslg_shift is the outgoing PTS */
Nicolas Dufresne's avatar
Nicolas Dufresne committed
130
#define QTSAMPLE_PTS(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (stream)->cslg_shift + (sample)->pts_offset))
131
132
/* timestamp + offset is the PTS used for internal seek calcuations */
#define QTSAMPLE_PTS_NO_CSLG(stream,sample) (QTSTREAMTIME_TO_GSTTIME((stream), (sample)->timestamp + (sample)->pts_offset))
133
/* timestamp + duration - dts is the duration */
134
#define QTSAMPLE_DUR_DTS(stream, sample, dts) (QTSTREAMTIME_TO_GSTTIME ((stream), (sample)->timestamp + (sample)->duration) - (dts))
135
136
137

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

138
139
140
141
142
143
144
145
146
147
148
/*
 * 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
149
 * and are basically tuples of media_time/duration/rate entries. We can have
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
 * 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... |
 *         '-----------------------------------------------------------'
Stefan Kost's avatar
Stefan Kost committed
170
 *         0              1              2              3              4
171
172
173
174
175
176
 *           .------------^              ^   .----------^              ^
 *          /              .-------------'  /       .------------------'
 *         /              /          .-----'       /
 *         .--------------.         .--------------.
 *         | segment 1    |         | segment 2    |
 *         '--------------'         '--------------'
Stefan Kost's avatar
Stefan Kost committed
177
 *
178
 * The challenge here is to cut out the right pieces of the track for each of
179
180
 * the playback segments. This fortunately can easily be done with the SEGMENT
 * events of GStreamer.
181
182
183
184
185
186
187
188
 *
 * 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.
Stefan Kost's avatar
Stefan Kost committed
189
 *
190
191
192
193
194
195
 * 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.
 */

196
197
198
struct _QtDemuxSegment
{
  /* global time and duration, all gst time */
199
200
201
  GstClockTime time;
  GstClockTime stop_time;
  GstClockTime duration;
202
  /* media time of trak, all gst time */
203
204
  GstClockTime media_start;
  GstClockTime media_stop;
205
  gdouble rate;
206
207
  /* Media start time in trak timescale units */
  guint32 trak_media_start;
208
209
};

210
211
#define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)

212
213
214
215
216
217
218
/* Used with fragmented MP4 files (mfra atom) */
typedef struct
{
  GstClockTime ts;
  guint64 moof_offset;
} QtDemuxRandomAccessEntry;

219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
typedef struct _QtDemuxStreamStsdEntry
{
  GstCaps *caps;
  guint32 fourcc;
  gboolean sparse;

  /* video info */
  gint width;
  gint height;
  gint par_w;
  gint par_h;
  /* Numerator/denominator framerate */
  gint fps_n;
  gint fps_d;
  GstVideoColorimetry colorimetry;
  guint16 bits_per_sample;
  guint16 color_table_id;
  GstMemory *rgb8_palette;
  guint interlace_mode;
  guint field_order;

  /* audio info */
  gdouble rate;
  gint n_channels;
  guint samples_per_packet;
  guint samples_per_frame;
  guint bytes_per_packet;
  guint bytes_per_sample;
  guint bytes_per_frame;
  guint compression;

  /* if we use chunks or samples */
  gboolean sampled;
  guint padding;

} QtDemuxStreamStsdEntry;

#define CUR_STREAM(s) (&((s)->stsd_entries[(s)->cur_stsd_entry_index]))

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
258
259
struct _QtDemuxStream
{
260
261
  GstPad *pad;

262
263
264
265
  QtDemuxStreamStsdEntry *stsd_entries;
  guint stsd_entries_length;
  guint cur_stsd_entry_index;

266
  /* stream type */
267
268
  guint32 subtype;

269
270
271
272
  gboolean new_caps;            /* If TRUE, caps need to be generated (by
                                 * calling _configure_stream()) This happens
                                 * for MSS and fragmented streams */

273
  gboolean new_stream;          /* signals that a stream_start is required */
274
275
276
277
  gboolean on_keyframe;         /* if this stream last pushed buffer was a
                                 * keyframe. This is important to identify
                                 * where to stop pushing buffers after a
                                 * segment stop time */
278

279
280
281
  /* if the stream has a redirect URI in its headers, we store it here */
  gchar *redirect_uri;

282
283
284
  /* track id */
  guint track_id;

285
  /* duration/scale */
286
  guint64 duration;             /* in timescale units */
287
  guint32 timescale;
288

289
  /* language */
290
  gchar lang_id[4];             /* ISO 639-2T language code */
291

292
293
294
295
  /* our samples */
  guint32 n_samples;
  QtDemuxSample *samples;
  gboolean all_keyframe;        /* TRUE when all samples are keyframes (no stss) */
296
  guint32 first_duration;       /* duration in timescale of first sample, used for figuring out
297
                                   the framerate */
298
299
300
  guint32 n_samples_moof;       /* sample count in a moof */
  guint64 duration_moof;        /* duration in timescale of a moof, used for figure out
                                 * the framerate of fragmented format stream */
301
  guint64 duration_last_moof;
302
303
304
305
306
307
308

  guint32 offset_in_sample;     /* Offset in the current sample, used for
                                 * streams which have got exceedingly big
                                 * sample size (such as 24s of raw audio).
                                 * Only used when max_buffer_size is non-NULL */
  guint32 max_buffer_size;      /* Maximum allowed size for output buffers.
                                 * Currently only set for raw audio streams*/
309

310
  /* video info */
311
312
313
  /* aspect ratio */
  gint display_width;
  gint display_height;
314

315
316
317
318
319
  /* allocation */
  gboolean use_allocator;
  GstAllocator *allocator;
  GstAllocationParams params;

320
321
  gsize alignment;

322
323
  /* when a discontinuity is pending */
  gboolean discont;
324

325
326
327
  /* list of buffers to push first */
  GSList *buffers;

328
329
330
331
  /* if we need to clip this buffer. This is only needed for uncompressed
   * data */
  gboolean need_clip;

332
333
334
  /* buffer needs some custom processing, e.g. subtitles */
  gboolean need_process;

335
336
337
  /* current position */
  guint32 segment_index;
  guint32 sample_index;
338
  GstClockTime time_position;   /* in gst time */
339
  guint64 accumulated_base;
340

341
342
343
  /* the Gst segment we are processing out, used for clipping */
  GstSegment segment;

344
345
346
  /* quicktime segments */
  guint32 n_segments;
  QtDemuxSegment *segments;
347
  gboolean dummy_segment;
348
349
  guint32 from_sample;
  guint32 to_sample;
350
351

  gboolean sent_eos;
352
  GstTagList *stream_tags;
353
  gboolean send_global_tags;
354
355

  GstEvent *pending_event;
356
357
358
359
360
361
362
363
364

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

365
  gboolean chunks_are_samples;  /* TRUE means treat chunks as samples */
366
  gint64 stbl_index;
367
368
  /* stco */
  guint co_size;
369
370
371
372
373
  GstByteReader co_chunk;
  guint32 first_chunk;
  guint32 current_chunk;
  guint32 last_chunk;
  guint32 samples_per_chunk;
374
  guint32 stsd_sample_description_id;
375
  guint32 stco_sample_index;
376
377
378
  /* stsz */
  guint32 sample_size;          /* 0 means variable sizes are stored in stsz */
  /* stsc */
379
  guint32 stsc_index;
380
  guint32 n_samples_per_chunk;
381
382
383
  guint32 stsc_chunk_index;
  guint32 stsc_sample_index;
  guint64 chunk_offset;
384
  /* stts */
385
386
  guint32 stts_index;
  guint32 stts_samples;
387
  guint32 n_sample_times;
388
  guint32 stts_sample_index;
389
  guint64 stts_time;
390
  guint32 stts_duration;
391
  /* stss */
392
  gboolean stss_present;
393
  guint32 n_sample_syncs;
394
  guint32 stss_index;
395
  /* stps */
396
  gboolean stps_present;
397
  guint32 n_sample_partial_syncs;
398
  guint32 stps_index;
399
400
401
  QtDemuxRandomAccessEntry *ra_entries;
  guint n_ra_entries;

402
403
  const QtDemuxRandomAccessEntry *pending_seek;

404
405
406
  /* ctts */
  gboolean ctts_present;
  guint32 n_composition_times;
407
408
409
410
  guint32 ctts_index;
  guint32 ctts_sample_index;
  guint32 ctts_count;
  gint32 ctts_soffset;
411

Nicolas Dufresne's avatar
Nicolas Dufresne committed
412
413
414
  /* cslg */
  guint32 cslg_shift;

415
416
417
418
419
  /* fragmented */
  gboolean parsed_trex;
  guint32 def_sample_duration;
  guint32 def_sample_size;
  guint32 def_sample_flags;
420
421

  gboolean disabled;
422
423
424
425

  /* stereoscopic video streams */
  GstVideoMultiviewMode multiview_mode;
  GstVideoMultiviewFlags multiview_flags;
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442

  /* protected streams */
  gboolean protected;
  guint32 protection_scheme_type;
  guint32 protection_scheme_version;
  gpointer protection_scheme_info;      /* specific to the protection scheme */
  GQueue protection_scheme_event_queue;
};

/* Contains properties and cryptographic info for a set of samples from a
 * track protected using Common Encryption (cenc) */
struct _QtDemuxCencSampleSetInfo
{
  GstStructure *default_properties;

  /* @crypto_info holds one GstStructure per sample */
  GPtrArray *crypto_info;
443
444
};

445
446
static const gchar *
qt_demux_state_string (enum QtDemuxState state)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
447
{
448
449
450
451
452
453
454
455
456
457
458
459
460
  switch (state) {
    case QTDEMUX_STATE_INITIAL:
      return "<INITIAL>";
    case QTDEMUX_STATE_HEADER:
      return "<HEADER>";
    case QTDEMUX_STATE_MOVIE:
      return "<MOVIE>";
    case QTDEMUX_STATE_BUFFER_MDAT:
      return "<BUFFER_MDAT>";
    default:
      return "<UNKNOWN>";
  }
}
461

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
462
static GNode *qtdemux_tree_get_child_by_type (GNode * node, guint32 fourcc);
463
static GNode *qtdemux_tree_get_child_by_type_full (GNode * node,
464
    guint32 fourcc, GstByteReader * parser);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
465
static GNode *qtdemux_tree_get_sibling_by_type (GNode * node, guint32 fourcc);
466
467
static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
    guint32 fourcc, GstByteReader * parser);
468

469
470
static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);

David Schleef's avatar
David Schleef committed
471
static GstStaticPadTemplate gst_qtdemux_sink_template =
472
    GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
473
    GST_PAD_SINK,
474
    GST_PAD_ALWAYS,
475
476
    GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
        "application/x-3gp")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
477
    );
David Schleef's avatar
David Schleef committed
478
479

static GstStaticPadTemplate gst_qtdemux_videosrc_template =
Wim Taymans's avatar
Wim Taymans committed
480
GST_STATIC_PAD_TEMPLATE ("video_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
481
482
483
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
David Schleef's avatar
David Schleef committed
484
485

static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
Wim Taymans's avatar
Wim Taymans committed
486
GST_STATIC_PAD_TEMPLATE ("audio_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
487
488
489
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
Artyom Baginski's avatar
Artyom Baginski committed
490

491
static GstStaticPadTemplate gst_qtdemux_subsrc_template =
Wim Taymans's avatar
Wim Taymans committed
492
GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
493
494
495
496
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
497
498
#define gst_qtdemux_parent_class parent_class
G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
Artyom Baginski's avatar
Artyom Baginski committed
499

500
static void gst_qtdemux_dispose (GObject * object);
501

502
503
static guint32
gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
504
    GstClockTime media_time);
505
506
507
508
static guint32
gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
    QtDemuxStream * str, gint64 media_offset);

509
#if 0
510
511
static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
static GstIndex *gst_qtdemux_get_index (GstElement * element);
512
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
513
514
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
    GstStateChange transition);
Wim Taymans's avatar
Wim Taymans committed
515
static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
Wim Taymans's avatar
Wim Taymans committed
516
517
static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
    GstObject * parent, GstPadMode mode, gboolean active);
518
519

static void gst_qtdemux_loop (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
520
521
522
523
static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
    GstBuffer * inbuf);
static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
Thiago Santos's avatar
Thiago Santos committed
524
static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
525
526
static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
    QtDemuxStream * stream);
527
528
static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
    QtDemuxStream * stream);
529
530
static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
    gboolean force);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
531

532
533
static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
    const guint8 * buffer, guint length);
534
static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
535
    const guint8 * buffer, guint length);
536
static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
Thiago Santos's avatar
Thiago Santos committed
537
538
static void qtdemux_parse_udta (GstQTDemux * qtdemux, GstTagList * taglist,
    GNode * udta);
539

540
static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
541
542
    QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
    GstTagList * list);
543
static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
544
545
    QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
    const guint8 * stsd_entry_data, gchar ** codec_name);
546
static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
547
548
549
550
    QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
    const guint8 * data, int len, gchar ** codec_name);
static GstCaps *qtdemux_sub_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
    QtDemuxStreamStsdEntry * entry, guint32 fourcc, const guint8 * data,
551
    gchar ** codec_name);
552
static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
553
554
    QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
    const guint8 * stsd_entry_data, gchar ** codec_name);
555

556
557
static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint32 n);
558
static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
559
560
static void gst_qtdemux_stream_free (GstQTDemux * qtdemux,
    QtDemuxStream * stream);
Thiago Santos's avatar
Thiago Santos committed
561
562
static void gst_qtdemux_stream_clear (GstQTDemux * qtdemux,
    QtDemuxStream * stream);
Arnaud Vrac's avatar
Arnaud Vrac committed
563
static void gst_qtdemux_remove_stream (GstQTDemux * qtdemux, int index);
564
static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
565
566
static void qtdemux_do_allocation (GstQTDemux * qtdemux,
    QtDemuxStream * stream);
567
568
569
570
571
static gboolean gst_qtdemux_activate_segment (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint32 seg_idx, GstClockTime offset);
static gboolean gst_qtdemux_stream_update_segment (GstQTDemux * qtdemux,
    QtDemuxStream * stream, gint seg_idx, GstClockTime offset,
    GstClockTime * _start, GstClockTime * _stop);
572
573
static void gst_qtdemux_send_gap_for_segment (GstQTDemux * demux,
    QtDemuxStream * stream, gint segment_index, GstClockTime pos);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
574

575
static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
576
static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
577

578
579
580
581
static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);

static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint sample_index);
582
583
static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
    const gchar * id);
584
static void qtdemux_gst_structure_free (GstStructure * gststructure);
585

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
586
587
static void
gst_qtdemux_class_init (GstQTDemuxClass * klass)
Artyom Baginski's avatar
Artyom Baginski committed
588
589
590
591
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
592
593
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Artyom Baginski's avatar
Artyom Baginski committed
594

595
  parent_class = g_type_class_peek_parent (klass);
596

597
598
  gobject_class->dispose = gst_qtdemux_dispose;

599
  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
600
#if 0
601
602
  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
  gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
603
#endif
604
605

  gst_tag_register_musicbrainz_tags ();
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
606

607
608
609
610
611
612
613
614
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_qtdemux_sink_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_qtdemux_videosrc_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_qtdemux_audiosrc_template);
  gst_element_class_add_static_pad_template (gstelement_class,
      &gst_qtdemux_subsrc_template);
615
  gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
616
617
618
619
620
621
      "Codec/Demuxer",
      "Demultiplex a QuickTime file into audio and video streams",
      "David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");

  GST_DEBUG_CATEGORY_INIT (qtdemux_debug, "qtdemux", 0, "qtdemux plugin");

Artyom Baginski's avatar
Artyom Baginski committed
622
623
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
624
static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
625
gst_qtdemux_init (GstQTDemux * qtdemux)
Artyom Baginski's avatar
Artyom Baginski committed
626
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
627
  qtdemux->sinkpad =
628
      gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
629
  gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
Wim Taymans's avatar
Wim Taymans committed
630
631
  gst_pad_set_activatemode_function (qtdemux->sinkpad,
      qtdemux_sink_activate_mode);
632
633
  gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
  gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
634
  gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
635

636
637
  qtdemux->state = QTDEMUX_STATE_INITIAL;
  qtdemux->pullbased = FALSE;
638
  qtdemux->posted_redirect = FALSE;
639
640
641
  qtdemux->neededbytes = 16;
  qtdemux->todrop = 0;
  qtdemux->adapter = gst_adapter_new ();
642
  qtdemux->offset = 0;
643
  qtdemux->first_mdat = -1;
644
  qtdemux->got_moov = FALSE;
645
  qtdemux->mdatoffset = -1;
646
  qtdemux->mdatbuffer = NULL;
647
  qtdemux->restoredata_buffer = NULL;
648
  qtdemux->restoredata_offset = -1;
649
  qtdemux->fragment_start = -1;
650
  qtdemux->fragment_start_offset = -1;
651
652
653
  qtdemux->media_caps = NULL;
  qtdemux->exposed = FALSE;
  qtdemux->mss_mode = FALSE;
654
  qtdemux->pending_newsegment = NULL;
655
  qtdemux->upstream_format_is_time = FALSE;
656
657
  qtdemux->have_group_id = FALSE;
  qtdemux->group_id = G_MAXUINT;
658
659
660
  qtdemux->cenc_aux_info_offset = 0;
  qtdemux->cenc_aux_info_sizes = NULL;
  qtdemux->cenc_aux_sample_count = 0;
661
662
  qtdemux->protection_system_ids = NULL;
  g_queue_init (&qtdemux->protection_event_queue);
663
  gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
664
665
  qtdemux->tag_list = gst_tag_list_new_empty ();
  gst_tag_list_set_scope (qtdemux->tag_list, GST_TAG_SCOPE_GLOBAL);
Thiago Santos's avatar
Thiago Santos committed
666
  qtdemux->flowcombiner = gst_flow_combiner_new ();
Wim Taymans's avatar
Wim Taymans committed
667
668

  GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
Artyom Baginski's avatar
Artyom Baginski committed
669
670
}

671
672
673
674
675
676
677
678
679
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;
  }
680
  gst_tag_list_unref (qtdemux->tag_list);
Thiago Santos's avatar
Thiago Santos committed
681
  gst_flow_combiner_free (qtdemux->flowcombiner);
682
683
684
  g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
      NULL);
  g_queue_clear (&qtdemux->protection_event_queue);
685

686
687
688
  g_free (qtdemux->cenc_aux_info_sizes);
  qtdemux->cenc_aux_info_sizes = NULL;

689
  G_OBJECT_CLASS (parent_class)->dispose (object);
690
691
}

692
693
694
695
static void
gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
{
  if (qtdemux->posted_redirect) {
696
    GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
697
698
699
        (_("This file contains no playable streams.")),
        ("no known streams found, a redirect message has been posted"));
  } else {
700
    GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
701
702
703
704
705
        (_("This file contains no playable streams.")),
        ("no known streams found"));
  }
}

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
706
707
708
static GstBuffer *
_gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
{
Wim Taymans's avatar
Wim Taymans committed
709
710
  return gst_buffer_new_wrapped_full (free_func ? 0 : GST_MEMORY_FLAG_READONLY,
      mem, size, 0, size, mem, free_func);
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
711
712
}

713
714
715
716
717
static GstFlowReturn
gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
    GstBuffer ** buf)
{
  GstFlowReturn flow;
Wim Taymans's avatar
Wim Taymans committed
718
  GstMapInfo map;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
719
  gsize bsize;
720

721
  if (G_UNLIKELY (size == 0)) {
722
723
724
725
726
727
728
    GstFlowReturn ret;
    GstBuffer *tmp = NULL;

    ret = gst_qtdemux_pull_atom (qtdemux, offset, sizeof (guint32), &tmp);
    if (ret != GST_FLOW_OK)
      return ret;

Wim Taymans's avatar
Wim Taymans committed
729
730
    gst_buffer_map (tmp, &map, GST_MAP_READ);
    size = QT_UINT32 (map.data);
731
    GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
732

Wim Taymans's avatar
Wim Taymans committed
733
    gst_buffer_unmap (tmp, &map);
734
735
736
    gst_buffer_unref (tmp);
  }

737
738
  /* Sanity check: catch bogus sizes (fuzzed/broken files) */
  if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
739
740
741
742
743
    if (qtdemux->state != QTDEMUX_STATE_MOVIE && qtdemux->got_moov) {
      /* we're pulling header but already got most interesting bits,
       * so never mind the rest (e.g. tags) (that much) */
      GST_WARNING_OBJECT (qtdemux, "atom has bogus size %" G_GUINT64_FORMAT,
          size);
744
      return GST_FLOW_EOS;
745
746
747
748
749
750
    } else {
      GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
          (_("This file is invalid and cannot be played.")),
          ("atom has bogus size %" G_GUINT64_FORMAT, size));
      return GST_FLOW_ERROR;
    }
751
752
753
754
755
756
757
  }

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

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

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
758
  bsize = gst_buffer_get_size (*buf);
759
  /* Catch short reads - we don't want any partial atoms */
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
760
  if (G_UNLIKELY (bsize < size)) {
761
762
    GST_WARNING_OBJECT (qtdemux,
        "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
763
764
    gst_buffer_unref (*buf);
    *buf = NULL;
765
    return GST_FLOW_EOS;
766
767
768
769
770
  }

  return flow;
}

771
#if 1
772
static gboolean
773
774
775
gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
    GstFormat src_format, gint64 src_value, GstFormat dest_format,
    gint64 * dest_value)
776
777
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
778
  QtDemuxStream *stream = gst_pad_get_element_private (pad);
779
  gint32 index;
780

Andy Wingo's avatar
Andy Wingo committed
781
782
783
784
  if (stream->subtype != FOURCC_vide) {
    res = FALSE;
    goto done;
  }
785
786
787

  switch (src_format) {
    case GST_FORMAT_TIME:
788
789
790
      switch (dest_format) {
        case GST_FORMAT_BYTES:{
          index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
791
792
793
794
          if (-1 == index) {
            res = FALSE;
            goto done;
          }
795
796
797
798
799
800

          *dest_value = stream->samples[index].offset;

          GST_DEBUG_OBJECT (qtdemux, "Format Conversion Time->Offset :%"
              GST_TIME_FORMAT "->%" G_GUINT64_FORMAT,
              GST_TIME_ARGS (src_value), *dest_value);
801
          break;
802
        }
803
804
805
        default:
          res = FALSE;
          break;
806
807
808
      }
      break;
    case GST_FORMAT_BYTES:
809
810
811
812
813
814
      switch (dest_format) {
        case GST_FORMAT_TIME:{
          index =
              gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
              stream, src_value);

815
816
817
818
          if (-1 == index) {
            res = FALSE;
            goto done;
          }
819
820

          *dest_value =
821
822
823
824
825
              QTSTREAMTIME_TO_GSTTIME (stream,
              stream->samples[index].timestamp);
          GST_DEBUG_OBJECT (qtdemux,
              "Format Conversion Offset->Time :%" G_GUINT64_FORMAT "->%"
              GST_TIME_FORMAT, src_value, GST_TIME_ARGS (*dest_value));
826
          break;
827
        }
828
829
830
        default:
          res = FALSE;
          break;
831
832
833
834
      }
      break;
    default:
      res = FALSE;
835
      break;
836
837
  }

Andy Wingo's avatar
Andy Wingo committed
838
done:
839
840
  return res;
}
841
#endif
842

843
static gboolean
844
gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
845
{
846
  gboolean res = FALSE;
847
848
849

  *duration = GST_CLOCK_TIME_NONE;

850
851
852
853
854
855
  if (qtdemux->duration != 0 &&
      qtdemux->duration != G_MAXINT64 && qtdemux->timescale != 0) {
    *duration = QTTIME_TO_GSTTIME (qtdemux, qtdemux->duration);
    res = TRUE;
  } else {
    *duration = GST_CLOCK_TIME_NONE;
856
  }
857

858
859
860
  return res;
}

861
static gboolean
Wim Taymans's avatar
Wim Taymans committed
862
863
gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
    GstQuery * query)
864
{
865
  gboolean res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
866
  GstQTDemux *qtdemux = GST_QTDEMUX (parent);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
867

868
869
  GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));

870
  switch (GST_QUERY_TYPE (query)) {
871
872
873
874
875
876
    case GST_QUERY_POSITION:{
      GstFormat fmt;

      gst_query_parse_position (query, &fmt, NULL);
      if (fmt == GST_FORMAT_TIME
          && GST_CLOCK_TIME_IS_VALID (qtdemux->segment.position)) {
877
        gst_query_set_position (query, GST_FORMAT_TIME,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
878
            qtdemux->segment.position);
Wim Taymans's avatar
Wim Taymans committed
879
880
        res = TRUE;
      }
881
    }
Wim Taymans's avatar
Wim Taymans committed
882
      break;
883
884
    case GST_QUERY_DURATION:{
      GstFormat fmt;
885

886
887
      gst_query_parse_duration (query, &fmt, NULL);
      if (fmt == GST_FORMAT_TIME) {
888
889
890
        /* First try to query upstream */
        res = gst_pad_query_default (pad, parent, query);
        if (!res) {
891
          GstClockTime duration;
892
          if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
893
894
895
            gst_query_set_duration (query, GST_FORMAT_TIME, duration);
            res = TRUE;
          }
896
897
898
        }
      }
      break;
899
    }
900
901
    case GST_QUERY_CONVERT:{
      GstFormat src_fmt, dest_fmt;
902
      gint64 src_value, dest_value = 0;
903
904
905

      gst_query_parse_convert (query, &src_fmt, &src_value, &dest_fmt, NULL);

906
      res = gst_qtdemux_src_convert (qtdemux, pad,
907
908
909
910
911
912
913
914
915
916
917
          src_fmt, src_value, dest_fmt, &dest_value);
      if (res) {
        gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
        res = TRUE;
      }
      break;
    }
    case GST_QUERY_FORMATS:
      gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
      res = TRUE;
      break;
918
919
    case GST_QUERY_SEEKING:{
      GstFormat fmt;
920
      gboolean seekable;
921

Thiago Santos's avatar
Thiago Santos committed
922
923
924
925
926
927
      /* try upstream first */
      res = gst_pad_query_default (pad, parent, query);

      if (!res) {
        gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
        if (fmt == GST_FORMAT_TIME) {
928
          GstClockTime duration;
Thiago Santos's avatar
Thiago Santos committed
929
930
931
932
933
934
935
936
937
938
939
940
941
942

          gst_qtdemux_get_duration (qtdemux, &duration);
          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);
            if (gst_pad_peer_query (qtdemux->sinkpad, q)) {
              gst_query_parse_seeking (q, &fmt, &seekable, NULL, NULL);
              GST_LOG_OBJECT (qtdemux, "upstream BYTE seekable %d", seekable);
            }
            gst_query_unref (q);
943
          }
Thiago Santos's avatar
Thiago Santos committed
944
945
          gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
          res = TRUE;
946
        }
947
      }
948
      break;
949
    }
Wim Taymans's avatar
Wim Taymans committed
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
    case GST_QUERY_SEGMENT:
    {
      GstFormat format;
      gint64 start, stop;

      format = qtdemux->segment.format;

      start =
          gst_segment_to_stream_time (&qtdemux->segment, format,
          qtdemux->segment.start);
      if ((stop = qtdemux->segment.stop) == -1)
        stop = qtdemux->segment.duration;
      else
        stop = gst_segment_to_stream_time (&qtdemux->segment, format, stop);

      gst_query_set_segment (query, qtdemux->segment.rate, format, start, stop);
      res = TRUE;
      break;
    }
969
    default:
Wim Taymans's avatar
Wim Taymans committed
970
      res = gst_pad_query_default (pad, parent, query);
971
972
973
974
975
976
      break;
  }

  return res;
}

977
978
979
980
981
982
983
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));

984
    if (!gst_tag_list_is_empty (stream->stream_tags)) {
985
      GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
986
          stream->stream_tags);
987
      gst_pad_push_event (stream->pad,
988
          gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
989
990
    }

991
    if (G_UNLIKELY (stream->send_global_tags)) {
992
993
994
      GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
          qtdemux->tag_list);
      gst_pad_push_event (stream->pad,
995
          gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
996
997
998
999
1000
      stream->send_global_tags = FALSE;
    }
  }
}

1001
/* push event on all source pads; takes ownership of the event */
1002
static void
1003
gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
1004
1005
{
  guint n;
1006
  gboolean has_valid_stream = FALSE;
1007
  GstEventType etype = GST_EVENT_TYPE (event);
1008
1009
1010
1011
1012

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

  for (n = 0; n < qtdemux->n_streams; n++) {
1013
    GstPad *pad;
1014
    QtDemuxStream *stream = qtdemux->streams[n];
Thiago Santos's avatar
Thiago Santos committed
1015
    GST_DEBUG_OBJECT (qtdemux, "pushing on pad %i", n);
1016

1017
1018
1019
    if ((pad = stream->pad)) {
      has_valid_stream = TRUE;

1020
1021
1022
1023
      if (etype == GST_EVENT_EOS) {
        /* let's not send twice */
        if (stream->sent_eos)
          continue;
1024
        stream->sent_eos = TRUE;
1025
      }
1026
1027

      gst_pad_push_event (pad, gst_event_ref (event));
1028
    }
1029
  }
1030

1031
  gst_event_unref (event);
1032

1033
1034
  /* if it is EOS and there are no pads, post an error */
  if (!has_valid_stream && etype == GST_EVENT_EOS) {
1035
1036
    gst_qtdemux_post_no_playable_stream_error (qtdemux);
  }
1037
1038
}

1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
/* 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;
  }
}

1049
1050
1051
1052
1053
1054
typedef struct
{
  guint64 media_time;
} FindData;

static gint
1055
find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
1056
{
1057
  if ((gint64) s1->timestamp + s1->pts_offset > *media_time)
1058
    return 1;
1059
1060
  if ((gint64) s1->timestamp + s1->pts_offset == *media_time)
    return 0;
1061
1062
1063
1064

  return -1;
}

1065
/* find the index of the sample that includes the data for @media_time using a
1066
 * binary search.  Only to be called in optimized cases of linear search below.
1067
 *
1068
 * Returns the index of the sample.
1069
 */
1070
1071
1072
static guint32
gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint64 media_time)
1073
{
1074
1075
  QtDemuxSample *result;
  guint32 index;
1076

1077
  /* convert media_time to mov format */
1078
1079
  media_time =
      gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
1080

1081
  result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
1082
1083
      sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
      GST_SEARCH_MODE_BEFORE, &media_time, NULL);
1084

1085
1086
1087
1088
1089
1090
  if (G_LIKELY (result))
    index = result - str->samples;
  else
    index = 0;

  return index;
1091
}
1092

1093
1094
1095
1096
1097
1098
1099
1100