gstoggdemux.c 57.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 78 79
  GstClockTime total_time;      /* the total time of this chain, this is the MAX of
                                   the totals of all streams */
  GstClockTime start_time;      /* the timestamp of the first sample */
  GstClockTime last_time;       /* the timestamp of the last page == last sample */
80

81 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
  GstOggPadMode mode;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
101

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

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

109
  GList *headers;
110

111 112 113
  gint serialno;
  gint64 packetno;
  gint64 offset;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
114

115 116
  gint64 start;
  gint64 stop;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
117

118
  gint64 current_granule;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
119

120
  GstClockTime start_time;      /* the timestamp of the first sample */
121

122 123
  gint64 first_granule;         /* the granulepos of first page == first sample in next page */
  GstClockTime first_time;      /* the timestamp of the second page */
124

125 126 127 128 129 130
  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;
131 132
};

133
struct _GstOggPadClass
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
134
{
135
  PARENTCLASS parent_class;
136 137
};

138 139
typedef enum
{
140 141 142
  OGG_STATE_NEW_CHAIN,
  OGG_STATE_STREAMING,
} OggState;
143

144 145 146 147
#define GST_CHAIN_LOCK(ogg)	g_mutex_lock((ogg)->chain_lock)
#define GST_CHAIN_UNLOCK(ogg)	g_mutex_unlock((ogg)->chain_lock)

struct _GstOggDemux
148
{
149
  GstElement element;
150

151
  GstPad *sinkpad;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
152

153 154
  gint64 length;
  gint64 offset;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
155

156 157
  OggState state;
  gboolean seekable;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
158

159
  gboolean need_chains;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
160

161 162 163
  /* state */
  GMutex *chain_lock;           /* we need the lock to protect the chains */
  GArray *chains;               /* list of chains we know */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
164

165 166 167 168
  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
169

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

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

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

185
static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg, gint64 pos);
186

187 188 189 190
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
191 192

#if 0
193 194
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
195
#endif
196
static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
197
static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
198 199 200
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
201

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

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
223
        g_type_register_static (GST_TYPE_PAD, "GstOggPad", &ogg_pad_info, 0);
224 225 226
  }
  return ogg_pad_type;
}
227

228 229
static void
gst_ogg_pad_class_init (GstOggPadClass * klass)
230
{
231
  GObjectClass *gobject_class;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
232

233
  gobject_class = (GObjectClass *) klass;
234

235
  ogg_pad_parent_class = g_type_class_ref (GST_TYPE_PAD);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
236

237 238 239
  gobject_class->dispose = gst_ogg_pad_dispose;
  gobject_class->finalize = gst_ogg_pad_finalize;
}
240

241 242 243 244 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;

  pad->start_time = -1;
  pad->first_time = -1;
  pad->last_time = -1;

  pad->headers = NULL;
264
}
265

266
static void
267
gst_ogg_pad_dispose (GObject * object)
268
{
269
  GstOggPad *pad = GST_OGG_PAD (object);
270

271 272 273 274 275 276 277
  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;

278
  g_list_foreach (pad->headers, (GFunc) gst_mini_object_unref, NULL);
279 280 281 282 283 284
  g_list_free (pad->headers);
  pad->headers = NULL;

  ogg_stream_reset (&pad->stream);

  G_OBJECT_CLASS (ogg_pad_parent_class)->dispose (object);
285
}
286

287
static void
288
gst_ogg_pad_finalize (GObject * object)
289
{
290
  GstOggPad *pad = GST_OGG_PAD (object);
291

292
  ogg_stream_clear (&pad->stream);
Johan Dahlin's avatar
Johan Dahlin committed
293

294
  G_OBJECT_CLASS (ogg_pad_parent_class)->finalize (object);
295 296
}

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

Wim Taymans's avatar
Wim Taymans committed
316
#if 0
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
317
static const GstEventMask *
318
gst_ogg_pad_event_masks (GstPad * pad)
319
{
320
  static const GstEventMask src_event_masks[] = {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
321 322
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
    {0,}
323
  };
324

325
  return src_event_masks;
326
}
Wim Taymans's avatar
Wim Taymans committed
327
#endif
328

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
329
static const GstQueryType *
330
gst_ogg_pad_query_types (GstPad * pad)
331
{
332
  static const GstQueryType query_types[] = {
333 334 335
    GST_QUERY_POSITION,
    0
  };
336

337
  return query_types;
338 339
}

340 341
static GstCaps *
gst_ogg_pad_getcaps (GstPad * pad)
342
{
343
  return gst_caps_ref (GST_PAD_CAPS (pad));
344 345
}

