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

Wim Taymans's avatar
Wim Taymans committed
22
23
/**
 * SECTION:element-oggdemux
24
 * @see_also: <link linkend="gst-plugins-base-plugins-oggmux">oggmux</link>
Wim Taymans's avatar
Wim Taymans committed
25
26
 *
 * This element demuxes ogg files into their encoded audio and video components.
27
28
 *
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
29
 * <title>Example pipelines</title>
30
 * |[
Wim Taymans's avatar
Wim Taymans committed
31
 * gst-launch -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
32
 * ]| Decodes the vorbis audio stored inside an ogg container.
Wim Taymans's avatar
Wim Taymans committed
33
34
35
36
37
38
 * </refsect2>
 *
 * Last reviewed on 2006-12-30 (0.10.5)
 */


39
40
41
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
42
#include <string.h>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
43
#include <gst/gst-i18n-plugin.h>
44
#include <gst/tag/tag.h>
45

Wim Taymans's avatar
Wim Taymans committed
46
47
#include "gstoggdemux.h"

48
#define CHUNKSIZE (8500)        /* this is out of vorbisfile */
49

50
#define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
51

52
53
54
#define GST_CHAIN_LOCK(ogg)     g_mutex_lock((ogg)->chain_lock)
#define GST_CHAIN_UNLOCK(ogg)   g_mutex_unlock((ogg)->chain_lock)

David Schleef's avatar
David Schleef committed
55
56
GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
57
58
#define GST_CAT_DEFAULT gst_ogg_demux_debug

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

static ogg_packet *
_ogg_packet_copy (const ogg_packet * packet)
{
  ogg_packet *ret = g_new0 (ogg_packet, 1);

  *ret = *packet;
  ret->packet = g_memdup (packet->packet, packet->bytes);

  return ret;
}

static void
_ogg_packet_free (ogg_packet * packet)
{
  g_free (packet->packet);
  g_free (packet);
}

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
static ogg_page *
gst_ogg_page_copy (ogg_page * page)
{
  ogg_page *p = g_new0 (ogg_page, 1);

  /* make a copy of the page */
  p->header = g_memdup (page->header, page->header_len);
  p->header_len = page->header_len;
  p->body = g_memdup (page->body, page->body_len);
  p->body_len = page->body_len;

  return p;
}

static void
gst_ogg_page_free (ogg_page * page)
{
  g_free (page->header);
  g_free (page->body);
  g_free (page);
}

100
101
102
103
static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
    GstOggChain * chain);
static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
    GstOggChain * chain, GstEvent * event);
104
static void gst_ogg_chain_mark_discont (GstOggChain * chain);
105

106
107
static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
    GstEvent * event);
108
109
static gboolean gst_ogg_demux_receive_event (GstElement * element,
    GstEvent * event);
110

111
112
static void gst_ogg_pad_dispose (GObject * object);
static void gst_ogg_pad_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
113

114
static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
115
static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
116
117
static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
118
119
120
static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
    glong serialno);

121
122
static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
    GstOggPad * pad, GstFlowReturn ret);
123
static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
124

125
126
127
GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg,
    GstCaps * caps, GList * headers);

128
GType gst_ogg_pad_get_type (void);
129
G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
130

131
132
static void
gst_ogg_pad_class_init (GstOggPadClass * klass)
133
{
134
  GObjectClass *gobject_class;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
135

136
  gobject_class = (GObjectClass *) klass;
137

138
139
140
  gobject_class->dispose = gst_ogg_pad_dispose;
  gobject_class->finalize = gst_ogg_pad_finalize;
}
141

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
static void
gst_ogg_pad_init (GstOggPad * pad)
{
  gst_pad_set_event_function (GST_PAD (pad),
      GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
  gst_pad_set_getcaps_function (GST_PAD (pad),
      GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps));
  gst_pad_set_query_type_function (GST_PAD (pad),
      GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types));
  gst_pad_set_query_function (GST_PAD (pad),
      GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));

  pad->mode = GST_OGG_PAD_MODE_INIT;

  pad->current_granule = -1;
