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