346
static gboolean
Wim Taymans's avatar
Wim Taymans committed
347
gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
348
{
349
  gboolean res = TRUE;
350 351 352
  GstOggDemux *ogg;
  GstOggPad *cur;

353 354
  ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad));
  cur = GST_OGG_PAD (pad);
355

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

369 370 371 372
  return res;
}

static gboolean
373
gst_ogg_pad_event (GstPad * pad, GstEvent * event)
374
{
375
  gboolean res;
376
  GstOggDemux *ogg;
377
  GstOggPad *cur;
378

379 380
  ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad));
  cur = GST_OGG_PAD (pad);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
381

382 383
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
384
    {
385
      gint64 offset;
386

387 388 389 390
      /* can't seek if we are not seekable */
      if (!ogg->seekable) {
        res = FALSE;
        goto done_unref;
391
      }
392 393 394 395
      /* we can only seek on time */
      if (GST_EVENT_SEEK_FORMAT (event) != GST_FORMAT_TIME) {
        res = FALSE;
        goto done_unref;
396
      }
397
      offset = GST_EVENT_SEEK_OFFSET (event);
398
      gst_event_unref (event);
399 400 401 402

      /* now do the seek */
      res = gst_ogg_demux_perform_seek (ogg, offset);
      break;
403
    }
404
    default:
405 406
      res = gst_pad_event_default (pad, event);
      break;
407 408
  }

409
  return res;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
410

411
done_unref:
412
  gst_event_unref (event);
413 414 415
  return res;
}

416
static void
417
gst_ogg_pad_reset (GstOggPad * pad)
418
{
419 420
  ogg_stream_reset (&pad->stream);
  /* FIXME: need a discont here */
421 422
}

423 424
/* the filter function for selecting the elements we can use in
 *  * autoplugging */
425
static gboolean
426
gst_ogg_demux_factory_filter (GstPluginFeature * feature, GstCaps * caps)
427
{
428 429
  guint rank;
  const gchar *klass;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
430

431 432 433 434 435 436 437 438 439
  /* 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;
440
  }
441

442 443 444 445
  /* only select elements with autoplugging rank */
  rank = gst_plugin_feature_get_rank (feature);
  if (rank < GST_RANK_MARGINAL)
    return FALSE;
446

447 448 449 450 451 452 453 454
  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 */
455
    templates = gst_element_factory_get_static_pad_templates (factory);
456 457

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

460 461 462
      /* we only care about the sink templates */
      if (templ->direction == GST_PAD_SINK) {
        GstCaps *intersect;
463

464
        /* try to intersect the caps with the caps of the template */
465 466
        intersect = gst_caps_intersect (caps,
            gst_static_caps_get (&templ->static_caps));
467 468 469 470 471 472 473 474

        /* 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);
475
      }
476
    }
477
  }
478
  return FALSE;
479

480 481
found:
  return TRUE;
482
}
483

484 485 486
/* function used to sort element features */
static gint
compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
487
{
488
  gint diff;
489

490 491 492 493 494
  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));
495 496
}

497 498
static GstFlowReturn
gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer)
499
{
500 501
  GstOggPad *oggpad;
  GstClockTime timestamp;
502

503
  oggpad = gst_pad_get_element_private (pad);
504

505 506 507
  timestamp = GST_BUFFER_TIMESTAMP (buffer);
  GST_DEBUG_OBJECT (oggpad, "received buffer from iternal pad, TS=%lld",
      timestamp);
508

509 510
  if (oggpad->start_time == -1)
    oggpad->start_time = timestamp;
511

Wim Taymans's avatar
Wim Taymans committed
512 513
  gst_buffer_unref (buffer);

514
  return GST_FLOW_OK;
515 516
}

517 518 519 520 521 522 523 524 525
/* 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)
526
{
527 528 529
  GstCaps *caps;
  GstElement *element = NULL;
  GstOggDemux *ogg = pad->ogg;
530

531 532
  if (GST_PAD_CAPS (pad) != NULL)
    return TRUE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
533

534
  caps = gst_ogg_type_find (packet);
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
  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
     * meaning of granulepos etc so we make one. We only do this if
     * we are in the seeking mode. */
    if (ogg->seekable) {
      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 */
561
          gst_object_ref (element);
562 563 564 565 566
          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);
567 568 569
          pad->elem_out =
              gst_pad_new_from_template (gst_static_pad_template_get
              (&internaltemplate), "internal");
570 571 572 573 574 575
          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.. */
