qtdemux.c 492 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
/**
 * SECTION:element-qtdemux
32
 * @title: qtdemux
33
34
 *
 * Demuxes a .mov file into raw or compressed audio and/or video streams.
35
 *
36
37
 * This element supports both push and pull-based scheduling, depending on the
 * capabilities of the upstream elements.
38
 *
39
 * ## Example launch line
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
 * 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.
46
 *
47
48
 */

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/base/base.h>
57
#include <gst/tag/tag.h>
Wim Taymans's avatar
Wim Taymans committed
58
#include <gst/audio/audio.h>
59
60
#include <gst/riff/riff.h>
#include <gst/pbutils/pbutils.h>
61

62
#include "gstisomp4elements.h"
63
#include "qtatomparser.h"
64
65
#include "qtdemux_types.h"
#include "qtdemux_dump.h"
66
#include "fourcc.h"
67
#include "descriptors.h"
68
#include "qtdemux_lang.h"
69
#include "qtdemux.h"
70
#include "qtpalette.h"
71
#include "qtdemux_tags.h"
72
#include "qtdemux_tree.h"
73

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

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

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

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

87
/* if the sample index is larger than this, something is likely wrong */
Michael Olbrich's avatar
Michael Olbrich committed
88
#define QTDEMUX_MAX_SAMPLE_INDEX_SIZE (200*1024*1024)
89

90
91
92
93
94
95
/* 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)

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

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

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

102
#define QTDEMUX_STREAM(s) ((QtDemuxStream *)(s))
103
104
105
106
107
#define QTDEMUX_N_STREAMS(demux) ((demux)->active_streams->len)
#define QTDEMUX_NTH_STREAM(demux,idx) \
   QTDEMUX_STREAM(g_ptr_array_index((demux)->active_streams,idx))
#define QTDEMUX_NTH_OLD_STREAM(demux,idx) \
   QTDEMUX_STREAM(g_ptr_array_index((demux)->old_streams,idx))
108

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

111
GST_DEBUG_CATEGORY (qtdemux_debug);
112
#define GST_CAT_DEFAULT qtdemux_debug
113

114
typedef struct _QtDemuxCencSampleSetInfo QtDemuxCencSampleSetInfo;
115
typedef struct _QtDemuxAavdEncryptionInfo QtDemuxAavdEncryptionInfo;
116

117
118
119
120
121
122
123
/* 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))

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

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

135
136
137
138
139
140
141
142
143
144
145
146
#define QTDEMUX_EXPOSE_GET_LOCK(demux) (&((demux)->expose_lock))
#define QTDEMUX_EXPOSE_LOCK(demux) G_STMT_START { \
    GST_TRACE("Locking from thread %p", g_thread_self()); \
    g_mutex_lock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
    GST_TRACE("Locked from thread %p", g_thread_self()); \
 } G_STMT_END

#define QTDEMUX_EXPOSE_UNLOCK(demux) G_STMT_START { \
    GST_TRACE("Unlocking from thread %p", g_thread_self()); \
    g_mutex_unlock (QTDEMUX_EXPOSE_GET_LOCK (demux)); \
 } G_STMT_END

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

205
206
207
struct _QtDemuxSegment
{
  /* global time and duration, all gst time */
208
209
210
  GstClockTime time;
  GstClockTime stop_time;
  GstClockTime duration;
211
  /* media time of trak, all gst time */
212
213
  GstClockTime media_start;
  GstClockTime media_stop;
214
  gdouble rate;
215
216
  /* Media start time in trak timescale units */
  guint32 trak_media_start;
217
218
};

219
220
#define QTSEGMENT_IS_EMPTY(s) ((s)->media_start == GST_CLOCK_TIME_NONE)

221
/* Used with fragmented MP4 files (mfra atom) */
222
struct _QtDemuxRandomAccessEntry
223
224
225
{
  GstClockTime ts;
  guint64 moof_offset;
226
227
};

228