157
  pad->keyframe_granule = -1;
158

159
  pad->start_time = GST_CLOCK_TIME_NONE;
160

161
162
  pad->last_stop = GST_CLOCK_TIME_NONE;

163
  pad->have_type = FALSE;
164
  pad->continued = NULL;
David Schleef's avatar
David Schleef committed
165
  pad->map.headers = NULL;
166
  pad->map.queued = NULL;
167
}
168

169
static void
170
gst_ogg_pad_dispose (GObject * object)
171
{
172
173
174
175
176
  GstOggPad *pad = GST_OGG_PAD (object);

  pad->chain = NULL;
  pad->ogg = NULL;

177
  g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
David Schleef's avatar
David Schleef committed
178
179
  g_list_free (pad->map.headers);
  pad->map.headers = NULL;
180
  g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
181
182
  g_list_free (pad->map.queued);
  pad->map.queued = NULL;
183

184
185
186
  g_free (pad->map.index);
  pad->map.index = NULL;

187
188
189
190
191
  /* clear continued pages */
  g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
  g_list_free (pad->continued);
  pad->continued = NULL;

192
193
194
195
196
197
198
199
200
201
  if (pad->map.caps) {
    gst_caps_unref (pad->map.caps);
    pad->map.caps = NULL;
  }

  if (pad->map.taglist) {
    gst_tag_list_free (pad->map.taglist);
    pad->map.taglist = NULL;
  }

David Schleef's avatar
David Schleef committed
202
  ogg_stream_reset (&pad->map.stream);
203

204
  G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
205
}
206

