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