gstoggdemux.c 87.2 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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
 * SECTION:element-oggdemux
 * @short_description: a demuxer for ogg files
 *
 * <refsect2>
 * <para>
 * This element demuxes ogg files into their encoded audio and video components.
 * </para>
 * <title>Example pipelines</title>
 * <para>
 * <programlisting>
 * gst-launch -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
 * </programlisting>
 * Decodes the vorbis audio stored inside an ogg container.
 * </para>
 * </refsect2>
 *
 * Last reviewed on 2006-12-30 (0.10.5)
 */


43
44
45
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
46
#include <string.h>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
47
#include <gst/gst-i18n-plugin.h>
48
49
#include <gst/base/gsttypefindhelper.h>

Wim Taymans's avatar
Wim Taymans committed
50
51
#include "gstoggdemux.h"

Stefan Kost's avatar
Stefan Kost committed
52
static const GstElementDetails gst_ogg_demux_details =
j^'s avatar
j^ committed
53
GST_ELEMENT_DETAILS ("Ogg demuxer",
54
55
    "Codec/Demuxer",
    "demux ogg streams (info about ogg: http://xiph.org)",
56
    "Wim Taymans <wim@fluendo.com>");
57

58
#define CHUNKSIZE (8500)        /* this is out of vorbisfile */
59
60
#define SKELETON_FISHEAD_SIZE 64
#define SKELETON_FISBONE_MIN_SIZE 52
61

62
63
64
65
66
67
68
enum
{
  OV_EREAD = -1,
  OV_EFAULT = -2,
  OV_FALSE = -3,
  OV_EOF = -4,
};
69

70
GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug);
71
GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_setup_debug);
72
73
#define GST_CAT_DEFAULT gst_ogg_demux_debug

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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);
}

96
97
98
99
100
101
static GstStaticPadTemplate internaltemplate =
GST_STATIC_PAD_TEMPLATE ("internal",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS_ANY);

102
103
104
105
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);
106
static void gst_ogg_chain_mark_discont (GstOggChain * chain);
107

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

113
114
115
116
static void gst_ogg_pad_class_init (GstOggPadClass * klass);
static void gst_ogg_pad_init (GstOggPad * pad);
static void gst_ogg_pad_dispose (GObject * object);
static void gst_ogg_pad_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
117
118

#if 0
119
120
static const GstFormat *gst_ogg_pad_formats (GstPad * pad);
static const GstEventMask *gst_ogg_pad_event_masks (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
121
#endif
122
static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
123
static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
124
125
static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
126
127
128
129
130
131
132
133
134
static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
    glong serialno);

static gboolean gst_ogg_pad_query_convert (GstOggPad * pad,
    GstFormat src_format, gint64 src_val,
    GstFormat * dest_format, gint64 * dest_val);
static GstClockTime gst_annodex_granule_to_time (gint64 granulepos,
    gint64 granulerate_n, gint64 granulerate_d, guint8 granuleshift);

135
136
static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
    GstOggPad * pad, GstFlowReturn ret);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
137

138
static GstPadClass *ogg_pad_parent_class = NULL;
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

static GType
gst_ogg_pad_get_type (void)
{
  static GType ogg_pad_type = 0;

  if (!ogg_pad_type) {
    static const GTypeInfo ogg_pad_info = {
      sizeof (GstOggPadClass),
      NULL,
      NULL,
      (GClassInitFunc) gst_ogg_pad_class_init,
      NULL,
      NULL,
      sizeof (GstOggPad),
      0,
      (GInstanceInitFunc) gst_ogg_pad_init,
    };

    ogg_pad_type =
Andy Wingo's avatar
Andy Wingo committed
159
        g_type_register_static (GST_TYPE_PAD, "GstOggPad", &ogg_pad_info, 0);
160
161
162
  }
  return ogg_pad_type;
}
163

164
165
static void
gst_ogg_pad_class_init (GstOggPadClass * klass)
166
{
167
  GObjectClass *gobject_class;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
168

169
  gobject_class = (GObjectClass *) klass;
170

171
  ogg_pad_parent_class = g_type_class_peek_parent (klass);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
172

173
174
175
  gobject_class->dispose = gst_ogg_pad_dispose;
  gobject_class->finalize = gst_ogg_pad_finalize;
}
176

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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->first_granule = -1;
  pad->current_granule = -1;