207
static void
208
gst_ogg_pad_finalize (GObject * object)
209
{
210
  GstOggPad *pad = GST_OGG_PAD (object);
211

David Schleef's avatar
David Schleef committed
212
  ogg_stream_clear (&pad->map.stream);
Johan Dahlin's avatar
Johan Dahlin committed
213

214
  G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
215
216
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
217
static const GstQueryType *
218
gst_ogg_pad_query_types (GstPad * pad)
219
{
220
  static const GstQueryType query_types[] = {
221
    GST_QUERY_DURATION,
Wim Taymans's avatar
Wim Taymans committed
222
    GST_QUERY_SEEKING,
223
224
    0
  };
225

226
  return query_types;
227
228
}

229
230
static GstCaps *
gst_ogg_pad_getcaps (GstPad * pad)
231
{
232
  return gst_caps_ref (GST_PAD_CAPS (pad));
233
234
}

235
static gboolean
Wim Taymans's avatar
Wim Taymans committed
236
gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
237
{
238
  gboolean res = TRUE;
239
240
  GstOggDemux *ogg;

241
  ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
242

Wim Taymans's avatar
Wim Taymans committed
243
  switch (GST_QUERY_TYPE (query)) {
Wim Taymans's avatar
Wim Taymans committed
244
    case GST_QUERY_DURATION:
245
246
    {
      GstFormat format;
247
      gint64 total_time = -1;
248

Wim Taymans's avatar
Wim Taymans committed
249
      gst_query_parse_duration (query, &format, NULL);
250
      /* can only get position in time */
251
252
253
      if (format != GST_FORMAT_TIME)
        goto wrong_format;

254
255
      if (ogg->total_time != -1) {
        /* we can return the total length */
256
257
        total_time = ogg->total_time;
      } else {
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
        gint bitrate = ogg->bitrate;

        /* try with length and bitrate */
        if (bitrate > 0) {
          GstQuery *uquery;

          /* ask upstream for total length in bytes */
          uquery = gst_query_new_duration (GST_FORMAT_BYTES);
          if (gst_pad_peer_query (ogg->sinkpad, uquery)) {
            gint64 length;

            gst_query_parse_duration (uquery, NULL, &length);

            /* estimate using the bitrate */
            total_time =
                gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);

            GST_LOG_OBJECT (ogg,
                "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
                GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
          }
          gst_query_unref (uquery);
        }
281
282
283
      }

      gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
284
      break;
285
    }
286
287
288
289
290
291
    case GST_QUERY_SEEKING:
    {
      GstFormat format;

      gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
      if (format == GST_FORMAT_TIME) {
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
        gboolean seekable = FALSE;
        gint64 stop = -1;

        if (ogg->pullmode) {
          seekable = TRUE;
          stop = ogg->total_time;
        } else if (ogg->current_chain->streams->len) {
          gint i;

          seekable = FALSE;
          for (i = 0; i < ogg->current_chain->streams->len; i++) {
            GstOggPad *pad =
                g_array_index (ogg->current_chain->streams, GstOggPad *, i);

            seekable |= (pad->map.index != NULL && pad->map.n_index != 0);

            if (pad->map.index != NULL && pad->map.n_index != 0) {
              GstOggIndex *idx;
              GstClockTime idx_time;

              idx = &pad->map.index[pad->map.n_index - 1];
              idx_time =
                  gst_util_uint64_scale (idx->timestamp, GST_SECOND,
                  pad->map.kp_denom);
              if (stop == -1)
                stop = idx_time;
              else
                stop = MAX (idx_time, stop);
            }
          }
        }

        gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
325
326
327
328
329
330
      } else {
        res = FALSE;
      }
      break;
    }

331
    default:
332
      res = gst_pad_query_default (pad, query);
333
334
      break;
  }
335
done:
336
337
  gst_object_unref (ogg);

338
  return res;
339
340
341
342
343
344
345
346

  /* ERRORS */
wrong_format:
  {
    GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
    res = FALSE;
    goto done;
  }
347
348
}

349
350
351
352
353
354
355
356
357
358
359
static gboolean
gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
{
  gboolean res;
  GstOggDemux *ogg;

  ogg = GST_OGG_DEMUX (element);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
      /* now do the seek */
360
      res = gst_ogg_demux_perform_seek (ogg, event);
361
      gst_event_unref (event);
362
363
      break;
    default:
364
      GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
365
366
367
368
369
      goto error;
  }

  return res;

370
  /* ERRORS */
371
error:
372
373
374
375
376
  {
    GST_DEBUG_OBJECT (ogg, "error handling event");
    gst_event_unref (event);
    return FALSE;
  }
377
378
}

379
static gboolean
380
gst_ogg_pad_event (GstPad * pad, GstEvent * event)
381
{
382
  gboolean res;
383
384
  GstOggDemux *ogg;

385
  ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
386

387
388
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
389
      /* now do the seek */
390
      res = gst_ogg_demux_perform_seek (ogg, event);
391
      gst_event_unref (event);
392
      break;
393
    default:
394
395
      res = gst_pad_event_default (pad, event);
      break;
396
  }
397
398
  gst_object_unref (ogg);

399
  return res;
400
401
}

402
static void
403
gst_ogg_pad_reset (GstOggPad * pad)
404
{
David Schleef's avatar
David Schleef committed
405
  ogg_stream_reset (&pad->map.stream);
406

407
408
  GST_DEBUG_OBJECT (pad, "doing reset");

409
410
411
412
413
414
  /* clear continued pages */
  g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
  g_list_free (pad->continued);
  pad->continued = NULL;

  pad->last_ret = GST_FLOW_OK;
415
  pad->last_stop = GST_CLOCK_TIME_NONE;
416
  pad->current_granule = -1;
417
  pad->keyframe_granule = -1;
418
  pad->is_eos = FALSE;
419
420
}

Wim Taymans's avatar
Wim Taymans committed
421
/* queue data, basically takes the packet, puts it in a buffer and store the
422
 * buffer in the queued list.  */