576 577 578 579 580
          {
            GstPad *p;

            p = gst_element_get_pad (element, "src");
            gst_pad_link (p, pad->elem_out);
581
            gst_object_unref (p);
582
          }
583 584 585 586 587 588 589 590
        }
      }
      g_list_free (factories);
    } else {
      pad->mode = GST_OGG_PAD_MODE_STREAMING;
    }
  }
  pad->element = element;
591

592 593 594 595
  gst_pad_set_caps (GST_PAD (pad), caps);
  gst_caps_unref (caps);

  return TRUE;
596 597
}

598 599 600 601 602 603
/* 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)
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
  GstBuffer *buf;
  gint64 granule;
  GstFlowReturn ret = GST_FLOW_OK;

  GstOggDemux *ogg = pad->ogg;

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

  granule = packet->granulepos;
  if (granule != -1) {
    pad->current_granule = granule;
    if (pad->first_granule == -1 && granule != 0) {
      pad->first_granule = granule;
    }
  }
  /* first packet, FIXME, do this in chain activation */
  if (pad->packetno == 0) {
    gst_ogg_pad_typefind (pad, packet);
  }

  /* stream packet to peer plugin */
  if (pad->mode == GST_OGG_PAD_MODE_STREAMING) {
Wim Taymans's avatar
Wim Taymans committed
629
    ret =
630
        gst_pad_alloc_buffer (GST_PAD (pad), GST_BUFFER_OFFSET_NONE,
Wim Taymans's avatar
Wim Taymans committed
631
        packet->bytes, GST_PAD_CAPS (pad), &buf);
632 633 634 635 636

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

Wim Taymans's avatar
Wim Taymans committed
637
    if (ret == GST_FLOW_OK) {
638 639 640 641 642 643 644 645 646 647 648
      memcpy (buf->data, packet->packet, packet->bytes);

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

      ret = gst_pad_push (GST_PAD (pad), buf);
    } else {
      GST_DEBUG_OBJECT (ogg,
          "%p could not get buffer from peer %08lx, packetno %lld", pad,
          pad->serialno, pad->packetno);
649 650 651 652 653 654 655 656 657 658

      /* if we are still building a chain, we have to queue this buffer and
       * push it when we activate the chain */
      if (ogg->building_chain) {
        buf = gst_buffer_new_and_alloc (packet->bytes);
        memcpy (buf->data, packet->packet, packet->bytes);
        pad->offset = packet->granulepos;
        GST_BUFFER_OFFSET (buf) = -1;
        GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
        pad->headers = g_list_append (pad->headers, buf);
659 660
        /* we are ok now */
        ret = GST_FLOW_OK;
661
      }
662
    }
663
  } else {
664
    /* initialize our internal decoder with packets */
Wim Taymans's avatar
Wim Taymans committed
665 666
    if (!pad->elem_pad)
      goto no_decoder;
667 668 669 670 671 672 673 674 675 676 677

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

    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;

678
    ret = gst_pad_chain (pad->elem_pad, buf);
679 680 681 682
  }
  pad->packetno++;

  return ret;
Wim Taymans's avatar
Wim Taymans committed
683 684 685 686 687 688 689

no_decoder:
  {
    GST_WARNING_OBJECT (ogg,
        "pad %08lx does not have elem_pad, no decoder ?", pad);
    return GST_FLOW_OK;
  }
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 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
}

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

  if (ogg_stream_pagein (&pad->stream, page) != 0) {
    GST_WARNING_OBJECT (ogg,
        "ogg stream choked on page (serial %08lx), resetting stream",
        pad->serialno);
    gst_ogg_pad_reset (pad);
    return GST_FLOW_OK;
  }

  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);
        if (result != GST_FLOW_OK) {
          GST_WARNING_OBJECT (ogg, "could not submit packet, error: %d",
              result);
          gst_ogg_pad_reset (pad);
          done = TRUE;
        }
        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;
}


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

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

769
    gst_object_unref (pad);
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
  }
  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 */
787
  gst_object_ref (ret);
788 789 790 791 792
  gst_object_sink (GST_OBJECT (ret));

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

793
  GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
794 795 796 797 798 799 800 801 802
  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);
803
    gst_object_unref (ret);
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
    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;
829
  }
830
  return NULL;
831 832
}

833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877
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 GstStaticPadTemplate ogg_demux_src_template_factory =
GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_SOMETIMES,
    GST_STATIC_CAPS_ANY);

static GstStaticPadTemplate ogg_demux_sink_template_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("application/ogg")
    );

static void gst_ogg_demux_finalize (GObject * object);

