gstoggdemux.c 66.9 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 22 23 24 25 26
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <ogg/ogg.h>
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
27
#include <string.h>
28

29
#define CHUNKSIZE (8500)        /* this is out of vorbisfile */
30

31 32 33 34 35 36 37
enum
{
  OV_EREAD = -1,
  OV_EFAULT = -2,
  OV_FALSE = -3,
  OV_EOF = -4,
};
38

39
GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug);
40
GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_setup_debug);
41 42
#define GST_CAT_DEFAULT gst_ogg_demux_debug

43 44 45 46 47 48 49 50 51
#define GST_TYPE_OGG_PAD (gst_ogg_pad_get_type())
#define GST_OGG_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_PAD, GstOggPad))
#define GST_OGG_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_PAD, GstOggPad))
#define GST_IS_OGG_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_PAD))
#define GST_IS_OGG_PAD_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_PAD))

typedef struct _GstOggPad GstOggPad;
typedef struct _GstOggPadClass GstOggPadClass;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
52
#define GST_TYPE_OGG_DEMUX (gst_ogg_demux_get_type())
53 54 55 56 57
#define GST_OGG_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_DEMUX, GstOggDemux))
#define GST_OGG_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_DEMUX, GstOggDemux))
#define GST_IS_OGG_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_DEMUX))
#define GST_IS_OGG_DEMUX_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_DEMUX))

58 59
static GType gst_ogg_demux_get_type (void);

60 61 62
typedef struct _GstOggDemux GstOggDemux;
typedef struct _GstOggDemuxClass GstOggDemuxClass;

63 64
/* all information needed for one ogg chain (relevant for chained bitstreams) */
typedef struct _GstOggChain
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
65
{
66
  GstOggDemux *ogg;
67

68 69 70
  gint64 offset;                /* starting offset of chain */
  gint64 end_offset;            /* end offset of chain */
  gint64 bytes;                 /* number of bytes */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
71

72
  gboolean have_bos;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
73

74
  GArray *streams;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
75

76 77
  GstClockTime total_time;      /* the total time of this chain, this is the MAX of
                                   the totals of all streams */
78 79 80 81
  GstClockTime start_time;      /* the timestamp of the first sample, this is the MIN of
                                   the start times of all streams. */
  GstClockTime last_time;       /* the timestamp of the last page, this is the MAX of the
                                   streams. */
82 83
  GstClockTime begin_time;      /* when this chain starts in the stream */
} GstOggChain;
84

85
/* different modes for the pad */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
86 87
typedef enum
{
88 89 90
  GST_OGG_PAD_MODE_INIT,        /* we are feeding our internal decoder to get info */
  GST_OGG_PAD_MODE_STREAMING,   /* we are streaming buffers to the outside */
} GstOggPadMode;
91

Andy Wingo Wingo's avatar
Andy Wingo Wingo committed
92 93
#define PARENT                GstPad
#define PARENTCLASS   GstPadClass
94

95 96
/* all information needed for one ogg stream */
struct _GstOggPad
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
97
{
98
  PARENT pad;                   /* subclass GstPad */
99

100
  gboolean have_type;
101
  GstOggPadMode mode;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
102

103 104 105
  GstPad *elem_pad;             /* sinkpad of internal element */
  GstElement *element;          /* internal element */
  GstPad *elem_out;             /* our sinkpad to receive buffers form the internal element */
106

107 108
  GstOggChain *chain;           /* the chain we are part of */
  GstOggDemux *ogg;             /* the ogg demuxer we are part of */
109

110
  GList *headers;
111

112 113 114
  gint serialno;
  gint64 packetno;
  gint64 current_granule;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
115

116
  GstClockTime start_time;      /* the timestamp of the first sample */
117

118 119
  gint64 first_granule;         /* the granulepos of first page == first sample in next page */
  GstClockTime first_time;      /* the timestamp of the second page */
120

121 122 123 124 125 126
  GstClockTime last_time;       /* the timestamp of the last page == last sample */
  gint64 last_granule;          /* the granulepos of the last page */

  GstClockTime total_time;      /* the total time of this stream */

  ogg_stream_state stream;
127 128
};

129
struct _GstOggPadClass
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
130
{
131
  PARENTCLASS parent_class;
132 133
};

