gstoggdemux.c 68.3 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
  GstClockTime begin_time;      /* when this chain starts in the stream */

  GstClockTime segment_start;   /* the timestamp of the first sample, this is the MIN of
81
                                   the start times of all streams. */
82
  GstClockTime segment_stop;    /* the timestamp of the last page, this is the MAX of the
83
                                   streams. */
84
} GstOggChain;
85

86
/* different modes for the pad */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
87 88
typedef enum
{
89 90 91
  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;
92

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

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

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

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

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

111
  GList *headers;
112

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

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

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

122 123 124 125 126 127
  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;
128 129
};

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

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

struct _GstOggDemux
139
{
140
  GstElement element;
141

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

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

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

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

152 153 154 155 156 157 158
  /* state */
  GMutex *chain_lock;           /* we need the lock to protect the chains */
  GArray *chains;               /* list of chains we know */
  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
  /* playback start/stop positions */
  GstClockTime segment_start;
  GstClockTime segment_stop;
  gboolean segment_play;
164
  gdouble segment_rate;
165

166 167 168
  gint64 current_granule;
  GstClockTime current_time;

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

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

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

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);

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

192 193 194 195
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
196 197

#if 0
198 199
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
200
#endif
201
static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
202
static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
203 204 205
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
206

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

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
228
        g_type_register_static (GST_TYPE_PAD, "GstOggPad", &ogg_pad_info, 0);
229 230 231
  }
  return ogg_pad_type;
}
232

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

238
  gobject_class = (GObjectClass *) klass;
239

240
  ogg_pad_parent_class = g_type_class_ref (GST_TYPE_PAD);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
241

242 243 244
  gobject_class->dispose = gst_ogg_pad_dispose;
  gobject_class->finalize = gst_ogg_pad_finalize;
}
245

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
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;

264 265 266 267
  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;
268

269
  pad->have_type = FALSE;
270
  pad->headers = NULL;
271
}
272

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

278 279 280
  if (pad->element)
    gst_element_set_state (pad->element, GST_STATE_NULL);

281 282 283 284 285 286 287
  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;

288
  g_list_foreach (pad->headers, (GFunc) gst_mini_object_unref, NULL);
289 290 291 292 293 294
  g_list_free (pad->headers);
  pad->headers = NULL;

  ogg_stream_reset (&pad->stream);

  G_OBJECT_CLASS (ogg_pad_parent_class)->dispose (object);
295
}
296

297
static void
298
gst_ogg_pad_finalize (GObject * object)
299
{
300
  GstOggPad *pad = GST_OGG_PAD (object);
301

302
  ogg_stream_clear (&pad->stream);
Johan Dahlin's avatar
Johan Dahlin committed
303

304
  G_OBJECT_CLASS (ogg_pad_parent_class)->finalize (object);
305 306
}

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

Wim Taymans's avatar
Wim Taymans committed
326
#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
327
static const GstEventMask *
328
gst_ogg_pad_event_masks (GstPad * pad)
329
{
330
  static const GstEventMask src_event_masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
331 332
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
    {0,}
333
  };
334

335
  return src_event_masks;
336
}
Wim Taymans's avatar
Wim Taymans committed
337
#endif
338

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
339
static const GstQueryType *
340
gst_ogg_pad_query_types (GstPad * pad)
341
{
342
  static const GstQueryType query_types[] = {
343 344 345
    GST_QUERY_POSITION,
    0
  };
346

347
  return query_types;
348 349
}

350 351
static GstCaps *
gst_ogg_pad_getcaps (GstPad * pad)
352
{
353
  return gst_caps_ref (GST_PAD_CAPS (pad));
354 355
}

356
static gboolean
Wim Taymans's avatar
Wim Taymans committed
357
gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
358
{
359
  gboolean res = TRUE;
360 361 362
  GstOggDemux *ogg;
  GstOggPad *cur;

363 364
  ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad));
  cur = GST_OGG_PAD (pad);
365

Wim Taymans's avatar
Wim Taymans committed
366
  switch (GST_QUERY_TYPE (query)) {
367
    case GST_QUERY_POSITION:
368 369 370 371 372 373 374 375 376 377 378
    {
      GstFormat format;

      gst_query_parse_position (query, &format, NULL, NULL);
      /* can only get position in time */
      if (format != GST_FORMAT_TIME) {
        GST_DEBUG ("only query position on TIME is supported");
        res = FALSE;
        goto done;
      }
      /* can only return the total time position */
Wim Taymans's avatar
Wim Taymans committed
379
      gst_query_set_position (query, GST_FORMAT_TIME, -1, ogg->total_time);
380
      break;
381
    }
Wim Taymans's avatar
Wim Taymans committed
382 383 384
    case GST_QUERY_CONVERT:
      /* hmm .. */
      res = FALSE;
385 386
      break;
    default:
387
      res = FALSE;
388 389
      break;
  }