194
195
  pad->start_time = GST_CLOCK_TIME_NONE;
  pad->first_time = GST_CLOCK_TIME_NONE;
196

197
  pad->have_type = FALSE;
198
  pad->continued = NULL;
199
  pad->headers = NULL;
200
}
201

202
static void
203
gst_ogg_pad_dispose (GObject * object)
204
{
205
  GstOggPad *pad = GST_OGG_PAD (object);
206
207
208
  GstPad **elem_pad_p;
  GstElement **element_p;
  GstPad **elem_out_p;
209

210
211
212
  if (pad->element)
    gst_element_set_state (pad->element, GST_STATE_NULL);

213
214
215
216
217
218
  elem_pad_p = &pad->elem_pad;
  element_p = &pad->element;
  elem_out_p = &pad->elem_out;
  gst_object_replace ((GstObject **) elem_pad_p, NULL);
  gst_object_replace ((GstObject **) element_p, NULL);
  gst_object_replace ((GstObject **) elem_out_p, NULL);
219
220
221
222

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

223
  g_list_foreach (pad->headers, (GFunc) gst_mini_object_unref, NULL);
224
225
226
  g_list_free (pad->headers);
  pad->headers = NULL;

227
228
229
230
231
  /* clear continued pages */
  g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
  g_list_free (pad->continued);
  pad->continued = NULL;

232
233
234
  ogg_stream_reset (&pad->stream);

  G_OBJECT_CLASS (ogg_pad_parent_class)->dispose (object);
235
}
236

237
static void
238
gst_ogg_pad_finalize (GObject * object)
239
{
240
  GstOggPad *pad = GST_OGG_PAD (object);
241

242
  ogg_stream_clear (&pad->stream);
Johan Dahlin's avatar
Johan Dahlin committed
243

244
  G_OBJECT_CLASS (ogg_pad_parent_class)->finalize (object);
245
246
}

Wim Taymans's avatar
Wim Taymans committed
247
#if 0
248
static const GstFormat *
249
gst_ogg_pad_formats (GstPad * pad)
250
251
{
  static GstFormat src_formats[] = {
252
253
    GST_FORMAT_DEFAULT,         /* time */
    GST_FORMAT_TIME,            /* granulepos */
254
255
256
257
258
259
260
261
262
263
    0
  };
  static GstFormat sink_formats[] = {
    GST_FORMAT_BYTES,
    GST_FORMAT_DEFAULT,         /* bytes */
    0
  };

  return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
}
Wim Taymans's avatar
Wim Taymans committed
264
#endif
265

Wim Taymans's avatar
Wim Taymans committed
266
#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
267
static const GstEventMask *
268
gst_ogg_pad_event_masks (GstPad * pad)
269
{
270
  static const GstEventMask src_event_masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
271
272
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
    {0,}
273
  };
274

275
  return src_event_masks;
276
}
Wim Taymans's avatar
Wim Taymans committed
277
#endif
278

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
279
static const GstQueryType *
280
gst_ogg_pad_query_types (GstPad * pad)
281
{
282
  static const GstQueryType query_types[] = {
283
    GST_QUERY_DURATION,
Wim Taymans's avatar
Wim Taymans committed
284
    GST_QUERY_SEEKING,
285
286
    0
  };
287

288
  return query_types;
289
290
}

291
292
static GstCaps *
gst_ogg_pad_getcaps (GstPad * pad)
293
{
294
  return gst_caps_ref (GST_PAD_CAPS (pad));
295
296
}

