summaryrefslogtreecommitdiff
path: root/ext/ogg/gstoggdemux.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/ogg/gstoggdemux.c')
-rw-r--r--ext/ogg/gstoggdemux.c3366
1 files changed, 0 insertions, 3366 deletions
diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c
deleted file mode 100644
index d8f00efb..00000000
--- a/ext/ogg/gstoggdemux.c
+++ /dev/null
@@ -1,3366 +0,0 @@
-/* GStreamer
- * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
- *
- * gstoggdemux.c: ogg stream demuxer
- *
- * 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.
- */
-
-/**
- * SECTION:element-oggdemux
- * @see_also: <link linkend="gst-plugins-base-plugins-oggmux">oggmux</link>
- *
- * This element demuxes ogg files into their encoded audio and video components.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
- * ]| Decodes the vorbis audio stored inside an ogg container.
- * </refsect2>
- *
- * Last reviewed on 2006-12-30 (0.10.5)
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-#include <string.h>
-#include <gst/gst-i18n-plugin.h>
-#include <gst/tag/tag.h>
-
-#include "gstoggdemux.h"
-
-static const GstElementDetails gst_ogg_demux_details =
-GST_ELEMENT_DETAILS ("Ogg demuxer",
- "Codec/Demuxer",
- "demux ogg streams (info about ogg: http://xiph.org)",
- "Wim Taymans <wim@fluendo.com>");
-
-#define CHUNKSIZE (8500) /* this is out of vorbisfile */
-#define SKELETON_FISHEAD_SIZE 64
-#define SKELETON_FISBONE_MIN_SIZE 52
-
-#define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
-
-#define GST_CHAIN_LOCK(ogg) g_mutex_lock((ogg)->chain_lock)
-#define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock((ogg)->chain_lock)
-
-GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
-GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
-#define GST_CAT_DEFAULT gst_ogg_demux_debug
-
-
-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);
-}
-
-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);
-}
-
-static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
- GstOggChain * chain);
-static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
- GstOggChain * chain, GstEvent * event);
-static void gst_ogg_chain_mark_discont (GstOggChain * chain);
-
-static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
- GstEvent * event);
-static gboolean gst_ogg_demux_receive_event (GstElement * element,
- GstEvent * event);
-
-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);
-
-static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
-static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
-static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
-static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
-static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
- glong serialno);
-
-static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
- GstOggPad * pad, GstFlowReturn ret);
-static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
-
-G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
-
-static void
-gst_ogg_pad_class_init (GstOggPadClass * klass)
-{
- GObjectClass *gobject_class;
-
- gobject_class = (GObjectClass *) klass;
-
- gobject_class->dispose = gst_ogg_pad_dispose;
- gobject_class->finalize = gst_ogg_pad_finalize;
-}
-
-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;
- pad->keyframe_granule = -1;
-
- pad->start_time = GST_CLOCK_TIME_NONE;
-
- pad->last_stop = GST_CLOCK_TIME_NONE;
-
- pad->have_type = FALSE;
- pad->continued = NULL;
- pad->map.headers = NULL;
- pad->map.queued = NULL;
-}
-
-static void
-gst_ogg_pad_dispose (GObject * object)
-{
- GstOggPad *pad = GST_OGG_PAD (object);
-
- pad->chain = NULL;
- pad->ogg = NULL;
-
- g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
- g_list_free (pad->map.headers);
- pad->map.headers = NULL;
- g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
- g_list_free (pad->map.queued);
- pad->map.queued = NULL;
-
- /* clear continued pages */
- g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
- g_list_free (pad->continued);
- pad->continued = NULL;
-
- ogg_stream_reset (&pad->map.stream);
-
- G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
-}
-
-static void
-gst_ogg_pad_finalize (GObject * object)
-{
- GstOggPad *pad = GST_OGG_PAD (object);
-
- ogg_stream_clear (&pad->map.stream);
-
- G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
-}
-
-static const GstQueryType *
-gst_ogg_pad_query_types (GstPad * pad)
-{
- static const GstQueryType query_types[] = {
- GST_QUERY_DURATION,
- GST_QUERY_SEEKING,
- 0
- };
-
- return query_types;
-}
-
-static GstCaps *
-gst_ogg_pad_getcaps (GstPad * pad)
-{
- return gst_caps_ref (GST_PAD_CAPS (pad));
-}
-
-static gboolean
-gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
-{
- gboolean res = TRUE;
- GstOggDemux *ogg;
-
- ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_DURATION:
- {
- GstFormat format;
- gint64 total_time;
-
- gst_query_parse_duration (query, &format, NULL);
- /* can only get position in time */
- if (format != GST_FORMAT_TIME)
- goto wrong_format;
-
- if (ogg->pullmode) {
- /* we must return the total length */
- total_time = ogg->total_time;
- } else {
- /* in push mode we can answer the query and we must return -1 */
- total_time = -1;
- }
-
- gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
- break;
- }
- 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->pullmode,
- 0, ogg->total_time);
- } else {
- res = FALSE;
- }
- break;
- }
-
- default:
- res = gst_pad_query_default (pad, query);
- break;
- }
-done:
- gst_object_unref (ogg);
-
- return res;
-
- /* ERRORS */
-wrong_format:
- {
- GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
- res = FALSE;
- goto done;
- }
-}
-
-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 pullmode, FIXME could pass the
- * seek query upstream after converting it to bytes using
- * the average bitrate of the stream. */
- if (!ogg->pullmode) {
- GST_DEBUG_OBJECT (ogg, "seek on pull mode stream not implemented yet");
- goto error;
- }
-
- /* now do the seek */
- res = gst_ogg_demux_perform_seek (ogg, event);
- gst_event_unref (event);
- break;
- default:
- GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
- goto error;
- }
-
- return res;
-
- /* ERRORS */
-error:
- {
- GST_DEBUG_OBJECT (ogg, "error handling event");
- gst_event_unref (event);
- return FALSE;
- }
-}
-
-static gboolean
-gst_ogg_pad_event (GstPad * pad, GstEvent * event)
-{
- gboolean res;
- GstOggDemux *ogg;
-
- ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_SEEK:
- /* can't seek if we are not pullmode, FIXME could pass the
- * seek query upstream after converting it to bytes using
- * the average bitrate of the stream. */
- if (!ogg->pullmode) {
- GST_DEBUG_OBJECT (ogg, "seek on pull mode stream not implemented yet");
- goto error;
- }
-
- /* now do the seek */
- res = gst_ogg_demux_perform_seek (ogg, event);
- gst_event_unref (event);
- break;
- default:
- res = gst_pad_event_default (pad, event);
- break;
- }
-done:
- gst_object_unref (ogg);
-
- return res;
-
- /* ERRORS */
-error:
- {
- GST_DEBUG_OBJECT (ogg, "error handling event");
- gst_event_unref (event);
- res = FALSE;
- goto done;
- }
-}
-
-static void
-gst_ogg_pad_reset (GstOggPad * pad)
-{
- ogg_stream_reset (&pad->map.stream);
-
- GST_DEBUG_OBJECT (pad, "doing reset");
-
- /* 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;
- pad->last_stop = GST_CLOCK_TIME_NONE;
- pad->current_granule = -1;
- pad->keyframe_granule = -1;
-}
-
-/* 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" */
- 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);
-
- ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
- ogg->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
- ogg->have_fishead = TRUE;
- pad->map.is_skeleton = TRUE;
- pad->start_time = GST_CLOCK_TIME_NONE;
- GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %"
- GST_TIME_FORMAT ", prestime: %" GST_TIME_FORMAT ")",
- GST_TIME_ARGS (ogg->basetime), GST_TIME_ARGS (ogg->prestime));
-}
-
-/* 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;
-
- serialno = GST_READ_UINT32_LE (data + 12);
-
- fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
- if (fisbone_pad) {
- if (fisbone_pad->map.have_fisbone)
- /* already parsed */
- return;
-
- 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);
-
- GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed "
- "(serialno: %08x start time: %" GST_TIME_FORMAT
- " granulerate_n: %d granulerate_d: %d "
- " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
- serialno, GST_TIME_ARGS (fisbone_pad->start_time),
- fisbone_pad->map.granulerate_n, fisbone_pad->map.granulerate_d,
- fisbone_pad->map.preroll, fisbone_pad->map.granuleshift);
- } else {
- GST_WARNING_OBJECT (pad->ogg,
- "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
- serialno);
- }
-}
-
-/* queue data, basically takes the packet, puts it in a buffer and store the
- * buffer in the queued list. */
-static GstFlowReturn
-gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
-{
-#ifndef GST_DISABLE_GST_DEBUG
- GstOggDemux *ogg = pad->ogg;
-#endif
-
- GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08lx", pad,
- pad->map.serialno);
-
- pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
-
- /* we are ok now */
- return GST_FLOW_OK;
-}
-
-static GstFlowReturn
-gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
- gboolean push_headers)
-{
- GstBuffer *buf = NULL;
- GstFlowReturn ret, cret;
- GstOggDemux *ogg = pad->ogg;
- gint64 current_time;
- GstOggChain *chain;
- gint64 duration;
- gint offset;
- gint trim;
- GstClockTime out_timestamp, out_duration;
- guint64 out_offset, out_offset_end;
- gboolean delta_unit = FALSE;
-
- GST_DEBUG_OBJECT (ogg,
- "%p streaming to peer serial %08lx", pad, pad->map.serialno);
-
- if (pad->map.is_ogm) {
- const guint8 *data;
- long bytes;
-
- data = packet->packet;
- bytes = packet->bytes;
-
- if (bytes < 1)
- goto empty_packet;
-
- if (data[0] & 1) {
- /* We don't push header packets for OGM */
- cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
- 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);
- goto done;
- }
-
- offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
- delta_unit = (((data[0] & 0x08) >> 3) == 0);
-
- trim = 0;
-
- /* Strip trailing \0 for subtitles */
- if (pad->map.is_ogm_text) {
- while (bytes && data[bytes - 1] == 0) {
- trim++;
- bytes--;
- }
- }
- } else {
- offset = 0;
- trim = 0;
- }
-
- /* get timing info for the packet */
- duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
- GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
-
- if (packet->b_o_s) {
- out_timestamp = GST_CLOCK_TIME_NONE;
- out_duration = GST_CLOCK_TIME_NONE;
- out_offset = 0;
- out_offset_end = -1;
- } else {
- if (packet->granulepos != -1) {
- 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,
- packet->granulepos);
- GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
- pad->current_granule);
- } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
- pad->current_granule += duration;
- GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT,
- pad->current_granule);
- }
- 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;
- } else {
- /* 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 if (pad->is_sparse) {
- out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
- pad->current_granule);
- out_duration = GST_CLOCK_TIME_NONE;
- } 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);
- }
- }
-
- /* check for invalid buffer sizes */
- if (G_UNLIKELY (offset + trim >= packet->bytes))
- goto empty_packet;
-
- ret =
- gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad),
- GST_BUFFER_OFFSET_NONE, packet->bytes - offset - trim,
- GST_PAD_CAPS (pad), &buf);
-
- /* combine flows */
- cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
- if (ret != GST_FLOW_OK)
- goto no_buffer;
-
- /* set delta flag for OGM content */
- if (delta_unit)
- GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
-
- /* copy packet in buffer */
- memcpy (buf->data, packet->packet + offset, packet->bytes - offset - trim);
-
- 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;
-
- /* Mark discont on the buffer */
- if (pad->discont) {
- GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
- pad->discont = FALSE;
- }
-
- pad->last_stop = ogg->segment.last_stop;
-
- /* don't push the header packets when we are asked to skip them */
- if (!packet->b_o_s || push_headers) {
- ret = gst_pad_push (GST_PAD_CAST (pad), buf);
- buf = NULL;
-
- /* combine flows */
- cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
- }
-
- /* we're done with skeleton stuff */
- if (pad->map.is_skeleton)
- goto done;
-
- /* check if valid granulepos, then we can calculate the current
- * 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. */
- if (packet->granulepos < 0)
- goto done;
-
- /* convert to time */
- current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
- packet->granulepos);
-
- /* convert to stream time */
- 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;
- }
-
- /* and store as the current position */
- gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time);
-
- GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
- GST_TIME_ARGS (current_time));
-
-done:
- if (buf)
- gst_buffer_unref (buf);
- /* return combined flow result */
- return cret;
-
- /* special cases */
-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;
- }
-no_buffer:
- {
- GST_DEBUG_OBJECT (ogg,
- "%p could not get buffer from peer %08lx, %d (%s), combined %d (%s)",
- pad, pad->map.serialno, ret, gst_flow_get_name (ret),
- cret, gst_flow_get_name (cret));
- goto done;
- }
-}
-
-/* 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)
-{
- gint64 granule;
- GstFlowReturn ret = GST_FLOW_OK;
-
- GstOggDemux *ogg = pad->ogg;
-
- GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08lx", pad,
- pad->map.serialno);
-
- if (!pad->have_type) {
- if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE &&
- !memcmp (packet->packet, "fishead\0", 8)) {
- gst_ogg_pad_parse_skeleton_fishead (pad, packet);
- }
- pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
- if (!pad->have_type) {
- pad->map.caps = gst_caps_new_simple ("application/x-unknown", NULL);
- }
- if (pad->map.caps) {
- gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
- } else {
- GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
- }
- }
-
- if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
- !memcmp (packet->packet, "fisbone\0", 8)) {
- gst_ogg_pad_parse_skeleton_fisbone (pad, packet);
- }
-
- granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
- packet->granulepos);
- if (granule != -1) {
- GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
- pad->current_granule = granule;
- }
-
- /* 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;
- if (!pad->map.have_headers) {
- GST_DEBUG_OBJECT (ogg, "clearing header packets");
- g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
- g_list_free (pad->map.headers);
- pad->map.headers = NULL;
- }
- }
-
- /* 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) {
- pad->map.have_headers = TRUE;
- if (pad->start_time == GST_CLOCK_TIME_NONE) {
- gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
- GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
- if (duration != -1) {
- pad->map.accumulated_granule += duration;
- GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
- pad->map.accumulated_granule);
- }
-
- if (packet->granulepos != -1) {
- ogg_int64_t start_granule;
- gint64 granule;
-
- granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
- packet->granulepos);
-
- if (granule > pad->map.accumulated_granule)
- start_granule = granule - pad->map.accumulated_granule;
- else
- start_granule = 0;
-
- pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
- start_granule);
- GST_DEBUG ("start time %" G_GINT64_FORMAT, pad->start_time);
- } else {
- packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
- pad->map.accumulated_granule, pad->keyframe_granule);
- }
- }
- } else {
- pad->map.n_header_packets_seen++;
- if (!pad->map.have_headers) {
- pad->map.headers =
- g_list_append (pad->map.headers, _ogg_packet_copy (packet));
- GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
- }
- }
-
- /* 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) {
- GstOggChain *chain = pad->chain;
-
- /* check if complete chain has start time */
- if (chain == ogg->building_chain) {
-
- /* see if we have enough info to activate the chain, we have enough info
- * when all streams have a valid start time. */
- if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
- GstEvent *event;
-
- GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (chain->segment_start));
- GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (chain->segment_stop));
- GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (chain->begin_time));
-
- /* create the newsegment event we are going to send out */
- event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
- GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
- chain->begin_time);
- gst_event_set_seqnum (event, ogg->seqnum);
-
- gst_ogg_demux_activate_chain (ogg, chain, event);
-
- ogg->building_chain = NULL;
- }
- }
- }
-
- /* 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) {
- /* bos packets where stored in the header list so we can discard
- * them here*/
- if (!packet->b_o_s)
- ret = gst_ogg_demux_queue_data (pad, packet);
- }
- /* else we are completely streaming to the peer */
- else {
- ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
- }
- return ret;
-}
-
-/* flush at most @npackets from the stream layer. All packets if
- * @npackets is 0;
- */
-static GstFlowReturn
-gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
-{
- GstFlowReturn result = GST_FLOW_OK;
- gboolean done = FALSE;
- GstOggDemux *ogg;
-
- ogg = pad->ogg;
-
- while (!done) {
- int ret;
- ogg_packet packet;
-
- ret = ogg_stream_packetout (&pad->map.stream, &packet);
- switch (ret) {
- case 0:
- GST_LOG_OBJECT (ogg, "packetout done");
- done = TRUE;
- break;
- case -1:
- GST_LOG_OBJECT (ogg, "packetout discont");
- gst_ogg_chain_mark_discont (pad->chain);
- break;
- case 1:
- GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
- result = gst_ogg_pad_submit_packet (pad, &packet);
- if (GST_FLOW_IS_FATAL (result))
- goto could_not_submit;
- break;
- default:
- GST_WARNING_OBJECT (ogg,
- "invalid return value %d for ogg_stream_packetout, resetting stream",
- ret);
- gst_ogg_pad_reset (pad);
- break;
- }
- if (npackets > 0) {
- npackets--;
- done = (npackets == 0);
- }
- }
- return result;
-
- /* ERRORS */
-could_not_submit:
- {
- GST_WARNING_OBJECT (ogg,
- "could not submit packet for stream %08lx, error: %d",
- pad->map.serialno, result);
- gst_ogg_pad_reset (pad);
- return result;
- }
-}
-
-/* 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)
-{
- GstFlowReturn result = GST_FLOW_OK;
- GstOggDemux *ogg;
- gboolean continued = FALSE;
-
- ogg = pad->ogg;
-
- /* for negative rates we read pages backwards and must therefore be carefull
- * with continued pages */
- if (ogg->segment.rate < 0.0) {
- gint npackets;
-
- continued = ogg_page_continued (page);
-
- /* number of completed packets in the page */
- npackets = ogg_page_packets (page);
- if (!continued) {
- /* page is not continued so it contains at least one packet start. It's
- * possible that no packet ends on this page (npackets == 0). In that
- * case, the next (continued) page(s) we kept contain the remainder of the
- * packets. We mark npackets=1 to make us start decoding the pages in the
- * remainder of the algorithm. */
- if (npackets == 0)
- npackets = 1;
- }
- GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
-
- if (npackets == 0) {
- GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
- goto done;
- }
- }
-
- if (ogg_stream_pagein (&pad->map.stream, page) != 0)
- goto choked;
-
- /* flush all packets in the stream layer, this might not give a packet if
- * the page had no packets finishing on the page (npackets == 0). */
- result = gst_ogg_pad_stream_out (pad, 0);
-
- if (pad->continued) {
- ogg_packet packet;
-
- /* now send the continued pages to the stream layer */
- while (pad->continued) {
- ogg_page *p = (ogg_page *) pad->continued->data;
-
- GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
- if (ogg_stream_pagein (&pad->map.stream, p) != 0)
- goto choked;
-
- pad->continued = g_list_delete_link (pad->continued, pad->continued);
-
- /* free the page */
- gst_ogg_page_free (p);
- }
-
- GST_LOG_OBJECT (ogg, "flushing last continued packet");
- /* flush 1 continued packet in the stream layer */
- result = gst_ogg_pad_stream_out (pad, 1);
-
- /* flush all remaining packets, we pushed them in the previous round.
- * We don't use _reset() because we still want to get the discont when
- * we submit a next page. */
- while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
- }
-
-done:
- /* keep continued pages (only in reverse mode) */
- if (continued) {
- ogg_page *p = gst_ogg_page_copy (page);
-
- GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
- pad->continued = g_list_prepend (pad->continued, p);
- }
-
- return result;
-
-choked:
- {
- GST_WARNING_OBJECT (ogg,
- "ogg stream choked on page (serial %08lx), resetting stream",
- pad->map.serialno);
- gst_ogg_pad_reset (pad);
- /* we continue to recover */
- return GST_FLOW_OK;
- }
-}
-
-
-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 *));
- chain->begin_time = GST_CLOCK_TIME_NONE;
- chain->segment_start = GST_CLOCK_TIME_NONE;
- chain->segment_stop = GST_CLOCK_TIME_NONE;
- chain->total_time = GST_CLOCK_TIME_NONE;
-
- 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);
-
- gst_object_unref (pad);
- }
- g_array_free (chain->streams, TRUE);
- g_free (chain);
-}
-
-static void
-gst_ogg_chain_mark_discont (GstOggChain * chain)
-{
- gint i;
-
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
-
- pad->discont = TRUE;
- pad->map.last_size = 0;
- }
-}
-
-static void
-gst_ogg_chain_reset (GstOggChain * chain)
-{
- gint i;
-
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
-
- gst_ogg_pad_reset (pad);
- }
-}
-
-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 */
- gst_object_ref (ret);
- gst_object_sink (ret);
-
- GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
- ret->discont = TRUE;
- ret->map.last_size = 0;
-
- ret->chain = chain;
- ret->ogg = chain->ogg;
-
- ret->map.serialno = serialno;
- if (ogg_stream_init (&ret->map.stream, serialno) != 0)
- goto init_failed;
-
- name = g_strdup_printf ("serial_%08lx", serialno);
- gst_object_set_name (GST_OBJECT (ret), name);
- g_free (name);
-
- /* FIXME: either do something with it or remove it */
- list = gst_tag_list_new ();
- gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
- NULL);
- gst_tag_list_free (list);
-
- GST_DEBUG_OBJECT (chain->ogg,
- "created new ogg src %p for stream with serial %08lx", ret, serialno);
-
- g_array_append_val (chain->streams, ret);
-
- return ret;
-
- /* ERRORS */
-init_failed:
- {
- GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
- serialno);
- gst_object_unref (ret);
- return NULL;
- }
-}
-
-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->map.serialno == serialno)
- return pad;
- }
- return NULL;
-}
-
-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_%d",
- 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; application/x-annodex")
- );
-
-static void gst_ogg_demux_finalize (GObject * object);
-
-static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
- GstOggChain ** chain);
-static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
- GstOggChain * chain);
-
-static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
-static void gst_ogg_demux_loop (GstOggPad * pad);
-static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
-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);
-static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
- GstStateChange transition);
-static void gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
-
-static void gst_ogg_print (GstOggDemux * demux);
-
-GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT);
-
-static void
-gst_ogg_demux_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
- 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;
- gstelement_class->send_event = gst_ogg_demux_receive_event;
-
- gobject_class->finalize = gst_ogg_demux_finalize;
-}
-
-static void
-gst_ogg_demux_init (GstOggDemux * ogg, GstOggDemuxClass * g_class)
-{
- /* create the sink pad */
- ogg->sinkpad =
- gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
- "sink");
-
- gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
- gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
- gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
- 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);
- 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->newsegment = NULL;
-}
-
-static void
-gst_ogg_demux_finalize (GObject * object)
-{
- GstOggDemux *ogg;
-
- ogg = GST_OGG_DEMUX (object);
-
- g_array_free (ogg->chains, TRUE);
- g_mutex_free (ogg->chain_lock);
- ogg_sync_clear (&ogg->sync);
-
- if (ogg->newsegment)
- gst_event_unref (ogg->newsegment);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static gboolean
-gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
-{
- gboolean res;
- GstOggDemux *ogg;
-
- ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_NEWSEGMENT:
- /* FIXME */
- GST_DEBUG_OBJECT (ogg, "got a new segment event");
- ogg_sync_reset (&ogg->sync);
- gst_event_unref (event);
- res = TRUE;
- break;
- case GST_EVENT_EOS:
- {
- GST_DEBUG_OBJECT (ogg, "got an EOS event");
- res = gst_pad_event_default (pad, event);
- if (ogg->current_chain == NULL) {
- GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
- ("can't get first chain"));
- }
- break;
- }
- default:
- res = gst_pad_event_default (pad, event);
- break;
- }
- gst_object_unref (ogg);
-
- return res;
-}
-
-/* submit the given buffer to the ogg sync.
- *
- * Returns the number of bytes submited.
- */
-static GstFlowReturn
-gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
-{
- gint size;
- guint8 *data;
- gchar *oggbuffer;
- GstFlowReturn ret = GST_FLOW_OK;
-
- size = GST_BUFFER_SIZE (buffer);
- data = GST_BUFFER_DATA (buffer);
-
- GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
- if (G_UNLIKELY (size == 0))
- goto done;
-
- oggbuffer = ogg_sync_buffer (&ogg->sync, size);
- if (G_UNLIKELY (oggbuffer == NULL))
- goto no_buffer;
-
- memcpy (oggbuffer, data, size);
- if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
- goto write_failed;
-
-done:
- gst_buffer_unref (buffer);
-
- return ret;
-
- /* ERRORS */
-no_buffer:
- {
- GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
- (NULL), ("failed to get ogg sync buffer"));
- ret = GST_FLOW_ERROR;
- goto done;
- }
-write_failed:
- {
- GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
- (NULL), ("failed to write %d bytes to the sync buffer", size));
- ret = GST_FLOW_ERROR;
- goto done;
- }
-}
-
-/* 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 %" G_GINT64_FORMAT, offset);
-
- ogg->offset = offset;
- ogg->read_offset = offset;
- ogg_sync_reset (&ogg->sync);
-}
-
-/* read more data from the current offset and submit to
- * the ogg sync layer.
- */
-static GstFlowReturn
-gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
-{
- GstFlowReturn ret;
- GstBuffer *buffer;
-
- GST_LOG_OBJECT (ogg,
- "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
- ogg->read_offset, ogg->length, end_offset);
-
- if (end_offset > 0 && ogg->read_offset >= end_offset)
- goto boundary_reached;
-
- if (ogg->read_offset == ogg->length)
- goto eos;
-
- ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
- if (ret != GST_FLOW_OK)
- goto error;
-
- ogg->read_offset += GST_BUFFER_SIZE (buffer);
-
- ret = gst_ogg_demux_submit_buffer (ogg, buffer);
-
- return ret;
-
- /* ERROR */
-boundary_reached:
- {
- GST_LOG_OBJECT (ogg, "reached boundary");
- return GST_FLOW_LIMIT;
- }
-eos:
- {
- GST_LOG_OBJECT (ogg, "reached EOS");
- return GST_FLOW_UNEXPECTED;
- }
-error:
- {
- GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
- gst_flow_get_name (ret));
- return ret;
- }
-}
-
-/* Read the next page from the current offset.
- * boundary: number of bytes ahead we allow looking for;
- * -1 if no boundary
- *
- * @offset will contain the offset the next page starts at when this function
- * returns GST_FLOW_OK.
- *
- * GST_FLOW_UNEXPECTED is returned on EOS.
- *
- * GST_FLOW_LIMIT is returned when we did not find a page before the
- * boundary. If @boundary is -1, this is never returned.
- *
- * Any other error returned while retrieving data from the peer is returned as
- * is.
- */
-static GstFlowReturn
-gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary,
- gint64 * offset)
-{
- gint64 end_offset = -1;
- GstFlowReturn ret;
-
- GST_LOG_OBJECT (ogg,
- "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
- G_GINT64_FORMAT, ogg->offset, boundary);
-
- if (boundary >= 0)
- end_offset = ogg->offset + boundary;
-
- while (TRUE) {
- glong more;
-
- if (end_offset > 0 && ogg->offset >= end_offset)
- goto boundary_reached;
-
- more = ogg_sync_pageseek (&ogg->sync, og);
-
- GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
-
- if (more < 0) {
- /* skipped n bytes */
- ogg->offset -= more;
- GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT, more,
- ogg->offset);
- } else if (more == 0) {
- /* we need more data */
- if (boundary == 0)
- goto boundary_reached;
-
- GST_LOG_OBJECT (ogg, "need more data");
- ret = gst_ogg_demux_get_data (ogg, end_offset);
- if (ret != GST_FLOW_OK)
- break;
- } else {
- gint64 res_offset = ogg->offset;
-
- /* got a page. Return the offset at the page beginning,
- advance the internal offset past the page end */
- if (offset)
- *offset = res_offset;
- ret = GST_FLOW_OK;
-
- ogg->offset += more;
-
- GST_LOG_OBJECT (ogg,
- "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
- G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
- ogg_page_serialno (og), ogg->offset,
- (gint64) ogg_page_granulepos (og));
- break;
- }
- }
- GST_LOG_OBJECT (ogg, "returning %d", ret);
-
- return ret;
-
- /* ERRORS */
-boundary_reached:
- {
- GST_LOG_OBJECT (ogg,
- "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
- ogg->offset, end_offset);
- return GST_FLOW_LIMIT;
- }
-}
-
-/* from the current offset, find the previous page, seeking backwards
- * until we find the page.
- */
-static GstFlowReturn
-gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
-{
- GstFlowReturn ret;
- gint64 begin = ogg->offset;
- gint64 end = begin;
- gint64 cur_offset = -1;
-
- GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
-
- while (cur_offset == -1) {
- begin -= CHUNKSIZE;
- if (begin < 0)
- begin = 0;
-
- /* seek CHUNKSIZE back */
- gst_ogg_demux_seek (ogg, begin);
-
- /* now continue reading until we run out of data, if we find a page
- * start, we save it. It might not be the final page as there could be
- * another page after this one. */
- while (ogg->offset < end) {
- gint64 new_offset;
-
- ret =
- gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
- /* we hit the upper limit, offset contains the last page start */
- if (ret == GST_FLOW_LIMIT) {
- GST_LOG_OBJECT (ogg, "hit limit");
- break;
- }
- /* something went wrong */
- if (ret == GST_FLOW_UNEXPECTED) {
- new_offset = 0;
- GST_LOG_OBJECT (ogg, "got unexpected");
- } else if (ret != GST_FLOW_OK) {
- GST_LOG_OBJECT (ogg, "got error %d", ret);
- return ret;
- }
-
- GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
-
- /* offset is next page start */
- cur_offset = new_offset;
- }
- }
-
- GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
-
- /* we have the offset. Actually snork and hold the page now */
- gst_ogg_demux_seek (ogg, cur_offset);
- ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
- if (ret != GST_FLOW_OK) {
- GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
- cur_offset);
- /* this shouldn't be possible */
- return ret;
- }
-
- if (offset)
- *offset = cur_offset;
-
- return ret;
-}
-
-static gboolean
-gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
-{
- gint i;
- GstOggChain *chain = ogg->current_chain;
-
- if (chain == NULL)
- return TRUE;
-
- GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
-
- /* send EOS on all the pads */
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
- GstEvent *event;
-
- if (!pad->added)
- continue;
-
- event = gst_event_new_eos ();
- gst_event_set_seqnum (event, ogg->seqnum);
- gst_pad_push_event (GST_PAD_CAST (pad), event);
-
- GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
-
- /* deactivate first */
- gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
-
- gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
-
- pad->added = FALSE;
- }
- /* if we cannot seek back to the chain, we can destroy the chain
- * completely */
- if (!ogg->pullmode) {
- gst_ogg_chain_free (chain);
- }
- ogg->current_chain = NULL;
-
- return TRUE;
-}
-
-static gboolean
-gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
- GstEvent * event)
-{
- gint i;
-
- if (chain == ogg->current_chain) {
- if (event)
- gst_event_unref (event);
- return TRUE;
- }
-
- /* FIXME, should not be called with NULL */
- if (chain != NULL) {
- GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
-
- /* first add the pads */
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *pad;
- GstStructure *structure;
-
- pad = g_array_index (chain->streams, GstOggPad *, i);
-
- if (pad->map.is_skeleton || pad->added || GST_PAD_CAPS (pad) == NULL)
- continue;
-
- GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
-
- /* mark discont */
- pad->discont = TRUE;
- pad->map.last_size = 0;
- pad->last_ret = GST_FLOW_OK;
- pad->added = TRUE;
-
- structure = gst_caps_get_structure (GST_PAD_CAPS (pad), 0);
- pad->is_sparse =
- gst_structure_has_name (structure, "application/x-ogm-text") ||
- gst_structure_has_name (structure, "text/x-cmml") ||
- gst_structure_has_name (structure, "subtitle/x-kate") ||
- gst_structure_has_name (structure, "application/x-kate");
-
- /* activate first */
- gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
-
- gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
- }
- }
-
- /* after adding the new pads, remove the old pads */
- gst_ogg_demux_deactivate_current_chain (ogg);
-
- ogg->current_chain = chain;
-
- /* we are finished now */
- gst_element_no_more_pads (GST_ELEMENT (ogg));
-
- /* FIXME, must be sent from the streaming thread */
- if (event) {
- gst_ogg_demux_send_event (ogg, event);
-
- gst_element_found_tags (GST_ELEMENT_CAST (ogg),
- gst_tag_list_new_full (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL));
- }
-
- GST_DEBUG_OBJECT (ogg, "starting chain");
-
- /* then send out any headers and queued packets */
- for (i = 0; i < chain->streams->len; i++) {
- GList *walk;
- GstOggPad *pad;
-
- pad = g_array_index (chain->streams, GstOggPad *, i);
-
- GST_DEBUG_OBJECT (ogg, "pushing headers");
- /* push headers */
- for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
- ogg_packet *p = walk->data;
-
- gst_ogg_demux_chain_peer (pad, p, TRUE);
- }
-
- GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
- /* push queued packets */
- for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
- ogg_packet *p = walk->data;
-
- gst_ogg_demux_chain_peer (pad, p, TRUE);
- _ogg_packet_free (p);
- }
- /* and free the queued buffers */
- g_list_free (pad->map.queued);
- pad->map.queued = NULL;
- }
- return TRUE;
-}
-
-static gboolean
-do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
- gint64 end, gint64 begintime, gint64 endtime, gint64 target,
- gint64 * offset)
-{
- gint64 best;
- GstFlowReturn ret;
- gint64 result = 0;
-
- best = begin;
-
- GST_DEBUG_OBJECT (ogg,
- "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, begin,
- end);
- GST_DEBUG_OBJECT (ogg,
- "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
- GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
- GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
-
- /* perform the seek */
- while (begin < end) {
- gint64 bisect;
-
- if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
- bisect = begin;
- } else {
- /* take a (pretty decent) guess, avoiding overflow */
- gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
-
- bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
-
- if (bisect <= begin)
- bisect = begin;
- GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
- }
- gst_ogg_demux_seek (ogg, bisect);
-
- while (begin < end) {
- ogg_page og;
-
- GST_DEBUG_OBJECT (ogg,
- "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
- ", end %" G_GINT64_FORMAT, bisect, begin, end);
-
- ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
- GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
- result);
-
- if (ret == GST_FLOW_LIMIT) {
- /* we hit the upper limit, go back a bit */
- if (bisect <= begin + 1) {
- end = begin; /* found it */
- } else {
- if (bisect == 0)
- goto seek_error;
-
- bisect -= CHUNKSIZE;
- if (bisect <= begin)
- bisect = begin + 1;
-
- gst_ogg_demux_seek (ogg, bisect);
- }
- } else if (ret == GST_FLOW_OK) {
- /* found offset of next ogg page */
- gint64 granulepos;
- GstClockTime granuletime;
- GstOggPad *pad;
-
- /* get the granulepos */
- GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
- result);
- granulepos = ogg_page_granulepos (&og);
- if (granulepos == -1) {
- GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
- continue;
- }
-
- /* get the stream */
- pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
- if (pad == NULL || pad->map.is_skeleton)
- continue;
-
- /* convert granulepos to time */
- granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
- granulepos);
- if (granuletime < pad->start_time)
- continue;
-
- GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
- GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
-
- granuletime -= pad->start_time;
- granuletime += chain->begin_time;
-
- GST_DEBUG_OBJECT (ogg,
- "found page with granule %" G_GINT64_FORMAT " and time %"
- GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
-
- if (granuletime < target) {
- best = result; /* raw offset of packet with granulepos */
- begin = ogg->offset; /* raw offset of next page */
- begintime = granuletime;
-
- bisect = begin; /* *not* begin + 1 */
- } else {
- if (bisect <= begin + 1) {
- end = begin; /* found it */
- } else {
- if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
- end = result;
- bisect -= CHUNKSIZE; /* an endless loop otherwise. */
- if (bisect <= begin)
- bisect = begin + 1;
- gst_ogg_demux_seek (ogg, bisect);
- } else {
- end = result;
- endtime = granuletime;
- break;
- }
- }
- }
- } else
- goto seek_error;
- }
- }
- GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
- gst_ogg_demux_seek (ogg, best);
- *offset = best;
-
- return TRUE;
-
- /* ERRORS */
-seek_error:
- {
- GST_DEBUG_OBJECT (ogg, "got a seek error");
- return FALSE;
- }
-}
-
-/*
- * do seek to time @position, return FALSE or chain and TRUE
- */
-static gboolean
-gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
- gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
-{
- guint64 position;
- GstOggChain *chain = NULL;
- gint64 begin, end;
- gint64 begintime, endtime;
- gint64 target, keytarget;
- gint64 best;
- gint64 total;
- gint64 result = 0;
- GstFlowReturn ret;
- gint i, pending, len;
-
- position = segment->last_stop;
-
- /* first find the chain to search in */
- total = ogg->total_time;
- if (ogg->chains->len == 0)
- goto no_chains;
-
- for (i = ogg->chains->len - 1; i >= 0; i--) {
- chain = g_array_index (ogg->chains, GstOggChain *, i);
- total -= chain->total_time;
- if (position >= total)
- break;
- }
-
- /* first step, locate page containing the required data */
- begin = chain->offset;
- end = chain->end_offset;
- begintime = chain->begin_time;
- endtime = begintime + chain->total_time;
- target = position - total + begintime;
-
- if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
- &best))
- goto seek_error;
-
- /* second step: find pages for all streams, we use the keyframe_granule to keep
- * track of which ones we saw. If we have seen a page for each stream we can
- * calculate the positions of each keyframe. */
- GST_DEBUG_OBJECT (ogg, "find keyframes");
- len = pending = chain->streams->len;
-
- /* figure out where the keyframes are */
- keytarget = target;
-
- while (TRUE) {
- ogg_page og;
- gint64 granulepos;
- GstOggPad *pad;
- GstClockTime keyframe_time, granule_time;
-
- ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
- GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
- result);
- if (ret == GST_FLOW_LIMIT) {
- GST_LOG_OBJECT (ogg, "reached limit");
- break;
- }
-
- /* get the stream */
- pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
- if (pad == NULL)
- continue;
-
- if (pad->map.is_skeleton)
- goto next;
-
- granulepos = ogg_page_granulepos (&og);
- if (granulepos == -1) {
- GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
- continue;
- }
-
- /* in reverse we want to go past the page with the lower timestamp */
- if (segment->rate < 0.0) {
- /* get time for this pad */
- granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
- granulepos);
-
- GST_LOG_OBJECT (ogg,
- "looking at page with ts %" GST_TIME_FORMAT ", target %"
- GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
- GST_TIME_ARGS (target));
- if (granule_time < target)
- continue;
- }
-
- /* we've seen this pad before */
- if (pad->keyframe_granule != -1)
- continue;
-
- /* convert granule of this pad to the granule of the keyframe */
- pad->keyframe_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map,
- granulepos);
- GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
- pad->keyframe_granule);
-
- /* get time of the keyframe */
- keyframe_time =
- gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
- GST_LOG_OBJECT (ogg, "stream %08lx granule time %" GST_TIME_FORMAT,
- pad->map.serialno, GST_TIME_ARGS (keyframe_time));
-
- /* collect smallest value */
- if (keyframe_time != -1) {
- keyframe_time += begintime;
- if (keyframe_time < keytarget)
- keytarget = keyframe_time;
- }
-
- next:
- pending--;
- if (pending == 0)
- break;
- }
-
- /* for negative rates we will get to the keyframe backwards */
- if (segment->rate < 0.0)
- goto done;
-
- if (keytarget != target) {
- GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
- GST_TIME_ARGS (keytarget));
-
- /* last step, seek to the location of the keyframe */
- if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
- keytarget, &best))
- goto seek_error;
- } else {
- /* seek back to previous position */
- GST_LOG_OBJECT (ogg, "keyframe on target");
- gst_ogg_demux_seek (ogg, best);
- }
-
-done:
- if (keyframe) {
- if (segment->rate > 0.0)
- segment->time = keytarget;
- segment->last_stop = keytarget - begintime;
- }
-
- *rchain = chain;
-
- return TRUE;
-
-no_chains:
- {
- GST_DEBUG_OBJECT (ogg, "no chains");
- return FALSE;
- }
-seek_error:
- {
- GST_DEBUG_OBJECT (ogg, "got a seek error");
- return FALSE;
- }
-}
-
-/* does not take ownership of the event */
-static gboolean
-gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
-{
- GstOggChain *chain = NULL;
- gboolean res;
- gboolean flush, accurate, keyframe;
- GstFormat format;
- gdouble rate;
- GstSeekFlags flags;
- GstSeekType cur_type, stop_type;
- gint64 cur, stop;
- gboolean update;
- guint32 seqnum;
- GstEvent *tevent;
-
- if (event) {
- GST_DEBUG_OBJECT (ogg, "seek with event");
-
- gst_event_parse_seek (event, &rate, &format, &flags,
- &cur_type, &cur, &stop_type, &stop);
-
- /* we can only seek on time */
- if (format != GST_FORMAT_TIME) {
- GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
- goto error;
- }
- seqnum = gst_event_get_seqnum (event);
- } else {
- GST_DEBUG_OBJECT (ogg, "seek without event");
-
- flags = 0;
- rate = 1.0;
- seqnum = gst_util_seqnum_next ();
- }
-
- GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
-
- flush = flags & GST_SEEK_FLAG_FLUSH;
- accurate = flags & GST_SEEK_FLAG_ACCURATE;
- keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
-
- /* first step is to unlock the streaming thread if it is
- * blocked in a chain call, we do this by starting the flush. because
- * we cannot yet hold any streaming lock, we have to protect the chains
- * with their own lock. */
- if (flush) {
- gint i;
-
- tevent = gst_event_new_flush_start ();
- gst_event_set_seqnum (tevent, seqnum);
-
- gst_event_ref (tevent);
- gst_pad_push_event (ogg->sinkpad, tevent);
-
- GST_CHAIN_LOCK (ogg);
- for (i = 0; i < ogg->chains->len; i++) {
- GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
- gint j;
-
- for (j = 0; j < chain->streams->len; j++) {
- GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
-
- gst_event_ref (tevent);
- gst_pad_push_event (GST_PAD (pad), tevent);
- }
- }
- GST_CHAIN_UNLOCK (ogg);
-
- gst_event_unref (tevent);
- } else {
- gst_pad_pause_task (ogg->sinkpad);
- }
-
- /* now grab the stream lock so that streaming cannot continue, for
- * non flushing seeks when the element is in PAUSED this could block
- * forever. */
- GST_PAD_STREAM_LOCK (ogg->sinkpad);
-
- if (ogg->segment_running && !flush) {
- /* create the segment event to close the current segment */
- if ((chain = ogg->current_chain)) {
- GstEvent *newseg;
- gint64 chain_start = 0;
-
- if (chain->segment_start != GST_CLOCK_TIME_NONE)
- chain_start = chain->segment_start;
-
- newseg = gst_event_new_new_segment (TRUE, ogg->segment.rate,
- GST_FORMAT_TIME, ogg->segment.start + chain_start,
- ogg->segment.last_stop + chain_start, ogg->segment.time);
- /* set the seqnum of the running segment */
- gst_event_set_seqnum (newseg, ogg->seqnum);
-
- /* send segment on old chain, FIXME, must be sent from streaming thread. */
- gst_ogg_demux_send_event (ogg, newseg);
- }
- }
-
- if (event) {
- gst_segment_set_seek (&ogg->segment, rate, format, flags,
- cur_type, cur, stop_type, stop, &update);
- }
-
- GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
- GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
- GST_TIME_ARGS (ogg->segment.stop));
-
- /* we need to stop flushing on the srcpad as we're going to use it
- * next. We can do this as we have the STREAM lock now. */
- if (flush) {
- tevent = gst_event_new_flush_stop ();
- gst_event_set_seqnum (tevent, seqnum);
- gst_pad_push_event (ogg->sinkpad, tevent);
- }
-
- {
- gint i;
-
- /* reset all ogg streams now, need to do this from within the lock to
- * make sure the streaming thread is not messing with the stream */
- for (i = 0; i < ogg->chains->len; i++) {
- GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
-
- gst_ogg_chain_reset (chain);
- }
- }
-
- /* for reverse we will already seek accurately */
- res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
-
- /* seek failed, make sure we continue the current chain */
- if (!res) {
- GST_DEBUG_OBJECT (ogg, "seek failed");
- chain = ogg->current_chain;
- } else {
- GST_DEBUG_OBJECT (ogg, "seek success");
- }
-
- if (!chain)
- goto no_chain;
-
- /* now we have a new position, prepare for streaming again */
- {
- GstEvent *event;
- gint64 stop;
- gint64 start;
- gint64 last_stop, begin_time;
-
- /* we have to send the flush to the old chain, not the new one */
- if (flush) {
- tevent = gst_event_new_flush_stop ();
- gst_event_set_seqnum (tevent, seqnum);
- gst_ogg_demux_send_event (ogg, tevent);
- }
-
- /* we need this to see how far inside the chain we need to start */
- if (chain->begin_time != GST_CLOCK_TIME_NONE)
- begin_time = chain->begin_time;
- else
- begin_time = 0;
-
- /* segment.start gives the start over all chains, we calculate the amount
- * of time into this chain we need to start */
- start = ogg->segment.start - begin_time;
- if (chain->segment_start != GST_CLOCK_TIME_NONE)
- start += chain->segment_start;
-
- if ((stop = ogg->segment.stop) == -1)
- stop = ogg->segment.duration;
-
- /* segment.stop gives the stop time over all chains, calculate the amount of
- * time we need to stop in this chain */
- if (stop != -1) {
- if (stop > begin_time)
- stop -= begin_time;
- else
- stop = 0;
- stop += chain->segment_start;
- /* we must stop when this chain ends and switch to the next chain to play
- * the remainder of the segment. */
- stop = MIN (stop, chain->segment_stop);
- }
-
- last_stop = ogg->segment.last_stop;
- if (chain->segment_start != GST_CLOCK_TIME_NONE)
- last_stop += chain->segment_start;
-
- /* create the segment event we are going to send out */
- if (ogg->segment.rate >= 0.0)
- event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
- ogg->segment.format, last_stop, stop, ogg->segment.time);
- else
- event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
- ogg->segment.format, start, last_stop, ogg->segment.time);
-
- gst_event_set_seqnum (event, seqnum);
-
- if (chain != ogg->current_chain) {
- /* switch to different chain, send segment on new chain */
- gst_ogg_demux_activate_chain (ogg, chain, event);
- } else {
- /* mark discont and send segment on current chain */
- gst_ogg_chain_mark_discont (chain);
- /* This event should be sent from the streaming thread (sink pad task) */
- if (ogg->newsegment)
- gst_event_unref (ogg->newsegment);
- ogg->newsegment = event;
- }
-
- /* notify start of new segment */
- if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
- GstMessage *message;
-
- message = gst_message_new_segment_start (GST_OBJECT (ogg),
- GST_FORMAT_TIME, ogg->segment.last_stop);
- gst_message_set_seqnum (message, seqnum);
-
- gst_element_post_message (GST_ELEMENT (ogg), message);
- }
-
- ogg->segment_running = TRUE;
- ogg->seqnum = seqnum;
- /* restart our task since it might have been stopped when we did the
- * flush. */
- gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
- ogg->sinkpad);
- }
-
- /* streaming can continue now */
- GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
-
- return res;
-
-error:
- {
- GST_DEBUG_OBJECT (ogg, "seek failed");
- return FALSE;
- }
-no_chain:
- {
- GST_DEBUG_OBJECT (ogg, "no chain to seek in");
- GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
- return FALSE;
- }
-}
-
-/* finds each bitstream link one at a time using a bisection search
- * (has to begin by knowing the offset of the lb's initial page).
- * Recurses for each link so it can alloc the link storage after
- * finding them all, then unroll and fill the cache at the same time
- */
-static GstFlowReturn
-gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
- gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
-{
- gint64 endsearched = end;
- gint64 next = end;
- ogg_page og;
- GstFlowReturn ret;
- gint64 offset;
- GstOggChain *nextchain;
-
- GST_LOG_OBJECT (ogg,
- "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
- ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
-
- /* the below guards against garbage seperating the last and
- * first pages of two links. */
- while (searched < endsearched) {
- gint64 bisect;
-
- if (endsearched - searched < CHUNKSIZE) {
- bisect = searched;
- } else {
- bisect = (searched + endsearched) / 2;
- }
-
- gst_ogg_demux_seek (ogg, bisect);
- ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
-
- if (ret == GST_FLOW_UNEXPECTED) {
- endsearched = bisect;
- } else if (ret == GST_FLOW_OK) {
- glong serial = ogg_page_serialno (&og);
-
- if (!gst_ogg_chain_has_stream (chain, serial)) {
- endsearched = bisect;
- next = offset;
- } else {
- searched = offset + og.header_len + og.body_len;
- }
- } else
- return ret;
- }
-
- GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
-
- chain->end_offset = searched;
- ret = gst_ogg_demux_read_end_chain (ogg, chain);
- if (ret != GST_FLOW_OK)
- return ret;
-
- GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
-
- gst_ogg_demux_seek (ogg, next);
- ret = gst_ogg_demux_read_chain (ogg, &nextchain);
- if (ret == GST_FLOW_UNEXPECTED) {
- nextchain = NULL;
- ret = GST_FLOW_OK;
- GST_LOG_OBJECT (ogg, "no next chain");
- } else if (ret != GST_FLOW_OK)
- goto done;
-
- if (searched < end && nextchain != NULL) {
- ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
- end, nextchain, m + 1);
- if (ret != GST_FLOW_OK)
- goto done;
- }
- GST_LOG_OBJECT (ogg, "adding chain %p", chain);
-
- g_array_insert_val (ogg->chains, 0, chain);
-
-done:
- return ret;
-}
-
-/* read a chain from the ogg file. This code will
- * read all BOS pages and will create and return a GstOggChain
- * structure with the results.
- *
- * This function will also read N pages from each stream in the
- * chain and submit them to the decoders. When the decoder has
- * decoded the first buffer, we know the timestamp of the first
- * page in the chain.
- */
-static GstFlowReturn
-gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
-{
- GstFlowReturn ret;
- GstOggChain *chain = NULL;
- gint64 offset = ogg->offset;
- ogg_page op;
- gboolean done;
- gint i;
-
- GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
-
- /* first read the BOS pages, do typefind on them, create
- * the decoders, send data to the decoders. */
- while (TRUE) {
- GstOggPad *pad;
- glong serial;
-
- ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
- if (ret != GST_FLOW_OK) {
- GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
- break;
- }
- if (!ogg_page_bos (&op)) {
- GST_WARNING_OBJECT (ogg, "page is not BOS page");
- /* if we did not find a chain yet, assume this is a bogus stream and
- * ignore it */
- if (!chain)
- ret = GST_FLOW_UNEXPECTED;
- break;
- }
-
- if (chain == NULL) {
- chain = gst_ogg_chain_new (ogg);
- chain->offset = offset;
- }
-
- serial = ogg_page_serialno (&op);
- if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
- GST_WARNING_OBJECT (ogg, "found serial %08lx BOS page twice, ignoring",
- serial);
- continue;
- }
-
- pad = gst_ogg_chain_new_stream (chain, serial);
- gst_ogg_pad_submit_page (pad, &op);
- }
-
- if (ret != GST_FLOW_OK || chain == NULL) {
- if (ret == GST_FLOW_OK) {
- GST_WARNING_OBJECT (ogg, "no chain was found");
- ret = GST_FLOW_ERROR;
- } else if (ret != GST_FLOW_UNEXPECTED) {
- GST_WARNING_OBJECT (ogg, "failed to read chain");
- } else {
- GST_DEBUG_OBJECT (ogg, "done reading chains");
- }
- if (chain) {
- gst_ogg_chain_free (chain);
- }
- if (res_chain)
- *res_chain = NULL;
- return ret;
- }
-
- chain->have_bos = TRUE;
- GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
-
- /* now read pages until we receive a buffer from each of the
- * stream decoders, this will tell us the timestamp of the
- * first packet in the chain then */
-
- /* save the offset to the first non bos page in the chain: if searching for
- * pad->first_time we read past the end of the chain, we'll seek back to this
- * position
- */
- offset = ogg->offset;
-
- done = FALSE;
- while (!done) {
- glong serial;
- gboolean known_serial = FALSE;
- GstFlowReturn ret;
-
- serial = ogg_page_serialno (&op);
- done = TRUE;
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
-
- GST_LOG_OBJECT (ogg, "serial %08lx time %" GST_TIME_FORMAT,
- pad->map.serialno, GST_TIME_ARGS (pad->start_time));
-
- if (pad->map.serialno == serial) {
- known_serial = TRUE;
-
- /* submit the page now, this will fill in the start_time when the
- * internal decoder finds it */
- gst_ogg_pad_submit_page (pad, &op);
-
- if (!pad->map.is_skeleton && pad->start_time == -1
- && ogg_page_eos (&op)) {
- /* got EOS on a pad before we could find its start_time.
- * We have no chance of finding a start_time for every pad so
- * stop searching for the other start_time(s).
- */
- done = TRUE;
- break;
- }
- }
- /* the timestamp will be filled in when we submit the pages */
- if (!pad->map.is_skeleton)
- done &= (pad->start_time != GST_CLOCK_TIME_NONE);
-
- GST_LOG_OBJECT (ogg, "done %08lx now %d", pad->map.serialno, done);
- }
-
- /* we read a page not belonging to the current chain: seek back to the
- * beginning of the chain
- */
- if (!known_serial) {
- GST_LOG_OBJECT (ogg, "unknown serial %08lx", serial);
- gst_ogg_demux_seek (ogg, offset);
- break;
- }
-
- if (!done) {
- ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
- if (ret != GST_FLOW_OK)
- break;
- }
- }
- GST_LOG_OBJECT (ogg, "done reading chain");
- /* now we can fill in the missing info using queries */
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
-
- if (pad->map.is_skeleton)
- continue;
-
- pad->mode = GST_OGG_PAD_MODE_STREAMING;
- }
-
- if (res_chain)
- *res_chain = chain;
-
- return GST_FLOW_OK;
-}
-
-/* read the last pages from the ogg stream to get the final
- * page end_offsets.
- */
-static GstFlowReturn
-gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
-{
- gint64 begin = chain->end_offset;
- gint64 end = begin;
- gint64 last_granule = -1;
- GstOggPad *last_pad = NULL;
- GstFlowReturn ret;
- gboolean done = FALSE;
- ogg_page og;
- gint i;
-
- while (!done) {
- begin -= CHUNKSIZE;
- if (begin < 0)
- begin = 0;
-
- gst_ogg_demux_seek (ogg, begin);
-
- /* now continue reading until we run out of data, if we find a page
- * start, we save it. It might not be the final page as there could be
- * another page after this one. */
- while (ogg->offset < end) {
- ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
-
- if (ret == GST_FLOW_LIMIT)
- break;
- if (ret != GST_FLOW_OK)
- return ret;
-
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
-
- if (pad->map.is_skeleton)
- continue;
-
- if (pad->map.serialno == ogg_page_serialno (&og)) {
- gint64 granulepos = ogg_page_granulepos (&og);
-
- if (last_granule == -1 || last_granule < granulepos) {
- last_granule = granulepos;
- last_pad = pad;
- }
- if (last_granule != -1) {
- done = TRUE;
- }
- break;
- }
- }
- }
- }
-
- if (last_pad) {
- chain->segment_stop =
- gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
- last_granule);
- } else {
- chain->segment_stop = GST_CLOCK_TIME_NONE;
- }
-
- GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
-
- return GST_FLOW_OK;
-}
-
-/* find a pad with a given serial number
- */
-static GstOggPad *
-gst_ogg_demux_find_pad (GstOggDemux * ogg, glong serialno)
-{
- GstOggPad *pad;
- gint i;
-
- /* first look in building chain if any */
- if (ogg->building_chain) {
- pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
- if (pad)
- return pad;
- }
-
- /* then look in current chain if any */
- if (ogg->current_chain) {
- pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
- if (pad)
- return pad;
- }
-
- for (i = 0; i < ogg->chains->len; i++) {
- GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
-
- pad = gst_ogg_chain_get_stream (chain, serialno);
- if (pad)
- return pad;
- }
- return NULL;
-}
-
-/* find a chain with a given serial number
- */
-static GstOggChain *
-gst_ogg_demux_find_chain (GstOggDemux * ogg, glong serialno)
-{
- GstOggPad *pad;
-
- pad = gst_ogg_demux_find_pad (ogg, serialno);
- if (pad) {
- return pad->chain;
- }
- return NULL;
-}
-
-/* returns TRUE if all streams have valid start time */
-static gboolean
-gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
-{
- gint i;
- gboolean res = TRUE;
-
- chain->total_time = GST_CLOCK_TIME_NONE;
- chain->segment_start = G_MAXUINT64;
-
- GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
-
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
-
- if (pad->map.is_skeleton)
- continue;
-
- /* can do this if the pad start time is not defined */
- if (pad->start_time == GST_CLOCK_TIME_NONE)
- res = FALSE;
- else
- chain->segment_start = MIN (chain->segment_start, pad->start_time);
- }
-
- if (chain->segment_stop != GST_CLOCK_TIME_NONE
- && chain->segment_start != G_MAXUINT64)
- chain->total_time = chain->segment_stop - chain->segment_start;
-
- GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
-
- GST_DEBUG_OBJECT (ogg, "return %d", res);
-
- return res;
-}
-
-static void
-gst_ogg_demux_collect_info (GstOggDemux * ogg)
-{
- gint i;
-
- /* collect all info */
- ogg->total_time = 0;
-
- for (i = 0; i < ogg->chains->len; i++) {
- GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
-
- chain->begin_time = ogg->total_time;
-
- gst_ogg_demux_collect_chain_info (ogg, chain);
-
- ogg->total_time += chain->total_time;
- }
- gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time);
-}
-
-/* find all the chains in the ogg file, this reads the first and
- * last page of the ogg stream, if they match then the ogg file has
- * just one chain, else we do a binary search for all chains.
- */
-static GstFlowReturn
-gst_ogg_demux_find_chains (GstOggDemux * ogg)
-{
- ogg_page og;
- GstPad *peer;
- GstFormat format;
- gboolean res;
- gulong serialno;
- GstOggChain *chain;
- GstFlowReturn ret;
-
- /* get peer to figure out length */
- if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
- goto no_peer;
-
- /* find length to read last page, we store this for later use. */
- format = GST_FORMAT_BYTES;
- res = gst_pad_query_duration (peer, &format, &ogg->length);
- gst_object_unref (peer);
- if (!res || ogg->length <= 0)
- goto no_length;
-
- GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
-
- /* read chain from offset 0, this is the first chain of the
- * ogg file. */
- gst_ogg_demux_seek (ogg, 0);
- ret = gst_ogg_demux_read_chain (ogg, &chain);
- if (ret != GST_FLOW_OK)
- goto no_first_chain;
-
- /* read page from end offset, we use this page to check if its serial
- * number is contained in the first chain. If this is the case then
- * this ogg is not a chained ogg and we can skip the scanning. */
- gst_ogg_demux_seek (ogg, ogg->length);
- ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
- if (ret != GST_FLOW_OK)
- goto no_last_page;
-
- serialno = ogg_page_serialno (&og);
-
- if (!gst_ogg_chain_has_stream (chain, serialno)) {
- /* the last page is not in the first stream, this means we should
- * find all the chains in this chained ogg. */
- ret =
- gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
- 0);
- } else {
- /* we still call this function here but with an empty range so that
- * we can reuse the setup code in this routine. */
- ret =
- gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length,
- chain, 0);
- }
- if (ret != GST_FLOW_OK)
- goto done;
-
- /* all fine, collect and print */
- gst_ogg_demux_collect_info (ogg);
-
- /* dump our chains and streams */
- gst_ogg_print (ogg);
-
-done:
- return ret;
-
- /*** error cases ***/
-no_peer:
- {
- GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
- return GST_FLOW_NOT_LINKED;
- }
-no_length:
- {
- GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
- return GST_FLOW_NOT_SUPPORTED;
- }
-no_first_chain:
- {
- GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
- return GST_FLOW_ERROR;
- }
-no_last_page:
- {
- GST_DEBUG_OBJECT (ogg, "can't get last page");
- if (chain)
- gst_ogg_chain_free (chain);
- return ret;
- }
-}
-
-static GstFlowReturn
-gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
-{
- GstOggPad *pad;
- gint64 granule;
- glong serialno;
- GstFlowReturn result = GST_FLOW_OK;
-
- serialno = ogg_page_serialno (page);
- granule = ogg_page_granulepos (page);
-
- GST_LOG_OBJECT (ogg,
- "processing ogg page (serial %08lx, pageno %ld, granulepos %"
- G_GINT64_FORMAT ", bos %d)",
- serialno, ogg_page_pageno (page), granule, ogg_page_bos (page));
-
- if (ogg_page_bos (page)) {
- GstOggChain *chain;
-
- /* first page */
- /* see if we know about the chain already */
- chain = gst_ogg_demux_find_chain (ogg, serialno);
- if (chain) {
- GstEvent *event;
- gint64 start = 0;
-
- if (chain->segment_start != GST_CLOCK_TIME_NONE)
- start = chain->segment_start;
-
- /* create the newsegment event we are going to send out */
- event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
- GST_FORMAT_TIME, start, chain->segment_stop, chain->begin_time);
- gst_event_set_seqnum (event, ogg->seqnum);
-
- GST_DEBUG_OBJECT (ogg,
- "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
- ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
- GST_TIME_ARGS (chain->segment_stop),
- GST_TIME_ARGS (chain->begin_time));
-
- /* activate it as it means we have a non-header, this will also deactivate
- * the currently running chain. */
- gst_ogg_demux_activate_chain (ogg, chain, event);
- pad = gst_ogg_demux_find_pad (ogg, serialno);
- } else {
- GstClockTime chain_time;
- GstOggChain *current_chain;
- gint64 current_time;
-
- /* this can only happen in push mode */
- if (ogg->pullmode)
- goto unknown_chain;
-
- current_chain = ogg->current_chain;
- current_time = ogg->segment.last_stop;
-
- /* time of new chain is current time */
- chain_time = current_time;
-
- if (ogg->building_chain == NULL) {
- GstOggChain *newchain;
-
- newchain = gst_ogg_chain_new (ogg);
- newchain->offset = 0;
- /* set new chain begin time aligned with end time of old chain */
- newchain->begin_time = chain_time;
- GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
- GST_TIME_ARGS (chain_time));
-
- /* and this is the one we are building now */
- ogg->building_chain = newchain;
- }
- pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
- }
- } else {
- pad = gst_ogg_demux_find_pad (ogg, serialno);
- }
- if (pad) {
- result = gst_ogg_pad_submit_page (pad, page);
- } else {
- /* no pad. This means an ogg page without bos has been seen for this
- * serialno. we just ignore it but post a warning... */
- GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
- (NULL), ("unknown ogg pad for serial %08lx detected", serialno));
- return GST_FLOW_OK;
- }
- return result;
-
- /* ERRORS */
-unknown_chain:
- {
- GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
- (NULL), ("unknown ogg chain for serial %08lx detected", serialno));
- return GST_FLOW_ERROR;
- }
-}
-
-/* streaming mode, receive a buffer, parse it, create pads for
- * the serialno, submit pages and packets to the oggpads
- */
-static GstFlowReturn
-gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
-{
- GstOggDemux *ogg;
- gint ret = 0;
- GstFlowReturn result = GST_FLOW_OK;
-
- ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
-
- GST_DEBUG_OBJECT (ogg, "chain");
- result = gst_ogg_demux_submit_buffer (ogg, buffer);
-
- while (result == GST_FLOW_OK) {
- ogg_page page;
-
- ret = ogg_sync_pageout (&ogg->sync, &page);
- if (ret == 0)
- /* need more data */
- break;
- if (ret == -1) {
- /* discontinuity in the pages */
- GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
- } else {
- result = gst_ogg_demux_handle_page (ogg, &page);
- }
- }
- if (ret == 0 || result == GST_FLOW_OK) {
- gst_ogg_demux_sync_streams (ogg);
- }
- return result;
-}
-
-static void
-gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
-{
- GstOggChain *chain = ogg->current_chain;
-
- if (chain) {
- gint i;
-
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
-
- gst_event_ref (event);
- GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
- gst_pad_push_event (GST_PAD (pad), event);
- }
- }
- gst_event_unref (event);
-}
-
-static GstFlowReturn
-gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
- GstFlowReturn ret)
-{
- GstOggChain *chain;
-
- /* store the value */
- pad->last_ret = ret;
-
- /* any other error that is not-linked can be returned right
- * away */
- if (ret != GST_FLOW_NOT_LINKED)
- goto done;
-
- /* only return NOT_LINKED if all other pads returned NOT_LINKED */
- chain = ogg->current_chain;
- if (chain) {
- gint i;
-
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
-
- ret = opad->last_ret;
- /* some other return value (must be SUCCESS but we can return
- * other values as well) */
- if (ret != GST_FLOW_NOT_LINKED)
- goto done;
- }
- /* if we get here, all other pads were unlinked and we return
- * NOT_LINKED then */
- }
-done:
- return ret;
-}
-
-static GstFlowReturn
-gst_ogg_demux_loop_forward (GstOggDemux * ogg)
-{
- GstFlowReturn ret;
- GstBuffer *buffer;
-
- if (ogg->offset == ogg->length) {
- GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
- " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
- ret = GST_FLOW_UNEXPECTED;
- goto done;
- }
-
- GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
- ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
- if (ret != GST_FLOW_OK) {
- GST_LOG_OBJECT (ogg, "Failed pull_range");
- goto done;
- }
-
- ogg->offset += GST_BUFFER_SIZE (buffer);
-
- if (G_UNLIKELY (ogg->newsegment)) {
- gst_ogg_demux_send_event (ogg, ogg->newsegment);
- ogg->newsegment = NULL;
- }
-
- ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
- if (ret != GST_FLOW_OK) {
- GST_LOG_OBJECT (ogg, "Failed demux_chain");
- goto done;
- }
-
- /* check for the end of the segment */
- if (ogg->segment.stop != -1 && ogg->segment.last_stop != -1) {
- if (ogg->segment.last_stop > ogg->segment.stop) {
- ret = GST_FLOW_UNEXPECTED;
- goto done;
- }
- }
-done:
- return ret;
-}
-
-/* reverse mode.
- *
- * We read the pages backwards and send the packets forwards. The first packet
- * in the page will be pushed with the DISCONT flag set.
- *
- * Special care has to be taken for continued pages, which we can only decode
- * when we have the previous page(s).
- */
-static GstFlowReturn
-gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
-{
- GstFlowReturn ret;
- ogg_page page;
- gint64 offset;
-
- if (ogg->offset == 0) {
- GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
- " == 0", ogg->offset);
- ret = GST_FLOW_UNEXPECTED;
- goto done;
- }
-
- GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
- ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
- if (ret != GST_FLOW_OK)
- goto done;
-
- ogg->offset = offset;
-
- if (G_UNLIKELY (ogg->newsegment)) {
- gst_ogg_demux_send_event (ogg, ogg->newsegment);
- ogg->newsegment = NULL;
- }
-
- ret = gst_ogg_demux_handle_page (ogg, &page);
- if (ret != GST_FLOW_OK)
- goto done;
-
- /* check for the end of the segment */
- if (ogg->segment.start != -1 && ogg->segment.last_stop != -1) {
- if (ogg->segment.last_stop <= ogg->segment.start) {
- ret = GST_FLOW_UNEXPECTED;
- goto done;
- }
- }
-done:
- return ret;
-}
-
-static void
-gst_ogg_demux_sync_streams (GstOggDemux * ogg)
-{
- GstClockTime cur;
- GstOggChain *chain;
- guint i;
-
- chain = ogg->current_chain;
- cur = ogg->segment.last_stop;
- if (chain == NULL || cur == -1)
- return;
-
- for (i = 0; i < chain->streams->len; i++) {
- GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
-
- /* Theoretically, we should be doing this for all streams, but we're only
- * doing it for known-to-be-sparse streams at the moment in order not to
- * break things for wrongly-muxed streams (like we used to produce once) */
- if (stream->is_sparse && stream->last_stop != GST_CLOCK_TIME_NONE) {
-
- /* Does this stream lag? Random threshold of 2 seconds */
- if (GST_CLOCK_DIFF (stream->last_stop, cur) > (2 * GST_SECOND)) {
- GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
- "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
- GST_TIME_ARGS (stream->last_stop), GST_TIME_ARGS (cur));
- stream->last_stop = cur;
- /* advance stream time (FIXME: is this right, esp. time_pos?) */
- gst_pad_push_event (GST_PAD_CAST (stream),
- gst_event_new_new_segment (TRUE, ogg->segment.rate,
- GST_FORMAT_TIME, stream->last_stop, -1, stream->last_stop));
- }
- }
- }
-}
-
-/* random access code
- *
- * - first find all the chains and streams by scanning the file.
- * - then get and chain buffers, just like the streaming case.
- * - when seeking, we can use the chain info to perform the seek.
- */
-static void
-gst_ogg_demux_loop (GstOggPad * pad)
-{
- GstOggDemux *ogg;
- GstFlowReturn ret;
- GstEvent *event;
-
- ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
-
- if (ogg->need_chains) {
- gboolean res;
-
- /* this is the only place where we write chains and thus need to lock. */
- GST_CHAIN_LOCK (ogg);
- ret = gst_ogg_demux_find_chains (ogg);
- GST_CHAIN_UNLOCK (ogg);
- if (ret != GST_FLOW_OK)
- goto chain_read_failed;
-
- ogg->need_chains = FALSE;
-
- GST_OBJECT_LOCK (ogg);
- ogg->running = TRUE;
- event = ogg->event;
- ogg->event = NULL;
- GST_OBJECT_UNLOCK (ogg);
-
- /* and seek to configured positions without FLUSH */
- res = gst_ogg_demux_perform_seek (ogg, event);
- if (event)
- gst_event_unref (event);
-
- if (!res)
- goto seek_failed;
- }
-
- if (ogg->segment.rate >= 0.0)
- ret = gst_ogg_demux_loop_forward (ogg);
- else
- ret = gst_ogg_demux_loop_reverse (ogg);
-
- if (ret != GST_FLOW_OK)
- goto pause;
-
- gst_ogg_demux_sync_streams (ogg);
- return;
-
- /* ERRORS */
-chain_read_failed:
- {
- /* error was posted */
- goto pause;
- }
-seek_failed:
- {
- GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
- ("failed to start demuxing ogg"));
- ret = GST_FLOW_ERROR;
- goto pause;
- }
-pause:
- {
- const gchar *reason = gst_flow_get_name (ret);
- GstEvent *event = NULL;
-
- GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
- ogg->segment_running = FALSE;
- gst_pad_pause_task (ogg->sinkpad);
-
- if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
- if (ret == GST_FLOW_UNEXPECTED) {
- /* perform EOS logic */
- if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
- gint64 stop;
- GstMessage *message;
-
- /* for segment playback we need to post when (in stream time)
- * we stopped, this is either stop (when set) or the duration. */
- if ((stop = ogg->segment.stop) == -1)
- stop = ogg->segment.duration;
-
- GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
- message =
- gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
- stop);
- gst_message_set_seqnum (message, ogg->seqnum);
-
- gst_element_post_message (GST_ELEMENT (ogg), message);
- } else {
- /* normal playback, send EOS to all linked pads */
- GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
- event = gst_event_new_eos ();
- }
- } else {
- GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
- (_("Internal data stream error.")),
- ("stream stopped, reason %s", reason));
- event = gst_event_new_eos ();
- }
- if (event) {
- gst_event_set_seqnum (event, ogg->seqnum);
- gst_ogg_demux_send_event (ogg, event);
- }
- }
- return;
- }
-}
-
-static void
-gst_ogg_demux_clear_chains (GstOggDemux * ogg)
-{
- gint i;
-
- gst_ogg_demux_deactivate_current_chain (ogg);
-
- GST_CHAIN_LOCK (ogg);
- for (i = 0; i < ogg->chains->len; i++) {
- GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
-
- gst_ogg_chain_free (chain);
- }
- ogg->chains = g_array_set_size (ogg->chains, 0);
- GST_CHAIN_UNLOCK (ogg);
-}
-
-/* this function is called when the pad is activated and should start
- * processing data.
- *
- * We check if we can do random access to decide if we work push or
- * pull based.
- */
-static gboolean
-gst_ogg_demux_sink_activate (GstPad * sinkpad)
-{
- if (gst_pad_check_pull_range (sinkpad)) {
- GST_DEBUG_OBJECT (sinkpad, "activating pull");
- return gst_pad_activate_pull (sinkpad, TRUE);
- } else {
- GST_DEBUG_OBJECT (sinkpad, "activating push");
- return gst_pad_activate_push (sinkpad, TRUE);
- }
-}
-
-/* this function gets called when we activate ourselves in push mode.
- * We cannot seek (ourselves) in the stream */
-static gboolean
-gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
-{
- GstOggDemux *ogg;
-
- ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
-
- ogg->pullmode = FALSE;
-
- return TRUE;
-}
-
-/* this function gets called when we activate ourselves in pull mode.
- * We can perform random access to the resource and we start a task
- * to start reading */
-static gboolean
-gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
-{
- GstOggDemux *ogg;
-
- ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
-
- if (active) {
- ogg->need_chains = TRUE;
- ogg->pullmode = TRUE;
-
- return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
- sinkpad);
- } else {
- return gst_pad_stop_task (sinkpad);
- }
-}
-
-static GstStateChangeReturn
-gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
-{
- GstOggDemux *ogg;
- GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
-
- ogg = GST_OGG_DEMUX (element);
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- ogg->basetime = 0;
- ogg->have_fishead = FALSE;
- ogg_sync_init (&ogg->sync);
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- ogg_sync_reset (&ogg->sync);
- ogg->running = FALSE;
- ogg->segment_running = FALSE;
- gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- break;
- default:
- break;
- }
-
- result = parent_class->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- gst_ogg_demux_clear_chains (ogg);
- GST_OBJECT_LOCK (ogg);
- ogg->running = FALSE;
- ogg->segment_running = FALSE;
- ogg->have_fishead = FALSE;
- GST_OBJECT_UNLOCK (ogg);
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- ogg_sync_clear (&ogg->sync);
- break;
- default:
- break;
- }
- return result;
-}
-
-gboolean
-gst_ogg_demux_plugin_init (GstPlugin * plugin)
-{
- GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
- GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
- "ogg demuxer setup stage when parsing pipeline");
-
-#if ENABLE_NLS
- GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
- LOCALEDIR);
- bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
-
- return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
- GST_TYPE_OGG_DEMUX);
-}
-
-/* prints all info about the element */
-#undef GST_CAT_DEFAULT
-#define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
-
-#ifdef GST_DISABLE_GST_DEBUG
-
-static void
-gst_ogg_print (GstOggDemux * ogg)
-{
- /* NOP */
-}
-
-#else /* !GST_DISABLE_GST_DEBUG */
-
-static void
-gst_ogg_print (GstOggDemux * ogg)
-{
- guint j, i;
-
- GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
- GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (ogg->total_time));
-
- for (i = 0; i < ogg->chains->len; i++) {
- GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
-
- GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
- GST_INFO_OBJECT (ogg, " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT,
- chain->offset, chain->end_offset);
- GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (chain->begin_time));
- GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (chain->total_time));
- GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (chain->segment_start));
- GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (chain->segment_stop));
-
- for (j = 0; j < chain->streams->len; j++) {
- GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
-
- GST_INFO_OBJECT (ogg, " stream %08lx:", stream->map.serialno);
- GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (stream->start_time));
- }
- }
-}
-#endif /* GST_DISABLE_GST_DEBUG */