229
230
231
232
233
234
235
236
/* 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;
237
238
};

239
240
241
242
243
struct _QtDemuxAavdEncryptionInfo
{
  GstStructure *default_properties;
};

244
245
static const gchar *
qt_demux_state_string (enum QtDemuxState state)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
246
{
247
248
249
250
251
252
253
254
255
256
257
258
259
  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>";
  }
}
260

261
262
static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);

263
264
static void gst_qtdemux_check_send_pending_segment (GstQTDemux * demux);

David Schleef's avatar
David Schleef committed
265
static GstStaticPadTemplate gst_qtdemux_sink_template =
266
    GST_STATIC_PAD_TEMPLATE ("sink",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
267
    GST_PAD_SINK,
268
    GST_PAD_ALWAYS,
269
270
    GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
        "application/x-3gp")
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
271
    );
David Schleef's avatar
David Schleef committed
272
273

static GstStaticPadTemplate gst_qtdemux_videosrc_template =
Wim Taymans's avatar
Wim Taymans committed
274
GST_STATIC_PAD_TEMPLATE ("video_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
275
276
277
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
David Schleef's avatar
David Schleef committed
278
279

static GstStaticPadTemplate gst_qtdemux_audiosrc_template =
Wim Taymans's avatar
Wim Taymans committed
280
GST_STATIC_PAD_TEMPLATE ("audio_%u",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
281
282
283
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);
Artyom Baginski's avatar
Artyom Baginski committed
284

285
static GstStaticPadTemplate gst_qtdemux_subsrc_template =
Wim Taymans's avatar
Wim Taymans committed
286
GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
287
288
289
290
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
291
292
#define gst_qtdemux_parent_class parent_class
G_DEFINE_TYPE (GstQTDemux, gst_qtdemux, GST_TYPE_ELEMENT);
293
294
GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (qtdemux, "qtdemux",
    GST_RANK_PRIMARY, GST_TYPE_QTDEMUX, isomp4_element_init (plugin));
Artyom Baginski's avatar
Artyom Baginski committed
295

296
static void gst_qtdemux_dispose (GObject * object);
297
static void gst_qtdemux_finalize (GObject * object);
298

299
300
static guint32
gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
301
    GstClockTime media_time);
302
303
304
305
static guint32
gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
    QtDemuxStream * str, gint64 media_offset);

306
#if 0
307
308
static void gst_qtdemux_set_index (GstElement * element, GstIndex * index);
static GstIndex *gst_qtdemux_get_index (GstElement * element);
309
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
310
311
static GstStateChangeReturn gst_qtdemux_change_state (GstElement * element,
    GstStateChange transition);
312
313
static void gst_qtdemux_set_context (GstElement * element,
    GstContext * context);
Wim Taymans's avatar
Wim Taymans committed
314
static gboolean qtdemux_sink_activate (GstPad * sinkpad, GstObject * parent);
Wim Taymans's avatar
Wim Taymans committed
315
316
static gboolean qtdemux_sink_activate_mode (GstPad * sinkpad,
    GstObject * parent, GstPadMode mode, gboolean active);
317
318

static void gst_qtdemux_loop (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
319
320
321
322
static GstFlowReturn gst_qtdemux_chain (GstPad * sinkpad, GstObject * parent,
    GstBuffer * inbuf);
static gboolean gst_qtdemux_handle_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event);
323
324
static gboolean gst_qtdemux_handle_sink_query (GstPad * pad, GstObject * parent,
    GstQuery * query);
Thiago Santos's avatar
Thiago Santos committed
325
static gboolean gst_qtdemux_setcaps (GstQTDemux * qtdemux, GstCaps * caps);
326
327
static gboolean gst_qtdemux_configure_stream (GstQTDemux * qtdemux,
    QtDemuxStream * stream);
328
329
static void gst_qtdemux_stream_check_and_change_stsd_index (GstQTDemux * demux,
    QtDemuxStream * stream);
330
331
static GstFlowReturn gst_qtdemux_process_adapter (GstQTDemux * demux,
    gboolean force);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
332

333
334
static void gst_qtdemux_check_seekability (GstQTDemux * demux);

335
336
static gboolean qtdemux_parse_moov (GstQTDemux * qtdemux,
    const guint8 * buffer, guint length);
337
static gboolean qtdemux_parse_node (GstQTDemux * qtdemux, GNode * node,
338
    const guint8 * buffer, guint length);
339
static gboolean qtdemux_parse_tree (GstQTDemux * qtdemux);
340

341
static void gst_qtdemux_handle_esds (GstQTDemux * qtdemux,
342
343
    QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, GNode * esds,
    GstTagList * list);
344
static GstCaps *qtdemux_video_caps (GstQTDemux * qtdemux,
345
346
    QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
    const guint8 * stsd_entry_data, gchar ** codec_name);
347
static GstCaps *qtdemux_audio_caps (GstQTDemux * qtdemux,
348
349
350
351
    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,
352
    gchar ** codec_name);
353
static GstCaps *qtdemux_generic_caps (GstQTDemux * qtdemux,
354
355
    QtDemuxStream * stream, QtDemuxStreamStsdEntry * entry, guint32 fourcc,
    const guint8 * stsd_entry_data, gchar ** codec_name);
356

357
358
static gboolean qtdemux_parse_samples (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint32 n);
359
static GstFlowReturn qtdemux_expose_streams (GstQTDemux * qtdemux);
360
static QtDemuxStream *gst_qtdemux_stream_ref (QtDemuxStream * stream);
361
static void gst_qtdemux_stream_unref (QtDemuxStream * stream);
362
static void gst_qtdemux_stream_clear (QtDemuxStream * stream);
363
static GstFlowReturn qtdemux_prepare_streams (GstQTDemux * qtdemux);
364
365
static void qtdemux_do_allocation (QtDemuxStream * stream,
    GstQTDemux * qtdemux);
366
367
368
369
370
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);
371
372
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
373

374
static gboolean qtdemux_pull_mfro_mfra (GstQTDemux * qtdemux);
375
static void check_update_duration (GstQTDemux * qtdemux, GstClockTime duration);
376

377
378
379
380
static gchar *qtdemux_uuid_bytes_to_string (gconstpointer uuid_bytes);

static GstStructure *qtdemux_get_cenc_sample_properties (GstQTDemux * qtdemux,
    QtDemuxStream * stream, guint sample_index);
381
382
static void gst_qtdemux_append_protection_system_id (GstQTDemux * qtdemux,
    const gchar * id);
383
static void qtdemux_gst_structure_free (GstStructure * gststructure);
384
static void gst_qtdemux_reset (GstQTDemux * qtdemux, gboolean hard);
385

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
386
387
static void
gst_qtdemux_class_init (GstQTDemuxClass * klass)
Artyom Baginski's avatar
Artyom Baginski committed
388
389
390
391
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
392
393
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Artyom Baginski's avatar
Artyom Baginski committed
394

395
  parent_class = g_type_class_peek_parent (klass);
396

397
  gobject_class->dispose = gst_qtdemux_dispose;
398
  gobject_class->finalize = gst_qtdemux_finalize;
399

400
  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qtdemux_change_state);
401
#if 0
402
403
  gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_qtdemux_set_index);
  gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_qtdemux_get_index);
404
#endif
405
  gstelement_class->set_context = GST_DEBUG_FUNCPTR (gst_qtdemux_set_context);
406
407

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

409
410
411
412
413
414
415
416
  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);
417
  gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
418
419
420
421
422
      "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");
423
  gst_riff_init ();
Artyom Baginski's avatar
Artyom Baginski committed
424
425
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
426
static void
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
427
gst_qtdemux_init (GstQTDemux * qtdemux)
Artyom Baginski's avatar
Artyom Baginski committed
428
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
429
  qtdemux->sinkpad =
430
      gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink");
431
  gst_pad_set_activate_function (qtdemux->sinkpad, qtdemux_sink_activate);
Wim Taymans's avatar
Wim Taymans committed
432
433
  gst_pad_set_activatemode_function (qtdemux->sinkpad,
      qtdemux_sink_activate_mode);
434
435
  gst_pad_set_chain_function (qtdemux->sinkpad, gst_qtdemux_chain);
  gst_pad_set_event_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_event);
436
  gst_pad_set_query_function (qtdemux->sinkpad, gst_qtdemux_handle_sink_query);
437
  gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad);
438

439
  qtdemux->adapter = gst_adapter_new ();
440
  g_queue_init (&qtdemux->protection_event_queue);
Thiago Santos's avatar
Thiago Santos committed
441
  qtdemux->flowcombiner = gst_flow_combiner_new ();
442
  g_mutex_init (&qtdemux->expose_lock);
Wim Taymans's avatar
Wim Taymans committed
443

444
445
446
447
448
  qtdemux->active_streams = g_ptr_array_new_with_free_func
      ((GDestroyNotify) gst_qtdemux_stream_unref);
  qtdemux->old_streams = g_ptr_array_new_with_free_func
      ((GDestroyNotify) gst_qtdemux_stream_unref);

Wim Taymans's avatar
Wim Taymans committed
449
  GST_OBJECT_FLAG_SET (qtdemux, GST_ELEMENT_FLAG_INDEXABLE);
450
451

  gst_qtdemux_reset (qtdemux, TRUE);
Artyom Baginski's avatar
Artyom Baginski committed
452
453
}

454
455
456
457
458
459
460
461
462
463
static void
gst_qtdemux_finalize (GObject * object)
{
  GstQTDemux *qtdemux = GST_QTDEMUX (object);

  g_free (qtdemux->redirect_location);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

464
465
466
467
468
469
470
471
472
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;
  }
473
  gst_tag_list_unref (qtdemux->tag_list);
Thiago Santos's avatar
Thiago Santos committed
474
  gst_flow_combiner_free (qtdemux->flowcombiner);
475
476
477
  g_queue_foreach (&qtdemux->protection_event_queue, (GFunc) gst_event_unref,
      NULL);
  g_queue_clear (&qtdemux->protection_event_queue);
478

479
480
  g_free (qtdemux->cenc_aux_info_sizes);
  qtdemux->cenc_aux_info_sizes = NULL;
481
  g_mutex_clear (&qtdemux->expose_lock);
482

483
484
485
  g_ptr_array_free (qtdemux->active_streams, TRUE);
  g_ptr_array_free (qtdemux->old_streams, TRUE);

486
  G_OBJECT_CLASS (parent_class)->dispose (object);
487
488
}

489
490
491
static void
gst_qtdemux_post_no_playable_stream_error (GstQTDemux * qtdemux)
{
492
493
  if (qtdemux->redirect_location) {
    GST_ELEMENT_ERROR_WITH_DETAILS (qtdemux, STREAM, DEMUX,
494
        (_("This file contains no playable streams.")),
495
496
        ("no known streams found, a redirect message has been posted"),
        ("redirect-location", G_TYPE_STRING, qtdemux->redirect_location, NULL));
497
  } else {
498
    GST_ELEMENT_ERROR (qtdemux, STREAM, DEMUX,
499
500
501
502
503
        (_("This file contains no playable streams.")),
        ("no known streams found"));
  }
}

Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
504
505
506
static GstBuffer *
_gst_buffer_new_wrapped (gpointer mem, gsize size, GFreeFunc free_func)
{
Wim Taymans's avatar
Wim Taymans committed
507
508
  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
509
510
}

511
512
513
514
515
static GstFlowReturn
gst_qtdemux_pull_atom (GstQTDemux * qtdemux, guint64 offset, guint64 size,
    GstBuffer ** buf)
{
  GstFlowReturn flow;
Wim Taymans's avatar
Wim Taymans committed
516
  GstMapInfo map;
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
517
  gsize bsize;
518

519
  if (G_UNLIKELY (size == 0)) {
520
521
522
523
524
525
526
    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
527
528
    gst_buffer_map (tmp, &map, GST_MAP_READ);
    size = QT_UINT32 (map.data);
529
    GST_DEBUG_OBJECT (qtdemux, "size 0x%08" G_GINT64_MODIFIER "x", size);
530

Wim Taymans's avatar
Wim Taymans committed
531
    gst_buffer_unmap (tmp, &map);
532
533
534
    gst_buffer_unref (tmp);
  }

535
536
  /* Sanity check: catch bogus sizes (fuzzed/broken files) */
  if (G_UNLIKELY (size > QTDEMUX_MAX_ATOM_SIZE)) {
537
538
539
540
541
    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);
