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

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


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

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

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

54
#define CHUNKSIZE (8500)        /* this is out of vorbisfile */
55 56
#define SKELETON_FISHEAD_SIZE 64
#define SKELETON_FISBONE_MIN_SIZE 52
57

58
#define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
59

60 61 62
#define GST_CHAIN_LOCK(ogg)     g_mutex_lock((ogg)->chain_lock)
#define GST_CHAIN_UNLOCK(ogg)   g_mutex_unlock((ogg)->chain_lock)

David Schleef's avatar
David Schleef committed
63 64
GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
65 66
#define GST_CAT_DEFAULT gst_ogg_demux_debug

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

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

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

  return ret;
}

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

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
static ogg_page *
gst_ogg_page_copy (ogg_page * page)
{
  ogg_page *p = g_new0 (ogg_page, 1);

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

  return p;
}

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

108 109 110 111
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);
112
static void gst_ogg_chain_mark_discont (GstOggChain * chain);
113

114 115
static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
    GstEvent * event);
116 117
static gboolean gst_ogg_demux_receive_event (GstElement * element,
    GstEvent * event);
118

119 120 121 122
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
123

124
static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
Wim Taymans's avatar
Wim Taymans committed
125
static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
126 127
static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
128 129 130
static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
    glong serialno);

131 132
static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
    GstOggPad * pad, GstFlowReturn ret);
133
static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
134

135
G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
136

137 138
static void
gst_ogg_pad_class_init (GstOggPadClass * klass)
139
{
140
  GObjectClass *gobject_class;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
141

142
  gobject_class = (GObjectClass *) klass;
143

144 145 146
  gobject_class->dispose = gst_ogg_pad_dispose;
  gobject_class->finalize = gst_ogg_pad_finalize;
}
147

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
static void
gst_ogg_pad_init (GstOggPad * pad)
{
  gst_pad_set_event_function (GST_PAD (pad),
      GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
  gst_pad_set_getcaps_function (GST_PAD (pad),
      GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps));
  gst_pad_set_query_type_function (GST_PAD (pad),
      GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types));
  gst_pad_set_query_function (GST_PAD (pad),
      GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));

  pad->mode = GST_OGG_PAD_MODE_INIT;

  pad->current_granule = -1;
163
  pad->keyframe_granule = -1;
164

165
  pad->start_time = GST_CLOCK_TIME_NONE;
166

167 168
  pad->last_stop = GST_CLOCK_TIME_NONE;

169
  pad->have_type = FALSE;
170
  pad->continued = NULL;
David Schleef's avatar
David Schleef committed
171
  pad->map.headers = NULL;
172
  pad->map.queued = NULL;
173
}
174

175
static void
176
gst_ogg_pad_dispose (GObject * object)
177
{
178 179 180 181 182
  GstOggPad *pad = GST_OGG_PAD (object);

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

183
  g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
David Schleef's avatar
David Schleef committed
184 185
  g_list_free (pad->map.headers);
  pad->map.headers = NULL;
186
  g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
187 188
  g_list_free (pad->map.queued);
  pad->map.queued = NULL;
189

190 191 192 193 194
  /* clear continued pages */
  g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
  g_list_free (pad->continued);
  pad->continued = NULL;

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

197
  G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
198
}
199

200
static void
201
gst_ogg_pad_finalize (GObject * object)
202
{
203
  GstOggPad *pad = GST_OGG_PAD (object);
204

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

207
  G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
208 209
}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
210
static const GstQueryType *
211
gst_ogg_pad_query_types (GstPad * pad)
212
{
213
  static const GstQueryType query_types[] = {
214
    GST_QUERY_DURATION,
Wim Taymans's avatar
Wim Taymans committed
215
    GST_QUERY_SEEKING,
216 217
    0
  };
218

219
  return query_types;
220 221
}

222 223
static GstCaps *
gst_ogg_pad_getcaps (GstPad * pad)
224
{
225
  return gst_caps_ref (GST_PAD_CAPS (pad));
226 227
}