134 135 136 137
#define GST_CHAIN_LOCK(ogg)	g_mutex_lock((ogg)->chain_lock)
#define GST_CHAIN_UNLOCK(ogg)	g_mutex_unlock((ogg)->chain_lock)

struct _GstOggDemux
138
{
139
  GstElement element;
140

141
  GstPad *sinkpad;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
142

143 144
  gint64 length;
  gint64 offset;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
145

146
  gboolean seekable;
147
  gboolean running;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
148

149
  gboolean need_chains;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
150

151 152 153
  /* state */
  GMutex *chain_lock;           /* we need the lock to protect the chains */
  GArray *chains;               /* list of chains we know */
154
  GstClockTime start_time;      /* the start time of the first chain */
155 156 157 158
  GstClockTime total_time;      /* the total time of this ogg, this is the sum of
                                   the totals of all chains */
  GstOggChain *current_chain;
  GstOggChain *building_chain;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
159

160 161 162 163 164
  /* playback start/stop positions */
  GstClockTime segment_start;
  GstClockTime segment_stop;
  gboolean segment_play;

165 166 167
  gint64 current_granule;
  GstClockTime current_time;

168 169 170
  /* ogg stuff */
  ogg_sync_state sync;
};
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
171

172 173 174 175
struct _GstOggDemuxClass
{
  GstElementClass parent_class;
};
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
176

177 178 179 180 181 182
static GstStaticPadTemplate internaltemplate =
GST_STATIC_PAD_TEMPLATE ("internal",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS_ANY);

183 184 185 186 187 188
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);

static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg, gboolean flush);
189

190 191 192 193
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
194 195

#if 0
196 197
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
198
#endif
199
static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
200
static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
201 202 203
static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
static GstCaps *gst_ogg_type_find (ogg_packet * packet);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
204

205
static GstPadClass *ogg_pad_parent_class = NULL;
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225

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 Wingo's avatar
Andy Wingo Wingo committed
226
        g_type_register_static (GST_TYPE_PAD, "GstOggPad", &ogg_pad_info, 0);
227 228 229
  }
  return ogg_pad_type;
}
230

231 232
static void
gst_ogg_pad_class_init (GstOggPadClass * klass)
233
{
234
  GObjectClass *gobject_class;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
235

236
  gobject_class = (GObjectClass *) klass;
237

238
  ogg_pad_parent_class = g_type_class_ref (GST_TYPE_PAD);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
239

240 241 242
  gobject_class->dispose = gst_ogg_pad_dispose;
  gobject_class->finalize = gst_ogg_pad_finalize;
}
243

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
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->last_granule = -1;
  pad->current_granule = -1;

262 263 264 265
  pad->start_time = GST_CLOCK_TIME_NONE;
  pad->first_time = GST_CLOCK_TIME_NONE;
  pad->last_time = GST_CLOCK_TIME_NONE;
  pad->total_time = GST_CLOCK_TIME_NONE;
266

267
  pad->have_type = FALSE;
268
  pad->headers = NULL;
269
}
270

271
static void
272
gst_ogg_pad_dispose (GObject * object)
273
{
274
  GstOggPad *pad = GST_OGG_PAD (object);
275

276 277 278 279 280 281 282
  gst_object_replace ((GstObject **) (&pad->elem_pad), NULL);
  gst_object_replace ((GstObject **) (&pad->element), NULL);
  gst_object_replace ((GstObject **) (&pad->elem_out), NULL);

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

283
  g_list_foreach (pad->headers, (GFunc) gst_mini_object_unref, NULL);
284 285 286 287 288 289
  g_list_free (pad->headers);
  pad->headers = NULL;

  ogg_stream_reset (&pad->stream);

  G_OBJECT_CLASS (ogg_pad_parent_class)->dispose (object);
290
}
291

292
static void
293
gst_ogg_pad_finalize (GObject * object)
294
{
295
  GstOggPad *pad = GST_OGG_PAD (object);
296

297
  ogg_stream_clear (&pad->stream);
Johan Dahlin's avatar
Johan Dahlin committed
298

299
  G_OBJECT_CLASS (ogg_pad_parent_class)->finalize (object);
300 301
}