542
      return GST_FLOW_EOS;
543
544
545
546
547
548
    } 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;
    }
549
550
551
552
553
554
555
  }

  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
556
  bsize = gst_buffer_get_size (*buf);
557
  /* Catch short reads - we don't want any partial atoms */
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
558
  if (G_UNLIKELY (bsize < size)) {
559
560
    GST_WARNING_OBJECT (qtdemux,
        "short read: %" G_GSIZE_FORMAT " < %" G_GUINT64_FORMAT, bsize, size);
561
562
    gst_buffer_unref (*buf);
    *buf = NULL;
563
    return GST_FLOW_EOS;
564
565
566
567
568
  }

  return flow;
}

569
#if 1
570
static gboolean
571
572
573
gst_qtdemux_src_convert (GstQTDemux * qtdemux, GstPad * pad,
    GstFormat src_format, gint64 src_value, GstFormat dest_format,
    gint64 * dest_value)
574
575
{
  gboolean res = TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
576
  QtDemuxStream *stream = gst_pad_get_element_private (pad);
577
  gint32 index;
578

Andy Wingo's avatar
Andy Wingo committed
579
580
581
582
  if (stream->subtype != FOURCC_vide) {
    res = FALSE;
    goto done;
  }
583
584
585

  switch (src_format) {
    case GST_FORMAT_TIME:
586
587
588
      switch (dest_format) {
        case GST_FORMAT_BYTES:{
          index = gst_qtdemux_find_index_linear (qtdemux, stream, src_value);
589
590
591
592
          if (-1 == index) {
            res = FALSE;
            goto done;
          }
593
594
595
596
597
598

          *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);
599
          break;
600
        }
601
602
603
        default:
          res = FALSE;
          break;
604
605
606
      }
      break;
    case GST_FORMAT_BYTES:
607
608
609
610
611
612
      switch (dest_format) {
        case GST_FORMAT_TIME:{
          index =
              gst_qtdemux_find_index_for_given_media_offset_linear (qtdemux,
              stream, src_value);

613
614
615
616
          if (-1 == index) {
            res = FALSE;
            goto done;
          }
617
618

          *dest_value =
619
620
621
622
623
              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));
624
          break;
625
        }