297
static gboolean
Wim Taymans's avatar
Wim Taymans committed
298
gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
299
{
300
  gboolean res = TRUE;
301
302
303
  GstOggDemux *ogg;
  GstOggPad *cur;

304
  ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
305
  cur = GST_OGG_PAD (pad);
306

Wim Taymans's avatar
Wim Taymans committed
307
  switch (GST_QUERY_TYPE (query)) {
Wim Taymans's avatar
Wim Taymans committed
308
    case GST_QUERY_DURATION:
309
310
311
    {
      GstFormat format;

Wim Taymans's avatar
Wim Taymans committed
312
      gst_query_parse_duration (query, &format, NULL);
313
      /* can only get position in time */
314
315
316
      if (format != GST_FORMAT_TIME)
        goto wrong_format;

317
      /* can only return the total time position */
318
      /* FIXME, return time for this specific stream */
Wim Taymans's avatar
Wim Taymans committed
319
      gst_query_set_duration (query, GST_FORMAT_TIME, ogg->total_time);
320
      break;
321
    }
322
323
324
325
326
327
328
329
330
331
332
333
334
335
    case GST_QUERY_SEEKING:
    {
      GstFormat format;

      gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
      if (format == GST_FORMAT_TIME) {
        gst_query_set_seeking (query, GST_FORMAT_TIME, ogg->seekable,
            0, ogg->total_time);
      } else {
        res = FALSE;
      }
      break;
    }

336
    default:
337
      res = gst_pad_query_default (pad, query);
338
339
      break;
  }
340
done:
341
342
  gst_object_unref (ogg);

343
  return res;
344
345
346
347
348
349
350
351

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

354
355
356
357
358
359
360
361
362
363
364
365
366
367
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:
      /* can't seek if we are not seekable, FIXME could pass the
       * seek query upstream after converting it to bytes using
       * the average bitrate of the stream. */
      if (!ogg->seekable) {
368
        GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
369
370
371
372
        goto error;
      }

      /* now do the seek */
373
      res = gst_ogg_demux_perform_seek (ogg, event);
374
      gst_event_unref (event);
375
376
      break;
    default:
377
      GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
378
379
380
381
382
      goto error;
  }

  return res;

383
  /* ERRORS */
384
error:
385
386
387
388
389
  {
    GST_DEBUG_OBJECT (ogg, "error handling event");
    gst_event_unref (event);
    return FALSE;
  }
390
391
}

392
static gboolean
393
gst_ogg_pad_event (GstPad * pad, GstEvent * event)
394
{
395
  gboolean res;
396
  GstOggDemux *ogg;
397
  GstOggPad *cur;
398

399
  ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
400
  cur = GST_OGG_PAD (pad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
401

402
403
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
404
405
406
      /* can't seek if we are not seekable, FIXME could pass the
       * seek query upstream after converting it to bytes using
       * the average bitrate of the stream. */
407
      if (!ogg->seekable) {
408
        GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
409
        goto error;
410
      }
411

412
      /* now do the seek */
413
      res = gst_ogg_demux_perform_seek (ogg, event);
414
      gst_event_unref (event);
415
      break;
416
    default:
417
418
      res = gst_pad_event_default (pad, event);
      break;
419
  }
420
421
422
done:
  gst_object_unref (ogg);

423
  return res;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
424

425
  /* ERRORS */
426
error:
427
  {
428
    GST_DEBUG_OBJECT (ogg, "error handling event");
429
    gst_event_unref (event);
430
431
    res = FALSE;
    goto done;
432
  }
433
434
}

435
static void
436
gst_ogg_pad_reset (GstOggPad * pad)
437
{
438
  ogg_stream_reset (&pad->stream);
439
440
441
442
443
444
445

  /* 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;
446
447
}

448
/* the filter function for selecting the elements we can use in
449
 * autoplugging */
450
static gboolean
451
gst_ogg_demux_factory_filter (GstPluginFeature * feature, GstCaps * caps)
452
{
453
454
  guint rank;
  const gchar *klass;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
455

456
457
458
459
460
461
462
463
464
  /* we only care about element factories */
  if (!GST_IS_ELEMENT_FACTORY (feature))
    return FALSE;

  klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
  /* only demuxers and decoders can play */
  if (strstr (klass, "Demux") == NULL &&
      strstr (klass, "Decoder") == NULL && strstr (klass, "Parse") == NULL) {
    return FALSE;
465
  }
466

467
468
469
470
  /* only select elements with autoplugging rank */
  rank = gst_plugin_feature_get_rank (feature);
  if (rank < GST_RANK_MARGINAL)
    return FALSE;
471

472
473
474
475
476
477
478
479
  GST_DEBUG ("checking factory %s", GST_PLUGIN_FEATURE_NAME (feature));
  /* now see if it is compatible with the caps */
  {
    GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
    const GList *templates;
    GList *walk;

    /* get the templates from the element factory */
480
    templates = gst_element_factory_get_static_pad_templates (factory);
481
482

    for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
483
      GstStaticPadTemplate *templ = walk->data;
484

485
486
487
      /* we only care about the sink templates */
      if (templ->direction == GST_PAD_SINK) {
        GstCaps *intersect;
488
489
        GstCaps *scaps;
        gboolean empty;
490

491
        /* try to intersect the caps with the caps of the template */
492
493
494
495
496
497
        scaps = gst_static_caps_get (&templ->static_caps);
        intersect = gst_caps_intersect (caps, scaps);
        gst_caps_unref (scaps);

        empty = gst_caps_is_empty (intersect);
        gst_caps_unref (intersect);
498
499

        /* check if the intersection is empty */
500
        if (!empty) {
501
502
503
          /* non empty intersection, we can use this element */
          goto found;
        }
504
      }
505
    }
506
  }
507
  return FALSE;
508

509
510
found:
  return TRUE;
511
}
512