Wim Taymans's avatar
Wim Taymans committed
302
#if 0
303
static const GstFormat *
304
gst_ogg_pad_formats (GstPad * pad)
305 306
{
  static GstFormat src_formats[] = {
307 308
    GST_FORMAT_DEFAULT,         /* time */
    GST_FORMAT_TIME,            /* granulepos */
309 310 311 312 313 314 315 316 317 318
    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
319
#endif
320

Wim Taymans's avatar
Wim Taymans committed
321
#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
322
static const GstEventMask *
323
gst_ogg_pad_event_masks (GstPad * pad)
324
{
325
  static const GstEventMask src_event_masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
326 327
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
    {0,}
328
  };
329

330
  return src_event_masks;
331
}
Wim Taymans's avatar
Wim Taymans committed
332
#endif
333

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
334
static const GstQueryType *
335
gst_ogg_pad_query_types (GstPad * pad)
336
{
337
  static const GstQueryType query_types[] = {
338 339 340
    GST_QUERY_POSITION,
    0
  };
341

342
  return query_types;
343 344
}

345 346
static GstCaps *
gst_ogg_pad_getcaps (GstPad * pad)
347
{
348
  return gst_caps_ref (GST_PAD_CAPS (pad));
349 350
}

351
static gboolean
Wim Taymans's avatar
Wim Taymans committed
352
gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
353
{
354
  gboolean res = TRUE;
355 356 357
  GstOggDemux *ogg;
  GstOggPad *cur;

358 359
  ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad));
  cur = GST_OGG_PAD (pad);
360

Wim Taymans's avatar
Wim Taymans committed
361
  switch (GST_QUERY_TYPE (query)) {
362
    case GST_QUERY_POSITION:
Wim Taymans's avatar
Wim Taymans committed
363
      gst_query_set_position (query, GST_FORMAT_TIME, -1, ogg->total_time);
364
      break;
Wim Taymans's avatar
Wim Taymans committed
365 366 367
    case GST_QUERY_CONVERT:
      /* hmm .. */
      res = FALSE;
368 369
      break;
    default:
370
      res = FALSE;
371 372
      break;
  }
373

374 375 376 377
  return res;
}

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

384 385
  ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad));
  cur = GST_OGG_PAD (pad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
386

387 388
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
389
    {
390 391
      gboolean running;
      gboolean flush;
392 393 394 395
      GstFormat format;
      GstSeekFlags flags;
      GstSeekType cur_type, stop_type;
      gint64 cur, stop;
396

397 398 399
      /* 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. */
400 401
      if (!ogg->seekable) {
        res = FALSE;
402
        GST_DEBUG ("seek on non seekable stream");
403
        goto done_unref;
404
      }
405 406 407
      gst_event_parse_seek (event, NULL, &format, &flags,
          &cur_type, &cur, &stop_type, &stop);

408
      /* we can only seek on time */
409
      if (format != GST_FORMAT_TIME) {
410
        res = FALSE;
411
        GST_DEBUG ("can only seek on TIME");
412
        goto done_unref;
413
      }
414 415 416 417
      ogg->segment_start = cur;
      ogg->segment_stop = stop;
      ogg->segment_play = !!(flags & GST_SEEK_FLAG_SEGMENT);
      flush = !!(flags & GST_SEEK_FLAG_FLUSH);
418
      gst_event_unref (event);
419

420 421 422 423 424 425 426 427 428
      GST_DEBUG ("segment positions set to %" GST_TIME_FORMAT "-%"
          GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment_start),
          GST_TIME_ARGS (ogg->segment_stop));

      /* check if we can do the seek now */
      GST_LOCK (ogg);
      running = ogg->running;
      GST_UNLOCK (ogg);

429
      /* now do the seek */
430 431 432 433
      if (running) {
        res = gst_ogg_demux_perform_seek (ogg, flush);
      } else
        res = TRUE;
434
      break;
435
    }
436
    default:
437 438
      res = gst_pad_event_default (pad, event);
      break;
439 440
  }

441
  return res;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
442

443
done_unref:
444
  GST_DEBUG ("error handling event");
445
  gst_event_unref (event);
446 447 448
  return res;
}

449
static void
450
gst_ogg_pad_reset (GstOggPad * pad)
451
{
452 453
  ogg_stream_reset (&pad->stream);
  /* FIXME: need a discont here */
454 455
}