390
done:
391 392 393 394
  return res;
}

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

401 402
  ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad));
  cur = GST_OGG_PAD (pad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
403

404 405
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
406
    {
407
      gboolean running;
408
      gboolean flush, accurate;
409
      GstFormat format;
410
      gdouble rate;
411 412 413
      GstSeekFlags flags;
      GstSeekType cur_type, stop_type;
      gint64 cur, stop;
414

415 416 417
      /* 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. */
418
      if (!ogg->seekable) {
419
        GST_DEBUG ("seek on non seekable stream");
420
        goto error;
421
      }
422 423

      gst_event_parse_seek (event, &rate, &format, &flags,
424 425
          &cur_type, &cur, &stop_type, &stop);

426
      /* we can only seek on time */
427
      if (format != GST_FORMAT_TIME) {
428
        GST_DEBUG ("can only seek on TIME");
429 430 431 432 433 434
        goto error;
      }
      /* cannot yet do backwards playback */
      if (rate <= 0.0) {
        GST_DEBUG ("can only seek with positive rate");
        goto error;
435
      }
436 437 438 439 440 441 442 443 444 445 446 447 448 449

      /* store start and stop values */
      GST_LOCK (ogg);
      if (cur_type == GST_SEEK_TYPE_SET)
        ogg->segment_start = cur;
      else if (cur_type == GST_SEEK_TYPE_CUR)
        ogg->segment_start += cur;

      if (stop_type != GST_SEEK_TYPE_NONE)
        ogg->segment_stop = stop;
      else if (stop_type == GST_SEEK_TYPE_CUR)
        ogg->segment_stop += cur;

      ogg->segment_rate = rate;
450
      ogg->segment_play = !!(flags & GST_SEEK_FLAG_SEGMENT);
451 452
      flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH;
      accurate = (flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE;
453
      gst_event_unref (event);
454

455 456 457 458 459 460 461 462
      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 */
      running = ogg->running;
      GST_UNLOCK (ogg);

463
      /* now do the seek */
464
      if (running) {
465
        res = gst_ogg_demux_perform_seek (ogg, accurate, flush);
466 467
      } else
        res = TRUE;
468
      break;
469
    }
470
    default:
471 472
      res = gst_pad_event_default (pad, event);
      break;
473
  }
474
  return res;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
475

476
error:
477
  GST_DEBUG ("error handling event");
478
  gst_event_unref (event);
479
  return FALSE;
480 481
}

482
static void
483
gst_ogg_pad_reset (GstOggPad * pad)
484
{
485 486
  ogg_stream_reset (&pad->stream);
  /* FIXME: need a discont here */
487 488
}

489
/* the filter function for selecting the elements we can use in
490
 * autoplugging */
491
static gboolean
492
gst_ogg_demux_factory_filter (GstPluginFeature * feature, GstCaps * caps)
493
{
494 495
  guint rank;
  const gchar *klass;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
496

497 498 499 500 501 502 503 504 505
  /* 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;
506
  }
507

508 509 510 511
  /* only select elements with autoplugging rank */
  rank = gst_plugin_feature_get_rank (feature);
  if (rank < GST_RANK_MARGINAL)
    return FALSE;
512

513 514 515 516 517 518 519 520
  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 */
521
    templates = gst_element_factory_get_static_pad_templates (factory);
522 523

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

526 527 528
      /* we only care about the sink templates */
      if (templ->direction == GST_PAD_SINK) {
        GstCaps *intersect;
529 530
        GstCaps *scaps;
        gboolean empty;
531

532
        /* try to intersect the caps with the caps of the template */
533 534 535 536 537 538
        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);
539 540

        /* check if the intersection is empty */
541
        if (!empty) {
542 543 544
          /* non empty intersection, we can use this element */
          goto found;
        }
545
      }
546
    }
547
  }
548
  return FALSE;
549

550 551
found:
  return TRUE;
552
}
553

554 555 556
/* function used to sort element features */
static gint
compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
557
{
558
  gint diff;
559

560 561 562 563 564
  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));