513
514
515
/* function used to sort element features */
static gint
compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
516
{
517
  gint diff;
518

519
520
521
522
523
  diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
  if (diff != 0)
    return diff;
  return strcmp (gst_plugin_feature_get_name (f2),
      gst_plugin_feature_get_name (f1));
524
525
}

526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
/* called when the skeleton fishead is found. Caller ensures the packet is
 * precisely the correct size; we don't re-check this here. */
static void
gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet)
{
  GstOggDemux *ogg = pad->ogg;
  guint8 *data = packet->packet;
  guint16 major, minor;
  gint64 prestime_n, prestime_d;
  gint64 basetime_n, basetime_d;

  /* skip "fishead\0" */
  data += 8;
  major = GST_READ_UINT16_LE (data);
  data += 2;
  minor = GST_READ_UINT16_LE (data);
  data += 2;
  prestime_n = (gint64) GST_READ_UINT64_LE (data);
  data += 8;
  prestime_d = (gint64) GST_READ_UINT64_LE (data);
  data += 8;
  basetime_n = (gint64) GST_READ_UINT64_LE (data);
  data += 8;
  basetime_d = (gint64) GST_READ_UINT64_LE (data);
  data += 8;

  ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
  ogg->have_fishead = TRUE;
  pad->is_skeleton = TRUE;
  pad->start_time = GST_CLOCK_TIME_NONE;
  pad->first_granule = -1;
  pad->first_time = GST_CLOCK_TIME_NONE;
  GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %"
      GST_TIME_FORMAT ")", GST_TIME_ARGS (ogg->basetime));
}

/* function called when a skeleton fisbone is found. Caller ensures that
 * the packet length is sufficient */
static void
gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet)
{
  GstOggPad *fisbone_pad;
  gint64 start_granule;
  guint32 serialno;
  guint8 *data = packet->packet;

  /* skip "fisbone\0" */
  data += 8;
  /* skip headers offset */
  data += 4;
  serialno = GST_READ_UINT32_LE (data);

  fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
  if (fisbone_pad) {
    if (fisbone_pad->have_fisbone)
      /* already parsed */
      return;

    fisbone_pad->have_fisbone = TRUE;

    data += 4;
    /* skip number of headers */
    data += 4;
    fisbone_pad->granulerate_n = GST_READ_UINT64_LE (data);
    data += 8;
    fisbone_pad->granulerate_d = GST_READ_UINT64_LE (data);
    data += 8;
    start_granule = GST_READ_UINT64_LE (data);
    data += 8;
    fisbone_pad->preroll = GST_READ_UINT32_LE (data);
    data += 4;
    fisbone_pad->granuleshift = GST_READ_UINT8 (data);
    data += 1;
    /* padding */
    data += 3;

    fisbone_pad->start_time = gst_annodex_granule_to_time (start_granule,
        fisbone_pad->granulerate_n, fisbone_pad->granulerate_d,
        fisbone_pad->granuleshift);

    GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed "
        "(serialno: %" G_GUINT32_FORMAT " start time: %" GST_TIME_FORMAT
        " granulerate_n: %" G_GINT64_FORMAT " granulerate_d: %" G_GINT64_FORMAT
        " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
        serialno, GST_TIME_ARGS (fisbone_pad->start_time),
        fisbone_pad->granulerate_n, fisbone_pad->granulerate_d,
        fisbone_pad->preroll, fisbone_pad->granuleshift);
  } else {
    GST_WARNING_OBJECT (pad->ogg,
        "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
        serialno);
  }
}