626
627
628
        default:
          res = FALSE;
          break;
629
630
631
632
      }
      break;
    default:
      res = FALSE;
633
      break;
634
635
  }

Andy Wingo's avatar
Andy Wingo committed
636
done:
637
638
  return res;
}
639
#endif
640

641
static gboolean
642
gst_qtdemux_get_duration (GstQTDemux * qtdemux, GstClockTime * duration)
643
{
644
  gboolean res = FALSE;
645
646
647

  *duration = GST_CLOCK_TIME_NONE;

648
649
650
651
652
653
  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;
654
  }
655

656
657
658
  return res;
}

659
static gboolean
Wim Taymans's avatar
Wim Taymans committed
660
661
gst_qtdemux_handle_src_query (GstPad * pad, GstObject * parent,
    GstQuery * query)
662
{
663
  gboolean res = FALSE;
Wim Taymans's avatar
Wim Taymans committed
664
  GstQTDemux *qtdemux = GST_QTDEMUX (parent);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
665

666
667
  GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query));

668
  switch (GST_QUERY_TYPE (query)) {
669
670
671
672
673
674
    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)) {
675
        gst_query_set_position (query, GST_FORMAT_TIME,
Mark Nauwelaerts's avatar
Mark Nauwelaerts committed
676
            qtdemux->segment.position);
Wim Taymans's avatar
Wim Taymans committed
677
678
        res = TRUE;
      }