423
424
425
static GstFlowReturn
gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
{
426
#ifndef GST_DISABLE_GST_DEBUG
427
  GstOggDemux *ogg = pad->ogg;
428
#endif
429

430
  GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08lx", pad,
David Schleef's avatar
David Schleef committed
431
      pad->map.serialno);
432

433
  pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
434
435
436
437
438
439

  /* we are ok now */
  return GST_FLOW_OK;
}

static GstFlowReturn
440
441
gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
    gboolean push_headers)
442
{
443
  GstBuffer *buf = NULL;
444
  GstFlowReturn ret, cret;
445
  GstOggDemux *ogg = pad->ogg;
446
447
  gint64 current_time;
  GstOggChain *chain;
448
  gint64 duration;
449
  gint offset;
450
  gint trim;
451
452
  GstClockTime out_timestamp, out_duration;
  guint64 out_offset, out_offset_end;
453
  gboolean delta_unit = FALSE;
454

455
456
  cret = GST_FLOW_OK;

457
  GST_DEBUG_OBJECT (ogg,
458
      "%p streaming to peer serial %08lx", pad, pad->map.serialno);
459

460
461
  if (pad->map.is_ogm) {
    const guint8 *data;
462
    long bytes;
463
464

    data = packet->packet;
465
466
467
468
    bytes = packet->bytes;

    if (bytes < 1)
      goto empty_packet;
469

470
    if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
471
472
473
474
475
      /* We don't push header packets for OGM */
      goto done;
    }

    offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
476
    delta_unit = (((data[0] & 0x08) >> 3) == 0);
477
478
479
480
481
482
483
484
485
486

    trim = 0;

    /* Strip trailing \0 for subtitles */
    if (pad->map.is_ogm_text) {
      while (bytes && data[bytes - 1] == 0) {
        trim++;
        bytes--;
      }
    }
487
  } else if (pad->map.is_vp8) {
488
489
490
    if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
        packet->b_o_s ||
        (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) {
491
492
493
494
495
      /* We don't push header packets for VP8 */
      goto done;
    }
    offset = 0;
    trim = 0;
496
497
  } else {
    offset = 0;
498
    trim = 0;
499
500
  }

501
  /* get timing info for the packet */
502
  duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
503
  GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
504

David Schleef's avatar
David Schleef committed
505
  if (packet->b_o_s) {
506
507
508
509
    out_timestamp = GST_CLOCK_TIME_NONE;
    out_duration = GST_CLOCK_TIME_NONE;
    out_offset = 0;
    out_offset_end = -1;
David Schleef's avatar
David Schleef committed
510
511
  } else {
    if (packet->granulepos != -1) {
512
513
514
515
      pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
          packet->granulepos);
      pad->keyframe_granule =
          gst_ogg_stream_granulepos_to_key_granule (&pad->map,
David Schleef's avatar
David Schleef committed
516
          packet->granulepos);
517
518
      GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
          pad->current_granule);
519
    } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
520
521
522
      pad->current_granule += duration;
      GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT,
          pad->current_granule);
David Schleef's avatar
David Schleef committed
523
    }
524
525
526
527
528
529
    if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
      /* negative rates, only set timestamp on the packets with a granulepos */
      out_timestamp = -1;
      out_duration = -1;
      out_offset = -1;
      out_offset_end = -1;
530
    } else {
531
532
533
534
535
536
537
538
539
540
541
542
      /* we only push buffers after we have a valid granule. This is done so that
       * we nicely skip packets without a timestamp after a seek. This is ok
       * because we base or seek on the packet after the page with the smaller
       * timestamp. */
      if (pad->current_granule == -1)
        goto no_timestamp;

      if (pad->map.is_ogm) {
        out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
            pad->current_granule);
        out_duration = gst_util_uint64_scale (duration,
            GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
543
      } else if (pad->map.is_sparse) {
544
545
        out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
            pad->current_granule);
546
547
548
549
550
551
        if (duration == GST_CLOCK_TIME_NONE) {
          out_duration = GST_CLOCK_TIME_NONE;
        } else {
          out_duration = gst_util_uint64_scale (duration,
              GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
        }
552
553
554
555
556
557
558
559
      } else {
        out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
            pad->current_granule - duration);
        out_duration =
            gst_ogg_stream_granule_to_time (&pad->map,
            pad->current_granule) - out_timestamp;
      }
      out_offset_end =
