diff options
Diffstat (limited to 'ext/ogg/gstoggparse.c')
-rw-r--r-- | ext/ogg/gstoggparse.c | 698 |
1 files changed, 0 insertions, 698 deletions
diff --git a/ext/ogg/gstoggparse.c b/ext/ogg/gstoggparse.c deleted file mode 100644 index a86958b5..00000000 --- a/ext/ogg/gstoggparse.c +++ /dev/null @@ -1,698 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Michael Smith <msmith@fluendo.com> - * - * gstoggparse.c: ogg stream parser - * - * 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. - */ - -/* This ogg parser is essentially a subset of the ogg demuxer - rather than - * fully demuxing into packets, we only parse out the pages, create one - * GstBuffer per page, set all the appropriate flags on those pages, set caps - * appropriately (particularly the 'streamheader' which gives all the header - * pages required for initialing decode). - * - * It's dramatically simpler than the full demuxer as it does not support - * seeking. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include <gst/gst.h> -#include <ogg/ogg.h> -#include <string.h> - -#include "gstoggstream.h" - -static const GstElementDetails gst_ogg_parse_details = -GST_ELEMENT_DETAILS ("Ogg parser", - "Codec/Parser", - "parse ogg streams into pages (info about ogg: http://xiph.org)", - "Michael Smith <msmith@fluendo.com>"); - -GST_DEBUG_CATEGORY_STATIC (gst_ogg_parse_debug); -#define GST_CAT_DEFAULT gst_ogg_parse_debug - -#define GST_TYPE_OGG_PARSE (gst_ogg_parse_get_type()) -#define GST_OGG_PARSE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_PARSE, GstOggParse)) -#define GST_OGG_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_PARSE, GstOggParse)) -#define GST_IS_OGG_PARSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_PARSE)) -#define GST_IS_OGG_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_PARSE)) - -static GType gst_ogg_parse_get_type (void); - -typedef struct _GstOggParse GstOggParse; -typedef struct _GstOggParseClass GstOggParseClass; - -struct _GstOggParse -{ - GstElement element; - - GstPad *sinkpad; /* Sink pad we're reading data from */ - - GstPad *srcpad; /* Source pad we're writing to */ - - GSList *oggstreams; /* list of GstOggStreams for known streams */ - - gint64 offset; /* Current stream offset */ - - gboolean in_headers; /* Set if we're reading headers for streams */ - - gboolean last_page_not_bos; /* Set if we've seen a non-BOS page */ - - ogg_sync_state sync; /* Ogg page synchronisation */ - - GstCaps *caps; /* Our src caps */ -}; - -struct _GstOggParseClass -{ - GstElementClass parent_class; -}; - -static void gst_ogg_parse_base_init (gpointer g_class); -static void gst_ogg_parse_class_init (GstOggParseClass * klass); -static void gst_ogg_parse_init (GstOggParse * ogg); -static GstElementClass *parent_class = NULL; - -static GType -gst_ogg_parse_get_type (void) -{ - static GType ogg_parse_type = 0; - - if (!ogg_parse_type) { - static const GTypeInfo ogg_parse_info = { - sizeof (GstOggParseClass), - gst_ogg_parse_base_init, - NULL, - (GClassInitFunc) gst_ogg_parse_class_init, - NULL, - NULL, - sizeof (GstOggParse), - 0, - (GInstanceInitFunc) gst_ogg_parse_init, - }; - - ogg_parse_type = g_type_register_static (GST_TYPE_ELEMENT, "GstOggParse", - &ogg_parse_info, 0); - } - return ogg_parse_type; -} - -static void -free_stream (GstOggStream * stream) -{ - g_list_foreach (stream->headers, (GFunc) gst_mini_object_unref, NULL); - g_list_foreach (stream->unknown_pages, (GFunc) gst_mini_object_unref, NULL); - - g_free (stream); -} - -static void -gst_ogg_parse_delete_all_streams (GstOggParse * ogg) -{ - g_slist_foreach (ogg->oggstreams, (GFunc) free_stream, NULL); - g_slist_free (ogg->oggstreams); - ogg->oggstreams = NULL; -} - -static GstOggStream * -gst_ogg_parse_new_stream (GstOggParse * parser, ogg_page * page) -{ - GstOggStream *stream; - ogg_packet packet; - int ret; - guint32 serialno; - - serialno = ogg_page_serialno (page); - - GST_DEBUG_OBJECT (parser, "creating new stream %08x", serialno); - - stream = g_new0 (GstOggStream, 1); - - stream->serialno = serialno; - stream->in_headers = 1; - - if (ogg_stream_init (&stream->stream, serialno) != 0) { - GST_ERROR ("Could not initialize ogg_stream struct for serial %08x.", - serialno); - return NULL; - } - - /* FIXME check return */ - ogg_stream_pagein (&stream->stream, page); - - /* FIXME check return */ - ret = ogg_stream_packetout (&stream->stream, &packet); - if (ret == 1) { - gst_ogg_stream_setup_map (stream, &packet); - } - - parser->oggstreams = g_slist_append (parser->oggstreams, stream); - - return stream; -} - -static GstOggStream * -gst_ogg_parse_find_stream (GstOggParse * parser, guint32 serialno) -{ - GSList *l; - - for (l = parser->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - - if (stream->serialno == serialno) - return stream; - } - return NULL; -} - -/* signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ - ARG_0 - /* FILL ME */ -}; - -static GstStaticPadTemplate ogg_parse_src_template_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/ogg") - ); - -static GstStaticPadTemplate ogg_parse_sink_template_factory = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("application/ogg") - ); - -static void gst_ogg_parse_dispose (GObject * object); -static GstStateChangeReturn gst_ogg_parse_change_state (GstElement * element, - GstStateChange transition); -static GstFlowReturn gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer); - -static void -gst_ogg_parse_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_details (element_class, &gst_ogg_parse_details); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&ogg_parse_sink_template_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&ogg_parse_src_template_factory)); -} - -static void -gst_ogg_parse_class_init (GstOggParseClass * klass) -{ - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - gstelement_class->change_state = gst_ogg_parse_change_state; - - gobject_class->dispose = gst_ogg_parse_dispose; -} - -static void -gst_ogg_parse_init (GstOggParse * ogg) -{ - /* create the sink and source pads */ - ogg->sinkpad = - gst_pad_new_from_static_template (&ogg_parse_sink_template_factory, - "sink"); - ogg->srcpad = - gst_pad_new_from_static_template (&ogg_parse_src_template_factory, "src"); - - /* TODO: Are there any events we must handle? */ - /* gst_pad_set_event_function (ogg->sinkpad, gst_ogg_parse_handle_event); */ - gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_parse_chain); - - gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad); - gst_element_add_pad (GST_ELEMENT (ogg), ogg->srcpad); - - ogg->oggstreams = NULL; -} - -static void -gst_ogg_parse_dispose (GObject * object) -{ - GstOggParse *ogg = GST_OGG_PARSE (object); - - GST_LOG_OBJECT (ogg, "Disposing of object %p", ogg); - - ogg_sync_clear (&ogg->sync); - gst_ogg_parse_delete_all_streams (ogg); - - if (ogg->caps) { - gst_caps_unref (ogg->caps); - ogg->caps = NULL; - } - - if (G_OBJECT_CLASS (parent_class)->dispose) - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -/* submit the given buffer to the ogg sync. - * - * Returns the number of bytes submited. - */ -static gint -gst_ogg_parse_submit_buffer (GstOggParse * ogg, GstBuffer * buffer) -{ - guint size; - guint8 *data; - gchar *oggbuffer; - - size = GST_BUFFER_SIZE (buffer); - data = GST_BUFFER_DATA (buffer); - - /* We now have a buffer, submit it to the ogg sync layer */ - oggbuffer = ogg_sync_buffer (&ogg->sync, size); - memcpy (oggbuffer, data, size); - ogg_sync_wrote (&ogg->sync, size); - - /* We've copied all the neccesary data, so we're done with the buffer */ - gst_buffer_unref (buffer); - - return size; -} - -static void -gst_ogg_parse_append_header (GValue * array, GstBuffer * buf) -{ - GValue value = { 0 }; - /* We require a copy to avoid circular refcounts */ - GstBuffer *buffer = gst_buffer_copy (buf); - - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); - - g_value_init (&value, GST_TYPE_BUFFER); - gst_value_set_buffer (&value, buffer); - gst_value_array_append_value (array, &value); - g_value_unset (&value); - -} - -typedef enum -{ - PAGE_HEADER, /* Header page */ - PAGE_DATA, /* Data page */ - PAGE_PENDING, /* We don't know yet, we'll have to see some future pages */ -} page_type; - -static page_type -gst_ogg_parse_is_header (GstOggParse * ogg, GstOggStream * stream, - ogg_page * page) -{ - ogg_int64_t gpos = ogg_page_granulepos (page); - - if (gpos < 0) - return PAGE_PENDING; - - /* This is good enough for now, but technically requires codec-specific - * behaviour to be perfect. This is where we need the mooted library for - * this stuff, which nobody has written. - */ - if (gpos > 0) - return PAGE_DATA; - else - return PAGE_HEADER; -} - -static GstBuffer * -gst_ogg_parse_buffer_from_page (ogg_page * page, - guint64 offset, gboolean delta, GstClockTime timestamp) -{ - int size = page->header_len + page->body_len; - GstBuffer *buf = gst_buffer_new_and_alloc (size); - - memcpy (GST_BUFFER_DATA (buf), page->header, page->header_len); - memcpy (GST_BUFFER_DATA (buf) + page->header_len, page->body, page->body_len); - - GST_BUFFER_TIMESTAMP (buf) = timestamp; - GST_BUFFER_OFFSET (buf) = offset; - GST_BUFFER_OFFSET_END (buf) = offset + size; - - return buf; -} - - -/* Reads in buffers, parses them, reframes into one-buffer-per-ogg-page, submits - * pages to output pad. - */ -static GstFlowReturn -gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) -{ - GstOggParse *ogg; - GstFlowReturn result = GST_FLOW_OK; - gint ret = -1; - guint32 serialno; - GstBuffer *pagebuffer; - GstClockTime buffertimestamp = GST_BUFFER_TIMESTAMP (buffer); - - ogg = GST_OGG_PARSE (GST_OBJECT_PARENT (pad)); - - GST_LOG_OBJECT (ogg, "Chain function received buffer of size %d", - GST_BUFFER_SIZE (buffer)); - - gst_ogg_parse_submit_buffer (ogg, buffer); - - while (ret != 0 && result == GST_FLOW_OK) { - ogg_page page; - - /* We use ogg_sync_pageseek() rather than ogg_sync_pageout() so that we can - * track how many bytes the ogg layer discarded (in the case of sync errors, - * etc.); this allows us to accurately track the current stream offset - */ - ret = ogg_sync_pageseek (&ogg->sync, &page); - if (ret == 0) { - /* need more data, that's fine... */ - break; - } else if (ret < 0) { - /* discontinuity; track how many bytes we skipped (-ret) */ - ogg->offset -= ret; - } else { - gint64 granule = ogg_page_granulepos (&page); -#ifndef GST_DISABLE_GST_DEBUG - int bos = ogg_page_bos (&page); -#endif - guint64 startoffset = ogg->offset; - GstOggStream *stream; - - serialno = ogg_page_serialno (&page); - stream = gst_ogg_parse_find_stream (ogg, serialno); - - GST_LOG_OBJECT (ogg, "Timestamping outgoing buffer as %" GST_TIME_FORMAT, - GST_TIME_ARGS (buffertimestamp)); - - buffertimestamp = gst_ogg_stream_get_end_time_for_granulepos (stream, - granule); - pagebuffer = gst_ogg_parse_buffer_from_page (&page, startoffset, FALSE, - buffertimestamp); - - /* We read out 'ret' bytes, so we set the next offset appropriately */ - ogg->offset += ret; - - GST_LOG_OBJECT (ogg, - "processing ogg page (serial %08x, pageno %ld, " - "granule pos %" G_GUINT64_FORMAT ", bos %d, offset %" - G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ")", - serialno, ogg_page_pageno (&page), - granule, bos, startoffset, ogg->offset); - - if (ogg_page_bos (&page)) { - /* If we've seen this serialno before, this is technically an error, - * we log this case but accept it - this one replaces the previous - * stream with this serialno. We can do this since we're streaming, and - * not supporting seeking... - */ - GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno); - - if (stream != NULL) { - GST_LOG_OBJECT (ogg, "Incorrect stream; repeats serial number %u " - "at offset %" G_GINT64_FORMAT, serialno, ogg->offset); - } - - if (ogg->last_page_not_bos) { - GST_LOG_OBJECT (ogg, "Deleting all referenced streams, found a new " - "chain starting with serial %u", serialno); - gst_ogg_parse_delete_all_streams (ogg); - } - - stream = gst_ogg_parse_new_stream (ogg, &page); - - ogg->last_page_not_bos = FALSE; - - gst_buffer_ref (pagebuffer); - stream->headers = g_list_append (stream->headers, pagebuffer); - - if (!ogg->in_headers) { - GST_LOG_OBJECT (ogg, - "Found start of new chain at offset %" G_GUINT64_FORMAT, - startoffset); - ogg->in_headers = 1; - } - - /* For now, we just keep the header buffer in the stream->headers list; - * it actually gets output once we've collected the entire set - */ - } else { - /* Non-BOS page. Either: we're outside headers, and this isn't a - * header (normal data), outside headers and this is (error!), inside - * headers, this is (append header), or inside headers and this isn't - * (we've found the end of headers; flush the lot!) - * - * Before that, we flag that the last page seen (this one) was not a - * BOS page; that way we know that when we next see a BOS page it's a - * new chain, and we can flush all existing streams. - */ - page_type type; - GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno); - - if (!stream) { - GST_LOG_OBJECT (ogg, - "Non-BOS page unexpectedly found at %" G_GINT64_FORMAT, - ogg->offset); - goto failure; - } - - ogg->last_page_not_bos = TRUE; - - type = gst_ogg_parse_is_header (ogg, stream, &page); - - if (type == PAGE_PENDING && ogg->in_headers) { - gst_buffer_ref (pagebuffer); - - stream->unknown_pages = g_list_append (stream->unknown_pages, - pagebuffer); - } else if (type == PAGE_HEADER) { - if (!ogg->in_headers) { - GST_LOG_OBJECT (ogg, "Header page unexpectedly found outside " - "headers at offset %" G_GINT64_FORMAT, ogg->offset); - goto failure; - } else { - /* Append the header to the buffer list, after any unknown previous - * pages - */ - stream->headers = g_list_concat (stream->headers, - stream->unknown_pages); - g_list_free (stream->unknown_pages); - gst_buffer_ref (pagebuffer); - stream->headers = g_list_append (stream->headers, pagebuffer); - } - } else { /* PAGE_DATA, or PAGE_PENDING but outside headers */ - if (ogg->in_headers) { - /* First non-header page... set caps, flush headers. - * - * First up, we build a single GValue list of all the pagebuffers - * we're using for the headers, in order. - * Then we set this on the caps structure. Then we can start pushing - * buffers for the headers, and finally we send this non-header - * page. - */ - GstCaps *caps; - GstStructure *structure; - GValue array = { 0 }; - gint count = 0; - gboolean found_pending_headers = FALSE; - GSList *l; - - g_value_init (&array, GST_TYPE_ARRAY); - - for (l = ogg->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - - if (g_list_length (stream->headers) == 0) { - GST_LOG_OBJECT (ogg, "No primary header found for stream %08lx", - stream->serialno); - goto failure; - } - - gst_ogg_parse_append_header (&array, - GST_BUFFER (stream->headers->data)); - count++; - } - - for (l = ogg->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - int j; - - for (j = 1; j < g_list_length (stream->headers); j++) { - gst_ogg_parse_append_header (&array, - GST_BUFFER (g_list_nth_data (stream->headers, j))); - count++; - } - } - - caps = gst_pad_get_caps (ogg->srcpad); - caps = gst_caps_make_writable (caps); - - structure = gst_caps_get_structure (caps, 0); - gst_structure_set_value (structure, "streamheader", &array); - - gst_pad_set_caps (ogg->srcpad, caps); - - g_value_unset (&array); - - if (ogg->caps) - gst_caps_unref (ogg->caps); - ogg->caps = caps; - - GST_LOG_OBJECT (ogg, "Set \"streamheader\" caps with %d buffers " - "(one per page)", count); - - /* Now, we do the same thing, but push buffers... */ - for (l = ogg->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - GstBuffer *buf = GST_BUFFER (stream->headers->data); - - gst_buffer_set_caps (buf, caps); - - result = gst_pad_push (ogg->srcpad, buf); - if (result != GST_FLOW_OK) - return result; - } - for (l = ogg->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - int j; - - for (j = 1; j < g_list_length (stream->headers); j++) { - GstBuffer *buf = - GST_BUFFER (g_list_nth_data (stream->headers, j)); - gst_buffer_set_caps (buf, caps); - - result = gst_pad_push (ogg->srcpad, buf); - if (result != GST_FLOW_OK) - return result; - } - } - - ogg->in_headers = 0; - - /* And finally the pending data pages */ - for (l = ogg->oggstreams; l != NULL; l = l->next) { - GstOggStream *stream = (GstOggStream *) l->data; - GList *k; - - if (stream->unknown_pages == NULL) - continue; - - if (found_pending_headers) { - GST_WARNING_OBJECT (ogg, "Incorrectly muxed headers found at " - "approximate offset %" G_GINT64_FORMAT, ogg->offset); - } - found_pending_headers = TRUE; - - GST_LOG_OBJECT (ogg, "Pushing %d pending pages after headers", - g_list_length (stream->unknown_pages) + 1); - - for (k = stream->unknown_pages; k != NULL; k = k->next) { - GstBuffer *buf; - - buf = GST_BUFFER (k->data); - gst_buffer_set_caps (buf, caps); - result = gst_pad_push (ogg->srcpad, buf); - if (result != GST_FLOW_OK) - return result; - } - g_list_foreach (stream->unknown_pages, - (GFunc) gst_mini_object_unref, NULL); - g_list_free (stream->unknown_pages); - stream->unknown_pages = NULL; - } - - gst_buffer_set_caps (pagebuffer, caps); - - result = gst_pad_push (ogg->srcpad, GST_BUFFER (pagebuffer)); - if (result != GST_FLOW_OK) - return result; - } else { - /* Normal data page, submit buffer */ - gst_buffer_set_caps (pagebuffer, ogg->caps); - result = gst_pad_push (ogg->srcpad, GST_BUFFER (pagebuffer)); - if (result != GST_FLOW_OK) - return result; - } - } - } - } - } - - return result; - -failure: - gst_pad_push_event (GST_PAD (ogg->srcpad), gst_event_new_eos ()); - return GST_FLOW_ERROR; -} - -static GstStateChangeReturn -gst_ogg_parse_change_state (GstElement * element, GstStateChange transition) -{ - GstOggParse *ogg; - GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE; - - ogg = GST_OGG_PARSE (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - ogg_sync_init (&ogg->sync); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - ogg_sync_reset (&ogg->sync); - 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: - break; - case GST_STATE_CHANGE_READY_TO_NULL: - ogg_sync_clear (&ogg->sync); - break; - default: - break; - } - return result; -} - -gboolean -gst_ogg_parse_plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_ogg_parse_debug, "oggparse", 0, "ogg parser"); - - return gst_element_register (plugin, "oggparse", GST_RANK_NONE, - GST_TYPE_OGG_PARSE); -} |