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:
279 gst_query_parse_duration (query, &format, NULL);
280 /* can only get position in time */
281 if (format != GST_FORMAT_TIME)
284 /* can only return the total time position */
285 /* FIXME, return time for this specific stream */
286 gst_query_set_duration (query, GST_FORMAT_TIME, ogg->total_time);
289 case GST_QUERY_SEEKING:
293 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
294 if (format == GST_FORMAT_TIME) {
295 gst_query_set_seeking (query, GST_FORMAT_TIME, ogg->seekable,
304 res = gst_pad_query_default (pad, query);
308 gst_object_unref (ogg);
315 GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
322 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
327 ogg = GST_OGG_DEMUX (element);
329 switch (GST_EVENT_TYPE (event)) {
331 /* can't seek if we are not seekable, FIXME could pass the
332 * seek query upstream after converting it to bytes using
333 * the average bitrate of the stream. */
334 if (!ogg->seekable) {
335 GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
339 /* now do the seek */
340 res = gst_ogg_demux_perform_seek (ogg, event);
341 gst_event_unref (event);
344 GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
353 GST_DEBUG_OBJECT (ogg, "error handling event");
354 gst_event_unref (event);
360 gst_ogg_pad_event (GstPad * pad, GstEvent * event)
366 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
367 cur = GST_OGG_PAD (pad);
369 switch (GST_EVENT_TYPE (event)) {
371 /* can't seek if we are not seekable, FIXME could pass the
372 * seek query upstream after converting it to bytes using
373 * the average bitrate of the stream. */
374 if (!ogg->seekable) {
375 GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
379 /* now do the seek */
380 res = gst_ogg_demux_perform_seek (ogg, event);
381 gst_event_unref (event);
384 res = gst_pad_event_default (pad, event);
388 gst_object_unref (ogg);
395 GST_DEBUG_OBJECT (ogg, "error handling event");
396 gst_event_unref (event);
403 gst_ogg_pad_reset (GstOggPad * pad)
405 ogg_stream_reset (&pad->stream);
407 GST_DEBUG_OBJECT (pad, "doing reset");
409 /* clear continued pages */
410 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
411 g_list_free (pad->continued);
412 pad->continued = NULL;
414 pad->last_ret = GST_FLOW_OK;
417 /* the filter function for selecting the elements we can use in
420 gst_ogg_demux_factory_filter (GstPluginFeature * feature, GstCaps * caps)
425 /* we only care about element factories */
426 if (!GST_IS_ELEMENT_FACTORY (feature))
429 klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
430 /* only demuxers and decoders can play */
431 if (strstr (klass, "Demux") == NULL &&
432 strstr (klass, "Decoder") == NULL && strstr (klass, "Parse") == NULL) {
436 /* only select elements with autoplugging rank */
437 rank = gst_plugin_feature_get_rank (feature);
438 if (rank < GST_RANK_MARGINAL)
441 GST_DEBUG ("checking factory %s", GST_PLUGIN_FEATURE_NAME (feature));
442 /* now see if it is compatible with the caps */
444 GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
445 const GList *templates;
448 /* get the templates from the element factory */
449 templates = gst_element_factory_get_static_pad_templates (factory);
451 for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
452 GstStaticPadTemplate *templ = walk->data;
454 /* we only care about the sink templates */
455 if (templ->direction == GST_PAD_SINK) {
460 /* try to intersect the caps with the caps of the template */
461 scaps = gst_static_caps_get (&templ->static_caps);
462 intersect = gst_caps_intersect (caps, scaps);
463 gst_caps_unref (scaps);
465 empty = gst_caps_is_empty (intersect);
466 gst_caps_unref (intersect);
468 /* check if the intersection is empty */
470 /* non empty intersection, we can use this element */
482 /* function used to sort element features */
484 compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
488 diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
491 return strcmp (gst_plugin_feature_get_name (f2),
492 gst_plugin_feature_get_name (f1));
495 /* called when the skeleton fishead is found. Caller ensures the packet is
496 * precisely the correct size; we don't re-check this here. */
498 gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet)
500 GstOggDemux *ogg = pad->ogg;
501 guint8 *data = packet->packet;
502 guint16 major, minor;
503 gint64 prestime_n, prestime_d;
504 gint64 basetime_n, basetime_d;
506 /* skip "fishead\0" */
508 major = GST_READ_UINT16_LE (data);
510 minor = GST_READ_UINT16_LE (data);
512 prestime_n = (gint64) GST_READ_UINT64_LE (data);
514 prestime_d = (gint64) GST_READ_UINT64_LE (data);
516 basetime_n = (gint64) GST_READ_UINT64_LE (data);
518 basetime_d = (gint64) GST_READ_UINT64_LE (data);
521 ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
522 ogg->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
523 ogg->have_fishead = TRUE;
524 pad->is_skeleton = TRUE;
525 pad->start_time = GST_CLOCK_TIME_NONE;
526 pad->first_granule = -1;
527 pad->first_time = GST_CLOCK_TIME_NONE;
528 GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %"
529 GST_TIME_FORMAT ", prestime: %" GST_TIME_FORMAT ")",
530 GST_TIME_ARGS (ogg->basetime), GST_TIME_ARGS (ogg->prestime));
533 /* function called when a skeleton fisbone is found. Caller ensures that
534 * the packet length is sufficient */
536 gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet)
538 GstOggDemux *ogg = pad->ogg;
539 GstOggPad *fisbone_pad;
540 gint64 start_granule;
542 guint8 *data = packet->packet;
544 /* skip "fisbone\0" */
546 /* skip headers offset */
548 serialno = GST_READ_UINT32_LE (data);
550 fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
552 if (fisbone_pad->have_fisbone)
556 fisbone_pad->have_fisbone = TRUE;
559 /* skip number of headers */
561 fisbone_pad->granulerate_n = GST_READ_UINT64_LE (data);
563 fisbone_pad->granulerate_d = GST_READ_UINT64_LE (data);
565 start_granule = GST_READ_UINT64_LE (data);
567 fisbone_pad->preroll = GST_READ_UINT32_LE (data);
569 fisbone_pad->granuleshift = GST_READ_UINT8 (data);
574 fisbone_pad->start_time = ogg->prestime - ogg->basetime;
576 GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed "
577 "(serialno: %08x start time: %" GST_TIME_FORMAT
578 " granulerate_n: %" G_GINT64_FORMAT " granulerate_d: %" G_GINT64_FORMAT
579 " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
580 serialno, GST_TIME_ARGS (fisbone_pad->start_time),
581 fisbone_pad->granulerate_n, fisbone_pad->granulerate_d,
582 fisbone_pad->preroll, fisbone_pad->granuleshift);
584 GST_WARNING_OBJECT (pad->ogg,
585 "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
590 /* function called to convert a granulepos to a timestamp */
592 gst_ogg_pad_query_convert (GstOggPad * pad, GstFormat src_format,
593 gint64 src_val, GstFormat * dest_format, gint64 * dest_val)
602 if (!pad->have_fisbone && pad->elem_pad == NULL)
605 switch (src_format) {
606 case GST_FORMAT_DEFAULT:
607 if (pad->have_fisbone && *dest_format == GST_FORMAT_TIME) {
608 *dest_val = gst_annodex_granule_to_time (src_val,
609 pad->granulerate_n, pad->granulerate_d, pad->granuleshift);
613 if (pad->elem_pad == NULL)
616 res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
617 dest_format, dest_val);
622 if (pad->elem_pad == NULL)
625 res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
626 dest_format, dest_val);
632 /* function called by the internal decoder elements when it outputs
633 * a buffer. We use it to get the first timestamp of the stream
636 gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer)
640 GstClockTime timestamp;
642 oggpad = gst_pad_get_element_private (pad);
643 ogg = GST_OGG_DEMUX (oggpad->ogg);
645 timestamp = GST_BUFFER_TIMESTAMP (buffer);
646 GST_DEBUG_OBJECT (oggpad, "received buffer from internal pad, TS=%"
647 GST_TIME_FORMAT "=%" G_GINT64_FORMAT, GST_TIME_ARGS (timestamp),
650 if (oggpad->start_time == GST_CLOCK_TIME_NONE) {
651 oggpad->start_time = timestamp;
652 GST_DEBUG_OBJECT (oggpad, "new start time: %" GST_TIME_FORMAT,
653 GST_TIME_ARGS (timestamp));
656 gst_buffer_unref (buffer);
662 internal_element_pad_added_cb (GstElement * element, GstPad * pad,
665 if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
666 if (!(gst_pad_link (pad, oggpad->elem_out) == GST_PAD_LINK_OK)) {
667 GST_ERROR ("Really couldn't find a valid pad");
669 oggpad->dynamic = FALSE;
670 g_signal_handler_disconnect (element, oggpad->padaddedid);
671 oggpad->padaddedid = 0;
675 /* runs typefind on the packet, which is assumed to be the first
676 * packet in the stream.
678 * Based on the type returned from the typefind function, an element
679 * is created to help in conversion between granulepos and timestamps
680 * so that we can do decent seeking.
683 gst_ogg_pad_typefind (GstOggPad * pad, ogg_packet * packet)
687 GstElement *element = NULL;
689 #ifndef GST_DISABLE_GST_DEBUG
690 GstOggDemux *ogg = pad->ogg;
693 if (GST_PAD_CAPS (pad) != NULL)
696 /* The ogg spec defines that the first packet of an ogg stream must identify
697 * the stream. Therefore ogg can use a simplified approach to typefinding
698 * and only needs to check the first packet */
699 buf = gst_buffer_new ();
700 GST_BUFFER_DATA (buf) = packet->packet;
701 GST_BUFFER_SIZE (buf) = packet->bytes;
702 GST_BUFFER_OFFSET (buf) = 0;
704 caps = gst_type_find_helper_for_buffer (GST_OBJECT (pad), buf, NULL);
705 gst_buffer_unref (buf);
708 GST_WARNING_OBJECT (ogg,
709 "couldn't find caps for stream with serial %08x", pad->serialno);
710 caps = gst_caps_new_simple ("application/octet-stream", NULL);
712 /* ogg requires you to use a decoder element to define the
713 * meaning of granulepos etc so we make one. We also do this if
714 * we are in the streaming mode to calculate the first timestamp. */
717 GST_LOG_OBJECT (ogg, "found caps: %" GST_PTR_FORMAT, caps);
719 /* first filter out the interesting element factories */
720 factories = gst_default_registry_feature_filter (
721 (GstPluginFeatureFilter) gst_ogg_demux_factory_filter, FALSE, caps);
723 /* sort them according to their ranks */
724 factories = g_list_sort (factories, (GCompareFunc) compare_ranks);
726 /* then pick the first factory to create an element */
729 gst_element_factory_create (GST_ELEMENT_FACTORY (factories->data),
732 GstPadTemplate *template;
735 gst_object_ref (element);
736 gst_object_sink (GST_OBJECT (element));
738 /* FIXME, it might not be named "sink" */
739 pad->elem_pad = gst_element_get_static_pad (element, "sink");
740 gst_element_set_state (element, GST_STATE_PAUSED);
741 template = gst_static_pad_template_get (&internaltemplate);
742 pad->elem_out = gst_pad_new_from_template (template, "internal");
743 gst_pad_set_chain_function (pad->elem_out, gst_ogg_pad_internal_chain);
744 gst_pad_set_element_private (pad->elem_out, pad);
745 gst_pad_set_active (pad->elem_out, TRUE);
746 gst_object_unref (template);
748 /* and this pad may not be named src.. */
749 /* And it might also not exist at this time... */
753 p = gst_element_get_static_pad (element, "src");
755 gst_pad_link (p, pad->elem_out);
756 gst_object_unref (p);
759 pad->padaddedid = g_signal_connect (G_OBJECT (element),
760 "pad-added", G_CALLBACK (internal_element_pad_added_cb), pad);
765 gst_plugin_feature_list_free (factories);
767 pad->element = element;
769 gst_pad_set_caps (GST_PAD (pad), caps);
770 gst_caps_unref (caps);
775 /* send packet to internal element */
777 gst_ogg_demux_chain_elem_pad (GstOggPad * pad, ogg_packet * packet)
782 #ifndef GST_DISABLE_GST_DEBUG
783 GstOggDemux *ogg = pad->ogg;
786 /* initialize our internal decoder with packets */
790 GST_DEBUG_OBJECT (ogg, "%p init decoder serial %08x", pad, pad->serialno);
792 buf = gst_buffer_new_and_alloc (packet->bytes);
793 memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
794 gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
795 GST_BUFFER_OFFSET (buf) = -1;
796 GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
798 ret = gst_pad_chain (pad->elem_pad, buf);
799 if (GST_FLOW_IS_FATAL (ret))
806 GST_WARNING_OBJECT (ogg,
807 "pad %p does not have elem_pad, no decoder ?", pad);
808 return GST_FLOW_ERROR;
812 GST_WARNING_OBJECT (ogg, "internal decoder error");
817 /* queue data, basically takes the packet, puts it in a buffer and store the
818 * buffer in the headers list.
821 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
825 #ifndef GST_DISABLE_GST_DEBUG
826 GstOggDemux *ogg = pad->ogg;
829 GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x", pad, pad->serialno);
831 buf = gst_buffer_new_and_alloc (packet->bytes);
832 memcpy (buf->data, packet->packet, packet->bytes);
833 GST_BUFFER_OFFSET (buf) = -1;
834 GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
835 pad->headers = g_list_append (pad->headers, buf);
841 /* send packet to internal element */
843 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
846 GstFlowReturn ret, cret;
847 GstOggDemux *ogg = pad->ogg;
852 GST_DEBUG_OBJECT (ogg,
853 "%p streaming to peer serial %08x", pad, pad->serialno);
856 gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad),
857 GST_BUFFER_OFFSET_NONE, packet->bytes, GST_PAD_CAPS (pad), &buf);
860 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
861 if (ret != GST_FLOW_OK)
864 /* copy packet in buffer */
865 memcpy (buf->data, packet->packet, packet->bytes);
867 GST_BUFFER_OFFSET (buf) = -1;
868 GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
870 /* Mark discont on the buffer */
872 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
873 pad->discont = FALSE;
876 ret = gst_pad_push (GST_PAD_CAST (pad), buf);
879 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
881 /* we're done with skeleton stuff */
882 if (pad->is_skeleton)
885 /* check if valid granulepos, then we can calculate the current
887 if (packet->granulepos < 0)
890 /* store current granule pos */
891 ogg->current_granule = packet->granulepos;
893 /* convert to time */
894 format = GST_FORMAT_TIME;
895 if (!gst_ogg_pad_query_convert (pad,
896 GST_FORMAT_DEFAULT, packet->granulepos, &format,
897 (gint64 *) & current_time))
900 /* convert to stream time */
901 if ((chain = pad->chain)) {
902 gint64 chain_start = 0;
904 if (chain->segment_start != GST_CLOCK_TIME_NONE)
905 chain_start = chain->segment_start;
907 current_time = current_time - chain_start + chain->begin_time;
910 /* and store as the current position */
911 gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time);
913 GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
914 GST_TIME_ARGS (current_time));
917 /* return combined flow result */
923 GST_DEBUG_OBJECT (ogg,
924 "%p could not get buffer from peer %08x, %d (%s), combined %d (%s)",
925 pad, pad->serialno, ret, gst_flow_get_name (ret),
926 cret, gst_flow_get_name (cret));
931 GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
936 /* submit a packet to the oggpad, this function will run the
937 * typefind code for the pad if this is the first packet for this
941 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
946 GstOggDemux *ogg = pad->ogg;
948 GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x", pad, pad->serialno);
950 if (!pad->have_type) {
951 if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE &&
952 !memcmp (packet->packet, "fishead\0", 8)) {
953 gst_ogg_pad_parse_skeleton_fishead (pad, packet);
955 gst_ogg_pad_typefind (pad, packet);
956 pad->have_type = TRUE;
959 if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
960 !memcmp (packet->packet, "fisbone\0", 8)) {
961 gst_ogg_pad_parse_skeleton_fisbone (pad, packet);
964 granule = packet->granulepos;
966 GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
967 ogg->current_granule = granule;
968 pad->current_granule = granule;
969 /* granulepos 0 and -1 are considered header packets.
970 * Note that since theora is busted, it can create non-header
971 * packets with 0 granulepos. We will correct for this when our
972 * internal decoder produced a frame and we don't have a
973 * granulepos because in that case the granulpos must have been 0 */
974 if (pad->first_granule == -1 && granule != 0) {
975 GST_DEBUG_OBJECT (ogg, "%p found first granulepos %" G_GINT64_FORMAT, pad,
977 pad->first_granule = granule;
981 if (granule != -1 && memcmp (packet->packet, "KW-DIRAC", 8) == 0) {
985 /* no start time known, stream to internal plugin to
986 * get time. always stream to the skeleton decoder */
987 if (pad->start_time == GST_CLOCK_TIME_NONE || pad->is_skeleton) {
988 ret = gst_ogg_demux_chain_elem_pad (pad, packet);
990 /* we know the start_time of the pad data, see if we
991 * can activate the complete chain if this is a dynamic
993 if (pad->start_time != GST_CLOCK_TIME_NONE) {
994 GstOggChain *chain = pad->chain;
996 /* correction for busted ogg, if the internal decoder outputed
997 * a timestamp but we did not get a granulepos, it must have
998 * been 0 and the time is therefore also 0 */
999 if (pad->first_granule == -1) {
1000 GST_DEBUG_OBJECT (ogg, "%p found start time without granulepos", pad);
1001 pad->first_granule = 0;
1002 pad->first_time = 0;
1005 /* check if complete chain has start time */
1006 if (chain == ogg->building_chain) {
1008 /* see if we have enough info to activate the chain, we have enough info
1009 * when all streams have a valid start time. */
1010 if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
1013 GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
1014 GST_TIME_ARGS (chain->segment_start));
1015 GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT,
1016 GST_TIME_ARGS (chain->segment_stop));
1017 GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT,
1018 GST_TIME_ARGS (chain->begin_time));
1020 /* create the newsegment event we are going to send out */
1021 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
1022 GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
1024 gst_event_set_seqnum (event, ogg->seqnum);
1026 gst_ogg_demux_activate_chain (ogg, chain, event);
1028 ogg->building_chain = NULL;
1033 /* if we are building a chain, store buffer for when we activate
1034 * it. This path is taken if we operate in streaming mode. */
1035 if (ogg->building_chain) {
1036 ret = gst_ogg_demux_queue_data (pad, packet);
1038 /* else we are completely streaming to the peer */
1040 ret = gst_ogg_demux_chain_peer (pad, packet);
1045 /* flush at most @npackets from the stream layer. All packets if
1048 static GstFlowReturn
1049 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1051 GstFlowReturn result = GST_FLOW_OK;
1052 gboolean done = FALSE;
1061 ret = ogg_stream_packetout (&pad->stream, &packet);
1064 GST_LOG_OBJECT (ogg, "packetout done");
1068 GST_LOG_OBJECT (ogg, "packetout discont");
1069 gst_ogg_chain_mark_discont (pad->chain);
1072 GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1073 result = gst_ogg_pad_submit_packet (pad, &packet);
1074 if (GST_FLOW_IS_FATAL (result))
1075 goto could_not_submit;
1078 GST_WARNING_OBJECT (ogg,
1079 "invalid return value %d for ogg_stream_packetout, resetting stream",
1081 gst_ogg_pad_reset (pad);
1086 done = (npackets == 0);
1094 GST_WARNING_OBJECT (ogg,
1095 "could not submit packet for stream %08x, error: %d", pad->serialno,
1097 gst_ogg_pad_reset (pad);
1102 /* submit a page to an oggpad, this function will then submit all
1103 * the packets in the page.
1105 static GstFlowReturn
1106 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
1108 GstFlowReturn result = GST_FLOW_OK;
1110 gboolean continued = FALSE;
1114 /* for negative rates we read pages backwards and must therefore be carefull
1115 * with continued pages */
1116 if (ogg->segment.rate < 0.0) {
1119 continued = ogg_page_continued (page);
1121 /* number of completed packets in the page */
1122 npackets = ogg_page_packets (page);
1124 /* page is not continued so it contains at least one packet start. It's
1125 * possible that no packet ends on this page (npackets == 0). In that
1126 * case, the next (continued) page(s) we kept contain the remainder of the
1127 * packets. We mark npackets=1 to make us start decoding the pages in the
1128 * remainder of the algorithm. */
1132 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
1134 if (npackets == 0) {
1135 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
1140 if (ogg_stream_pagein (&pad->stream, page) != 0)
1143 /* flush all packets in the stream layer, this might not give a packet if
1144 * the page had no packets finishing on the page (npackets == 0). */
1145 result = gst_ogg_pad_stream_out (pad, 0);
1147 if (pad->continued) {
1150 /* now send the continued pages to the stream layer */
1151 while (pad->continued) {
1152 ogg_page *p = (ogg_page *) pad->continued->data;
1154 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
1155 if (ogg_stream_pagein (&pad->stream, p) != 0)
1158 pad->continued = g_list_delete_link (pad->continued, pad->continued);
1161 gst_ogg_page_free (p);
1164 GST_LOG_OBJECT (ogg, "flushing last continued packet");
1165 /* flush 1 continued packet in the stream layer */
1166 result = gst_ogg_pad_stream_out (pad, 1);
1168 /* flush all remaining packets, we pushed them in the previous round.
1169 * We don't use _reset() because we still want to get the discont when
1170 * we submit a next page. */
1171 while (ogg_stream_packetout (&pad->stream, &packet) != 0);
1175 /* keep continued pages (only in reverse mode) */
1177 ogg_page *p = gst_ogg_page_copy (page);
1179 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
1180 pad->continued = g_list_prepend (pad->continued, p);
1187 GST_WARNING_OBJECT (ogg,
1188 "ogg stream choked on page (serial %08x), resetting stream",
1190 gst_ogg_pad_reset (pad);
1191 /* we continue to recover */
1197 static GstOggChain *
1198 gst_ogg_chain_new (GstOggDemux * ogg)
1200 GstOggChain *chain = g_new0 (GstOggChain, 1);
1202 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
1206 chain->have_bos = FALSE;
1207 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
1208 chain->begin_time = GST_CLOCK_TIME_NONE;
1209 chain->segment_start = GST_CLOCK_TIME_NONE;
1210 chain->segment_stop = GST_CLOCK_TIME_NONE;
1211 chain->total_time = GST_CLOCK_TIME_NONE;
1217 gst_ogg_chain_free (GstOggChain * chain)
1221 for (i = 0; i < chain->streams->len; i++) {
1222 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1224 gst_object_unref (pad);
1226 g_array_free (chain->streams, TRUE);
1231 gst_ogg_chain_mark_discont (GstOggChain * chain)
1235 for (i = 0; i < chain->streams->len; i++) {
1236 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1238 pad->discont = TRUE;
1243 gst_ogg_chain_reset (GstOggChain * chain)
1247 for (i = 0; i < chain->streams->len; i++) {
1248 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1250 gst_ogg_pad_reset (pad);
1255 gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno)
1261 GST_DEBUG_OBJECT (chain->ogg, "creating new stream %08lx in chain %p",
1264 ret = g_object_new (GST_TYPE_OGG_PAD, NULL);
1265 /* we own this one */
1266 gst_object_ref (ret);
1267 gst_object_sink (ret);
1269 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1270 ret->discont = TRUE;
1273 ret->ogg = chain->ogg;
1275 ret->serialno = serialno;
1276 if (ogg_stream_init (&ret->stream, serialno) != 0)
1279 name = g_strdup_printf ("serial_%08lx", serialno);
1280 gst_object_set_name (GST_OBJECT (ret), name);
1283 /* FIXME: either do something with it or remove it */
1284 list = gst_tag_list_new ();
1285 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1287 gst_tag_list_free (list);
1289 GST_DEBUG_OBJECT (chain->ogg,
1290 "created new ogg src %p for stream with serial %08lx", ret, serialno);
1292 g_array_append_val (chain->streams, ret);
1299 GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
1301 gst_object_unref (ret);
1307 gst_ogg_chain_get_stream (GstOggChain * chain, glong serialno)
1311 for (i = 0; i < chain->streams->len; i++) {
1312 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1314 if (pad->serialno == serialno)
1321 gst_ogg_chain_has_stream (GstOggChain * chain, glong serialno)
1323 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1326 #define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain))
1328 /* signals and args */
1341 static GstStaticPadTemplate ogg_demux_src_template_factory =
1342 GST_STATIC_PAD_TEMPLATE ("src_%d",
1345 GST_STATIC_CAPS_ANY);
1347 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1348 GST_STATIC_PAD_TEMPLATE ("sink",
1351 GST_STATIC_CAPS ("application/ogg; application/x-annodex")
1354 static void gst_ogg_demux_finalize (GObject * object);
1356 //static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad);
1357 //static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad);
1358 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1359 GstOggChain ** chain);
1360 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1361 GstOggChain * chain);
1363 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
1364 static void gst_ogg_demux_loop (GstOggPad * pad);
1365 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
1366 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
1367 static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
1369 static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
1371 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1372 GstStateChange transition);
1373 static void gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
1375 static void gst_ogg_print (GstOggDemux * demux);
1377 GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT);
1380 gst_ogg_demux_base_init (gpointer g_class)
1382 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1384 gst_element_class_set_details (element_class, &gst_ogg_demux_details);
1386 gst_element_class_add_pad_template (element_class,
1387 gst_static_pad_template_get (&ogg_demux_sink_template_factory));
1388 gst_element_class_add_pad_template (element_class,
1389 gst_static_pad_template_get (&ogg_demux_src_template_factory));
1393 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1395 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1396 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1398 gstelement_class->change_state = gst_ogg_demux_change_state;
1399 gstelement_class->send_event = gst_ogg_demux_receive_event;
1401 gobject_class->finalize = gst_ogg_demux_finalize;
1405 gst_ogg_demux_init (GstOggDemux * ogg, GstOggDemuxClass * g_class)
1407 /* create the sink pad */
1409 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
1412 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
1413 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
1414 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
1415 gst_pad_set_activatepull_function (ogg->sinkpad,
1416 gst_ogg_demux_sink_activate_pull);
1417 gst_pad_set_activatepush_function (ogg->sinkpad,
1418 gst_ogg_demux_sink_activate_push);
1419 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
1421 ogg->chain_lock = g_mutex_new ();
1422 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
1424 ogg->newsegment = NULL;
1428 gst_ogg_demux_finalize (GObject * object)
1432 ogg = GST_OGG_DEMUX (object);
1434 g_array_free (ogg->chains, TRUE);
1435 g_mutex_free (ogg->chain_lock);
1436 ogg_sync_clear (&ogg->sync);
1438 if (ogg->newsegment)
1439 gst_event_unref (ogg->newsegment);
1441 G_OBJECT_CLASS (parent_class)->finalize (object);
1445 gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
1450 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
1452 switch (GST_EVENT_TYPE (event)) {
1453 case GST_EVENT_NEWSEGMENT:
1455 GST_DEBUG_OBJECT (ogg, "got a new segment event");
1456 ogg_sync_reset (&ogg->sync);
1457 gst_event_unref (event);
1462 GST_DEBUG_OBJECT (ogg, "got an EOS event");
1463 res = gst_pad_event_default (pad, event);
1464 if (ogg->current_chain == NULL) {
1465 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
1466 ("can't get first chain"));
1471 res = gst_pad_event_default (pad, event);
1474 gst_object_unref (ogg);
1479 /* submit the given buffer to the ogg sync.
1481 * Returns the number of bytes submited.
1483 static GstFlowReturn
1484 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
1489 GstFlowReturn ret = GST_FLOW_OK;
1491 size = GST_BUFFER_SIZE (buffer);
1492 data = GST_BUFFER_DATA (buffer);
1494 GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
1495 if (G_UNLIKELY (size == 0))
1498 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
1499 if (G_UNLIKELY (oggbuffer == NULL))
1502 memcpy (oggbuffer, data, size);
1503 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
1507 gst_buffer_unref (buffer);
1514 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1515 (NULL), ("failed to get ogg sync buffer"));
1516 ret = GST_FLOW_ERROR;
1521 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1522 (NULL), ("failed to write %d bytes to the sync buffer", size));
1523 ret = GST_FLOW_ERROR;
1528 /* in random access mode this code updates the current read position
1529 * and resets the ogg sync buffer so that the next read will happen
1530 * from this new location.
1533 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
1535 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
1537 ogg->offset = offset;
1538 ogg_sync_reset (&ogg->sync);
1541 /* read more data from the current offset and submit to
1542 * the ogg sync layer.
1544 static GstFlowReturn
1545 gst_ogg_demux_get_data (GstOggDemux * ogg)
1550 GST_LOG_OBJECT (ogg, "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
1551 ogg->offset, ogg->length);
1552 if (ogg->offset == ogg->length)
1555 ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
1556 if (ret != GST_FLOW_OK)
1559 ret = gst_ogg_demux_submit_buffer (ogg, buffer);
1566 GST_LOG_OBJECT (ogg, "reached EOS");
1567 return GST_FLOW_UNEXPECTED;
1571 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
1572 gst_flow_get_name (ret));
1577 /* Read the next page from the current offset.
1578 * boundary: number of bytes ahead we allow looking for;
1581 * @offset will contain the offset the next page starts at when this function
1582 * returns GST_FLOW_OK.
1584 * GST_FLOW_UNEXPECTED is returned on EOS.
1586 * GST_FLOW_LIMIT is returned when we did not find a page before the
1587 * boundary. If @boundary is -1, this is never returned.
1589 * Any other error returned while retrieving data from the peer is returned as
1592 static GstFlowReturn
1593 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary,
1596 gint64 end_offset = 0;
1599 GST_LOG_OBJECT (ogg,
1600 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
1601 G_GINT64_FORMAT, ogg->offset, boundary);
1604 end_offset = ogg->offset + boundary;
1609 if (boundary > 0 && ogg->offset >= end_offset)
1610 goto boundary_reached;
1612 more = ogg_sync_pageseek (&ogg->sync, og);
1614 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
1617 /* skipped n bytes */
1618 GST_LOG_OBJECT (ogg, "skipped %ld bytes", more);
1619 ogg->offset -= more;
1620 } else if (more == 0) {
1621 /* we need more data */
1623 goto boundary_reached;
1625 GST_LOG_OBJECT (ogg, "need more data");
1626 ret = gst_ogg_demux_get_data (ogg);
1627 if (ret != GST_FLOW_OK)
1630 gint64 res_offset = ogg->offset;
1632 /* got a page. Return the offset at the page beginning,
1633 advance the internal offset past the page end */
1635 *offset = res_offset;
1638 ogg->offset += more;
1639 /* need to reset as we do not keep track of the bytes we
1640 * sent to the sync layer */
1641 ogg_sync_reset (&ogg->sync);
1643 GST_LOG_OBJECT (ogg,
1644 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
1645 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
1646 ogg_page_serialno (og), ogg->offset, ogg_page_granulepos (og));
1650 GST_LOG_OBJECT (ogg, "returning %d", ret);
1657 GST_LOG_OBJECT (ogg,
1658 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
1659 ogg->offset, end_offset);
1660 return GST_FLOW_LIMIT;
1664 /* from the current offset, find the previous page, seeking backwards
1665 * until we find the page.
1667 static GstFlowReturn
1668 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
1671 gint64 begin = ogg->offset;
1673 gint64 cur_offset = -1;
1675 while (cur_offset == -1) {
1680 /* seek CHUNKSIZE back */
1681 gst_ogg_demux_seek (ogg, begin);
1683 /* now continue reading until we run out of data, if we find a page
1684 * start, we save it. It might not be the final page as there could be
1685 * another page after this one. */
1686 while (ogg->offset < end) {
1690 gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
1691 /* we hit the upper limit, offset contains the last page start */
1692 if (ret == GST_FLOW_LIMIT)
1694 /* something went wrong */
1695 if (ret == GST_FLOW_UNEXPECTED)
1697 else if (ret != GST_FLOW_OK)
1700 /* offset is next page start */
1701 cur_offset = new_offset;
1705 /* we have the offset. Actually snork and hold the page now */
1706 gst_ogg_demux_seek (ogg, cur_offset);
1707 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
1708 if (ret != GST_FLOW_OK)
1709 /* this shouldn't be possible */
1713 *offset = cur_offset;
1719 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
1722 GstOggChain *chain = ogg->current_chain;
1727 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
1729 /* send EOS on all the pads */
1730 for (i = 0; i < chain->streams->len; i++) {
1731 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1734 if (pad->is_skeleton)
1737 event = gst_event_new_eos ();
1738 gst_event_set_seqnum (event, ogg->seqnum);
1739 gst_pad_push_event (GST_PAD_CAST (pad), event);
1741 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
1743 /* deactivate first */
1744 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
1746 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
1748 /* if we cannot seek back to the chain, we can destroy the chain
1750 if (!ogg->seekable) {
1751 gst_ogg_chain_free (chain);
1753 ogg->current_chain = NULL;
1759 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
1764 if (chain == ogg->current_chain) {
1766 gst_event_unref (event);
1770 /* FIXME, should not be called with NULL */
1771 if (chain != NULL) {
1772 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
1774 /* first add the pads */
1775 for (i = 0; i < chain->streams->len; i++) {
1778 pad = g_array_index (chain->streams, GstOggPad *, i);
1780 if (pad->is_skeleton)
1783 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
1786 pad->discont = TRUE;
1787 pad->last_ret = GST_FLOW_OK;
1789 /* activate first */
1790 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
1792 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
1796 /* after adding the new pads, remove the old pads */
1797 gst_ogg_demux_deactivate_current_chain (ogg);
1799 ogg->current_chain = chain;
1801 /* we are finished now */
1802 gst_element_no_more_pads (GST_ELEMENT (ogg));
1804 /* FIXME, must be sent from the streaming thread */
1806 gst_ogg_demux_send_event (ogg, event);
1808 GST_DEBUG_OBJECT (ogg, "starting chain");
1810 /* then send out any queued buffers */
1811 for (i = 0; i < chain->streams->len; i++) {
1815 pad = g_array_index (chain->streams, GstOggPad *, i);
1817 for (headers = pad->headers; headers; headers = g_list_next (headers)) {
1818 GstBuffer *buffer = GST_BUFFER (headers->data);
1821 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
1822 pad->discont = FALSE;
1825 /* we don't care about the return value here */
1826 gst_pad_push (GST_PAD_CAST (pad), buffer);
1828 /* and free the headers */
1829 g_list_free (pad->headers);
1830 pad->headers = NULL;
1836 * do seek to time @position, return FALSE or chain and TRUE
1839 gst_ogg_demux_do_seek (GstOggDemux * ogg, gint64 position, gboolean accurate,
1840 GstOggChain ** rchain)
1842 GstOggChain *chain = NULL;
1844 gint64 begintime, endtime;
1852 /* first find the chain to search in */
1853 total = ogg->total_time;
1854 if (ogg->chains->len == 0)
1857 for (i = ogg->chains->len - 1; i >= 0; i--) {
1858 chain = g_array_index (ogg->chains, GstOggChain *, i);
1859 total -= chain->total_time;
1860 if (position >= total)
1864 begin = chain->offset;
1865 end = chain->end_offset;
1866 begintime = chain->begin_time;
1867 endtime = chain->begin_time + chain->total_time;
1868 target = position - total + begintime;
1870 /* FIXME, seek 4 seconds early to catch keyframes, better implement
1871 * keyframe detection. */
1872 target = target - (gint64) 4 *GST_SECOND;
1874 target = MAX (target, 0);
1877 GST_DEBUG_OBJECT (ogg,
1878 "seeking to %" GST_TIME_FORMAT " in chain %p",
1879 GST_TIME_ARGS (position), chain);
1880 GST_DEBUG_OBJECT (ogg,
1881 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, begin,
1883 GST_DEBUG_OBJECT (ogg,
1884 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
1885 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
1886 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
1888 /* perform the seek */
1889 while (begin < end) {
1892 if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
1895 /* take a (pretty decent) guess, avoiding overflow */
1896 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
1898 bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
1900 if (bisect <= begin)
1902 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
1904 gst_ogg_demux_seek (ogg, bisect);
1906 while (begin < end) {
1909 GST_DEBUG_OBJECT (ogg,
1910 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
1911 ", end %" G_GINT64_FORMAT, bisect, begin, end);
1913 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
1914 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
1917 if (ret == GST_FLOW_LIMIT) {
1918 /* we hit the upper limit, go back a bit */
1919 if (bisect <= begin + 1) {
1920 end = begin; /* found it */
1925 bisect -= CHUNKSIZE;
1926 if (bisect <= begin)
1929 gst_ogg_demux_seek (ogg, bisect);
1931 } else if (ret == GST_FLOW_OK) {
1932 /* found offset of next ogg page */
1934 GstClockTime granuletime;
1938 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
1940 granulepos = ogg_page_granulepos (&og);
1941 if (granulepos == -1) {
1942 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
1946 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
1947 if (pad == NULL || pad->is_skeleton)
1950 format = GST_FORMAT_TIME;
1951 if (!gst_ogg_pad_query_convert (pad,
1952 GST_FORMAT_DEFAULT, granulepos, &format,
1953 (gint64 *) & granuletime)) {
1954 GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
1955 granuletime = target;
1957 if (granuletime < pad->start_time)
1959 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
1960 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
1962 granuletime -= pad->start_time;
1965 GST_DEBUG_OBJECT (ogg,
1966 "found page with granule %" G_GINT64_FORMAT " and time %"
1967 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
1969 if (granuletime < target) {
1970 best = result; /* raw offset of packet with granulepos */
1971 begin = ogg->offset; /* raw offset of next page */
1972 begintime = granuletime;
1974 if (target - begintime > GST_SECOND)
1977 bisect = begin; /* *not* begin + 1 */
1979 if (bisect <= begin + 1) {
1980 end = begin; /* found it */
1982 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
1984 bisect -= CHUNKSIZE; /* an endless loop otherwise. */
1985 if (bisect <= begin)
1987 gst_ogg_demux_seek (ogg, bisect);
1990 endtime = granuletime;
2007 GST_DEBUG_OBJECT (ogg, "no chains");
2012 GST_DEBUG_OBJECT (ogg, "got a seek error");
2017 /* does not take ownership of the event */
2019 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
2021 GstOggChain *chain = NULL;
2023 gboolean flush, accurate;
2027 GstSeekType cur_type, stop_type;
2034 GST_DEBUG_OBJECT (ogg, "seek with event");
2036 gst_event_parse_seek (event, &rate, &format, &flags,
2037 &cur_type, &cur, &stop_type, &stop);
2039 /* we can only seek on time */
2040 if (format != GST_FORMAT_TIME) {
2041 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
2044 seqnum = gst_event_get_seqnum (event);
2046 GST_DEBUG_OBJECT (ogg, "seek without event");
2050 seqnum = gst_util_seqnum_next ();
2053 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
2055 flush = flags & GST_SEEK_FLAG_FLUSH;
2056 accurate = flags & GST_SEEK_FLAG_ACCURATE;
2058 /* first step is to unlock the streaming thread if it is
2059 * blocked in a chain call, we do this by starting the flush. because
2060 * we cannot yet hold any streaming lock, we have to protect the chains
2061 * with their own lock. */
2065 tevent = gst_event_new_flush_start ();
2066 gst_event_set_seqnum (tevent, seqnum);
2068 gst_event_ref (tevent);
2069 gst_pad_push_event (ogg->sinkpad, tevent);
2071 GST_CHAIN_LOCK (ogg);
2072 for (i = 0; i < ogg->chains->len; i++) {
2073 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2076 for (j = 0; j < chain->streams->len; j++) {
2077 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
2079 gst_event_ref (tevent);
2080 gst_pad_push_event (GST_PAD (pad), tevent);
2083 GST_CHAIN_UNLOCK (ogg);
2085 gst_event_unref (tevent);
2087 gst_pad_pause_task (ogg->sinkpad);
2090 /* now grab the stream lock so that streaming cannot continue, for
2091 * non flushing seeks when the element is in PAUSED this could block
2093 GST_PAD_STREAM_LOCK (ogg->sinkpad);
2095 if (ogg->segment_running && !flush) {
2096 /* create the segment event to close the current segment */
2097 if ((chain = ogg->current_chain)) {
2099 gint64 chain_start = 0;
2101 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2102 chain_start = chain->segment_start;
2104 newseg = gst_event_new_new_segment (TRUE, ogg->segment.rate,
2105 GST_FORMAT_TIME, ogg->segment.start + chain_start,
2106 ogg->segment.last_stop + chain_start, ogg->segment.time);
2107 /* set the seqnum of the running segment */
2108 gst_event_set_seqnum (newseg, ogg->seqnum);
2110 /* send segment on old chain, FIXME, must be sent from streaming thread. */
2111 gst_ogg_demux_send_event (ogg, newseg);
2116 gst_segment_set_seek (&ogg->segment, rate, format, flags,
2117 cur_type, cur, stop_type, stop, &update);
2120 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
2121 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
2122 GST_TIME_ARGS (ogg->segment.stop));
2124 /* we need to stop flushing on the srcpad as we're going to use it
2125 * next. We can do this as we have the STREAM lock now. */
2127 tevent = gst_event_new_flush_stop ();
2128 gst_event_set_seqnum (tevent, seqnum);
2129 gst_pad_push_event (ogg->sinkpad, tevent);
2135 /* reset all ogg streams now, need to do this from within the lock to
2136 * make sure the streaming thread is not messing with the stream */
2137 for (i = 0; i < ogg->chains->len; i++) {
2138 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2140 gst_ogg_chain_reset (chain);
2144 /* for reverse we will already seek accurately */
2148 res = gst_ogg_demux_do_seek (ogg, ogg->segment.last_stop, accurate, &chain);
2150 /* seek failed, make sure we continue the current chain */
2152 chain = ogg->current_chain;
2158 /* now we have a new position, prepare for streaming again */
2163 gint64 last_stop, begin_time;
2165 /* we have to send the flush to the old chain, not the new one */
2167 tevent = gst_event_new_flush_stop ();
2168 gst_event_set_seqnum (tevent, seqnum);
2169 gst_ogg_demux_send_event (ogg, tevent);
2172 /* we need this to see how far inside the chain we need to start */
2173 if (chain->begin_time != GST_CLOCK_TIME_NONE)
2174 begin_time = chain->begin_time;
2178 /* segment.start gives the start over all chains, we calculate the amount
2179 * of time into this chain we need to start */
2180 start = ogg->segment.start - begin_time;
2181 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2182 start += chain->segment_start;
2184 if ((stop = ogg->segment.stop) == -1)
2185 stop = ogg->segment.duration;
2187 /* segment.stop gives the stop time over all chains, calculate the amount of
2188 * time we need to stop in this chain */
2190 if (stop > begin_time)
2194 stop += chain->segment_start;
2195 /* we must stop when this chain ends and switch to the next chain to play
2196 * the remainder of the segment. */
2197 stop = MIN (stop, chain->segment_stop);
2200 last_stop = ogg->segment.last_stop - begin_time;
2201 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2202 last_stop += chain->segment_start;
2204 /* create the segment event we are going to send out */
2205 if (ogg->segment.rate >= 0.0)
2206 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2207 ogg->segment.format, last_stop, stop, ogg->segment.time);
2209 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2210 ogg->segment.format, start, last_stop, ogg->segment.time);
2212 gst_event_set_seqnum (event, seqnum);
2214 if (chain != ogg->current_chain) {
2215 /* switch to different chain, send segment on new chain */
2216 gst_ogg_demux_activate_chain (ogg, chain, event);
2218 /* mark discont and send segment on current chain */
2219 gst_ogg_chain_mark_discont (chain);
2220 /* This event should be sent from the streaming thread (sink pad task) */
2221 if (ogg->newsegment)
2222 gst_event_unref (ogg->newsegment);
2223 ogg->newsegment = event;
2226 /* notify start of new segment */
2227 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
2228 GstMessage *message;
2230 message = gst_message_new_segment_start (GST_OBJECT (ogg),
2231 GST_FORMAT_TIME, ogg->segment.last_stop);
2232 gst_message_set_seqnum (message, seqnum);
2234 gst_element_post_message (GST_ELEMENT (ogg), message);
2237 ogg->segment_running = TRUE;
2238 ogg->seqnum = seqnum;
2239 /* restart our task since it might have been stopped when we did the
2241 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
2245 /* streaming can continue now */
2246 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
2252 GST_DEBUG_OBJECT (ogg, "seek failed");
2257 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
2258 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
2263 /* finds each bitstream link one at a time using a bisection search
2264 * (has to begin by knowing the offset of the lb's initial page).
2265 * Recurses for each link so it can alloc the link storage after
2266 * finding them all, then unroll and fill the cache at the same time
2268 static GstFlowReturn
2269 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
2270 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
2272 gint64 endsearched = end;
2277 GstOggChain *nextchain;
2279 GST_LOG_OBJECT (ogg,
2280 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
2281 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
2283 /* the below guards against garbage seperating the last and
2284 * first pages of two links. */
2285 while (searched < endsearched) {
2288 if (endsearched - searched < CHUNKSIZE) {
2291 bisect = (searched + endsearched) / 2;
2294 gst_ogg_demux_seek (ogg, bisect);
2295 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
2297 if (ret == GST_FLOW_UNEXPECTED) {
2298 endsearched = bisect;
2299 } else if (ret == GST_FLOW_OK) {
2300 glong serial = ogg_page_serialno (&og);
2302 if (!gst_ogg_chain_has_stream (chain, serial)) {
2303 endsearched = bisect;
2306 searched = offset + og.header_len + og.body_len;
2312 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
2314 chain->end_offset = searched;
2315 ret = gst_ogg_demux_read_end_chain (ogg, chain);
2316 if (ret != GST_FLOW_OK)
2319 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
2321 gst_ogg_demux_seek (ogg, next);
2322 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
2323 if (ret == GST_FLOW_UNEXPECTED) {
2326 GST_LOG_OBJECT (ogg, "no next chain");
2327 } else if (ret != GST_FLOW_OK)
2330 if (searched < end && nextchain != NULL) {
2331 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
2332 end, nextchain, m + 1);
2333 if (ret != GST_FLOW_OK)
2336 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
2338 g_array_insert_val (ogg->chains, 0, chain);
2344 /* read a chain from the ogg file. This code will
2345 * read all BOS pages and will create and return a GstOggChain
2346 * structure with the results.
2348 * This function will also read N pages from each stream in the
2349 * chain and submit them to the decoders. When the decoder has
2350 * decoded the first buffer, we know the timestamp of the first
2351 * page in the chain.
2353 static GstFlowReturn
2354 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
2357 GstOggChain *chain = NULL;
2358 gint64 offset = ogg->offset;
2363 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
2365 /* first read the BOS pages, do typefind on them, create
2366 * the decoders, send data to the decoders. */
2371 ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
2372 if (ret != GST_FLOW_OK) {
2373 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
2376 if (!ogg_page_bos (&op)) {
2377 GST_WARNING_OBJECT (ogg, "page is not BOS page");
2381 if (chain == NULL) {
2382 chain = gst_ogg_chain_new (ogg);
2383 chain->offset = offset;
2386 serial = ogg_page_serialno (&op);
2387 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
2388 GST_WARNING_OBJECT (ogg, "found serial %08lx BOS page twice, ignoring",
2393 pad = gst_ogg_chain_new_stream (chain, serial);
2394 gst_ogg_pad_submit_page (pad, &op);
2397 if (ret != GST_FLOW_OK || chain == NULL) {
2398 if (ret == GST_FLOW_OK) {
2399 GST_WARNING_OBJECT (ogg, "no chain was found");
2400 ret = GST_FLOW_ERROR;
2401 } else if (ret != GST_FLOW_UNEXPECTED) {
2402 GST_WARNING_OBJECT (ogg, "failed to read chain");
2404 GST_DEBUG_OBJECT (ogg, "done reading chains");
2407 gst_ogg_chain_free (chain);
2414 chain->have_bos = TRUE;
2415 GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
2417 /* now read pages until we receive a buffer from each of the
2418 * stream decoders, this will tell us the timestamp of the
2419 * first packet in the chain then */
2421 /* save the offset to the first non bos page in the chain: if searching for
2422 * pad->first_time we read past the end of the chain, we'll seek back to this
2425 offset = ogg->offset;
2430 gboolean known_serial = FALSE;
2433 serial = ogg_page_serialno (&op);
2435 for (i = 0; i < chain->streams->len; i++) {
2436 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2438 GST_LOG_OBJECT (ogg, "serial %08x time %" GST_TIME_FORMAT, pad->serialno,
2439 GST_TIME_ARGS (pad->start_time));
2441 if (pad->serialno == serial) {
2442 known_serial = TRUE;
2444 /* submit the page now, this will fill in the start_time when the
2445 * internal decoder finds it */
2446 gst_ogg_pad_submit_page (pad, &op);
2448 if (!pad->is_skeleton && pad->start_time == -1 && ogg_page_eos (&op)) {
2449 /* got EOS on a pad before we could find its start_time.
2450 * We have no chance of finding a start_time for every pad so
2451 * stop searching for the other start_time(s).
2457 /* the timestamp will be filled in when we submit the pages */
2458 if (!pad->is_skeleton)
2459 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
2461 GST_LOG_OBJECT (ogg, "done %08x now %d", pad->serialno, done);
2464 /* we read a page not belonging to the current chain: seek back to the
2465 * beginning of the chain
2467 if (!known_serial) {
2468 GST_LOG_OBJECT (ogg, "unknown serial %08lx", serial);
2469 gst_ogg_demux_seek (ogg, offset);
2474 ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
2475 if (ret != GST_FLOW_OK)
2479 GST_LOG_OBJECT (ogg, "done reading chain");
2480 /* now we can fill in the missing info using queries */
2481 for (i = 0; i < chain->streams->len; i++) {
2482 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2485 if (pad->is_skeleton)
2488 GST_LOG_OBJECT (ogg, "convert first granule %" G_GUINT64_FORMAT " to time ",
2489 pad->first_granule);
2491 target = GST_FORMAT_TIME;
2492 if (!gst_ogg_pad_query_convert (pad,
2493 GST_FORMAT_DEFAULT, pad->first_granule, &target,
2494 (gint64 *) & pad->first_time)) {
2495 GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
2497 GST_LOG_OBJECT (ogg, "converted to first time %" GST_TIME_FORMAT,
2498 GST_TIME_ARGS (pad->first_time));
2501 pad->mode = GST_OGG_PAD_MODE_STREAMING;
2510 /* read the last pages from the ogg stream to get the final
2513 static GstFlowReturn
2514 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
2516 gint64 begin = chain->end_offset;
2518 gint64 last_granule = -1;
2519 GstOggPad *last_pad = NULL;
2522 gboolean done = FALSE;
2531 gst_ogg_demux_seek (ogg, begin);
2533 /* now continue reading until we run out of data, if we find a page
2534 * start, we save it. It might not be the final page as there could be
2535 * another page after this one. */
2536 while (ogg->offset < end) {
2537 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
2539 if (ret == GST_FLOW_LIMIT)
2541 if (ret != GST_FLOW_OK)
2544 for (i = 0; i < chain->streams->len; i++) {
2545 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2547 if (pad->is_skeleton)
2550 if (pad->serialno == ogg_page_serialno (&og)) {
2551 gint64 granulepos = ogg_page_granulepos (&og);
2553 if (last_granule < granulepos) {
2554 last_granule = granulepos;
2564 target = GST_FORMAT_TIME;
2565 if (last_granule == -1 || !gst_ogg_pad_query_convert (last_pad,
2566 GST_FORMAT_DEFAULT, last_granule, &target,
2567 (gint64 *) & chain->segment_stop)) {
2568 GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
2569 chain->segment_stop = GST_CLOCK_TIME_NONE;
2575 /* find a pad with a given serial number
2578 gst_ogg_demux_find_pad (GstOggDemux * ogg, int serialno)
2583 /* first look in building chain if any */
2584 if (ogg->building_chain) {
2585 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
2590 /* then look in current chain if any */
2591 if (ogg->current_chain) {
2592 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
2597 for (i = 0; i < ogg->chains->len; i++) {
2598 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2600 pad = gst_ogg_chain_get_stream (chain, serialno);
2607 /* find a chain with a given serial number
2609 static GstOggChain *
2610 gst_ogg_demux_find_chain (GstOggDemux * ogg, int serialno)
2614 pad = gst_ogg_demux_find_pad (ogg, serialno);
2621 /* returns TRUE if all streams have valid start time */
2623 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
2626 gboolean res = TRUE;
2628 chain->total_time = GST_CLOCK_TIME_NONE;
2629 chain->segment_start = G_MAXUINT64;
2631 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
2633 for (i = 0; i < chain->streams->len; i++) {
2634 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2636 if (pad->is_skeleton)
2639 /* can do this if the pad start time is not defined */
2640 if (pad->start_time == GST_CLOCK_TIME_NONE)
2643 chain->segment_start = MIN (chain->segment_start, pad->start_time);
2646 if (chain->segment_stop != GST_CLOCK_TIME_NONE
2647 && chain->segment_start != G_MAXUINT64)
2648 chain->total_time = chain->segment_stop - chain->segment_start;
2650 GST_DEBUG_OBJECT (ogg, "return %d", res);
2656 gst_ogg_demux_collect_info (GstOggDemux * ogg)
2660 /* collect all info */
2661 ogg->total_time = 0;
2663 for (i = 0; i < ogg->chains->len; i++) {
2664 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2666 chain->begin_time = ogg->total_time;
2668 gst_ogg_demux_collect_chain_info (ogg, chain);
2670 ogg->total_time += chain->total_time;
2672 gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time);
2675 /* find all the chains in the ogg file, this reads the first and
2676 * last page of the ogg stream, if they match then the ogg file has
2677 * just one chain, else we do a binary search for all chains.
2679 static GstFlowReturn
2680 gst_ogg_demux_find_chains (GstOggDemux * ogg)
2690 /* get peer to figure out length */
2691 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
2694 /* find length to read last page, we store this for later use. */
2695 format = GST_FORMAT_BYTES;
2696 res = gst_pad_query_duration (peer, &format, &ogg->length);
2697 gst_object_unref (peer);
2698 if (!res || ogg->length <= 0)
2701 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
2703 /* read chain from offset 0, this is the first chain of the
2705 gst_ogg_demux_seek (ogg, 0);
2706 ret = gst_ogg_demux_read_chain (ogg, &chain);
2707 if (ret != GST_FLOW_OK)
2708 goto no_first_chain;
2710 /* read page from end offset, we use this page to check if its serial
2711 * number is contained in the first chain. If this is the case then
2712 * this ogg is not a chained ogg and we can skip the scanning. */
2713 gst_ogg_demux_seek (ogg, ogg->length);
2714 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
2715 if (ret != GST_FLOW_OK)
2718 serialno = ogg_page_serialno (&og);
2720 if (!gst_ogg_chain_has_stream (chain, serialno)) {
2721 /* the last page is not in the first stream, this means we should
2722 * find all the chains in this chained ogg. */
2724 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
2727 /* we still call this function here but with an empty range so that
2728 * we can reuse the setup code in this routine. */
2730 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length,
2733 if (ret != GST_FLOW_OK)
2736 /* all fine, collect and print */
2737 gst_ogg_demux_collect_info (ogg);
2739 /* dump our chains and streams */
2740 gst_ogg_print (ogg);
2745 /*** error cases ***/
2748 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
2749 return GST_FLOW_NOT_LINKED;
2753 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
2754 return GST_FLOW_NOT_SUPPORTED;
2758 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
2759 return GST_FLOW_ERROR;
2763 GST_DEBUG_OBJECT (ogg, "can't get last page");
2765 gst_ogg_chain_free (chain);
2770 static GstFlowReturn
2771 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
2776 GstFlowReturn result = GST_FLOW_OK;
2778 serialno = ogg_page_serialno (page);
2779 granule = ogg_page_granulepos (page);
2781 GST_LOG_OBJECT (ogg,
2782 "processing ogg page (serial %08x, pageno %ld, granulepos %"
2783 G_GINT64_FORMAT ", bos %d)",
2784 serialno, ogg_page_pageno (page), granule, ogg_page_bos (page));
2786 if (ogg_page_bos (page)) {
2790 /* see if we know about the chain already */
2791 chain = gst_ogg_demux_find_chain (ogg, serialno);
2796 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2797 start = chain->segment_start;
2799 /* create the newsegment event we are going to send out */
2800 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2801 GST_FORMAT_TIME, start, chain->segment_stop, chain->begin_time);
2802 gst_event_set_seqnum (event, ogg->seqnum);
2804 GST_DEBUG_OBJECT (ogg,
2805 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
2806 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
2807 GST_TIME_ARGS (chain->segment_stop),
2808 GST_TIME_ARGS (chain->begin_time));
2810 /* activate it as it means we have a non-header, this will also deactivate
2811 * the currently running chain. */
2812 gst_ogg_demux_activate_chain (ogg, chain, event);
2813 pad = gst_ogg_demux_find_pad (ogg, serialno);
2815 GstClockTime chain_time;
2816 GstOggChain *current_chain;
2817 gint64 current_time;
2819 /* this can only happen in non-seekabe mode */
2823 current_chain = ogg->current_chain;
2824 current_time = ogg->segment.last_stop;
2826 /* time of new chain is current time */
2827 chain_time = current_time;
2829 if (ogg->building_chain == NULL) {
2830 GstOggChain *newchain;
2832 newchain = gst_ogg_chain_new (ogg);
2833 newchain->offset = 0;
2834 /* set new chain begin time aligned with end time of old chain */
2835 newchain->begin_time = chain_time;
2836 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
2837 GST_TIME_ARGS (chain_time));
2839 /* and this is the one we are building now */
2840 ogg->building_chain = newchain;
2842 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
2845 pad = gst_ogg_demux_find_pad (ogg, serialno);
2848 result = gst_ogg_pad_submit_page (pad, page);
2850 /* no pad, this is pretty fatal. This means an ogg page without bos
2851 * has been seen for this serialno. could just ignore it too... */
2859 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2860 (NULL), ("unknown ogg chain for serial %08x detected", serialno));
2861 return GST_FLOW_ERROR;
2865 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2866 (NULL), ("unknown ogg pad for serial %08x detected", serialno));
2867 return GST_FLOW_ERROR;
2871 /* streaming mode, receive a buffer, parse it, create pads for
2872 * the serialno, submit pages and packets to the oggpads
2874 static GstFlowReturn
2875 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
2879 GstFlowReturn result = GST_FLOW_OK;
2881 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
2883 GST_DEBUG_OBJECT (ogg, "chain");
2884 result = gst_ogg_demux_submit_buffer (ogg, buffer);
2886 while (result == GST_FLOW_OK) {
2889 ret = ogg_sync_pageout (&ogg->sync, &page);
2891 /* need more data */
2894 /* discontinuity in the pages */
2895 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
2897 result = gst_ogg_demux_handle_page (ogg, &page);
2904 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
2906 GstOggChain *chain = ogg->current_chain;
2911 for (i = 0; i < chain->streams->len; i++) {
2912 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2914 gst_event_ref (event);
2915 GST_DEBUG_OBJECT (ogg, "Pushing event on pad %p", pad);
2916 gst_pad_push_event (GST_PAD (pad), event);
2919 gst_event_unref (event);
2922 static GstFlowReturn
2923 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
2928 /* store the value */
2929 pad->last_ret = ret;
2931 /* any other error that is not-linked can be returned right
2933 if (ret != GST_FLOW_NOT_LINKED)
2936 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
2937 chain = ogg->current_chain;
2941 for (i = 0; i < chain->streams->len; i++) {
2942 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
2944 ret = opad->last_ret;
2945 /* some other return value (must be SUCCESS but we can return
2946 * other values as well) */
2947 if (ret != GST_FLOW_NOT_LINKED)
2950 /* if we get here, all other pads were unlinked and we return
2951 * NOT_LINKED then */
2957 static GstFlowReturn
2958 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
2963 if (ogg->offset == ogg->length) {
2964 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
2965 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
2966 ret = GST_FLOW_UNEXPECTED;
2970 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
2971 ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
2972 if (ret != GST_FLOW_OK) {
2973 GST_LOG_OBJECT (ogg, "Failed pull_range");
2977 ogg->offset += GST_BUFFER_SIZE (buffer);
2979 if (G_UNLIKELY (ogg->newsegment)) {
2980 gst_ogg_demux_send_event (ogg, ogg->newsegment);
2981 ogg->newsegment = NULL;
2984 ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
2985 if (ret != GST_FLOW_OK) {
2986 GST_LOG_OBJECT (ogg, "Failed demux_chain");
2990 /* check for the end of the segment */
2991 if (ogg->segment.stop != -1 && ogg->segment.last_stop != -1) {
2992 if (ogg->segment.last_stop > ogg->segment.stop) {
2993 ret = GST_FLOW_UNEXPECTED;
3003 * We read the pages backwards and send the packets forwards. The first packet
3004 * in the page will be pushed with the DISCONT flag set.
3006 * Special care has to be taken for continued pages, which we can only decode
3007 * when we have the previous page(s).
3009 static GstFlowReturn
3010 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
3016 if (ogg->offset == 0) {
3017 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
3018 " == 0", ogg->offset);
3019 ret = GST_FLOW_UNEXPECTED;
3023 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
3024 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
3025 if (ret != GST_FLOW_OK)
3028 ogg->offset = offset;
3030 if (G_UNLIKELY (ogg->newsegment)) {
3031 gst_ogg_demux_send_event (ogg, ogg->newsegment);
3032 ogg->newsegment = NULL;
3035 ret = gst_ogg_demux_handle_page (ogg, &page);
3036 if (ret != GST_FLOW_OK)
3039 /* check for the end of the segment */
3040 if (ogg->segment.start != -1 && ogg->segment.last_stop != -1) {
3041 if (ogg->segment.last_stop <= ogg->segment.start) {
3042 ret = GST_FLOW_UNEXPECTED;
3050 /* random access code
3052 * - first find all the chains and streams by scanning the file.
3053 * - then get and chain buffers, just like the streaming case.
3054 * - when seeking, we can use the chain info to perform the seek.
3057 gst_ogg_demux_loop (GstOggPad * pad)
3063 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
3065 if (ogg->need_chains) {
3068 /* this is the only place where we write chains and thus need to lock. */
3069 GST_CHAIN_LOCK (ogg);
3070 ret = gst_ogg_demux_find_chains (ogg);
3071 GST_CHAIN_UNLOCK (ogg);
3072 if (ret != GST_FLOW_OK)
3073 goto chain_read_failed;
3075 ogg->need_chains = FALSE;
3077 GST_OBJECT_LOCK (ogg);
3078 ogg->running = TRUE;
3081 GST_OBJECT_UNLOCK (ogg);
3083 /* and seek to configured positions without FLUSH */
3084 res = gst_ogg_demux_perform_seek (ogg, event);
3086 gst_event_unref (event);
3092 if (ogg->segment.rate >= 0.0)
3093 ret = gst_ogg_demux_loop_forward (ogg);
3095 ret = gst_ogg_demux_loop_reverse (ogg);
3097 if (ret != GST_FLOW_OK)
3105 /* error was posted */
3110 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
3111 ("failed to start demuxing ogg"));
3112 ret = GST_FLOW_ERROR;
3117 const gchar *reason = gst_flow_get_name (ret);
3118 GstEvent *event = NULL;
3120 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
3121 ogg->segment_running = FALSE;
3122 gst_pad_pause_task (ogg->sinkpad);
3124 if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
3125 if (ret == GST_FLOW_UNEXPECTED) {
3126 /* perform EOS logic */
3127 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3129 GstMessage *message;
3131 /* for segment playback we need to post when (in stream time)
3132 * we stopped, this is either stop (when set) or the duration. */
3133 if ((stop = ogg->segment.stop) == -1)
3134 stop = ogg->segment.duration;
3136 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
3138 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
3140 gst_message_set_seqnum (message, ogg->seqnum);
3142 gst_element_post_message (GST_ELEMENT (ogg), message);
3144 /* normal playback, send EOS to all linked pads */
3145 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
3146 event = gst_event_new_eos ();
3149 GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
3150 (_("Internal data stream error.")),
3151 ("stream stopped, reason %s", reason));
3152 event = gst_event_new_eos ();
3155 gst_event_set_seqnum (event, ogg->seqnum);
3156 gst_ogg_demux_send_event (ogg, event);
3164 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
3168 gst_ogg_demux_deactivate_current_chain (ogg);
3170 GST_CHAIN_LOCK (ogg);
3171 for (i = 0; i < ogg->chains->len; i++) {
3172 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3174 gst_ogg_chain_free (chain);
3176 ogg->chains = g_array_set_size (ogg->chains, 0);
3177 GST_CHAIN_UNLOCK (ogg);
3180 /* this function is called when the pad is activated and should start
3183 * We check if we can do random access to decide if we work push or
3187 gst_ogg_demux_sink_activate (GstPad * sinkpad)
3189 if (gst_pad_check_pull_range (sinkpad)) {
3190 GST_DEBUG_OBJECT (sinkpad, "activating pull");
3191 return gst_pad_activate_pull (sinkpad, TRUE);
3193 GST_DEBUG_OBJECT (sinkpad, "activating push");
3194 return gst_pad_activate_push (sinkpad, TRUE);
3198 /* this function gets called when we activate ourselves in push mode.
3199 * We cannot seek (ourselves) in the stream */
3201 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
3205 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
3207 ogg->seekable = FALSE;
3212 /* this function gets called when we activate ourselves in pull mode.
3213 * We can perform random access to the resource and we start a task
3214 * to start reading */
3216 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
3220 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
3223 ogg->need_chains = TRUE;
3224 ogg->seekable = TRUE;
3226 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3229 return gst_pad_stop_task (sinkpad);
3233 static GstStateChangeReturn
3234 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
3237 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
3239 ogg = GST_OGG_DEMUX (element);
3241 switch (transition) {
3242 case GST_STATE_CHANGE_NULL_TO_READY:
3244 ogg->have_fishead = FALSE;
3245 ogg_sync_init (&ogg->sync);
3247 case GST_STATE_CHANGE_READY_TO_PAUSED:
3248 ogg_sync_reset (&ogg->sync);
3249 ogg->current_granule = -1;
3250 ogg->running = FALSE;
3251 ogg->segment_running = FALSE;
3252 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
3254 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3260 result = parent_class->change_state (element, transition);
3262 switch (transition) {
3263 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3265 case GST_STATE_CHANGE_PAUSED_TO_READY:
3266 gst_ogg_demux_clear_chains (ogg);
3267 GST_OBJECT_LOCK (ogg);
3268 ogg->running = FALSE;
3269 ogg->segment_running = FALSE;
3270 ogg->have_fishead = FALSE;
3271 GST_OBJECT_UNLOCK (ogg);
3273 case GST_STATE_CHANGE_READY_TO_NULL:
3274 ogg_sync_clear (&ogg->sync);
3283 gst_annodex_granule_to_time (gint64 granulepos, gint64 granulerate_n,
3284 gint64 granulerate_d, guint8 granuleshift)
3286 gint64 keyindex, keyoffset;
3290 if (granulepos == 0 || granulerate_n == 0 || granulerate_d == 0)
3293 if (granuleshift != 0) {
3294 keyindex = granulepos >> granuleshift;
3295 keyoffset = granulepos - (keyindex << granuleshift);
3296 granulepos = keyindex + keyoffset;
3299 /* GST_SECOND / (granulerate_n / granulerate_d) */
3300 granulerate = gst_util_uint64_scale (GST_SECOND,
3301 granulerate_d, granulerate_n);
3302 res = gst_util_uint64_scale (granulepos, granulerate, 1);
3307 gst_ogg_demux_plugin_init (GstPlugin * plugin)
3309 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
3310 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
3311 "ogg demuxer setup stage when parsing pipeline");
3314 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
3316 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
3317 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3320 return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
3321 GST_TYPE_OGG_DEMUX);
3324 /* prints all info about the element */
3325 #undef GST_CAT_DEFAULT
3326 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
3328 #ifdef GST_DISABLE_GST_DEBUG
3331 gst_ogg_print (GstOggDemux * ogg)
3336 #else /* !GST_DISABLE_GST_DEBUG */
3339 gst_ogg_print (GstOggDemux * ogg)
3343 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
3344 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
3345 GST_TIME_ARGS (ogg->total_time));
3347 for (i = 0; i < ogg->chains->len; i++) {
3348 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3350 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
3351 GST_INFO_OBJECT (ogg, " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT,
3352 chain->offset, chain->end_offset);
3353 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
3354 GST_TIME_ARGS (chain->begin_time));
3355 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
3356 GST_TIME_ARGS (chain->total_time));
3357 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
3358 GST_TIME_ARGS (chain->segment_start));
3359 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
3360 GST_TIME_ARGS (chain->segment_stop));
3362 for (j = 0; j < chain->streams->len; j++) {
3363 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
3365 GST_INFO_OBJECT (ogg, " stream %08x:", stream->serialno);
3366 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
3367 GST_TIME_ARGS (stream->start_time));
3368 GST_INFO_OBJECT (ogg, " first granulepos: %" G_GINT64_FORMAT,
3369 stream->first_granule);
3370 GST_INFO_OBJECT (ogg, " first time: %" GST_TIME_FORMAT,
3371 GST_TIME_ARGS (stream->first_time));
3375 #endif /* GST_DISABLE_GST_DEBUG */