update for _get_caps() -> _query_caps()
[platform/upstream/gstreamer.git] / ext / ogg / gstoggparse.c
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 #include "gstogg.h"
40 #include "gstoggstream.h"
41
42 GST_DEBUG_CATEGORY_STATIC (gst_ogg_parse_debug);
43 #define GST_CAT_DEFAULT gst_ogg_parse_debug
44
45 #define GST_TYPE_OGG_PARSE (gst_ogg_parse_get_type())
46 #define GST_OGG_PARSE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OGG_PARSE, GstOggParse))
47 #define GST_OGG_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OGG_PARSE, GstOggParse))
48 #define GST_IS_OGG_PARSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_PARSE))
49 #define GST_IS_OGG_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_PARSE))
50
51 static GType gst_ogg_parse_get_type (void);
52
53 typedef struct _GstOggParse GstOggParse;
54 typedef struct _GstOggParseClass GstOggParseClass;
55
56 struct _GstOggParse
57 {
58   GstElement element;
59
60   GstPad *sinkpad;              /* Sink pad we're reading data from */
61
62   GstPad *srcpad;               /* Source pad we're writing to */
63
64   GSList *oggstreams;           /* list of GstOggStreams for known streams */
65
66   gint64 offset;                /* Current stream offset */
67
68   gboolean in_headers;          /* Set if we're reading headers for streams */
69
70   gboolean last_page_not_bos;   /* Set if we've seen a non-BOS page */
71
72   ogg_sync_state sync;          /* Ogg page synchronisation */
73
74   GstCaps *caps;                /* Our src caps */
75
76   GstOggStream *video_stream;   /* Stream used to construct delta_unit flags */
77 };
78
79 struct _GstOggParseClass
80 {
81   GstElementClass parent_class;
82 };
83
84 static void gst_ogg_parse_base_init (gpointer g_class);
85 static void gst_ogg_parse_class_init (GstOggParseClass * klass);
86 static void gst_ogg_parse_init (GstOggParse * ogg);
87 static GstElementClass *parent_class = NULL;
88
89 static GType
90 gst_ogg_parse_get_type (void)
91 {
92   static GType ogg_parse_type = 0;
93
94   if (!ogg_parse_type) {
95     static const GTypeInfo ogg_parse_info = {
96       sizeof (GstOggParseClass),
97       gst_ogg_parse_base_init,
98       NULL,
99       (GClassInitFunc) gst_ogg_parse_class_init,
100       NULL,
101       NULL,
102       sizeof (GstOggParse),
103       0,
104       (GInstanceInitFunc) gst_ogg_parse_init,
105     };
106
107     ogg_parse_type = g_type_register_static (GST_TYPE_ELEMENT, "GstOggParse",
108         &ogg_parse_info, 0);
109   }
110   return ogg_parse_type;
111 }
112
113 static void
114 free_stream (GstOggStream * stream)
115 {
116   g_list_foreach (stream->headers, (GFunc) gst_mini_object_unref, NULL);
117   g_list_foreach (stream->unknown_pages, (GFunc) gst_mini_object_unref, NULL);
118   g_list_foreach (stream->stored_buffers, (GFunc) gst_mini_object_unref, NULL);
119
120   g_slice_free (GstOggStream, stream);
121 }
122
123 static void
124 gst_ogg_parse_delete_all_streams (GstOggParse * ogg)
125 {
126   g_slist_foreach (ogg->oggstreams, (GFunc) free_stream, NULL);
127   g_slist_free (ogg->oggstreams);
128   ogg->oggstreams = NULL;
129 }
130
131 static GstOggStream *
132 gst_ogg_parse_new_stream (GstOggParse * parser, ogg_page * page)
133 {
134   GstOggStream *stream;
135   ogg_packet packet;
136   int ret;
137   guint32 serialno;
138
139   serialno = ogg_page_serialno (page);
140
141   GST_DEBUG_OBJECT (parser, "creating new stream %08x", serialno);
142
143   stream = g_slice_new0 (GstOggStream);
144
145   stream->serialno = serialno;
146   stream->in_headers = 1;
147
148   if (ogg_stream_init (&stream->stream, serialno) != 0) {
149     GST_ERROR ("Could not initialize ogg_stream struct for serial %08x.",
150         serialno);
151     return NULL;
152   }
153
154   /* FIXME check return */
155   ogg_stream_pagein (&stream->stream, page);
156
157   /* FIXME check return */
158   ret = ogg_stream_packetout (&stream->stream, &packet);
159   if (ret == 1) {
160     gst_ogg_stream_setup_map (stream, &packet);
161     if (stream->is_video) {
162       parser->video_stream = stream;
163     }
164   }
165
166   parser->oggstreams = g_slist_append (parser->oggstreams, stream);
167
168   return stream;
169 }
170
171 static GstOggStream *
172 gst_ogg_parse_find_stream (GstOggParse * parser, guint32 serialno)
173 {
174   GSList *l;
175
176   for (l = parser->oggstreams; l != NULL; l = l->next) {
177     GstOggStream *stream = (GstOggStream *) l->data;
178
179     if (stream->serialno == serialno)
180       return stream;
181   }
182   return NULL;
183 }
184
185 /* signals and args */
186 enum
187 {
188   /* FILL ME */
189   LAST_SIGNAL
190 };
191
192 enum
193 {
194   ARG_0
195       /* FILL ME */
196 };
197
198 static GstStaticPadTemplate ogg_parse_src_template_factory =
199 GST_STATIC_PAD_TEMPLATE ("src",
200     GST_PAD_SRC,
201     GST_PAD_ALWAYS,
202     GST_STATIC_CAPS ("application/ogg")
203     );
204
205 static GstStaticPadTemplate ogg_parse_sink_template_factory =
206 GST_STATIC_PAD_TEMPLATE ("sink",
207     GST_PAD_SINK,
208     GST_PAD_ALWAYS,
209     GST_STATIC_CAPS ("application/ogg")
210     );
211
212 static void gst_ogg_parse_dispose (GObject * object);
213 static GstStateChangeReturn gst_ogg_parse_change_state (GstElement * element,
214     GstStateChange transition);
215 static GstFlowReturn gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer);
216
217 static void
218 gst_ogg_parse_base_init (gpointer g_class)
219 {
220   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
221
222   gst_element_class_set_details_simple (element_class,
223       "Ogg parser", "Codec/Parser",
224       "parse ogg streams into pages (info about ogg: http://xiph.org)",
225       "Michael Smith <msmith@fluendo.com>");
226
227   gst_element_class_add_pad_template (element_class,
228       gst_static_pad_template_get (&ogg_parse_sink_template_factory));
229   gst_element_class_add_pad_template (element_class,
230       gst_static_pad_template_get (&ogg_parse_src_template_factory));
231 }
232
233 static void
234 gst_ogg_parse_class_init (GstOggParseClass * klass)
235 {
236   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
237   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
238
239   parent_class = g_type_class_peek_parent (klass);
240
241   gstelement_class->change_state = gst_ogg_parse_change_state;
242
243   gobject_class->dispose = gst_ogg_parse_dispose;
244 }
245
246 static void
247 gst_ogg_parse_init (GstOggParse * ogg)
248 {
249   /* create the sink and source pads */
250   ogg->sinkpad =
251       gst_pad_new_from_static_template (&ogg_parse_sink_template_factory,
252       "sink");
253   ogg->srcpad =
254       gst_pad_new_from_static_template (&ogg_parse_src_template_factory, "src");
255
256   /* TODO: Are there any events we must handle? */
257   /* gst_pad_set_event_function (ogg->sinkpad, gst_ogg_parse_handle_event); */
258   gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_parse_chain);
259
260   gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
261   gst_element_add_pad (GST_ELEMENT (ogg), ogg->srcpad);
262
263   ogg->oggstreams = NULL;
264 }
265
266 static void
267 gst_ogg_parse_dispose (GObject * object)
268 {
269   GstOggParse *ogg = GST_OGG_PARSE (object);
270
271   GST_LOG_OBJECT (ogg, "Disposing of object %p", ogg);
272
273   ogg_sync_clear (&ogg->sync);
274   gst_ogg_parse_delete_all_streams (ogg);
275
276   if (ogg->caps) {
277     gst_caps_unref (ogg->caps);
278     ogg->caps = NULL;
279   }
280
281   if (G_OBJECT_CLASS (parent_class)->dispose)
282     G_OBJECT_CLASS (parent_class)->dispose (object);
283 }
284
285 /* submit the given buffer to the ogg sync */
286 static GstFlowReturn
287 gst_ogg_parse_submit_buffer (GstOggParse * ogg, GstBuffer * buffer)
288 {
289   gsize size;
290   guint8 *data;
291   gchar *oggbuffer;
292   GstFlowReturn ret = GST_FLOW_OK;
293
294   data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
295
296   GST_DEBUG_OBJECT (ogg, "submitting %" G_GSIZE_FORMAT " bytes", size);
297   if (G_UNLIKELY (size == 0))
298     goto done;
299
300   oggbuffer = ogg_sync_buffer (&ogg->sync, size);
301   if (G_UNLIKELY (oggbuffer == NULL)) {
302     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
303         (NULL), ("failed to get ogg sync buffer"));
304     ret = GST_FLOW_ERROR;
305     goto done;
306   }
307
308   memcpy (oggbuffer, data, size);
309   if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0)) {
310     GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
311         ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
312     ret = GST_FLOW_ERROR;
313   }
314
315 done:
316   gst_buffer_unmap (buffer, data, size);
317   gst_buffer_unref (buffer);
318
319   return ret;
320 }
321
322 static void
323 gst_ogg_parse_append_header (GValue * array, GstBuffer * buf)
324 {
325   GValue value = { 0 };
326   /* We require a copy to avoid circular refcounts */
327   GstBuffer *buffer = gst_buffer_copy (buf);
328
329   GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
330
331   g_value_init (&value, GST_TYPE_BUFFER);
332   gst_value_set_buffer (&value, buffer);
333   gst_value_array_append_value (array, &value);
334   g_value_unset (&value);
335
336 }
337
338 typedef enum
339 {
340   PAGE_HEADER,                  /* Header page */
341   PAGE_DATA,                    /* Data page */
342   PAGE_PENDING,                 /* We don't know yet, we'll have to see some future pages */
343 } page_type;
344
345 static page_type
346 gst_ogg_parse_is_header (GstOggParse * ogg, GstOggStream * stream,
347     ogg_page * page)
348 {
349   ogg_int64_t gpos = ogg_page_granulepos (page);
350
351   if (gpos < 0)
352     return PAGE_PENDING;
353
354   /* This is good enough for now, but technically requires codec-specific
355    * behaviour to be perfect. This is where we need the mooted library for 
356    * this stuff, which nobody has written.
357    */
358   if (gpos > 0)
359     return PAGE_DATA;
360   else
361     return PAGE_HEADER;
362 }
363
364 static GstBuffer *
365 gst_ogg_parse_buffer_from_page (ogg_page * page,
366     guint64 offset, GstClockTime timestamp)
367 {
368   int size = page->header_len + page->body_len;
369   GstBuffer *buf = gst_buffer_new_and_alloc (size);
370
371   gst_buffer_fill (buf, 0, page->header, page->header_len);
372   gst_buffer_fill (buf, page->header_len, page->body, page->body_len);
373
374   GST_BUFFER_TIMESTAMP (buf) = timestamp;
375   GST_BUFFER_OFFSET (buf) = offset;
376   GST_BUFFER_OFFSET_END (buf) = offset + size;
377
378   return buf;
379 }
380
381
382 /* Reads in buffers, parses them, reframes into one-buffer-per-ogg-page, submits
383  * pages to output pad.
384  */
385 static GstFlowReturn
386 gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer)
387 {
388   GstOggParse *ogg;
389   GstFlowReturn result = GST_FLOW_OK;
390   gint ret = -1;
391   guint32 serialno;
392   GstBuffer *pagebuffer;
393   GstClockTime buffertimestamp = GST_BUFFER_TIMESTAMP (buffer);
394
395   ogg = GST_OGG_PARSE (GST_OBJECT_PARENT (pad));
396
397   GST_LOG_OBJECT (ogg,
398       "Chain function received buffer of size %" G_GSIZE_FORMAT,
399       gst_buffer_get_size (buffer));
400
401   gst_ogg_parse_submit_buffer (ogg, buffer);
402
403   while (ret != 0 && result == GST_FLOW_OK) {
404     ogg_page page;
405
406     /* We use ogg_sync_pageseek() rather than ogg_sync_pageout() so that we can
407      * track how many bytes the ogg layer discarded (in the case of sync errors,
408      * etc.); this allows us to accurately track the current stream offset
409      */
410     ret = ogg_sync_pageseek (&ogg->sync, &page);
411     if (ret == 0) {
412       /* need more data, that's fine... */
413       break;
414     } else if (ret < 0) {
415       /* discontinuity; track how many bytes we skipped (-ret) */
416       ogg->offset -= ret;
417     } else {
418       gint64 granule = ogg_page_granulepos (&page);
419 #ifndef GST_DISABLE_GST_DEBUG
420       int bos = ogg_page_bos (&page);
421 #endif
422       guint64 startoffset = ogg->offset;
423       GstOggStream *stream;
424       gboolean keyframe;
425
426       serialno = ogg_page_serialno (&page);
427       stream = gst_ogg_parse_find_stream (ogg, serialno);
428
429       GST_LOG_OBJECT (ogg, "Timestamping outgoing buffer as %" GST_TIME_FORMAT,
430           GST_TIME_ARGS (buffertimestamp));
431
432       if (stream) {
433         buffertimestamp = gst_ogg_stream_get_end_time_for_granulepos (stream,
434             granule);
435         if (ogg->video_stream) {
436           if (stream == ogg->video_stream) {
437             keyframe = gst_ogg_stream_granulepos_is_key_frame (stream, granule);
438           } else {
439             keyframe = FALSE;
440           }
441         } else {
442           keyframe = TRUE;
443         }
444       } else {
445         buffertimestamp = GST_CLOCK_TIME_NONE;
446         keyframe = TRUE;
447       }
448       pagebuffer = gst_ogg_parse_buffer_from_page (&page, startoffset,
449           buffertimestamp);
450
451       /* We read out 'ret' bytes, so we set the next offset appropriately */
452       ogg->offset += ret;
453
454       GST_LOG_OBJECT (ogg,
455           "processing ogg page (serial %08x, pageno %ld, "
456           "granule pos %" G_GUINT64_FORMAT ", bos %d, offset %"
457           G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ") keyframe=%d",
458           serialno, ogg_page_pageno (&page),
459           granule, bos, startoffset, ogg->offset, keyframe);
460
461       if (ogg_page_bos (&page)) {
462         /* If we've seen this serialno before, this is technically an error,
463          * we log this case but accept it - this one replaces the previous
464          * stream with this serialno. We can do this since we're streaming, and
465          * not supporting seeking...
466          */
467         GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno);
468
469         if (stream != NULL) {
470           GST_LOG_OBJECT (ogg, "Incorrect stream; repeats serial number %08x "
471               "at offset %" G_GINT64_FORMAT, serialno, ogg->offset);
472         }
473
474         if (ogg->last_page_not_bos) {
475           GST_LOG_OBJECT (ogg, "Deleting all referenced streams, found a new "
476               "chain starting with serial %u", serialno);
477           gst_ogg_parse_delete_all_streams (ogg);
478         }
479
480         stream = gst_ogg_parse_new_stream (ogg, &page);
481
482         ogg->last_page_not_bos = FALSE;
483
484         gst_buffer_ref (pagebuffer);
485         stream->headers = g_list_append (stream->headers, pagebuffer);
486
487         if (!ogg->in_headers) {
488           GST_LOG_OBJECT (ogg,
489               "Found start of new chain at offset %" G_GUINT64_FORMAT,
490               startoffset);
491           ogg->in_headers = 1;
492         }
493
494         /* For now, we just keep the header buffer in the stream->headers list;
495          * it actually gets output once we've collected the entire set
496          */
497       } else {
498         /* Non-BOS page. Either: we're outside headers, and this isn't a 
499          * header (normal data), outside headers and this is (error!), inside
500          * headers, this is (append header), or inside headers and this isn't 
501          * (we've found the end of headers; flush the lot!)
502          *
503          * Before that, we flag that the last page seen (this one) was not a 
504          * BOS page; that way we know that when we next see a BOS page it's a
505          * new chain, and we can flush all existing streams.
506          */
507         page_type type;
508         GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno);
509
510         if (!stream) {
511           GST_LOG_OBJECT (ogg,
512               "Non-BOS page unexpectedly found at %" G_GINT64_FORMAT,
513               ogg->offset);
514           goto failure;
515         }
516
517         ogg->last_page_not_bos = TRUE;
518
519         type = gst_ogg_parse_is_header (ogg, stream, &page);
520
521         if (type == PAGE_PENDING && ogg->in_headers) {
522           gst_buffer_ref (pagebuffer);
523
524           stream->unknown_pages = g_list_append (stream->unknown_pages,
525               pagebuffer);
526         } else if (type == PAGE_HEADER) {
527           if (!ogg->in_headers) {
528             GST_LOG_OBJECT (ogg, "Header page unexpectedly found outside "
529                 "headers at offset %" G_GINT64_FORMAT, ogg->offset);
530             goto failure;
531           } else {
532             /* Append the header to the buffer list, after any unknown previous
533              * pages
534              */
535             stream->headers = g_list_concat (stream->headers,
536                 stream->unknown_pages);
537             g_list_free (stream->unknown_pages);
538             gst_buffer_ref (pagebuffer);
539             stream->headers = g_list_append (stream->headers, pagebuffer);
540           }
541         } else {                /* PAGE_DATA, or PAGE_PENDING but outside headers */
542           if (ogg->in_headers) {
543             /* First non-header page... set caps, flush headers.
544              *
545              * First up, we build a single GValue list of all the pagebuffers
546              * we're using for the headers, in order.
547              * Then we set this on the caps structure. Then we can start pushing
548              * buffers for the headers, and finally we send this non-header
549              * page.
550              */
551             GstCaps *caps;
552             GstStructure *structure;
553             GValue array = { 0 };
554             gint count = 0;
555             gboolean found_pending_headers = FALSE;
556             GSList *l;
557
558             g_value_init (&array, GST_TYPE_ARRAY);
559
560             for (l = ogg->oggstreams; l != NULL; l = l->next) {
561               GstOggStream *stream = (GstOggStream *) l->data;
562
563               if (g_list_length (stream->headers) == 0) {
564                 GST_LOG_OBJECT (ogg, "No primary header found for stream %08x",
565                     stream->serialno);
566                 goto failure;
567               }
568
569               gst_ogg_parse_append_header (&array,
570                   GST_BUFFER (stream->headers->data));
571               count++;
572             }
573
574             for (l = ogg->oggstreams; l != NULL; l = l->next) {
575               GstOggStream *stream = (GstOggStream *) l->data;
576               GList *j;
577
578               /* already appended the first header, now do headers 2-N */
579               for (j = stream->headers->next; j != NULL; j = j->next) {
580                 gst_ogg_parse_append_header (&array, GST_BUFFER (j->data));
581                 count++;
582               }
583             }
584
585             caps = gst_pad_query_caps (ogg->srcpad, NULL);
586             caps = gst_caps_make_writable (caps);
587
588             structure = gst_caps_get_structure (caps, 0);
589             gst_structure_set_value (structure, "streamheader", &array);
590
591             gst_pad_set_caps (ogg->srcpad, caps);
592
593             g_value_unset (&array);
594
595             if (ogg->caps)
596               gst_caps_unref (ogg->caps);
597             ogg->caps = caps;
598
599             GST_LOG_OBJECT (ogg, "Set \"streamheader\" caps with %d buffers "
600                 "(one per page)", count);
601
602             /* Now, we do the same thing, but push buffers... */
603             for (l = ogg->oggstreams; l != NULL; l = l->next) {
604               GstOggStream *stream = (GstOggStream *) l->data;
605               GstBuffer *buf = GST_BUFFER (stream->headers->data);
606
607               result = gst_pad_push (ogg->srcpad, buf);
608               if (result != GST_FLOW_OK)
609                 return result;
610             }
611             for (l = ogg->oggstreams; l != NULL; l = l->next) {
612               GstOggStream *stream = (GstOggStream *) l->data;
613               GList *j;
614
615               /* pushed the first one for each stream already, now do 2-N */
616               for (j = stream->headers->next; j != NULL; j = j->next) {
617                 GstBuffer *buf = GST_BUFFER (j->data);
618
619                 result = gst_pad_push (ogg->srcpad, buf);
620                 if (result != GST_FLOW_OK)
621                   return result;
622               }
623             }
624
625             ogg->in_headers = 0;
626
627             /* And finally the pending data pages */
628             for (l = ogg->oggstreams; l != NULL; l = l->next) {
629               GstOggStream *stream = (GstOggStream *) l->data;
630               GList *k;
631
632               if (stream->unknown_pages == NULL)
633                 continue;
634
635               if (found_pending_headers) {
636                 GST_WARNING_OBJECT (ogg, "Incorrectly muxed headers found at "
637                     "approximate offset %" G_GINT64_FORMAT, ogg->offset);
638               }
639               found_pending_headers = TRUE;
640
641               GST_LOG_OBJECT (ogg, "Pushing %d pending pages after headers",
642                   g_list_length (stream->unknown_pages) + 1);
643
644               for (k = stream->unknown_pages; k != NULL; k = k->next) {
645                 GstBuffer *buf = GST_BUFFER (k->data);
646
647                 result = gst_pad_push (ogg->srcpad, buf);
648                 if (result != GST_FLOW_OK)
649                   return result;
650               }
651               g_list_foreach (stream->unknown_pages,
652                   (GFunc) gst_mini_object_unref, NULL);
653               g_list_free (stream->unknown_pages);
654               stream->unknown_pages = NULL;
655             }
656           }
657
658           if (granule == -1) {
659             stream->stored_buffers = g_list_append (stream->stored_buffers,
660                 pagebuffer);
661           } else {
662             while (stream->stored_buffers) {
663               GstBuffer *buf = stream->stored_buffers->data;
664
665               buf = gst_buffer_make_writable (buf);
666
667               GST_BUFFER_TIMESTAMP (buf) = buffertimestamp;
668               if (!keyframe) {
669                 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
670               } else {
671                 keyframe = FALSE;
672               }
673
674               result = gst_pad_push (ogg->srcpad, buf);
675               if (result != GST_FLOW_OK)
676                 return result;
677
678               stream->stored_buffers =
679                   g_list_delete_link (stream->stored_buffers,
680                   stream->stored_buffers);
681             }
682
683             pagebuffer = gst_buffer_make_writable (pagebuffer);
684             if (!keyframe) {
685               GST_BUFFER_FLAG_SET (pagebuffer, GST_BUFFER_FLAG_DELTA_UNIT);
686             } else {
687               keyframe = FALSE;
688             }
689
690             result = gst_pad_push (ogg->srcpad, pagebuffer);
691             if (result != GST_FLOW_OK)
692               return result;
693           }
694         }
695       }
696     }
697   }
698
699   return result;
700
701 failure:
702   gst_pad_push_event (GST_PAD (ogg->srcpad), gst_event_new_eos ());
703   return GST_FLOW_ERROR;
704 }
705
706 static GstStateChangeReturn
707 gst_ogg_parse_change_state (GstElement * element, GstStateChange transition)
708 {
709   GstOggParse *ogg;
710   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
711
712   ogg = GST_OGG_PARSE (element);
713
714   switch (transition) {
715     case GST_STATE_CHANGE_NULL_TO_READY:
716       ogg_sync_init (&ogg->sync);
717       break;
718     case GST_STATE_CHANGE_READY_TO_PAUSED:
719       ogg_sync_reset (&ogg->sync);
720       break;
721     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
722       break;
723     default:
724       break;
725   }
726
727   result = parent_class->change_state (element, transition);
728
729   switch (transition) {
730     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
731       break;
732     case GST_STATE_CHANGE_PAUSED_TO_READY:
733       break;
734     case GST_STATE_CHANGE_READY_TO_NULL:
735       ogg_sync_clear (&ogg->sync);
736       break;
737     default:
738       break;
739   }
740   return result;
741 }
742
743 gboolean
744 gst_ogg_parse_plugin_init (GstPlugin * plugin)
745 {
746   GST_DEBUG_CATEGORY_INIT (gst_ogg_parse_debug, "oggparse", 0, "ogg parser");
747
748   return gst_element_register (plugin, "oggparse", GST_RANK_NONE,
749       GST_TYPE_OGG_PARSE);
750 }