Added gst-plugins-base-subtitles0.10-0.10.34 for Meego Harmattan 1.2
[mafwsubrenderer] / gst-plugins-base-subtitles0.10 / 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_free (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_new0 (GstOggStream, 1);
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   guint size;
290   guint8 *data;
291   gchar *oggbuffer;
292   GstFlowReturn ret = GST_FLOW_OK;
293
294   size = GST_BUFFER_SIZE (buffer);
295   data = GST_BUFFER_DATA (buffer);
296
297   GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
298   if (G_UNLIKELY (size == 0))
299     goto done;
300
301   oggbuffer = ogg_sync_buffer (&ogg->sync, size);
302   if (G_UNLIKELY (oggbuffer == NULL)) {
303     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
304         (NULL), ("failed to get ogg sync buffer"));
305     ret = GST_FLOW_ERROR;
306     goto done;
307   }
308
309   memcpy (oggbuffer, data, size);
310   if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0)) {
311     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
312         (NULL), ("failed to write %d bytes to the sync buffer", size));
313     ret = GST_FLOW_ERROR;
314   }
315
316 done:
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   memcpy (GST_BUFFER_DATA (buf), page->header, page->header_len);
372   memcpy (GST_BUFFER_DATA (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, "Chain function received buffer of size %d",
398       GST_BUFFER_SIZE (buffer));
399
400   gst_ogg_parse_submit_buffer (ogg, buffer);
401
402   while (ret != 0 && result == GST_FLOW_OK) {
403     ogg_page page;
404
405     /* We use ogg_sync_pageseek() rather than ogg_sync_pageout() so that we can
406      * track how many bytes the ogg layer discarded (in the case of sync errors,
407      * etc.); this allows us to accurately track the current stream offset
408      */
409     ret = ogg_sync_pageseek (&ogg->sync, &page);
410     if (ret == 0) {
411       /* need more data, that's fine... */
412       break;
413     } else if (ret < 0) {
414       /* discontinuity; track how many bytes we skipped (-ret) */
415       ogg->offset -= ret;
416     } else {
417       gint64 granule = ogg_page_granulepos (&page);
418 #ifndef GST_DISABLE_GST_DEBUG
419       int bos = ogg_page_bos (&page);
420 #endif
421       guint64 startoffset = ogg->offset;
422       GstOggStream *stream;
423       gboolean keyframe;
424
425       serialno = ogg_page_serialno (&page);
426       stream = gst_ogg_parse_find_stream (ogg, serialno);
427
428       GST_LOG_OBJECT (ogg, "Timestamping outgoing buffer as %" GST_TIME_FORMAT,
429           GST_TIME_ARGS (buffertimestamp));
430
431       if (stream) {
432         buffertimestamp = gst_ogg_stream_get_end_time_for_granulepos (stream,
433             granule);
434         if (ogg->video_stream) {
435           if (stream == ogg->video_stream) {
436             keyframe = gst_ogg_stream_granulepos_is_key_frame (stream, granule);
437           } else {
438             keyframe = FALSE;
439           }
440         } else {
441           keyframe = TRUE;
442         }
443       } else {
444         buffertimestamp = GST_CLOCK_TIME_NONE;
445         keyframe = TRUE;
446       }
447       pagebuffer = gst_ogg_parse_buffer_from_page (&page, startoffset,
448           buffertimestamp);
449
450       /* We read out 'ret' bytes, so we set the next offset appropriately */
451       ogg->offset += ret;
452
453       GST_LOG_OBJECT (ogg,
454           "processing ogg page (serial %08x, pageno %ld, "
455           "granule pos %" G_GUINT64_FORMAT ", bos %d, offset %"
456           G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ") keyframe=%d",
457           serialno, ogg_page_pageno (&page),
458           granule, bos, startoffset, ogg->offset, keyframe);
459
460       if (ogg_page_bos (&page)) {
461         /* If we've seen this serialno before, this is technically an error,
462          * we log this case but accept it - this one replaces the previous
463          * stream with this serialno. We can do this since we're streaming, and
464          * not supporting seeking...
465          */
466         GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno);
467
468         if (stream != NULL) {
469           GST_LOG_OBJECT (ogg, "Incorrect stream; repeats serial number %u "
470               "at offset %" G_GINT64_FORMAT, serialno, ogg->offset);
471         }
472
473         if (ogg->last_page_not_bos) {
474           GST_LOG_OBJECT (ogg, "Deleting all referenced streams, found a new "
475               "chain starting with serial %u", serialno);
476           gst_ogg_parse_delete_all_streams (ogg);
477         }
478
479         stream = gst_ogg_parse_new_stream (ogg, &page);
480
481         ogg->last_page_not_bos = FALSE;
482
483         gst_buffer_ref (pagebuffer);
484         stream->headers = g_list_append (stream->headers, pagebuffer);
485
486         if (!ogg->in_headers) {
487           GST_LOG_OBJECT (ogg,
488               "Found start of new chain at offset %" G_GUINT64_FORMAT,
489               startoffset);
490           ogg->in_headers = 1;
491         }
492
493         /* For now, we just keep the header buffer in the stream->headers list;
494          * it actually gets output once we've collected the entire set
495          */
496       } else {
497         /* Non-BOS page. Either: we're outside headers, and this isn't a 
498          * header (normal data), outside headers and this is (error!), inside
499          * headers, this is (append header), or inside headers and this isn't 
500          * (we've found the end of headers; flush the lot!)
501          *
502          * Before that, we flag that the last page seen (this one) was not a 
503          * BOS page; that way we know that when we next see a BOS page it's a
504          * new chain, and we can flush all existing streams.
505          */
506         page_type type;
507         GstOggStream *stream = gst_ogg_parse_find_stream (ogg, serialno);
508
509         if (!stream) {
510           GST_LOG_OBJECT (ogg,
511               "Non-BOS page unexpectedly found at %" G_GINT64_FORMAT,
512               ogg->offset);
513           goto failure;
514         }
515
516         ogg->last_page_not_bos = TRUE;
517
518         type = gst_ogg_parse_is_header (ogg, stream, &page);
519
520         if (type == PAGE_PENDING && ogg->in_headers) {
521           gst_buffer_ref (pagebuffer);
522
523           stream->unknown_pages = g_list_append (stream->unknown_pages,
524               pagebuffer);
525         } else if (type == PAGE_HEADER) {
526           if (!ogg->in_headers) {
527             GST_LOG_OBJECT (ogg, "Header page unexpectedly found outside "
528                 "headers at offset %" G_GINT64_FORMAT, ogg->offset);
529             goto failure;
530           } else {
531             /* Append the header to the buffer list, after any unknown previous
532              * pages
533              */
534             stream->headers = g_list_concat (stream->headers,
535                 stream->unknown_pages);
536             g_list_free (stream->unknown_pages);
537             gst_buffer_ref (pagebuffer);
538             stream->headers = g_list_append (stream->headers, pagebuffer);
539           }
540         } else {                /* PAGE_DATA, or PAGE_PENDING but outside headers */
541           if (ogg->in_headers) {
542             /* First non-header page... set caps, flush headers.
543              *
544              * First up, we build a single GValue list of all the pagebuffers
545              * we're using for the headers, in order.
546              * Then we set this on the caps structure. Then we can start pushing
547              * buffers for the headers, and finally we send this non-header
548              * page.
549              */
550             GstCaps *caps;
551             GstStructure *structure;
552             GValue array = { 0 };
553             gint count = 0;
554             gboolean found_pending_headers = FALSE;
555             GSList *l;
556
557             g_value_init (&array, GST_TYPE_ARRAY);
558
559             for (l = ogg->oggstreams; l != NULL; l = l->next) {
560               GstOggStream *stream = (GstOggStream *) l->data;
561
562               if (g_list_length (stream->headers) == 0) {
563                 GST_LOG_OBJECT (ogg, "No primary header found for stream %08lx",
564                     stream->serialno);
565                 goto failure;
566               }
567
568               gst_ogg_parse_append_header (&array,
569                   GST_BUFFER (stream->headers->data));
570               count++;
571             }
572
573             for (l = ogg->oggstreams; l != NULL; l = l->next) {
574               GstOggStream *stream = (GstOggStream *) l->data;
575               GList *j;
576
577               /* already appended the first header, now do headers 2-N */
578               for (j = stream->headers->next; j != NULL; j = j->next) {
579                 gst_ogg_parse_append_header (&array, GST_BUFFER (j->data));
580                 count++;
581               }
582             }
583
584             caps = gst_pad_get_caps (ogg->srcpad);
585             caps = gst_caps_make_writable (caps);
586
587             structure = gst_caps_get_structure (caps, 0);
588             gst_structure_set_value (structure, "streamheader", &array);
589
590             gst_pad_set_caps (ogg->srcpad, caps);
591
592             g_value_unset (&array);
593
594             if (ogg->caps)
595               gst_caps_unref (ogg->caps);
596             ogg->caps = caps;
597
598             GST_LOG_OBJECT (ogg, "Set \"streamheader\" caps with %d buffers "
599                 "(one per page)", count);
600
601             /* Now, we do the same thing, but push buffers... */
602             for (l = ogg->oggstreams; l != NULL; l = l->next) {
603               GstOggStream *stream = (GstOggStream *) l->data;
604               GstBuffer *buf = GST_BUFFER (stream->headers->data);
605
606               buf = gst_buffer_make_metadata_writable (buf);
607               gst_buffer_set_caps (buf, caps);
608
609               result = gst_pad_push (ogg->srcpad, buf);
610               if (result != GST_FLOW_OK)
611                 return result;
612             }
613             for (l = ogg->oggstreams; l != NULL; l = l->next) {
614               GstOggStream *stream = (GstOggStream *) l->data;
615               GList *j;
616
617               /* pushed the first one for each stream already, now do 2-N */
618               for (j = stream->headers->next; j != NULL; j = j->next) {
619                 GstBuffer *buf = GST_BUFFER (j->data);
620
621                 buf = gst_buffer_make_metadata_writable (buf);
622                 gst_buffer_set_caps (buf, caps);
623
624                 result = gst_pad_push (ogg->srcpad, buf);
625                 if (result != GST_FLOW_OK)
626                   return result;
627               }
628             }
629
630             ogg->in_headers = 0;
631
632             /* And finally the pending data pages */
633             for (l = ogg->oggstreams; l != NULL; l = l->next) {
634               GstOggStream *stream = (GstOggStream *) l->data;
635               GList *k;
636
637               if (stream->unknown_pages == NULL)
638                 continue;
639
640               if (found_pending_headers) {
641                 GST_WARNING_OBJECT (ogg, "Incorrectly muxed headers found at "
642                     "approximate offset %" G_GINT64_FORMAT, ogg->offset);
643               }
644               found_pending_headers = TRUE;
645
646               GST_LOG_OBJECT (ogg, "Pushing %d pending pages after headers",
647                   g_list_length (stream->unknown_pages) + 1);
648
649               for (k = stream->unknown_pages; k != NULL; k = k->next) {
650                 GstBuffer *buf;
651
652                 buf = gst_buffer_make_metadata_writable (GST_BUFFER (k->data));
653                 gst_buffer_set_caps (buf, caps);
654                 result = gst_pad_push (ogg->srcpad, buf);
655                 if (result != GST_FLOW_OK)
656                   return result;
657               }
658               g_list_foreach (stream->unknown_pages,
659                   (GFunc) gst_mini_object_unref, NULL);
660               g_list_free (stream->unknown_pages);
661               stream->unknown_pages = NULL;
662             }
663           }
664
665           if (granule == -1) {
666             stream->stored_buffers = g_list_append (stream->stored_buffers,
667                 pagebuffer);
668           } else {
669             while (stream->stored_buffers) {
670               GstBuffer *buf = stream->stored_buffers->data;
671
672               buf = gst_buffer_make_metadata_writable (buf);
673               gst_buffer_set_caps (buf, ogg->caps);
674               GST_BUFFER_TIMESTAMP (buf) = buffertimestamp;
675               if (!keyframe) {
676                 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
677               } else {
678                 keyframe = FALSE;
679               }
680
681               result = gst_pad_push (ogg->srcpad, buf);
682               if (result != GST_FLOW_OK)
683                 return result;
684
685               stream->stored_buffers =
686                   g_list_delete_link (stream->stored_buffers,
687                   stream->stored_buffers);
688             }
689
690             pagebuffer = gst_buffer_make_metadata_writable (pagebuffer);
691             gst_buffer_set_caps (pagebuffer, ogg->caps);
692             if (!keyframe) {
693               GST_BUFFER_FLAG_SET (pagebuffer, GST_BUFFER_FLAG_DELTA_UNIT);
694             } else {
695               keyframe = FALSE;
696             }
697
698             result = gst_pad_push (ogg->srcpad, pagebuffer);
699             if (result != GST_FLOW_OK)
700               return result;
701           }
702         }
703       }
704     }
705   }
706
707   return result;
708
709 failure:
710   gst_pad_push_event (GST_PAD (ogg->srcpad), gst_event_new_eos ());
711   return GST_FLOW_ERROR;
712 }
713
714 static GstStateChangeReturn
715 gst_ogg_parse_change_state (GstElement * element, GstStateChange transition)
716 {
717   GstOggParse *ogg;
718   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
719
720   ogg = GST_OGG_PARSE (element);
721
722   switch (transition) {
723     case GST_STATE_CHANGE_NULL_TO_READY:
724       ogg_sync_init (&ogg->sync);
725       break;
726     case GST_STATE_CHANGE_READY_TO_PAUSED:
727       ogg_sync_reset (&ogg->sync);
728       break;
729     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
730       break;
731     default:
732       break;
733   }
734
735   result = parent_class->change_state (element, transition);
736
737   switch (transition) {
738     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
739       break;
740     case GST_STATE_CHANGE_PAUSED_TO_READY:
741       break;
742     case GST_STATE_CHANGE_READY_TO_NULL:
743       ogg_sync_clear (&ogg->sync);
744       break;
745     default:
746       break;
747   }
748   return result;
749 }
750
751 gboolean
752 gst_ogg_parse_plugin_init (GstPlugin * plugin)
753 {
754   GST_DEBUG_CATEGORY_INIT (gst_ogg_parse_debug, "oggparse", 0, "ogg parser");
755
756   return gst_element_register (plugin, "oggparse", GST_RANK_NONE,
757       GST_TYPE_OGG_PARSE);
758 }