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 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
44 * with newer GLib versions (>= 2.31.0) */
45 #define GLIB_DISABLE_DEPRECATION_WARNINGS
48 #include <gst/gst-i18n-plugin.h>
49 #include <gst/tag/tag.h>
51 #include "gstoggdemux.h"
53 #include "gst/glib-compat-private.h"
55 #define CHUNKSIZE (8500) /* this is out of vorbisfile */
57 /* we hope we get a granpos within this many bytes off the end */
58 #define DURATION_CHUNK_OFFSET (64*1024)
60 /* stop duration checks within this much of EOS */
61 #define EOS_AVOIDANCE_THRESHOLD 8192
63 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
64 #define GST_FLOW_SKIP_PUSH GST_FLOW_CUSTOM_SUCCESS_1
66 #define GST_CHAIN_LOCK(ogg) g_mutex_lock((ogg)->chain_lock)
67 #define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock((ogg)->chain_lock)
69 #define GST_PUSH_LOCK(ogg) \
71 GST_TRACE_OBJECT(ogg, "Push lock"); \
72 g_mutex_lock((ogg)->push_lock); \
75 #define GST_PUSH_UNLOCK(ogg) \
77 GST_TRACE_OBJECT(ogg, "Push unlock"); \
78 g_mutex_unlock((ogg)->push_lock); \
81 GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
82 GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
83 #define GST_CAT_DEFAULT gst_ogg_demux_debug
87 _ogg_packet_copy (const ogg_packet * packet)
89 ogg_packet *ret = g_slice_new (ogg_packet);
92 ret->packet = g_memdup (packet->packet, packet->bytes);
98 _ogg_packet_free (ogg_packet * packet)
100 g_free (packet->packet);
101 g_slice_free (ogg_packet, packet);
105 gst_ogg_page_copy (ogg_page * page)
107 ogg_page *p = g_slice_new (ogg_page);
109 /* make a copy of the page */
110 p->header = g_memdup (page->header, page->header_len);
111 p->header_len = page->header_len;
112 p->body = g_memdup (page->body, page->body_len);
113 p->body_len = page->body_len;
119 gst_ogg_page_free (ogg_page * page)
121 g_free (page->header);
123 g_slice_free (ogg_page, page);
126 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
127 GstOggChain * chain);
128 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
129 GstOggChain * chain, GstEvent * event);
130 static void gst_ogg_pad_mark_discont (GstOggPad * pad);
131 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
133 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
135 static gboolean gst_ogg_demux_receive_event (GstElement * element,
138 static void gst_ogg_pad_dispose (GObject * object);
139 static void gst_ogg_pad_finalize (GObject * object);
141 static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
142 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
143 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
144 static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
145 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
148 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
149 GstOggPad * pad, GstFlowReturn ret);
150 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
152 GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg,
153 GstCaps * caps, GList * headers);
154 static gboolean gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
155 static gboolean gst_ogg_demux_perform_seek_push (GstOggDemux * ogg,
157 static gboolean gst_ogg_demux_check_duration_push (GstOggDemux * ogg,
158 GstSeekFlags flags, GstEvent * event);
160 GType gst_ogg_pad_get_type (void);
161 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
164 gst_ogg_pad_class_init (GstOggPadClass * klass)
166 GObjectClass *gobject_class;
168 gobject_class = (GObjectClass *) klass;
170 gobject_class->dispose = gst_ogg_pad_dispose;
171 gobject_class->finalize = gst_ogg_pad_finalize;
175 gst_ogg_pad_init (GstOggPad * pad)
177 gst_pad_set_event_function (GST_PAD (pad),
178 GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
179 gst_pad_set_getcaps_function (GST_PAD (pad),
180 GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps));
181 gst_pad_set_query_type_function (GST_PAD (pad),
182 GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types));
183 gst_pad_set_query_function (GST_PAD (pad),
184 GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
186 pad->mode = GST_OGG_PAD_MODE_INIT;
188 pad->current_granule = -1;
189 pad->keyframe_granule = -1;
191 pad->start_time = GST_CLOCK_TIME_NONE;
193 pad->last_stop = GST_CLOCK_TIME_NONE;
195 pad->have_type = FALSE;
196 pad->continued = NULL;
197 pad->map.headers = NULL;
198 pad->map.queued = NULL;
200 pad->map.granulerate_n = 0;
201 pad->map.granulerate_d = 0;
202 pad->map.granuleshift = -1;
206 gst_ogg_pad_dispose (GObject * object)
208 GstOggPad *pad = GST_OGG_PAD (object);
213 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
214 g_list_free (pad->map.headers);
215 pad->map.headers = NULL;
216 g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
217 g_list_free (pad->map.queued);
218 pad->map.queued = NULL;
220 g_free (pad->map.index);
221 pad->map.index = NULL;
223 /* clear continued pages */
224 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
225 g_list_free (pad->continued);
226 pad->continued = NULL;
229 gst_caps_unref (pad->map.caps);
230 pad->map.caps = NULL;
233 if (pad->map.taglist) {
234 gst_tag_list_free (pad->map.taglist);
235 pad->map.taglist = NULL;
238 ogg_stream_reset (&pad->map.stream);
240 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
244 gst_ogg_pad_finalize (GObject * object)
246 GstOggPad *pad = GST_OGG_PAD (object);
248 ogg_stream_clear (&pad->map.stream);
250 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
253 static const GstQueryType *
254 gst_ogg_pad_query_types (GstPad * pad)
256 static const GstQueryType query_types[] = {
266 gst_ogg_pad_getcaps (GstPad * pad)
268 return gst_caps_ref (GST_PAD_CAPS (pad));
272 gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
277 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
279 switch (GST_QUERY_TYPE (query)) {
280 case GST_QUERY_DURATION:
283 gint64 total_time = -1;
285 gst_query_parse_duration (query, &format, NULL);
286 /* can only get position in time */
287 if (format != GST_FORMAT_TIME)
290 if (ogg->total_time != -1) {
291 /* we can return the total length */
292 total_time = ogg->total_time;
294 gint bitrate = ogg->bitrate;
296 /* try with length and bitrate */
300 /* ask upstream for total length in bytes */
301 uquery = gst_query_new_duration (GST_FORMAT_BYTES);
302 if (gst_pad_peer_query (ogg->sinkpad, uquery)) {
305 gst_query_parse_duration (uquery, NULL, &length);
307 /* estimate using the bitrate */
309 gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);
312 "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
313 GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
315 gst_query_unref (uquery);
319 gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
322 case GST_QUERY_SEEKING:
326 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
327 if (format == GST_FORMAT_TIME) {
328 gboolean seekable = FALSE;
333 stop = ogg->total_time;
334 } else if (ogg->push_disable_seeking) {
336 } else if (ogg->current_chain->streams->len) {
340 for (i = 0; i < ogg->current_chain->streams->len; i++) {
342 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
345 if (pad->map.index != NULL && pad->map.n_index != 0) {
347 GstClockTime idx_time;
349 idx = &pad->map.index[pad->map.n_index - 1];
351 gst_util_uint64_scale (idx->timestamp, GST_SECOND,
356 stop = MAX (idx_time, stop);
358 stop = -1; /* we've no clue, sadly, without seeking */
363 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
371 res = gst_pad_query_default (pad, query);
375 gst_object_unref (ogg);
382 GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
389 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
394 ogg = GST_OGG_DEMUX (element);
396 switch (GST_EVENT_TYPE (event)) {
398 /* now do the seek */
399 res = gst_ogg_demux_perform_seek (ogg, event);
400 gst_event_unref (event);
403 GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
412 GST_DEBUG_OBJECT (ogg, "error handling event");
413 gst_event_unref (event);
419 gst_ogg_pad_event (GstPad * pad, GstEvent * event)
424 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
426 switch (GST_EVENT_TYPE (event)) {
428 /* now do the seek */
429 res = gst_ogg_demux_perform_seek (ogg, event);
430 gst_event_unref (event);
433 res = gst_pad_event_default (pad, event);
436 gst_object_unref (ogg);
442 gst_ogg_pad_reset (GstOggPad * pad)
444 ogg_stream_reset (&pad->map.stream);
446 GST_DEBUG_OBJECT (pad, "doing reset");
448 /* clear continued pages */
449 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
450 g_list_free (pad->continued);
451 pad->continued = NULL;
453 pad->last_ret = GST_FLOW_OK;
454 pad->last_stop = GST_CLOCK_TIME_NONE;
455 pad->current_granule = -1;
456 pad->keyframe_granule = -1;
460 /* queue data, basically takes the packet, puts it in a buffer and store the
461 * buffer in the queued list. */
463 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
465 #ifndef GST_DISABLE_GST_DEBUG
466 GstOggDemux *ogg = pad->ogg;
469 GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x",
470 pad, pad->map.serialno);
472 pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
479 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
480 gboolean push_headers)
482 GstBuffer *buf = NULL;
483 GstFlowReturn ret, cret;
484 GstOggDemux *ogg = pad->ogg;
490 GstClockTime out_timestamp, out_duration;
491 guint64 out_offset, out_offset_end;
492 gboolean delta_unit = FALSE;
496 GST_DEBUG_OBJECT (pad, "Chaining %d %d %" GST_TIME_FORMAT " %d %p",
497 ogg->pullmode, ogg->push_state, GST_TIME_ARGS (ogg->push_time_length),
498 ogg->push_disable_seeking, ogg->building_chain);
500 if (!ogg->pullmode && ogg->push_state == PUSH_PLAYING
501 && ogg->push_time_length == GST_CLOCK_TIME_NONE
502 && !ogg->push_disable_seeking) {
503 if (!ogg->building_chain) {
504 /* we got all headers, now try to get duration */
505 if (!gst_ogg_demux_check_duration_push (ogg, GST_SEEK_FLAG_FLUSH, NULL)) {
506 GST_PUSH_UNLOCK (ogg);
510 GST_PUSH_UNLOCK (ogg);
513 GST_PUSH_UNLOCK (ogg);
515 GST_DEBUG_OBJECT (ogg,
516 "%p streaming to peer serial %08x", pad, pad->map.serialno);
518 if (pad->map.is_ogm) {
522 data = packet->packet;
523 bytes = packet->bytes;
528 if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
529 /* We don't push header packets for OGM */
533 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
534 delta_unit = (((data[0] & 0x08) >> 3) == 0);
538 /* Strip trailing \0 for subtitles */
539 if (pad->map.is_ogm_text) {
540 while (bytes && data[bytes - 1] == 0) {
545 } else if (pad->map.is_vp8) {
546 if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
548 (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) {
549 /* We don't push header packets for VP8 */
559 /* get timing info for the packet */
560 if (gst_ogg_stream_packet_is_header (&pad->map, packet)) {
562 GST_DEBUG_OBJECT (ogg, "packet is header");
564 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
565 GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
569 out_timestamp = GST_CLOCK_TIME_NONE;
570 out_duration = GST_CLOCK_TIME_NONE;
574 if (packet->granulepos != -1) {
575 pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
577 pad->keyframe_granule =
578 gst_ogg_stream_granulepos_to_key_granule (&pad->map,
580 GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
581 pad->current_granule);
582 } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
583 pad->current_granule += duration;
584 if (gst_ogg_stream_packet_is_key_frame (&pad->map, packet)) {
585 pad->keyframe_granule = pad->current_granule;
587 GST_DEBUG_OBJECT (ogg, "interpolating granule %" G_GUINT64_FORMAT,
588 pad->current_granule);
590 if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
591 /* negative rates, only set timestamp on the packets with a granulepos */
597 /* we only push buffers after we have a valid granule. This is done so that
598 * we nicely skip packets without a timestamp after a seek. This is ok
599 * because we base or seek on the packet after the page with the smaller
601 if (pad->current_granule == -1)
604 if (pad->map.is_ogm) {
605 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
606 pad->current_granule);
607 out_duration = gst_util_uint64_scale (duration,
608 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
609 } else if (pad->map.is_sparse) {
610 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
611 pad->current_granule);
612 if (duration == GST_CLOCK_TIME_NONE) {
613 out_duration = GST_CLOCK_TIME_NONE;
615 out_duration = gst_util_uint64_scale (duration,
616 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
619 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
620 pad->current_granule - duration);
622 gst_ogg_stream_granule_to_time (&pad->map,
623 pad->current_granule) - out_timestamp;
626 gst_ogg_stream_granule_to_granulepos (&pad->map,
627 pad->current_granule, pad->keyframe_granule);
629 gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
633 if (pad->map.is_ogm_text) {
634 /* check for invalid buffer sizes */
635 if (G_UNLIKELY (offset + trim >= packet->bytes))
642 buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
643 gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
645 /* set delta flag for OGM content */
647 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
649 /* copy packet in buffer */
650 memcpy (buf->data, packet->packet + offset, packet->bytes - offset - trim);
652 GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
653 GST_BUFFER_DURATION (buf) = out_duration;
654 GST_BUFFER_OFFSET (buf) = out_offset;
655 GST_BUFFER_OFFSET_END (buf) = out_offset_end;
657 /* Mark discont on the buffer */
659 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
660 pad->discont = FALSE;
663 pad->last_stop = ogg->segment.last_stop;
665 /* don't push the header packets when we are asked to skip them */
666 if (!packet->b_o_s || push_headers) {
667 ret = gst_pad_push (GST_PAD_CAST (pad), buf);
671 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
674 /* we're done with skeleton stuff */
675 if (pad->map.is_skeleton)
678 /* check if valid granulepos, then we can calculate the current
679 * position. We know the granule for each packet but we only want to update
680 * the last_stop when we have a valid granulepos on the packet because else
681 * our time jumps around for the different streams. */
682 if (packet->granulepos < 0)
685 /* convert to time */
686 current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
689 /* convert to stream time */
690 if ((chain = pad->chain)) {
691 gint64 chain_start = 0;
693 if (chain->segment_start != GST_CLOCK_TIME_NONE)
694 chain_start = chain->segment_start;
696 current_time = current_time - chain_start + chain->begin_time;
699 /* and store as the current position */
700 gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time);
702 GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
703 GST_TIME_ARGS (current_time));
705 /* check stream eos */
706 if ((ogg->segment.rate > 0.0 && ogg->segment.stop != GST_CLOCK_TIME_NONE &&
707 current_time > ogg->segment.stop) ||
708 (ogg->segment.rate < 0.0 && ogg->segment.start != GST_CLOCK_TIME_NONE &&
709 current_time < ogg->segment.start)) {
710 GST_DEBUG_OBJECT (ogg, "marking pad %p EOS", pad);
716 gst_buffer_unref (buf);
717 /* return combined flow result */
723 GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
729 GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
734 GST_DEBUG_OBJECT (ogg, "pad not added yet");
740 gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
743 guint64 start_time = G_MAXUINT64;
745 for (i = 0; i < chain->streams->len; i++) {
746 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
748 if (pad->map.is_skeleton)
751 /* can do this if the pad start time is not defined */
752 GST_DEBUG_OBJECT (ogg, "Pad %08x (%s) start time is %" GST_TIME_FORMAT,
753 pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map),
754 GST_TIME_ARGS (pad->start_time));
755 if (pad->start_time == GST_CLOCK_TIME_NONE) {
756 if (!pad->map.is_sparse) {
757 start_time = G_MAXUINT64;
761 start_time = MIN (start_time, pad->start_time);
768 gst_ogg_demux_collect_sync_time (GstOggDemux * ogg, GstOggChain * chain)
771 GstClockTime sync_time = GST_CLOCK_TIME_NONE;
774 GST_WARNING_OBJECT (ogg, "No chain!");
775 return GST_CLOCK_TIME_NONE;
778 for (i = 0; i < chain->streams->len; i++) {
779 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
781 if (pad->map.is_sparse)
784 if (pad->push_sync_time == GST_CLOCK_TIME_NONE) {
785 sync_time = GST_CLOCK_TIME_NONE;
788 if (sync_time == GST_CLOCK_TIME_NONE)
789 sync_time = pad->push_sync_time;
791 sync_time = MAX (sync_time, pad->push_sync_time);
797 /* submit a packet to the oggpad, this function will run the
798 * typefind code for the pad if this is the first packet for this
802 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
805 GstFlowReturn ret = GST_FLOW_OK;
807 GstOggDemux *ogg = pad->ogg;
809 GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x",
810 pad, pad->map.serialno);
812 if (!pad->have_type) {
813 pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
814 if (!pad->have_type) {
815 pad->map.caps = gst_caps_new_simple ("application/x-unknown", NULL);
817 if (pad->map.is_skeleton) {
818 GST_DEBUG_OBJECT (ogg, "we have a fishead");
819 /* copy values over to global ogg level */
820 ogg->basetime = pad->map.basetime;
821 ogg->prestime = pad->map.prestime;
823 /* use total time to update the total ogg time */
824 if (ogg->total_time == -1) {
825 ogg->total_time = pad->map.total_time;
826 } else if (pad->map.total_time > 0) {
827 ogg->total_time = MAX (ogg->total_time, pad->map.total_time);
831 gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
833 GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
837 if (pad->map.is_skeleton) {
842 /* try to parse the serialno first */
843 if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
846 GST_DEBUG_OBJECT (pad->ogg,
847 "got skeleton packet for stream 0x%08x", serialno);
849 skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
852 case GST_OGG_SKELETON_FISBONE:
853 /* parse the remainder of the fisbone in the pad with the serialno,
854 * note that we ignore the start_time as this is usually wrong for
856 gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
857 packet->bytes, NULL);
859 case GST_OGG_SKELETON_INDEX:
860 gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
863 /* use total time to update the total ogg time */
864 if (ogg->total_time == -1) {
865 ogg->total_time = skel_pad->map.total_time;
866 } else if (skel_pad->map.total_time > 0) {
867 ogg->total_time = MAX (ogg->total_time, skel_pad->map.total_time);
875 GST_WARNING_OBJECT (pad->ogg,
876 "found skeleton fisbone for an unknown stream 0x%08x", serialno);
881 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
884 GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
885 pad->current_granule = granule;
888 /* restart header packet count when seeing a b_o_s page;
889 * particularly useful following a seek or even following chain finding */
891 GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
892 pad->map.n_header_packets_seen = 0;
893 if (!pad->map.have_headers) {
894 GST_DEBUG_OBJECT (ogg, "clearing header packets");
895 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
896 g_list_free (pad->map.headers);
897 pad->map.headers = NULL;
901 /* Overload the value of b_o_s in ogg_packet with a flag whether or
902 * not this is a header packet. Maybe some day this could be cleaned
904 packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
905 if (!packet->b_o_s) {
906 GST_DEBUG ("found non-header packet");
907 pad->map.have_headers = TRUE;
908 if (pad->start_time == GST_CLOCK_TIME_NONE) {
909 gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
910 GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
911 if (duration != -1) {
912 pad->map.accumulated_granule += duration;
913 GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
914 pad->map.accumulated_granule);
917 if (packet->granulepos != -1) {
918 ogg_int64_t start_granule;
921 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
924 if (granule > pad->map.accumulated_granule)
925 start_granule = granule - pad->map.accumulated_granule;
929 pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
931 GST_DEBUG_OBJECT (ogg,
932 "start time %" GST_TIME_FORMAT " (%" GST_TIME_FORMAT ") for %s "
933 "from granpos %" G_GINT64_FORMAT " (granule %" G_GINT64_FORMAT ", "
934 "accumulated granule %" G_GINT64_FORMAT,
935 GST_TIME_ARGS (pad->start_time), GST_TIME_ARGS (pad->start_time),
936 gst_ogg_stream_get_media_type (&pad->map),
937 (gint64) packet->granulepos, granule, pad->map.accumulated_granule);
939 packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
940 pad->map.accumulated_granule, pad->keyframe_granule);
944 /* look for tags in header packet (before inc header count) */
945 gst_ogg_stream_extract_tags (&pad->map, packet);
946 pad->map.n_header_packets_seen++;
947 if (!pad->map.have_headers) {
949 g_list_append (pad->map.headers, _ogg_packet_copy (packet));
950 GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
954 /* we know the start_time of the pad data, see if we
955 * can activate the complete chain if this is a dynamic
956 * chain. We need all the headers too for this. */
957 if (pad->start_time != GST_CLOCK_TIME_NONE && pad->map.have_headers) {
958 GstOggChain *chain = pad->chain;
960 /* check if complete chain has start time */
961 if (chain == ogg->building_chain) {
962 GstEvent *event = NULL;
967 GST_DEBUG_OBJECT (ogg, "need to resync");
969 /* when we need to resync after a seek, we wait until we have received
970 * timestamps on all streams */
971 start_time = gst_ogg_demux_collect_start_time (ogg, chain);
973 if (start_time != G_MAXUINT64) {
976 GST_DEBUG_OBJECT (ogg, "start_time: %" GST_TIME_FORMAT,
977 GST_TIME_ARGS (start_time));
979 if (chain->segment_start < start_time)
981 (start_time - chain->segment_start) + chain->begin_time;
983 segment_time = chain->begin_time;
985 /* create the newsegment event we are going to send out */
987 if (!ogg->pullmode && ogg->push_state == PUSH_LINEAR2) {
988 /* if we are fast forwarding to the actual seek target,
989 ensure previous frames are clipped */
990 GST_DEBUG_OBJECT (ogg,
991 "Resynced, starting segment at %" GST_TIME_FORMAT
992 ", start_time %" GST_TIME_FORMAT,
993 GST_TIME_ARGS (ogg->push_seek_time_original_target),
994 GST_TIME_ARGS (start_time));
996 gst_event_new_new_segment (FALSE, ogg->push_seek_rate,
997 GST_FORMAT_TIME, ogg->push_seek_time_original_target, -1,
998 ogg->push_seek_time_original_target);
999 ogg->push_state = PUSH_PLAYING;
1001 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
1002 GST_FORMAT_TIME, start_time, chain->segment_stop, segment_time);
1004 GST_PUSH_UNLOCK (ogg);
1006 ogg->resync = FALSE;
1009 /* see if we have enough info to activate the chain, we have enough info
1010 * when all streams have a valid start time. */
1011 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,
1028 gst_event_set_seqnum (event, ogg->seqnum);
1030 gst_ogg_demux_activate_chain (ogg, chain, event);
1032 ogg->building_chain = NULL;
1037 /* if we are building a chain, store buffer for when we activate
1038 * it. This path is taken if we operate in streaming mode. */
1039 if (ogg->building_chain) {
1040 /* bos packets where stored in the header list so we can discard
1043 ret = gst_ogg_demux_queue_data (pad, packet);
1045 /* else we are completely streaming to the peer */
1047 ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
1052 /* flush at most @npackets from the stream layer. All packets if
1055 static GstFlowReturn
1056 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1058 GstFlowReturn result = GST_FLOW_OK;
1059 gboolean done = FALSE;
1068 ret = ogg_stream_packetout (&pad->map.stream, &packet);
1071 GST_LOG_OBJECT (ogg, "packetout done");
1075 GST_LOG_OBJECT (ogg, "packetout discont");
1076 if (!pad->map.is_sparse) {
1077 gst_ogg_chain_mark_discont (pad->chain);
1079 gst_ogg_pad_mark_discont (pad);
1083 GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1084 if (packet.bytes > ogg->max_packet_size)
1085 ogg->max_packet_size = packet.bytes;
1086 result = gst_ogg_pad_submit_packet (pad, &packet);
1087 /* not linked is not a problem, it's possible that we are still
1088 * collecting headers and that we don't have exposed the pads yet */
1089 if (result == GST_FLOW_NOT_LINKED)
1091 else if (result <= GST_FLOW_UNEXPECTED)
1092 goto could_not_submit;
1095 GST_WARNING_OBJECT (ogg,
1096 "invalid return value %d for ogg_stream_packetout, resetting stream",
1098 gst_ogg_pad_reset (pad);
1103 done = (npackets == 0);
1111 GST_WARNING_OBJECT (ogg,
1112 "could not submit packet for stream %08x, "
1113 "error: %d", pad->map.serialno, result);
1114 gst_ogg_pad_reset (pad);
1120 gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg)
1122 if (ogg->push_last_seek_time >= ogg->push_seek_time_target) {
1123 GST_DEBUG_OBJECT (ogg, "We overshot by %" GST_TIME_FORMAT,
1124 GST_TIME_ARGS (ogg->push_last_seek_time - ogg->push_seek_time_target));
1125 ogg->push_offset1 = ogg->push_last_seek_offset;
1126 ogg->push_time1 = ogg->push_last_seek_time;
1127 ogg->seek_undershot = FALSE;
1129 GST_DEBUG_OBJECT (ogg, "We undershot by %" GST_TIME_FORMAT,
1130 GST_TIME_ARGS (ogg->push_seek_time_target - ogg->push_last_seek_time));
1131 ogg->push_offset0 = ogg->push_last_seek_offset;
1132 ogg->push_time0 = ogg->push_last_seek_time;
1133 ogg->seek_undershot = TRUE;
1138 gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg, float seek_quality)
1141 gint64 segment_bitrate;
1144 /* we might not know the length of the stream in time,
1145 so push_time1 might not be set */
1146 GST_DEBUG_OBJECT (ogg,
1147 "push time 1: %" GST_TIME_FORMAT ", dbytes %" G_GINT64_FORMAT,
1148 GST_TIME_ARGS (ogg->push_time1), ogg->push_offset1 - ogg->push_offset0);
1149 if (ogg->push_time1 == GST_CLOCK_TIME_NONE) {
1150 GST_DEBUG_OBJECT (ogg,
1151 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1152 ", time %" GST_TIME_FORMAT " (open ended)", ogg->push_offset0,
1153 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0));
1154 if (ogg->push_last_seek_time == ogg->push_start_time) {
1155 /* if we're at start and don't know the end time, we can't estimate
1156 bitrate, so get the nominal declared bitrate as a failsafe, or some
1157 random constant which will be discarded after we made a (probably
1158 dire) first guess */
1159 segment_bitrate = (ogg->bitrate > 0 ? ogg->bitrate : 1000);
1162 gst_util_uint64_scale (ogg->push_last_seek_offset - 0,
1163 8 * GST_SECOND, ogg->push_last_seek_time - ogg->push_start_time);
1167 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1168 segment_bitrate, 8 * GST_SECOND);
1169 ogg->seek_secant = TRUE;
1171 GST_DEBUG_OBJECT (ogg,
1172 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1173 ", time %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, ogg->push_offset0,
1174 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
1175 GST_TIME_ARGS (ogg->push_time1));
1176 if (ogg->push_time0 == ogg->push_time1) {
1177 best = ogg->push_offset0;
1180 gst_util_uint64_scale (ogg->push_offset1 - ogg->push_offset0,
1181 8 * GST_SECOND, ogg->push_time1 - ogg->push_time0);
1182 GST_DEBUG_OBJECT (ogg,
1183 "Local bitrate on the %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1184 " segment: %" G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_time0),
1185 GST_TIME_ARGS (ogg->push_time1), segment_bitrate);
1189 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1190 segment_bitrate, 8 * GST_SECOND);
1191 if (seek_quality < 0.5f && ogg->seek_secant) {
1192 gint64 new_best, best2 = (ogg->push_offset0 + ogg->push_offset1) / 2;
1193 /* if dire result, give as much as 25% weight to a dumb bisection guess */
1194 float secant_weight = 1.0f - ((0.5 - seek_quality) / 0.5f) * 0.25;
1195 new_best = (best * secant_weight + best2 * (1.0f - secant_weight));
1196 GST_DEBUG_OBJECT (ogg,
1197 "Secant says %" G_GINT64_FORMAT ", straight is %" G_GINT64_FORMAT
1198 ", new best %" G_GINT64_FORMAT " with secant_weight %f", best,
1199 best2, new_best, secant_weight);
1201 ogg->seek_secant = FALSE;
1203 ogg->seek_secant = TRUE;
1208 GST_DEBUG_OBJECT (ogg, "Raw best guess: %" G_GINT64_FORMAT, best);
1210 /* offset the guess down as we need to capture the start of the
1211 page we are targetting - but only do so if we did not undershoot
1212 last time, as we're likely to still do this time */
1213 if (!ogg->seek_undershot) {
1214 /* very small packets are packed on pages, so offset by at least
1215 a value which is likely to get us at least one page where the
1218 ogg->max_packet_size >
1219 ogg->max_page_size ? ogg->max_packet_size : ogg->max_page_size;
1220 GST_DEBUG_OBJECT (ogg, "Offsetting by %" G_GINT64_FORMAT, skew);
1224 /* do not seek too close to the bounds, as we stop seeking
1225 when we get to within max_packet_size before the target */
1226 if (best > ogg->push_offset1 - ogg->max_packet_size) {
1227 best = ogg->push_offset1 - ogg->max_packet_size;
1228 GST_DEBUG_OBJECT (ogg,
1229 "Too close to high bound, pushing back to %" G_GINT64_FORMAT, best);
1230 } else if (best < ogg->push_offset0 + ogg->max_packet_size) {
1231 best = ogg->push_offset0 + ogg->max_packet_size;
1232 GST_DEBUG_OBJECT (ogg,
1233 "Too close to low bound, pushing forth to %" G_GINT64_FORMAT, best);
1236 /* keep within bounds */
1237 if (best > ogg->push_offset1)
1238 best = ogg->push_offset1;
1239 if (best < ogg->push_offset0)
1240 best = ogg->push_offset0;
1242 GST_DEBUG_OBJECT (ogg, "Choosing target %" G_GINT64_FORMAT, best);
1247 gst_ogg_demux_record_keyframe_time (GstOggDemux * ogg, GstOggPad * pad,
1248 ogg_int64_t granpos)
1251 GstClockTime kf_time;
1253 kf_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, granpos);
1254 kf_time = gst_ogg_stream_granule_to_time (&pad->map, kf_granule);
1256 pad->push_kf_time = kf_time;
1259 /* returns the earliest keyframe time for all non sparse pads in the chain,
1260 * if known, and GST_CLOCK_TIME_NONE if not */
1262 gst_ogg_demux_get_earliest_keyframe_time (GstOggDemux * ogg)
1264 GstClockTime t = GST_CLOCK_TIME_NONE;
1265 GstOggChain *chain = ogg->building_chain;
1269 GST_WARNING_OBJECT (ogg, "No chain!");
1270 return GST_CLOCK_TIME_NONE;
1272 for (i = 0; i < chain->streams->len; i++) {
1273 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1275 if (pad->map.is_sparse)
1277 if (pad->push_kf_time == GST_CLOCK_TIME_NONE)
1278 return GST_CLOCK_TIME_NONE;
1279 if (t == GST_CLOCK_TIME_NONE || pad->push_kf_time < t)
1280 t = pad->push_kf_time;
1286 /* MUST be called with the push lock locked, and will unlock it
1287 regardless of return value. */
1288 static GstFlowReturn
1289 gst_ogg_demux_seek_back_after_push_duration_check_unlock (GstOggDemux * ogg)
1293 /* Get the delayed event, if any */
1294 event = ogg->push_mode_seek_delayed_event;
1295 ogg->push_mode_seek_delayed_event = NULL;
1297 ogg->push_state = PUSH_PLAYING;
1299 GST_PUSH_UNLOCK (ogg);
1302 /* If there is one, perform it */
1303 gst_ogg_demux_perform_seek_push (ogg, event);
1305 /* If there wasn't, seek back at start to start normal playback */
1306 GST_INFO_OBJECT (ogg, "Seeking back to 0 after duration check");
1307 event = gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1308 GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
1309 GST_SEEK_TYPE_SET, 1, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
1310 if (!gst_pad_push_event (ogg->sinkpad, event)) {
1311 GST_WARNING_OBJECT (ogg, "Failed seeking back to start");
1312 return GST_FLOW_ERROR;
1320 gst_ogg_demux_estimate_seek_quality (GstOggDemux * ogg)
1322 gint64 diff; /* how far from the goal we ended up */
1323 gint64 dist; /* how far we moved this iteration */
1326 if (ogg->push_prev_seek_time == GST_CLOCK_TIME_NONE) {
1327 /* for the first seek, we pretend we got a good seek,
1328 as we don't have a previous seek yet */
1332 /* We take a guess at how good the last seek was at guessing
1333 the byte target by comparing the amplitude of the last
1334 seek to the error */
1335 diff = ogg->push_seek_time_target - ogg->push_last_seek_time;
1338 dist = ogg->push_last_seek_time - ogg->push_prev_seek_time;
1342 seek_quality = (dist == 0) ? 0.0f : 1.0f / (1.0f + diff / (float) dist);
1344 GST_DEBUG_OBJECT (ogg,
1345 "We moved %" GST_TIME_FORMAT ", we're off by %" GST_TIME_FORMAT
1346 ", seek quality %f", GST_TIME_ARGS (dist), GST_TIME_ARGS (diff),
1348 return seek_quality;
1352 gst_ogg_demux_update_bisection_stats (GstOggDemux * ogg)
1356 GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps",
1357 ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]);
1359 for (n = 0; n < 2; ++n) {
1360 ogg->stats_bisection_steps[n] += ogg->push_bisection_steps[n];
1361 if (ogg->stats_bisection_max_steps[n] < ogg->push_bisection_steps[n])
1362 ogg->stats_bisection_max_steps[n] = ogg->push_bisection_steps[n];
1364 ogg->stats_nbisections++;
1366 GST_INFO_OBJECT (ogg,
1367 "So far, %.2f + %.2f bisections needed per seek (max %d + %d)",
1368 ogg->stats_bisection_steps[0] / (float) ogg->stats_nbisections,
1369 ogg->stats_bisection_steps[1] / (float) ogg->stats_nbisections,
1370 ogg->stats_bisection_max_steps[0], ogg->stats_bisection_max_steps[1]);
1374 gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
1376 GstOggDemux *ogg = pad->ogg;
1377 ogg_int64_t granpos = ogg_page_granulepos (page);
1379 GST_PUSH_LOCK (ogg);
1381 if (ogg->push_start_time == GST_CLOCK_TIME_NONE) {
1382 ogg->push_start_time =
1383 gst_ogg_stream_get_start_time_for_granulepos (&pad->map, granpos);
1384 GST_DEBUG_OBJECT (ogg, "Stream start time: %" GST_TIME_FORMAT,
1385 GST_TIME_ARGS (ogg->push_start_time));
1387 ogg->push_time_offset =
1388 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1389 if (ogg->push_time_offset > 0) {
1390 GST_DEBUG_OBJECT (ogg, "Bitrate since start: %" G_GUINT64_FORMAT,
1391 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
1392 ogg->push_time_offset));
1395 if (ogg->push_state == PUSH_DURATION) {
1397 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1399 if (ogg->total_time == GST_CLOCK_TIME_NONE || t > ogg->total_time) {
1400 GST_DEBUG_OBJECT (ogg, "New total time: %" GST_TIME_FORMAT,
1402 ogg->total_time = t;
1403 ogg->push_time_length = t;
1406 /* If we were determining the duration of the stream, we're now done,
1407 and can get back to sending the original event we delayed.
1408 We stop a bit before the end of the stream, as if we get a EOS
1409 event and there is a queue2 upstream (such as when using playbin2),
1410 it will pause the task *after* we come back from the EOS handler,
1411 so we cannot prevent the pausing by issuing a seek. */
1412 if (ogg->push_byte_offset + EOS_AVOIDANCE_THRESHOLD >=
1413 ogg->push_byte_length) {
1414 GstMessage *message;
1417 /* tell the pipeline we've just found out the duration */
1418 ogg->push_time_length = ogg->total_time;
1419 GST_INFO_OBJECT (ogg, "New duration found: %" GST_TIME_FORMAT,
1420 GST_TIME_ARGS (ogg->total_time));
1422 gst_message_new_duration (GST_OBJECT (ogg), GST_FORMAT_TIME,
1424 gst_element_post_message (GST_ELEMENT (ogg), message);
1426 GST_DEBUG_OBJECT (ogg,
1427 "We're close enough to the end, and we're scared "
1428 "to get too close, seeking back to start");
1430 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1431 if (res != GST_FLOW_OK)
1433 return GST_FLOW_SKIP_PUSH;
1435 GST_PUSH_UNLOCK (ogg);
1437 return GST_FLOW_SKIP_PUSH;
1441 /* if we're seeking, look at time, and decide what to do */
1442 if (ogg->push_state != PUSH_PLAYING && ogg->push_state != PUSH_LINEAR2) {
1447 gboolean close_enough;
1450 /* ignore -1 granpos when seeking, we want to sync on a real granpos */
1452 GST_PUSH_UNLOCK (ogg);
1453 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1455 return GST_FLOW_SKIP_PUSH;
1458 t = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1460 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1461 GstClockTime sync_time;
1463 if (pad->push_sync_time == GST_CLOCK_TIME_NONE)
1464 pad->push_sync_time = t;
1465 GST_DEBUG_OBJECT (ogg, "Got timestamp %" GST_TIME_FORMAT " for %s",
1466 GST_TIME_ARGS (t), gst_ogg_stream_get_media_type (&pad->map));
1467 sync_time = gst_ogg_demux_collect_sync_time (ogg, ogg->building_chain);
1468 if (sync_time == GST_CLOCK_TIME_NONE) {
1469 GST_PUSH_UNLOCK (ogg);
1470 GST_DEBUG_OBJECT (ogg,
1471 "Not enough timing info collected for sync, waiting for more");
1472 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1474 return GST_FLOW_SKIP_PUSH;
1476 ogg->push_last_seek_time = sync_time;
1478 GST_DEBUG_OBJECT (ogg,
1479 "Bisection just seeked at %" G_GINT64_FORMAT ", time %"
1480 GST_TIME_FORMAT ", target was %" GST_TIME_FORMAT,
1481 ogg->push_last_seek_offset,
1482 GST_TIME_ARGS (ogg->push_last_seek_time),
1483 GST_TIME_ARGS (ogg->push_seek_time_target));
1485 if (ogg->push_time1 != GST_CLOCK_TIME_NONE) {
1486 seek_quality = gst_ogg_demux_estimate_seek_quality (ogg);
1487 GST_DEBUG_OBJECT (ogg,
1488 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1489 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1490 " (%" GST_TIME_FORMAT "), seek quality %f", ogg->push_offset0,
1491 ogg->push_offset1, ogg->push_offset1 - ogg->push_offset0,
1492 GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1),
1493 GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0), seek_quality);
1495 /* in a open ended seek, we can't do bisection, so we pretend
1496 we like our result so far */
1497 seek_quality = 1.0f;
1498 GST_DEBUG_OBJECT (ogg,
1499 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1500 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - unknown",
1501 ogg->push_offset0, ogg->push_offset1,
1502 ogg->push_offset1 - ogg->push_offset0,
1503 GST_TIME_ARGS (ogg->push_time0));
1505 ogg->push_prev_seek_time = ogg->push_last_seek_time;
1507 gst_ogg_demux_setup_bisection_bounds (ogg);
1509 best = gst_ogg_demux_estimate_bisection_target (ogg, seek_quality);
1511 if (ogg->push_seek_time_target == 0) {
1512 GST_DEBUG_OBJECT (ogg, "Seeking to 0, deemed close enough");
1513 close_enough = (ogg->push_last_seek_time == 0);
1515 /* TODO: make this dependent on framerate ? */
1516 GstClockTime time_threshold = GST_SECOND / 2;
1517 guint64 byte_threshold =
1518 (ogg->max_packet_size >
1519 64 * 1024 ? ogg->max_packet_size : 64 * 1024);
1521 /* We want to be within half a second before the target,
1522 or before the target and half less or equal to the max
1523 packet size left to search in */
1524 if (time_threshold > ogg->push_seek_time_target)
1525 time_threshold = ogg->push_seek_time_target;
1526 close_enough = ogg->push_last_seek_time < ogg->push_seek_time_target
1527 && (ogg->push_last_seek_time >=
1528 ogg->push_seek_time_target - time_threshold
1529 || ogg->push_offset1 <= ogg->push_offset0 + byte_threshold);
1530 GST_DEBUG_OBJECT (ogg,
1531 "testing if we're close enough: %" GST_TIME_FORMAT " <= %"
1532 GST_TIME_FORMAT " < %" GST_TIME_FORMAT ", or %" G_GUINT64_FORMAT
1533 " <= %" G_GUINT64_FORMAT " ? %s",
1534 GST_TIME_ARGS (ogg->push_seek_time_target - time_threshold),
1535 GST_TIME_ARGS (ogg->push_last_seek_time),
1536 GST_TIME_ARGS (ogg->push_seek_time_target),
1537 ogg->push_offset1 - ogg->push_offset0, byte_threshold,
1538 close_enough ? "Yes" : "No");
1541 if (close_enough || best == ogg->push_last_seek_offset) {
1542 if (ogg->push_state == PUSH_BISECT1) {
1543 /* we now know the time segment we'll have to search for
1544 the second bisection */
1545 ogg->push_time0 = ogg->push_start_time;
1546 ogg->push_offset0 = 0;
1548 GST_DEBUG_OBJECT (ogg,
1549 "Seek to %" GST_TIME_FORMAT
1550 " (%lx) done, now gathering pages for all non-sparse streams",
1551 GST_TIME_ARGS (ogg->push_seek_time_target), (long) granpos);
1552 ogg->push_state = PUSH_LINEAR1;
1554 /* If we're asked for an accurate seek, we'll go forward till
1555 we get to the original seek target time, else we'll just drop
1556 here at the keyframe */
1557 if (ogg->push_seek_flags & GST_SEEK_FLAG_ACCURATE) {
1558 GST_INFO_OBJECT (ogg,
1559 "Seek to keyframe at %" GST_TIME_FORMAT " done (we're at %"
1560 GST_TIME_FORMAT "), skipping to original target (%"
1561 GST_TIME_FORMAT ")",
1562 GST_TIME_ARGS (ogg->push_seek_time_target),
1563 GST_TIME_ARGS (sync_time),
1564 GST_TIME_ARGS (ogg->push_seek_time_original_target));
1565 ogg->push_state = PUSH_LINEAR2;
1567 GST_INFO_OBJECT (ogg, "Seek to keyframe done, playing");
1569 /* we're synced to the seek target, so flush stream and stuff
1570 any queued pages into the stream so we start decoding there */
1571 ogg->push_state = PUSH_PLAYING;
1573 gst_ogg_demux_update_bisection_stats (ogg);
1576 } else if (ogg->push_state == PUSH_LINEAR1) {
1577 if (pad->push_kf_time == GST_CLOCK_TIME_NONE) {
1578 GstClockTime earliest_keyframe_time;
1580 gst_ogg_demux_record_keyframe_time (ogg, pad, granpos);
1581 GST_DEBUG_OBJECT (ogg,
1582 "Previous keyframe for %s stream at %" GST_TIME_FORMAT,
1583 gst_ogg_stream_get_media_type (&pad->map),
1584 GST_TIME_ARGS (pad->push_kf_time));
1585 earliest_keyframe_time = gst_ogg_demux_get_earliest_keyframe_time (ogg);
1586 if (earliest_keyframe_time != GST_CLOCK_TIME_NONE) {
1587 if (earliest_keyframe_time > ogg->push_last_seek_time) {
1588 GST_INFO_OBJECT (ogg,
1589 "All non sparse streams now have a previous keyframe time, "
1590 "and we already decoded it, switching to playing");
1591 ogg->push_state = PUSH_PLAYING;
1592 gst_ogg_demux_update_bisection_stats (ogg);
1594 GST_INFO_OBJECT (ogg,
1595 "All non sparse streams now have a previous keyframe time, "
1596 "bisecting again to %" GST_TIME_FORMAT,
1597 GST_TIME_ARGS (earliest_keyframe_time));
1599 ogg->push_seek_time_target = earliest_keyframe_time;
1600 ogg->push_offset0 = 0;
1601 ogg->push_time0 = ogg->push_start_time;
1602 ogg->push_offset1 = ogg->push_last_seek_offset;
1603 ogg->push_time1 = ogg->push_last_seek_time;
1604 ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
1605 ogg->seek_secant = FALSE;
1606 ogg->seek_undershot = FALSE;
1608 ogg->push_state = PUSH_BISECT2;
1609 best = gst_ogg_demux_estimate_bisection_target (ogg, 1.0f);
1615 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1618 ogg_sync_reset (&ogg->sync);
1619 for (i = 0; i < ogg->building_chain->streams->len; i++) {
1621 g_array_index (ogg->building_chain->streams, GstOggPad *, i);
1623 pad->push_sync_time = GST_CLOCK_TIME_NONE;
1624 ogg_stream_reset (&pad->map.stream);
1627 GST_DEBUG_OBJECT (ogg,
1628 "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, best,
1631 g_assert (best != -1);
1632 ogg->push_bisection_steps[ogg->push_state == PUSH_BISECT2 ? 1 : 0]++;
1634 gst_event_new_seek (ogg->push_seek_rate, GST_FORMAT_BYTES,
1635 ogg->push_seek_flags, GST_SEEK_TYPE_SET, best,
1636 GST_SEEK_TYPE_NONE, -1);
1638 GST_PUSH_UNLOCK (ogg);
1639 res = gst_pad_push_event (ogg->sinkpad, sevent);
1641 /* We failed to send the seek event, notify the pipeline */
1642 GST_ELEMENT_ERROR (ogg, RESOURCE, SEEK, (NULL), ("Failed to seek"));
1643 return GST_FLOW_ERROR;
1645 return GST_FLOW_SKIP_PUSH;
1648 if (ogg->push_state != PUSH_PLAYING) {
1649 GST_PUSH_UNLOCK (ogg);
1650 return GST_FLOW_SKIP_PUSH;
1653 GST_PUSH_UNLOCK (ogg);
1659 GST_WARNING_OBJECT (ogg,
1660 "ogg stream choked on page (serial %08x), "
1661 "resetting stream", pad->map.serialno);
1662 gst_ogg_pad_reset (pad);
1663 /* we continue to recover */
1664 return GST_FLOW_SKIP_PUSH;
1668 /* submit a page to an oggpad, this function will then submit all
1669 * the packets in the page.
1671 static GstFlowReturn
1672 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
1674 GstFlowReturn result = GST_FLOW_OK;
1676 gboolean continued = FALSE;
1680 /* for negative rates we read pages backwards and must therefore be careful
1681 * with continued pages */
1682 if (ogg->segment.rate < 0.0) {
1685 continued = ogg_page_continued (page);
1687 /* number of completed packets in the page */
1688 npackets = ogg_page_packets (page);
1690 /* page is not continued so it contains at least one packet start. It's
1691 * possible that no packet ends on this page (npackets == 0). In that
1692 * case, the next (continued) page(s) we kept contain the remainder of the
1693 * packets. We mark npackets=1 to make us start decoding the pages in the
1694 * remainder of the algorithm. */
1698 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
1700 if (npackets == 0) {
1701 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
1706 /* keep track of time in push mode */
1707 if (!ogg->pullmode) {
1708 result = gst_ogg_pad_handle_push_mode_state (pad, page);
1709 if (result == GST_FLOW_SKIP_PUSH)
1711 if (result != GST_FLOW_OK)
1715 if (page->header_len + page->body_len > ogg->max_page_size)
1716 ogg->max_page_size = page->header_len + page->body_len;
1718 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1721 /* flush all packets in the stream layer, this might not give a packet if
1722 * the page had no packets finishing on the page (npackets == 0). */
1723 result = gst_ogg_pad_stream_out (pad, 0);
1725 if (pad->continued) {
1728 /* now send the continued pages to the stream layer */
1729 while (pad->continued) {
1730 ogg_page *p = (ogg_page *) pad->continued->data;
1732 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
1733 if (ogg_stream_pagein (&pad->map.stream, p) != 0)
1736 pad->continued = g_list_delete_link (pad->continued, pad->continued);
1739 gst_ogg_page_free (p);
1742 GST_LOG_OBJECT (ogg, "flushing last continued packet");
1743 /* flush 1 continued packet in the stream layer */
1744 result = gst_ogg_pad_stream_out (pad, 1);
1746 /* flush all remaining packets, we pushed them in the previous round.
1747 * We don't use _reset() because we still want to get the discont when
1748 * we submit a next page. */
1749 while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
1753 /* keep continued pages (only in reverse mode) */
1755 ogg_page *p = gst_ogg_page_copy (page);
1757 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
1758 pad->continued = g_list_prepend (pad->continued, p);
1765 GST_WARNING_OBJECT (ogg,
1766 "ogg stream choked on page (serial %08x), "
1767 "resetting stream", pad->map.serialno);
1768 gst_ogg_pad_reset (pad);
1769 /* we continue to recover */
1775 static GstOggChain *
1776 gst_ogg_chain_new (GstOggDemux * ogg)
1778 GstOggChain *chain = g_slice_new0 (GstOggChain);
1780 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
1784 chain->have_bos = FALSE;
1785 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
1786 chain->begin_time = GST_CLOCK_TIME_NONE;
1787 chain->segment_start = GST_CLOCK_TIME_NONE;
1788 chain->segment_stop = GST_CLOCK_TIME_NONE;
1789 chain->total_time = GST_CLOCK_TIME_NONE;
1795 gst_ogg_chain_free (GstOggChain * chain)
1799 for (i = 0; i < chain->streams->len; i++) {
1800 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1802 gst_object_unref (pad);
1804 g_array_free (chain->streams, TRUE);
1805 g_slice_free (GstOggChain, chain);
1809 gst_ogg_pad_mark_discont (GstOggPad * pad)
1811 pad->discont = TRUE;
1812 pad->map.last_size = 0;
1816 gst_ogg_chain_mark_discont (GstOggChain * chain)
1820 for (i = 0; i < chain->streams->len; i++) {
1821 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1823 gst_ogg_pad_mark_discont (pad);
1828 gst_ogg_chain_reset (GstOggChain * chain)
1832 for (i = 0; i < chain->streams->len; i++) {
1833 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1835 gst_ogg_pad_reset (pad);
1840 gst_ogg_chain_new_stream (GstOggChain * chain, guint32 serialno)
1846 GST_DEBUG_OBJECT (chain->ogg,
1847 "creating new stream %08x in chain %p", serialno, chain);
1849 name = g_strdup_printf ("serial_%08x", serialno);
1850 ret = g_object_new (GST_TYPE_OGG_PAD, "name", name, NULL);
1852 /* we own this one */
1853 gst_object_ref (ret);
1854 gst_object_sink (ret);
1856 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1857 gst_ogg_pad_mark_discont (ret);
1860 ret->ogg = chain->ogg;
1862 ret->map.serialno = serialno;
1863 if (ogg_stream_init (&ret->map.stream, serialno) != 0)
1866 /* FIXME: either do something with it or remove it */
1867 list = gst_tag_list_new ();
1868 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1870 gst_tag_list_free (list);
1872 GST_DEBUG_OBJECT (chain->ogg,
1873 "created new ogg src %p for stream with serial %08x", ret, serialno);
1875 g_array_append_val (chain->streams, ret);
1882 GST_ERROR ("Could not initialize ogg_stream struct for serial %08x",
1884 gst_object_unref (ret);
1890 gst_ogg_chain_get_stream (GstOggChain * chain, guint32 serialno)
1894 for (i = 0; i < chain->streams->len; i++) {
1895 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1897 if (pad->map.serialno == serialno)
1904 gst_ogg_chain_has_stream (GstOggChain * chain, guint32 serialno)
1906 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1909 /* signals and args */
1922 static GstStaticPadTemplate ogg_demux_src_template_factory =
1923 GST_STATIC_PAD_TEMPLATE ("src_%d",
1926 GST_STATIC_CAPS_ANY);
1928 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1929 GST_STATIC_PAD_TEMPLATE ("sink",
1932 GST_STATIC_CAPS ("application/ogg; application/x-annodex")
1935 static void gst_ogg_demux_finalize (GObject * object);
1937 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1938 GstOggChain ** chain);
1939 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1940 GstOggChain * chain);
1942 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
1943 static void gst_ogg_demux_loop (GstOggPad * pad);
1944 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
1945 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
1946 static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
1948 static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
1950 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1951 GstStateChange transition);
1953 static void gst_ogg_print (GstOggDemux * demux);
1955 GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT);
1958 gst_ogg_demux_base_init (gpointer g_class)
1960 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1962 gst_element_class_set_details_simple (element_class,
1963 "Ogg demuxer", "Codec/Demuxer",
1964 "demux ogg streams (info about ogg: http://xiph.org)",
1965 "Wim Taymans <wim@fluendo.com>");
1967 gst_element_class_add_static_pad_template (element_class,
1968 &ogg_demux_sink_template_factory);
1969 gst_element_class_add_static_pad_template (element_class,
1970 &ogg_demux_src_template_factory);
1974 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1976 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1977 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1979 gstelement_class->change_state = gst_ogg_demux_change_state;
1980 gstelement_class->send_event = gst_ogg_demux_receive_event;
1982 gobject_class->finalize = gst_ogg_demux_finalize;
1986 gst_ogg_demux_init (GstOggDemux * ogg, GstOggDemuxClass * g_class)
1988 /* create the sink pad */
1990 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
1993 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
1994 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
1995 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
1996 gst_pad_set_activatepull_function (ogg->sinkpad,
1997 gst_ogg_demux_sink_activate_pull);
1998 gst_pad_set_activatepush_function (ogg->sinkpad,
1999 gst_ogg_demux_sink_activate_push);
2000 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
2002 ogg->chain_lock = g_mutex_new ();
2003 ogg->push_lock = g_mutex_new ();
2004 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
2006 ogg->stats_nbisections = 0;
2007 ogg->stats_bisection_steps[0] = 0;
2008 ogg->stats_bisection_steps[1] = 0;
2009 ogg->stats_bisection_max_steps[0] = 0;
2010 ogg->stats_bisection_max_steps[1] = 0;
2012 ogg->newsegment = NULL;
2016 gst_ogg_demux_finalize (GObject * object)
2020 ogg = GST_OGG_DEMUX (object);
2022 g_array_free (ogg->chains, TRUE);
2023 g_mutex_free (ogg->chain_lock);
2024 g_mutex_free (ogg->push_lock);
2025 ogg_sync_clear (&ogg->sync);
2027 if (ogg->newsegment)
2028 gst_event_unref (ogg->newsegment);
2030 G_OBJECT_CLASS (parent_class)->finalize (object);
2034 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
2039 chain = ogg->current_chain;
2043 for (i = 0; i < chain->streams->len; i++) {
2044 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2046 stream->start_time = -1;
2047 stream->map.accumulated_granule = 0;
2049 ogg->building_chain = chain;
2050 GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2051 ogg->current_chain = NULL;
2056 gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
2061 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
2063 switch (GST_EVENT_TYPE (event)) {
2064 case GST_EVENT_FLUSH_START:
2065 res = gst_ogg_demux_send_event (ogg, event);
2067 case GST_EVENT_FLUSH_STOP:
2068 GST_DEBUG_OBJECT (ogg, "got a flush stop event");
2069 ogg_sync_reset (&ogg->sync);
2070 res = gst_ogg_demux_send_event (ogg, event);
2071 if (ogg->pullmode || ogg->push_state != PUSH_DURATION) {
2072 /* it's starting to feel reaaaally dirty :(
2073 if we're on a spliced seek to get duration, don't reset streams,
2074 we'll need them for the delayed seek */
2075 gst_ogg_demux_reset_streams (ogg);
2078 case GST_EVENT_NEWSEGMENT:
2079 GST_DEBUG_OBJECT (ogg, "got a new segment event");
2083 gdouble rate, arate;
2084 gint64 start, stop, time;
2086 gst_event_parse_new_segment_full (event, &update, &rate, &arate,
2087 &format, &start, &stop, &time);
2088 if (format == GST_FORMAT_BYTES) {
2089 GST_PUSH_LOCK (ogg);
2090 ogg->push_byte_offset = start;
2091 ogg->push_last_seek_offset = start;
2092 GST_PUSH_UNLOCK (ogg);
2094 GST_WARNING_OBJECT (ogg, "unexpected segment format: %s",
2095 gst_format_get_name (format));
2098 gst_event_unref (event);
2103 GST_DEBUG_OBJECT (ogg, "got an EOS event");
2105 /* This would be what is needed (recover from EOS by going on to
2106 the next step (issue the delayed seek)), but it does not work
2107 if there is a queue2 upstream - see more details comment in
2108 gst_ogg_pad_submit_page.
2109 If I could find a way to bypass queue2 behavior, this should
2111 GST_PUSH_LOCK (ogg);
2112 if (ogg->push_state == PUSH_DURATION) {
2113 GST_DEBUG_OBJECT (ogg, "Got EOS while determining length");
2114 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
2115 if (res != GST_FLOW_OK) {
2116 GST_DEBUG_OBJECT (ogg, "Error seeking back after duration check: %d",
2121 GST_PUSH_UNLOCK (ogg);
2123 res = gst_ogg_demux_send_event (ogg, event);
2124 if (ogg->current_chain == NULL) {
2125 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
2126 ("can't get first chain"));
2131 res = gst_ogg_demux_send_event (ogg, event);
2134 gst_object_unref (ogg);
2139 /* submit the given buffer to the ogg sync */
2140 static GstFlowReturn
2141 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
2146 GstFlowReturn ret = GST_FLOW_OK;
2148 size = GST_BUFFER_SIZE (buffer);
2149 data = GST_BUFFER_DATA (buffer);
2151 GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
2152 if (G_UNLIKELY (size == 0))
2155 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
2156 if (G_UNLIKELY (oggbuffer == NULL))
2159 memcpy (oggbuffer, data, size);
2160 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2163 if (!ogg->pullmode) {
2164 GST_PUSH_LOCK (ogg);
2165 ogg->push_byte_offset += size;
2166 GST_PUSH_UNLOCK (ogg);
2170 gst_buffer_unref (buffer);
2177 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2178 (NULL), ("failed to get ogg sync buffer"));
2179 ret = GST_FLOW_ERROR;
2184 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2185 (NULL), ("failed to write %d bytes to the sync buffer", size));
2186 ret = GST_FLOW_ERROR;
2191 /* in random access mode this code updates the current read position
2192 * and resets the ogg sync buffer so that the next read will happen
2193 * from this new location.
2196 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
2198 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
2200 ogg->offset = offset;
2201 ogg->read_offset = offset;
2202 ogg_sync_reset (&ogg->sync);
2205 /* read more data from the current offset and submit to
2206 * the ogg sync layer.
2208 static GstFlowReturn
2209 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
2214 GST_LOG_OBJECT (ogg,
2215 "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
2216 ogg->read_offset, ogg->length, end_offset);
2218 if (end_offset > 0 && ogg->read_offset >= end_offset)
2219 goto boundary_reached;
2221 if (ogg->read_offset == ogg->length)
2224 ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
2225 if (ret != GST_FLOW_OK)
2228 ogg->read_offset += GST_BUFFER_SIZE (buffer);
2230 ret = gst_ogg_demux_submit_buffer (ogg, buffer);
2237 GST_LOG_OBJECT (ogg, "reached boundary");
2238 return GST_FLOW_LIMIT;
2242 GST_LOG_OBJECT (ogg, "reached EOS");
2243 return GST_FLOW_UNEXPECTED;
2247 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
2248 gst_flow_get_name (ret));
2253 /* Read the next page from the current offset.
2254 * boundary: number of bytes ahead we allow looking for;
2257 * @offset will contain the offset the next page starts at when this function
2258 * returns GST_FLOW_OK.
2260 * GST_FLOW_UNEXPECTED is returned on EOS.
2262 * GST_FLOW_LIMIT is returned when we did not find a page before the
2263 * boundary. If @boundary is -1, this is never returned.
2265 * Any other error returned while retrieving data from the peer is returned as
2268 static GstFlowReturn
2269 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
2270 gint64 boundary, gint64 * offset)
2272 gint64 end_offset = -1;
2275 GST_LOG_OBJECT (ogg,
2276 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
2277 G_GINT64_FORMAT, ogg->offset, boundary);
2280 end_offset = ogg->offset + boundary;
2285 if (end_offset > 0 && ogg->offset >= end_offset)
2286 goto boundary_reached;
2288 more = ogg_sync_pageseek (&ogg->sync, og);
2290 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
2293 /* skipped n bytes */
2294 ogg->offset -= more;
2295 GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
2297 } else if (more == 0) {
2298 /* we need more data */
2300 goto boundary_reached;
2302 GST_LOG_OBJECT (ogg, "need more data");
2303 ret = gst_ogg_demux_get_data (ogg, end_offset);
2304 if (ret != GST_FLOW_OK)
2307 gint64 res_offset = ogg->offset;
2309 /* got a page. Return the offset at the page beginning,
2310 advance the internal offset past the page end */
2312 *offset = res_offset;
2315 ogg->offset += more;
2317 GST_LOG_OBJECT (ogg,
2318 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
2319 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
2320 ogg_page_serialno (og), ogg->offset,
2321 (gint64) ogg_page_granulepos (og));
2325 GST_LOG_OBJECT (ogg, "returning %d", ret);
2332 GST_LOG_OBJECT (ogg,
2333 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
2334 ogg->offset, end_offset);
2335 return GST_FLOW_LIMIT;
2339 /* from the current offset, find the previous page, seeking backwards
2340 * until we find the page.
2342 static GstFlowReturn
2343 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
2346 gint64 begin = ogg->offset;
2348 gint64 cur_offset = -1;
2350 GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
2352 while (cur_offset == -1) {
2357 /* seek CHUNKSIZE back */
2358 gst_ogg_demux_seek (ogg, begin);
2360 /* now continue reading until we run out of data, if we find a page
2361 * start, we save it. It might not be the final page as there could be
2362 * another page after this one. */
2363 while (ogg->offset < end) {
2367 gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
2368 /* we hit the upper limit, offset contains the last page start */
2369 if (ret == GST_FLOW_LIMIT) {
2370 GST_LOG_OBJECT (ogg, "hit limit");
2373 /* something went wrong */
2374 if (ret == GST_FLOW_UNEXPECTED) {
2376 GST_LOG_OBJECT (ogg, "got unexpected");
2379 } else if (ret != GST_FLOW_OK) {
2380 GST_LOG_OBJECT (ogg, "got error %d", ret);
2384 GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
2386 /* offset is next page start */
2387 cur_offset = new_offset;
2391 GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
2393 /* we have the offset. Actually snork and hold the page now */
2394 gst_ogg_demux_seek (ogg, cur_offset);
2395 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
2396 if (ret != GST_FLOW_OK) {
2397 GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
2399 /* this shouldn't be possible */
2404 *offset = cur_offset;
2411 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
2414 GstOggChain *chain = ogg->current_chain;
2419 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
2421 /* send EOS on all the pads */
2422 for (i = 0; i < chain->streams->len; i++) {
2423 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2429 event = gst_event_new_eos ();
2430 gst_event_set_seqnum (event, ogg->seqnum);
2431 gst_pad_push_event (GST_PAD_CAST (pad), event);
2433 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
2435 /* deactivate first */
2436 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
2438 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2443 /* if we cannot seek back to the chain, we can destroy the chain
2445 if (!ogg->pullmode) {
2446 gst_ogg_chain_free (chain);
2453 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
2456 GstStructure *structure;
2457 GValue array = { 0 };
2459 GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
2461 if (G_UNLIKELY (!caps))
2463 if (G_UNLIKELY (!headers))
2466 caps = gst_caps_make_writable (caps);
2467 structure = gst_caps_get_structure (caps, 0);
2469 g_value_init (&array, GST_TYPE_ARRAY);
2472 GValue value = { 0 };
2474 ogg_packet *op = headers->data;
2476 buffer = gst_buffer_new_and_alloc (op->bytes);
2477 memcpy (GST_BUFFER_DATA (buffer), op->packet, op->bytes);
2478 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS);
2479 g_value_init (&value, GST_TYPE_BUFFER);
2480 gst_value_take_buffer (&value, buffer);
2481 gst_value_array_append_value (&array, &value);
2482 g_value_unset (&value);
2483 headers = headers->next;
2486 gst_structure_set_value (structure, "streamheader", &array);
2487 g_value_unset (&array);
2488 GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
2494 gst_ogg_demux_push_queued_buffers (GstOggDemux * ogg, GstOggPad * pad)
2498 /* push queued packets */
2499 for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
2500 ogg_packet *p = walk->data;
2502 gst_ogg_demux_chain_peer (pad, p, TRUE);
2503 _ogg_packet_free (p);
2505 /* and free the queued buffers */
2506 g_list_free (pad->map.queued);
2507 pad->map.queued = NULL;
2511 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
2515 gint bitrate, idx_bitrate;
2517 g_return_val_if_fail (chain != NULL, FALSE);
2519 if (chain == ogg->current_chain) {
2521 gst_event_unref (event);
2523 for (i = 0; i < chain->streams->len; i++) {
2524 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2525 gst_ogg_demux_push_queued_buffers (ogg, pad);
2531 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
2533 bitrate = idx_bitrate = 0;
2535 /* first add the pads */
2536 for (i = 0; i < chain->streams->len; i++) {
2539 pad = g_array_index (chain->streams, GstOggPad *, i);
2541 if (pad->map.idx_bitrate)
2542 idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
2544 bitrate += pad->map.bitrate;
2547 gst_ogg_pad_mark_discont (pad);
2548 pad->last_ret = GST_FLOW_OK;
2550 if (pad->map.is_skeleton || pad->added || GST_PAD_CAPS (pad) == NULL)
2553 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
2555 /* activate first */
2556 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
2558 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2561 /* prefer the index bitrate over the ones encoded in the streams */
2562 ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
2564 /* after adding the new pads, remove the old pads */
2565 gst_ogg_demux_deactivate_current_chain (ogg);
2567 GST_DEBUG_OBJECT (ogg, "Setting current chain to %p", chain);
2568 ogg->current_chain = chain;
2570 /* we are finished now */
2571 gst_element_no_more_pads (GST_ELEMENT (ogg));
2573 /* FIXME, must be sent from the streaming thread */
2575 gst_ogg_demux_send_event (ogg, event);
2577 gst_element_found_tags (GST_ELEMENT_CAST (ogg),
2578 gst_tag_list_new_full (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL));
2581 GST_DEBUG_OBJECT (ogg, "starting chain");
2583 /* then send out any headers and queued packets */
2584 for (i = 0; i < chain->streams->len; i++) {
2588 pad = g_array_index (chain->streams, GstOggPad *, i);
2590 /* FIXME also streaming thread */
2591 if (pad->map.taglist) {
2592 GST_DEBUG_OBJECT (ogg, "pushing tags");
2593 gst_element_found_tags_for_pad (GST_ELEMENT_CAST (ogg),
2594 GST_PAD_CAST (pad), pad->map.taglist);
2595 pad->map.taglist = NULL;
2598 /* Set headers on caps */
2600 gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers);
2601 gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps);
2603 GST_DEBUG_OBJECT (ogg, "pushing headers");
2605 for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
2606 ogg_packet *p = walk->data;
2608 gst_ogg_demux_chain_peer (pad, p, TRUE);
2611 GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
2612 gst_ogg_demux_push_queued_buffers (ogg, pad);
2618 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2619 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2628 GST_DEBUG_OBJECT (ogg,
2629 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
2631 GST_DEBUG_OBJECT (ogg,
2632 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
2633 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
2634 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
2636 /* perform the seek */
2637 while (begin < end) {
2640 if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
2643 /* take a (pretty decent) guess, avoiding overflow */
2644 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
2646 bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
2648 if (bisect <= begin)
2650 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
2652 gst_ogg_demux_seek (ogg, bisect);
2654 while (begin < end) {
2657 GST_DEBUG_OBJECT (ogg,
2658 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
2659 ", end %" G_GINT64_FORMAT, bisect, begin, end);
2661 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2662 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2665 if (ret == GST_FLOW_LIMIT) {
2666 /* we hit the upper limit, go back a bit */
2667 if (bisect <= begin + 1) {
2668 end = begin; /* found it */
2673 bisect -= CHUNKSIZE;
2674 if (bisect <= begin)
2677 gst_ogg_demux_seek (ogg, bisect);
2679 } else if (ret == GST_FLOW_OK) {
2680 /* found offset of next ogg page */
2682 GstClockTime granuletime;
2685 /* get the granulepos */
2686 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
2688 granulepos = ogg_page_granulepos (&og);
2689 if (granulepos == -1) {
2690 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2694 /* get the stream */
2695 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2696 if (pad == NULL || pad->map.is_skeleton)
2699 /* convert granulepos to time */
2700 granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2702 if (granuletime < pad->start_time)
2705 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
2706 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2708 granuletime -= pad->start_time;
2709 granuletime += chain->begin_time;
2711 GST_DEBUG_OBJECT (ogg,
2712 "found page with granule %" G_GINT64_FORMAT " and time %"
2713 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2715 if (granuletime < target) {
2716 best = result; /* raw offset of packet with granulepos */
2717 begin = ogg->offset; /* raw offset of next page */
2718 begintime = granuletime;
2720 bisect = begin; /* *not* begin + 1 */
2722 if (bisect <= begin + 1) {
2723 end = begin; /* found it */
2725 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
2727 bisect -= CHUNKSIZE; /* an endless loop otherwise. */
2728 if (bisect <= begin)
2730 gst_ogg_demux_seek (ogg, bisect);
2733 endtime = granuletime;
2742 GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
2743 gst_ogg_demux_seek (ogg, best);
2751 GST_DEBUG_OBJECT (ogg, "got a seek error");
2757 do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2758 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2759 gint64 * p_offset, gint64 * p_timestamp)
2762 guint64 timestamp, offset;
2763 guint64 r_timestamp, r_offset;
2764 gboolean result = FALSE;
2766 target -= begintime;
2771 for (i = 0; i < chain->streams->len; i++) {
2772 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2775 if (gst_ogg_map_search_index (&pad->map, TRUE, ×tamp, &offset)) {
2776 GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
2779 if (r_offset == -1 || offset < r_offset) {
2781 r_timestamp = timestamp;
2788 *p_timestamp = r_timestamp;
2790 *p_offset = r_offset;
2796 * do seek to time @position, return FALSE or chain and TRUE
2799 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
2800 gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
2803 GstOggChain *chain = NULL;
2805 gint64 begintime, endtime;
2806 gint64 target, keytarget;
2811 gint i, pending, len;
2812 gboolean first_parsed_page = TRUE;
2814 position = segment->last_stop;
2816 /* first find the chain to search in */
2817 total = ogg->total_time;
2818 if (ogg->chains->len == 0)
2821 for (i = ogg->chains->len - 1; i >= 0; i--) {
2822 chain = g_array_index (ogg->chains, GstOggChain *, i);
2823 total -= chain->total_time;
2824 if (position >= total)
2828 /* first step, locate page containing the required data */
2829 begin = chain->offset;
2830 end = chain->end_offset;
2831 begintime = chain->begin_time;
2832 endtime = begintime + chain->total_time;
2833 target = position - total + begintime;
2835 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
2839 /* second step: find pages for all streams, we use the keyframe_granule to keep
2840 * track of which ones we saw. If we have seen a page for each stream we can
2841 * calculate the positions of each keyframe. */
2842 GST_DEBUG_OBJECT (ogg, "find keyframes");
2843 len = pending = chain->streams->len;
2845 /* figure out where the keyframes are */
2852 GstClockTime keyframe_time, granule_time;
2854 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2855 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2857 if (ret == GST_FLOW_LIMIT) {
2858 GST_LOG_OBJECT (ogg, "reached limit");
2860 } else if (ret != GST_FLOW_OK)
2863 /* get the stream */
2864 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2868 if (pad->map.is_skeleton)
2871 granulepos = ogg_page_granulepos (&og);
2872 if (granulepos == -1 || granulepos == 0) {
2873 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2877 /* we only do this the first time we pass here */
2878 if (first_parsed_page) {
2879 /* Now that we have a time reference from the page, we can check
2880 * whether all streams still have pages from here on.
2882 * This would be more elegant before the loop, but getting the page from
2883 * there without breaking anything would be more costly */
2884 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2886 for (i = 0; i < len; i++) {
2887 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2890 /* we already know we have at least one page (the current one)
2891 * for this stream */
2894 if (granule_time > stream->map.total_time)
2895 /* we won't encounter any more pages of this stream, so we don't
2896 * try finding a key frame for it */
2899 first_parsed_page = FALSE;
2903 /* in reverse we want to go past the page with the lower timestamp */
2904 if (segment->rate < 0.0) {
2905 /* get time for this pad */
2906 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2909 GST_LOG_OBJECT (ogg,
2910 "looking at page with ts %" GST_TIME_FORMAT ", target %"
2911 GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
2912 GST_TIME_ARGS (target));
2913 if (granule_time < target)
2917 /* we've seen this pad before */
2918 if (pad->keyframe_granule != -1)
2921 /* convert granule of this pad to the granule of the keyframe */
2922 pad->keyframe_granule =
2923 gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
2924 GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
2925 pad->keyframe_granule);
2927 /* get time of the keyframe */
2929 gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
2930 GST_LOG_OBJECT (ogg,
2931 "stream %08x granule time %" GST_TIME_FORMAT,
2932 pad->map.serialno, GST_TIME_ARGS (keyframe_time));
2934 /* collect smallest value */
2935 if (keyframe_time != -1) {
2936 keyframe_time += begintime;
2937 if (keyframe_time < keytarget)
2938 keytarget = keyframe_time;
2947 /* for negative rates we will get to the keyframe backwards */
2948 if (segment->rate < 0.0)
2951 if (keytarget != target) {
2952 GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
2953 GST_TIME_ARGS (keytarget));
2955 /* last step, seek to the location of the keyframe */
2956 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
2960 /* seek back to previous position */
2961 GST_LOG_OBJECT (ogg, "keyframe on target");
2962 gst_ogg_demux_seek (ogg, best);
2967 if (segment->rate > 0.0)
2968 segment->time = keytarget;
2969 segment->last_stop = keytarget - begintime;
2978 GST_DEBUG_OBJECT (ogg, "no chains");
2983 GST_DEBUG_OBJECT (ogg, "got a seek error");
2988 /* does not take ownership of the event */
2990 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
2992 GstOggChain *chain = NULL;
2994 gboolean flush, accurate, keyframe;
2998 GstSeekType cur_type, stop_type;
3005 GST_DEBUG_OBJECT (ogg, "seek with event");
3007 gst_event_parse_seek (event, &rate, &format, &flags,
3008 &cur_type, &cur, &stop_type, &stop);
3010 /* we can only seek on time */
3011 if (format != GST_FORMAT_TIME) {
3012 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3015 seqnum = gst_event_get_seqnum (event);
3017 GST_DEBUG_OBJECT (ogg, "seek without event");
3021 seqnum = gst_util_seqnum_next ();
3024 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
3026 flush = flags & GST_SEEK_FLAG_FLUSH;
3027 accurate = flags & GST_SEEK_FLAG_ACCURATE;
3028 keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
3030 /* first step is to unlock the streaming thread if it is
3031 * blocked in a chain call, we do this by starting the flush. because
3032 * we cannot yet hold any streaming lock, we have to protect the chains
3033 * with their own lock. */
3037 tevent = gst_event_new_flush_start ();
3038 gst_event_set_seqnum (tevent, seqnum);
3040 gst_event_ref (tevent);
3041 gst_pad_push_event (ogg->sinkpad, tevent);
3043 GST_CHAIN_LOCK (ogg);
3044 for (i = 0; i < ogg->chains->len; i++) {
3045 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3048 for (j = 0; j < chain->streams->len; j++) {
3049 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
3051 gst_event_ref (tevent);
3052 gst_pad_push_event (GST_PAD (pad), tevent);
3055 GST_CHAIN_UNLOCK (ogg);
3057 gst_event_unref (tevent);
3059 gst_pad_pause_task (ogg->sinkpad);
3062 /* now grab the stream lock so that streaming cannot continue, for
3063 * non flushing seeks when the element is in PAUSED this could block
3065 GST_PAD_STREAM_LOCK (ogg->sinkpad);
3067 if (ogg->segment_running && !flush) {
3068 /* create the segment event to close the current segment */
3069 if ((chain = ogg->current_chain)) {
3071 gint64 chain_start = 0;
3073 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3074 chain_start = chain->segment_start;
3076 newseg = gst_event_new_new_segment (TRUE, ogg->segment.rate,
3077 GST_FORMAT_TIME, ogg->segment.start + chain_start,
3078 ogg->segment.last_stop + chain_start, ogg->segment.time);
3079 /* set the seqnum of the running segment */
3080 gst_event_set_seqnum (newseg, ogg->seqnum);
3082 /* send segment on old chain, FIXME, must be sent from streaming thread. */
3083 gst_ogg_demux_send_event (ogg, newseg);
3088 gst_segment_set_seek (&ogg->segment, rate, format, flags,
3089 cur_type, cur, stop_type, stop, &update);
3092 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
3093 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
3094 GST_TIME_ARGS (ogg->segment.stop));
3096 /* we need to stop flushing on the srcpad as we're going to use it
3097 * next. We can do this as we have the STREAM lock now. */
3099 tevent = gst_event_new_flush_stop ();
3100 gst_event_set_seqnum (tevent, seqnum);
3101 gst_pad_push_event (ogg->sinkpad, tevent);
3107 /* reset all ogg streams now, need to do this from within the lock to
3108 * make sure the streaming thread is not messing with the stream */
3109 for (i = 0; i < ogg->chains->len; i++) {
3110 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3112 gst_ogg_chain_reset (chain);
3116 /* for reverse we will already seek accurately */
3117 res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
3119 /* seek failed, make sure we continue the current chain */
3121 GST_DEBUG_OBJECT (ogg, "seek failed");
3122 chain = ogg->current_chain;
3124 GST_DEBUG_OBJECT (ogg, "seek success");
3130 /* now we have a new position, prepare for streaming again */
3135 gint64 last_stop, begin_time;
3137 /* we have to send the flush to the old chain, not the new one */
3139 tevent = gst_event_new_flush_stop ();
3140 gst_event_set_seqnum (tevent, seqnum);
3141 gst_ogg_demux_send_event (ogg, tevent);
3144 /* we need this to see how far inside the chain we need to start */
3145 if (chain->begin_time != GST_CLOCK_TIME_NONE)
3146 begin_time = chain->begin_time;
3150 /* segment.start gives the start over all chains, we calculate the amount
3151 * of time into this chain we need to start */
3152 start = ogg->segment.start - begin_time;
3153 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3154 start += chain->segment_start;
3156 if ((stop = ogg->segment.stop) == -1)
3157 stop = ogg->segment.duration;
3159 /* segment.stop gives the stop time over all chains, calculate the amount of
3160 * time we need to stop in this chain */
3162 if (stop > begin_time)
3166 stop += chain->segment_start;
3167 /* we must stop when this chain ends and switch to the next chain to play
3168 * the remainder of the segment. */
3169 stop = MIN (stop, chain->segment_stop);
3172 last_stop = ogg->segment.last_stop;
3173 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3174 last_stop += chain->segment_start;
3176 /* create the segment event we are going to send out */
3177 if (ogg->segment.rate >= 0.0)
3178 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
3179 ogg->segment.format, last_stop, stop, ogg->segment.time);
3181 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
3182 ogg->segment.format, start, last_stop, ogg->segment.time);
3184 gst_event_set_seqnum (event, seqnum);
3186 if (chain != ogg->current_chain) {
3187 /* switch to different chain, send segment on new chain */
3188 gst_ogg_demux_activate_chain (ogg, chain, event);
3190 /* mark discont and send segment on current chain */
3191 gst_ogg_chain_mark_discont (chain);
3192 /* This event should be sent from the streaming thread (sink pad task) */
3193 if (ogg->newsegment)
3194 gst_event_unref (ogg->newsegment);
3195 ogg->newsegment = event;
3198 /* notify start of new segment */
3199 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3200 GstMessage *message;
3202 message = gst_message_new_segment_start (GST_OBJECT (ogg),
3203 GST_FORMAT_TIME, ogg->segment.last_stop);
3204 gst_message_set_seqnum (message, seqnum);
3206 gst_element_post_message (GST_ELEMENT (ogg), message);
3209 ogg->segment_running = TRUE;
3210 ogg->seqnum = seqnum;
3211 /* restart our task since it might have been stopped when we did the
3213 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3217 /* streaming can continue now */
3218 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3225 GST_DEBUG_OBJECT (ogg, "seek failed");
3230 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
3231 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3237 gst_ogg_demux_get_duration_push (GstOggDemux * ogg, int flags)
3239 /* In push mode, we get to the end of the stream to get the duration */
3244 /* A full Ogg page can be almost 64 KB. There's no guarantee that there'll be a
3245 granpos there, but it's fairly likely */
3247 ogg->push_byte_length - DURATION_CHUNK_OFFSET - EOS_AVOIDANCE_THRESHOLD;
3249 if (ogg->push_byte_length > EOS_AVOIDANCE_THRESHOLD)
3250 position = ogg->push_byte_length - (EOS_AVOIDANCE_THRESHOLD / 2);
3255 GST_DEBUG_OBJECT (ogg,
3256 "Getting duration, seeking near the end, to %" G_GINT64_FORMAT, position);
3257 ogg->push_state = PUSH_DURATION;
3258 /* do not read the last byte */
3259 sevent = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET,
3260 position, GST_SEEK_TYPE_SET, ogg->push_byte_length - 1);
3261 res = gst_pad_push_event (ogg->sinkpad, sevent);
3263 GST_DEBUG_OBJECT (ogg, "Seek succesful");
3266 GST_INFO_OBJECT (ogg, "Seek failed, duration will stay unknown");
3267 ogg->push_state = PUSH_PLAYING;
3268 ogg->push_disable_seeking = TRUE;
3274 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3277 if (ogg->push_byte_length < 0) {
3280 GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3281 if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3282 GstFormat format = GST_FORMAT_BYTES;
3286 res = gst_pad_query_duration (peer, &format, &length);
3287 if (res && length > 0) {
3288 ogg->push_byte_length = length;
3289 GST_DEBUG_OBJECT (ogg,
3290 "File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
3292 GST_DEBUG_OBJECT (ogg, "File byte length unknown, assuming live");
3293 ogg->push_disable_seeking = TRUE;
3296 format = GST_FORMAT_TIME;
3297 res = gst_pad_query_duration (peer, &format, &length);
3298 gst_object_unref (peer);
3299 if (res && length >= 0) {
3300 ogg->push_time_length = length;
3301 GST_DEBUG_OBJECT (ogg, "File time length %" GST_TIME_FORMAT,
3302 GST_TIME_ARGS (ogg->push_time_length));
3303 } else if (!ogg->push_disable_seeking) {
3306 res = gst_ogg_demux_get_duration_push (ogg, flags);
3308 GST_DEBUG_OBJECT (ogg,
3309 "File time length unknown, trying to determine");
3310 ogg->push_mode_seek_delayed_event = NULL;
3312 GST_DEBUG_OBJECT (ogg,
3313 "Let me intercept this innocent looking seek request");
3314 ogg->push_mode_seek_delayed_event = gst_event_copy (event);
3325 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3328 gboolean res = TRUE;
3332 GstSeekType start_type, stop_type;
3336 gint64 best, best_time;
3339 GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3341 gst_event_parse_seek (event, &rate, &format, &flags,
3342 &start_type, &start, &stop_type, &stop);
3344 if (format != GST_FORMAT_TIME) {
3345 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3349 if (start_type != GST_SEEK_TYPE_SET) {
3350 GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3354 if (!(flags & GST_SEEK_FLAG_FLUSH)) {
3355 GST_DEBUG_OBJECT (ogg, "can only do flushing seeks");
3359 GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3360 GST_TIME_ARGS (start));
3362 chain = ogg->current_chain;
3364 GST_WARNING_OBJECT (ogg, "No chain to seek on");
3368 /* start accessing push_* members */
3369 GST_PUSH_LOCK (ogg);
3371 /* not if we disabled seeking (chained streams) */
3372 if (ogg->push_disable_seeking) {
3373 GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3377 /* not when we're trying to work out duration */
3378 if (ogg->push_state == PUSH_DURATION) {
3379 GST_DEBUG_OBJECT (ogg, "Busy working out duration, try again later");
3383 /* actually, not if we're doing any seeking already */
3384 if (ogg->push_state != PUSH_PLAYING) {
3385 GST_DEBUG_OBJECT (ogg, "Already doing some seeking, try again later");
3389 /* on the first seek, get length if we can */
3390 if (!gst_ogg_demux_check_duration_push (ogg, flags, event)) {
3391 GST_PUSH_UNLOCK (ogg);
3395 if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
3396 /* the index gave some result */
3397 GST_DEBUG_OBJECT (ogg,
3398 "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
3401 if (ogg->push_time_length > 0) {
3402 /* if we know the time length, we know the full segment bitrate */
3403 GST_DEBUG_OBJECT (ogg, "Using real file bitrate");
3405 gst_util_uint64_scale (ogg->push_byte_length, 8 * GST_SECOND,
3406 ogg->push_time_length);
3407 } else if (ogg->push_time_offset > 0) {
3408 /* get a first approximation using known bitrate to the current position */
3409 GST_DEBUG_OBJECT (ogg, "Using file bitrate so far");
3411 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
3412 ogg->push_time_offset);
3413 } else if (ogg->bitrate > 0) {
3414 /* nominal bitrate is better than nothing, even if it lies often */
3415 GST_DEBUG_OBJECT (ogg, "Using nominal bitrate");
3416 bitrate = ogg->bitrate;
3419 GST_DEBUG_OBJECT (ogg,
3420 "At stream start, and no nominal bitrate, using some random magic "
3422 /* the bisection, once started, should give us a better approximation */
3425 best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3428 /* offset by typical page length, and ensure our best guess is within
3429 reasonable bounds */
3433 if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3434 best = ogg->push_byte_length - 1;
3436 /* set up bisection search */
3437 ogg->push_offset0 = 0;
3438 ogg->push_offset1 = ogg->push_byte_length - 1;
3439 ogg->push_time0 = ogg->push_start_time;
3440 ogg->push_time1 = ogg->push_time_length;
3441 ogg->push_seek_time_target = start;
3442 ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
3443 ogg->push_seek_time_original_target = start;
3444 ogg->push_state = PUSH_BISECT1;
3445 ogg->seek_secant = FALSE;
3446 ogg->seek_undershot = FALSE;
3448 /* reset pad push mode seeking state */
3449 for (i = 0; i < chain->streams->len; i++) {
3450 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3451 pad->push_kf_time = GST_CLOCK_TIME_NONE;
3452 pad->push_sync_time = GST_CLOCK_TIME_NONE;
3455 GST_DEBUG_OBJECT (ogg,
3456 "Setting up bisection search for %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
3457 " (time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", ogg->push_offset0,
3458 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
3459 GST_TIME_ARGS (ogg->push_time1));
3460 GST_DEBUG_OBJECT (ogg,
3461 "Target time is %" GST_TIME_FORMAT ", best first guess is %"
3462 G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target), best);
3464 ogg->push_seek_rate = rate;
3465 ogg->push_seek_flags = flags;
3466 ogg->push_mode_seek_delayed_event = NULL;
3467 ogg->push_bisection_steps[0] = 1;
3468 ogg->push_bisection_steps[1] = 0;
3469 sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
3470 start_type, best, GST_SEEK_TYPE_NONE, -1);
3472 GST_PUSH_UNLOCK (ogg);
3473 res = gst_pad_push_event (ogg->sinkpad, sevent);
3480 GST_DEBUG_OBJECT (ogg, "seek failed");
3485 GST_PUSH_UNLOCK (ogg);
3490 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
3494 if (ogg->pullmode) {
3495 res = gst_ogg_demux_perform_seek_pull (ogg, event);
3497 res = gst_ogg_demux_perform_seek_push (ogg, event);
3503 /* finds each bitstream link one at a time using a bisection search
3504 * (has to begin by knowing the offset of the lb's initial page).
3505 * Recurses for each link so it can alloc the link storage after
3506 * finding them all, then unroll and fill the cache at the same time
3508 static GstFlowReturn
3509 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
3510 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
3512 gint64 endsearched = end;
3517 GstOggChain *nextchain;
3519 GST_LOG_OBJECT (ogg,
3520 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
3521 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
3523 /* the below guards against garbage seperating the last and
3524 * first pages of two links. */
3525 while (searched < endsearched) {
3528 if (endsearched - searched < CHUNKSIZE) {
3531 bisect = (searched + endsearched) / 2;
3534 gst_ogg_demux_seek (ogg, bisect);
3535 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
3537 if (ret == GST_FLOW_UNEXPECTED) {
3538 endsearched = bisect;
3539 } else if (ret == GST_FLOW_OK) {
3540 guint32 serial = ogg_page_serialno (&og);
3542 if (!gst_ogg_chain_has_stream (chain, serial)) {
3543 endsearched = bisect;
3546 searched = offset + og.header_len + og.body_len;
3552 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
3554 chain->end_offset = searched;
3555 ret = gst_ogg_demux_read_end_chain (ogg, chain);
3556 if (ret != GST_FLOW_OK)
3559 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
3561 gst_ogg_demux_seek (ogg, next);
3562 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
3563 if (ret == GST_FLOW_UNEXPECTED) {
3566 GST_LOG_OBJECT (ogg, "no next chain");
3567 } else if (ret != GST_FLOW_OK)
3570 if (searched < end && nextchain != NULL) {
3571 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
3572 end, nextchain, m + 1);
3573 if (ret != GST_FLOW_OK)
3576 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
3578 g_array_insert_val (ogg->chains, 0, chain);
3584 /* read a chain from the ogg file. This code will
3585 * read all BOS pages and will create and return a GstOggChain
3586 * structure with the results.
3588 * This function will also read N pages from each stream in the
3589 * chain and submit them to the decoders. When the decoder has
3590 * decoded the first buffer, we know the timestamp of the first
3591 * page in the chain.
3593 static GstFlowReturn
3594 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
3597 GstOggChain *chain = NULL;
3598 gint64 offset = ogg->offset;
3603 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
3605 /* first read the BOS pages, do typefind on them, create
3606 * the decoders, send data to the decoders. */
3611 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3612 if (ret != GST_FLOW_OK) {
3613 if (ret == GST_FLOW_UNEXPECTED) {
3614 GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
3616 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
3620 if (!ogg_page_bos (&og)) {
3621 GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
3622 /* if we did not find a chain yet, assume this is a bogus stream and
3625 GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
3626 ret = GST_FLOW_UNEXPECTED;
3631 if (chain == NULL) {
3632 chain = gst_ogg_chain_new (ogg);
3633 chain->offset = offset;
3636 serial = ogg_page_serialno (&og);
3637 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
3638 GST_WARNING_OBJECT (ogg,
3639 "found serial %08x BOS page twice, ignoring", serial);
3643 pad = gst_ogg_chain_new_stream (chain, serial);
3644 gst_ogg_pad_submit_page (pad, &og);
3647 if (ret != GST_FLOW_OK || chain == NULL) {
3648 if (ret == GST_FLOW_OK) {
3649 GST_WARNING_OBJECT (ogg, "no chain was found");
3650 ret = GST_FLOW_ERROR;
3651 } else if (ret != GST_FLOW_UNEXPECTED) {
3652 GST_WARNING_OBJECT (ogg, "failed to read chain");
3654 GST_DEBUG_OBJECT (ogg, "done reading chains");
3657 gst_ogg_chain_free (chain);
3664 chain->have_bos = TRUE;
3665 GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
3667 /* now read pages until we receive a buffer from each of the
3668 * stream decoders, this will tell us the timestamp of the
3669 * first packet in the chain then */
3671 /* save the offset to the first non bos page in the chain: if searching for
3672 * pad->first_time we read past the end of the chain, we'll seek back to this
3675 offset = ogg->offset;
3680 gboolean known_serial = FALSE;
3683 serial = ogg_page_serialno (&og);
3685 for (i = 0; i < chain->streams->len; i++) {
3686 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3688 GST_LOG_OBJECT (ogg,
3689 "serial %08x time %" GST_TIME_FORMAT,
3690 pad->map.serialno, GST_TIME_ARGS (pad->start_time));
3692 if (pad->map.serialno == serial) {
3693 known_serial = TRUE;
3695 /* submit the page now, this will fill in the start_time when the
3696 * internal decoder finds it */
3697 gst_ogg_pad_submit_page (pad, &og);
3699 if (!pad->map.is_skeleton && pad->start_time == -1
3700 && ogg_page_eos (&og)) {
3701 /* got EOS on a pad before we could find its start_time.
3702 * We have no chance of finding a start_time for every pad so
3703 * stop searching for the other start_time(s).
3709 /* the timestamp will be filled in when we submit the pages */
3710 if (!pad->map.is_sparse)
3711 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
3713 GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
3716 /* we read a page not belonging to the current chain: seek back to the
3717 * beginning of the chain
3719 if (!known_serial) {
3720 GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
3721 gst_ogg_demux_seek (ogg, offset);
3726 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3727 if (ret != GST_FLOW_OK)
3731 GST_LOG_OBJECT (ogg, "done reading chain");
3732 /* now we can fill in the missing info using queries */
3733 for (i = 0; i < chain->streams->len; i++) {
3734 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3736 if (pad->map.is_skeleton)
3739 pad->mode = GST_OGG_PAD_MODE_STREAMING;
3748 /* read the last pages from the ogg stream to get the final
3751 static GstFlowReturn
3752 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
3754 gint64 begin = chain->end_offset;
3756 gint64 last_granule = -1;
3757 GstOggPad *last_pad = NULL;
3759 gboolean done = FALSE;
3768 gst_ogg_demux_seek (ogg, begin);
3770 /* now continue reading until we run out of data, if we find a page
3771 * start, we save it. It might not be the final page as there could be
3772 * another page after this one. */
3773 while (ogg->offset < end) {
3774 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
3776 if (ret == GST_FLOW_LIMIT)
3778 if (ret != GST_FLOW_OK)
3781 for (i = 0; i < chain->streams->len; i++) {
3782 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3784 if (pad->map.is_skeleton)
3787 if (pad->map.serialno == ogg_page_serialno (&og)) {
3788 gint64 granulepos = ogg_page_granulepos (&og);
3790 if (granulepos != -1) {
3791 last_granule = granulepos;
3802 chain->segment_stop =
3803 gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
3806 chain->segment_stop = GST_CLOCK_TIME_NONE;
3809 GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
3814 /* find a pad with a given serial number
3817 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
3822 /* first look in building chain if any */
3823 if (ogg->building_chain) {
3824 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
3829 /* then look in current chain if any */
3830 if (ogg->current_chain) {
3831 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
3836 for (i = 0; i < ogg->chains->len; i++) {
3837 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3839 pad = gst_ogg_chain_get_stream (chain, serialno);
3846 /* find a chain with a given serial number
3848 static GstOggChain *
3849 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
3853 pad = gst_ogg_demux_find_pad (ogg, serialno);
3860 /* returns TRUE if all streams have valid start time */
3862 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
3864 gboolean res = TRUE;
3866 chain->total_time = GST_CLOCK_TIME_NONE;
3867 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
3869 /* see if we have a start time on all streams */
3870 chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
3872 if (chain->segment_start == G_MAXUINT64) {
3873 /* not yet, stream some more data */
3875 } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
3876 /* we can calculate a total time */
3877 chain->total_time = chain->segment_stop - chain->segment_start;
3880 GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
3882 GST_DEBUG_OBJECT (ogg, "return %d", res);
3888 gst_ogg_demux_collect_info (GstOggDemux * ogg)
3892 /* collect all info */
3893 ogg->total_time = 0;
3895 for (i = 0; i < ogg->chains->len; i++) {
3896 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3898 chain->begin_time = ogg->total_time;
3900 gst_ogg_demux_collect_chain_info (ogg, chain);
3902 ogg->total_time += chain->total_time;
3904 gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time);
3907 /* find all the chains in the ogg file, this reads the first and
3908 * last page of the ogg stream, if they match then the ogg file has
3909 * just one chain, else we do a binary search for all chains.
3911 static GstFlowReturn
3912 gst_ogg_demux_find_chains (GstOggDemux * ogg)
3922 /* get peer to figure out length */
3923 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
3926 /* find length to read last page, we store this for later use. */
3927 format = GST_FORMAT_BYTES;
3928 res = gst_pad_query_duration (peer, &format, &ogg->length);
3929 gst_object_unref (peer);
3930 if (!res || ogg->length <= 0)
3933 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
3935 /* read chain from offset 0, this is the first chain of the
3937 gst_ogg_demux_seek (ogg, 0);
3938 ret = gst_ogg_demux_read_chain (ogg, &chain);
3939 if (ret != GST_FLOW_OK)
3940 goto no_first_chain;
3942 /* read page from end offset, we use this page to check if its serial
3943 * number is contained in the first chain. If this is the case then
3944 * this ogg is not a chained ogg and we can skip the scanning. */
3945 gst_ogg_demux_seek (ogg, ogg->length);
3946 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
3947 if (ret != GST_FLOW_OK)
3950 serialno = ogg_page_serialno (&og);
3952 if (!gst_ogg_chain_has_stream (chain, serialno)) {
3953 /* the last page is not in the first stream, this means we should
3954 * find all the chains in this chained ogg. */
3956 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
3959 /* we still call this function here but with an empty range so that
3960 * we can reuse the setup code in this routine. */
3962 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
3963 ogg->length, chain, 0);
3965 if (ret != GST_FLOW_OK)
3968 /* all fine, collect and print */
3969 gst_ogg_demux_collect_info (ogg);
3971 /* dump our chains and streams */
3972 gst_ogg_print (ogg);
3977 /*** error cases ***/
3980 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
3981 return GST_FLOW_NOT_LINKED;
3985 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
3986 return GST_FLOW_NOT_SUPPORTED;
3990 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
3991 return GST_FLOW_ERROR;
3995 GST_DEBUG_OBJECT (ogg, "can't get last page");
3997 gst_ogg_chain_free (chain);
4002 static GstFlowReturn
4003 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
4008 GstFlowReturn result = GST_FLOW_OK;
4010 serialno = ogg_page_serialno (page);
4011 granule = ogg_page_granulepos (page);
4013 GST_LOG_OBJECT (ogg,
4014 "processing ogg page (serial %08x, "
4015 "pageno %ld, granulepos %" G_GINT64_FORMAT ", bos %d)", serialno,
4016 ogg_page_pageno (page), granule, ogg_page_bos (page));
4018 if (ogg_page_bos (page)) {
4022 /* see if we know about the chain already */
4023 chain = gst_ogg_demux_find_chain (ogg, serialno);
4028 if (chain->segment_start != GST_CLOCK_TIME_NONE)
4029 start = chain->segment_start;
4031 /* create the newsegment event we are going to send out */
4032 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
4033 GST_FORMAT_TIME, start, chain->segment_stop, chain->begin_time);
4034 gst_event_set_seqnum (event, ogg->seqnum);
4036 GST_DEBUG_OBJECT (ogg,
4037 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
4038 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
4039 GST_TIME_ARGS (chain->segment_stop),
4040 GST_TIME_ARGS (chain->begin_time));
4042 /* activate it as it means we have a non-header, this will also deactivate
4043 * the currently running chain. */
4044 gst_ogg_demux_activate_chain (ogg, chain, event);
4045 pad = gst_ogg_demux_find_pad (ogg, serialno);
4047 GstClockTime chain_time;
4048 gint64 current_time;
4050 /* this can only happen in push mode */
4054 current_time = ogg->segment.last_stop;
4056 /* time of new chain is current time */
4057 chain_time = current_time;
4059 if (ogg->building_chain == NULL) {
4060 GstOggChain *newchain;
4062 newchain = gst_ogg_chain_new (ogg);
4063 newchain->offset = 0;
4064 /* set new chain begin time aligned with end time of old chain */
4065 newchain->begin_time = chain_time;
4066 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
4067 GST_TIME_ARGS (chain_time));
4069 /* and this is the one we are building now */
4070 ogg->building_chain = newchain;
4072 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
4075 pad = gst_ogg_demux_find_pad (ogg, serialno);
4078 result = gst_ogg_pad_submit_page (pad, page);
4080 GST_PUSH_LOCK (ogg);
4081 if (!ogg->pullmode && !ogg->push_disable_seeking) {
4082 /* no pad while probing for duration, we must have a chained stream,
4083 and we don't support them, so back off */
4084 GST_INFO_OBJECT (ogg, "We seem to have a chained stream, we won't seek");
4085 if (ogg->push_state == PUSH_DURATION) {
4088 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
4089 if (res != GST_FLOW_OK)
4093 /* only once we seeked back */
4094 GST_PUSH_LOCK (ogg);
4095 ogg->push_disable_seeking = TRUE;
4097 GST_PUSH_UNLOCK (ogg);
4098 /* no pad. This means an ogg page without bos has been seen for this
4099 * serialno. we just ignore it but post a warning... */
4100 GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
4101 (NULL), ("unknown ogg pad for serial %08x detected", serialno));
4104 GST_PUSH_UNLOCK (ogg);
4111 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
4112 (NULL), ("unknown ogg chain for serial %08x detected", serialno));
4113 return GST_FLOW_ERROR;
4117 /* streaming mode, receive a buffer, parse it, create pads for
4118 * the serialno, submit pages and packets to the oggpads
4120 static GstFlowReturn
4121 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
4125 GstFlowReturn result = GST_FLOW_OK;
4127 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4129 GST_DEBUG_OBJECT (ogg, "enter");
4130 result = gst_ogg_demux_submit_buffer (ogg, buffer);
4132 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
4135 while (result == GST_FLOW_OK) {
4138 ret = ogg_sync_pageout (&ogg->sync, &page);
4140 /* need more data */
4143 /* discontinuity in the pages */
4144 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
4146 result = gst_ogg_demux_handle_page (ogg, &page);
4148 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
4152 if (ret == 0 || result == GST_FLOW_OK) {
4153 gst_ogg_demux_sync_streams (ogg);
4155 GST_DEBUG_OBJECT (ogg, "leave with %d", result);
4160 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
4162 GstOggChain *chain = ogg->current_chain;
4163 gboolean res = TRUE;
4166 chain = ogg->building_chain;
4171 for (i = 0; i < chain->streams->len; i++) {
4172 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4174 gst_event_ref (event);
4175 GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
4176 res &= gst_pad_push_event (GST_PAD (pad), event);
4179 GST_WARNING_OBJECT (ogg, "No chain to forward event to");
4181 gst_event_unref (event);
4186 static GstFlowReturn
4187 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
4192 /* store the value */
4193 pad->last_ret = ret;
4195 /* any other error that is not-linked can be returned right
4197 if (ret != GST_FLOW_NOT_LINKED)
4200 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
4201 chain = ogg->current_chain;
4205 for (i = 0; i < chain->streams->len; i++) {
4206 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4208 ret = opad->last_ret;
4209 /* some other return value (must be SUCCESS but we can return
4210 * other values as well) */
4211 if (ret != GST_FLOW_NOT_LINKED)
4214 /* if we get here, all other pads were unlinked and we return
4215 * NOT_LINKED then */
4221 /* returns TRUE if all streams in current chain reached EOS, FALSE otherwise */
4223 gst_ogg_demux_check_eos (GstOggDemux * ogg)
4226 gboolean eos = TRUE;
4228 chain = ogg->current_chain;
4229 if (G_LIKELY (chain)) {
4232 for (i = 0; i < chain->streams->len; i++) {
4233 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4235 eos = eos && opad->is_eos;
4244 static GstFlowReturn
4245 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4250 if (ogg->offset == ogg->length) {
4251 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4252 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
4253 ret = GST_FLOW_UNEXPECTED;
4257 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4258 ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
4259 if (ret != GST_FLOW_OK) {
4260 GST_LOG_OBJECT (ogg, "Failed pull_range");
4264 ogg->offset += GST_BUFFER_SIZE (buffer);
4266 if (G_UNLIKELY (ogg->newsegment)) {
4267 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4268 ogg->newsegment = NULL;
4271 ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
4272 if (ret != GST_FLOW_OK) {
4273 GST_LOG_OBJECT (ogg, "Failed demux_chain");
4277 /* check for the end of the segment */
4278 if (gst_ogg_demux_check_eos (ogg)) {
4279 GST_LOG_OBJECT (ogg, "got EOS");
4280 ret = GST_FLOW_UNEXPECTED;
4289 * We read the pages backwards and send the packets forwards. The first packet
4290 * in the page will be pushed with the DISCONT flag set.
4292 * Special care has to be taken for continued pages, which we can only decode
4293 * when we have the previous page(s).
4295 static GstFlowReturn
4296 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4302 if (ogg->offset == 0) {
4303 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4304 " == 0", ogg->offset);
4305 ret = GST_FLOW_UNEXPECTED;
4309 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
4310 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
4311 if (ret != GST_FLOW_OK)
4314 ogg->offset = offset;
4316 if (G_UNLIKELY (ogg->newsegment)) {
4317 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4318 ogg->newsegment = NULL;
4321 ret = gst_ogg_demux_handle_page (ogg, &page);
4322 if (ret != GST_FLOW_OK)
4325 /* check for the end of the segment */
4326 if (gst_ogg_demux_check_eos (ogg)) {
4327 GST_LOG_OBJECT (ogg, "got EOS");
4328 ret = GST_FLOW_UNEXPECTED;
4336 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4342 chain = ogg->current_chain;
4343 cur = ogg->segment.last_stop;
4344 if (chain == NULL || cur == -1)
4347 for (i = 0; i < chain->streams->len; i++) {
4348 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4350 /* Theoretically, we should be doing this for all streams, but we're only
4351 * doing it for known-to-be-sparse streams at the moment in order not to
4352 * break things for wrongly-muxed streams (like we used to produce once) */
4353 if (stream->map.is_sparse && stream->last_stop != GST_CLOCK_TIME_NONE) {
4355 /* Does this stream lag? Random threshold of 2 seconds */
4356 if (GST_CLOCK_DIFF (stream->last_stop, cur) > (2 * GST_SECOND)) {
4357 GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
4358 "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
4359 GST_TIME_ARGS (stream->last_stop), GST_TIME_ARGS (cur));
4360 stream->last_stop = cur;
4361 /* advance stream time (FIXME: is this right, esp. time_pos?) */
4362 gst_pad_push_event (GST_PAD_CAST (stream),
4363 gst_event_new_new_segment (TRUE, ogg->segment.rate,
4364 GST_FORMAT_TIME, stream->last_stop, -1, stream->last_stop));
4370 /* random access code
4372 * - first find all the chains and streams by scanning the file.
4373 * - then get and chain buffers, just like the streaming case.
4374 * - when seeking, we can use the chain info to perform the seek.
4377 gst_ogg_demux_loop (GstOggPad * pad)
4383 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4385 if (ogg->need_chains) {
4388 /* this is the only place where we write chains and thus need to lock. */
4389 GST_CHAIN_LOCK (ogg);
4390 ret = gst_ogg_demux_find_chains (ogg);
4391 GST_CHAIN_UNLOCK (ogg);
4392 if (ret != GST_FLOW_OK)
4393 goto chain_read_failed;
4395 ogg->need_chains = FALSE;
4397 GST_OBJECT_LOCK (ogg);
4398 ogg->running = TRUE;
4401 GST_OBJECT_UNLOCK (ogg);
4403 /* and seek to configured positions without FLUSH */
4404 res = gst_ogg_demux_perform_seek_pull (ogg, event);
4406 gst_event_unref (event);
4412 if (ogg->segment.rate >= 0.0)
4413 ret = gst_ogg_demux_loop_forward (ogg);
4415 ret = gst_ogg_demux_loop_reverse (ogg);
4417 if (ret != GST_FLOW_OK)
4420 gst_ogg_demux_sync_streams (ogg);
4426 /* error was posted */
4431 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4432 ("failed to start demuxing ogg"));
4433 ret = GST_FLOW_ERROR;
4438 const gchar *reason = gst_flow_get_name (ret);
4439 GstEvent *event = NULL;
4441 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4442 ogg->segment_running = FALSE;
4443 gst_pad_pause_task (ogg->sinkpad);
4445 if (ret == GST_FLOW_UNEXPECTED) {
4446 /* perform EOS logic */
4447 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4449 GstMessage *message;
4451 /* for segment playback we need to post when (in stream time)
4452 * we stopped, this is either stop (when set) or the duration. */
4453 if ((stop = ogg->segment.stop) == -1)
4454 stop = ogg->segment.duration;
4456 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
4458 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
4460 gst_message_set_seqnum (message, ogg->seqnum);
4462 gst_element_post_message (GST_ELEMENT (ogg), message);
4464 /* normal playback, send EOS to all linked pads */
4465 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
4466 event = gst_event_new_eos ();
4468 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) {
4469 GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
4470 (_("Internal data stream error.")),
4471 ("stream stopped, reason %s", reason));
4472 event = gst_event_new_eos ();
4475 /* For wrong-state we still want to pause the task and stop
4476 * but no error message or other things are necessary.
4477 * wrong-state is no real error and will be caused by flushing,
4478 * e.g. because of a flushing seek.
4481 /* guard against corrupt/truncated files, where one can hit EOS
4482 before prerolling is done and a chain created. If we have no
4483 chain to send the event to, error out. */
4484 if (ogg->current_chain || ogg->building_chain) {
4485 gst_event_set_seqnum (event, ogg->seqnum);
4486 gst_ogg_demux_send_event (ogg, event);
4488 gst_event_unref (event);
4489 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4490 ("EOS before finding a chain"));
4498 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
4502 gst_ogg_demux_deactivate_current_chain (ogg);
4504 GST_CHAIN_LOCK (ogg);
4505 for (i = 0; i < ogg->chains->len; i++) {
4506 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4508 gst_ogg_chain_free (chain);
4510 ogg->chains = g_array_set_size (ogg->chains, 0);
4511 GST_CHAIN_UNLOCK (ogg);
4514 /* this function is called when the pad is activated and should start
4517 * We check if we can do random access to decide if we work push or
4521 gst_ogg_demux_sink_activate (GstPad * sinkpad)
4523 if (gst_pad_check_pull_range (sinkpad)) {
4524 GST_DEBUG_OBJECT (sinkpad, "activating pull");
4525 return gst_pad_activate_pull (sinkpad, TRUE);
4527 GST_DEBUG_OBJECT (sinkpad, "activating push");
4528 return gst_pad_activate_push (sinkpad, TRUE);
4532 /* this function gets called when we activate ourselves in push mode.
4533 * We cannot seek (ourselves) in the stream */
4535 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
4539 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
4541 ogg->pullmode = FALSE;
4542 ogg->resync = FALSE;
4547 /* this function gets called when we activate ourselves in pull mode.
4548 * We can perform random access to the resource and we start a task
4549 * to start reading */
4551 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
4555 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
4558 ogg->need_chains = TRUE;
4559 ogg->pullmode = TRUE;
4561 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
4564 return gst_pad_stop_task (sinkpad);
4568 static GstStateChangeReturn
4569 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
4572 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
4574 ogg = GST_OGG_DEMUX (element);
4576 switch (transition) {
4577 case GST_STATE_CHANGE_NULL_TO_READY:
4579 ogg_sync_init (&ogg->sync);
4581 case GST_STATE_CHANGE_READY_TO_PAUSED:
4582 ogg_sync_reset (&ogg->sync);
4583 ogg->running = FALSE;
4585 ogg->segment_running = FALSE;
4586 ogg->total_time = -1;
4587 GST_PUSH_LOCK (ogg);
4588 ogg->push_byte_offset = 0;
4589 ogg->push_byte_length = -1;
4590 ogg->push_time_length = GST_CLOCK_TIME_NONE;
4591 ogg->push_time_offset = GST_CLOCK_TIME_NONE;
4592 ogg->push_state = PUSH_PLAYING;
4594 ogg->push_disable_seeking = FALSE;
4595 if (!ogg->pullmode) {
4597 if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
4598 GstFormat format = GST_FORMAT_BYTES;
4600 if (!gst_pad_query_duration (peer, &format, &length) || length <= 0) {
4601 GST_DEBUG_OBJECT (ogg,
4602 "Unable to determine stream size, assuming live, seeking disabled");
4603 ogg->push_disable_seeking = TRUE;
4605 gst_object_unref (peer);
4609 GST_PUSH_UNLOCK (ogg);
4610 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
4612 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4618 result = parent_class->change_state (element, transition);
4620 switch (transition) {
4621 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4623 case GST_STATE_CHANGE_PAUSED_TO_READY:
4624 gst_ogg_demux_clear_chains (ogg);
4625 GST_OBJECT_LOCK (ogg);
4626 ogg->running = FALSE;
4627 ogg->segment_running = FALSE;
4628 GST_OBJECT_UNLOCK (ogg);
4630 case GST_STATE_CHANGE_READY_TO_NULL:
4631 ogg_sync_clear (&ogg->sync);
4640 gst_ogg_demux_plugin_init (GstPlugin * plugin)
4642 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
4643 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
4644 "ogg demuxer setup stage when parsing pipeline");
4647 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
4649 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4650 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4653 return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
4654 GST_TYPE_OGG_DEMUX);
4657 /* prints all info about the element */
4658 #undef GST_CAT_DEFAULT
4659 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
4661 #ifdef GST_DISABLE_GST_DEBUG
4664 gst_ogg_print (GstOggDemux * ogg)
4669 #else /* !GST_DISABLE_GST_DEBUG */
4672 gst_ogg_print (GstOggDemux * ogg)
4676 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
4677 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4678 GST_TIME_ARGS (ogg->total_time));
4680 for (i = 0; i < ogg->chains->len; i++) {
4681 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4683 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
4684 GST_INFO_OBJECT (ogg,
4685 " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
4687 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
4688 GST_TIME_ARGS (chain->begin_time));
4689 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4690 GST_TIME_ARGS (chain->total_time));
4691 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
4692 GST_TIME_ARGS (chain->segment_start));
4693 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
4694 GST_TIME_ARGS (chain->segment_stop));
4696 for (j = 0; j < chain->streams->len; j++) {
4697 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
4699 GST_INFO_OBJECT (ogg, " stream %08x: %s", stream->map.serialno,
4700 gst_ogg_stream_get_media_type (&stream->map));
4701 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
4702 GST_TIME_ARGS (stream->start_time));
4706 #endif /* GST_DISABLE_GST_DEBUG */