679
    }
Wim Taymans's avatar
Wim Taymans committed
680
      break;
681
682
    case GST_QUERY_DURATION:{
      GstFormat fmt;
683

684
685
      gst_query_parse_duration (query, &fmt, NULL);
      if (fmt == GST_FORMAT_TIME) {
686
687
688
        /* First try to query upstream */
        res = gst_pad_query_default (pad, parent, query);
        if (!res) {
689
          GstClockTime duration;
690
          if (gst_qtdemux_get_duration (qtdemux, &duration) && duration > 0) {
691
692
693
            gst_query_set_duration (query, GST_FORMAT_TIME, duration);
            res = TRUE;
          }
694
695
696
        }
      }
      break;
697
    }
698
699
    case GST_QUERY_CONVERT:{
      GstFormat src_fmt, dest_fmt;
700
      gint64 src_value, dest_value = 0;
701
702
703

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

704
      res = gst_qtdemux_src_convert (qtdemux, pad,
705
          src_fmt, src_value, dest_fmt, &dest_value);
Jimmy Ohn's avatar
Jimmy Ohn committed
706
      if (res)
707
        gst_query_set_convert (query, src_fmt, src_value, dest_fmt, dest_value);
Jimmy Ohn's avatar
Jimmy Ohn committed
708

709
710
711
712
713
714
      break;
    }
    case GST_QUERY_FORMATS:
      gst_query_set_formats (query, 2, GST_FORMAT_TIME, GST_FORMAT_BYTES);
      res = TRUE;
      break;