560
561
          gst_ogg_stream_granule_to_granulepos (&pad->map,
          pad->current_granule, pad->keyframe_granule);
562
563
      out_offset =
          gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
564
    }
David Schleef's avatar
David Schleef committed
565
  }
566

567
568
569
570
571
  if (pad->map.is_ogm_text) {
    /* check for invalid buffer sizes */
    if (G_UNLIKELY (offset + trim >= packet->bytes))
      goto empty_packet;
  }
572

573
574
575
  if (!pad->added)
    goto not_added;

576
577
  buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
  gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
578

579
580
581
582
  /* set delta flag for OGM content */
  if (delta_unit)
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);

583
  /* copy packet in buffer */
584
  memcpy (buf->data, packet->packet + offset, packet->bytes - offset - trim);
585
586
587
588
589
590

  GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
  GST_BUFFER_DURATION (buf) = out_duration;
  GST_BUFFER_OFFSET (buf) = out_offset;
  GST_BUFFER_OFFSET_END (buf) = out_offset_end;

591
592
593
594
595
596
  /* Mark discont on the buffer */
  if (pad->discont) {
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
    pad->discont = FALSE;
  }

597
598
  pad->last_stop = ogg->segment.last_stop;

599
600
601
  /* don't push the header packets when we are asked to skip them */
  if (!packet->b_o_s || push_headers) {
    ret = gst_pad_push (GST_PAD_CAST (pad), buf);
602
    buf = NULL;
603

604
605
606
    /* combine flows */
    cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
  }
607

608
  /* we're done with skeleton stuff */
David Schleef's avatar
David Schleef committed
609
  if (pad->map.is_skeleton)
610
    goto done;
611

612
  /* check if valid granulepos, then we can calculate the current
Wim Taymans's avatar
Wim Taymans committed
613
614
615
   * position. We know the granule for each packet but we only want to update
   * the last_stop when we have a valid granulepos on the packet because else
   * our time jumps around for the different streams. */
616
617
  if (packet->granulepos < 0)
    goto done;
618

619
  /* convert to time */
David Schleef's avatar
David Schleef committed
620
621
  current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
      packet->granulepos);
622
623

  /* convert to stream time */
624
625
626
627
628
629
630
631
  if ((chain = pad->chain)) {
    gint64 chain_start = 0;

    if (chain->segment_start != GST_CLOCK_TIME_NONE)
      chain_start = chain->segment_start;

    current_time = current_time - chain_start + chain->begin_time;
  }
632
633
634

  /* and store as the current position */
  gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time);
635

636
637
638
  GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
      GST_TIME_ARGS (current_time));

639
640
641
  /* check stream eos */
  if ((ogg->segment.rate > 0.0 && ogg->segment.stop != GST_CLOCK_TIME_NONE &&
          current_time > ogg->segment.stop) ||
Wim Taymans's avatar
Wim Taymans committed
642
      (ogg->segment.rate < 0.0 && ogg->segment.start != GST_CLOCK_TIME_NONE &&
643
          current_time < ogg->segment.start)) {
Wim Taymans's avatar
Wim Taymans committed
644
    GST_DEBUG_OBJECT (ogg, "marking pad %p EOS", pad);
645
646
647
    pad->is_eos = TRUE;
  }

648
done:
649
650
  if (buf)
    gst_buffer_unref (buf);
651
652
  /* return combined flow result */
  return cret;
653
654

  /* special cases */
655
656
657
658
659
empty_packet:
  {
    GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
    goto done;
  }