565 566
}

567 568 569
/* function called by the internal decoder elements when it outputs
 * a buffer. We use it to get the first timestamp of the stream 
 */
570 571
static GstFlowReturn
gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer)
572
{
573
  GstOggPad *oggpad;
574
  GstOggDemux *ogg;
575
  GstClockTime timestamp;
576

577
  oggpad = gst_pad_get_element_private (pad);
578
  ogg = GST_OGG_DEMUX (oggpad->ogg);
579

580 581 582
  timestamp = GST_BUFFER_TIMESTAMP (buffer);
  GST_DEBUG_OBJECT (oggpad, "received buffer from iternal pad, TS=%lld",
      timestamp);
583

584
  if (oggpad->start_time == GST_CLOCK_TIME_NONE) {
585
    oggpad->start_time = timestamp;
586 587
    ogg->current_time = timestamp;
  }
588

Wim Taymans's avatar
Wim Taymans committed
589 590
  gst_buffer_unref (buffer);

591
  return GST_FLOW_OK;
592 593
}

594 595 596 597 598 599 600 601 602
/* 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)
603
{
604 605 606
  GstCaps *caps;
  GstElement *element = NULL;
  GstOggDemux *ogg = pad->ogg;
607

608 609
  if (GST_PAD_CAPS (pad) != NULL)
    return TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
610

611
  caps = gst_ogg_type_find (packet);
612

613 614 615 616 617 618
  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
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
     * 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);
657 658 659
        }
      }
    }
660
    g_list_free (factories);
661 662
  }
  pad->element = element;
663

664 665 666 667
  gst_pad_set_caps (GST_PAD (pad), caps);
  gst_caps_unref (caps);

  return TRUE;
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 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
/* 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);
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764

    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));
      }
    }
765 766 767 768 769 770 771 772 773 774
  } 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;
}

775 776 777 778 779 780
/* 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)
781
{
782
  gint64 granule;
783
  GstFlowReturn ret;
784 785 786

  GstOggDemux *ogg = pad->ogg;

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

789 790 791 792 793 794
  /* first packet */
  if (!pad->have_type) {
    gst_ogg_pad_typefind (pad, packet);
    pad->have_type = TRUE;
  }

795 796
  granule = packet->granulepos;
  if (granule != -1) {
797
    ogg->current_granule = granule;
798 799 800 801 802 803
    pad->current_granule = granule;
    if (pad->first_granule == -1 && granule != 0) {
      pad->first_granule = granule;
    }
  }

804 805 806 807 808 809 810 811 812
  /* 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) {
813 814
    GstOggChain *chain = pad->chain;

815
    /* check if complete chain has start time */
816 817 818 819
    if (chain == ogg->building_chain) {

      /* see if we have enough info to activate the chain */
      if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
820 821 822
        GstEvent *event;

        /* create the discont event we are going to send out */
823 824 825 826
        event = gst_event_new_newsegment (ogg->segment_rate,
            GST_FORMAT_TIME,
            chain->segment_start - chain->begin_time,
            chain->segment_stop - chain->begin_time, 0);
827

828
        gst_ogg_demux_activate_chain (ogg, chain, event);
829 830

        ogg->building_chain = NULL;
831
      }
832 833
    }
  }
Wim Taymans's avatar
Wim Taymans committed
834

835 836 837 838
  /* 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);
839
  }
840 841 842
  /* else we are completely streaming to the peer */
  else {
    ret = gst_ogg_demux_chain_peer (pad, packet);
Wim Taymans's avatar
Wim Taymans committed
843
  }
844
  return ret;
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
}

/* 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));

861 862
  if (ogg_stream_pagein (&pad->stream, page) != 0)
    goto choked;
863 864 865 866 867 868 869 870 871 872 873 874 875 876

  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);
877 878
        if (GST_FLOW_IS_FATAL (result))
          goto could_not_submit;
879 880 881 882 883 884 885 886 887 888
        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;
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904

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;
  }
905 906 907 908 909 910 911 912 913 914 915 916 917 918
}


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 *));
919
  chain->begin_time = GST_CLOCK_TIME_NONE;
920
  chain->segment_start = GST_CLOCK_TIME_NONE;
921
  chain->total_time = GST_CLOCK_TIME_NONE;
922 923 924 925 926 927 928 929 930 931 932 933

  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);

934
    gst_object_unref (pad);
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
  }
  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 */
952
  gst_object_ref (ret);
953 954 955