456 457
/* the filter function for selecting the elements we can use in
 *  * autoplugging */
458
static gboolean
459
gst_ogg_demux_factory_filter (GstPluginFeature * feature, GstCaps * caps)
460
{
461 462
  guint rank;
  const gchar *klass;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
463

464 465 466 467 468 469 470 471 472
  /* 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;
473
  }
474

475 476 477 478
  /* only select elements with autoplugging rank */
  rank = gst_plugin_feature_get_rank (feature);
  if (rank < GST_RANK_MARGINAL)
    return FALSE;
479

480 481 482 483 484 485 486 487
  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 */
488
    templates = gst_element_factory_get_static_pad_templates (factory);
489 490

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

493 494 495
      /* we only care about the sink templates */
      if (templ->direction == GST_PAD_SINK) {
        GstCaps *intersect;
496

497
        /* try to intersect the caps with the caps of the template */
498 499
        intersect = gst_caps_intersect (caps,
            gst_static_caps_get (&templ->static_caps));
500 501 502 503 504 505 506 507

        /* check if the intersection is empty */
        if (!gst_caps_is_empty (intersect)) {
          /* non empty intersection, we can use this element */
          gst_caps_unref (intersect);
          goto found;
        }
        gst_caps_unref (intersect);
508
      }
509
    }
510
  }
511
  return FALSE;
512

513 514
found:
  return TRUE;
515
}
516

517 518 519
/* function used to sort element features */
static gint
compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
520
{
521
  gint diff;
522

523 524 525 526 527
  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));
528 529
}

530 531
static GstFlowReturn
gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer)
532
{
533
  GstOggPad *oggpad;
534
  GstOggDemux *ogg;
535
  GstClockTime timestamp;
536

537
  oggpad = gst_pad_get_element_private (pad);
538
  ogg = GST_OGG_DEMUX (oggpad->ogg);
539

540 541 542
  timestamp = GST_BUFFER_TIMESTAMP (buffer);
  GST_DEBUG_OBJECT (oggpad, "received buffer from iternal pad, TS=%lld",
      timestamp);
543

544
  if (oggpad->start_time == GST_CLOCK_TIME_NONE) {
545
    oggpad->start_time = timestamp;
546 547
    ogg->current_time = timestamp;
  }
548

Wim Taymans's avatar
Wim Taymans committed
549 550
  gst_buffer_unref (buffer);

551
  return GST_FLOW_OK;
552 553
}

554 555 556 557 558 559 560 561 562
/* 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)
563
{
564 565 566
  GstCaps *caps;
  GstElement *element = NULL;
  GstOggDemux *ogg = pad->ogg;
567

568 569
  if (GST_PAD_CAPS (pad) != NULL)
    return TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
570

571
  caps = gst_ogg_type_find (packet);
572

573 574 575 576 577 578
  if (caps == NULL) {
    GST_WARNING_OBJECT (ogg,
        "couldn't find caps for stream with serial %08lx", pad->serialno);
    caps = gst_caps_new_simple ("application/octet-stream", NULL);
  } else {
    /* ogg requires you to use a decoder element to define the
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
     * 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;

    /* first filter out the interesting element factories */
    factories = gst_registry_pool_feature_filter (
        (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) {
        /* 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);
        pad->elem_out =
            gst_pad_new_from_template (gst_static_pad_template_get
            (&internaltemplate), "internal");
        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);

        /* and this pad may not be named src.. */
        {
          GstPad *p;

          p = gst_element_get_pad (element, "src");
          gst_pad_link (p, pad->elem_out);
          gst_object_unref (p);
617 618 619
        }
      }
    }
620
    g_list_free (factories);
621 622
  }
  pad->element = element;
623

624 625 626 627
  gst_pad_set_caps (GST_PAD (pad), caps);
  gst_caps_unref (caps);

  return TRUE;
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 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
/* send packet to internal element */
static GstFlowReturn
gst_ogg_demux_chain_elem_pad (GstOggPad * pad, ogg_packet * packet)
{
  GstBuffer *buf;
  GstFlowReturn ret;
  GstOggDemux *ogg = pad->ogg;

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

  GST_DEBUG_OBJECT (ogg, "%p init decoder serial %08lx", pad, pad->serialno);

  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,
        "pad %08lx does not have elem_pad, no decoder ?", pad);
    return GST_FLOW_ERROR;
  }