715
716
    case GST_QUERY_SEEKING:{
      GstFormat fmt;
717
      gboolean seekable;
718

719
720
721
722
723
724
725
      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);

      if (fmt == GST_FORMAT_BYTES) {
        /* We always refuse BYTES seeks from downstream */
        break;
      }

Thiago Santos's avatar
Thiago Santos committed
726
727
728
729
730
731
      /* 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) {
732
          GstClockTime duration;
Thiago Santos's avatar
Thiago Santos committed
733
734
735
736
737
738
739
740
741
742
743
744
745
746

          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);
747
          }
Thiago Santos's avatar
Thiago Santos committed
748
749
          gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration);
          res = TRUE;
750
        }
751
      }
752
      break;
753
    }
Wim Taymans's avatar
Wim Taymans committed
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
    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;
    }
773
    default:
Wim Taymans's avatar
Wim Taymans committed
774
      res = gst_pad_query_default (pad, parent, query);
775
776
777
778
779
780
      break;
  }

  return res;
}

781
782
783
784
785
786
787
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));

788
    if (!gst_tag_list_is_empty (stream->stream_tags)) {
789
      GST_DEBUG_OBJECT (qtdemux, "Sending tags %" GST_PTR_FORMAT,
790
          stream->stream_tags);
791
      gst_pad_push_event (stream->pad,
792
          gst_event_new_tag (gst_tag_list_ref (stream->stream_tags)));
793
794
    }

795
    if (G_UNLIKELY (stream->send_global_tags)) {
796
797
798
      GST_DEBUG_OBJECT (qtdemux, "Sending global tags %" GST_PTR_FORMAT,
          qtdemux->tag_list);
      gst_pad_push_event (stream->pad,
799
          gst_event_new_tag (gst_tag_list_ref (qtdemux->tag_list)));
800
801
802
803
804
      stream->send_global_tags = FALSE;
    }
  }
}

805
/* push event on all source pads; takes ownership of the event */
806
static void
807
gst_qtdemux_push_event (GstQTDemux * qtdemux, GstEvent * event)
808
{
809
  gboolean has_valid_stream = FALSE;
810
  GstEventType etype = GST_EVENT_TYPE (event);
811
  guint i;
812
813
814
815

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

816
  for (i = 0; i < QTDEMUX_N_STREAMS (qtdemux); i++) {
817
    GstPad *pad;
818
    QtDemuxStream *stream = QTDEMUX_NTH_STREAM (qtdemux, i);
819
    GST_DEBUG_OBJECT (qtdemux, "pushing on track-id %u", stream->track_id);
820

821
822
823
    if ((pad = stream->pad)) {
      has_valid_stream = TRUE;

824
825
826
827
      if (etype == GST_EVENT_EOS) {
        /* let's not send twice */
        if (stream->sent_eos)
          continue;
828
        stream->sent_eos = TRUE;
829
      }
830
831

      gst_pad_push_event (pad, gst_event_ref (event));
832
    }
833
  }
834

835
  gst_event_unref (event);
836

837
838
  /* if it is EOS and there are no pads, post an error */
  if (!has_valid_stream && etype == GST_EVENT_EOS) {
839
840
    gst_qtdemux_post_no_playable_stream_error (qtdemux);
  }
841
842
}