/* function called to convert a granulepos to a timestamp */
static gboolean
gst_ogg_pad_query_convert (GstOggPad * pad, GstFormat src_format,
    gint64 src_val, GstFormat * dest_format, gint64 * dest_val)
{
  gboolean res;

  if (src_val == -1) {
    *dest_val = -1;
    return TRUE;
  }

  if (!pad->have_fisbone && pad->elem_pad == NULL)
    return FALSE;

  switch (src_format) {
    case GST_FORMAT_DEFAULT:
      if (pad->have_fisbone && *dest_format == GST_FORMAT_TIME) {
        *dest_val = gst_annodex_granule_to_time (src_val,
            pad->granulerate_n, pad->granulerate_d, pad->granuleshift);

        res = TRUE;
      } else {
        if (pad->elem_pad == NULL)
          res = FALSE;
        else
          res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
              dest_format, dest_val);
      }

      break;
    default:
      if (pad->elem_pad == NULL)
        res = FALSE;
      else
        res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
            dest_format, dest_val);
  }

  return res;
}

662
663
664
/* function called by the internal decoder elements when it outputs
 * a buffer. We use it to get the first timestamp of the stream 
 */
665
666
static GstFlowReturn
gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer)
667
{
668
  GstOggPad *oggpad;
669
  GstOggDemux *ogg;
670
  GstClockTime timestamp;
671

672
  oggpad = gst_pad_get_element_private (pad);
673
  ogg = GST_OGG_DEMUX (oggpad->ogg);
674

675
  timestamp = GST_BUFFER_TIMESTAMP (buffer);
676
677
  GST_DEBUG_OBJECT (oggpad, "received buffer from internal pad, TS=%"
      GST_TIME_FORMAT "=%" G_GINT64_FORMAT, GST_TIME_ARGS (timestamp),
678
      timestamp);
679

680
  if (oggpad->start_time == GST_CLOCK_TIME_NONE) {
681
    oggpad->start_time = timestamp;
682
683
    GST_DEBUG_OBJECT (oggpad, "new start time: %" GST_TIME_FORMAT,
        GST_TIME_ARGS (timestamp));
684
  }
685

Wim Taymans's avatar
Wim Taymans committed
686
687
  gst_buffer_unref (buffer);

688
  return GST_FLOW_OK;
689
690
}

Edward Hervey's avatar
Edward Hervey committed
691
692
693
694
695
696
697
698
699
700
701
702
703
704
static void
internal_element_pad_added_cb (GstElement * element, GstPad * pad,
    GstOggPad * oggpad)
{
  if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
    if (!(gst_pad_link (pad, oggpad->elem_out) == GST_PAD_LINK_OK)) {
      GST_ERROR ("Really couldn't find a valid pad");
    }
    oggpad->dynamic = FALSE;
    g_signal_handler_disconnect (element, oggpad->padaddedid);
    oggpad->padaddedid = 0;
  }
}

705
706
707
708
709
710
711
712
713
/* runs typefind on the packet, which is assumed to be the first
 * packet in the stream.
 * 
 * Based on the type returned from the typefind function, an element
 * is created to help in conversion between granulepos and timestamps
 * so that we can do decent seeking.
 */
