gst_plugins_base/ext/ogg/gstoggparse.c
branchRCL_3
changeset 29 567bb019e3e3
parent 6 9b2c3c7a1a9c
child 30 7e817e7e631c
equal deleted inserted replaced
6:9b2c3c7a1a9c 29:567bb019e3e3
     1 /* GStreamer
       
     2  * Copyright (C) 2005 Michael Smith <msmith@fluendo.com>
       
     3  *
       
     4  * gstoggparse.c: ogg stream parser
       
     5  *
       
     6  * This library is free software; you can redistribute it and/or
       
     7  * modify it under the terms of the GNU Library General Public
       
     8  * License as published by the Free Software Foundation; either
       
     9  * version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This library is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * Library General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU Library General Public
       
    17  * License along with this library; if not, write to the
       
    18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    19  * Boston, MA 02111-1307, USA.
       
    20  */
       
    21 
       
    22 /* This ogg parser is essentially a subset of the ogg demuxer - rather than
       
    23  * fully demuxing into packets, we only parse out the pages, create one
       
    24  * GstBuffer per page, set all the appropriate flags on those pages, set caps
       
    25  * appropriately (particularly the 'streamheader' which gives all the header
       
    26  * pages required for initialing decode).
       
    27  *
       
    28  * It's dramatically simpler than the full demuxer as it does not  support 
       
    29  * seeking.
       
    30  */
       
    31 
       
    32 #ifdef HAVE_CONFIG_H
       
    33 #include "config.h"
       
    34 #endif
       
    35 #include <gst/gst.h>
       
    36 #include <ogg/ogg.h>
       
    37 #include <string.h>
       
    38 
       
    39 static const GstElementDetails gst_ogg_parse_details =
       
    40 GST_ELEMENT_DETAILS ("Ogg parser",
       
    41     "Codec/Parser",
       
    42     "parse ogg streams into pages (info about ogg: http://xiph.org)",
       
    43     "Michael Smith <msmith@fluendo.com>");
       
    44 
       
    45 GST_DEBUG_CATEGORY_STATIC (gst_ogg_parse_debug);
       
    46 #define GST_CAT_DEFAULT gst_ogg_parse_debug
       
    47 
       
    48 #define GST_TYPE_OGG_PARSE (gst_ogg_parse_get_type())
       
    49 #define GST_OGG_PARSE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_PARSE, GstOggParse))
       
    50 #define GST_OGG_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_PARSE, GstOggParse))
       
    51 #define GST_IS_OGG_PARSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_PARSE))
       
    52 #define GST_IS_OGG_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_PARSE))
       
    53 
       
    54 static GType gst_ogg_parse_get_type (void);
       
    55 
       
    56 typedef struct _GstOggParse GstOggParse;
       
    57 typedef struct _GstOggParseClass GstOggParseClass;
       
    58 
       
    59 /* Each ogg logical stream has a GstOggStream associated with it */
       
    60 typedef struct
       
    61 {
       
    62   /*ogg_stream_state stream; *//* We need this to get the packets out in order
       
    63      to do codec identification, for various
       
    64      codec-specific tasks */
       
    65 
       
    66   gboolean in_headers;          /* Initially true, false once we've read all the
       
    67                                    headers for this logical stream */
       
    68 
       
    69   guint32 serialno;             /* Unique serial number of this stream */
       
    70 
       
    71   GSList *headers;              /* List of ogg pages that we'll set on caps */
       
    72   GSList *unknown_pages;        /* List of pages we haven't yet classified */
       
    73 } GstOggStream;
       
    74 
       
    75 struct _GstOggParse
       
    76 {
       
    77   GstElement element;
       
    78 
       
    79   GstPad *sinkpad;              /* Sink pad we're reading data from */
       
    80 
       
    81   GstPad *srcpad;               /* Source pad we're writing to */
       
    82 
       
    83   GSList *oggstreams;           /* list of GstOggStreams for known streams */
       
    84 
       
    85   gint64 offset;                /* Current stream offset */
       
    86 
       
    87   gboolean in_headers;          /* Set if we're reading headers for streams */
       
    88 
       
    89   gboolean last_page_not_bos;   /* Set if we've seen a non-BOS page */
       
    90 
       
    91   ogg_sync_state sync;          /* Ogg page synchronisation */
       
    92 
       
    93   GstCaps *caps;                /* Our src caps */
       
    94 };
       
    95 
       
    96 struct _GstOggParseClass
       
    97 {
       
    98   GstElementClass parent_class;
       
    99 };
       
   100 
       
   101 static void gst_ogg_parse_base_init (gpointer g_class);
       
   102 static void gst_ogg_parse_class_init (GstOggParseClass * klass);
       
   103 static void gst_ogg_parse_init (GstOggParse * ogg);
       
   104 static GstElementClass *parent_class = NULL;
       
   105 
       
   106 static GType
       
   107 gst_ogg_parse_get_type (void)
       
   108 {
       
   109   static GType ogg_parse_type = 0;
       
   110 
       
   111   if (!ogg_parse_type) {
       
   112     static const GTypeInfo ogg_parse_info = {
       
   113       sizeof (GstOggParseClass),
       
   114       gst_ogg_parse_base_init,
       
   115       NULL,
       
   116       (GClassInitFunc) gst_ogg_parse_class_init,
       
   117       NULL,
       
   118       NULL,
       
   119       sizeof (GstOggParse),
       
   120       0,
       
   121       (GInstanceInitFunc) gst_ogg_parse_init,
       
   122     };
       
   123 
       
   124     ogg_parse_type = g_type_register_static (GST_TYPE_ELEMENT, "GstOggParse",
       
   125         &ogg_parse_info, 0);
       
   126   }
       
   127   return ogg_parse_type;
       
   128 }
       
   129 
       
   130 static void
       
   131 free_stream (GstOggStream * stream)
       
   132 {
       
   133   g_slist_foreach (stream->headers, (GFunc) gst_mini_object_unref, NULL);
       
   134   g_slist_foreach (stream->unknown_pages, (GFunc) gst_mini_object_unref, NULL);
       
   135 
       
   136   g_free (stream);
       
   137 }
       
   138 
       
   139 static void
       
   140 gst_ogg_parse_delete_all_streams (GstOggParse * ogg)
       
   141 {
       
   142   g_slist_foreach (ogg->oggstreams, (GFunc) free_stream, NULL);
       
   143   g_slist_free (ogg->oggstreams);
       
   144   ogg->oggstreams = NULL;
       
   145 }
       
   146 
       
   147 static GstOggStream *
       
   148 gst_ogg_parse_new_stream (GstOggParse * parser, guint32 serialno)
       
   149 {
       
   150   GstOggStream *ret;
       
   151 
       
   152   GST_DEBUG_OBJECT (parser, "creating new stream %08x", serialno);
       
   153 
       
   154   ret = g_new0 (GstOggStream, 1);
       
   155 
       
   156   ret->serialno = serialno;
       
   157   ret->in_headers = 1;
       
   158 
       
   159   /*
       
   160      if (ogg_stream_init (&ret->stream, serialno) != 0) {
       
   161      GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
       
   162      serialno);
       
   163      return NULL;
       
   164      }
       
   165    */
       
   166 
       
   167   parser->oggstreams = g_slist_append (parser->oggstreams, ret);
       
   168 
       
   169   return ret;
       
   170 }
       
   171 
       
   172 static GstOggStream *
       
   173 gst_ogg_parse_find_stream (GstOggParse * parser, guint32 serialno)
       
   174 {
       
   175   GSList *l;
       
   176 
       
   177   for (l = parser->oggstreams; l != NULL; l = l->next) {
       
   178     GstOggStream *stream = (GstOggStream *) l->data;
       
   179 
       
   180     if (stream->serialno == serialno)
       
   181       return stream;
       
   182   }
       
   183   return NULL;
       
   184 }
       
   185 
       
   186 /* signals and args */
       
   187 enum
       
   188 {
       
   189   /* FILL ME */
       
   190   LAST_SIGNAL
       
   191 };
       
   192 
       
   193 enum
       
   194 {
       
   195   ARG_0
       
   196       /* FILL ME */
       
   197 };
       
   198 
       
   199 static GstStaticPadTemplate ogg_parse_src_template_factory =
       
   200 GST_STATIC_PAD_TEMPLATE ("src",
       
   201     GST_PAD_SRC,
       
   202     GST_PAD_ALWAYS,
       
   203     GST_STATIC_CAPS ("application/ogg")
       
   204     );
       
   205 
       
   206 static GstStaticPadTemplate ogg_parse_sink_template_factory =
       
   207 GST_STATIC_PAD_TEMPLATE ("sink",
       
   208     GST_PAD_SINK,
       
   209     GST_PAD_ALWAYS,
       
   210     GST_STATIC_CAPS ("application/ogg")
       
   211     );
       
   212 
       
   213 static void gst_ogg_parse_dispose (GObject * object);
       
   214 static GstStateChangeReturn gst_ogg_parse_change_state (GstElement * element,
       
   215     GstStateChange transition);
       
   216 static GstFlowReturn gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer);
       
   217 
       
   218 static void
       
   219 gst_ogg_parse_base_init (gpointer g_class)
       
   220 {
       
   221   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   222 
       
   223   gst_element_class_set_details (element_class, &gst_ogg_parse_details);
       
   224 
       
   225   gst_element_class_add_pad_template (element_class,
       
   226       gst_static_pad_template_get (&ogg_parse_sink_template_factory));
       
   227   gst_element_class_add_pad_template (element_class,
       
   228       gst_static_pad_template_get (&ogg_parse_src_template_factory));
       
   229 }
       
   230 
       
   231 static void
       
   232 gst_ogg_parse_class_init (GstOggParseClass * klass)
       
   233 {
       
   234   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
       
   235   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
       
   236 
       
   237   parent_class = g_type_class_peek_parent (klass);
       
   238 
       
   239   gstelement_class->change_state = gst_ogg_parse_change_state;
       
   240 
       
   241   gobject_class->dispose = gst_ogg_parse_dispose;
       
   242 }
       
   243 
       
   244 static void
       
   245 gst_ogg_parse_init (GstOggParse * ogg)
       
   246 {
       
   247   /* create the sink and source pads */
       
   248   ogg->sinkpad =
       
   249       gst_pad_new_from_static_template (&ogg_parse_sink_template_factory,
       
   250       "sink");
       
   251   ogg->srcpad =
       
   252       gst_pad_new_from_static_template (&ogg_parse_src_template_factory, "src");
       
   253 
       
   254   /* TODO: Are there any events we must handle? */
       
   255   /* gst_pad_set_event_function (ogg->sinkpad, gst_ogg_parse_handle_event); */
       
   256   gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_parse_chain);
       
   257 
       
   258   gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
       
   259   gst_element_add_pad (GST_ELEMENT (ogg), ogg->srcpad);
       
   260 
       
   261   ogg->oggstreams = NULL;
       
   262 }
       
   263 
       
   264 static void
       
   265 gst_ogg_parse_dispose (GObject * object)
       
   266 {
       
   267   GstOggParse *ogg = GST_OGG_PARSE (object);
       
   268 
       
   269   GST_LOG_OBJECT (ogg, "Disposing of object %p", ogg);
       
   270 
       
   271   ogg_sync_clear (&ogg->sync);
       
   272   gst_ogg_parse_delete_all_streams (ogg);
       
   273 
       
   274   if (ogg->caps) {
       
   275     gst_caps_unref (ogg->caps);
       
   276     ogg->caps = NULL;
       
   277   }
       
   278 
       
   279   if (G_OBJECT_CLASS (parent_class)->dispose)
       
   280     G_OBJECT_CLASS (parent_class)->dispose (object);
       
   281 }
       
   282 
       
   283 /* submit the given buffer to the ogg sync.
       
   284  *
       
   285  * Returns the number of bytes submited.
       
   286  */
       
   287 static gint
       
   288 gst_ogg_parse_submit_buffer (GstOggParse * ogg, GstBuffer * buffer)
       
   289 {
       
   290   guint size;
       
   291   guint8 *data;
       
   292   gchar *oggbuffer;
       
   293 
       
   294   size = GST_BUFFER_SIZE (buffer);
       
   295   data = GST_BUFFER_DATA (buffer);
       
   296 
       
   297   /* We now have a buffer, submit it to the ogg sync layer */
       
   298   oggbuffer = ogg_sync_buffer (&ogg->sync, size);
       
   299   memcpy (oggbuffer, data, size);
       
   300   ogg_sync_wrote (&ogg->sync, size);
       
   301 
       
   302   /* We've copied all the neccesary data, so we're done with the buffer */
       
   303   gst_buffer_unref (buffer);
       
   304 
       
   305   return size;
       
   306 }
       
   307 
       
   308 static void
       
   309 gst_ogg_parse_append_header (GValue * array, GstBuffer * buf)
       
   310 {
       
   311   GValue value = { 0 };
       
   312   /* We require a copy to avoid circular refcounts */
       
   313   GstBuffer *buffer = gst_buffer_copy (buf);
       
   314 
       
   315   GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
       
   316 
       
   317   g_value_init (&value, GST_TYPE_BUFFER);
       
   318   gst_value_set_buffer (&value, buffer);
       
   319   gst_value_array_append_value (array, &value);
       
   320   g_value_unset (&value);
       
   321 
       
   322 }
       
   323 
       
   324 typedef enum
       
   325 {
       
   326   PAGE_HEADER,                  /* Header page */
       
   327   PAGE_DATA,                    /* Data page */
       
   328   PAGE_PENDING,                 /* We don't know yet, we'll have to see some future pages */
       
   329 } page_type;
       
   330 
       
   331 static page_type
       
   332 gst_ogg_parse_is_header (GstOggParse * ogg, GstOggStream * stream,
       
   333     ogg_page * page)
       
   334 {
       
   335   ogg_int64_t gpos = ogg_page_granulepos (page);
       
   336 
       
   337   if (gpos < 0)
       
   338     return PAGE_PENDING;
       
   339 
       
   340   /* This is good enough for now, but technically requires codec-specific
       
   341    * behaviour to be perfect. This is where we need the mooted library for 
       
   342    * this stuff, which nobody has written.
       
   343    */
       
   344   if (gpos > 0)
       
   345     return PAGE_DATA;
       
   346   else
       
   347     return PAGE_HEADER;
       
   348 }
       
   349 
       
   350 static GstBuffer *
       
   351 gst_ogg_parse_buffer_from_page (ogg_page * page,
       
   352     guint64 offset, gboolean delta, GstClockTime timestamp)
       
   353 {
       
   354   int size = page->header_len + page->body_len;
       
   355   GstBuffer *buf = gst_buffer_new_and_alloc (size);
       
   356 
       
   357   memcpy (GST_BUFFER_DATA (buf), page->header, page->header_len);
       
   358   memcpy (GST_BUFFER_DATA (buf) + page->header_len, page->body, page->body_len);
       
   359 
       
   360   GST_BUFFER_TIMESTAMP (buf) = timestamp;
       
   361   GST_BUFFER_OFFSET (buf) = offset;
       
   362   GST_BUFFER_OFFSET_END (buf) = offset + size;
       
   363 
       
   364   return buf;
       
   365 }
       
   366 
       
   367 
       
   368 /* Reads in buffers, parses them, reframes into one-buffer-per-ogg-page, submits
       
   369  * pages to output pad.
       
   370  */
       
   371 static GstFlowReturn
       
   372 gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer)
       
   373 {
       
   374   GstOggParse *ogg;
       
   375   GstFlowReturn result = GST_FLOW_OK;
       
   376   gint ret = -1;
       
   377   guint32 serialno;
       
   378   GstBuffer *pagebuffer;
       
   379   GstClockTime buffertimestamp = GST_BUFFER_TIMESTAMP (buffer);
       
   380 
       
   381   ogg = GST_OGG_PARSE (GST_OBJECT_PARENT (pad));
       
   382 
       
   383   GST_LOG_OBJECT (ogg, "Chain function received buffer of size %d",
       
   384       GST_BUFFER_SIZE (buffer));
       
   385 
       
   386   gst_ogg_parse_submit_buffer (ogg, buffer);
       
   387 
       
   388   while (ret != 0 && result == GST_FLOW_OK) {
       
   389     ogg_page page;
       
   390 
       
   391     /* We use ogg_sync_pageseek() rather than ogg_sync_pageout() so that we can
       
   392      * track how many bytes the ogg layer discarded (in the case of sync errors,
       
   393      * etc.); this allows us to accurately track the current stream offset
       
   394      */
       
   395     ret = ogg_sync_pageseek (&ogg->sync, &page);
       
   396     if (ret == 0) {
       
   397       /* need more data, that's fine... */
       
   398       break;
       
   399     } else if (ret < 0) {
       
   400       /* discontinuity; track how many bytes we skipped (-ret) */
       
   401       ogg->offset -= ret;
       
   402     } else {
       
   403 #ifndef GST_DISABLE_GST_DEBUG
       
   404       gint64 granule = ogg_page_granulepos (&page);
       
   405       int bos = ogg_page_bos (&page);
       
   406 #endif
       
   407       guint64 startoffset = ogg->offset;
       
   408 
       
   409       GST_LOG_OBJECT (ogg, "Timestamping outgoing buffer as %" GST_TIME_FORMAT,
       
   410           GST_TIME_ARGS (buffertimestamp));
       
   411       /* Turn our page into a GstBuffer TODO: better timestamps? Requires format
       
   412        * parsing. */
       
   413       pagebuffer = gst_ogg_parse_buffer_from_page (&page, startoffset, FALSE,
       
   414           buffertimestamp);
       
   415 
       
   416       /* We read out 'ret' bytes, so we set the next offset appropriately */
       
   417       ogg->offset += ret;
       
   418 
       
   419       serialno = ogg_page_serialno (&page);
       
   420 
       
   421       GST_LOG_OBJECT (ogg,
       
   422           "processing ogg page (serial %08x, pageno %ld, "
       
   423           "granule pos %" G_GUINT64_FORMAT ", bos %d, offset %"
       
   424           G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ")",
       
   425           serialno, ogg_page_pageno (&page),
       
   426           granule, bos, startoffset, ogg->offset);
       
   427 
       
   428       if (ogg_page_bos (&page)) {
       
   429         /* If we've seen this serialno before, this is technically an error,
       
   430          * we log this case but accept it - this one replaces the previous
       
   431          * stream with this serialno. We can do this since we're streaming, and
       
   432          * not supporting seeking...
       
   433          */
       
   434         GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno);
       
   435 
       
   436         if (stream != NULL) {
       
   437           GST_LOG_OBJECT (ogg, "Incorrect stream; repeats serial number %u "
       
   438               "at offset %lld", serialno, ogg->offset);
       
   439         }
       
   440 
       
   441         if (ogg->last_page_not_bos) {
       
   442           GST_LOG_OBJECT (ogg, "Deleting all referenced streams, found a new "
       
   443               "chain starting with serial %u", serialno);
       
   444           gst_ogg_parse_delete_all_streams (ogg);
       
   445         }
       
   446 
       
   447         stream = gst_ogg_parse_new_stream (ogg, serialno);
       
   448 
       
   449         ogg->last_page_not_bos = FALSE;
       
   450 
       
   451         gst_buffer_ref (pagebuffer);
       
   452         stream->headers = g_slist_append (stream->headers, pagebuffer);
       
   453 
       
   454         if (!ogg->in_headers) {
       
   455           GST_LOG_OBJECT (ogg, "Found start of new chain at offset %llu",
       
   456               startoffset);
       
   457           ogg->in_headers = 1;
       
   458         }
       
   459 
       
   460         /* For now, we just keep the header buffer in the stream->headers list;
       
   461          * it actually gets output once we've collected the entire set
       
   462          */
       
   463       } else {
       
   464         /* Non-BOS page. Either: we're outside headers, and this isn't a 
       
   465          * header (normal data), outside headers and this is (error!), inside
       
   466          * headers, this is (append header), or inside headers and this isn't 
       
   467          * (we've found the end of headers; flush the lot!)
       
   468          *
       
   469          * Before that, we flag that the last page seen (this one) was not a 
       
   470          * BOS page; that way we know that when we next see a BOS page it's a
       
   471          * new chain, and we can flush all existing streams.
       
   472          */
       
   473         page_type type;
       
   474         GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno);
       
   475 
       
   476         if (!stream) {
       
   477           GST_LOG_OBJECT (ogg, "Non-BOS page unexpectedly found at %lld",
       
   478               ogg->offset);
       
   479           goto failure;
       
   480         }
       
   481 
       
   482         ogg->last_page_not_bos = TRUE;
       
   483 
       
   484         type = gst_ogg_parse_is_header (ogg, stream, &page);
       
   485 
       
   486         if (type == PAGE_PENDING && ogg->in_headers) {
       
   487           gst_buffer_ref (pagebuffer);
       
   488 
       
   489           stream->unknown_pages = g_slist_append (stream->unknown_pages,
       
   490               pagebuffer);
       
   491         } else if (type == PAGE_HEADER) {
       
   492           if (!ogg->in_headers) {
       
   493             GST_LOG_OBJECT (ogg, "Header page unexpectedly found outside "
       
   494                 "headers at offset %lld", ogg->offset);
       
   495             goto failure;
       
   496           } else {
       
   497             /* Append the header to the buffer list, after any unknown previous
       
   498              * pages
       
   499              */
       
   500             stream->headers = g_slist_concat (stream->headers,
       
   501                 stream->unknown_pages);
       
   502             g_slist_free (stream->unknown_pages);
       
   503             gst_buffer_ref (pagebuffer);
       
   504             stream->headers = g_slist_append (stream->headers, pagebuffer);
       
   505           }
       
   506         } else {                /* PAGE_DATA, or PAGE_PENDING but outside headers */
       
   507           if (ogg->in_headers) {
       
   508             /* First non-header page... set caps, flush headers.
       
   509              *
       
   510              * First up, we build a single GValue list of all the pagebuffers
       
   511              * we're using for the headers, in order.
       
   512              * Then we set this on the caps structure. Then we can start pushing
       
   513              * buffers for the headers, and finally we send this non-header
       
   514              * page.
       
   515              */
       
   516             GstCaps *caps;
       
   517             GstStructure *structure;
       
   518             GValue array = { 0 };
       
   519             gint count = 0;
       
   520             gboolean found_pending_headers = FALSE;
       
   521             GSList *l;
       
   522 
       
   523             g_value_init (&array, GST_TYPE_ARRAY);
       
   524 
       
   525             for (l = ogg->oggstreams; l != NULL; l = l->next) {
       
   526               GstOggStream *stream = (GstOggStream *) l->data;
       
   527 
       
   528               if (g_slist_length (stream->headers) == 0) {
       
   529                 GST_LOG_OBJECT (ogg, "No primary header found for stream %u",
       
   530                     stream->serialno);
       
   531                 goto failure;
       
   532               }
       
   533 
       
   534               gst_ogg_parse_append_header (&array,
       
   535                   GST_BUFFER (stream->headers->data));
       
   536               count++;
       
   537             }
       
   538 
       
   539             for (l = ogg->oggstreams; l != NULL; l = l->next) {
       
   540               GstOggStream *stream = (GstOggStream *) l->data;
       
   541               int j;
       
   542 
       
   543               for (j = 1; j < g_slist_length (stream->headers); j++) {
       
   544                 gst_ogg_parse_append_header (&array,
       
   545                     GST_BUFFER (g_slist_nth_data (stream->headers, j)));
       
   546                 count++;
       
   547               }
       
   548             }
       
   549 
       
   550             caps = gst_pad_get_caps (ogg->srcpad);
       
   551             caps = gst_caps_make_writable (caps);
       
   552 
       
   553             structure = gst_caps_get_structure (caps, 0);
       
   554             gst_structure_set_value (structure, "streamheader", &array);
       
   555 
       
   556             gst_pad_set_caps (ogg->srcpad, caps);
       
   557 
       
   558             g_value_unset (&array);
       
   559 
       
   560             if (ogg->caps)
       
   561               gst_caps_unref (ogg->caps);
       
   562             ogg->caps = caps;
       
   563 
       
   564             GST_LOG_OBJECT (ogg, "Set \"streamheader\" caps with %d buffers "
       
   565                 "(one per page)", count);
       
   566 
       
   567             /* Now, we do the same thing, but push buffers... */
       
   568             for (l = ogg->oggstreams; l != NULL; l = l->next) {
       
   569               GstOggStream *stream = (GstOggStream *) l->data;
       
   570               GstBuffer *buf = GST_BUFFER (stream->headers->data);
       
   571 
       
   572               gst_buffer_set_caps (buf, caps);
       
   573 
       
   574               result = gst_pad_push (ogg->srcpad, buf);
       
   575               if (result != GST_FLOW_OK)
       
   576                 return result;
       
   577             }
       
   578             for (l = ogg->oggstreams; l != NULL; l = l->next) {
       
   579               GstOggStream *stream = (GstOggStream *) l->data;
       
   580               int j;
       
   581 
       
   582               for (j = 1; j < g_slist_length (stream->headers); j++) {
       
   583                 GstBuffer *buf =
       
   584                     GST_BUFFER (g_slist_nth_data (stream->headers, j));
       
   585                 gst_buffer_set_caps (buf, caps);
       
   586 
       
   587                 result = gst_pad_push (ogg->srcpad, buf);
       
   588                 if (result != GST_FLOW_OK)
       
   589                   return result;
       
   590               }
       
   591             }
       
   592 
       
   593             ogg->in_headers = 0;
       
   594 
       
   595             /* And finally the pending data pages */
       
   596             for (l = ogg->oggstreams; l != NULL; l = l->next) {
       
   597               GstOggStream *stream = (GstOggStream *) l->data;
       
   598               GSList *k;
       
   599 
       
   600               if (stream->unknown_pages == NULL)
       
   601                 continue;
       
   602 
       
   603               if (found_pending_headers) {
       
   604                 GST_WARNING_OBJECT (ogg, "Incorrectly muxed headers found at "
       
   605                     "approximate offset %lld", ogg->offset);
       
   606               }
       
   607               found_pending_headers = TRUE;
       
   608 
       
   609               GST_LOG_OBJECT (ogg, "Pushing %d pending pages after headers",
       
   610                   g_slist_length (stream->unknown_pages) + 1);
       
   611 
       
   612               for (k = stream->unknown_pages; k != NULL; k = k->next) {
       
   613                 GstBuffer *buf;
       
   614 
       
   615                 buf = GST_BUFFER (k->data);
       
   616                 gst_buffer_set_caps (buf, caps);
       
   617                 result = gst_pad_push (ogg->srcpad, buf);
       
   618                 if (result != GST_FLOW_OK)
       
   619                   return result;
       
   620               }
       
   621               g_slist_foreach (stream->unknown_pages,
       
   622                   (GFunc) gst_mini_object_unref, NULL);
       
   623               g_slist_free (stream->unknown_pages);
       
   624               stream->unknown_pages = NULL;
       
   625             }
       
   626 
       
   627             gst_buffer_set_caps (pagebuffer, caps);
       
   628 
       
   629             result = gst_pad_push (ogg->srcpad, GST_BUFFER (pagebuffer));
       
   630             if (result != GST_FLOW_OK)
       
   631               return result;
       
   632           } else {
       
   633             /* Normal data page, submit buffer */
       
   634             gst_buffer_set_caps (pagebuffer, ogg->caps);
       
   635             result = gst_pad_push (ogg->srcpad, GST_BUFFER (pagebuffer));
       
   636             if (result != GST_FLOW_OK)
       
   637               return result;
       
   638           }
       
   639         }
       
   640       }
       
   641     }
       
   642   }
       
   643 
       
   644   return result;
       
   645 
       
   646 failure:
       
   647   gst_pad_push_event (GST_PAD (ogg->srcpad), gst_event_new_eos ());
       
   648   return GST_FLOW_ERROR;
       
   649 }
       
   650 
       
   651 static GstStateChangeReturn
       
   652 gst_ogg_parse_change_state (GstElement * element, GstStateChange transition)
       
   653 {
       
   654   GstOggParse *ogg;
       
   655   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
       
   656 
       
   657   ogg = GST_OGG_PARSE (element);
       
   658 
       
   659   switch (transition) {
       
   660     case GST_STATE_CHANGE_NULL_TO_READY:
       
   661       ogg_sync_init (&ogg->sync);
       
   662       break;
       
   663     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
   664       ogg_sync_reset (&ogg->sync);
       
   665       break;
       
   666     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
       
   667       break;
       
   668     default:
       
   669       break;
       
   670   }
       
   671 
       
   672   result = parent_class->change_state (element, transition);
       
   673 
       
   674   switch (transition) {
       
   675     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       
   676       break;
       
   677     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
   678       break;
       
   679     case GST_STATE_CHANGE_READY_TO_NULL:
       
   680       ogg_sync_clear (&ogg->sync);
       
   681       break;
       
   682     default:
       
   683       break;
       
   684   }
       
   685   return result;
       
   686 }
       
   687 
       
   688 gboolean
       
   689 gst_ogg_parse_plugin_init (GstPlugin * plugin)
       
   690 {
       
   691   GST_DEBUG_CATEGORY_INIT (gst_ogg_parse_debug, "oggparse", 0, "ogg parser");
       
   692 
       
   693   return gst_element_register (plugin, "oggparse", GST_RANK_NONE,
       
   694       GST_TYPE_OGG_PARSE);
       
   695 }