843
844
845
846
847
848
typedef struct
{
  guint64 media_time;
} FindData;

static gint
849
find_func (QtDemuxSample * s1, gint64 * media_time, gpointer user_data)
850
{
851
  if ((gint64) s1->timestamp > *media_time)
852
    return 1;
853
  if ((gint64) s1->timestamp == *media_time)
854
    return 0;
855
856
857
858

  return -1;
}

859
/* find the index of the sample that includes the data for @media_time using a
860
 * binary search.  Only to be called in optimized cases of linear search below.
861
 *
862
 * Returns the index of the sample with the corresponding *DTS*.
863
 */
864
865
866
static guint32
gst_qtdemux_find_index (GstQTDemux * qtdemux, QtDemuxStream * str,
    guint64 media_time)
867
{
868
869
  QtDemuxSample *result;
  guint32 index;
870

871
  /* convert media_time to mov format */
872
873
  media_time =
      gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);
874

875
  result = gst_util_array_binary_search (str->samples, str->stbl_index + 1,
876
877
      sizeof (QtDemuxSample), (GCompareDataFunc) find_func,
      GST_SEARCH_MODE_BEFORE, &media_time, NULL);
878

879
880
881
882
883
884
  if (G_LIKELY (result))
    index = result - str->samples;
  else
    index = 0;

  return index;
885
}
886

887
888
889
890
891
892
893
894
895
896
897
898
899
900


/* find the index of the sample that includes the data for @media_offset using a
 * linear search
 *
 * Returns the index of the sample.
 */
static guint32
gst_qtdemux_find_index_for_given_media_offset_linear (GstQTDemux * qtdemux,
    QtDemuxStream * str, gint64 media_offset)
{
  QtDemuxSample *result = str->samples;
  guint32 index = 0;

901
902
903
  if (result == NULL || str->n_samples == 0)
    return -1;

904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
  if (media_offset == result->offset)
    return index;

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

    if (media_offset < result->offset)
      break;

    index++;
    result++;
  }
  return index;

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

928
/* find the index of the sample that includes the data for @media_time using a
929
930
 * linear search, and keeping in mind that not all samples may have been parsed
 * yet.  If possible, it will delegate to binary search.
931
932
933
934
935
 *
 * Returns the index of the sample.
 */
static guint32
gst_qtdemux_find_index_linear (GstQTDemux * qtdemux, QtDemuxStream * str,
936
    GstClockTime media_time)
937
938
{
  guint32 index = 0;
939
  guint64 mov_time;
940
  QtDemuxSample *sample;
941

942
  /* convert media_time to mov format */
943
  mov_time =
944
      gst_util_uint64_scale_ceil (media_time, str->timescale, GST_SECOND);