228
static gboolean
Wim Taymans's avatar
Wim Taymans committed
229
gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
230
{
231
  gboolean res = TRUE;
232 233
  GstOggDemux *ogg;

234
  ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
235

Wim Taymans's avatar
Wim Taymans committed
236
  switch (GST_QUERY_TYPE (query)) {
Wim Taymans's avatar
Wim Taymans committed
237
    case GST_QUERY_DURATION:
238 239
    {
      GstFormat format;
240
      gint64 total_time;
241

Wim Taymans's avatar
Wim Taymans committed
242
      gst_query_parse_duration (query, &format, NULL);
243
      /* can only get position in time */
244 245 246
      if (format != GST_FORMAT_TIME)
        goto wrong_format;

247 248 249 250 251 252 253 254 255
      if (ogg->seekable) {
        /* we must return the total seekable length */
        total_time = ogg->total_time;
      } else {
        /* in non-seek mode we can answer the query and we must return -1 */
        total_time = -1;
      }

      gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
256
      break;
257
    }
258 259 260 261 262 263 264 265 266 267 268 269 270 271
    case GST_QUERY_SEEKING:
    {
      GstFormat format;

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

272
    default:
273
      res = gst_pad_query_default (pad, query);
274 275
      break;
  }
276
done:
277 278
  gst_object_unref (ogg);

279
  return res;
280 281 282 283 284 285 286 287

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

290 291 292 293 294 295 296 297 298 299 300 301 302 303
static gboolean
gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
{
  gboolean res;
  GstOggDemux *ogg;

  ogg = GST_OGG_DEMUX (element);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
      /* can't seek if we are not seekable, FIXME could pass the
       * seek query upstream after converting it to bytes using
       * the average bitrate of the stream. */
      if (!ogg->seekable) {
304
        GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
305 306 307 308
        goto error;
      }

      /* now do the seek */
309
      res = gst_ogg_demux_perform_seek (ogg, event);
310
      gst_event_unref (event);
311 312
      break;
    default:
313
      GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
314 315 316 317 318
      goto error;
  }

  return res;

319
  /* ERRORS */
320
error:
321 322 323 324 325
  {
    GST_DEBUG_OBJECT (ogg, "error handling event");
    gst_event_unref (event);
    return FALSE;
  }
326 327
}

328
static gboolean
329
gst_ogg_pad_event (GstPad * pad, GstEvent * event)
330
{
331
  gboolean res;
332 333
  GstOggDemux *ogg;

334
  ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
335

336 337
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
338 339 340
      /* 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. */
341
      if (!ogg->seekable) {
342
        GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
343
        goto error;
344
      }
345

346
      /* now do the seek */
347
      res = gst_ogg_demux_perform_seek (ogg, event);
348
      gst_event_unref (event);
349
      break;
350
    default:
351 352
      res = gst_pad_event_default (pad, event);
      break;
353
  }
354 355 356
done:
  gst_object_unref (ogg);

357
  return res;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
358

359
  /* ERRORS */
360
error:
361
  {
362
    GST_DEBUG_OBJECT (ogg, "error handling event");
363
    gst_event_unref (event);
364 365
    res = FALSE;
    goto done;
366
  }
367 368
}

369
static void
370
gst_ogg_pad_reset (GstOggPad * pad)
371
{
David Schleef's avatar
David Schleef committed
372
  ogg_stream_reset (&pad->map.stream);
373

374 375
  GST_DEBUG_OBJECT (pad, "doing reset");

376 377 378 379 380 381
  /* clear continued pages */
  g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
  g_list_free (pad->continued);
  pad->continued = NULL;

  pad->last_ret = GST_FLOW_OK;
382
  pad->last_stop = GST_CLOCK_TIME_NONE;
383
  pad->current_granule = -1;
384
  pad->keyframe_granule = -1;
385 386
}

387 388 389 390 391 392 393 394 395 396 397 398
/* called when the skeleton fishead is found. Caller ensures the packet is
 * precisely the correct size; we don't re-check this here. */
