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/tag/tag.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 (gst_ogg_demux_debug);
64 GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
65 #define GST_CAT_DEFAULT gst_ogg_demux_debug
69 _ogg_packet_copy (const ogg_packet * packet)
71 ogg_packet *ret = g_new0 (ogg_packet, 1);
74 ret->packet = g_memdup (packet->packet, packet->bytes);
80 _ogg_packet_free (ogg_packet * packet)
82 g_free (packet->packet);
87 gst_ogg_page_copy (ogg_page * page)
89 ogg_page *p = g_new0 (ogg_page, 1);
91 /* make a copy of the page */
92 p->header = g_memdup (page->header, page->header_len);
93 p->header_len = page->header_len;
94 p->body = g_memdup (page->body, page->body_len);
95 p->body_len = page->body_len;
101 gst_ogg_page_free (ogg_page * page)
103 g_free (page->header);
108 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
109 GstOggChain * chain);
110 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
111 GstOggChain * chain, GstEvent * event);
112 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
114 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
116 static gboolean gst_ogg_demux_receive_event (GstElement * element,
119 static void gst_ogg_pad_class_init (GstOggPadClass * klass);
120 static void gst_ogg_pad_init (GstOggPad * pad);
121 static void gst_ogg_pad_dispose (GObject * object);
122 static void gst_ogg_pad_finalize (GObject * object);
124 static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
125 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
126 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
127 static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
128 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
131 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
132 GstOggPad * pad, GstFlowReturn ret);
133 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
135 GType gst_ogg_pad_get_type (void);
136 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
139 gst_ogg_pad_class_init (GstOggPadClass * klass)
141 GObjectClass *gobject_class;
143 gobject_class = (GObjectClass *) klass;
145 gobject_class->dispose = gst_ogg_pad_dispose;
146 gobject_class->finalize = gst_ogg_pad_finalize;
150 gst_ogg_pad_init (GstOggPad * pad)
152 gst_pad_set_event_function (GST_PAD (pad),
153 GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
154 gst_pad_set_getcaps_function (GST_PAD (pad),
155 GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps));
156 gst_pad_set_query_type_function (GST_PAD (pad),
157 GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types));
158 gst_pad_set_query_function (GST_PAD (pad),
159 GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
161 pad->mode = GST_OGG_PAD_MODE_INIT;
163 pad->current_granule = -1;
164 pad->keyframe_granule = -1;
166 pad->start_time = GST_CLOCK_TIME_NONE;
168 pad->last_stop = GST_CLOCK_TIME_NONE;
170 pad->have_type = FALSE;
171 pad->continued = NULL;
172 pad->map.headers = NULL;
173 pad->map.queued = NULL;
177 gst_ogg_pad_dispose (GObject * object)
179 GstOggPad *pad = GST_OGG_PAD (object);
184 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
185 g_list_free (pad->map.headers);
186 pad->map.headers = NULL;
187 g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
188 g_list_free (pad->map.queued);
189 pad->map.queued = NULL;
191 /* clear continued pages */
192 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
193 g_list_free (pad->continued);
194 pad->continued = NULL;
196 ogg_stream_reset (&pad->map.stream);
198 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
202 gst_ogg_pad_finalize (GObject * object)
204 GstOggPad *pad = GST_OGG_PAD (object);
206 ogg_stream_clear (&pad->map.stream);
208 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
211 static const GstQueryType *
212 gst_ogg_pad_query_types (GstPad * pad)
214 static const GstQueryType query_types[] = {
224 gst_ogg_pad_getcaps (GstPad * pad)
226 return gst_caps_ref (GST_PAD_CAPS (pad));
230 gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
235 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
237 switch (GST_QUERY_TYPE (query)) {
238 case GST_QUERY_DURATION:
243 gst_query_parse_duration (query, &format, NULL);
244 /* can only get position in time */
245 if (format != GST_FORMAT_TIME)
249 /* we must return the total length */
250 total_time = ogg->total_time;
252 /* in push mode we can answer the query and we must return -1 */
256 gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
259 case GST_QUERY_SEEKING:
263 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
264 if (format == GST_FORMAT_TIME) {
265 gst_query_set_seeking (query, GST_FORMAT_TIME, ogg->pullmode,
274 res = gst_pad_query_default (pad, query);
278 gst_object_unref (ogg);
285 GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
292 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
297 ogg = GST_OGG_DEMUX (element);
299 switch (GST_EVENT_TYPE (event)) {
301 /* can't seek if we are not pullmode, FIXME could pass the
302 * seek query upstream after converting it to bytes using
303 * the average bitrate of the stream. */
304 if (!ogg->pullmode) {
305 GST_DEBUG_OBJECT (ogg, "seek on pull mode stream not implemented yet");
309 /* now do the seek */
310 res = gst_ogg_demux_perform_seek (ogg, event);
311 gst_event_unref (event);
314 GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
323 GST_DEBUG_OBJECT (ogg, "error handling event");
324 gst_event_unref (event);
330 gst_ogg_pad_event (GstPad * pad, GstEvent * event)
335 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
337 switch (GST_EVENT_TYPE (event)) {
339 /* can't seek if we are not pullmode, FIXME could pass the
340 * seek query upstream after converting it to bytes using
341 * the average bitrate of the stream. */
342 if (!ogg->pullmode) {
343 GST_DEBUG_OBJECT (ogg, "seek on pull mode stream not implemented yet");
347 /* now do the seek */
348 res = gst_ogg_demux_perform_seek (ogg, event);
349 gst_event_unref (event);
352 res = gst_pad_event_default (pad, event);
356 gst_object_unref (ogg);
363 GST_DEBUG_OBJECT (ogg, "error handling event");
364 gst_event_unref (event);
371 gst_ogg_pad_reset (GstOggPad * pad)
373 ogg_stream_reset (&pad->map.stream);
375 GST_DEBUG_OBJECT (pad, "doing reset");
377 /* clear continued pages */
378 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
379 g_list_free (pad->continued);
380 pad->continued = NULL;
382 pad->last_ret = GST_FLOW_OK;
383 pad->last_stop = GST_CLOCK_TIME_NONE;
384 pad->current_granule = -1;
385 pad->keyframe_granule = -1;
388 /* called when the skeleton fishead is found. Caller ensures the packet is
389 * precisely the correct size; we don't re-check this here. */
391 gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet)
393 GstOggDemux *ogg = pad->ogg;
394 guint8 *data = packet->packet;
395 guint16 major, minor;
396 gint64 prestime_n, prestime_d;
397 gint64 basetime_n, basetime_d;
399 /* skip "fishead\0" */
400 major = GST_READ_UINT16_LE (data + 8);
401 minor = GST_READ_UINT16_LE (data + 10);
402 prestime_n = (gint64) GST_READ_UINT64_LE (data + 12);
403 prestime_d = (gint64) GST_READ_UINT64_LE (data + 20);
404 basetime_n = (gint64) GST_READ_UINT64_LE (data + 28);
405 basetime_d = (gint64) GST_READ_UINT64_LE (data + 36);
407 ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
408 ogg->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
409 ogg->have_fishead = TRUE;
410 pad->map.is_skeleton = TRUE;
411 pad->start_time = GST_CLOCK_TIME_NONE;
412 GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %"
413 GST_TIME_FORMAT ", prestime: %" GST_TIME_FORMAT ")",
414 GST_TIME_ARGS (ogg->basetime), GST_TIME_ARGS (ogg->prestime));
417 /* function called when a skeleton fisbone is found. Caller ensures that
418 * the packet length is sufficient */
420 gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet)
422 GstOggPad *fisbone_pad;
423 gint64 start_granule;
425 guint8 *data = packet->packet;
427 serialno = GST_READ_UINT32_LE (data + 12);
429 fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
431 if (fisbone_pad->map.have_fisbone)
435 fisbone_pad->map.have_fisbone = TRUE;
437 fisbone_pad->map.granulerate_n = GST_READ_UINT64_LE (data + 20);
438 fisbone_pad->map.granulerate_d = GST_READ_UINT64_LE (data + 28);
439 start_granule = GST_READ_UINT64_LE (data + 36);
440 fisbone_pad->map.preroll = GST_READ_UINT32_LE (data + 44);
441 fisbone_pad->map.granuleshift = GST_READ_UINT8 (data + 48);
443 GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed "
444 "(serialno: %08x start time: %" GST_TIME_FORMAT
445 " granulerate_n: %d granulerate_d: %d "
446 " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
447 serialno, GST_TIME_ARGS (fisbone_pad->start_time),
448 fisbone_pad->map.granulerate_n, fisbone_pad->map.granulerate_d,
449 fisbone_pad->map.preroll, fisbone_pad->map.granuleshift);
451 GST_WARNING_OBJECT (pad->ogg,
452 "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
457 /* queue data, basically takes the packet, puts it in a buffer and store the
458 * buffer in the queued list. */
460 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
462 #ifndef GST_DISABLE_GST_DEBUG
463 GstOggDemux *ogg = pad->ogg;
466 GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08lx", pad,
469 pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
476 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
477 gboolean push_headers)
479 GstBuffer *buf = NULL;
480 GstFlowReturn ret, cret;
481 GstOggDemux *ogg = pad->ogg;
487 GstClockTime out_timestamp, out_duration;
488 guint64 out_offset, out_offset_end;
489 gboolean delta_unit = FALSE;
491 GST_DEBUG_OBJECT (ogg,
492 "%p streaming to peer serial %08lx", pad, pad->map.serialno);
494 if (pad->map.is_ogm) {
498 data = packet->packet;
499 bytes = packet->bytes;
505 /* We don't push header packets for OGM */
506 cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
508 } else if (data[0] & 3 && pad->map.is_ogm_text) {
511 /* We don't push comment packets either for text streams,
512 * other streams will handle the comment packets in the
514 buf = gst_buffer_new ();
516 GST_BUFFER_DATA (buf) = (guint8 *) data;
517 GST_BUFFER_SIZE (buf) = bytes;
519 tags = gst_tag_list_from_vorbiscomment_buffer (buf,
520 (guint8 *) "\003vorbis", 7, NULL);
521 gst_buffer_unref (buf);
525 GST_DEBUG_OBJECT (ogg, "tags = %" GST_PTR_FORMAT, tags);
526 gst_element_found_tags_for_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad),
529 GST_DEBUG_OBJECT (ogg, "failed to extract tags from vorbis comment");
532 cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
536 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
537 delta_unit = (((data[0] & 0x08) >> 3) == 0);
541 /* Strip trailing \0 for subtitles */
542 if (pad->map.is_ogm_text) {
543 while (bytes && data[bytes - 1] == 0) {
553 /* get timing info for the packet */
554 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
555 GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
558 out_timestamp = GST_CLOCK_TIME_NONE;
559 out_duration = GST_CLOCK_TIME_NONE;
563 if (packet->granulepos != -1) {
564 pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
566 pad->keyframe_granule =
567 gst_ogg_stream_granulepos_to_key_granule (&pad->map,
569 GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
570 pad->current_granule);
571 } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
572 pad->current_granule += duration;
573 GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT,
574 pad->current_granule);
576 if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
577 /* negative rates, only set timestamp on the packets with a granulepos */
583 /* we only push buffers after we have a valid granule. This is done so that
584 * we nicely skip packets without a timestamp after a seek. This is ok
585 * because we base or seek on the packet after the page with the smaller
587 if (pad->current_granule == -1)
590 if (pad->map.is_ogm) {
591 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
592 pad->current_granule);
593 out_duration = gst_util_uint64_scale (duration,
594 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
595 } else if (pad->is_sparse) {
596 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
597 pad->current_granule);
598 out_duration = GST_CLOCK_TIME_NONE;
600 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
601 pad->current_granule - duration);
603 gst_ogg_stream_granule_to_time (&pad->map,
604 pad->current_granule) - out_timestamp;
607 gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule,
608 pad->keyframe_granule);
610 gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
614 /* check for invalid buffer sizes */
615 if (G_UNLIKELY (offset + trim >= packet->bytes))
619 gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad),
620 GST_BUFFER_OFFSET_NONE, packet->bytes - offset - trim,
621 GST_PAD_CAPS (pad), &buf);
624 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
625 if (ret != GST_FLOW_OK)
628 /* set delta flag for OGM content */
630 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
632 /* copy packet in buffer */
633 memcpy (buf->data, packet->packet + offset, packet->bytes - offset - trim);
635 GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
636 GST_BUFFER_DURATION (buf) = out_duration;
637 GST_BUFFER_OFFSET (buf) = out_offset;
638 GST_BUFFER_OFFSET_END (buf) = out_offset_end;
640 /* Mark discont on the buffer */
642 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
643 pad->discont = FALSE;
646 pad->last_stop = ogg->segment.last_stop;
648 /* don't push the header packets when we are asked to skip them */
649 if (!packet->b_o_s || push_headers) {
650 ret = gst_pad_push (GST_PAD_CAST (pad), buf);
654 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
657 /* we're done with skeleton stuff */
658 if (pad->map.is_skeleton)
661 /* check if valid granulepos, then we can calculate the current
662 * position. We know the granule for each packet but we only want to update
663 * the last_stop when we have a valid granulepos on the packet because else
664 * our time jumps around for the different streams. */
665 if (packet->granulepos < 0)
668 /* convert to time */
669 current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
672 /* convert to stream time */
673 if ((chain = pad->chain)) {
674 gint64 chain_start = 0;
676 if (chain->segment_start != GST_CLOCK_TIME_NONE)
677 chain_start = chain->segment_start;
679 current_time = current_time - chain_start + chain->begin_time;
682 /* and store as the current position */
683 gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time);
685 GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
686 GST_TIME_ARGS (current_time));
690 gst_buffer_unref (buf);
691 /* return combined flow result */
697 GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
698 cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
703 GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
704 cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
709 GST_DEBUG_OBJECT (ogg,
710 "%p could not get buffer from peer %08lx, %d (%s), combined %d (%s)",
711 pad, pad->map.serialno, ret, gst_flow_get_name (ret),
712 cret, gst_flow_get_name (cret));
717 /* submit a packet to the oggpad, this function will run the
718 * typefind code for the pad if this is the first packet for this
722 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
725 GstFlowReturn ret = GST_FLOW_OK;
727 GstOggDemux *ogg = pad->ogg;
729 GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08lx", pad,
732 if (!pad->have_type) {
733 if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE &&
734 !memcmp (packet->packet, "fishead\0", 8)) {
735 gst_ogg_pad_parse_skeleton_fishead (pad, packet);
737 pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
738 if (!pad->have_type) {
739 pad->map.caps = gst_caps_new_simple ("application/x-unknown", NULL);
742 gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
744 GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
748 if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
749 !memcmp (packet->packet, "fisbone\0", 8)) {
750 gst_ogg_pad_parse_skeleton_fisbone (pad, packet);
753 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
756 GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
757 pad->current_granule = granule;
760 /* restart header packet count when seeing a b_o_s page;
761 * particularly useful following a seek or even following chain finding */
763 GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
764 pad->map.n_header_packets_seen = 0;
765 if (!pad->map.have_headers) {
766 GST_DEBUG_OBJECT (ogg, "clearing header packets");
767 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
768 g_list_free (pad->map.headers);
769 pad->map.headers = NULL;
773 /* Overload the value of b_o_s in ogg_packet with a flag whether or
774 * not this is a header packet. Maybe some day this could be cleaned
776 packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
777 if (!packet->b_o_s) {
778 pad->map.have_headers = TRUE;
779 if (pad->start_time == GST_CLOCK_TIME_NONE) {
780 gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
781 GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
782 if (duration != -1) {
783 pad->map.accumulated_granule += duration;
784 GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
785 pad->map.accumulated_granule);
788 if (packet->granulepos != -1) {
789 ogg_int64_t start_granule;
792 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
795 if (granule > pad->map.accumulated_granule)
796 start_granule = granule - pad->map.accumulated_granule;
800 pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
802 GST_DEBUG ("start time %" G_GINT64_FORMAT, pad->start_time);
804 packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
805 pad->map.accumulated_granule, pad->keyframe_granule);
809 pad->map.n_header_packets_seen++;
810 if (!pad->map.have_headers) {
812 g_list_append (pad->map.headers, _ogg_packet_copy (packet));
813 GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
817 /* we know the start_time of the pad data, see if we
818 * can activate the complete chain if this is a dynamic
820 if (pad->start_time != GST_CLOCK_TIME_NONE) {
821 GstOggChain *chain = pad->chain;
823 /* check if complete chain has start time */
824 if (chain == ogg->building_chain) {
826 /* see if we have enough info to activate the chain, we have enough info
827 * when all streams have a valid start time. */
828 if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
831 GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
832 GST_TIME_ARGS (chain->segment_start));
833 GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT,
834 GST_TIME_ARGS (chain->segment_stop));
835 GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT,
836 GST_TIME_ARGS (chain->begin_time));
838 /* create the newsegment event we are going to send out */
839 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
840 GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
842 gst_event_set_seqnum (event, ogg->seqnum);
844 gst_ogg_demux_activate_chain (ogg, chain, event);
846 ogg->building_chain = NULL;
851 /* if we are building a chain, store buffer for when we activate
852 * it. This path is taken if we operate in streaming mode. */
853 if (ogg->building_chain) {
854 /* bos packets where stored in the header list so we can discard
857 ret = gst_ogg_demux_queue_data (pad, packet);
859 /* else we are completely streaming to the peer */
861 ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
866 /* flush at most @npackets from the stream layer. All packets if
870 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
872 GstFlowReturn result = GST_FLOW_OK;
873 gboolean done = FALSE;
882 ret = ogg_stream_packetout (&pad->map.stream, &packet);
885 GST_LOG_OBJECT (ogg, "packetout done");
889 GST_LOG_OBJECT (ogg, "packetout discont");
890 gst_ogg_chain_mark_discont (pad->chain);
893 GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
894 result = gst_ogg_pad_submit_packet (pad, &packet);
895 if (GST_FLOW_IS_FATAL (result))
896 goto could_not_submit;
899 GST_WARNING_OBJECT (ogg,
900 "invalid return value %d for ogg_stream_packetout, resetting stream",
902 gst_ogg_pad_reset (pad);
907 done = (npackets == 0);
915 GST_WARNING_OBJECT (ogg,
916 "could not submit packet for stream %08lx, error: %d",
917 pad->map.serialno, result);
918 gst_ogg_pad_reset (pad);
923 /* submit a page to an oggpad, this function will then submit all
924 * the packets in the page.
927 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
929 GstFlowReturn result = GST_FLOW_OK;
931 gboolean continued = FALSE;
935 /* for negative rates we read pages backwards and must therefore be carefull
936 * with continued pages */
937 if (ogg->segment.rate < 0.0) {
940 continued = ogg_page_continued (page);
942 /* number of completed packets in the page */
943 npackets = ogg_page_packets (page);
945 /* page is not continued so it contains at least one packet start. It's
946 * possible that no packet ends on this page (npackets == 0). In that
947 * case, the next (continued) page(s) we kept contain the remainder of the
948 * packets. We mark npackets=1 to make us start decoding the pages in the
949 * remainder of the algorithm. */
953 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
956 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
961 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
964 /* flush all packets in the stream layer, this might not give a packet if
965 * the page had no packets finishing on the page (npackets == 0). */
966 result = gst_ogg_pad_stream_out (pad, 0);
968 if (pad->continued) {
971 /* now send the continued pages to the stream layer */
972 while (pad->continued) {
973 ogg_page *p = (ogg_page *) pad->continued->data;
975 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
976 if (ogg_stream_pagein (&pad->map.stream, p) != 0)
979 pad->continued = g_list_delete_link (pad->continued, pad->continued);
982 gst_ogg_page_free (p);
985 GST_LOG_OBJECT (ogg, "flushing last continued packet");
986 /* flush 1 continued packet in the stream layer */
987 result = gst_ogg_pad_stream_out (pad, 1);
989 /* flush all remaining packets, we pushed them in the previous round.
990 * We don't use _reset() because we still want to get the discont when
991 * we submit a next page. */
992 while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
996 /* keep continued pages (only in reverse mode) */
998 ogg_page *p = gst_ogg_page_copy (page);
1000 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
1001 pad->continued = g_list_prepend (pad->continued, p);
1008 GST_WARNING_OBJECT (ogg,
1009 "ogg stream choked on page (serial %08lx), resetting stream",
1011 gst_ogg_pad_reset (pad);
1012 /* we continue to recover */
1018 static GstOggChain *
1019 gst_ogg_chain_new (GstOggDemux * ogg)
1021 GstOggChain *chain = g_new0 (GstOggChain, 1);
1023 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
1027 chain->have_bos = FALSE;
1028 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
1029 chain->begin_time = GST_CLOCK_TIME_NONE;
1030 chain->segment_start = GST_CLOCK_TIME_NONE;
1031 chain->segment_stop = GST_CLOCK_TIME_NONE;
1032 chain->total_time = GST_CLOCK_TIME_NONE;
1038 gst_ogg_chain_free (GstOggChain * chain)
1042 for (i = 0; i < chain->streams->len; i++) {
1043 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1045 gst_object_unref (pad);
1047 g_array_free (chain->streams, TRUE);
1052 gst_ogg_chain_mark_discont (GstOggChain * chain)
1056 for (i = 0; i < chain->streams->len; i++) {
1057 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1059 pad->discont = TRUE;
1060 pad->map.last_size = 0;
1065 gst_ogg_chain_reset (GstOggChain * chain)
1069 for (i = 0; i < chain->streams->len; i++) {
1070 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1072 gst_ogg_pad_reset (pad);
1077 gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno)
1083 GST_DEBUG_OBJECT (chain->ogg, "creating new stream %08lx in chain %p",
1086 ret = g_object_new (GST_TYPE_OGG_PAD, NULL);
1087 /* we own this one */
1088 gst_object_ref (ret);
1089 gst_object_sink (ret);
1091 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1092 ret->discont = TRUE;
1093 ret->map.last_size = 0;
1096 ret->ogg = chain->ogg;
1098 ret->map.serialno = serialno;
1099 if (ogg_stream_init (&ret->map.stream, serialno) != 0)
1102 name = g_strdup_printf ("serial_%08lx", serialno);
1103 gst_object_set_name (GST_OBJECT (ret), name);
1106 /* FIXME: either do something with it or remove it */
1107 list = gst_tag_list_new ();
1108 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1110 gst_tag_list_free (list);
1112 GST_DEBUG_OBJECT (chain->ogg,
1113 "created new ogg src %p for stream with serial %08lx", ret, serialno);
1115 g_array_append_val (chain->streams, ret);
1122 GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
1124 gst_object_unref (ret);
1130 gst_ogg_chain_get_stream (GstOggChain * chain, glong serialno)
1134 for (i = 0; i < chain->streams->len; i++) {
1135 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1137 if (pad->map.serialno == serialno)
1144 gst_ogg_chain_has_stream (GstOggChain * chain, glong serialno)
1146 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1149 #define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain))
1151 /* signals and args */
1164 static GstStaticPadTemplate ogg_demux_src_template_factory =
1165 GST_STATIC_PAD_TEMPLATE ("src_%d",
1168 GST_STATIC_CAPS_ANY);
1170 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1171 GST_STATIC_PAD_TEMPLATE ("sink",
1174 GST_STATIC_CAPS ("application/ogg; application/x-annodex")
1177 static void gst_ogg_demux_finalize (GObject * object);
1179 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1180 GstOggChain ** chain);
1181 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1182 GstOggChain * chain);
1184 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
1185 static void gst_ogg_demux_loop (GstOggPad * pad);
1186 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
1187 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
1188 static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
1190 static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
1192 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1193 GstStateChange transition);
1194 static void gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
1196 static void gst_ogg_print (GstOggDemux * demux);
1198 GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT);
1201 gst_ogg_demux_base_init (gpointer g_class)
1203 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1205 gst_element_class_set_details (element_class, &gst_ogg_demux_details);
1207 gst_element_class_add_pad_template (element_class,
1208 gst_static_pad_template_get (&ogg_demux_sink_template_factory));
1209 gst_element_class_add_pad_template (element_class,
1210 gst_static_pad_template_get (&ogg_demux_src_template_factory));
1214 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1216 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1217 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1219 gstelement_class->change_state = gst_ogg_demux_change_state;
1220 gstelement_class->send_event = gst_ogg_demux_receive_event;
1222 gobject_class->finalize = gst_ogg_demux_finalize;
1226 gst_ogg_demux_init (GstOggDemux * ogg, GstOggDemuxClass * g_class)
1228 /* create the sink pad */
1230 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
1233 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
1234 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
1235 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
1236 gst_pad_set_activatepull_function (ogg->sinkpad,
1237 gst_ogg_demux_sink_activate_pull);
1238 gst_pad_set_activatepush_function (ogg->sinkpad,
1239 gst_ogg_demux_sink_activate_push);
1240 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
1242 ogg->chain_lock = g_mutex_new ();
1243 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
1245 ogg->newsegment = NULL;
1249 gst_ogg_demux_finalize (GObject * object)
1253 ogg = GST_OGG_DEMUX (object);
1255 g_array_free (ogg->chains, TRUE);
1256 g_mutex_free (ogg->chain_lock);
1257 ogg_sync_clear (&ogg->sync);
1259 if (ogg->newsegment)
1260 gst_event_unref (ogg->newsegment);
1262 G_OBJECT_CLASS (parent_class)->finalize (object);
1266 gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
1271 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
1273 switch (GST_EVENT_TYPE (event)) {
1274 case GST_EVENT_NEWSEGMENT:
1276 GST_DEBUG_OBJECT (ogg, "got a new segment event");
1277 ogg_sync_reset (&ogg->sync);
1278 gst_event_unref (event);
1283 GST_DEBUG_OBJECT (ogg, "got an EOS event");
1284 res = gst_pad_event_default (pad, event);
1285 if (ogg->current_chain == NULL) {
1286 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
1287 ("can't get first chain"));
1292 res = gst_pad_event_default (pad, event);
1295 gst_object_unref (ogg);
1300 /* submit the given buffer to the ogg sync.
1302 * Returns the number of bytes submited.
1304 static GstFlowReturn
1305 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
1310 GstFlowReturn ret = GST_FLOW_OK;
1312 size = GST_BUFFER_SIZE (buffer);
1313 data = GST_BUFFER_DATA (buffer);
1315 GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
1316 if (G_UNLIKELY (size == 0))
1319 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
1320 if (G_UNLIKELY (oggbuffer == NULL))
1323 memcpy (oggbuffer, data, size);
1324 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
1328 gst_buffer_unref (buffer);
1335 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1336 (NULL), ("failed to get ogg sync buffer"));
1337 ret = GST_FLOW_ERROR;
1342 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1343 (NULL), ("failed to write %d bytes to the sync buffer", size));
1344 ret = GST_FLOW_ERROR;
1349 /* in random access mode this code updates the current read position
1350 * and resets the ogg sync buffer so that the next read will happen
1351 * from this new location.
1354 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
1356 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
1358 ogg->offset = offset;
1359 ogg->read_offset = offset;
1360 ogg_sync_reset (&ogg->sync);
1363 /* read more data from the current offset and submit to
1364 * the ogg sync layer.
1366 static GstFlowReturn
1367 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
1372 GST_LOG_OBJECT (ogg,
1373 "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
1374 ogg->read_offset, ogg->length, end_offset);
1376 if (end_offset > 0 && ogg->read_offset >= end_offset)
1377 goto boundary_reached;
1379 if (ogg->read_offset == ogg->length)
1382 ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
1383 if (ret != GST_FLOW_OK)
1386 ogg->read_offset += GST_BUFFER_SIZE (buffer);
1388 ret = gst_ogg_demux_submit_buffer (ogg, buffer);
1395 GST_LOG_OBJECT (ogg, "reached boundary");
1396 return GST_FLOW_LIMIT;
1400 GST_LOG_OBJECT (ogg, "reached EOS");
1401 return GST_FLOW_UNEXPECTED;
1405 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
1406 gst_flow_get_name (ret));
1411 /* Read the next page from the current offset.
1412 * boundary: number of bytes ahead we allow looking for;
1415 * @offset will contain the offset the next page starts at when this function
1416 * returns GST_FLOW_OK.
1418 * GST_FLOW_UNEXPECTED is returned on EOS.
1420 * GST_FLOW_LIMIT is returned when we did not find a page before the
1421 * boundary. If @boundary is -1, this is never returned.
1423 * Any other error returned while retrieving data from the peer is returned as
1426 static GstFlowReturn
1427 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary,
1430 gint64 end_offset = -1;
1433 GST_LOG_OBJECT (ogg,
1434 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
1435 G_GINT64_FORMAT, ogg->offset, boundary);
1438 end_offset = ogg->offset + boundary;
1443 if (end_offset > 0 && ogg->offset >= end_offset)
1444 goto boundary_reached;
1446 more = ogg_sync_pageseek (&ogg->sync, og);
1448 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
1451 /* skipped n bytes */
1452 ogg->offset -= more;
1453 GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT, more,
1455 } else if (more == 0) {
1456 /* we need more data */
1458 goto boundary_reached;
1460 GST_LOG_OBJECT (ogg, "need more data");
1461 ret = gst_ogg_demux_get_data (ogg, end_offset);
1462 if (ret != GST_FLOW_OK)
1465 gint64 res_offset = ogg->offset;
1467 /* got a page. Return the offset at the page beginning,
1468 advance the internal offset past the page end */
1470 *offset = res_offset;
1473 ogg->offset += more;
1475 GST_LOG_OBJECT (ogg,
1476 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
1477 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
1478 ogg_page_serialno (og), ogg->offset,
1479 (gint64) ogg_page_granulepos (og));
1483 GST_LOG_OBJECT (ogg, "returning %d", ret);
1490 GST_LOG_OBJECT (ogg,
1491 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
1492 ogg->offset, end_offset);
1493 return GST_FLOW_LIMIT;
1497 /* from the current offset, find the previous page, seeking backwards
1498 * until we find the page.
1500 static GstFlowReturn
1501 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
1504 gint64 begin = ogg->offset;
1506 gint64 cur_offset = -1;
1508 GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
1510 while (cur_offset == -1) {
1515 /* seek CHUNKSIZE back */
1516 gst_ogg_demux_seek (ogg, begin);
1518 /* now continue reading until we run out of data, if we find a page
1519 * start, we save it. It might not be the final page as there could be
1520 * another page after this one. */
1521 while (ogg->offset < end) {
1525 gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
1526 /* we hit the upper limit, offset contains the last page start */
1527 if (ret == GST_FLOW_LIMIT) {
1528 GST_LOG_OBJECT (ogg, "hit limit");
1531 /* something went wrong */
1532 if (ret == GST_FLOW_UNEXPECTED) {
1534 GST_LOG_OBJECT (ogg, "got unexpected");
1535 } else if (ret != GST_FLOW_OK) {
1536 GST_LOG_OBJECT (ogg, "got error %d", ret);
1540 GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
1542 /* offset is next page start */
1543 cur_offset = new_offset;
1547 GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
1549 /* we have the offset. Actually snork and hold the page now */
1550 gst_ogg_demux_seek (ogg, cur_offset);
1551 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
1552 if (ret != GST_FLOW_OK) {
1553 GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
1555 /* this shouldn't be possible */
1560 *offset = cur_offset;
1566 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
1569 GstOggChain *chain = ogg->current_chain;
1574 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
1576 /* send EOS on all the pads */
1577 for (i = 0; i < chain->streams->len; i++) {
1578 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1584 event = gst_event_new_eos ();
1585 gst_event_set_seqnum (event, ogg->seqnum);
1586 gst_pad_push_event (GST_PAD_CAST (pad), event);
1588 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
1590 /* deactivate first */
1591 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
1593 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
1597 /* if we cannot seek back to the chain, we can destroy the chain
1599 if (!ogg->pullmode) {
1600 gst_ogg_chain_free (chain);
1602 ogg->current_chain = NULL;
1608 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
1613 if (chain == ogg->current_chain) {
1615 gst_event_unref (event);
1619 /* FIXME, should not be called with NULL */
1620 if (chain != NULL) {
1621 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
1623 /* first add the pads */
1624 for (i = 0; i < chain->streams->len; i++) {
1626 GstStructure *structure;
1628 pad = g_array_index (chain->streams, GstOggPad *, i);
1630 if (pad->map.is_skeleton || pad->added || GST_PAD_CAPS (pad) == NULL)
1633 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
1636 pad->discont = TRUE;
1637 pad->map.last_size = 0;
1638 pad->last_ret = GST_FLOW_OK;
1641 structure = gst_caps_get_structure (GST_PAD_CAPS (pad), 0);
1643 gst_structure_has_name (structure, "application/x-ogm-text") ||
1644 gst_structure_has_name (structure, "text/x-cmml") ||
1645 gst_structure_has_name (structure, "subtitle/x-kate") ||
1646 gst_structure_has_name (structure, "application/x-kate");
1648 /* activate first */
1649 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
1651 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
1655 /* after adding the new pads, remove the old pads */
1656 gst_ogg_demux_deactivate_current_chain (ogg);
1658 ogg->current_chain = chain;
1660 /* we are finished now */
1661 gst_element_no_more_pads (GST_ELEMENT (ogg));
1663 /* FIXME, must be sent from the streaming thread */
1665 gst_ogg_demux_send_event (ogg, event);
1667 gst_element_found_tags (GST_ELEMENT_CAST (ogg),
1668 gst_tag_list_new_full (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL));
1671 GST_DEBUG_OBJECT (ogg, "starting chain");
1673 /* then send out any headers and queued packets */
1674 for (i = 0; i < chain->streams->len; i++) {
1678 pad = g_array_index (chain->streams, GstOggPad *, i);
1680 GST_DEBUG_OBJECT (ogg, "pushing headers");
1682 for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
1683 ogg_packet *p = walk->data;
1685 gst_ogg_demux_chain_peer (pad, p, TRUE);
1688 GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
1689 /* push queued packets */
1690 for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
1691 ogg_packet *p = walk->data;
1693 gst_ogg_demux_chain_peer (pad, p, TRUE);
1694 _ogg_packet_free (p);
1696 /* and free the queued buffers */
1697 g_list_free (pad->map.queued);
1698 pad->map.queued = NULL;
1704 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
1705 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
1714 GST_DEBUG_OBJECT (ogg,
1715 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, begin,
1717 GST_DEBUG_OBJECT (ogg,
1718 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
1719 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
1720 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
1722 /* perform the seek */
1723 while (begin < end) {
1726 if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
1729 /* take a (pretty decent) guess, avoiding overflow */
1730 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
1732 bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
1734 if (bisect <= begin)
1736 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
1738 gst_ogg_demux_seek (ogg, bisect);
1740 while (begin < end) {
1743 GST_DEBUG_OBJECT (ogg,
1744 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
1745 ", end %" G_GINT64_FORMAT, bisect, begin, end);
1747 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
1748 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
1751 if (ret == GST_FLOW_LIMIT) {
1752 /* we hit the upper limit, go back a bit */
1753 if (bisect <= begin + 1) {
1754 end = begin; /* found it */
1759 bisect -= CHUNKSIZE;
1760 if (bisect <= begin)
1763 gst_ogg_demux_seek (ogg, bisect);
1765 } else if (ret == GST_FLOW_OK) {
1766 /* found offset of next ogg page */
1768 GstClockTime granuletime;
1771 /* get the granulepos */
1772 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
1774 granulepos = ogg_page_granulepos (&og);
1775 if (granulepos == -1) {
1776 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
1780 /* get the stream */
1781 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
1782 if (pad == NULL || pad->map.is_skeleton)
1785 /* convert granulepos to time */
1786 granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
1788 if (granuletime < pad->start_time)
1791 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
1792 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
1794 granuletime -= pad->start_time;
1795 granuletime += chain->begin_time;
1797 GST_DEBUG_OBJECT (ogg,
1798 "found page with granule %" G_GINT64_FORMAT " and time %"
1799 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
1801 if (granuletime < target) {
1802 best = result; /* raw offset of packet with granulepos */
1803 begin = ogg->offset; /* raw offset of next page */
1804 begintime = granuletime;
1806 bisect = begin; /* *not* begin + 1 */
1808 if (bisect <= begin + 1) {
1809 end = begin; /* found it */
1811 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
1813 bisect -= CHUNKSIZE; /* an endless loop otherwise. */
1814 if (bisect <= begin)
1816 gst_ogg_demux_seek (ogg, bisect);
1819 endtime = granuletime;
1828 GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
1829 gst_ogg_demux_seek (ogg, best);
1837 GST_DEBUG_OBJECT (ogg, "got a seek error");
1843 * do seek to time @position, return FALSE or chain and TRUE
1846 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
1847 gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
1850 GstOggChain *chain = NULL;
1852 gint64 begintime, endtime;
1853 gint64 target, keytarget;
1858 gint i, pending, len;
1860 position = segment->last_stop;
1862 /* first find the chain to search in */
1863 total = ogg->total_time;
1864 if (ogg->chains->len == 0)
1867 for (i = ogg->chains->len - 1; i >= 0; i--) {
1868 chain = g_array_index (ogg->chains, GstOggChain *, i);
1869 total -= chain->total_time;
1870 if (position >= total)
1874 /* first step, locate page containing the required data */
1875 begin = chain->offset;
1876 end = chain->end_offset;
1877 begintime = chain->begin_time;
1878 endtime = begintime + chain->total_time;
1879 target = position - total + begintime;
1881 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
1885 /* second step: find pages for all streams, we use the keyframe_granule to keep
1886 * track of which ones we saw. If we have seen a page for each stream we can
1887 * calculate the positions of each keyframe. */
1888 GST_DEBUG_OBJECT (ogg, "find keyframes");
1889 len = pending = chain->streams->len;
1891 /* figure out where the keyframes are */
1898 GstClockTime keyframe_time, granule_time;
1900 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
1901 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
1903 if (ret == GST_FLOW_LIMIT) {
1904 GST_LOG_OBJECT (ogg, "reached limit");
1908 /* get the stream */
1909 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
1913 if (pad->map.is_skeleton)
1916 granulepos = ogg_page_granulepos (&og);
1917 if (granulepos == -1) {
1918 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
1922 /* in reverse we want to go past the page with the lower timestamp */
1923 if (segment->rate < 0.0) {
1924 /* get time for this pad */
1925 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
1928 GST_LOG_OBJECT (ogg,
1929 "looking at page with ts %" GST_TIME_FORMAT ", target %"
1930 GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
1931 GST_TIME_ARGS (target));
1932 if (granule_time < target)
1936 /* we've seen this pad before */
1937 if (pad->keyframe_granule != -1)
1940 /* convert granule of this pad to the granule of the keyframe */
1941 pad->keyframe_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map,
1943 GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
1944 pad->keyframe_granule);
1946 /* get time of the keyframe */
1948 gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
1949 GST_LOG_OBJECT (ogg, "stream %08lx granule time %" GST_TIME_FORMAT,
1950 pad->map.serialno, GST_TIME_ARGS (keyframe_time));
1952 /* collect smallest value */
1953 if (keyframe_time != -1) {
1954 keyframe_time += begintime;
1955 if (keyframe_time < keytarget)
1956 keytarget = keyframe_time;
1965 /* for negative rates we will get to the keyframe backwards */
1966 if (segment->rate < 0.0)
1969 if (keytarget != target) {
1970 GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
1971 GST_TIME_ARGS (keytarget));
1973 /* last step, seek to the location of the keyframe */
1974 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
1978 /* seek back to previous position */
1979 GST_LOG_OBJECT (ogg, "keyframe on target");
1980 gst_ogg_demux_seek (ogg, best);
1985 if (segment->rate > 0.0)
1986 segment->time = keytarget;
1987 segment->last_stop = keytarget - begintime;
1996 GST_DEBUG_OBJECT (ogg, "no chains");
2001 GST_DEBUG_OBJECT (ogg, "got a seek error");
2006 /* does not take ownership of the event */
2008 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
2010 GstOggChain *chain = NULL;
2012 gboolean flush, accurate, keyframe;
2016 GstSeekType cur_type, stop_type;
2023 GST_DEBUG_OBJECT (ogg, "seek with event");
2025 gst_event_parse_seek (event, &rate, &format, &flags,
2026 &cur_type, &cur, &stop_type, &stop);
2028 /* we can only seek on time */
2029 if (format != GST_FORMAT_TIME) {
2030 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
2033 seqnum = gst_event_get_seqnum (event);
2035 GST_DEBUG_OBJECT (ogg, "seek without event");
2039 seqnum = gst_util_seqnum_next ();
2042 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
2044 flush = flags & GST_SEEK_FLAG_FLUSH;
2045 accurate = flags & GST_SEEK_FLAG_ACCURATE;
2046 keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
2048 /* first step is to unlock the streaming thread if it is
2049 * blocked in a chain call, we do this by starting the flush. because
2050 * we cannot yet hold any streaming lock, we have to protect the chains
2051 * with their own lock. */
2055 tevent = gst_event_new_flush_start ();
2056 gst_event_set_seqnum (tevent, seqnum);
2058 gst_event_ref (tevent);
2059 gst_pad_push_event (ogg->sinkpad, tevent);
2061 GST_CHAIN_LOCK (ogg);
2062 for (i = 0; i < ogg->chains->len; i++) {
2063 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2066 for (j = 0; j < chain->streams->len; j++) {
2067 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
2069 gst_event_ref (tevent);
2070 gst_pad_push_event (GST_PAD (pad), tevent);
2073 GST_CHAIN_UNLOCK (ogg);
2075 gst_event_unref (tevent);
2077 gst_pad_pause_task (ogg->sinkpad);
2080 /* now grab the stream lock so that streaming cannot continue, for
2081 * non flushing seeks when the element is in PAUSED this could block
2083 GST_PAD_STREAM_LOCK (ogg->sinkpad);
2085 if (ogg->segment_running && !flush) {
2086 /* create the segment event to close the current segment */
2087 if ((chain = ogg->current_chain)) {
2089 gint64 chain_start = 0;
2091 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2092 chain_start = chain->segment_start;
2094 newseg = gst_event_new_new_segment (TRUE, ogg->segment.rate,
2095 GST_FORMAT_TIME, ogg->segment.start + chain_start,
2096 ogg->segment.last_stop + chain_start, ogg->segment.time);
2097 /* set the seqnum of the running segment */
2098 gst_event_set_seqnum (newseg, ogg->seqnum);
2100 /* send segment on old chain, FIXME, must be sent from streaming thread. */
2101 gst_ogg_demux_send_event (ogg, newseg);
2106 gst_segment_set_seek (&ogg->segment, rate, format, flags,
2107 cur_type, cur, stop_type, stop, &update);
2110 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
2111 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
2112 GST_TIME_ARGS (ogg->segment.stop));
2114 /* we need to stop flushing on the srcpad as we're going to use it
2115 * next. We can do this as we have the STREAM lock now. */
2117 tevent = gst_event_new_flush_stop ();
2118 gst_event_set_seqnum (tevent, seqnum);
2119 gst_pad_push_event (ogg->sinkpad, tevent);
2125 /* reset all ogg streams now, need to do this from within the lock to
2126 * make sure the streaming thread is not messing with the stream */
2127 for (i = 0; i < ogg->chains->len; i++) {
2128 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2130 gst_ogg_chain_reset (chain);
2134 /* for reverse we will already seek accurately */
2135 res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
2137 /* seek failed, make sure we continue the current chain */
2139 GST_DEBUG_OBJECT (ogg, "seek failed");
2140 chain = ogg->current_chain;
2142 GST_DEBUG_OBJECT (ogg, "seek success");
2148 /* now we have a new position, prepare for streaming again */
2153 gint64 last_stop, begin_time;
2155 /* we have to send the flush to the old chain, not the new one */
2157 tevent = gst_event_new_flush_stop ();
2158 gst_event_set_seqnum (tevent, seqnum);
2159 gst_ogg_demux_send_event (ogg, tevent);
2162 /* we need this to see how far inside the chain we need to start */
2163 if (chain->begin_time != GST_CLOCK_TIME_NONE)
2164 begin_time = chain->begin_time;
2168 /* segment.start gives the start over all chains, we calculate the amount
2169 * of time into this chain we need to start */
2170 start = ogg->segment.start - begin_time;
2171 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2172 start += chain->segment_start;
2174 if ((stop = ogg->segment.stop) == -1)
2175 stop = ogg->segment.duration;
2177 /* segment.stop gives the stop time over all chains, calculate the amount of
2178 * time we need to stop in this chain */
2180 if (stop > begin_time)
2184 stop += chain->segment_start;
2185 /* we must stop when this chain ends and switch to the next chain to play
2186 * the remainder of the segment. */
2187 stop = MIN (stop, chain->segment_stop);
2190 last_stop = ogg->segment.last_stop;
2191 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2192 last_stop += chain->segment_start;
2194 /* create the segment event we are going to send out */
2195 if (ogg->segment.rate >= 0.0)
2196 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2197 ogg->segment.format, last_stop, stop, ogg->segment.time);
2199 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2200 ogg->segment.format, start, last_stop, ogg->segment.time);
2202 gst_event_set_seqnum (event, seqnum);
2204 if (chain != ogg->current_chain) {
2205 /* switch to different chain, send segment on new chain */
2206 gst_ogg_demux_activate_chain (ogg, chain, event);
2208 /* mark discont and send segment on current chain */
2209 gst_ogg_chain_mark_discont (chain);
2210 /* This event should be sent from the streaming thread (sink pad task) */
2211 if (ogg->newsegment)
2212 gst_event_unref (ogg->newsegment);
2213 ogg->newsegment = event;
2216 /* notify start of new segment */
2217 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
2218 GstMessage *message;
2220 message = gst_message_new_segment_start (GST_OBJECT (ogg),
2221 GST_FORMAT_TIME, ogg->segment.last_stop);
2222 gst_message_set_seqnum (message, seqnum);
2224 gst_element_post_message (GST_ELEMENT (ogg), message);
2227 ogg->segment_running = TRUE;
2228 ogg->seqnum = seqnum;
2229 /* restart our task since it might have been stopped when we did the
2231 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
2235 /* streaming can continue now */
2236 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
2242 GST_DEBUG_OBJECT (ogg, "seek failed");
2247 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
2248 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
2253 /* finds each bitstream link one at a time using a bisection search
2254 * (has to begin by knowing the offset of the lb's initial page).
2255 * Recurses for each link so it can alloc the link storage after
2256 * finding them all, then unroll and fill the cache at the same time
2258 static GstFlowReturn
2259 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
2260 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
2262 gint64 endsearched = end;
2267 GstOggChain *nextchain;
2269 GST_LOG_OBJECT (ogg,
2270 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
2271 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
2273 /* the below guards against garbage seperating the last and
2274 * first pages of two links. */
2275 while (searched < endsearched) {
2278 if (endsearched - searched < CHUNKSIZE) {
2281 bisect = (searched + endsearched) / 2;
2284 gst_ogg_demux_seek (ogg, bisect);
2285 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
2287 if (ret == GST_FLOW_UNEXPECTED) {
2288 endsearched = bisect;
2289 } else if (ret == GST_FLOW_OK) {
2290 glong serial = ogg_page_serialno (&og);
2292 if (!gst_ogg_chain_has_stream (chain, serial)) {
2293 endsearched = bisect;
2296 searched = offset + og.header_len + og.body_len;
2302 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
2304 chain->end_offset = searched;
2305 ret = gst_ogg_demux_read_end_chain (ogg, chain);
2306 if (ret != GST_FLOW_OK)
2309 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
2311 gst_ogg_demux_seek (ogg, next);
2312 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
2313 if (ret == GST_FLOW_UNEXPECTED) {
2316 GST_LOG_OBJECT (ogg, "no next chain");
2317 } else if (ret != GST_FLOW_OK)
2320 if (searched < end && nextchain != NULL) {
2321 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
2322 end, nextchain, m + 1);
2323 if (ret != GST_FLOW_OK)
2326 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
2328 g_array_insert_val (ogg->chains, 0, chain);
2334 /* read a chain from the ogg file. This code will
2335 * read all BOS pages and will create and return a GstOggChain
2336 * structure with the results.
2338 * This function will also read N pages from each stream in the
2339 * chain and submit them to the decoders. When the decoder has
2340 * decoded the first buffer, we know the timestamp of the first
2341 * page in the chain.
2343 static GstFlowReturn
2344 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
2347 GstOggChain *chain = NULL;
2348 gint64 offset = ogg->offset;
2353 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
2355 /* first read the BOS pages, do typefind on them, create
2356 * the decoders, send data to the decoders. */
2361 ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
2362 if (ret != GST_FLOW_OK) {
2363 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
2366 if (!ogg_page_bos (&op)) {
2367 GST_WARNING_OBJECT (ogg, "page is not BOS page");
2368 /* if we did not find a chain yet, assume this is a bogus stream and
2371 ret = GST_FLOW_UNEXPECTED;
2375 if (chain == NULL) {
2376 chain = gst_ogg_chain_new (ogg);
2377 chain->offset = offset;
2380 serial = ogg_page_serialno (&op);
2381 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
2382 GST_WARNING_OBJECT (ogg, "found serial %08lx BOS page twice, ignoring",
2387 pad = gst_ogg_chain_new_stream (chain, serial);
2388 gst_ogg_pad_submit_page (pad, &op);
2391 if (ret != GST_FLOW_OK || chain == NULL) {
2392 if (ret == GST_FLOW_OK) {
2393 GST_WARNING_OBJECT (ogg, "no chain was found");
2394 ret = GST_FLOW_ERROR;
2395 } else if (ret != GST_FLOW_UNEXPECTED) {
2396 GST_WARNING_OBJECT (ogg, "failed to read chain");
2398 GST_DEBUG_OBJECT (ogg, "done reading chains");
2401 gst_ogg_chain_free (chain);
2408 chain->have_bos = TRUE;
2409 GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
2411 /* now read pages until we receive a buffer from each of the
2412 * stream decoders, this will tell us the timestamp of the
2413 * first packet in the chain then */
2415 /* save the offset to the first non bos page in the chain: if searching for
2416 * pad->first_time we read past the end of the chain, we'll seek back to this
2419 offset = ogg->offset;
2424 gboolean known_serial = FALSE;
2427 serial = ogg_page_serialno (&op);
2429 for (i = 0; i < chain->streams->len; i++) {
2430 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2432 GST_LOG_OBJECT (ogg, "serial %08lx time %" GST_TIME_FORMAT,
2433 pad->map.serialno, GST_TIME_ARGS (pad->start_time));
2435 if (pad->map.serialno == serial) {
2436 known_serial = TRUE;
2438 /* submit the page now, this will fill in the start_time when the
2439 * internal decoder finds it */
2440 gst_ogg_pad_submit_page (pad, &op);
2442 if (!pad->map.is_skeleton && pad->start_time == -1
2443 && ogg_page_eos (&op)) {
2444 /* got EOS on a pad before we could find its start_time.
2445 * We have no chance of finding a start_time for every pad so
2446 * stop searching for the other start_time(s).
2452 /* the timestamp will be filled in when we submit the pages */
2453 if (!pad->map.is_skeleton)
2454 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
2456 GST_LOG_OBJECT (ogg, "done %08lx now %d", pad->map.serialno, done);
2459 /* we read a page not belonging to the current chain: seek back to the
2460 * beginning of the chain
2462 if (!known_serial) {
2463 GST_LOG_OBJECT (ogg, "unknown serial %08lx", serial);
2464 gst_ogg_demux_seek (ogg, offset);
2469 ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
2470 if (ret != GST_FLOW_OK)
2474 GST_LOG_OBJECT (ogg, "done reading chain");
2475 /* now we can fill in the missing info using queries */
2476 for (i = 0; i < chain->streams->len; i++) {
2477 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2479 if (pad->map.is_skeleton)
2482 pad->mode = GST_OGG_PAD_MODE_STREAMING;
2491 /* read the last pages from the ogg stream to get the final
2494 static GstFlowReturn
2495 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
2497 gint64 begin = chain->end_offset;
2499 gint64 last_granule = -1;
2500 GstOggPad *last_pad = NULL;
2502 gboolean done = FALSE;
2511 gst_ogg_demux_seek (ogg, begin);
2513 /* now continue reading until we run out of data, if we find a page
2514 * start, we save it. It might not be the final page as there could be
2515 * another page after this one. */
2516 while (ogg->offset < end) {
2517 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
2519 if (ret == GST_FLOW_LIMIT)
2521 if (ret != GST_FLOW_OK)
2524 for (i = 0; i < chain->streams->len; i++) {
2525 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2527 if (pad->map.is_skeleton)
2530 if (pad->map.serialno == ogg_page_serialno (&og)) {
2531 gint64 granulepos = ogg_page_granulepos (&og);
2533 if (last_granule == -1 || last_granule < granulepos) {
2534 last_granule = granulepos;
2537 if (last_granule != -1) {
2547 chain->segment_stop =
2548 gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
2551 chain->segment_stop = GST_CLOCK_TIME_NONE;
2554 GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
2559 /* find a pad with a given serial number
2562 gst_ogg_demux_find_pad (GstOggDemux * ogg, glong serialno)
2567 /* first look in building chain if any */
2568 if (ogg->building_chain) {
2569 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
2574 /* then look in current chain if any */
2575 if (ogg->current_chain) {
2576 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
2581 for (i = 0; i < ogg->chains->len; i++) {
2582 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2584 pad = gst_ogg_chain_get_stream (chain, serialno);
2591 /* find a chain with a given serial number
2593 static GstOggChain *
2594 gst_ogg_demux_find_chain (GstOggDemux * ogg, glong serialno)
2598 pad = gst_ogg_demux_find_pad (ogg, serialno);
2605 /* returns TRUE if all streams have valid start time */
2607 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
2610 gboolean res = TRUE;
2612 chain->total_time = GST_CLOCK_TIME_NONE;
2613 chain->segment_start = G_MAXUINT64;
2615 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
2617 for (i = 0; i < chain->streams->len; i++) {
2618 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2620 if (pad->map.is_skeleton)
2623 /* can do this if the pad start time is not defined */
2624 if (pad->start_time == GST_CLOCK_TIME_NONE)
2627 chain->segment_start = MIN (chain->segment_start, pad->start_time);
2630 if (chain->segment_stop != GST_CLOCK_TIME_NONE
2631 && chain->segment_start != G_MAXUINT64)
2632 chain->total_time = chain->segment_stop - chain->segment_start;
2634 GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
2636 GST_DEBUG_OBJECT (ogg, "return %d", res);
2642 gst_ogg_demux_collect_info (GstOggDemux * ogg)
2646 /* collect all info */
2647 ogg->total_time = 0;
2649 for (i = 0; i < ogg->chains->len; i++) {
2650 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2652 chain->begin_time = ogg->total_time;
2654 gst_ogg_demux_collect_chain_info (ogg, chain);
2656 ogg->total_time += chain->total_time;
2658 gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time);
2661 /* find all the chains in the ogg file, this reads the first and
2662 * last page of the ogg stream, if they match then the ogg file has
2663 * just one chain, else we do a binary search for all chains.
2665 static GstFlowReturn
2666 gst_ogg_demux_find_chains (GstOggDemux * ogg)
2676 /* get peer to figure out length */
2677 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
2680 /* find length to read last page, we store this for later use. */
2681 format = GST_FORMAT_BYTES;
2682 res = gst_pad_query_duration (peer, &format, &ogg->length);
2683 gst_object_unref (peer);
2684 if (!res || ogg->length <= 0)
2687 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
2689 /* read chain from offset 0, this is the first chain of the
2691 gst_ogg_demux_seek (ogg, 0);
2692 ret = gst_ogg_demux_read_chain (ogg, &chain);
2693 if (ret != GST_FLOW_OK)
2694 goto no_first_chain;
2696 /* read page from end offset, we use this page to check if its serial
2697 * number is contained in the first chain. If this is the case then
2698 * this ogg is not a chained ogg and we can skip the scanning. */
2699 gst_ogg_demux_seek (ogg, ogg->length);
2700 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
2701 if (ret != GST_FLOW_OK)
2704 serialno = ogg_page_serialno (&og);
2706 if (!gst_ogg_chain_has_stream (chain, serialno)) {
2707 /* the last page is not in the first stream, this means we should
2708 * find all the chains in this chained ogg. */
2710 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
2713 /* we still call this function here but with an empty range so that
2714 * we can reuse the setup code in this routine. */
2716 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length,
2719 if (ret != GST_FLOW_OK)
2722 /* all fine, collect and print */
2723 gst_ogg_demux_collect_info (ogg);
2725 /* dump our chains and streams */
2726 gst_ogg_print (ogg);
2731 /*** error cases ***/
2734 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
2735 return GST_FLOW_NOT_LINKED;
2739 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
2740 return GST_FLOW_NOT_SUPPORTED;
2744 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
2745 return GST_FLOW_ERROR;
2749 GST_DEBUG_OBJECT (ogg, "can't get last page");
2751 gst_ogg_chain_free (chain);
2756 static GstFlowReturn
2757 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
2762 GstFlowReturn result = GST_FLOW_OK;
2764 serialno = ogg_page_serialno (page);
2765 granule = ogg_page_granulepos (page);
2767 GST_LOG_OBJECT (ogg,
2768 "processing ogg page (serial %08lx, pageno %ld, granulepos %"
2769 G_GINT64_FORMAT ", bos %d)",
2770 serialno, ogg_page_pageno (page), granule, ogg_page_bos (page));
2772 if (ogg_page_bos (page)) {
2776 /* see if we know about the chain already */
2777 chain = gst_ogg_demux_find_chain (ogg, serialno);
2782 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2783 start = chain->segment_start;
2785 /* create the newsegment event we are going to send out */
2786 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2787 GST_FORMAT_TIME, start, chain->segment_stop, chain->begin_time);
2788 gst_event_set_seqnum (event, ogg->seqnum);
2790 GST_DEBUG_OBJECT (ogg,
2791 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
2792 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
2793 GST_TIME_ARGS (chain->segment_stop),
2794 GST_TIME_ARGS (chain->begin_time));
2796 /* activate it as it means we have a non-header, this will also deactivate
2797 * the currently running chain. */
2798 gst_ogg_demux_activate_chain (ogg, chain, event);
2799 pad = gst_ogg_demux_find_pad (ogg, serialno);
2801 GstClockTime chain_time;
2802 GstOggChain *current_chain;
2803 gint64 current_time;
2805 /* this can only happen in push mode */
2809 current_chain = ogg->current_chain;
2810 current_time = ogg->segment.last_stop;
2812 /* time of new chain is current time */
2813 chain_time = current_time;
2815 if (ogg->building_chain == NULL) {
2816 GstOggChain *newchain;
2818 newchain = gst_ogg_chain_new (ogg);
2819 newchain->offset = 0;
2820 /* set new chain begin time aligned with end time of old chain */
2821 newchain->begin_time = chain_time;
2822 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
2823 GST_TIME_ARGS (chain_time));
2825 /* and this is the one we are building now */
2826 ogg->building_chain = newchain;
2828 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
2831 pad = gst_ogg_demux_find_pad (ogg, serialno);
2834 result = gst_ogg_pad_submit_page (pad, page);
2836 /* no pad. This means an ogg page without bos has been seen for this
2837 * serialno. we just ignore it but post a warning... */
2838 GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
2839 (NULL), ("unknown ogg pad for serial %08lx detected", serialno));
2847 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2848 (NULL), ("unknown ogg chain for serial %08lx detected", serialno));
2849 return GST_FLOW_ERROR;
2853 /* streaming mode, receive a buffer, parse it, create pads for
2854 * the serialno, submit pages and packets to the oggpads
2856 static GstFlowReturn
2857 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
2861 GstFlowReturn result = GST_FLOW_OK;
2863 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
2865 GST_DEBUG_OBJECT (ogg, "chain");
2866 result = gst_ogg_demux_submit_buffer (ogg, buffer);
2868 while (result == GST_FLOW_OK) {
2871 ret = ogg_sync_pageout (&ogg->sync, &page);
2873 /* need more data */
2876 /* discontinuity in the pages */
2877 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
2879 result = gst_ogg_demux_handle_page (ogg, &page);
2882 if (ret == 0 || result == GST_FLOW_OK) {
2883 gst_ogg_demux_sync_streams (ogg);
2889 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
2891 GstOggChain *chain = ogg->current_chain;
2896 for (i = 0; i < chain->streams->len; i++) {
2897 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2899 gst_event_ref (event);
2900 GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
2901 gst_pad_push_event (GST_PAD (pad), event);
2904 gst_event_unref (event);
2907 static GstFlowReturn
2908 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
2913 /* store the value */
2914 pad->last_ret = ret;
2916 /* any other error that is not-linked can be returned right
2918 if (ret != GST_FLOW_NOT_LINKED)
2921 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
2922 chain = ogg->current_chain;
2926 for (i = 0; i < chain->streams->len; i++) {
2927 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
2929 ret = opad->last_ret;
2930 /* some other return value (must be SUCCESS but we can return
2931 * other values as well) */
2932 if (ret != GST_FLOW_NOT_LINKED)
2935 /* if we get here, all other pads were unlinked and we return
2936 * NOT_LINKED then */
2942 static GstFlowReturn
2943 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
2948 if (ogg->offset == ogg->length) {
2949 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
2950 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
2951 ret = GST_FLOW_UNEXPECTED;
2955 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
2956 ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
2957 if (ret != GST_FLOW_OK) {
2958 GST_LOG_OBJECT (ogg, "Failed pull_range");
2962 ogg->offset += GST_BUFFER_SIZE (buffer);
2964 if (G_UNLIKELY (ogg->newsegment)) {
2965 gst_ogg_demux_send_event (ogg, ogg->newsegment);
2966 ogg->newsegment = NULL;
2969 ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
2970 if (ret != GST_FLOW_OK) {
2971 GST_LOG_OBJECT (ogg, "Failed demux_chain");
2975 /* check for the end of the segment */
2976 if (ogg->segment.stop != -1 && ogg->segment.last_stop != -1) {
2977 if (ogg->segment.last_stop > ogg->segment.stop) {
2978 ret = GST_FLOW_UNEXPECTED;
2988 * We read the pages backwards and send the packets forwards. The first packet
2989 * in the page will be pushed with the DISCONT flag set.
2991 * Special care has to be taken for continued pages, which we can only decode
2992 * when we have the previous page(s).
2994 static GstFlowReturn
2995 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
3001 if (ogg->offset == 0) {
3002 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
3003 " == 0", ogg->offset);
3004 ret = GST_FLOW_UNEXPECTED;
3008 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
3009 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
3010 if (ret != GST_FLOW_OK)
3013 ogg->offset = offset;
3015 if (G_UNLIKELY (ogg->newsegment)) {
3016 gst_ogg_demux_send_event (ogg, ogg->newsegment);
3017 ogg->newsegment = NULL;
3020 ret = gst_ogg_demux_handle_page (ogg, &page);
3021 if (ret != GST_FLOW_OK)
3024 /* check for the end of the segment */
3025 if (ogg->segment.start != -1 && ogg->segment.last_stop != -1) {
3026 if (ogg->segment.last_stop <= ogg->segment.start) {
3027 ret = GST_FLOW_UNEXPECTED;
3036 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
3042 chain = ogg->current_chain;
3043 cur = ogg->segment.last_stop;
3044 if (chain == NULL || cur == -1)
3047 for (i = 0; i < chain->streams->len; i++) {
3048 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
3050 /* Theoretically, we should be doing this for all streams, but we're only
3051 * doing it for known-to-be-sparse streams at the moment in order not to
3052 * break things for wrongly-muxed streams (like we used to produce once) */
3053 if (stream->is_sparse && stream->last_stop != GST_CLOCK_TIME_NONE) {
3055 /* Does this stream lag? Random threshold of 2 seconds */
3056 if (GST_CLOCK_DIFF (stream->last_stop, cur) > (2 * GST_SECOND)) {
3057 GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
3058 "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
3059 GST_TIME_ARGS (stream->last_stop), GST_TIME_ARGS (cur));
3060 stream->last_stop = cur;
3061 /* advance stream time (FIXME: is this right, esp. time_pos?) */
3062 gst_pad_push_event (GST_PAD_CAST (stream),
3063 gst_event_new_new_segment (TRUE, ogg->segment.rate,
3064 GST_FORMAT_TIME, stream->last_stop, -1, stream->last_stop));
3070 /* random access code
3072 * - first find all the chains and streams by scanning the file.
3073 * - then get and chain buffers, just like the streaming case.
3074 * - when seeking, we can use the chain info to perform the seek.
3077 gst_ogg_demux_loop (GstOggPad * pad)
3083 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
3085 if (ogg->need_chains) {
3088 /* this is the only place where we write chains and thus need to lock. */
3089 GST_CHAIN_LOCK (ogg);
3090 ret = gst_ogg_demux_find_chains (ogg);
3091 GST_CHAIN_UNLOCK (ogg);
3092 if (ret != GST_FLOW_OK)
3093 goto chain_read_failed;
3095 ogg->need_chains = FALSE;
3097 GST_OBJECT_LOCK (ogg);
3098 ogg->running = TRUE;
3101 GST_OBJECT_UNLOCK (ogg);
3103 /* and seek to configured positions without FLUSH */
3104 res = gst_ogg_demux_perform_seek (ogg, event);
3106 gst_event_unref (event);
3112 if (ogg->segment.rate >= 0.0)
3113 ret = gst_ogg_demux_loop_forward (ogg);
3115 ret = gst_ogg_demux_loop_reverse (ogg);
3117 if (ret != GST_FLOW_OK)
3120 gst_ogg_demux_sync_streams (ogg);
3126 /* error was posted */
3131 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
3132 ("failed to start demuxing ogg"));
3133 ret = GST_FLOW_ERROR;
3138 const gchar *reason = gst_flow_get_name (ret);
3139 GstEvent *event = NULL;
3141 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
3142 ogg->segment_running = FALSE;
3143 gst_pad_pause_task (ogg->sinkpad);
3145 if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
3146 if (ret == GST_FLOW_UNEXPECTED) {
3147 /* perform EOS logic */
3148 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3150 GstMessage *message;
3152 /* for segment playback we need to post when (in stream time)
3153 * we stopped, this is either stop (when set) or the duration. */
3154 if ((stop = ogg->segment.stop) == -1)
3155 stop = ogg->segment.duration;
3157 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
3159 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
3161 gst_message_set_seqnum (message, ogg->seqnum);
3163 gst_element_post_message (GST_ELEMENT (ogg), message);
3165 /* normal playback, send EOS to all linked pads */
3166 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
3167 event = gst_event_new_eos ();
3170 GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
3171 (_("Internal data stream error.")),
3172 ("stream stopped, reason %s", reason));
3173 event = gst_event_new_eos ();
3176 gst_event_set_seqnum (event, ogg->seqnum);
3177 gst_ogg_demux_send_event (ogg, event);
3185 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
3189 gst_ogg_demux_deactivate_current_chain (ogg);
3191 GST_CHAIN_LOCK (ogg);
3192 for (i = 0; i < ogg->chains->len; i++) {
3193 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3195 gst_ogg_chain_free (chain);
3197 ogg->chains = g_array_set_size (ogg->chains, 0);
3198 GST_CHAIN_UNLOCK (ogg);
3201 /* this function is called when the pad is activated and should start
3204 * We check if we can do random access to decide if we work push or
3208 gst_ogg_demux_sink_activate (GstPad * sinkpad)
3210 if (gst_pad_check_pull_range (sinkpad)) {
3211 GST_DEBUG_OBJECT (sinkpad, "activating pull");
3212 return gst_pad_activate_pull (sinkpad, TRUE);
3214 GST_DEBUG_OBJECT (sinkpad, "activating push");
3215 return gst_pad_activate_push (sinkpad, TRUE);
3219 /* this function gets called when we activate ourselves in push mode.
3220 * We cannot seek (ourselves) in the stream */
3222 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
3226 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
3228 ogg->pullmode = FALSE;
3233 /* this function gets called when we activate ourselves in pull mode.
3234 * We can perform random access to the resource and we start a task
3235 * to start reading */
3237 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
3241 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
3244 ogg->need_chains = TRUE;
3245 ogg->pullmode = TRUE;
3247 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3250 return gst_pad_stop_task (sinkpad);
3254 static GstStateChangeReturn
3255 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
3258 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
3260 ogg = GST_OGG_DEMUX (element);
3262 switch (transition) {
3263 case GST_STATE_CHANGE_NULL_TO_READY:
3265 ogg->have_fishead = FALSE;
3266 ogg_sync_init (&ogg->sync);
3268 case GST_STATE_CHANGE_READY_TO_PAUSED:
3269 ogg_sync_reset (&ogg->sync);
3270 ogg->running = FALSE;
3271 ogg->segment_running = FALSE;
3272 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
3274 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3280 result = parent_class->change_state (element, transition);
3282 switch (transition) {
3283 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3285 case GST_STATE_CHANGE_PAUSED_TO_READY:
3286 gst_ogg_demux_clear_chains (ogg);
3287 GST_OBJECT_LOCK (ogg);
3288 ogg->running = FALSE;
3289 ogg->segment_running = FALSE;
3290 ogg->have_fishead = FALSE;
3291 GST_OBJECT_UNLOCK (ogg);
3293 case GST_STATE_CHANGE_READY_TO_NULL:
3294 ogg_sync_clear (&ogg->sync);
3303 gst_ogg_demux_plugin_init (GstPlugin * plugin)
3305 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
3306 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
3307 "ogg demuxer setup stage when parsing pipeline");
3310 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
3312 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
3313 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3316 return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
3317 GST_TYPE_OGG_DEMUX);
3320 /* prints all info about the element */
3321 #undef GST_CAT_DEFAULT
3322 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
3324 #ifdef GST_DISABLE_GST_DEBUG
3327 gst_ogg_print (GstOggDemux * ogg)
3332 #else /* !GST_DISABLE_GST_DEBUG */
3335 gst_ogg_print (GstOggDemux * ogg)
3339 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
3340 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
3341 GST_TIME_ARGS (ogg->total_time));
3343 for (i = 0; i < ogg->chains->len; i++) {
3344 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3346 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
3347 GST_INFO_OBJECT (ogg, " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT,
3348 chain->offset, chain->end_offset);
3349 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
3350 GST_TIME_ARGS (chain->begin_time));
3351 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
3352 GST_TIME_ARGS (chain->total_time));
3353 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
3354 GST_TIME_ARGS (chain->segment_start));
3355 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
3356 GST_TIME_ARGS (chain->segment_stop));
3358 for (j = 0; j < chain->streams->len; j++) {
3359 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
3361 GST_INFO_OBJECT (ogg, " stream %08lx:", stream->map.serialno);
3362 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
3363 GST_TIME_ARGS (stream->start_time));
3367 #endif /* GST_DISABLE_GST_DEBUG */