660

661
662
663
664
665
no_timestamp:
  {
    GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
    goto done;
  }
666
not_added:
667
  {
668
    GST_DEBUG_OBJECT (ogg, "pad not added yet");
669
    goto done;
670
  }
671
672
}

673
674
675
676
677
678
679
680
681
static guint64
gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
{
  gint i;
  guint64 start_time = G_MAXUINT64;

  for (i = 0; i < chain->streams->len; i++) {
    GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);

682
    if (pad->map.is_sparse)
683
684
685
686
687
688
689
690
691
692
693
694
695
      continue;

    /*  can do this if the pad start time is not defined */
    if (pad->start_time == GST_CLOCK_TIME_NONE) {
      start_time = G_MAXUINT64;
      break;
    } else {
      start_time = MIN (start_time, pad->start_time);
    }
  }
  return start_time;
}

696
697
698
699
700
701
/* submit a packet to the oggpad, this function will run the
 * typefind code for the pad if this is the first packet for this
 * stream 
 */
static GstFlowReturn
gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
702
{
703
  gint64 granule;
704
  GstFlowReturn ret = GST_FLOW_OK;
705
706
707

  GstOggDemux *ogg = pad->ogg;

708
  GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08lx", pad,
David Schleef's avatar
David Schleef committed
709
      pad->map.serialno);
710

711
  if (!pad->have_type) {
David Schleef's avatar
David Schleef committed
712
    pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
713
714
715
    if (!pad->have_type) {
      pad->map.caps = gst_caps_new_simple ("application/x-unknown", NULL);
    }
716
717
718
719
720
    if (pad->map.is_skeleton) {
      GST_DEBUG_OBJECT (ogg, "we have a fishead");
      /* copy values over to global ogg level */
      ogg->basetime = pad->map.basetime;
      ogg->prestime = pad->map.prestime;
721
722
723
724
725
726
727

      /* use total time to update the total ogg time */
      if (ogg->total_time == -1) {
        ogg->total_time = pad->map.total_time;
      } else if (pad->map.total_time > 0) {
        ogg->total_time = MAX (ogg->total_time, pad->map.total_time);
      }
728
    }
David Schleef's avatar
David Schleef committed
729
730
    if (pad->map.caps) {
      gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
731
732
    } else {
      GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
David Schleef's avatar
David Schleef committed
733
    }
734
735
  }

736
737
  if (pad->map.is_skeleton) {
    guint32 serialno;
738
739
    GstOggPad *skel_pad;
    GstOggSkeleton type;
740
741
742

    /* try to parse the serialno first */
    if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
743
744
745
            &serialno, &type)) {

      GST_WARNING_OBJECT (pad->ogg,
746
          "got skeleton packet for stream 0x%08x", serialno);
747
748
749
750
751

      skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
      if (skel_pad) {
        switch (type) {
          case GST_OGG_SKELETON_FISBONE:
752
753
754
            /* parse the remainder of the fisbone in the pad with the serialno,
             * note that we ignore the start_time as this is usually wrong for
             * live streams */
755
            gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
756
                packet->bytes, NULL);
757
758
            break;
          case GST_OGG_SKELETON_INDEX:
759
            gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
760
                packet->bytes);
761
762
763
764
765
766
767

            /* use total time to update the total ogg time */
            if (ogg->total_time == -1) {
              ogg->total_time = skel_pad->map.total_time;
            } else if (skel_pad->map.total_time > 0) {
              ogg->total_time = MAX (ogg->total_time, skel_pad->map.total_time);
            }
768
769
770
771
772
            break;
          default:
            break;
        }

773
774
      } else {
        GST_WARNING_OBJECT (pad->ogg,
775
            "found skeleton fisbone for an unknown stream 0x%08x", serialno);
776
777
      }
    }
778
779
  }

780
781
  granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
      packet->granulepos);
782
  if (granule != -1) {
783
    GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
784
785
786
    pad->current_granule = granule;
  }