static void
gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet)
{
  GstOggDemux *ogg = pad->ogg;
  guint8 *data = packet->packet;
  guint16 major, minor;
  gint64 prestime_n, prestime_d;
  gint64 basetime_n, basetime_d;

  /* skip "fishead\0" */
David Schleef's avatar
David Schleef committed
399 400 401 402 403 404
  major = GST_READ_UINT16_LE (data + 8);
  minor = GST_READ_UINT16_LE (data + 10);
  prestime_n = (gint64) GST_READ_UINT64_LE (data + 12);
  prestime_d = (gint64) GST_READ_UINT64_LE (data + 20);
  basetime_n = (gint64) GST_READ_UINT64_LE (data + 28);
  basetime_d = (gint64) GST_READ_UINT64_LE (data + 36);
405 406

  ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
407
  ogg->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
408
  ogg->have_fishead = TRUE;
David Schleef's avatar
David Schleef committed
409
  pad->map.is_skeleton = TRUE;
410 411
  pad->start_time = GST_CLOCK_TIME_NONE;
  GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %"
412 413
      GST_TIME_FORMAT ", prestime: %" GST_TIME_FORMAT ")",
      GST_TIME_ARGS (ogg->basetime), GST_TIME_ARGS (ogg->prestime));
414 415 416 417 418 419 420 421 422 423 424 425
}

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

David Schleef's avatar
David Schleef committed
426
  serialno = GST_READ_UINT32_LE (data + 12);
427 428 429

  fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
  if (fisbone_pad) {
David Schleef's avatar
David Schleef committed
430
    if (fisbone_pad->map.have_fisbone)
431 432 433
      /* already parsed */
      return;

David Schleef's avatar
David Schleef committed
434 435 436 437 438 439 440
    fisbone_pad->map.have_fisbone = TRUE;

    fisbone_pad->map.granulerate_n = GST_READ_UINT64_LE (data + 20);
    fisbone_pad->map.granulerate_d = GST_READ_UINT64_LE (data + 28);
    start_granule = GST_READ_UINT64_LE (data + 36);
    fisbone_pad->map.preroll = GST_READ_UINT32_LE (data + 44);
    fisbone_pad->map.granuleshift = GST_READ_UINT8 (data + 48);
441 442

    GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed "
443
        "(serialno: %08x start time: %" GST_TIME_FORMAT
David Schleef's avatar
David Schleef committed
444
        " granulerate_n: %d granulerate_d: %d "
445 446
        " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
        serialno, GST_TIME_ARGS (fisbone_pad->start_time),
David Schleef's avatar
David Schleef committed
447 448
        fisbone_pad->map.granulerate_n, fisbone_pad->map.granulerate_d,
        fisbone_pad->map.preroll, fisbone_pad->map.granuleshift);
449 450 451 452 453 454 455
  } else {
    GST_WARNING_OBJECT (pad->ogg,
        "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
        serialno);
  }
}

Wim Taymans's avatar
Wim Taymans committed
456
/* queue data, basically takes the packet, puts it in a buffer and store the
457
 * buffer in the queued list.  */
458 459 460
static GstFlowReturn
gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
{
461
#ifndef GST_DISABLE_GST_DEBUG
462
  GstOggDemux *ogg = pad->ogg;
463
#endif
464

David Schleef's avatar
David Schleef committed
465 466
  GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x", pad,
      pad->map.serialno);
467

468
  pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
469 470 471 472 473 474 475 476 477

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