static gboolean
gst_ogg_pad_typefind (GstOggPad * pad, ogg_packet * packet)
714
{
715
  GstBuffer *buf;
716
717
  GstCaps *caps;
  GstElement *element = NULL;
718
719

#ifndef GST_DISABLE_GST_DEBUG
720
  GstOggDemux *ogg = pad->ogg;
721
#endif
722

723
724
  if (GST_PAD_CAPS (pad) != NULL)
    return TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
725

726
727
728
729
730
731
732
733
734
735
  /* The ogg spec defines that the first packet of an ogg stream must identify
   * the stream. Therefore ogg can use a simplified approach to typefinding
   * and only needs to check the first packet */
  buf = gst_buffer_new ();
  GST_BUFFER_DATA (buf) = packet->packet;
  GST_BUFFER_SIZE (buf) = packet->bytes;
  GST_BUFFER_OFFSET (buf) = 0;

  caps = gst_type_find_helper_for_buffer (GST_OBJECT (pad), buf, NULL);
  gst_buffer_unref (buf);
736

737
738
  if (caps == NULL) {
    GST_WARNING_OBJECT (ogg,
739
        "couldn't find caps for stream with serial %08x", pad->serialno);
740
741
742
    caps = gst_caps_new_simple ("application/octet-stream", NULL);
  } else {
    /* ogg requires you to use a decoder element to define the
743
744
745
746
     * meaning of granulepos etc so we make one. We also do this if
     * we are in the streaming mode to calculate the first timestamp. */
    GList *factories;

747
748
    GST_LOG_OBJECT (ogg, "found caps: %" GST_PTR_FORMAT, caps);

749
    /* first filter out the interesting element factories */
750
    factories = gst_default_registry_feature_filter (
751
752
753
754
755
756
757
758
759
760
761
        (GstPluginFeatureFilter) gst_ogg_demux_factory_filter, FALSE, caps);

    /* sort them according to their ranks */
    factories = g_list_sort (factories, (GCompareFunc) compare_ranks);

    /* then pick the first factory to create an element */
    if (factories) {
      element =
          gst_element_factory_create (GST_ELEMENT_FACTORY (factories->data),
          NULL);
      if (element) {
Johan Dahlin's avatar
Johan Dahlin committed
762
763
        GstPadTemplate *template;

764
765
766
767
768
769
770
        /* this is ours */
        gst_object_ref (element);
        gst_object_sink (GST_OBJECT (element));

        /* FIXME, it might not be named "sink" */
        pad->elem_pad = gst_element_get_pad (element, "sink");
        gst_element_set_state (element, GST_STATE_PAUSED);
Johan Dahlin's avatar
Johan Dahlin committed
771
772
        template = gst_static_pad_template_get (&internaltemplate);
        pad->elem_out = gst_pad_new_from_template (template, "internal");
773
774
775
        gst_pad_set_chain_function (pad->elem_out, gst_ogg_pad_internal_chain);
        gst_pad_set_element_private (pad->elem_out, pad);
        gst_pad_set_active (pad->elem_out, TRUE);
776
        gst_object_unref (template);
777
778

        /* and this pad may not be named src.. */
Edward Hervey's avatar
Edward Hervey committed
779
        /* And it might also not exist at this time... */
780
781
782
783
        {
          GstPad *p;

          p = gst_element_get_pad (element, "src");
Edward Hervey's avatar
Edward Hervey committed
784
785
786
787
788
789
790
791
          if (p) {
            gst_pad_link (p, pad->elem_out);
            gst_object_unref (p);
          } else {
            pad->dynamic = TRUE;
            pad->padaddedid = g_signal_connect (G_OBJECT (element),
                "pad-added", G_CALLBACK (internal_element_pad_added_cb), pad);
          }
792
793
794
        }
      }
    }
795
    g_list_free (factories);
796
797
  }
  pad->element = element;
798

799
800
801
802
  gst_pad_set_caps (GST_PAD (pad), caps);
  gst_caps_unref (caps);

  return TRUE;
803
804
}

805
806
807
808
809
810
/* send packet to internal element */
static GstFlowReturn
gst_ogg_demux_chain_elem_pad (GstOggPad * pad, ogg_packet * packet)
{
  GstBuffer *buf;
  GstFlowReturn ret;
811
812

#ifndef GST_DISABLE_GST_DEBUG
813
  GstOggDemux *ogg = pad->ogg;
814
#endif
815
816
817
818
819

  /* initialize our internal decoder with packets */
  if (!pad->elem_pad)
    goto no_decoder;

820
  GST_DEBUG_OBJECT (ogg, "%p init decoder serial %08x", pad, pad->serialno);
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836

  buf = gst_buffer_new_and_alloc (packet->bytes);
  memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
  gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
  GST_BUFFER_OFFSET (buf) = -1;
  GST_BUFFER_OFFSET_END (buf) = packet->granulepos;

  ret = gst_pad_chain (pad->elem_pad, buf);
  if (GST_FLOW_IS_FATAL (ret))
    goto decoder_error;

  return ret;

no_decoder:
  {
    GST_WARNING_OBJECT (ogg,
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
837
        "pad %p does not have elem_pad, no decoder ?", pad);
838
839
840
841
842
843
844
845
846
    return GST_FLOW_ERROR;
  }
decoder_error:
  {
    GST_WARNING_OBJECT (ogg, "internal decoder error");
    return GST_FLOW_ERROR;
  }
}

Wim Taymans's avatar
Wim Taymans committed
847
848
849
/* queue data, basically takes the packet, puts it in a buffer and store the
 * buffer in the headers list.
 */