787
788
789
790
791
  /* restart header packet count when seeing a b_o_s page;
   * particularly useful following a seek or even following chain finding */
  if (packet->b_o_s) {
    GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
    pad->map.n_header_packets_seen = 0;
792
793
    if (!pad->map.have_headers) {
      GST_DEBUG_OBJECT (ogg, "clearing header packets");
794
      g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
795
796
797
      g_list_free (pad->map.headers);
      pad->map.headers = NULL;
    }
798
799
  }

David Schleef's avatar
David Schleef committed
800
801
802
803
804
  /* Overload the value of b_o_s in ogg_packet with a flag whether or
   * not this is a header packet.  Maybe some day this could be cleaned
   * up.  */
  packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
  if (!packet->b_o_s) {
805
    GST_DEBUG ("found non-header packet");
806
    pad->map.have_headers = TRUE;
David Schleef's avatar
David Schleef committed
807
808
    if (pad->start_time == GST_CLOCK_TIME_NONE) {
      gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
809
      GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
David Schleef's avatar
David Schleef committed
810
811
      if (duration != -1) {
        pad->map.accumulated_granule += duration;
812
813
        GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
            pad->map.accumulated_granule);
David Schleef's avatar
David Schleef committed
814
      }
815

David Schleef's avatar
David Schleef committed
816
817
      if (packet->granulepos != -1) {
        ogg_int64_t start_granule;
818
819
820
821
        gint64 granule;

        granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
            packet->granulepos);
David Schleef's avatar
David Schleef committed
822

823
824
825
826
        if (granule > pad->map.accumulated_granule)
          start_granule = granule - pad->map.accumulated_granule;
        else
          start_granule = 0;
David Schleef's avatar
David Schleef committed
827
828
829

        pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
            start_granule);
David Schleef's avatar
David Schleef committed
830
831
832
        GST_DEBUG ("start time %" G_GINT64_FORMAT, pad->start_time);
      } else {
        packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
833
            pad->map.accumulated_granule, pad->keyframe_granule);
David Schleef's avatar
David Schleef committed
834
835
836
      }
    }
  } else {
837
838
    /* look for tags in header packet (before inc header count) */
    gst_ogg_stream_extract_tags (&pad->map, packet);
David Schleef's avatar
David Schleef committed
839
    pad->map.n_header_packets_seen++;
840
    if (!pad->map.have_headers) {
841
842
      pad->map.headers =
          g_list_append (pad->map.headers, _ogg_packet_copy (packet));
843
844
      GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
    }
845
  }
David Schleef's avatar
David Schleef committed
846

847
848
  /* we know the start_time of the pad data, see if we
   * can activate the complete chain if this is a dynamic
849
850
   * chain. We need all the headers too for this. */
  if (pad->start_time != GST_CLOCK_TIME_NONE && pad->map.have_headers) {
851
852
    GstOggChain *chain = pad->chain;

853
    /* check if complete chain has start time */
854
    if (chain == ogg->building_chain) {
855
856
857
858
      GstEvent *event = NULL;

      if (ogg->resync) {
        guint64 start_time;
859

860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
        GST_DEBUG_OBJECT (ogg, "need to resync");

        /* when we need to resync after a seek, we wait until we have received
         * timestamps on all streams */
        start_time = gst_ogg_demux_collect_start_time (ogg, chain);

        if (start_time != G_MAXUINT64) {
          gint64 segment_time;

          GST_DEBUG_OBJECT (ogg, "start_time:  %" GST_TIME_FORMAT,
              GST_TIME_ARGS (start_time));

          if (chain->segment_start < start_time)
            segment_time =
                (start_time - chain->segment_start) + chain->begin_time;
          else
            segment_time = chain->begin_time;

          /* create the newsegment event we are going to send out */
          event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
              GST_FORMAT_TIME, start_time, chain->segment_stop, segment_time);

          ogg->resync = FALSE;
        }
      } else {
        /* see if we have enough info to activate the chain, we have enough info
         * when all streams have a valid start time. */
        if (gst_ogg_demux_collect_chain_info (ogg, chain)) {

          GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
              GST_TIME_ARGS (chain->segment_start));
          GST_DEBUG_OBJECT (ogg, "segment_stop:  %" GST_TIME_FORMAT,
              GST_TIME_ARGS (chain->segment_stop));
          GST_DEBUG_OBJECT (ogg, "segment_time:  %" GST_TIME_FORMAT,
              GST_TIME_ARGS (chain->begin_time));

          /* create the newsegment event we are going to send out */
          event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
              GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
              chain->begin_time);
        }
      }

      if (event) {
904
        gst_event_set_seqnum (event, ogg->seqnum);
905

906
        gst_ogg_demux_activate_chain (ogg, chain, event);
907
908

        ogg->building_chain = NULL;
909
      }