static GstFlowReturn
gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
{
  GstBuffer *buf;
478
  GstFlowReturn ret, cret;
479
  GstOggDemux *ogg = pad->ogg;
480 481
  gint64 current_time;
  GstOggChain *chain;
482
  gint64 duration;
483 484 485
  gint offset;
  GstClockTime out_timestamp, out_duration;
  guint64 out_offset, out_offset_end;
486
  gboolean delta_unit = FALSE;
487 488

  GST_DEBUG_OBJECT (ogg,
David Schleef's avatar
David Schleef committed
489
      "%p streaming to peer serial %08x", pad, pad->map.serialno);
490

491 492
  if (pad->map.is_ogm) {
    const guint8 *data;
493
    long bytes;
494 495

    data = packet->packet;
496 497 498 499
    bytes = packet->bytes;

    if (bytes < 1)
      goto empty_packet;
500 501 502 503

    if (data[0] & 1) {
      /* We don't push header packets for OGM */
      cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
      goto done;
    } else if (data[0] & 3 && pad->map.is_ogm_text) {
      GstTagList *tags;

      /* We don't push comment packets either for text streams,
       * other streams will handle the comment packets in the
       * decoder */
      buf = gst_buffer_new ();

      GST_BUFFER_DATA (buf) = (guint8 *) data;
      GST_BUFFER_SIZE (buf) = bytes;

      tags = gst_tag_list_from_vorbiscomment_buffer (buf,
          (guint8 *) "\003vorbis", 7, NULL);
      gst_buffer_unref (buf);
      buf = NULL;

      if (tags) {
        GST_DEBUG_OBJECT (ogg, "tags = %" GST_PTR_FORMAT, tags);
        gst_element_found_tags_for_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad),
            tags);
      } else {
        GST_DEBUG_OBJECT (ogg, "failed to extract tags from vorbis comment");
      }

      cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
530 531 532 533
      goto done;
    }

    offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
534
    delta_unit = (((data[0] & 0x08) >> 3) == 0);
535 536 537 538
  } else {
    offset = 0;
  }

539
  /* get timing info for the packet */
540
  duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
541
  GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
542

David Schleef's avatar
David Schleef committed
543
  if (packet->b_o_s) {
544 545 546 547
    out_timestamp = GST_CLOCK_TIME_NONE;
    out_duration = GST_CLOCK_TIME_NONE;
    out_offset = 0;
    out_offset_end = -1;
David Schleef's avatar
David Schleef committed
548 549
  } else {
    if (packet->granulepos != -1) {
550 551 552 553
      pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
          packet->granulepos);
      pad->keyframe_granule =
          gst_ogg_stream_granulepos_to_key_granule (&pad->map,
David Schleef's avatar
David Schleef committed
554
          packet->granulepos);
555 556
      GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
          pad->current_granule);
557
    } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
558 559 560
      pad->current_granule += duration;
      GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT,
          pad->current_granule);
David Schleef's avatar
David Schleef committed
561
    }
562 563 564 565 566 567
    if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
      /* negative rates, only set timestamp on the packets with a granulepos */
      out_timestamp = -1;
      out_duration = -1;
      out_offset = -1;
      out_offset_end = -1;
568
    } else {
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
      /* we only push buffers after we have a valid granule. This is done so that
       * we nicely skip packets without a timestamp after a seek. This is ok
       * because we base or seek on the packet after the page with the smaller
       * timestamp. */
      if (pad->current_granule == -1)
        goto no_timestamp;

      if (pad->map.is_ogm) {
        out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
            pad->current_granule);
        out_duration = gst_util_uint64_scale (duration,
            GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
      } else {
        out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
            pad->current_granule - duration);
        out_duration =
            gst_ogg_stream_granule_to_time (&pad->map,
            pad->current_granule) - out_timestamp;
      }
      out_offset_end =
          gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule,
          pad->keyframe_granule);
      out_offset =
          gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
593
    }
David Schleef's avatar
David Schleef committed
594
  }
595

596
  /* check for invalid buffer sizes */
597
  if (G_UNLIKELY (offset >= packet->bytes))
598 599 600 601
    goto empty_packet;

  ret =
      gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad),
602
      GST_BUFFER_OFFSET_NONE, packet->bytes - offset, GST_PAD_CAPS (pad), &buf);
603 604 605 606 607 608

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

609 610 611 612
  /* set delta flag for OGM content */
  if (delta_unit)
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);