decoder_error:
  {
    GST_WARNING_OBJECT (ogg, "internal decoder error");
    return GST_FLOW_ERROR;
  }
}

/* queue data */
static GstFlowReturn
gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
{
  GstBuffer *buf;
  GstOggDemux *ogg = pad->ogg;

  GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08lx", pad, pad->serialno);

  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;
  GstFlowReturn ret;
  GstOggDemux *ogg = pad->ogg;


  ret = gst_pad_alloc_buffer (GST_PAD (pad), GST_BUFFER_OFFSET_NONE,
      packet->bytes, GST_PAD_CAPS (pad), &buf);

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

  if (ret == GST_FLOW_OK) {
    memcpy (buf->data, packet->packet, packet->bytes);

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

    ret = gst_pad_push (GST_PAD (pad), buf);
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724

    if (packet->granulepos != -1) {
      GstFormat format;

      ogg->current_granule = packet->granulepos;
      format = GST_FORMAT_TIME;
      if (!gst_pad_query_convert (pad->elem_pad,
              GST_FORMAT_DEFAULT, packet->granulepos, &format,
              (gint64 *) & ogg->current_time)) {
        g_warning ("could not convert granulepos to time");
      } else {
        GST_DEBUG ("ogg current time %" GST_TIME_FORMAT,
            GST_TIME_ARGS (ogg->current_time));
      }
    }
725 726 727 728 729 730 731 732 733 734
  } else {
    GST_DEBUG_OBJECT (ogg,
        "%p could not get buffer from peer %08lx", pad, pad->serialno);

    if (ret == GST_FLOW_NOT_LINKED)
      ret = GST_FLOW_OK;
  }
  return ret;
}

735 736 737 738 739 740
/* 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)
741
{
742
  gint64 granule;
743
  GstFlowReturn ret;
744 745 746

  GstOggDemux *ogg = pad->ogg;

747
  GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08lx", pad, pad->serialno);
748

749 750 751 752 753 754
  /* first packet */
  if (!pad->have_type) {
    gst_ogg_pad_typefind (pad, packet);
    pad->have_type = TRUE;
  }

755 756
  granule = packet->granulepos;
  if (granule != -1) {
757
    ogg->current_granule = granule;
758 759 760 761 762 763
    pad->current_granule = granule;
    if (pad->first_granule == -1 && granule != 0) {
      pad->first_granule = granule;
    }
  }

764 765 766 767 768 769 770 771 772
  /* no start time known, stream to internal plugin to
   * get time */
  if (pad->start_time == GST_CLOCK_TIME_NONE) {
    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) {
773 774
    GstOggChain *chain = pad->chain;

775
    /* check if complete chain has start time */
776 777 778 779
    if (chain == ogg->building_chain) {

      /* see if we have enough info to activate the chain */
      if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
780 781 782
        GstEvent *event;

        /* create the discont event we are going to send out */
783
        event = gst_event_new_newsegment (1.0,
784
            GST_FORMAT_TIME, (gint64) chain->start_time - chain->begin_time,
785
            (gint64) chain->last_time - chain->begin_time, (gint64) 0);
786

787
        gst_ogg_demux_activate_chain (ogg, chain, event);
788 789

        ogg->building_chain = NULL;
790
      }
791 792
    }
  }
Wim Taymans's avatar
Wim Taymans committed
793

794 795 796 797
  /* if we are building a chain, store buffer for when we activate 
   * it. This path is taken if we operate in streaming mode. */
  if (ogg->building_chain) {
    ret = gst_ogg_demux_queue_data (pad, packet);
798
  }
799 800 801
  /* else we are completely streaming to the peer */
  else {
    ret = gst_ogg_demux_chain_peer (pad, packet);
Wim Taymans's avatar
Wim Taymans committed
802
  }
803
  return ret;
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819
}