910
911
    }
  }
Wim Taymans's avatar
Wim Taymans committed
912

913
  /* if we are building a chain, store buffer for when we activate
914
915
   * it. This path is taken if we operate in streaming mode. */
  if (ogg->building_chain) {
916
917
    /* bos packets where stored in the header list so we can discard
     * them here*/
918
919
    if (!packet->b_o_s)
      ret = gst_ogg_demux_queue_data (pad, packet);
920
  }
921
922
  /* else we are completely streaming to the peer */
  else {
923
    ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
Wim Taymans's avatar
Wim Taymans committed
924
  }
925
  return ret;
926
927
}

928
929
/* flush at most @npackets from the stream layer. All packets if 
 * @npackets is 0;
930
931
 */
static GstFlowReturn
932
gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
933
934
{
  GstFlowReturn result = GST_FLOW_OK;
935
  gboolean done = FALSE;
936
937
  GstOggDemux *ogg;

938
  ogg = pad->ogg;
939

940
  while (!done) {
941
942
943
    int ret;
    ogg_packet packet;

David Schleef's avatar
David Schleef committed
944
    ret = ogg_stream_packetout (&pad->map.stream, &packet);
945
946
    switch (ret) {
      case 0:
947
        GST_LOG_OBJECT (ogg, "packetout done");
948
949
950
        done = TRUE;
        break;
      case -1:
951
        GST_LOG_OBJECT (ogg, "packetout discont");
952
        gst_ogg_chain_mark_discont (pad->chain);
953
954
        break;
      case 1:
955
        GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
956
        result = gst_ogg_pad_submit_packet (pad, &packet);
957
958
        /* not linked is not a problem, it's possible that we are still
         * collecting headers and that we don't have exposed the pads yet */
959
        if (result == GST_FLOW_NOT_LINKED)
960
          break;
961
        else if (result <= GST_FLOW_UNEXPECTED)
962
          goto could_not_submit;
963
964
965
966
967
968
969
970
        break;
      default:
        GST_WARNING_OBJECT (ogg,
            "invalid return value %d for ogg_stream_packetout, resetting stream",
            ret);
        gst_ogg_pad_reset (pad);
        break;
    }
971
972
973
974
975
976
977
978
979
980
981
    if (npackets > 0) {
      npackets--;
      done = (npackets == 0);
    }
  }
  return result;

  /* ERRORS */
could_not_submit:
  {
    GST_WARNING_OBJECT (ogg,
982
983
        "could not submit packet for stream %08lx, error: %d",
        pad->map.serialno, result);
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
    gst_ogg_pad_reset (pad);
    return result;
  }
}

/* submit a page to an oggpad, this function will then submit all
 * the packets in the page.
 */
static GstFlowReturn
gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
{
  GstFlowReturn result = GST_FLOW_OK;
  GstOggDemux *ogg;
  gboolean continued = FALSE;

  ogg = pad->ogg;

For faster browsing, not all history is shown. View entire blame