613
  /* copy packet in buffer */
614
  memcpy (buf->data, packet->packet + offset, packet->bytes - offset);
615 616 617 618 619 620

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

621 622 623 624 625 626
  /* Mark discont on the buffer */
  if (pad->discont) {
    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
    pad->discont = FALSE;
  }

627 628
  pad->last_stop = ogg->segment.last_stop;

629 630 631 632
  ret = gst_pad_push (GST_PAD_CAST (pad), buf);

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

634
  /* we're done with skeleton stuff */
David Schleef's avatar
David Schleef committed
635
  if (pad->map.is_skeleton)
636
    goto done;
637

638
  /* check if valid granulepos, then we can calculate the current
Wim Taymans's avatar
Wim Taymans committed
639 640 641
   * position. We know the granule for each packet but we only want to update
   * the last_stop when we have a valid granulepos on the packet because else
   * our time jumps around for the different streams. */
642 643
  if (packet->granulepos < 0)
    goto done;
644

645
  /* convert to time */
David Schleef's avatar
David Schleef committed
646 647
  current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
      packet->granulepos);
648 649

  /* convert to stream time */
650 651 652 653 654 655 656 657
  if ((chain = pad->chain)) {
    gint64 chain_start = 0;

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

    current_time = current_time - chain_start + chain->begin_time;
  }
658 659 660

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

662 663 664 665
  GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
      GST_TIME_ARGS (current_time));

done:
666 667
  /* return combined flow result */
  return cret;
668 669

  /* special cases */
670 671 672 673 674 675 676 677 678 679 680 681
empty_packet:
  {
    GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
    cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
    goto done;
  }
no_timestamp:
  {
    GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
    cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
    goto done;
  }
682 683 684
no_buffer:
  {
    GST_DEBUG_OBJECT (ogg,
685
        "%p could not get buffer from peer %08x, %d (%s), combined %d (%s)",
David Schleef's avatar
David Schleef committed
686
        pad, pad->map.serialno, ret, gst_flow_get_name (ret),
687 688
        cret, gst_flow_get_name (cret));
    goto done;
689
  }
690 691
}

692 693 694 695 696 697
/* 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)
698
{
699
  gint64 granule;
700
  GstFlowReturn ret = GST_FLOW_OK;
701 702 703

  GstOggDemux *ogg = pad->ogg;

David Schleef's avatar
David Schleef committed
704 705
  GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x", pad,
      pad->map.serialno);
706

707
  if (!pad->have_type) {
708 709 710 711
    if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE &&
        !memcmp (packet->packet, "fishead\0", 8)) {
      gst_ogg_pad_parse_skeleton_fishead (pad, packet);
    }
David Schleef's avatar
David Schleef committed
712
    pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
713 714 715
    if (!pad->have_type) {
      pad->map.caps = gst_caps_new_simple ("application/x-unknown", NULL);
    }
David Schleef's avatar
David Schleef committed
716 717
    if (pad->map.caps) {
      gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
718 719
    } else {
      GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
David Schleef's avatar
David Schleef committed
720
    }
721 722
  }

723 724 725 726 727
  if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
      !memcmp (packet->packet, "fisbone\0", 8)) {
    gst_ogg_pad_parse_skeleton_fisbone (pad, packet);
  }

728 729
  granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
      packet->granulepos);
730
  if (granule != -1) {
731
    GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
732 733 734
    pad->current_granule = granule;
  }

735 736 737 738 739
  /* restart header packet count when seeing a b_o_s page;
   * particularly useful following a seek or even following chain finding */
  if (packet->b_o_s) {
    GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
    pad->map.n_header_packets_seen = 0;
740 741 742 743 744 745
    if (!pad->map.have_headers) {
      GST_DEBUG_OBJECT (ogg, "clearing header packets");
      g_list_foreach (pad->map.headers, (GFunc) gst_mini_object_unref, NULL);
      g_list_free (pad->map.headers);
      pad->map.headers = NULL;
    }
746 747
  }