/* 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)
{
  ogg_packet packet;
  int ret;
  gboolean done = FALSE;
  GstFlowReturn result = GST_FLOW_OK;
  GstOggDemux *ogg;

  ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad));

820 821
  if (ogg_stream_pagein (&pad->stream, page) != 0)
    goto choked;
822 823 824 825 826 827 828 829 830 831 832 833 834 835

  while (!done) {
    ret = ogg_stream_packetout (&pad->stream, &packet);
    GST_LOG_OBJECT (ogg, "packetout gave %d", ret);
    switch (ret) {
      case 0:
        done = TRUE;
        break;
      case -1:
        /* out of sync, could call gst_ogg_pad_reset() here but ogg can decode
         * the packet just fine. We should probably send a DISCONT though. */
        break;
      case 1:
        result = gst_ogg_pad_submit_packet (pad, &packet);
836 837
        if (GST_FLOW_IS_FATAL (result))
          goto could_not_submit;
838 839 840 841 842 843 844 845 846 847
        break;
      default:
        GST_WARNING_OBJECT (ogg,
            "invalid return value %d for ogg_stream_packetout, resetting stream",
            ret);
        gst_ogg_pad_reset (pad);
        break;
    }
  }
  return result;
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863

choked:
  {
    GST_WARNING_OBJECT (ogg,
        "ogg stream choked on page (serial %08lx), resetting stream",
        pad->serialno);
    gst_ogg_pad_reset (pad);
    /* we continue to recover */
    return GST_FLOW_OK;
  }
could_not_submit:
  {
    GST_WARNING_OBJECT (ogg, "could not submit packet, error: %d", result);
    gst_ogg_pad_reset (pad);
    return result;
  }
864 865 866 867 868 869 870 871 872 873 874 875 876 877
}


static GstOggChain *
gst_ogg_chain_new (GstOggDemux * ogg)
{
  GstOggChain *chain = g_new0 (GstOggChain, 1);

  GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
  chain->ogg = ogg;
  chain->offset = -1;
  chain->bytes = -1;
  chain->have_bos = FALSE;
  chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
878
  chain->begin_time = GST_CLOCK_TIME_NONE;
879 880
  chain->start_time = GST_CLOCK_TIME_NONE;
  chain->total_time = GST_CLOCK_TIME_NONE;
881 882 883 884 885 886 887 888 889 890 891 892

  return chain;
}

static void
gst_ogg_chain_free (GstOggChain * chain)
{
  gint i;

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

893
    gst_object_unref (pad);
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
  }
  g_array_free (chain->streams, TRUE);
  chain->streams = NULL;
}

static GstOggPad *
gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno)
{
  GstOggPad *ret;
  GstTagList *list;
  gchar *name;

  GST_DEBUG_OBJECT (chain->ogg, "creating new stream %08lx in chain %p",
      serialno, chain);

  ret = g_object_new (GST_TYPE_OGG_PAD, NULL);
  /* we own this one */
911
  gst_object_ref (ret);
912 913 914 915 916
  gst_object_sink (GST_OBJECT (ret));

  list = gst_tag_list_new ();
  name = g_strdup_printf ("serial_%08lx", serialno);

917
  GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
918 919 920 921 922 923 924 925 926
  ret->chain = chain;
  ret->ogg = chain->ogg;
  gst_object_set_name (GST_OBJECT (ret), name);
  g_free (name);

  ret->serialno = serialno;
  if (ogg_stream_init (&ret->stream, serialno) != 0) {
    GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
        serialno);
927
    gst_object_unref (ret);
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
    return NULL;
  }
  gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
      NULL);
  //gst_element_found_tags (GST_ELEMENT (ogg), list);
  gst_tag_list_free (list);

  GST_LOG ("created new ogg src %p for stream with serial %08lx", ret,
      serialno);

  g_array_append_val (chain->streams, ret);

  return ret;
}

static GstOggPad *
gst_ogg_chain_get_stream (GstOggChain * chain, glong serialno)
{
  gint i;

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

    if (pad->serialno == serialno)
      return pad;
953
  }
954
  return NULL;
955 956
}

957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
static gboolean
gst_ogg_chain_has_stream (GstOggChain * chain, glong serialno)
{
  return gst_ogg_chain_get_stream (chain, serialno) != NULL;
}

#define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain))

/* signals and args */
enum
{
  /* FILL ME */
  LAST_SIGNAL
};

enum
{
  ARG_0
      /* FILL ME */
};

static GstStaticPadTem