850
851
852
853
static GstFlowReturn
gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
{
  GstBuffer *buf;
854
855

#ifndef GST_DISABLE_GST_DEBUG
856
  GstOggDemux *ogg = pad->ogg;
857
#endif
858

859
  GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x", pad, pad->serialno);
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875

  buf = gst_buffer_new_and_alloc (packet->bytes);
  memcpy (buf->data, packet->packet, packet->bytes);
  GST_BUFFER_OFFSET (buf) = -1;
  GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
  pad->headers = g_list_append (pad->headers, buf);

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

/* send packet to internal element */
static GstFlowReturn
gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
{
  GstBuffer *buf;
876
  GstFlowReturn ret, cret;
877
  GstOggDemux *ogg = pad->ogg;
878
879
880
881
882
  GstFormat format;
  gint64 current_time;
  GstOggChain *chain;

  GST_DEBUG_OBJECT (ogg,
883
      "%p streaming to peer serial %08x", pad, pad->serialno);
884

Andy Wingo's avatar
Andy Wingo committed
885
  ret =
886
887
888
889
890
      gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad),
      GST_BUFFER_OFFSET_NONE, packet->bytes, GST_PAD_CAPS (pad), &buf);

  /* combine flows */
  cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
891
892
  if (ret != GST_FLOW_OK)
    goto no_buffer;
893

894
895
  /* copy packet in buffer */
  memcpy (buf->data, packet->packet, packet->bytes);
896

897
898
  GST_BUFFER_OFFSET (buf) = -1;
  GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
899

900
901
902
903
904
905
  /* Mark discont on the buffer */
  if (pad->discont) {
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
    pad->discont = FALSE;
  }

906
907
908
909
  ret = gst_pad_push (GST_PAD_CAST (pad), buf);

  /* combine flows */
  cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
910

911
912
913
  /* we're done with skeleton stuff */
  if (pad->is_skeleton)
    goto done;
914

915
916
917
918
  /* check if valid granulepos, then we can calculate the current
   * position */
  if (packet->granulepos < 0)
    goto done;
919

920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
  /* store current granule pos */
  ogg->current_granule = packet->granulepos;

  /* convert to time */
  format = GST_FORMAT_TIME;
  if (!gst_ogg_pad_query_convert (pad,
          GST_FORMAT_DEFAULT, packet->granulepos, &format,
          (gint64 *) & current_time))
    goto convert_failed;

  /* convert to stream time */
  if ((chain = pad->chain))
    current_time = current_time - chain->segment_start + chain->begin_time;

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

937
938
939
940
  GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
      GST_TIME_ARGS (current_time));

done:
941
942
  /* return combined flow result */
  return cret;
943
944
945
946
947

  /* special cases */
no_buffer:
  {
    GST_DEBUG_OBJECT (ogg,
948
949
950
951
        "%p could not get buffer from peer %08x, %d (%s), combined %d (%s)",
        pad, pad->serialno, ret, gst_flow_get_name (ret),
        cret, gst_flow_get_name (cret));
    goto done;
952
953
954
955
  }
convert_failed:
  {
    GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
956
    goto done;
957
958
959
  }
}

960
961
962
963
964
965
/* 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)
966
{
967
  gint64 granule;
968
  GstFlowReturn ret;
969
970
971

  GstOggDemux *ogg = pad->ogg;

972
  GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x", pad, pad->serialno);
973

974
  if (!pad->have_type) {
975
976
977
978
    if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE &&
        !memcmp (packet->packet, "fishead\0", 8)) {
      gst_ogg_pad_parse_skeleton_fishead (pad, packet);
    }
979
980
981
982
    gst_ogg_pad_typefind (pad, packet);
    pad->have_type = TRUE;
  }

983
984
985
986
987
  if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
      !memcmp (packet->packet, "fisbone\0", 8)) {
    gst_ogg_pad_parse_skeleton_fisbone (pad, packet);
  }

988
989
  granule = packet->granulepos;
  if (granule != -1) {
990
    GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
991
    ogg->current_granule = granule;
992
    pad->current_granule = granule;
993
994
995
996
997
    /* granulepos 0 and -1 are considered header packets.
     * Note that since theora is busted, it can create non-header
     * packets with 0 granulepos. We will correct for this when our
     * internal decoder produced a frame and we don't have a
     * granulepos because in that case the granulpos must have been 0 */
998
    if (pad->first_granule == -1 && granule != 0) {
999
1000
      GST_DEBUG_OBJECT (ogg, "%p found first granulepos %" G_GINT64_FORMAT, pad,
          granule);
1001
1002
1003
1004
      pad->first_granule = granule;
    }
  }

1005
1006
1007
1008
  if (granule != -1 && memcmp (packet->packet, "KW-DIRAC", 8) == 0) {
    return GST_FLOW_OK;
  }

