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