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