1009
  /* no start time known, stream to internal plugin to
1010
1011
   * get time. always stream to the skeleton decoder */
  if (pad->start_time == GST_CLOCK_TIME_NONE || pad->is_skeleton) {
1012
1013
1014
1015
1016
1017
    ret = gst_ogg_demux_chain_elem_pad (pad, packet);
  }
  /* we know the start_time of the pad data, see if we
   * can activate the complete chain if this is a dynamic
   * chain. */
  if (pad->start_time != GST_CLOCK_TIME_NONE) {
1018
1019
    GstOggChain *chain = pad->chain;

1020
1021
1022
    /* correction for busted ogg, if the internal decoder outputed
     * a timestamp but we did not get a granulepos, it must have
     * been 0 and the time is therefore also 0 */
1023
    if (pad->first_granule == -1) {
1024
1025
1026
1027
1028
      GST_DEBUG_OBJECT (ogg, "%p found start time without granulepos", pad);
      pad->first_granule = 0;
      pad->first_time = 0;
    }

1029
    /* check if complete chain has start time */
1030
1031
1032
1033
    if (chain == ogg->building_chain) {

      /* see if we have enough info to activate the chain */
      if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
1034
        GstEvent *event;
1035
        GstClockTime segment_start, segment_stop, segment_time;
1036

1037
        GST_DEBUG_OBJECT (ogg, "chain->begin_time:    %" GST_TIME_FORMAT,
1038
            GST_TIME_ARGS (chain->begin_time));
1039
        GST_DEBUG_OBJECT (ogg, "chain->segment_start: %" GST_TIME_FORMAT,
1040
            GST_TIME_ARGS (chain->segment_start));
1041
        GST_DEBUG_OBJECT (ogg, "chain->segment_stop:  %" GST_TIME_FORMAT,
1042
1043
            GST_TIME_ARGS (chain->segment_stop));

1044
        if (chain->begin_time != GST_CLOCK_TIME_NONE) {
1045
          segment_time = chain->begin_time;
1046
          segment_start = chain->segment_start - chain->begin_time;
1047
1048
1049
1050
1051
          if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
            segment_stop = chain->segment_stop - chain->begin_time;
          } else {
            segment_stop = GST_CLOCK_TIME_NONE;
          }
1052
1053
1054
        } else {
          segment_start = chain->segment_start;
          segment_stop = chain->segment_stop;
1055
          segment_time = 0;
1056
        }
1057

1058
        GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
1059
            GST_TIME_ARGS (segment_start));
1060
        GST_DEBUG_OBJECT (ogg, "segment_stop:  %" GST_TIME_FORMAT,
1061
            GST_TIME_ARGS (segment_stop));
1062
1063
        GST_DEBUG_OBJECT (ogg, "segment_time:  %" GST_TIME_FORMAT,
            GST_TIME_ARGS (segment_time));
1064

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

1069
        gst_ogg_demux_activate_chain (ogg, chain, event);
1070
1071

        ogg->building_chain = NULL;
1072
      }
1073
1074
    }
  }
Wim Taymans's avatar
Wim Taymans committed
1075

1076
  /* if we are building a chain, store buffer for when we activate
1077
1078
1079
   * it. This path is taken if we operate in streaming mode. */
  if (ogg->building_chain) {
    ret = gst_ogg_demux_queue_data (pad, packet);
1080
  }
1081
1082
1083
  /* else we are completely streaming to the peer */
  else {
    ret = gst_ogg_demux_chain_peer (pad, packet);
Wim Taymans's avatar
Wim Taymans committed
1084
  }
1085
  return ret;
1086
1087
}

1088
1089
/* flush at most @npackets from the stream layer. All packets if 
 * @npackets is 0;
1090
1091
 */
static GstFlowReturn
1092
gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1093
1094
{
  GstFlowReturn result = GST_FLOW_OK;
1095
  gboolean done = FALSE;
1096
1097
  GstOggDemux *ogg;

1098
  ogg = pad->ogg;
1099

1100
  while (!done) {
1101
1102
1103
    int ret;
    ogg_packet packet;

1104
1105
1106
    ret = ogg_stream_packetout (&pad->stream, &packet);
    switch (ret) {
      case 0:
1107
        GST_LOG_OBJECT (ogg, "packetout done");
1108
1109
1110
        done = TRUE;
        break;
      case -1:
1111
        GST_LOG_OBJECT (ogg, "packetout discont");