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