David Schleef's avatar
David Schleef committed
748 749 750 751 752
  /* Overload the value of b_o_s in ogg_packet with a flag whether or
   * not this is a header packet.  Maybe some day this could be cleaned
   * up.  */
  packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
  if (!packet->b_o_s) {
753
    pad->map.have_headers = TRUE;
David Schleef's avatar
David Schleef committed
754 755
    if (pad->start_time == GST_CLOCK_TIME_NONE) {
      gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
756
      GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
David Schleef's avatar
David Schleef committed
757 758
      if (duration != -1) {
        pad->map.accumulated_granule += duration;
759 760
        GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
            pad->map.accumulated_granule);
David Schleef's avatar
David Schleef committed
761
      }
762

David Schleef's avatar
David Schleef committed
763 764
      if (packet->granulepos != -1) {
        ogg_int64_t start_granule;
765 766 767 768
        gint64 granule;

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

770 771 772 773
        if (granule > pad->map.accumulated_granule)
          start_granule = granule - pad->map.accumulated_granule;
        else
          start_granule = 0;
David Schleef's avatar
David Schleef committed
774 775 776

        pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
            start_granule);
David Schleef's avatar
David Schleef committed
777 778 779
        GST_DEBUG ("start time %" G_GINT64_FORMAT, pad->start_time);
      } else {
        packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
780
            pad->map.accumulated_granule, pad->keyframe_granule);
David Schleef's avatar
David Schleef committed
781 782 783 784
      }
    }
  } else {
    pad->map.n_header_packets_seen++;
785
    if (!pad->map.have_headers) {
786 787
      pad->map.headers =
          g_list_append (pad->map.headers, _ogg_packet_copy (packet));
788 789
      GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
    }
790
  }
David Schleef's avatar
David Schleef committed
791

792 793 794 795
  /* 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) {
796 797
    GstOggChain *chain = pad->chain;

798
    /* check if complete chain has start time */
799 800
    if (chain == ogg->building_chain) {

801 802
      /* see if we have enough info to activate the chain, we have enough info
       * when all streams have a valid start time. */
803
      if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
804 805
        GstEvent *event;

806
        GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
807
            GST_TIME_ARGS (chain->segment_start));
808
        GST_DEBUG_OBJECT (ogg, "segment_stop:  %" GST_TIME_FORMAT,
809
            GST_TIME_ARGS (chain->segment_stop));
810
        GST_DEBUG_OBJECT (ogg, "segment_time:  %" GST_TIME_FORMAT,
811
            GST_TIME_ARGS (chain->begin_time));
812

813 814
        /* create the newsegment event we are going to send out */
        event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
815 816
            GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
            chain->begin_time);
817
        gst_event_set_seqnum (event, ogg->seqnum);
818

819
        gst_ogg_demux_activate_chain (ogg, chain, event);
820 821

        ogg->building_chain = NULL;
822
      }
823 824
    }
  }
Wim Taymans's avatar
Wim Taymans committed
825

826
  /* if we are building a chain, store buffer for when we activate
827 828
   * it. This path is taken if we operate in streaming mode. */
  if (ogg->building_chain) {
829 830 831
    /* bos packets where stored in the header list */
    if (!packet->b_o_s)
      ret = gst_ogg_demux_queue_data (pad, packet);
832
  }
833 834 835
  /* else we are completely streaming to the peer */
  else {
    ret = gst_ogg_demux_chain_peer (pad, packet);
Wim Taymans's avatar
Wim Taymans committed
836
  }
837
  return ret;
838 839
}

840 841
/* flush at most @npackets from the stream layer. All packets if 
 * @npackets is 0;
842 843
 */
static GstFlowReturn
844
gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
845 846
{
  GstFlowReturn result = GST_FLOW_OK;
847
  gboolean done = FALSE;
848 849
  GstOggDemux *ogg;

850
  ogg = pad->ogg;
851

852
  while (!done) {
853 854 855