//static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad);
//static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad);
static GstOggChain *gst_ogg_demux_read_chain (GstOggDemux * ogg);
static gint gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
    GstOggChain * chain);

static gboolean gst_ogg_demux_handle_event (GstPad * pad, GstEvent * event);
static void gst_ogg_demux_loop (GstOggPad * pad);
static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
878 879 880 881 882
static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
    gboolean active);
static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
    gboolean active);
883 884 885 886 887 888
static GstElementStateReturn gst_ogg_demux_change_state (GstElement * element);

static void gst_ogg_print (GstOggDemux * demux);

GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT);

889
static void
890
gst_ogg_demux_base_init (gpointer g_class)
891
{
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
  static GstElementDetails gst_ogg_demux_details =
      GST_ELEMENT_DETAILS ("ogg demuxer",
      "Codec/Demuxer",
      "demux ogg streams (info about ogg: http://xiph.org)",
      "Wim Taymand <wim@fluendo.com>");

  gst_element_class_set_details (element_class, &gst_ogg_demux_details);

  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&ogg_demux_sink_template_factory));
  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&ogg_demux_src_template_factory));
}
static void
gst_ogg_demux_class_init (GstOggDemuxClass * klass)
{
  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  gstelement_class->change_state = gst_ogg_demux_change_state;

  gobject_class->finalize = gst_ogg_demux_finalize;
915 916
}

917
static void
918
gst_ogg_demux_init (GstOggDemux * ogg)
919
{
920 921 922 923 924 925 926
  /* create the sink pad */
  ogg->sinkpad =
      gst_pad_new_from_template (gst_static_pad_template_get
      (&ogg_demux_sink_template_factory), "sink");
  gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_handle_event);
  gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
  gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
927 928 929 930
  gst_pad_set_activatepull_function (ogg->sinkpad,
      gst_ogg_demux_sink_activate_pull);
  gst_pad_set_activatepush_function (ogg->sinkpad,
      gst_ogg_demux_sink_activate_push);
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
  gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);

  ogg->chain_lock = g_mutex_new ();
  ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
  ogg->state = OGG_STATE_NEW_CHAIN;
}

static void
gst_ogg_demux_finalize (GObject * object)
{
  GstOggDemux *ogg;

  ogg = GST_OGG_DEMUX (object);

  g_mutex_free (ogg->chain_lock);
  ogg_sync_clear (&ogg->sync);

  if (G_OBJECT_CLASS (parent_class)->finalize)
    G_OBJECT_CLASS (parent_class)->finalize (object);
950 951 952
}

static gboolean
953 954 955
gst_ogg_demux_handle_event (GstPad * pad, GstEvent * event)
{
  GstOggDemux *ogg = GST_OGG_DEMUX (GST_PAD_PARENT (pad));
956

957 958 959 960 961 962 963 964 965
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_DISCONTINUOUS:
      GST_DEBUG_OBJECT (ogg, "got a discont event");
      ogg_sync_reset (&ogg->sync);
      gst_event_unref (event);
      break;
    default:
      return gst_pad_event_default (pad, event);
  }
966 967 968
  return TRUE;
}

969 970 971
/* submit the given buffer to the ogg sync.
 *
 * Returns the number of bytes submited.
972
 */
973 974 975 976 977 978 979 980 981 982 983 984 985
static gint
gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
{
  guint size;
  guint8 *data;
  gchar *oggbuffer;

  size = GST_BUFFER_SIZE (buffer);
  data = GST_BUFFER_DATA (buffer);

  oggbuffer = ogg_sync_buffer (&ogg->sync, size);
  memcpy (oggbuffer, data, size);
  ogg_sync_wrote (&ogg->sync, size);
Wim Taymans's avatar
Wim Taymans committed
986
  gst_buffer_unref (buffer);
987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015

  return size;
}

/* in random access mode this code updates the current read position
 * and resets the ogg sync buffer so that the next read will happen
 * from this new location.
 */
static void
gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
{
  GST_LOG_OBJECT (ogg, "seeking to %lld", offset);

  ogg->offset = offset;
  ogg_sync_reset (&ogg->sync);
}

/* read more data from the current offset and submit to
 * the ogg sync layer.
 *
 * Return number of bytes written.
 */
static gint
gst_ogg_demux_get_data (GstOggDemux * ogg)
{
  GstFlowReturn ret;
  GstBuffer *buffer;
  gint size;

1016
  GST_LOG_OBJECT (ogg, "get data %lld %lld", ogg->offset, ogg->length);
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062