2 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
4 * gstoggdemux.c: ogg stream demuxer
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.
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.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 * SECTION:element-oggdemux
24 * @see_also: <link linkend="gst-plugins-base-plugins-oggmux">oggmux</link>
26 * This element demuxes ogg files into their encoded audio and video components.
29 * <title>Example pipelines</title>
31 * gst-launch -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
32 * ]| Decodes the vorbis audio stored inside an ogg container.
35 * Last reviewed on 2006-12-30 (0.10.5)
43 #include <gst/gst-i18n-plugin.h>
44 #include <gst/base/gsttypefindhelper.h>
46 #include "gstoggdemux.h"
48 static const GstElementDetails gst_ogg_demux_details =
49 GST_ELEMENT_DETAILS ("Ogg demuxer",
51 "demux ogg streams (info about ogg: http://xiph.org)",
52 "Wim Taymans <wim@fluendo.com>");
54 #define CHUNKSIZE (8500) /* this is out of vorbisfile */
55 #define SKELETON_FISHEAD_SIZE 64
56 #define SKELETON_FISBONE_MIN_SIZE 52
58 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
60 #define GST_CHAIN_LOCK(ogg) g_mutex_lock((ogg)->chain_lock)
61 #define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock((ogg)->chain_lock)
63 GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug);
64 GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_setup_debug);
65 #define GST_CAT_DEFAULT gst_ogg_demux_debug
68 gst_ogg_page_copy (ogg_page * page)
70 ogg_page *p = g_new0 (ogg_page, 1);
72 /* make a copy of the page */
73 p->header = g_memdup (page->header, page->header_len);
74 p->header_len = page->header_len;
75 p->body = g_memdup (page->body, page->body_len);
76 p->body_len = page->body_len;
82 gst_ogg_page_free (ogg_page * page)
84 g_free (page->header);
89 static GstStaticPadTemplate internaltemplate =
90 GST_STATIC_PAD_TEMPLATE ("internal",
95 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
97 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
98 GstOggChain * chain, GstEvent * event);
99 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
101 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
103 static gboolean gst_ogg_demux_receive_event (GstElement * element,
106 static void gst_ogg_pad_class_init (GstOggPadClass * klass);
107 static void gst_ogg_pad_init (GstOggPad * pad);
108 static void gst_ogg_pad_dispose (GObject * object);
109 static void gst_ogg_pad_finalize (GObject * object);
112 static const GstFormat *gst_ogg_pad_formats (GstPad * pad);
113 static const GstEventMask *gst_ogg_pad_event_masks (GstPad * pad);
115 static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
116 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
117 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
118 static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
119 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
122 static gboolean gst_ogg_pad_query_convert (GstOggPad * pad,
123 GstFormat src_format, gint64 src_val,
124 GstFormat * dest_format, gint64 * dest_val);
125 static GstClockTime gst_annodex_granule_to_time (gint64 granulepos,
126 gint64 granulerate_n, gint64 granulerate_d, guint8 granuleshift);
128 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
129 GstOggPad * pad, GstFlowReturn ret);
131 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
134 gst_ogg_pad_class_init (GstOggPadClass * klass)
136 GObjectClass *gobject_class;
138 gobject_class = (GObjectClass *) klass;
140 gobject_class->dispose = gst_ogg_pad_dispose;
141 gobject_class->finalize = gst_ogg_pad_finalize;
145 gst_ogg_pad_init (GstOggPad * pad)
147 gst_pad_set_event_function (GST_PAD (pad),
148 GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
149 gst_pad_set_getcaps_function (GST_PAD (pad),
150 GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps));
151 gst_pad_set_query_type_function (GST_PAD (pad),
152 GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types));
153 gst_pad_set_query_function (GST_PAD (pad),
154 GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
156 pad->mode = GST_OGG_PAD_MODE_INIT;
158 pad->first_granule = -1;
159 pad->current_granule = -1;
161 pad->start_time = GST_CLOCK_TIME_NONE;
162 pad->first_time = GST_CLOCK_TIME_NONE;
164 pad->have_type = FALSE;
165 pad->continued = NULL;
170 gst_ogg_pad_dispose (GObject * object)
172 GstOggPad *pad = GST_OGG_PAD (object);
174 GstElement **element_p;
178 gst_element_set_state (pad->element, GST_STATE_NULL);
180 elem_pad_p = &pad->elem_pad;
181 element_p = &pad->element;
182 elem_out_p = &pad->elem_out;
183 gst_object_replace ((GstObject **) elem_pad_p, NULL);
184 gst_object_replace ((GstObject **) element_p, NULL);
185 gst_object_replace ((GstObject **) elem_out_p, NULL);
190 g_list_foreach (pad->headers, (GFunc) gst_mini_object_unref, NULL);
191 g_list_free (pad->headers);
194 /* clear continued pages */
195 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
196 g_list_free (pad->continued);
197 pad->continued = NULL;
199 ogg_stream_reset (&pad->stream);
201 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
205 gst_ogg_pad_finalize (GObject * object)
207 GstOggPad *pad = GST_OGG_PAD (object);
209 ogg_stream_clear (&pad->stream);
211 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
215 static const GstFormat *
216 gst_ogg_pad_formats (GstPad * pad)
218 static GstFormat src_formats[] = {
219 GST_FORMAT_DEFAULT, /* time */
220 GST_FORMAT_TIME, /* granulepos */
223 static GstFormat sink_formats[] = {
225 GST_FORMAT_DEFAULT, /* bytes */
229 return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
234 static const GstEventMask *
235 gst_ogg_pad_event_masks (GstPad * pad)
237 static const GstEventMask src_event_masks[] = {
238 {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
242 return src_event_masks;
246 static const GstQueryType *
247 gst_ogg_pad_query_types (GstPad * pad)
249 static const GstQueryType query_types[] = {
259 gst_ogg_pad_getcaps (GstPad * pad)
261 return gst_caps_ref (GST_PAD_CAPS (pad));
265 gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
271 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
272 cur = GST_OGG_PAD (pad);
274 switch (GST_QUERY_TYPE (query)) {
275 case GST_QUERY_DURATION:
280 gst_query_parse_duration (query, &format, NULL);
281 /* can only get position in time */
282 if (format != GST_FORMAT_TIME)
286 /* we must return the total seekable length */
287 total_time = ogg->total_time;
289 /* in non-seek mode we can answer the query and we must return -1 */
293 gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
296 case GST_QUERY_SEEKING:
300 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
301 if (format == GST_FORMAT_TIME) {
302 gst_query_set_seeking (query, GST_FORMAT_TIME, ogg->seekable,
311 res = gst_pad_query_default (pad, query);
315 gst_object_unref (ogg);
322 GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
329 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
334 ogg = GST_OGG_DEMUX (element);
336 switch (GST_EVENT_TYPE (event)) {
338 /* can't seek if we are not seekable, FIXME could pass the
339 * seek query upstream after converting it to bytes using
340 * the average bitrate of the stream. */
341 if (!ogg->seekable) {
342 GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
346 /* now do the seek */
347 res = gst_ogg_demux_perform_seek (ogg, event);
348 gst_event_unref (event);
351 GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
360 GST_DEBUG_OBJECT (ogg, "error handling event");
361 gst_event_unref (event);
367 gst_ogg_pad_event (GstPad * pad, GstEvent * event)
373 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
374 cur = GST_OGG_PAD (pad);
376 switch (GST_EVENT_TYPE (event)) {
378 /* can't seek if we are not seekable, FIXME could pass the
379 * seek query upstream after converting it to bytes using
380 * the average bitrate of the stream. */
381 if (!ogg->seekable) {
382 GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
386 /* now do the seek */
387 res = gst_ogg_demux_perform_seek (ogg, event);
388 gst_event_unref (event);
391 res = gst_pad_event_default (pad, event);
395 gst_object_unref (ogg);
402 GST_DEBUG_OBJECT (ogg, "error handling event");
403 gst_event_unref (event);
410 gst_ogg_pad_reset (GstOggPad * pad)
412 ogg_stream_reset (&pad->stream);
414 GST_DEBUG_OBJECT (pad, "doing reset");
416 /* clear continued pages */
417 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
418 g_list_free (pad->continued);
419 pad->continued = NULL;
421 pad->last_ret = GST_FLOW_OK;
424 /* the filter function for selecting the elements we can use in
427 gst_ogg_demux_factory_filter (GstPluginFeature * feature, GstCaps * caps)
432 /* we only care about element factories */
433 if (!GST_IS_ELEMENT_FACTORY (feature))
436 klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
437 /* only demuxers and decoders can play */
438 if (strstr (klass, "Demux") == NULL &&
439 strstr (klass, "Decoder") == NULL && strstr (klass, "Parse") == NULL) {
443 /* only select elements with autoplugging rank */
444 rank = gst_plugin_feature_get_rank (feature);
445 if (rank < GST_RANK_MARGINAL)
448 GST_DEBUG ("checking factory %s", GST_PLUGIN_FEATURE_NAME (feature));
449 /* now see if it is compatible with the caps */
451 GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
452 const GList *templates;
455 /* get the templates from the element factory */
456 templates = gst_element_factory_get_static_pad_templates (factory);
458 for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
459 GstStaticPadTemplate *templ = walk->data;
461 /* we only care about the sink templates */
462 if (templ->direction == GST_PAD_SINK) {
467 /* try to intersect the caps with the caps of the template */
468 scaps = gst_static_caps_get (&templ->static_caps);
469 intersect = gst_caps_intersect (caps, scaps);
470 gst_caps_unref (scaps);
472 empty = gst_caps_is_empty (intersect);
473 gst_caps_unref (intersect);
475 /* check if the intersection is empty */
477 /* non empty intersection, we can use this element */
489 /* function used to sort element features */
491 compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
495 diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
498 return strcmp (gst_plugin_feature_get_name (f2),
499 gst_plugin_feature_get_name (f1));
502 /* called when the skeleton fishead is found. Caller ensures the packet is
503 * precisely the correct size; we don't re-check this here. */
505 gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet)
507 GstOggDemux *ogg = pad->ogg;
508 guint8 *data = packet->packet;
509 guint16 major, minor;
510 gint64 prestime_n, prestime_d;
511 gint64 basetime_n, basetime_d;
513 /* skip "fishead\0" */
515 major = GST_READ_UINT16_LE (data);
517 minor = GST_READ_UINT16_LE (data);
519 prestime_n = (gint64) GST_READ_UINT64_LE (data);
521 prestime_d = (gint64) GST_READ_UINT64_LE (data);
523 basetime_n = (gint64) GST_READ_UINT64_LE (data);
525 basetime_d = (gint64) GST_READ_UINT64_LE (data);
528 ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
529 ogg->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
530 ogg->have_fishead = TRUE;
531 pad->is_skeleton = TRUE;
532 pad->start_time = GST_CLOCK_TIME_NONE;
533 pad->first_granule = -1;
534 pad->first_time = GST_CLOCK_TIME_NONE;
535 GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %"
536 GST_TIME_FORMAT ", prestime: %" GST_TIME_FORMAT ")",
537 GST_TIME_ARGS (ogg->basetime), GST_TIME_ARGS (ogg->prestime));
540 /* function called when a skeleton fisbone is found. Caller ensures that
541 * the packet length is sufficient */
543 gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet)
545 GstOggDemux *ogg = pad->ogg;
546 GstOggPad *fisbone_pad;
547 gint64 start_granule;
549 guint8 *data = packet->packet;
551 /* skip "fisbone\0" */
553 /* skip headers offset */
555 serialno = GST_READ_UINT32_LE (data);
557 fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
559 if (fisbone_pad->have_fisbone)
563 fisbone_pad->have_fisbone = TRUE;
566 /* skip number of headers */
568 fisbone_pad->granulerate_n = GST_READ_UINT64_LE (data);
570 fisbone_pad->granulerate_d = GST_READ_UINT64_LE (data);
572 start_granule = GST_READ_UINT64_LE (data);
574 fisbone_pad->preroll = GST_READ_UINT32_LE (data);
576 fisbone_pad->granuleshift = GST_READ_UINT8 (data);
581 fisbone_pad->start_time = ogg->prestime - ogg->basetime;
583 GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed "
584 "(serialno: %08x start time: %" GST_TIME_FORMAT
585 " granulerate_n: %" G_GINT64_FORMAT " granulerate_d: %" G_GINT64_FORMAT
586 " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
587 serialno, GST_TIME_ARGS (fisbone_pad->start_time),
588 fisbone_pad->granulerate_n, fisbone_pad->granulerate_d,
589 fisbone_pad->preroll, fisbone_pad->granuleshift);
591 GST_WARNING_OBJECT (pad->ogg,
592 "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
597 /* function called to convert a granulepos to a timestamp */
599 gst_ogg_pad_query_convert (GstOggPad * pad, GstFormat src_format,
600 gint64 src_val, GstFormat * dest_format, gint64 * dest_val)
609 if (!pad->have_fisbone && pad->elem_pad == NULL)
612 switch (src_format) {
613 case GST_FORMAT_DEFAULT:
614 if (pad->have_fisbone && *dest_format == GST_FORMAT_TIME) {
615 *dest_val = gst_annodex_granule_to_time (src_val,
616 pad->granulerate_n, pad->granulerate_d, pad->granuleshift);
620 if (pad->elem_pad == NULL)
623 res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
624 dest_format, dest_val);
629 if (pad->elem_pad == NULL)
632 res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
633 dest_format, dest_val);
639 /* function called by the internal decoder elements when it outputs
640 * a buffer. We use it to get the first timestamp of the stream
643 gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer)
647 GstClockTime timestamp;
649 oggpad = gst_pad_get_element_private (pad);
650 ogg = GST_OGG_DEMUX (oggpad->ogg);
652 timestamp = GST_BUFFER_TIMESTAMP (buffer);
653 GST_DEBUG_OBJECT (oggpad, "received buffer from internal pad, TS=%"
654 GST_TIME_FORMAT "=%" G_GINT64_FORMAT, GST_TIME_ARGS (timestamp),
657 if (oggpad->start_time == GST_CLOCK_TIME_NONE) {
658 oggpad->start_time = timestamp;
659 GST_DEBUG_OBJECT (oggpad, "new start time: %" GST_TIME_FORMAT,
660 GST_TIME_ARGS (timestamp));
663 gst_buffer_unref (buffer);
669 internal_element_pad_added_cb (GstElement * element, GstPad * pad,
672 if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
673 if (!(gst_pad_link (pad, oggpad->elem_out) == GST_PAD_LINK_OK)) {
674 GST_ERROR ("Really couldn't find a valid pad");
676 oggpad->dynamic = FALSE;
677 g_signal_handler_disconnect (element, oggpad->padaddedid);
678 oggpad->padaddedid = 0;
682 /* runs typefind on the packet, which is assumed to be the first
683 * packet in the stream.
685 * Based on the type returned from the typefind function, an element
686 * is created to help in conversion between granulepos and timestamps
687 * so that we can do decent seeking.
690 gst_ogg_pad_typefind (GstOggPad * pad, ogg_packet * packet)
694 GstElement *element = NULL;
696 #ifndef GST_DISABLE_GST_DEBUG
697 GstOggDemux *ogg = pad->ogg;
700 if (GST_PAD_CAPS (pad) != NULL)
703 /* The ogg spec defines that the first packet of an ogg stream must identify
704 * the stream. Therefore ogg can use a simplified approach to typefinding
705 * and only needs to check the first packet */
706 buf = gst_buffer_new ();
707 GST_BUFFER_DATA (buf) = packet->packet;
708 GST_BUFFER_SIZE (buf) = packet->bytes;
709 GST_BUFFER_OFFSET (buf) = 0;
711 caps = gst_type_find_helper_for_buffer (GST_OBJECT (pad), buf, NULL);
712 gst_buffer_unref (buf);
715 GST_WARNING_OBJECT (ogg,
716 "couldn't find caps for stream with serial %08x", pad->serialno);
717 caps = gst_caps_new_simple ("application/octet-stream", NULL);
719 /* ogg requires you to use a decoder element to define the
720 * meaning of granulepos etc so we make one. We also do this if
721 * we are in the streaming mode to calculate the first timestamp. */
724 GST_LOG_OBJECT (ogg, "found caps: %" GST_PTR_FORMAT, caps);
726 /* first filter out the interesting element factories */
727 factories = gst_default_registry_feature_filter (
728 (GstPluginFeatureFilter) gst_ogg_demux_factory_filter, FALSE, caps);
730 /* sort them according to their ranks */
731 factories = g_list_sort (factories, (GCompareFunc) compare_ranks);
733 /* then pick the first factory to create an element */
736 gst_element_factory_create (GST_ELEMENT_FACTORY (factories->data),
739 GstPadTemplate *template;
742 gst_object_ref (element);
743 gst_object_sink (GST_OBJECT (element));
745 /* FIXME, it might not be named "sink" */
746 pad->elem_pad = gst_element_get_static_pad (element, "sink");
747 gst_element_set_state (element, GST_STATE_PAUSED);
748 template = gst_static_pad_template_get (&internaltemplate);
749 pad->elem_out = gst_pad_new_from_template (template, "internal");
750 gst_pad_set_chain_function (pad->elem_out, gst_ogg_pad_internal_chain);
751 gst_pad_set_element_private (pad->elem_out, pad);
752 gst_pad_set_active (pad->elem_out, TRUE);
753 gst_object_unref (template);
755 /* and this pad may not be named src.. */
756 /* And it might also not exist at this time... */
760 p = gst_element_get_static_pad (element, "src");
762 gst_pad_link (p, pad->elem_out);
763 gst_object_unref (p);
766 pad->padaddedid = g_signal_connect (G_OBJECT (element),
767 "pad-added", G_CALLBACK (internal_element_pad_added_cb), pad);
772 gst_plugin_feature_list_free (factories);
774 pad->element = element;
776 gst_pad_set_caps (GST_PAD (pad), caps);
777 gst_caps_unref (caps);
782 /* send packet to internal element */
784 gst_ogg_demux_chain_elem_pad (GstOggPad * pad, ogg_packet * packet)
789 #ifndef GST_DISABLE_GST_DEBUG
790 GstOggDemux *ogg = pad->ogg;
793 /* initialize our internal decoder with packets */
797 GST_DEBUG_OBJECT (ogg, "%p init decoder serial %08x", pad, pad->serialno);
799 buf = gst_buffer_new_and_alloc (packet->bytes);
800 memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
801 gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
802 GST_BUFFER_OFFSET (buf) = -1;
803 GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
805 ret = gst_pad_chain (pad->elem_pad, buf);
806 if (GST_FLOW_IS_FATAL (ret))
813 GST_WARNING_OBJECT (ogg,
814 "pad %p does not have elem_pad, no decoder ?", pad);
815 return GST_FLOW_ERROR;
819 GST_WARNING_OBJECT (ogg, "internal decoder error");
824 /* queue data, basically takes the packet, puts it in a buffer and store the
825 * buffer in the headers list.
828 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
832 #ifndef GST_DISABLE_GST_DEBUG
833 GstOggDemux *ogg = pad->ogg;
836 GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x", pad, pad->serialno);
838 buf = gst_buffer_new_and_alloc (packet->bytes);
839 memcpy (buf->data, packet->packet, packet->bytes);
840 GST_BUFFER_OFFSET (buf) = -1;
841 GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
842 pad->headers = g_list_append (pad->headers, buf);
848 /* send packet to internal element */
850 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
853 GstFlowReturn ret, cret;
854 GstOggDemux *ogg = pad->ogg;
859 GST_DEBUG_OBJECT (ogg,
860 "%p streaming to peer serial %08x", pad, pad->serialno);
863 gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad),
864 GST_BUFFER_OFFSET_NONE, packet->bytes, GST_PAD_CAPS (pad), &buf);
867 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
868 if (ret != GST_FLOW_OK)
871 /* copy packet in buffer */
872 memcpy (buf->data, packet->packet, packet->bytes);
874 GST_BUFFER_OFFSET (buf) = -1;
875 GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
877 /* Mark discont on the buffer */
879 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
880 pad->discont = FALSE;
883 ret = gst_pad_push (GST_PAD_CAST (pad), buf);
886 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
888 /* we're done with skeleton stuff */
889 if (pad->is_skeleton)
892 /* check if valid granulepos, then we can calculate the current
894 if (packet->granulepos < 0)
897 /* store current granule pos */
898 ogg->current_granule = packet->granulepos;
900 /* convert to time */
901 format = GST_FORMAT_TIME;
902 if (!gst_ogg_pad_query_convert (pad,
903 GST_FORMAT_DEFAULT, packet->granulepos, &format,
904 (gint64 *) & current_time))
907 /* convert to stream time */
908 if ((chain = pad->chain)) {
909 gint64 chain_start = 0;
911 if (chain->segment_start != GST_CLOCK_TIME_NONE)
912 chain_start = chain->segment_start;
914 current_time = current_time - chain_start + chain->begin_time;
917 /* and store as the current position */
918 gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time);
920 GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
921 GST_TIME_ARGS (current_time));
924 /* return combined flow result */
930 GST_DEBUG_OBJECT (ogg,
931 "%p could not get buffer from peer %08x, %d (%s), combined %d (%s)",
932 pad, pad->serialno, ret, gst_flow_get_name (ret),
933 cret, gst_flow_get_name (cret));
938 GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
943 /* submit a packet to the oggpad, this function will run the
944 * typefind code for the pad if this is the first packet for this
948 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
953 GstOggDemux *ogg = pad->ogg;
955 GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x", pad, pad->serialno);
957 if (!pad->have_type) {
958 if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE &&
959 !memcmp (packet->packet, "fishead\0", 8)) {
960 gst_ogg_pad_parse_skeleton_fishead (pad, packet);
962 gst_ogg_pad_typefind (pad, packet);
963 pad->have_type = TRUE;
966 if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
967 !memcmp (packet->packet, "fisbone\0", 8)) {
968 gst_ogg_pad_parse_skeleton_fisbone (pad, packet);
971 granule = packet->granulepos;
973 GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
974 ogg->current_granule = granule;
975 pad->current_granule = granule;
976 /* granulepos 0 and -1 are considered header packets.
977 * Note that since theora is busted, it can create non-header
978 * packets with 0 granulepos. We will correct for this when our
979 * internal decoder produced a frame and we don't have a
980 * granulepos because in that case the granulpos must have been 0 */
981 if (pad->first_granule == -1 && granule != 0) {
982 GST_DEBUG_OBJECT (ogg, "%p found first granulepos %" G_GINT64_FORMAT, pad,
984 pad->first_granule = granule;
988 if (granule != -1 && memcmp (packet->packet, "KW-DIRAC", 8) == 0) {
992 /* no start time known, stream to internal plugin to
993 * get time. always stream to the skeleton decoder */
994 if (pad->start_time == GST_CLOCK_TIME_NONE || pad->is_skeleton) {
995 ret = gst_ogg_demux_chain_elem_pad (pad, packet);
997 /* we know the start_time of the pad data, see if we
998 * can activate the complete chain if this is a dynamic
1000 if (pad->start_time != GST_CLOCK_TIME_NONE) {
1001 GstOggChain *chain = pad->chain;
1003 /* correction for busted ogg, if the internal decoder outputed
1004 * a timestamp but we did not get a granulepos, it must have
1005 * been 0 and the time is therefore also 0 */
1006 if (pad->first_granule == -1) {
1007 GST_DEBUG_OBJECT (ogg, "%p found start time without granulepos", pad);
1008 pad->first_granule = 0;
1009 pad->first_time = 0;
1012 /* check if complete chain has start time */
1013 if (chain == ogg->building_chain) {
1015 /* see if we have enough info to activate the chain, we have enough info
1016 * when all streams have a valid start time. */
1017 if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
1020 GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
1021 GST_TIME_ARGS (chain->segment_start));
1022 GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT,
1023 GST_TIME_ARGS (chain->segment_stop));
1024 GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT,
1025 GST_TIME_ARGS (chain->begin_time));
1027 /* create the newsegment event we are going to send out */
1028 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
1029 GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
1031 gst_event_set_seqnum (event, ogg->seqnum);
1033 gst_ogg_demux_activate_chain (ogg, chain, event);
1035 ogg->building_chain = NULL;
1040 /* if we are building a chain, store buffer for when we activate
1041 * it. This path is taken if we operate in streaming mode. */
1042 if (ogg->building_chain) {
1043 ret = gst_ogg_demux_queue_data (pad, packet);
1045 /* else we are completely streaming to the peer */
1047 ret = gst_ogg_demux_chain_peer (pad, packet);
1052 /* flush at most @npackets from the stream layer. All packets if
1055 static GstFlowReturn
1056 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1058 GstFlowReturn result = GST_FLOW_OK;
1059 gboolean done = FALSE;
1068 ret = ogg_stream_packetout (&pad->stream, &packet);
1071 GST_LOG_OBJECT (ogg, "packetout done");
1075 GST_LOG_OBJECT (ogg, "packetout discont");
1076 gst_ogg_chain_mark_discont (pad->chain);
1079 GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1080 result = gst_ogg_pad_submit_packet (pad, &packet);
1081 if (GST_FLOW_IS_FATAL (result))
1082 goto could_not_submit;
1085 GST_WARNING_OBJECT (ogg,
1086 "invalid return value %d for ogg_stream_packetout, resetting stream",
1088 gst_ogg_pad_reset (pad);
1093 done = (npackets == 0);
1101 GST_WARNING_OBJECT (ogg,
1102 "could not submit packet for stream %08x, error: %d", pad->serialno,
1104 gst_ogg_pad_reset (pad);
1109 /* submit a page to an oggpad, this function will then submit all
1110 * the packets in the page.
1112 static GstFlowReturn
1113 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
1115 GstFlowReturn result = GST_FLOW_OK;
1117 gboolean continued = FALSE;
1121 /* for negative rates we read pages backwards and must therefore be carefull
1122 * with continued pages */
1123 if (ogg->segment.rate < 0.0) {
1126 continued = ogg_page_continued (page);
1128 /* number of completed packets in the page */
1129 npackets = ogg_page_packets (page);
1131 /* page is not continued so it contains at least one packet start. It's
1132 * possible that no packet ends on this page (npackets == 0). In that
1133 * case, the next (continued) page(s) we kept contain the remainder of the
1134 * packets. We mark npackets=1 to make us start decoding the pages in the
1135 * remainder of the algorithm. */
1139 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
1141 if (npackets == 0) {
1142 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
1147 if (ogg_stream_pagein (&pad->stream, page) != 0)
1150 /* flush all packets in the stream layer, this might not give a packet if
1151 * the page had no packets finishing on the page (npackets == 0). */
1152 result = gst_ogg_pad_stream_out (pad, 0);
1154 if (pad->continued) {
1157 /* now send the continued pages to the stream layer */
1158 while (pad->continued) {
1159 ogg_page *p = (ogg_page *) pad->continued->data;
1161 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
1162 if (ogg_stream_pagein (&pad->stream, p) != 0)
1165 pad->continued = g_list_delete_link (pad->continued, pad->continued);
1168 gst_ogg_page_free (p);
1171 GST_LOG_OBJECT (ogg, "flushing last continued packet");
1172 /* flush 1 continued packet in the stream layer */
1173 result = gst_ogg_pad_stream_out (pad, 1);
1175 /* flush all remaining packets, we pushed them in the previous round.
1176 * We don't use _reset() because we still want to get the discont when
1177 * we submit a next page. */
1178 while (ogg_stream_packetout (&pad->stream, &packet) != 0);
1182 /* keep continued pages (only in reverse mode) */
1184 ogg_page *p = gst_ogg_page_copy (page);
1186 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
1187 pad->continued = g_list_prepend (pad->continued, p);
1194 GST_WARNING_OBJECT (ogg,
1195 "ogg stream choked on page (serial %08x), resetting stream",
1197 gst_ogg_pad_reset (pad);
1198 /* we continue to recover */
1204 static GstOggChain *
1205 gst_ogg_chain_new (GstOggDemux * ogg)
1207 GstOggChain *chain = g_new0 (GstOggChain, 1);
1209 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
1213 chain->have_bos = FALSE;
1214 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
1215 chain->begin_time = GST_CLOCK_TIME_NONE;
1216 chain->segment_start = GST_CLOCK_TIME_NONE;
1217 chain->segment_stop = GST_CLOCK_TIME_NONE;
1218 chain->total_time = GST_CLOCK_TIME_NONE;
1224 gst_ogg_chain_free (GstOggChain * chain)
1228 for (i = 0; i < chain->streams->len; i++) {
1229 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1231 gst_object_unref (pad);
1233 g_array_free (chain->streams, TRUE);
1238 gst_ogg_chain_mark_discont (GstOggChain * chain)
1242 for (i = 0; i < chain->streams->len; i++) {
1243 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1245 pad->discont = TRUE;
1250 gst_ogg_chain_reset (GstOggChain * chain)
1254 for (i = 0; i < chain->streams->len; i++) {
1255 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1257 gst_ogg_pad_reset (pad);
1262 gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno)
1268 GST_DEBUG_OBJECT (chain->ogg, "creating new stream %08lx in chain %p",
1271 ret = g_object_new (GST_TYPE_OGG_PAD, NULL);
1272 /* we own this one */
1273 gst_object_ref (ret);
1274 gst_object_sink (ret);
1276 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1277 ret->discont = TRUE;
1280 ret->ogg = chain->ogg;
1282 ret->serialno = serialno;
1283 if (ogg_stream_init (&ret->stream, serialno) != 0)
1286 name = g_strdup_printf ("serial_%08lx", serialno);
1287 gst_object_set_name (GST_OBJECT (ret), name);
1290 /* FIXME: either do something with it or remove it */
1291 list = gst_tag_list_new ();
1292 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1294 gst_tag_list_free (list);
1296 GST_DEBUG_OBJECT (chain->ogg,
1297 "created new ogg src %p for stream with serial %08lx", ret, serialno);
1299 g_array_append_val (chain->streams, ret);
1306 GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
1308 gst_object_unref (ret);
1314 gst_ogg_chain_get_stream (GstOggChain * chain, glong serialno)
1318 for (i = 0; i < chain->streams->len; i++) {
1319 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1321 if (pad->serialno == serialno)
1328 gst_ogg_chain_has_stream (GstOggChain * chain, glong serialno)
1330 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1333 #define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain))
1335 /* signals and args */
1348 static GstStaticPadTemplate ogg_demux_src_template_factory =
1349 GST_STATIC_PAD_TEMPLATE ("src_%d",
1352 GST_STATIC_CAPS_ANY);
1354 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1355 GST_STATIC_PAD_TEMPLATE ("sink",
1358 GST_STATIC_CAPS ("application/ogg; application/x-annodex")
1361 static void gst_ogg_demux_finalize (GObject * object);
1363 //static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad);
1364 //static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad);
1365 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1366 GstOggChain ** chain);
1367 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1368 GstOggChain * chain);
1370 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
1371 static void gst_ogg_demux_loop (GstOggPad * pad);
1372 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
1373 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
1374 static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
1376 static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
1378 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1379 GstStateChange transition);
1380 static void gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
1382 static void gst_ogg_print (GstOggDemux * demux);
1384 GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT);
1387 gst_ogg_demux_base_init (gpointer g_class)
1389 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1391 gst_element_class_set_details (element_class, &gst_ogg_demux_details);
1393 gst_element_class_add_pad_template (element_class,
1394 gst_static_pad_template_get (&ogg_demux_sink_template_factory));
1395 gst_element_class_add_pad_template (element_class,
1396 gst_static_pad_template_get (&ogg_demux_src_template_factory));
1400 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1402 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1403 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1405 gstelement_class->change_state = gst_ogg_demux_change_state;
1406 gstelement_class->send_event = gst_ogg_demux_receive_event;
1408 gobject_class->finalize = gst_ogg_demux_finalize;
1412 gst_ogg_demux_init (GstOggDemux * ogg, GstOggDemuxClass * g_class)
1414 /* create the sink pad */
1416 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
1419 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
1420 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
1421 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
1422 gst_pad_set_activatepull_function (ogg->sinkpad,
1423 gst_ogg_demux_sink_activate_pull);
1424 gst_pad_set_activatepush_function (ogg->sinkpad,
1425 gst_ogg_demux_sink_activate_push);
1426 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
1428 ogg->chain_lock = g_mutex_new ();
1429 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
1431 ogg->newsegment = NULL;
1435 gst_ogg_demux_finalize (GObject * object)
1439 ogg = GST_OGG_DEMUX (object);
1441 g_array_free (ogg->chains, TRUE);
1442 g_mutex_free (ogg->chain_lock);
1443 ogg_sync_clear (&ogg->sync);
1445 if (ogg->newsegment)
1446 gst_event_unref (ogg->newsegment);
1448 G_OBJECT_CLASS (parent_class)->finalize (object);
1452 gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
1457 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
1459 switch (GST_EVENT_TYPE (event)) {
1460 case GST_EVENT_NEWSEGMENT:
1462 GST_DEBUG_OBJECT (ogg, "got a new segment event");
1463 ogg_sync_reset (&ogg->sync);
1464 gst_event_unref (event);
1469 GST_DEBUG_OBJECT (ogg, "got an EOS event");
1470 res = gst_pad_event_default (pad, event);
1471 if (ogg->current_chain == NULL) {
1472 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
1473 ("can't get first chain"));
1478 res = gst_pad_event_default (pad, event);
1481 gst_object_unref (ogg);
1486 /* submit the given buffer to the ogg sync.
1488 * Returns the number of bytes submited.
1490 static GstFlowReturn
1491 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
1496 GstFlowReturn ret = GST_FLOW_OK;
1498 size = GST_BUFFER_SIZE (buffer);
1499 data = GST_BUFFER_DATA (buffer);
1501 GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
1502 if (G_UNLIKELY (size == 0))
1505 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
1506 if (G_UNLIKELY (oggbuffer == NULL))
1509 memcpy (oggbuffer, data, size);
1510 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
1514 gst_buffer_unref (buffer);
1521 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1522 (NULL), ("failed to get ogg sync buffer"));
1523 ret = GST_FLOW_ERROR;
1528 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1529 (NULL), ("failed to write %d bytes to the sync buffer", size));
1530 ret = GST_FLOW_ERROR;
1535 /* in random access mode this code updates the current read position
1536 * and resets the ogg sync buffer so that the next read will happen
1537 * from this new location.
1540 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
1542 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
1544 ogg->offset = offset;
1545 ogg->read_offset = offset;
1546 ogg_sync_reset (&ogg->sync);
1549 /* read more data from the current offset and submit to
1550 * the ogg sync layer.
1552 static GstFlowReturn
1553 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
1558 GST_LOG_OBJECT (ogg,
1559 "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
1560 ogg->read_offset, ogg->length, end_offset);
1562 if (end_offset > 0 && ogg->read_offset >= end_offset)
1563 goto boundary_reached;
1565 if (ogg->read_offset == ogg->length)
1568 ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
1569 if (ret != GST_FLOW_OK)
1572 ogg->read_offset += GST_BUFFER_SIZE (buffer);
1574 ret = gst_ogg_demux_submit_buffer (ogg, buffer);
1581 GST_LOG_OBJECT (ogg, "reached boundary");
1582 return GST_FLOW_LIMIT;
1586 GST_LOG_OBJECT (ogg, "reached EOS");
1587 return GST_FLOW_UNEXPECTED;
1591 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
1592 gst_flow_get_name (ret));
1597 /* Read the next page from the current offset.
1598 * boundary: number of bytes ahead we allow looking for;
1601 * @offset will contain the offset the next page starts at when this function
1602 * returns GST_FLOW_OK.
1604 * GST_FLOW_UNEXPECTED is returned on EOS.
1606 * GST_FLOW_LIMIT is returned when we did not find a page before the
1607 * boundary. If @boundary is -1, this is never returned.
1609 * Any other error returned while retrieving data from the peer is returned as
1612 static GstFlowReturn
1613 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary,
1616 gint64 end_offset = -1;
1619 GST_LOG_OBJECT (ogg,
1620 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
1621 G_GINT64_FORMAT, ogg->offset, boundary);
1624 end_offset = ogg->offset + boundary;
1629 if (end_offset > 0 && ogg->offset >= end_offset)
1630 goto boundary_reached;
1632 more = ogg_sync_pageseek (&ogg->sync, og);
1634 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
1637 /* skipped n bytes */
1638 ogg->offset -= more;
1639 GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT, more,
1641 } else if (more == 0) {
1642 /* we need more data */
1644 goto boundary_reached;
1646 GST_LOG_OBJECT (ogg, "need more data");
1647 ret = gst_ogg_demux_get_data (ogg, end_offset);
1648 if (ret != GST_FLOW_OK)
1651 gint64 res_offset = ogg->offset;
1653 /* got a page. Return the offset at the page beginning,
1654 advance the internal offset past the page end */
1656 *offset = res_offset;
1659 ogg->offset += more;
1661 GST_LOG_OBJECT (ogg,
1662 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
1663 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
1664 ogg_page_serialno (og), ogg->offset, ogg_page_granulepos (og));
1668 GST_LOG_OBJECT (ogg, "returning %d", ret);
1675 GST_LOG_OBJECT (ogg,
1676 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
1677 ogg->offset, end_offset);
1678 return GST_FLOW_LIMIT;
1682 /* from the current offset, find the previous page, seeking backwards
1683 * until we find the page.
1685 static GstFlowReturn
1686 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
1689 gint64 begin = ogg->offset;
1691 gint64 cur_offset = -1;
1693 GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
1695 while (cur_offset == -1) {
1700 /* seek CHUNKSIZE back */
1701 gst_ogg_demux_seek (ogg, begin);
1703 /* now continue reading until we run out of data, if we find a page
1704 * start, we save it. It might not be the final page as there could be
1705 * another page after this one. */
1706 while (ogg->offset < end) {
1710 gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
1711 /* we hit the upper limit, offset contains the last page start */
1712 if (ret == GST_FLOW_LIMIT) {
1713 GST_LOG_OBJECT (ogg, "hit limit");
1716 /* something went wrong */
1717 if (ret == GST_FLOW_UNEXPECTED) {
1719 GST_LOG_OBJECT (ogg, "got unexpected");
1720 } else if (ret != GST_FLOW_OK) {
1721 GST_LOG_OBJECT (ogg, "got error %d", ret);
1725 GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
1727 /* offset is next page start */
1728 cur_offset = new_offset;
1732 GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
1734 /* we have the offset. Actually snork and hold the page now */
1735 gst_ogg_demux_seek (ogg, cur_offset);
1736 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
1737 if (ret != GST_FLOW_OK) {
1738 GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
1740 /* this shouldn't be possible */
1745 *offset = cur_offset;
1751 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
1754 GstOggChain *chain = ogg->current_chain;
1759 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
1761 /* send EOS on all the pads */
1762 for (i = 0; i < chain->streams->len; i++) {
1763 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1766 if (pad->is_skeleton)
1769 event = gst_event_new_eos ();
1770 gst_event_set_seqnum (event, ogg->seqnum);
1771 gst_pad_push_event (GST_PAD_CAST (pad), event);
1773 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
1775 /* deactivate first */
1776 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
1778 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
1780 /* if we cannot seek back to the chain, we can destroy the chain
1782 if (!ogg->seekable) {
1783 gst_ogg_chain_free (chain);
1785 ogg->current_chain = NULL;
1791 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
1796 if (chain == ogg->current_chain) {
1798 gst_event_unref (event);
1802 /* FIXME, should not be called with NULL */
1803 if (chain != NULL) {
1804 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
1806 /* first add the pads */
1807 for (i = 0; i < chain->streams->len; i++) {
1810 pad = g_array_index (chain->streams, GstOggPad *, i);
1812 if (pad->is_skeleton)
1815 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
1818 pad->discont = TRUE;
1819 pad->last_ret = GST_FLOW_OK;
1821 /* activate first */
1822 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
1824 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
1828 /* after adding the new pads, remove the old pads */
1829 gst_ogg_demux_deactivate_current_chain (ogg);
1831 ogg->current_chain = chain;
1833 /* we are finished now */
1834 gst_element_no_more_pads (GST_ELEMENT (ogg));
1836 /* FIXME, must be sent from the streaming thread */
1838 gst_ogg_demux_send_event (ogg, event);
1840 gst_element_found_tags (GST_ELEMENT_CAST (ogg),
1841 gst_tag_list_new_full (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL));
1844 GST_DEBUG_OBJECT (ogg, "starting chain");
1846 /* then send out any queued buffers */
1847 for (i = 0; i < chain->streams->len; i++) {
1851 pad = g_array_index (chain->streams, GstOggPad *, i);
1853 for (headers = pad->headers; headers; headers = g_list_next (headers)) {
1854 GstBuffer *buffer = GST_BUFFER (headers->data);
1857 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
1858 pad->discont = FALSE;
1861 /* we don't care about the return value here */
1862 gst_pad_push (GST_PAD_CAST (pad), buffer);
1864 /* and free the headers */
1865 g_list_free (pad->headers);
1866 pad->headers = NULL;
1872 * do seek to time @position, return FALSE or chain and TRUE
1875 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
1876 gboolean accurate, GstOggChain ** rchain)
1879 GstOggChain *chain = NULL;
1881 gint64 begintime, endtime;
1889 position = segment->last_stop;
1891 /* first find the chain to search in */
1892 total = ogg->total_time;
1893 if (ogg->chains->len == 0)
1896 for (i = ogg->chains->len - 1; i >= 0; i--) {
1897 chain = g_array_index (ogg->chains, GstOggChain *, i);
1898 total -= chain->total_time;
1899 if (position >= total)
1903 begin = chain->offset;
1904 end = chain->end_offset;
1905 begintime = chain->begin_time;
1906 endtime = begintime + chain->total_time;
1907 target = position - total + begintime;
1909 if (segment->rate > 0.0) {
1910 /* FIXME, seek 2 seconds early to catch keyframes, better implement
1911 * keyframe detection. */
1912 if (target - 2 * GST_SECOND > begintime)
1913 target = target - (gint64) 2 *GST_SECOND;
1917 if (target + GST_SECOND < endtime)
1918 target = target + (gint64) GST_SECOND;
1925 GST_DEBUG_OBJECT (ogg,
1926 "seeking to %" GST_TIME_FORMAT " in chain %p",
1927 GST_TIME_ARGS (position), chain);
1928 GST_DEBUG_OBJECT (ogg,
1929 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, begin,
1931 GST_DEBUG_OBJECT (ogg,
1932 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
1933 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
1934 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
1936 /* perform the seek */
1937 while (begin < end) {
1940 if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
1943 /* take a (pretty decent) guess, avoiding overflow */
1944 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
1946 bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
1948 if (bisect <= begin)
1950 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
1952 gst_ogg_demux_seek (ogg, bisect);
1954 while (begin < end) {
1957 GST_DEBUG_OBJECT (ogg,
1958 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
1959 ", end %" G_GINT64_FORMAT, bisect, begin, end);
1961 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
1962 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
1965 if (ret == GST_FLOW_LIMIT) {
1966 /* we hit the upper limit, go back a bit */
1967 if (bisect <= begin + 1) {
1968 end = begin; /* found it */
1973 bisect -= CHUNKSIZE;
1974 if (bisect <= begin)
1977 gst_ogg_demux_seek (ogg, bisect);
1979 } else if (ret == GST_FLOW_OK) {
1980 /* found offset of next ogg page */
1982 GstClockTime granuletime;
1986 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
1988 granulepos = ogg_page_granulepos (&og);
1989 if (granulepos == -1) {
1990 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
1994 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
1995 if (pad == NULL || pad->is_skeleton)
1998 format = GST_FORMAT_TIME;
1999 if (!gst_ogg_pad_query_convert (pad,
2000 GST_FORMAT_DEFAULT, granulepos, &format,
2001 (gint64 *) & granuletime)) {
2002 GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
2003 granuletime = target;
2005 if (granuletime < pad->start_time)
2007 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
2008 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2010 granuletime -= pad->start_time;
2013 GST_DEBUG_OBJECT (ogg,
2014 "found page with granule %" G_GINT64_FORMAT " and time %"
2015 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2017 if (granuletime < target) {
2018 best = result; /* raw offset of packet with granulepos */
2019 begin = ogg->offset; /* raw offset of next page */
2020 begintime = granuletime;
2022 if (target - begintime > GST_SECOND)
2025 bisect = begin; /* *not* begin + 1 */
2027 if (bisect <= begin + 1) {
2028 end = begin; /* found it */
2030 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
2032 bisect -= CHUNKSIZE; /* an endless loop otherwise. */
2033 if (bisect <= begin)
2035 gst_ogg_demux_seek (ogg, bisect);
2038 endtime = granuletime;
2048 GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
2049 gst_ogg_demux_seek (ogg, best);
2056 GST_DEBUG_OBJECT (ogg, "no chains");
2061 GST_DEBUG_OBJECT (ogg, "got a seek error");
2066 /* does not take ownership of the event */
2068 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
2070 GstOggChain *chain = NULL;
2072 gboolean flush, accurate;
2076 GstSeekType cur_type, stop_type;
2083 GST_DEBUG_OBJECT (ogg, "seek with event");
2085 gst_event_parse_seek (event, &rate, &format, &flags,
2086 &cur_type, &cur, &stop_type, &stop);
2088 /* we can only seek on time */
2089 if (format != GST_FORMAT_TIME) {
2090 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
2093 seqnum = gst_event_get_seqnum (event);
2095 GST_DEBUG_OBJECT (ogg, "seek without event");
2099 seqnum = gst_util_seqnum_next ();
2102 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
2104 flush = flags & GST_SEEK_FLAG_FLUSH;
2105 accurate = flags & GST_SEEK_FLAG_ACCURATE;
2107 /* first step is to unlock the streaming thread if it is
2108 * blocked in a chain call, we do this by starting the flush. because
2109 * we cannot yet hold any streaming lock, we have to protect the chains
2110 * with their own lock. */
2114 tevent = gst_event_new_flush_start ();
2115 gst_event_set_seqnum (tevent, seqnum);
2117 gst_event_ref (tevent);
2118 gst_pad_push_event (ogg->sinkpad, tevent);
2120 GST_CHAIN_LOCK (ogg);
2121 for (i = 0; i < ogg->chains->len; i++) {
2122 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2125 for (j = 0; j < chain->streams->len; j++) {
2126 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
2128 gst_event_ref (tevent);
2129 gst_pad_push_event (GST_PAD (pad), tevent);
2132 GST_CHAIN_UNLOCK (ogg);
2134 gst_event_unref (tevent);
2136 gst_pad_pause_task (ogg->sinkpad);
2139 /* now grab the stream lock so that streaming cannot continue, for
2140 * non flushing seeks when the element is in PAUSED this could block
2142 GST_PAD_STREAM_LOCK (ogg->sinkpad);
2144 if (ogg->segment_running && !flush) {
2145 /* create the segment event to close the current segment */
2146 if ((chain = ogg->current_chain)) {
2148 gint64 chain_start = 0;
2150 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2151 chain_start = chain->segment_start;
2153 newseg = gst_event_new_new_segment (TRUE, ogg->segment.rate,
2154 GST_FORMAT_TIME, ogg->segment.start + chain_start,
2155 ogg->segment.last_stop + chain_start, ogg->segment.time);
2156 /* set the seqnum of the running segment */
2157 gst_event_set_seqnum (newseg, ogg->seqnum);
2159 /* send segment on old chain, FIXME, must be sent from streaming thread. */
2160 gst_ogg_demux_send_event (ogg, newseg);
2165 gst_segment_set_seek (&ogg->segment, rate, format, flags,
2166 cur_type, cur, stop_type, stop, &update);
2169 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
2170 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
2171 GST_TIME_ARGS (ogg->segment.stop));
2173 /* we need to stop flushing on the srcpad as we're going to use it
2174 * next. We can do this as we have the STREAM lock now. */
2176 tevent = gst_event_new_flush_stop ();
2177 gst_event_set_seqnum (tevent, seqnum);
2178 gst_pad_push_event (ogg->sinkpad, tevent);
2184 /* reset all ogg streams now, need to do this from within the lock to
2185 * make sure the streaming thread is not messing with the stream */
2186 for (i = 0; i < ogg->chains->len; i++) {
2187 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2189 gst_ogg_chain_reset (chain);
2193 /* for reverse we will already seek accurately */
2194 res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, &chain);
2196 /* seek failed, make sure we continue the current chain */
2198 GST_DEBUG_OBJECT (ogg, "seek failed");
2199 chain = ogg->current_chain;
2201 GST_DEBUG_OBJECT (ogg, "seek success");
2207 /* now we have a new position, prepare for streaming again */
2212 gint64 last_stop, begin_time;
2214 /* we have to send the flush to the old chain, not the new one */
2216 tevent = gst_event_new_flush_stop ();
2217 gst_event_set_seqnum (tevent, seqnum);
2218 gst_ogg_demux_send_event (ogg, tevent);
2221 /* we need this to see how far inside the chain we need to start */
2222 if (chain->begin_time != GST_CLOCK_TIME_NONE)
2223 begin_time = chain->begin_time;
2227 /* segment.start gives the start over all chains, we calculate the amount
2228 * of time into this chain we need to start */
2229 start = ogg->segment.start - begin_time;
2230 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2231 start += chain->segment_start;
2233 if ((stop = ogg->segment.stop) == -1)
2234 stop = ogg->segment.duration;
2236 /* segment.stop gives the stop time over all chains, calculate the amount of
2237 * time we need to stop in this chain */
2239 if (stop > begin_time)
2243 stop += chain->segment_start;
2244 /* we must stop when this chain ends and switch to the next chain to play
2245 * the remainder of the segment. */
2246 stop = MIN (stop, chain->segment_stop);
2249 last_stop = ogg->segment.last_stop - begin_time;
2250 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2251 last_stop += chain->segment_start;
2253 /* create the segment event we are going to send out */
2254 if (ogg->segment.rate >= 0.0)
2255 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2256 ogg->segment.format, last_stop, stop, ogg->segment.time);
2258 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2259 ogg->segment.format, start, last_stop, ogg->segment.time);
2261 gst_event_set_seqnum (event, seqnum);
2263 if (chain != ogg->current_chain) {
2264 /* switch to different chain, send segment on new chain */
2265 gst_ogg_demux_activate_chain (ogg, chain, event);
2267 /* mark discont and send segment on current chain */
2268 gst_ogg_chain_mark_discont (chain);
2269 /* This event should be sent from the streaming thread (sink pad task) */
2270 if (ogg->newsegment)
2271 gst_event_unref (ogg->newsegment);
2272 ogg->newsegment = event;
2275 /* notify start of new segment */
2276 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
2277 GstMessage *message;
2279 message = gst_message_new_segment_start (GST_OBJECT (ogg),
2280 GST_FORMAT_TIME, ogg->segment.last_stop);
2281 gst_message_set_seqnum (message, seqnum);
2283 gst_element_post_message (GST_ELEMENT (ogg), message);
2286 ogg->segment_running = TRUE;
2287 ogg->seqnum = seqnum;
2288 /* restart our task since it might have been stopped when we did the
2290 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
2294 /* streaming can continue now */
2295 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
2301 GST_DEBUG_OBJECT (ogg, "seek failed");
2306 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
2307 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
2312 /* finds each bitstream link one at a time using a bisection search
2313 * (has to begin by knowing the offset of the lb's initial page).
2314 * Recurses for each link so it can alloc the link storage after
2315 * finding them all, then unroll and fill the cache at the same time
2317 static GstFlowReturn
2318 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
2319 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
2321 gint64 endsearched = end;
2326 GstOggChain *nextchain;
2328 GST_LOG_OBJECT (ogg,
2329 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
2330 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
2332 /* the below guards against garbage seperating the last and
2333 * first pages of two links. */
2334 while (searched < endsearched) {
2337 if (endsearched - searched < CHUNKSIZE) {
2340 bisect = (searched + endsearched) / 2;
2343 gst_ogg_demux_seek (ogg, bisect);
2344 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
2346 if (ret == GST_FLOW_UNEXPECTED) {
2347 endsearched = bisect;
2348 } else if (ret == GST_FLOW_OK) {
2349 glong serial = ogg_page_serialno (&og);
2351 if (!gst_ogg_chain_has_stream (chain, serial)) {
2352 endsearched = bisect;
2355 searched = offset + og.header_len + og.body_len;
2361 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
2363 chain->end_offset = searched;
2364 ret = gst_ogg_demux_read_end_chain (ogg, chain);
2365 if (ret != GST_FLOW_OK)
2368 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
2370 gst_ogg_demux_seek (ogg, next);
2371 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
2372 if (ret == GST_FLOW_UNEXPECTED) {
2375 GST_LOG_OBJECT (ogg, "no next chain");
2376 } else if (ret != GST_FLOW_OK)
2379 if (searched < end && nextchain != NULL) {
2380 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
2381 end, nextchain, m + 1);
2382 if (ret != GST_FLOW_OK)
2385 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
2387 g_array_insert_val (ogg->chains, 0, chain);
2393 /* read a chain from the ogg file. This code will
2394 * read all BOS pages and will create and return a GstOggChain
2395 * structure with the results.
2397 * This function will also read N pages from each stream in the
2398 * chain and submit them to the decoders. When the decoder has
2399 * decoded the first buffer, we know the timestamp of the first
2400 * page in the chain.
2402 static GstFlowReturn
2403 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
2406 GstOggChain *chain = NULL;
2407 gint64 offset = ogg->offset;
2412 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
2414 /* first read the BOS pages, do typefind on them, create
2415 * the decoders, send data to the decoders. */
2420 ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
2421 if (ret != GST_FLOW_OK) {
2422 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
2425 if (!ogg_page_bos (&op)) {
2426 GST_WARNING_OBJECT (ogg, "page is not BOS page");
2427 /* if we did not find a chain yet, assume this is a bogus stream and
2430 ret = GST_FLOW_UNEXPECTED;
2434 if (chain == NULL) {
2435 chain = gst_ogg_chain_new (ogg);
2436 chain->offset = offset;
2439 serial = ogg_page_serialno (&op);
2440 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
2441 GST_WARNING_OBJECT (ogg, "found serial %08lx BOS page twice, ignoring",
2446 pad = gst_ogg_chain_new_stream (chain, serial);
2447 gst_ogg_pad_submit_page (pad, &op);
2450 if (ret != GST_FLOW_OK || chain == NULL) {
2451 if (ret == GST_FLOW_OK) {
2452 GST_WARNING_OBJECT (ogg, "no chain was found");
2453 ret = GST_FLOW_ERROR;
2454 } else if (ret != GST_FLOW_UNEXPECTED) {
2455 GST_WARNING_OBJECT (ogg, "failed to read chain");
2457 GST_DEBUG_OBJECT (ogg, "done reading chains");
2460 gst_ogg_chain_free (chain);
2467 chain->have_bos = TRUE;
2468 GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
2470 /* now read pages until we receive a buffer from each of the
2471 * stream decoders, this will tell us the timestamp of the
2472 * first packet in the chain then */
2474 /* save the offset to the first non bos page in the chain: if searching for
2475 * pad->first_time we read past the end of the chain, we'll seek back to this
2478 offset = ogg->offset;
2483 gboolean known_serial = FALSE;
2486 serial = ogg_page_serialno (&op);
2488 for (i = 0; i < chain->streams->len; i++) {
2489 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2491 GST_LOG_OBJECT (ogg, "serial %08x time %" GST_TIME_FORMAT, pad->serialno,
2492 GST_TIME_ARGS (pad->start_time));
2494 if (pad->serialno == serial) {
2495 known_serial = TRUE;
2497 /* submit the page now, this will fill in the start_time when the
2498 * internal decoder finds it */
2499 gst_ogg_pad_submit_page (pad, &op);
2501 if (!pad->is_skeleton && pad->start_time == -1 && ogg_page_eos (&op)) {
2502 /* got EOS on a pad before we could find its start_time.
2503 * We have no chance of finding a start_time for every pad so
2504 * stop searching for the other start_time(s).
2510 /* the timestamp will be filled in when we submit the pages */
2511 if (!pad->is_skeleton)
2512 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
2514 GST_LOG_OBJECT (ogg, "done %08x now %d", pad->serialno, done);
2517 /* we read a page not belonging to the current chain: seek back to the
2518 * beginning of the chain
2520 if (!known_serial) {
2521 GST_LOG_OBJECT (ogg, "unknown serial %08lx", serial);
2522 gst_ogg_demux_seek (ogg, offset);
2527 ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
2528 if (ret != GST_FLOW_OK)
2532 GST_LOG_OBJECT (ogg, "done reading chain");
2533 /* now we can fill in the missing info using queries */
2534 for (i = 0; i < chain->streams->len; i++) {
2535 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2538 if (pad->is_skeleton)
2541 GST_LOG_OBJECT (ogg, "convert first granule %" G_GUINT64_FORMAT " to time ",
2542 pad->first_granule);
2544 target = GST_FORMAT_TIME;
2545 if (!gst_ogg_pad_query_convert (pad,
2546 GST_FORMAT_DEFAULT, pad->first_granule, &target,
2547 (gint64 *) & pad->first_time)) {
2548 GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
2550 GST_LOG_OBJECT (ogg, "converted to first time %" GST_TIME_FORMAT,
2551 GST_TIME_ARGS (pad->first_time));
2554 pad->mode = GST_OGG_PAD_MODE_STREAMING;
2563 /* read the last pages from the ogg stream to get the final
2566 static GstFlowReturn
2567 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
2569 gint64 begin = chain->end_offset;
2571 gint64 last_granule = -1;
2572 GstOggPad *last_pad = NULL;
2575 gboolean done = FALSE;
2584 gst_ogg_demux_seek (ogg, begin);
2586 /* now continue reading until we run out of data, if we find a page
2587 * start, we save it. It might not be the final page as there could be
2588 * another page after this one. */
2589 while (ogg->offset < end) {
2590 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
2592 if (ret == GST_FLOW_LIMIT)
2594 if (ret != GST_FLOW_OK)
2597 for (i = 0; i < chain->streams->len; i++) {
2598 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2600 if (pad->is_skeleton)
2603 if (pad->serialno == ogg_page_serialno (&og)) {
2604 gint64 granulepos = ogg_page_granulepos (&og);
2606 if (last_granule < granulepos) {
2607 last_granule = granulepos;
2617 target = GST_FORMAT_TIME;
2618 if (last_granule == -1 || !gst_ogg_pad_query_convert (last_pad,
2619 GST_FORMAT_DEFAULT, last_granule, &target,
2620 (gint64 *) & chain->segment_stop)) {
2621 GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
2622 chain->segment_stop = GST_CLOCK_TIME_NONE;
2628 /* find a pad with a given serial number
2631 gst_ogg_demux_find_pad (GstOggDemux * ogg, int serialno)
2636 /* first look in building chain if any */
2637 if (ogg->building_chain) {
2638 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
2643 /* then look in current chain if any */
2644 if (ogg->current_chain) {
2645 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
2650 for (i = 0; i < ogg->chains->len; i++) {
2651 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2653 pad = gst_ogg_chain_get_stream (chain, serialno);
2660 /* find a chain with a given serial number
2662 static GstOggChain *
2663 gst_ogg_demux_find_chain (GstOggDemux * ogg, int serialno)
2667 pad = gst_ogg_demux_find_pad (ogg, serialno);
2674 /* returns TRUE if all streams have valid start time */
2676 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
2679 gboolean res = TRUE;
2681 chain->total_time = GST_CLOCK_TIME_NONE;
2682 chain->segment_start = G_MAXUINT64;
2684 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
2686 for (i = 0; i < chain->streams->len; i++) {
2687 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2689 if (pad->is_skeleton)
2692 /* can do this if the pad start time is not defined */
2693 if (pad->start_time == GST_CLOCK_TIME_NONE)
2696 chain->segment_start = MIN (chain->segment_start, pad->start_time);
2699 if (chain->segment_stop != GST_CLOCK_TIME_NONE
2700 && chain->segment_start != G_MAXUINT64)
2701 chain->total_time = chain->segment_stop - chain->segment_start;
2703 GST_DEBUG_OBJECT (ogg, "return %d", res);
2709 gst_ogg_demux_collect_info (GstOggDemux * ogg)
2713 /* collect all info */
2714 ogg->total_time = 0;
2716 for (i = 0; i < ogg->chains->len; i++) {
2717 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2719 chain->begin_time = ogg->total_time;
2721 gst_ogg_demux_collect_chain_info (ogg, chain);
2723 ogg->total_time += chain->total_time;
2725 gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time);
2728 /* find all the chains in the ogg file, this reads the first and
2729 * last page of the ogg stream, if they match then the ogg file has
2730 * just one chain, else we do a binary search for all chains.
2732 static GstFlowReturn
2733 gst_ogg_demux_find_chains (GstOggDemux * ogg)
2743 /* get peer to figure out length */
2744 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
2747 /* find length to read last page, we store this for later use. */
2748 format = GST_FORMAT_BYTES;
2749 res = gst_pad_query_duration (peer, &format, &ogg->length);
2750 gst_object_unref (peer);
2751 if (!res || ogg->length <= 0)
2754 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
2756 /* read chain from offset 0, this is the first chain of the
2758 gst_ogg_demux_seek (ogg, 0);
2759 ret = gst_ogg_demux_read_chain (ogg, &chain);
2760 if (ret != GST_FLOW_OK)
2761 goto no_first_chain;
2763 /* read page from end offset, we use this page to check if its serial
2764 * number is contained in the first chain. If this is the case then
2765 * this ogg is not a chained ogg and we can skip the scanning. */
2766 gst_ogg_demux_seek (ogg, ogg->length);
2767 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
2768 if (ret != GST_FLOW_OK)
2771 serialno = ogg_page_serialno (&og);
2773 if (!gst_ogg_chain_has_stream (chain, serialno)) {
2774 /* the last page is not in the first stream, this means we should
2775 * find all the chains in this chained ogg. */
2777 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
2780 /* we still call this function here but with an empty range so that
2781 * we can reuse the setup code in this routine. */
2783 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length,
2786 if (ret != GST_FLOW_OK)
2789 /* all fine, collect and print */
2790 gst_ogg_demux_collect_info (ogg);
2792 /* dump our chains and streams */
2793 gst_ogg_print (ogg);
2798 /*** error cases ***/
2801 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
2802 return GST_FLOW_NOT_LINKED;
2806 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
2807 return GST_FLOW_NOT_SUPPORTED;
2811 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
2812 return GST_FLOW_ERROR;
2816 GST_DEBUG_OBJECT (ogg, "can't get last page");
2818 gst_ogg_chain_free (chain);
2823 static GstFlowReturn
2824 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
2829 GstFlowReturn result = GST_FLOW_OK;
2831 serialno = ogg_page_serialno (page);
2832 granule = ogg_page_granulepos (page);
2834 GST_LOG_OBJECT (ogg,
2835 "processing ogg page (serial %08x, pageno %ld, granulepos %"
2836 G_GINT64_FORMAT ", bos %d)",
2837 serialno, ogg_page_pageno (page), granule, ogg_page_bos (page));
2839 if (ogg_page_bos (page)) {
2843 /* see if we know about the chain already */
2844 chain = gst_ogg_demux_find_chain (ogg, serialno);
2849 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2850 start = chain->segment_start;
2852 /* create the newsegment event we are going to send out */
2853 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2854 GST_FORMAT_TIME, start, chain->segment_stop, chain->begin_time);
2855 gst_event_set_seqnum (event, ogg->seqnum);
2857 GST_DEBUG_OBJECT (ogg,
2858 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
2859 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
2860 GST_TIME_ARGS (chain->segment_stop),
2861 GST_TIME_ARGS (chain->begin_time));
2863 /* activate it as it means we have a non-header, this will also deactivate
2864 * the currently running chain. */
2865 gst_ogg_demux_activate_chain (ogg, chain, event);
2866 pad = gst_ogg_demux_find_pad (ogg, serialno);
2868 GstClockTime chain_time;
2869 GstOggChain *current_chain;
2870 gint64 current_time;
2872 /* this can only happen in non-seekabe mode */
2876 current_chain = ogg->current_chain;
2877 current_time = ogg->segment.last_stop;
2879 /* time of new chain is current time */
2880 chain_time = current_time;
2882 if (ogg->building_chain == NULL) {
2883 GstOggChain *newchain;
2885 newchain = gst_ogg_chain_new (ogg);
2886 newchain->offset = 0;
2887 /* set new chain begin time aligned with end time of old chain */
2888 newchain->begin_time = chain_time;
2889 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
2890 GST_TIME_ARGS (chain_time));
2892 /* and this is the one we are building now */
2893 ogg->building_chain = newchain;
2895 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
2898 pad = gst_ogg_demux_find_pad (ogg, serialno);
2901 result = gst_ogg_pad_submit_page (pad, page);
2903 /* no pad. This means an ogg page without bos has been seen for this
2904 * serialno. we just ignore it but post a warning... */
2905 GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
2906 (NULL), ("unknown ogg pad for serial %08x detected", serialno));
2914 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2915 (NULL), ("unknown ogg chain for serial %08x detected", serialno));
2916 return GST_FLOW_ERROR;
2920 /* streaming mode, receive a buffer, parse it, create pads for
2921 * the serialno, submit pages and packets to the oggpads
2923 static GstFlowReturn
2924 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
2928 GstFlowReturn result = GST_FLOW_OK;
2930 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
2932 GST_DEBUG_OBJECT (ogg, "chain");
2933 result = gst_ogg_demux_submit_buffer (ogg, buffer);
2935 while (result == GST_FLOW_OK) {
2938 ret = ogg_sync_pageout (&ogg->sync, &page);
2940 /* need more data */
2943 /* discontinuity in the pages */
2944 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
2946 result = gst_ogg_demux_handle_page (ogg, &page);
2953 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
2955 GstOggChain *chain = ogg->current_chain;
2960 for (i = 0; i < chain->streams->len; i++) {
2961 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2963 gst_event_ref (event);
2964 GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
2965 gst_pad_push_event (GST_PAD (pad), event);
2968 gst_event_unref (event);
2971 static GstFlowReturn
2972 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
2977 /* store the value */
2978 pad->last_ret = ret;
2980 /* any other error that is not-linked can be returned right
2982 if (ret != GST_FLOW_NOT_LINKED)
2985 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
2986 chain = ogg->current_chain;
2990 for (i = 0; i < chain->streams->len; i++) {
2991 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
2993 ret = opad->last_ret;
2994 /* some other return value (must be SUCCESS but we can return
2995 * other values as well) */
2996 if (ret != GST_FLOW_NOT_LINKED)
2999 /* if we get here, all other pads were unlinked and we return
3000 * NOT_LINKED then */
3006 static GstFlowReturn
3007 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
3012 if (ogg->offset == ogg->length) {
3013 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
3014 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
3015 ret = GST_FLOW_UNEXPECTED;
3019 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
3020 ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
3021 if (ret != GST_FLOW_OK) {
3022 GST_LOG_OBJECT (ogg, "Failed pull_range");
3026 ogg->offset += GST_BUFFER_SIZE (buffer);
3028 if (G_UNLIKELY (ogg->newsegment)) {
3029 gst_ogg_demux_send_event (ogg, ogg->newsegment);
3030 ogg->newsegment = NULL;
3033 ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
3034 if (ret != GST_FLOW_OK) {
3035 GST_LOG_OBJECT (ogg, "Failed demux_chain");
3039 /* check for the end of the segment */
3040 if (ogg->segment.stop != -1 && ogg->segment.last_stop != -1) {
3041 if (ogg->segment.last_stop > ogg->segment.stop) {
3042 ret = GST_FLOW_UNEXPECTED;
3052 * We read the pages backwards and send the packets forwards. The first packet
3053 * in the page will be pushed with the DISCONT flag set.
3055 * Special care has to be taken for continued pages, which we can only decode
3056 * when we have the previous page(s).
3058 static GstFlowReturn
3059 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
3065 if (ogg->offset == 0) {
3066 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
3067 " == 0", ogg->offset);
3068 ret = GST_FLOW_UNEXPECTED;
3072 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
3073 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
3074 if (ret != GST_FLOW_OK)
3077 ogg->offset = offset;
3079 if (G_UNLIKELY (ogg->newsegment)) {
3080 gst_ogg_demux_send_event (ogg, ogg->newsegment);
3081 ogg->newsegment = NULL;
3084 ret = gst_ogg_demux_handle_page (ogg, &page);
3085 if (ret != GST_FLOW_OK)
3088 /* check for the end of the segment */
3089 if (ogg->segment.start != -1 && ogg->segment.last_stop != -1) {
3090 if (ogg->segment.last_stop <= ogg->segment.start) {
3091 ret = GST_FLOW_UNEXPECTED;
3099 /* random access code
3101 * - first find all the chains and streams by scanning the file.
3102 * - then get and chain buffers, just like the streaming case.
3103 * - when seeking, we can use the chain info to perform the seek.
3106 gst_ogg_demux_loop (GstOggPad * pad)
3112 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
3114 if (ogg->need_chains) {
3117 /* this is the only place where we write chains and thus need to lock. */
3118 GST_CHAIN_LOCK (ogg);
3119 ret = gst_ogg_demux_find_chains (ogg);
3120 GST_CHAIN_UNLOCK (ogg);
3121 if (ret != GST_FLOW_OK)
3122 goto chain_read_failed;
3124 ogg->need_chains = FALSE;
3126 GST_OBJECT_LOCK (ogg);
3127 ogg->running = TRUE;
3130 GST_OBJECT_UNLOCK (ogg);
3132 /* and seek to configured positions without FLUSH */
3133 res = gst_ogg_demux_perform_seek (ogg, event);
3135 gst_event_unref (event);
3141 if (ogg->segment.rate >= 0.0)
3142 ret = gst_ogg_demux_loop_forward (ogg);
3144 ret = gst_ogg_demux_loop_reverse (ogg);
3146 if (ret != GST_FLOW_OK)
3154 /* error was posted */
3159 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
3160 ("failed to start demuxing ogg"));
3161 ret = GST_FLOW_ERROR;
3166 const gchar *reason = gst_flow_get_name (ret);
3167 GstEvent *event = NULL;
3169 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
3170 ogg->segment_running = FALSE;
3171 gst_pad_pause_task (ogg->sinkpad);
3173 if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
3174 if (ret == GST_FLOW_UNEXPECTED) {
3175 /* perform EOS logic */
3176 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3178 GstMessage *message;
3180 /* for segment playback we need to post when (in stream time)
3181 * we stopped, this is either stop (when set) or the duration. */
3182 if ((stop = ogg->segment.stop) == -1)
3183 stop = ogg->segment.duration;
3185 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
3187 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
3189 gst_message_set_seqnum (message, ogg->seqnum);
3191 gst_element_post_message (GST_ELEMENT (ogg), message);
3193 /* normal playback, send EOS to all linked pads */
3194 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
3195 event = gst_event_new_eos ();
3198 GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
3199 (_("Internal data stream error.")),
3200 ("stream stopped, reason %s", reason));
3201 event = gst_event_new_eos ();
3204 gst_event_set_seqnum (event, ogg->seqnum);
3205 gst_ogg_demux_send_event (ogg, event);
3213 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
3217 gst_ogg_demux_deactivate_current_chain (ogg);
3219 GST_CHAIN_LOCK (ogg);
3220 for (i = 0; i < ogg->chains->len; i++) {
3221 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3223 gst_ogg_chain_free (chain);
3225 ogg->chains = g_array_set_size (ogg->chains, 0);
3226 GST_CHAIN_UNLOCK (ogg);
3229 /* this function is called when the pad is activated and should start
3232 * We check if we can do random access to decide if we work push or
3236 gst_ogg_demux_sink_activate (GstPad * sinkpad)
3238 if (gst_pad_check_pull_range (sinkpad)) {
3239 GST_DEBUG_OBJECT (sinkpad, "activating pull");
3240 return gst_pad_activate_pull (sinkpad, TRUE);
3242 GST_DEBUG_OBJECT (sinkpad, "activating push");
3243 return gst_pad_activate_push (sinkpad, TRUE);
3247 /* this function gets called when we activate ourselves in push mode.
3248 * We cannot seek (ourselves) in the stream */
3250 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
3254 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
3256 ogg->seekable = FALSE;
3261 /* this function gets called when we activate ourselves in pull mode.
3262 * We can perform random access to the resource and we start a task
3263 * to start reading */
3265 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
3269 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
3272 ogg->need_chains = TRUE;
3273 ogg->seekable = TRUE;
3275 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3278 return gst_pad_stop_task (sinkpad);
3282 static GstStateChangeReturn
3283 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
3286 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
3288 ogg = GST_OGG_DEMUX (element);
3290 switch (transition) {
3291 case GST_STATE_CHANGE_NULL_TO_READY:
3293 ogg->have_fishead = FALSE;
3294 ogg_sync_init (&ogg->sync);
3296 case GST_STATE_CHANGE_READY_TO_PAUSED:
3297 ogg_sync_reset (&ogg->sync);
3298 ogg->current_granule = -1;
3299 ogg->running = FALSE;
3300 ogg->segment_running = FALSE;
3301 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
3303 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3309 result = parent_class->change_state (element, transition);
3311 switch (transition) {
3312 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3314 case GST_STATE_CHANGE_PAUSED_TO_READY:
3315 gst_ogg_demux_clear_chains (ogg);
3316 GST_OBJECT_LOCK (ogg);
3317 ogg->running = FALSE;
3318 ogg->segment_running = FALSE;
3319 ogg->have_fishead = FALSE;
3320 GST_OBJECT_UNLOCK (ogg);
3322 case GST_STATE_CHANGE_READY_TO_NULL:
3323 ogg_sync_clear (&ogg->sync);
3332 gst_annodex_granule_to_time (gint64 granulepos, gint64 granulerate_n,
3333 gint64 granulerate_d, guint8 granuleshift)
3335 gint64 keyindex, keyoffset;
3339 if (granulepos == 0 || granulerate_n == 0 || granulerate_d == 0)
3342 if (granuleshift != 0) {
3343 keyindex = granulepos >> granuleshift;
3344 keyoffset = granulepos - (keyindex << granuleshift);
3345 granulepos = keyindex + keyoffset;
3348 /* GST_SECOND / (granulerate_n / granulerate_d) */
3349 granulerate = gst_util_uint64_scale (GST_SECOND,
3350 granulerate_d, granulerate_n);
3351 res = gst_util_uint64_scale (granulepos, granulerate, 1);
3356 gst_ogg_demux_plugin_init (GstPlugin * plugin)
3358 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
3359 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
3360 "ogg demuxer setup stage when parsing pipeline");
3363 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
3365 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
3366 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3369 return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
3370 GST_TYPE_OGG_DEMUX);
3373 /* prints all info about the element */
3374 #undef GST_CAT_DEFAULT
3375 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
3377 #ifdef GST_DISABLE_GST_DEBUG
3380 gst_ogg_print (GstOggDemux * ogg)
3385 #else /* !GST_DISABLE_GST_DEBUG */
3388 gst_ogg_print (GstOggDemux * ogg)
3392 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
3393 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
3394 GST_TIME_ARGS (ogg->total_time));
3396 for (i = 0; i < ogg->chains->len; i++) {
3397 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3399 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
3400 GST_INFO_OBJECT (ogg, " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT,
3401 chain->offset, chain->end_offset);
3402 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
3403 GST_TIME_ARGS (chain->begin_time));
3404 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
3405 GST_TIME_ARGS (chain->total_time));
3406 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
3407 GST_TIME_ARGS (chain->segment_start));
3408 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
3409 GST_TIME_ARGS (chain->segment_stop));
3411 for (j = 0; j < chain->streams->len; j++) {
3412 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
3414 GST_INFO_OBJECT (ogg, " stream %08x:", stream->serialno);
3415 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
3416 GST_TIME_ARGS (stream->start_time));
3417 GST_INFO_OBJECT (ogg, " first granulepos: %" G_GINT64_FORMAT,
3418 stream->first_granule);
3419 GST_INFO_OBJECT (ogg, " first time: %" GST_TIME_FORMAT,
3420 GST_TIME_ARGS (stream->first_time));
3424 #endif /* GST_DISABLE_GST_DEBUG */