2 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
4 * gstoggdemux.c: ogg stream demuxer
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 * SECTION:element-oggdemux
24 * @see_also: <link linkend="gst-plugins-base-plugins-oggmux">oggmux</link>
26 * This element demuxes ogg files into their encoded audio and video components.
29 * <title>Example pipelines</title>
31 * gst-launch -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
32 * ]| Decodes the vorbis audio stored inside an ogg container.
35 * Last reviewed on 2006-12-30 (0.10.5)
43 #include <gst/gst-i18n-plugin.h>
44 #include <gst/tag/tag.h>
46 #include "gstoggdemux.h"
48 #define CHUNKSIZE (8500) /* this is out of vorbisfile */
50 /* we hope we get a granpos within this many bytes off the end */
51 #define DURATION_CHUNK_OFFSET (64*1024)
53 /* stop duration checks within this much of EOS */
54 #define EOS_AVOIDANCE_THRESHOLD 8192
56 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
57 #define GST_FLOW_SKIP_PUSH GST_FLOW_CUSTOM_SUCCESS_1
59 #define GST_CHAIN_LOCK(ogg) g_mutex_lock((ogg)->chain_lock)
60 #define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock((ogg)->chain_lock)
62 #define GST_PUSH_LOCK(ogg) \
64 GST_TRACE_OBJECT(ogg, "Push lock"); \
65 g_mutex_lock((ogg)->push_lock); \
68 #define GST_PUSH_UNLOCK(ogg) \
70 GST_TRACE_OBJECT(ogg, "Push unlock"); \
71 g_mutex_unlock((ogg)->push_lock); \
74 GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
75 GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
76 #define GST_CAT_DEFAULT gst_ogg_demux_debug
80 _ogg_packet_copy (const ogg_packet * packet)
82 ogg_packet *ret = g_slice_new (ogg_packet);
85 ret->packet = g_memdup (packet->packet, packet->bytes);
91 _ogg_packet_free (ogg_packet * packet)
93 g_free (packet->packet);
94 g_slice_free (ogg_packet, packet);
98 gst_ogg_page_copy (ogg_page * page)
100 ogg_page *p = g_slice_new (ogg_page);
102 /* make a copy of the page */
103 p->header = g_memdup (page->header, page->header_len);
104 p->header_len = page->header_len;
105 p->body = g_memdup (page->body, page->body_len);
106 p->body_len = page->body_len;
112 gst_ogg_page_free (ogg_page * page)
114 g_free (page->header);
116 g_slice_free (ogg_page, page);
119 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
120 GstOggChain * chain);
121 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
122 GstOggChain * chain, GstEvent * event);
123 static void gst_ogg_pad_mark_discont (GstOggPad * pad);
124 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
126 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
128 static gboolean gst_ogg_demux_receive_event (GstElement * element,
131 static void gst_ogg_pad_dispose (GObject * object);
132 static void gst_ogg_pad_finalize (GObject * object);
134 static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
135 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
136 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
137 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
140 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
141 GstOggPad * pad, GstFlowReturn ret);
142 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
144 GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg,
145 GstCaps * caps, GList * headers);
146 static gboolean gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
147 static gboolean gst_ogg_demux_perform_seek_push (GstOggDemux * ogg,
149 static gboolean gst_ogg_demux_check_duration_push (GstOggDemux * ogg,
150 GstSeekFlags flags, GstEvent * event);
152 GType gst_ogg_pad_get_type (void);
153 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
156 gst_ogg_pad_class_init (GstOggPadClass * klass)
158 GObjectClass *gobject_class;
160 gobject_class = (GObjectClass *) klass;
162 gobject_class->dispose = gst_ogg_pad_dispose;
163 gobject_class->finalize = gst_ogg_pad_finalize;
167 gst_ogg_pad_init (GstOggPad * pad)
169 gst_pad_set_event_function (GST_PAD (pad),
170 GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
171 gst_pad_set_query_type_function (GST_PAD (pad),
172 GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types));
173 gst_pad_set_query_function (GST_PAD (pad),
174 GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
175 gst_pad_use_fixed_caps (GST_PAD (pad));
177 pad->mode = GST_OGG_PAD_MODE_INIT;
179 pad->current_granule = -1;
180 pad->keyframe_granule = -1;
182 pad->start_time = GST_CLOCK_TIME_NONE;
184 pad->position = GST_CLOCK_TIME_NONE;
186 pad->have_type = FALSE;
187 pad->continued = NULL;
188 pad->map.headers = NULL;
189 pad->map.queued = NULL;
191 pad->map.granulerate_n = 0;
192 pad->map.granulerate_d = 0;
193 pad->map.granuleshift = -1;
197 gst_ogg_pad_dispose (GObject * object)
199 GstOggPad *pad = GST_OGG_PAD (object);
204 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
205 g_list_free (pad->map.headers);
206 pad->map.headers = NULL;
207 g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
208 g_list_free (pad->map.queued);
209 pad->map.queued = NULL;
211 g_free (pad->map.index);
212 pad->map.index = NULL;
214 /* clear continued pages */
215 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
216 g_list_free (pad->continued);
217 pad->continued = NULL;
220 gst_caps_unref (pad->map.caps);
221 pad->map.caps = NULL;
224 if (pad->map.taglist) {
225 gst_tag_list_free (pad->map.taglist);
226 pad->map.taglist = NULL;
229 ogg_stream_reset (&pad->map.stream);
231 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
235 gst_ogg_pad_finalize (GObject * object)
237 GstOggPad *pad = GST_OGG_PAD (object);
239 ogg_stream_clear (&pad->map.stream);
241 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
244 static const GstQueryType *
245 gst_ogg_pad_query_types (GstPad * pad)
247 static const GstQueryType query_types[] = {
257 gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
262 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
264 switch (GST_QUERY_TYPE (query)) {
265 case GST_QUERY_DURATION:
268 gint64 total_time = -1;
270 gst_query_parse_duration (query, &format, NULL);
271 /* can only get position in time */
272 if (format != GST_FORMAT_TIME)
275 if (ogg->total_time != -1) {
276 /* we can return the total length */
277 total_time = ogg->total_time;
279 gint bitrate = ogg->bitrate;
281 /* try with length and bitrate */
285 /* ask upstream for total length in bytes */
286 uquery = gst_query_new_duration (GST_FORMAT_BYTES);
287 if (gst_pad_peer_query (ogg->sinkpad, uquery)) {
290 gst_query_parse_duration (uquery, NULL, &length);
292 /* estimate using the bitrate */
294 gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);
297 "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
298 GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
300 gst_query_unref (uquery);
304 gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
307 case GST_QUERY_SEEKING:
311 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
312 if (format == GST_FORMAT_TIME) {
313 gboolean seekable = FALSE;
318 stop = ogg->total_time;
319 } else if (ogg->current_chain->streams->len) {
323 for (i = 0; i < ogg->current_chain->streams->len; i++) {
325 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
328 if (pad->map.index != NULL && pad->map.n_index != 0) {
330 GstClockTime idx_time;
332 idx = &pad->map.index[pad->map.n_index - 1];
334 gst_util_uint64_scale (idx->timestamp, GST_SECOND,
339 stop = MAX (idx_time, stop);
341 stop = -1; /* we've no clue, sadly, without seeking */
346 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
354 res = gst_pad_query_default (pad, query);
358 gst_object_unref (ogg);
365 GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
372 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
377 ogg = GST_OGG_DEMUX (element);
379 switch (GST_EVENT_TYPE (event)) {
381 /* now do the seek */
382 res = gst_ogg_demux_perform_seek (ogg, event);
383 gst_event_unref (event);
386 GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
395 GST_DEBUG_OBJECT (ogg, "error handling event");
396 gst_event_unref (event);
402 gst_ogg_pad_event (GstPad * pad, GstEvent * event)
407 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
409 switch (GST_EVENT_TYPE (event)) {
411 /* now do the seek */
412 res = gst_ogg_demux_perform_seek (ogg, event);
413 gst_event_unref (event);
416 res = gst_pad_event_default (pad, event);
419 gst_object_unref (ogg);
425 gst_ogg_pad_reset (GstOggPad * pad)
427 ogg_stream_reset (&pad->map.stream);
429 GST_DEBUG_OBJECT (pad, "doing reset");
431 /* clear continued pages */
432 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
433 g_list_free (pad->continued);
434 pad->continued = NULL;
436 pad->last_ret = GST_FLOW_OK;
437 pad->position = GST_CLOCK_TIME_NONE;
438 pad->current_granule = -1;
439 pad->keyframe_granule = -1;
443 /* queue data, basically takes the packet, puts it in a buffer and store the
444 * buffer in the queued list. */
446 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
448 #ifndef GST_DISABLE_GST_DEBUG
449 GstOggDemux *ogg = pad->ogg;
452 GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x",
453 pad, pad->map.serialno);
455 pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
462 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
463 gboolean push_headers)
465 GstBuffer *buf = NULL;
466 GstFlowReturn ret, cret;
467 GstOggDemux *ogg = pad->ogg;
473 GstClockTime out_timestamp, out_duration;
474 guint64 out_offset, out_offset_end;
475 gboolean delta_unit = FALSE;
480 if (!ogg->pullmode && ogg->push_state == PUSH_PLAYING
481 && ogg->push_time_length == GST_CLOCK_TIME_NONE
482 && !ogg->push_disable_seeking) {
483 if (!ogg->building_chain) {
484 /* we got all headers, now try to get duration */
485 if (!gst_ogg_demux_check_duration_push (ogg, GST_SEEK_FLAG_FLUSH, NULL)) {
486 GST_PUSH_UNLOCK (ogg);
490 GST_PUSH_UNLOCK (ogg);
493 GST_PUSH_UNLOCK (ogg);
495 GST_DEBUG_OBJECT (ogg,
496 "%p streaming to peer serial %08x", pad, pad->map.serialno);
498 if (pad->map.is_ogm) {
502 data = packet->packet;
503 bytes = packet->bytes;
508 if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
509 /* We don't push header packets for OGM */
513 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
514 delta_unit = (((data[0] & 0x08) >> 3) == 0);
518 /* Strip trailing \0 for subtitles */
519 if (pad->map.is_ogm_text) {
520 while (bytes && data[bytes - 1] == 0) {
525 } else if (pad->map.is_vp8) {
526 if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
528 (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) {
529 /* We don't push header packets for VP8 */
539 /* get timing info for the packet */
540 if (gst_ogg_stream_packet_is_header (&pad->map, packet)) {
542 GST_DEBUG_OBJECT (ogg, "packet is header");
544 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
545 GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
549 out_timestamp = GST_CLOCK_TIME_NONE;
550 out_duration = GST_CLOCK_TIME_NONE;
554 if (packet->granulepos != -1) {
555 pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
557 pad->keyframe_granule =
558 gst_ogg_stream_granulepos_to_key_granule (&pad->map,
560 GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
561 pad->current_granule);
562 } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
563 pad->current_granule += duration;
564 GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT,
565 pad->current_granule);
567 if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
568 /* negative rates, only set timestamp on the packets with a granulepos */
574 /* we only push buffers after we have a valid granule. This is done so that
575 * we nicely skip packets without a timestamp after a seek. This is ok
576 * because we base or seek on the packet after the page with the smaller
578 if (pad->current_granule == -1)
581 if (pad->map.is_ogm) {
582 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
583 pad->current_granule);
584 out_duration = gst_util_uint64_scale (duration,
585 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
586 } else if (pad->map.is_sparse) {
587 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
588 pad->current_granule);
589 if (duration == GST_CLOCK_TIME_NONE) {
590 out_duration = GST_CLOCK_TIME_NONE;
592 out_duration = gst_util_uint64_scale (duration,
593 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
596 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
597 pad->current_granule - duration);
599 gst_ogg_stream_granule_to_time (&pad->map,
600 pad->current_granule) - out_timestamp;
603 gst_ogg_stream_granule_to_granulepos (&pad->map,
604 pad->current_granule, pad->keyframe_granule);
606 gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
610 if (pad->map.is_ogm_text) {
611 /* check for invalid buffer sizes */
612 if (G_UNLIKELY (offset + trim >= packet->bytes))
619 buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
621 /* set delta flag for OGM content */
623 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
625 /* copy packet in buffer */
626 gst_buffer_fill (buf, 0, packet->packet + offset,
627 packet->bytes - offset - trim);
629 GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
630 GST_BUFFER_DURATION (buf) = out_duration;
631 GST_BUFFER_OFFSET (buf) = out_offset;
632 GST_BUFFER_OFFSET_END (buf) = out_offset_end;
634 /* Mark discont on the buffer */
636 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
637 pad->discont = FALSE;
640 pad->position = ogg->segment.position;
642 /* don't push the header packets when we are asked to skip them */
643 if (!packet->b_o_s || push_headers) {
644 ret = gst_pad_push (GST_PAD_CAST (pad), buf);
648 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
651 /* we're done with skeleton stuff */
652 if (pad->map.is_skeleton)
655 /* check if valid granulepos, then we can calculate the current
656 * position. We know the granule for each packet but we only want to update
657 * the position when we have a valid granulepos on the packet because else
658 * our time jumps around for the different streams. */
659 if (packet->granulepos < 0)
662 /* convert to time */
663 current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
666 /* convert to stream time */
667 if ((chain = pad->chain)) {
668 gint64 chain_start = 0;
670 if (chain->segment_start != GST_CLOCK_TIME_NONE)
671 chain_start = chain->segment_start;
673 current_time = current_time - chain_start + chain->begin_time;
676 /* and store as the current position */
677 ogg->segment.position = current_time;
679 GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
680 GST_TIME_ARGS (current_time));
682 /* check stream eos */
683 if ((ogg->segment.rate > 0.0 && ogg->segment.stop != GST_CLOCK_TIME_NONE &&
684 current_time > ogg->segment.stop) ||
685 (ogg->segment.rate < 0.0 && ogg->segment.start != GST_CLOCK_TIME_NONE &&
686 current_time < ogg->segment.start)) {
687 GST_DEBUG_OBJECT (ogg, "marking pad %p EOS", pad);
693 gst_buffer_unref (buf);
694 /* return combined flow result */
700 GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
706 GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
711 GST_DEBUG_OBJECT (ogg, "pad not added yet");
717 gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
720 guint64 start_time = G_MAXUINT64;
722 for (i = 0; i < chain->streams->len; i++) {
723 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
725 if (pad->map.is_skeleton)
728 /* can do this if the pad start time is not defined */
729 GST_DEBUG_OBJECT (ogg, "Pad %08x (%s) start time is %" GST_TIME_FORMAT,
730 pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map),
731 GST_TIME_ARGS (pad->start_time));
732 if (pad->start_time == GST_CLOCK_TIME_NONE) {
733 if (!pad->map.is_sparse) {
734 start_time = G_MAXUINT64;
738 start_time = MIN (start_time, pad->start_time);
745 gst_ogg_demux_collect_sync_time (GstOggDemux * ogg, GstOggChain * chain)
748 GstClockTime sync_time = GST_CLOCK_TIME_NONE;
751 GST_WARNING_OBJECT (ogg, "No chain!");
752 return GST_CLOCK_TIME_NONE;
755 for (i = 0; i < chain->streams->len; i++) {
756 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
758 if (pad->map.is_sparse)
761 if (pad->push_sync_time == GST_CLOCK_TIME_NONE) {
762 sync_time = GST_CLOCK_TIME_NONE;
765 if (sync_time == GST_CLOCK_TIME_NONE)
766 sync_time = pad->push_sync_time;
768 sync_time = MAX (sync_time, pad->push_sync_time);
774 /* submit a packet to the oggpad, this function will run the
775 * typefind code for the pad if this is the first packet for this
779 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
782 GstFlowReturn ret = GST_FLOW_OK;
784 GstOggDemux *ogg = pad->ogg;
786 GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x",
787 pad, pad->map.serialno);
789 if (!pad->have_type) {
790 pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
791 if (!pad->have_type) {
792 pad->map.caps = gst_caps_new_empty_simple ("application/x-unknown");
794 if (pad->map.is_skeleton) {
795 GST_DEBUG_OBJECT (ogg, "we have a fishead");
796 /* copy values over to global ogg level */
797 ogg->basetime = pad->map.basetime;
798 ogg->prestime = pad->map.prestime;
800 /* use total time to update the total ogg time */
801 if (ogg->total_time == -1) {
802 ogg->total_time = pad->map.total_time;
803 } else if (pad->map.total_time > 0) {
804 ogg->total_time = MAX (ogg->total_time, pad->map.total_time);
808 gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
810 GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
814 if (pad->map.is_skeleton) {
819 /* try to parse the serialno first */
820 if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
823 GST_DEBUG_OBJECT (pad->ogg,
824 "got skeleton packet for stream 0x%08x", serialno);
826 skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
829 case GST_OGG_SKELETON_FISBONE:
830 /* parse the remainder of the fisbone in the pad with the serialno,
831 * note that we ignore the start_time as this is usually wrong for
833 gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
834 packet->bytes, NULL);
836 case GST_OGG_SKELETON_INDEX:
837 gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
840 /* use total time to update the total ogg time */
841 if (ogg->total_time == -1) {
842 ogg->total_time = skel_pad->map.total_time;
843 } else if (skel_pad->map.total_time > 0) {
844 ogg->total_time = MAX (ogg->total_time, skel_pad->map.total_time);
852 GST_WARNING_OBJECT (pad->ogg,
853 "found skeleton fisbone for an unknown stream 0x%08x", serialno);
858 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
861 GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
862 pad->current_granule = granule;
865 /* restart header packet count when seeing a b_o_s page;
866 * particularly useful following a seek or even following chain finding */
868 GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
869 pad->map.n_header_packets_seen = 0;
870 if (!pad->map.have_headers) {
871 GST_DEBUG_OBJECT (ogg, "clearing header packets");
872 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
873 g_list_free (pad->map.headers);
874 pad->map.headers = NULL;
878 /* Overload the value of b_o_s in ogg_packet with a flag whether or
879 * not this is a header packet. Maybe some day this could be cleaned
881 packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
882 if (!packet->b_o_s) {
883 GST_DEBUG ("found non-header packet");
884 pad->map.have_headers = TRUE;
885 if (pad->start_time == GST_CLOCK_TIME_NONE) {
886 gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
887 GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
888 if (duration != -1) {
889 pad->map.accumulated_granule += duration;
890 GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
891 pad->map.accumulated_granule);
894 if (packet->granulepos != -1) {
895 ogg_int64_t start_granule;
898 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
901 if (granule > pad->map.accumulated_granule)
902 start_granule = granule - pad->map.accumulated_granule;
906 pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
908 GST_DEBUG_OBJECT (ogg,
909 "start time %" GST_TIME_FORMAT " (%" GST_TIME_FORMAT ") for %s",
910 GST_TIME_ARGS (pad->start_time), GST_TIME_ARGS (pad->start_time),
911 gst_ogg_stream_get_media_type (&pad->map));
913 packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
914 pad->map.accumulated_granule, pad->keyframe_granule);
918 /* look for tags in header packet (before inc header count) */
919 gst_ogg_stream_extract_tags (&pad->map, packet);
920 pad->map.n_header_packets_seen++;
921 if (!pad->map.have_headers) {
923 g_list_append (pad->map.headers, _ogg_packet_copy (packet));
924 GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
928 /* we know the start_time of the pad data, see if we
929 * can activate the complete chain if this is a dynamic
930 * chain. We need all the headers too for this. */
931 if (pad->start_time != GST_CLOCK_TIME_NONE && pad->map.have_headers) {
932 GstOggChain *chain = pad->chain;
934 /* check if complete chain has start time */
935 if (chain == ogg->building_chain) {
936 GstEvent *event = NULL;
941 GST_DEBUG_OBJECT (ogg, "need to resync");
943 /* when we need to resync after a seek, we wait until we have received
944 * timestamps on all streams */
945 start_time = gst_ogg_demux_collect_start_time (ogg, chain);
947 if (start_time != G_MAXUINT64) {
951 GST_DEBUG_OBJECT (ogg, "start_time: %" GST_TIME_FORMAT,
952 GST_TIME_ARGS (start_time));
954 if (chain->segment_start < start_time)
956 (start_time - chain->segment_start) + chain->begin_time;
958 segment_time = chain->begin_time;
960 /* create the newsegment event we are going to send out */
961 gst_segment_init (&segment, GST_FORMAT_TIME);
964 if (!ogg->pullmode && ogg->push_state == PUSH_LINEAR2) {
965 /* if we are fast forwarding to the actual seek target,
966 ensure previous frames are clipped */
967 GST_DEBUG_OBJECT (ogg,
968 "Resynced, starting segment at %" GST_TIME_FORMAT
969 ", start_time %" GST_TIME_FORMAT,
970 GST_TIME_ARGS (ogg->push_seek_time_original_target),
971 GST_TIME_ARGS (start_time));
972 segment.rate = ogg->push_seek_rate;
973 segment.start = ogg->push_seek_time_original_target;
975 segment.time = ogg->push_seek_time_original_target;
976 event = gst_event_new_segment (&segment);
977 ogg->push_state = PUSH_PLAYING;
979 segment.rate = ogg->segment.rate;
980 segment.applied_rate = ogg->segment.applied_rate;
981 segment.start = start_time;
982 segment.stop = chain->segment_stop;
983 segment.time = segment_time;
984 event = gst_event_new_segment (&segment);
986 GST_PUSH_UNLOCK (ogg);
991 /* see if we have enough info to activate the chain, we have enough info
992 * when all streams have a valid start time. */
993 if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
996 GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
997 GST_TIME_ARGS (chain->segment_start));
998 GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT,
999 GST_TIME_ARGS (chain->segment_stop));
1000 GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT,
1001 GST_TIME_ARGS (chain->begin_time));
1003 /* create the newsegment event we are going to send out */
1004 gst_segment_init (&segment, GST_FORMAT_TIME);
1005 segment.rate = ogg->segment.rate;
1006 segment.applied_rate = ogg->segment.applied_rate;
1007 segment.start = chain->segment_start;
1008 segment.stop = chain->segment_stop;
1009 segment.time = chain->begin_time;
1010 event = gst_event_new_segment (&segment);
1015 gst_event_set_seqnum (event, ogg->seqnum);
1017 gst_ogg_demux_activate_chain (ogg, chain, event);
1019 ogg->building_chain = NULL;
1024 /* if we are building a chain, store buffer for when we activate
1025 * it. This path is taken if we operate in streaming mode. */
1026 if (ogg->building_chain) {
1027 /* bos packets where stored in the header list so we can discard
1030 ret = gst_ogg_demux_queue_data (pad, packet);
1032 /* else we are completely streaming to the peer */
1034 ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
1039 /* flush at most @npackets from the stream layer. All packets if
1042 static GstFlowReturn
1043 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1045 GstFlowReturn result = GST_FLOW_OK;
1046 gboolean done = FALSE;
1055 ret = ogg_stream_packetout (&pad->map.stream, &packet);
1058 GST_LOG_OBJECT (ogg, "packetout done");
1062 GST_LOG_OBJECT (ogg, "packetout discont");
1063 if (!pad->map.is_sparse) {
1064 gst_ogg_chain_mark_discont (pad->chain);
1066 gst_ogg_pad_mark_discont (pad);
1070 GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1071 result = gst_ogg_pad_submit_packet (pad, &packet);
1072 /* not linked is not a problem, it's possible that we are still
1073 * collecting headers and that we don't have exposed the pads yet */
1074 if (result == GST_FLOW_NOT_LINKED)
1076 else if (result <= GST_FLOW_EOS)
1077 goto could_not_submit;
1080 GST_WARNING_OBJECT (ogg,
1081 "invalid return value %d for ogg_stream_packetout, resetting stream",
1083 gst_ogg_pad_reset (pad);
1088 done = (npackets == 0);
1096 GST_WARNING_OBJECT (ogg,
1097 "could not submit packet for stream %08x, "
1098 "error: %d", pad->map.serialno, result);
1099 gst_ogg_pad_reset (pad);
1105 gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg)
1107 if (ogg->push_last_seek_time >= ogg->push_seek_time_target) {
1108 GST_DEBUG_OBJECT (ogg, "We overshot by %" GST_TIME_FORMAT,
1109 GST_TIME_ARGS (ogg->push_last_seek_time - ogg->push_seek_time_target));
1110 ogg->push_offset1 = ogg->push_last_seek_offset;
1111 ogg->push_time1 = ogg->push_last_seek_time;
1113 GST_DEBUG_OBJECT (ogg, "We undershot by %" GST_TIME_FORMAT,
1114 GST_TIME_ARGS (ogg->push_seek_time_target - ogg->push_last_seek_time));
1115 ogg->push_offset0 = ogg->push_last_seek_offset;
1116 ogg->push_time0 = ogg->push_last_seek_time;
1121 gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg)
1124 gint64 segment_bitrate;
1126 /* we might not know the length of the stream in time,
1127 so push_time1 might not be set */
1128 GST_DEBUG_OBJECT (ogg,
1129 "push time 1: %" GST_TIME_FORMAT ", dbytes %" G_GINT64_FORMAT,
1130 GST_TIME_ARGS (ogg->push_time1), ogg->push_offset1 - ogg->push_offset0);
1131 if (ogg->push_time1 == GST_CLOCK_TIME_NONE) {
1132 GST_DEBUG_OBJECT (ogg,
1133 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1134 ", time %" GST_TIME_FORMAT " (open ended)", ogg->push_offset0,
1135 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0));
1136 if (ogg->push_last_seek_time == ogg->push_start_time) {
1137 /* if we're at start and don't know the end time, we can't estimate
1138 bitrate, so get the nominal declared bitrate as a failsafe, or some
1139 random constant which will be discarded after we made a (probably
1140 dire) first guess */
1141 segment_bitrate = (ogg->bitrate > 0 ? ogg->bitrate : 1000);
1144 gst_util_uint64_scale (ogg->push_last_seek_offset - 0,
1145 8 * GST_SECOND, ogg->push_last_seek_time - ogg->push_start_time);
1149 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1150 segment_bitrate, 8 * GST_SECOND);
1152 GST_DEBUG_OBJECT (ogg,
1153 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1154 ", time %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, ogg->push_offset0,
1155 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
1156 GST_TIME_ARGS (ogg->push_time1));
1157 if (ogg->push_time0 == ogg->push_time1) {
1158 best = ogg->push_offset0;
1161 gst_util_uint64_scale (ogg->push_offset1 - ogg->push_offset0,
1162 8 * GST_SECOND, ogg->push_time1 - ogg->push_time0);
1163 GST_DEBUG_OBJECT (ogg,
1164 "Local bitrate on the %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1165 " segment: %" G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_time0),
1166 GST_TIME_ARGS (ogg->push_time1), segment_bitrate);
1169 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1170 segment_bitrate, 8 * GST_SECOND);
1174 /* offset by typical page size */
1176 if (best < ogg->push_offset0)
1177 best = ogg->push_offset0;
1185 gst_ogg_demux_record_keyframe_time (GstOggDemux * ogg, GstOggPad * pad,
1186 ogg_int64_t granpos)
1189 GstClockTime kf_time;
1191 kf_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, granpos);
1192 kf_time = gst_ogg_stream_granule_to_time (&pad->map, kf_granule);
1194 pad->push_kf_time = kf_time;
1197 /* returns the earliest keyframe time for all non sparse pads in the chain,
1198 * if known, and GST_CLOCK_TIME_NONE if not */
1200 gst_ogg_demux_get_earliest_keyframe_time (GstOggDemux * ogg)
1202 GstClockTime t = GST_CLOCK_TIME_NONE;
1203 GstOggChain *chain = ogg->building_chain;
1207 GST_WARNING_OBJECT (ogg, "No chain!");
1208 return GST_CLOCK_TIME_NONE;
1210 for (i = 0; i < chain->streams->len; i++) {
1211 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1213 if (pad->map.is_sparse)
1215 if (pad->push_kf_time == GST_CLOCK_TIME_NONE)
1216 return GST_CLOCK_TIME_NONE;
1217 if (t == GST_CLOCK_TIME_NONE || pad->push_kf_time < t)
1218 t = pad->push_kf_time;
1224 /* MUST be called with the push lock locked, and will unlock it
1225 regardless of return value. */
1226 static GstFlowReturn
1227 gst_ogg_demux_seek_back_after_push_duration_check_unlock (GstOggDemux * ogg)
1231 /* Get the delayed event, if any */
1232 event = ogg->push_mode_seek_delayed_event;
1233 ogg->push_mode_seek_delayed_event = NULL;
1235 ogg->push_state = PUSH_PLAYING;
1237 GST_PUSH_UNLOCK (ogg);
1240 /* If there is one, perform it */
1241 gst_ogg_demux_perform_seek_push (ogg, event);
1243 /* If there wasn't, seek back at start to start normal playback */
1244 GST_INFO_OBJECT (ogg, "Seeking back to 0 after duration check");
1245 event = gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1246 GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
1247 GST_SEEK_TYPE_SET, 1, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
1248 if (!gst_pad_push_event (ogg->sinkpad, event)) {
1249 GST_WARNING_OBJECT (ogg, "Failed seeking back to start");
1250 return GST_FLOW_ERROR;
1258 gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
1260 GstOggDemux *ogg = pad->ogg;
1261 ogg_int64_t granpos = ogg_page_granulepos (page);
1263 GST_PUSH_LOCK (ogg);
1265 if (ogg->push_start_time == GST_CLOCK_TIME_NONE) {
1266 ogg->push_start_time =
1267 gst_ogg_stream_get_start_time_for_granulepos (&pad->map, granpos);
1268 GST_DEBUG_OBJECT (ogg, "Stream start time: %" GST_TIME_FORMAT,
1269 GST_TIME_ARGS (ogg->push_start_time));
1271 ogg->push_time_offset =
1272 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1273 if (ogg->push_time_offset > 0) {
1274 GST_DEBUG_OBJECT (ogg, "Bitrate since start: %" G_GUINT64_FORMAT,
1275 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
1276 ogg->push_time_offset));
1279 if (ogg->push_state == PUSH_DURATION) {
1281 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1283 if (ogg->total_time == GST_CLOCK_TIME_NONE || t > ogg->total_time) {
1284 GST_DEBUG_OBJECT (ogg, "New total time: %" GST_TIME_FORMAT,
1286 ogg->total_time = t;
1287 ogg->push_time_length = t;
1290 /* If we were determining the duration of the stream, we're now done,
1291 and can get back to sending the original event we delayed.
1292 We stop a bit before the end of the stream, as if we get a EOS
1293 event and there is a queue2 upstream (such as when using playbin2),
1294 it will pause the task *after* we come back from the EOS handler,
1295 so we cannot prevent the pausing by issuing a seek. */
1296 if (ogg->push_byte_offset + EOS_AVOIDANCE_THRESHOLD >=
1297 ogg->push_byte_length) {
1298 GstMessage *message;
1301 /* tell the pipeline we've just found out the duration */
1302 ogg->push_time_length = ogg->total_time;
1303 GST_INFO_OBJECT (ogg, "New duration found: %" GST_TIME_FORMAT,
1304 GST_TIME_ARGS (ogg->total_time));
1306 gst_message_new_duration (GST_OBJECT (ogg), GST_FORMAT_TIME,
1308 gst_element_post_message (GST_ELEMENT (ogg), message);
1310 GST_DEBUG_OBJECT (ogg,
1311 "We're close enough to the end, and we're scared "
1312 "to get too close, seeking back to start");
1314 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1315 if (res != GST_FLOW_OK)
1317 return GST_FLOW_SKIP_PUSH;
1319 GST_PUSH_UNLOCK (ogg);
1321 return GST_FLOW_SKIP_PUSH;
1325 /* if we're seeking, look at time, and decide what to do */
1326 if (ogg->push_state != PUSH_PLAYING && ogg->push_state != PUSH_LINEAR2) {
1331 gboolean close_enough;
1333 /* ignore -1 granpos when seeking, we want to sync on a real granpos */
1335 GST_PUSH_UNLOCK (ogg);
1336 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1338 return GST_FLOW_SKIP_PUSH;
1341 t = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1343 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1344 GstClockTime sync_time;
1346 if (pad->push_sync_time == GST_CLOCK_TIME_NONE)
1347 pad->push_sync_time = t;
1348 GST_DEBUG_OBJECT (ogg, "Got timestamp %" GST_TIME_FORMAT " for %s",
1349 GST_TIME_ARGS (t), gst_ogg_stream_get_media_type (&pad->map));
1350 sync_time = gst_ogg_demux_collect_sync_time (ogg, ogg->building_chain);
1351 if (sync_time == GST_CLOCK_TIME_NONE) {
1352 GST_PUSH_UNLOCK (ogg);
1353 GST_DEBUG_OBJECT (ogg,
1354 "Not enough timing info collected for sync, waiting for more");
1355 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1357 return GST_FLOW_SKIP_PUSH;
1359 ogg->push_last_seek_time = sync_time;
1361 GST_DEBUG_OBJECT (ogg,
1362 "Bisection just seeked at %" G_GINT64_FORMAT ", time %"
1363 GST_TIME_FORMAT ", target was %" GST_TIME_FORMAT,
1364 ogg->push_last_seek_offset,
1365 GST_TIME_ARGS (ogg->push_last_seek_time),
1366 GST_TIME_ARGS (ogg->push_seek_time_target));
1368 if (ogg->push_time1 != GST_CLOCK_TIME_NONE) {
1369 GST_DEBUG_OBJECT (ogg,
1370 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1371 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1372 " (%" GST_TIME_FORMAT ")", ogg->push_offset0, ogg->push_offset1,
1373 ogg->push_offset1 - ogg->push_offset0,
1374 GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1),
1375 GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0));
1377 GST_DEBUG_OBJECT (ogg,
1378 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1379 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - unknown",
1380 ogg->push_offset0, ogg->push_offset1,
1381 ogg->push_offset1 - ogg->push_offset0,
1382 GST_TIME_ARGS (ogg->push_time0));
1385 gst_ogg_demux_setup_bisection_bounds (ogg);
1387 best = gst_ogg_demux_estimate_bisection_target (ogg);
1389 if (ogg->push_seek_time_target == 0) {
1390 GST_DEBUG_OBJECT (ogg, "Seeking to 0, deemed close enough");
1391 close_enough = (ogg->push_last_seek_time == 0);
1393 /* TODO: make this dependent on framerate ? */
1394 GstClockTime threshold = GST_SECOND / 2;
1396 /* We want to be within half a second before the target */
1397 if (threshold > ogg->push_seek_time_target)
1398 threshold = ogg->push_seek_time_target;
1399 close_enough = ogg->push_last_seek_time < ogg->push_seek_time_target
1400 && ogg->push_last_seek_time >=
1401 ogg->push_seek_time_target - threshold;
1402 GST_DEBUG_OBJECT (ogg,
1403 "testing if we're close enough: %" GST_TIME_FORMAT " <= %"
1404 GST_TIME_FORMAT " < %" GST_TIME_FORMAT " ? %s",
1405 GST_TIME_ARGS (ogg->push_seek_time_target - threshold),
1406 GST_TIME_ARGS (ogg->push_last_seek_time),
1407 GST_TIME_ARGS (ogg->push_seek_time_target),
1408 close_enough ? "Yes" : "No");
1411 if (close_enough || best == ogg->push_last_seek_offset) {
1412 if (ogg->push_state == PUSH_BISECT1) {
1413 /* we now know the time segment we'll have to search for
1414 the second bisection */
1415 ogg->push_time0 = ogg->push_start_time;
1416 ogg->push_offset0 = 0;
1418 GST_DEBUG_OBJECT (ogg,
1419 "Seek to %" GST_TIME_FORMAT
1420 " (%lx) done, now gathering pages for all non-sparse streams",
1421 GST_TIME_ARGS (ogg->push_seek_time_target), (long) granpos);
1422 ogg->push_state = PUSH_LINEAR1;
1424 /* If we're asked for an accurate seek, we'll go forward till
1425 we get to the original seek target time, else we'll just drop
1426 here at the keyframe */
1427 if (ogg->push_seek_flags & GST_SEEK_FLAG_ACCURATE) {
1428 GST_INFO_OBJECT (ogg,
1429 "Seek to keyframe at %" GST_TIME_FORMAT " done (we're at %"
1430 GST_TIME_FORMAT "), skipping to original target (%"
1431 GST_TIME_FORMAT ")",
1432 GST_TIME_ARGS (ogg->push_seek_time_target),
1433 GST_TIME_ARGS (sync_time),
1434 GST_TIME_ARGS (ogg->push_seek_time_original_target));
1435 ogg->push_state = PUSH_LINEAR2;
1437 GST_DEBUG_OBJECT (ogg, "Seek to keyframe done, playing");
1439 /* we're synced to the seek target, so flush stream and stuff
1440 any queued pages into the stream so we start decoding there */
1441 ogg->push_state = PUSH_PLAYING;
1443 GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps",
1444 ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]);
1447 } else if (ogg->push_state == PUSH_LINEAR1) {
1448 if (pad->push_kf_time == GST_CLOCK_TIME_NONE) {
1449 GstClockTime earliest_keyframe_time;
1451 gst_ogg_demux_record_keyframe_time (ogg, pad, granpos);
1452 GST_DEBUG_OBJECT (ogg,
1453 "Previous keyframe for %s stream at %" GST_TIME_FORMAT,
1454 gst_ogg_stream_get_media_type (&pad->map),
1455 GST_TIME_ARGS (pad->push_kf_time));
1456 earliest_keyframe_time = gst_ogg_demux_get_earliest_keyframe_time (ogg);
1457 if (earliest_keyframe_time != GST_CLOCK_TIME_NONE) {
1458 GST_DEBUG_OBJECT (ogg,
1459 "All non sparse streams now have a previous keyframe time,"
1460 "bisecting again to %" GST_TIME_FORMAT,
1461 GST_TIME_ARGS (earliest_keyframe_time));
1462 ogg->push_seek_time_target = earliest_keyframe_time;
1464 ogg->push_state = PUSH_BISECT2;
1465 best = gst_ogg_demux_estimate_bisection_target (ogg);
1470 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1473 ogg_sync_reset (&ogg->sync);
1474 for (i = 0; i < ogg->building_chain->streams->len; i++) {
1476 g_array_index (ogg->building_chain->streams, GstOggPad *, i);
1478 pad->push_sync_time = GST_CLOCK_TIME_NONE;
1479 ogg_stream_reset (&pad->map.stream);
1482 GST_DEBUG_OBJECT (ogg,
1483 "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, best,
1486 g_assert (best != -1);
1487 ogg->push_bisection_steps[ogg->push_state == PUSH_BISECT2 ? 1 : 0]++;
1489 gst_event_new_seek (ogg->push_seek_rate, GST_FORMAT_BYTES,
1490 ogg->push_seek_flags, GST_SEEK_TYPE_SET, best,
1491 GST_SEEK_TYPE_NONE, -1);
1493 GST_PUSH_UNLOCK (ogg);
1494 res = gst_pad_push_event (ogg->sinkpad, sevent);
1496 /* We failed to send the seek event, notify the pipeline */
1497 GST_ELEMENT_ERROR (ogg, RESOURCE, SEEK, (NULL), ("Failed to seek"));
1498 return GST_FLOW_ERROR;
1500 return GST_FLOW_SKIP_PUSH;
1503 if (ogg->push_state != PUSH_PLAYING) {
1504 GST_PUSH_UNLOCK (ogg);
1505 return GST_FLOW_SKIP_PUSH;
1508 GST_PUSH_UNLOCK (ogg);
1514 GST_WARNING_OBJECT (ogg,
1515 "ogg stream choked on page (serial %08x), "
1516 "resetting stream", pad->map.serialno);
1517 gst_ogg_pad_reset (pad);
1518 /* we continue to recover */
1519 return GST_FLOW_SKIP_PUSH;
1523 /* submit a page to an oggpad, this function will then submit all
1524 * the packets in the page.
1526 static GstFlowReturn
1527 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
1529 GstFlowReturn result = GST_FLOW_OK;
1531 gboolean continued = FALSE;
1535 /* for negative rates we read pages backwards and must therefore be careful
1536 * with continued pages */
1537 if (ogg->segment.rate < 0.0) {
1540 continued = ogg_page_continued (page);
1542 /* number of completed packets in the page */
1543 npackets = ogg_page_packets (page);
1545 /* page is not continued so it contains at least one packet start. It's
1546 * possible that no packet ends on this page (npackets == 0). In that
1547 * case, the next (continued) page(s) we kept contain the remainder of the
1548 * packets. We mark npackets=1 to make us start decoding the pages in the
1549 * remainder of the algorithm. */
1553 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
1555 if (npackets == 0) {
1556 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
1561 /* keep track of time in push mode */
1562 if (!ogg->pullmode) {
1563 result = gst_ogg_pad_handle_push_mode_state (pad, page);
1564 if (result == GST_FLOW_SKIP_PUSH)
1566 if (result != GST_FLOW_OK)
1570 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1573 /* flush all packets in the stream layer, this might not give a packet if
1574 * the page had no packets finishing on the page (npackets == 0). */
1575 result = gst_ogg_pad_stream_out (pad, 0);
1577 if (pad->continued) {
1580 /* now send the continued pages to the stream layer */
1581 while (pad->continued) {
1582 ogg_page *p = (ogg_page *) pad->continued->data;
1584 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
1585 if (ogg_stream_pagein (&pad->map.stream, p) != 0)
1588 pad->continued = g_list_delete_link (pad->continued, pad->continued);
1591 gst_ogg_page_free (p);
1594 GST_LOG_OBJECT (ogg, "flushing last continued packet");
1595 /* flush 1 continued packet in the stream layer */
1596 result = gst_ogg_pad_stream_out (pad, 1);
1598 /* flush all remaining packets, we pushed them in the previous round.
1599 * We don't use _reset() because we still want to get the discont when
1600 * we submit a next page. */
1601 while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
1605 /* keep continued pages (only in reverse mode) */
1607 ogg_page *p = gst_ogg_page_copy (page);
1609 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
1610 pad->continued = g_list_prepend (pad->continued, p);
1617 GST_WARNING_OBJECT (ogg,
1618 "ogg stream choked on page (serial %08x), "
1619 "resetting stream", pad->map.serialno);
1620 gst_ogg_pad_reset (pad);
1621 /* we continue to recover */
1627 static GstOggChain *
1628 gst_ogg_chain_new (GstOggDemux * ogg)
1630 GstOggChain *chain = g_slice_new0 (GstOggChain);
1632 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
1636 chain->have_bos = FALSE;
1637 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
1638 chain->begin_time = GST_CLOCK_TIME_NONE;
1639 chain->segment_start = GST_CLOCK_TIME_NONE;
1640 chain->segment_stop = GST_CLOCK_TIME_NONE;
1641 chain->total_time = GST_CLOCK_TIME_NONE;
1647 gst_ogg_chain_free (GstOggChain * chain)
1651 for (i = 0; i < chain->streams->len; i++) {
1652 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1654 gst_object_unref (pad);
1656 g_array_free (chain->streams, TRUE);
1657 g_slice_free (GstOggChain, chain);
1661 gst_ogg_pad_mark_discont (GstOggPad * pad)
1663 pad->discont = TRUE;
1664 pad->map.last_size = 0;
1668 gst_ogg_chain_mark_discont (GstOggChain * chain)
1672 for (i = 0; i < chain->streams->len; i++) {
1673 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1675 gst_ogg_pad_mark_discont (pad);
1680 gst_ogg_chain_reset (GstOggChain * chain)
1684 for (i = 0; i < chain->streams->len; i++) {
1685 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1687 gst_ogg_pad_reset (pad);
1692 gst_ogg_chain_new_stream (GstOggChain * chain, guint32 serialno)
1698 GST_DEBUG_OBJECT (chain->ogg,
1699 "creating new stream %08x in chain %p", serialno, chain);
1701 name = g_strdup_printf ("src_%08x", serialno);
1702 ret = g_object_new (GST_TYPE_OGG_PAD, "name", name, NULL);
1704 /* we own this one */
1705 gst_object_ref_sink (ret);
1707 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1708 gst_ogg_pad_mark_discont (ret);
1711 ret->ogg = chain->ogg;
1713 ret->map.serialno = serialno;
1714 if (ogg_stream_init (&ret->map.stream, serialno) != 0)
1717 /* FIXME: either do something with it or remove it */
1718 list = gst_tag_list_new_empty ();
1719 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1721 gst_tag_list_free (list);
1723 GST_DEBUG_OBJECT (chain->ogg,
1724 "created new ogg src %p for stream with serial %08x", ret, serialno);
1726 g_array_append_val (chain->streams, ret);
1727 gst_pad_set_active (GST_PAD_CAST (ret), TRUE);
1734 GST_ERROR ("Could not initialize ogg_stream struct for serial %08x",
1736 gst_object_unref (ret);
1742 gst_ogg_chain_get_stream (GstOggChain * chain, guint32 serialno)
1746 for (i = 0; i < chain->streams->len; i++) {
1747 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1749 if (pad->map.serialno == serialno)
1756 gst_ogg_chain_has_stream (GstOggChain * chain, guint32 serialno)
1758 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1761 /* signals and args */
1774 static GstStaticPadTemplate ogg_demux_src_template_factory =
1775 GST_STATIC_PAD_TEMPLATE ("src_%08x",
1778 GST_STATIC_CAPS_ANY);
1780 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1781 GST_STATIC_PAD_TEMPLATE ("sink",
1784 GST_STATIC_CAPS ("application/ogg; application/x-annodex")
1787 static void gst_ogg_demux_finalize (GObject * object);
1789 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1790 GstOggChain ** chain);
1791 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1792 GstOggChain * chain);
1794 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
1795 static void gst_ogg_demux_loop (GstOggPad * pad);
1796 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
1797 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
1798 static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
1800 static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
1802 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1803 GstStateChange transition);
1805 static void gst_ogg_print (GstOggDemux * demux);
1807 #define gst_ogg_demux_parent_class parent_class
1808 G_DEFINE_TYPE (GstOggDemux, gst_ogg_demux, GST_TYPE_ELEMENT);
1811 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1813 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1814 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1816 gst_element_class_set_details_simple (gstelement_class,
1817 "Ogg demuxer", "Codec/Demuxer",
1818 "demux ogg streams (info about ogg: http://xiph.org)",
1819 "Wim Taymans <wim@fluendo.com>");
1821 gst_element_class_add_pad_template (gstelement_class,
1822 gst_static_pad_template_get (&ogg_demux_sink_template_factory));
1823 gst_element_class_add_pad_template (gstelement_class,
1824 gst_static_pad_template_get (&ogg_demux_src_template_factory));
1826 gstelement_class->change_state = gst_ogg_demux_change_state;
1827 gstelement_class->send_event = gst_ogg_demux_receive_event;
1829 gobject_class->finalize = gst_ogg_demux_finalize;
1833 gst_ogg_demux_init (GstOggDemux * ogg)
1835 /* create the sink pad */
1837 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
1840 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
1841 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
1842 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
1843 gst_pad_set_activatepull_function (ogg->sinkpad,
1844 gst_ogg_demux_sink_activate_pull);
1845 gst_pad_set_activatepush_function (ogg->sinkpad,
1846 gst_ogg_demux_sink_activate_push);
1847 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
1849 ogg->chain_lock = g_mutex_new ();
1850 ogg->push_lock = g_mutex_new ();
1851 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
1853 ogg->newsegment = NULL;
1857 gst_ogg_demux_finalize (GObject * object)
1861 ogg = GST_OGG_DEMUX (object);
1863 g_array_free (ogg->chains, TRUE);
1864 g_mutex_free (ogg->chain_lock);
1865 g_mutex_free (ogg->push_lock);
1866 ogg_sync_clear (&ogg->sync);
1868 if (ogg->newsegment)
1869 gst_event_unref (ogg->newsegment);
1871 G_OBJECT_CLASS (parent_class)->finalize (object);
1875 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
1880 chain = ogg->current_chain;
1884 for (i = 0; i < chain->streams->len; i++) {
1885 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
1887 stream->start_time = -1;
1888 stream->map.accumulated_granule = 0;
1890 ogg->building_chain = chain;
1891 GST_DEBUG_OBJECT (ogg, "Resetting current chain");
1892 ogg->current_chain = NULL;
1897 gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
1902 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
1904 switch (GST_EVENT_TYPE (event)) {
1905 case GST_EVENT_FLUSH_START:
1906 res = gst_ogg_demux_send_event (ogg, event);
1908 case GST_EVENT_FLUSH_STOP:
1909 GST_DEBUG_OBJECT (ogg, "got a flush stop event");
1910 ogg_sync_reset (&ogg->sync);
1911 res = gst_ogg_demux_send_event (ogg, event);
1912 if (ogg->pullmode || ogg->push_state != PUSH_DURATION) {
1913 /* it's starting to feel reaaaally dirty :(
1914 if we're on a spliced seek to get duration, don't reset streams,
1915 we'll need them for the delayed seek */
1916 gst_ogg_demux_reset_streams (ogg);
1919 case GST_EVENT_SEGMENT:
1920 GST_DEBUG_OBJECT (ogg, "got a new segment event");
1924 gst_event_copy_segment (event, &segment);
1926 if (segment.format == GST_FORMAT_BYTES) {
1927 GST_PUSH_LOCK (ogg);
1928 ogg->push_byte_offset = segment.start;
1929 ogg->push_last_seek_offset = segment.start;
1930 GST_PUSH_UNLOCK (ogg);
1932 GST_WARNING_OBJECT (ogg, "unexpected segment format: %s",
1933 gst_format_get_name (segment.format));
1936 gst_event_unref (event);
1941 GST_DEBUG_OBJECT (ogg, "got an EOS event");
1943 /* This would be what is needed (recover from EOS by going on to
1944 the next step (issue the delayed seek)), but it does not work
1945 if there is a queue2 upstream - see more details comment in
1946 gst_ogg_pad_submit_page.
1947 If I could find a way to bypass queue2 behavior, this should
1949 GST_PUSH_LOCK (ogg);
1950 if (ogg->push_state == PUSH_DURATION) {
1951 GST_DEBUG_OBJECT (ogg, "Got EOS while determining length");
1952 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1953 if (res != GST_FLOW_OK) {
1954 GST_DEBUG_OBJECT (ogg, "Error seeking back after duration check: %d",
1959 GST_PUSH_UNLOCK (ogg);
1961 res = gst_ogg_demux_send_event (ogg, event);
1962 if (ogg->current_chain == NULL) {
1963 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
1964 ("can't get first chain"));
1969 res = gst_ogg_demux_send_event (ogg, event);
1972 gst_object_unref (ogg);
1977 /* submit the given buffer to the ogg sync */
1978 static GstFlowReturn
1979 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
1983 GstFlowReturn ret = GST_FLOW_OK;
1985 size = gst_buffer_get_size (buffer);
1986 GST_DEBUG_OBJECT (ogg, "submitting %" G_GSIZE_FORMAT " bytes", size);
1987 if (G_UNLIKELY (size == 0))
1990 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
1991 if (G_UNLIKELY (oggbuffer == NULL))
1994 gst_buffer_extract (buffer, 0, oggbuffer, size);
1996 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
1999 if (!ogg->pullmode) {
2000 GST_PUSH_LOCK (ogg);
2001 ogg->push_byte_offset += size;
2002 GST_PUSH_UNLOCK (ogg);
2006 gst_buffer_unref (buffer);
2013 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2014 (NULL), ("failed to get ogg sync buffer"));
2015 ret = GST_FLOW_ERROR;
2020 GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2021 ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2022 ret = GST_FLOW_ERROR;
2027 /* in random access mode this code updates the current read position
2028 * and resets the ogg sync buffer so that the next read will happen
2029 * from this new location.
2032 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
2034 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
2036 ogg->offset = offset;
2037 ogg->read_offset = offset;
2038 ogg_sync_reset (&ogg->sync);
2041 /* read more data from the current offset and submit to
2042 * the ogg sync layer.
2044 static GstFlowReturn
2045 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
2050 GST_LOG_OBJECT (ogg,
2051 "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
2052 ogg->read_offset, ogg->length, end_offset);
2054 if (end_offset > 0 && ogg->read_offset >= end_offset)
2055 goto boundary_reached;
2057 if (ogg->read_offset == ogg->length)
2060 ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
2061 if (ret != GST_FLOW_OK)
2064 ogg->read_offset += gst_buffer_get_size (buffer);
2066 ret = gst_ogg_demux_submit_buffer (ogg, buffer);
2073 GST_LOG_OBJECT (ogg, "reached boundary");
2074 return GST_FLOW_LIMIT;
2078 GST_LOG_OBJECT (ogg, "reached EOS");
2079 return GST_FLOW_EOS;
2083 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
2084 gst_flow_get_name (ret));
2089 /* Read the next page from the current offset.
2090 * boundary: number of bytes ahead we allow looking for;
2093 * @offset will contain the offset the next page starts at when this function
2094 * returns GST_FLOW_OK.
2096 * GST_FLOW_EOS is returned on EOS.
2098 * GST_FLOW_LIMIT is returned when we did not find a page before the
2099 * boundary. If @boundary is -1, this is never returned.
2101 * Any other error returned while retrieving data from the peer is returned as
2104 static GstFlowReturn
2105 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
2106 gint64 boundary, gint64 * offset)
2108 gint64 end_offset = -1;
2111 GST_LOG_OBJECT (ogg,
2112 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
2113 G_GINT64_FORMAT, ogg->offset, boundary);
2116 end_offset = ogg->offset + boundary;
2121 if (end_offset > 0 && ogg->offset >= end_offset)
2122 goto boundary_reached;
2124 more = ogg_sync_pageseek (&ogg->sync, og);
2126 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
2129 /* skipped n bytes */
2130 ogg->offset -= more;
2131 GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
2133 } else if (more == 0) {
2134 /* we need more data */
2136 goto boundary_reached;
2138 GST_LOG_OBJECT (ogg, "need more data");
2139 ret = gst_ogg_demux_get_data (ogg, end_offset);
2140 if (ret != GST_FLOW_OK)
2143 gint64 res_offset = ogg->offset;
2145 /* got a page. Return the offset at the page beginning,
2146 advance the internal offset past the page end */
2148 *offset = res_offset;
2151 ogg->offset += more;
2153 GST_LOG_OBJECT (ogg,
2154 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
2155 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
2156 ogg_page_serialno (og), ogg->offset,
2157 (gint64) ogg_page_granulepos (og));
2161 GST_LOG_OBJECT (ogg, "returning %d", ret);
2168 GST_LOG_OBJECT (ogg,
2169 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
2170 ogg->offset, end_offset);
2171 return GST_FLOW_LIMIT;
2175 /* from the current offset, find the previous page, seeking backwards
2176 * until we find the page.
2178 static GstFlowReturn
2179 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
2182 gint64 begin = ogg->offset;
2184 gint64 cur_offset = -1;
2186 GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
2188 while (cur_offset == -1) {
2193 /* seek CHUNKSIZE back */
2194 gst_ogg_demux_seek (ogg, begin);
2196 /* now continue reading until we run out of data, if we find a page
2197 * start, we save it. It might not be the final page as there could be
2198 * another page after this one. */
2199 while (ogg->offset < end) {
2203 gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
2204 /* we hit the upper limit, offset contains the last page start */
2205 if (ret == GST_FLOW_LIMIT) {
2206 GST_LOG_OBJECT (ogg, "hit limit");
2209 /* something went wrong */
2210 if (ret == GST_FLOW_EOS) {
2212 GST_LOG_OBJECT (ogg, "got unexpected");
2213 } else if (ret != GST_FLOW_OK) {
2214 GST_LOG_OBJECT (ogg, "got error %d", ret);
2218 GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
2220 /* offset is next page start */
2221 cur_offset = new_offset;
2225 GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
2227 /* we have the offset. Actually snork and hold the page now */
2228 gst_ogg_demux_seek (ogg, cur_offset);
2229 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
2230 if (ret != GST_FLOW_OK) {
2231 GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
2233 /* this shouldn't be possible */
2238 *offset = cur_offset;
2244 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
2247 GstOggChain *chain = ogg->current_chain;
2252 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
2254 /* send EOS on all the pads */
2255 for (i = 0; i < chain->streams->len; i++) {
2256 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2262 event = gst_event_new_eos ();
2263 gst_event_set_seqnum (event, ogg->seqnum);
2264 gst_pad_push_event (GST_PAD_CAST (pad), event);
2266 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
2268 /* deactivate first */
2269 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
2271 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2275 /* With push mode seeking implemented, we can now seek back to the chain,
2276 so we do not destroy it */
2277 GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2278 ogg->current_chain = NULL;
2284 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
2287 GstStructure *structure;
2288 GValue array = { 0 };
2290 GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
2292 if (G_UNLIKELY (!caps))
2294 if (G_UNLIKELY (!headers))
2297 caps = gst_caps_make_writable (caps);
2298 structure = gst_caps_get_structure (caps, 0);
2300 g_value_init (&array, GST_TYPE_ARRAY);
2303 GValue value = { 0 };
2305 ogg_packet *op = headers->data;
2307 buffer = gst_buffer_new_and_alloc (op->bytes);
2308 gst_buffer_fill (buffer, 0, op->packet, op->bytes);
2309 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS);
2310 g_value_init (&value, GST_TYPE_BUFFER);
2311 gst_value_take_buffer (&value, buffer);
2312 gst_value_array_append_value (&array, &value);
2313 g_value_unset (&value);
2314 headers = headers->next;
2317 gst_structure_set_value (structure, "streamheader", &array);
2318 g_value_unset (&array);
2319 GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
2325 gst_ogg_demux_push_queued_buffers (GstOggDemux * ogg, GstOggPad * pad)
2329 /* push queued packets */
2330 for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
2331 ogg_packet *p = walk->data;
2333 gst_ogg_demux_chain_peer (pad, p, TRUE);
2334 _ogg_packet_free (p);
2336 /* and free the queued buffers */
2337 g_list_free (pad->map.queued);
2338 pad->map.queued = NULL;
2342 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
2346 gint bitrate, idx_bitrate;
2348 g_return_val_if_fail (chain != NULL, FALSE);
2350 if (chain == ogg->current_chain) {
2352 gst_event_unref (event);
2354 for (i = 0; i < chain->streams->len; i++) {
2355 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2356 gst_ogg_demux_push_queued_buffers (ogg, pad);
2362 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
2364 bitrate = idx_bitrate = 0;
2366 /* first add the pads */
2367 for (i = 0; i < chain->streams->len; i++) {
2370 pad = g_array_index (chain->streams, GstOggPad *, i);
2372 if (pad->map.idx_bitrate)
2373 idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
2375 bitrate += pad->map.bitrate;
2378 gst_ogg_pad_mark_discont (pad);
2379 pad->last_ret = GST_FLOW_OK;
2381 if (pad->map.is_skeleton || pad->added
2382 || !gst_pad_has_current_caps (GST_PAD_CAST (pad)))
2385 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
2387 /* activate first */
2388 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
2390 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2393 /* prefer the index bitrate over the ones encoded in the streams */
2394 ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
2396 /* after adding the new pads, remove the old pads */
2397 gst_ogg_demux_deactivate_current_chain (ogg);
2399 GST_DEBUG_OBJECT (ogg, "Setting current chain to %p", chain);
2400 ogg->current_chain = chain;
2402 /* we are finished now */
2403 gst_element_no_more_pads (GST_ELEMENT (ogg));
2405 /* FIXME, must be sent from the streaming thread */
2409 gst_ogg_demux_send_event (ogg, event);
2411 tags = gst_tag_list_new (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL);
2412 gst_ogg_demux_send_event (ogg, gst_event_new_tag (tags));
2415 GST_DEBUG_OBJECT (ogg, "starting chain");
2417 /* then send out any headers and queued packets */
2418 for (i = 0; i < chain->streams->len; i++) {
2422 pad = g_array_index (chain->streams, GstOggPad *, i);
2424 /* FIXME also streaming thread */
2425 if (pad->map.taglist) {
2426 GST_DEBUG_OBJECT (ogg, "pushing tags");
2427 gst_pad_push_event (GST_PAD_CAST (pad),
2428 gst_event_new_tag (pad->map.taglist));
2429 pad->map.taglist = NULL;
2432 /* Set headers on caps */
2434 gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers);
2435 gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps);
2437 GST_DEBUG_OBJECT (ogg, "pushing headers");
2439 for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
2440 ogg_packet *p = walk->data;
2442 gst_ogg_demux_chain_peer (pad, p, TRUE);
2445 GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
2446 gst_ogg_demux_push_queued_buffers (ogg, pad);
2452 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2453 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2462 GST_DEBUG_OBJECT (ogg,
2463 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
2465 GST_DEBUG_OBJECT (ogg,
2466 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
2467 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
2468 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
2470 /* perform the seek */
2471 while (begin < end) {
2474 if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
2477 /* take a (pretty decent) guess, avoiding overflow */
2478 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
2480 bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
2482 if (bisect <= begin)
2484 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
2486 gst_ogg_demux_seek (ogg, bisect);
2488 while (begin < end) {
2491 GST_DEBUG_OBJECT (ogg,
2492 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
2493 ", end %" G_GINT64_FORMAT, bisect, begin, end);
2495 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2496 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2499 if (ret == GST_FLOW_LIMIT) {
2500 /* we hit the upper limit, go back a bit */
2501 if (bisect <= begin + 1) {
2502 end = begin; /* found it */
2507 bisect -= CHUNKSIZE;
2508 if (bisect <= begin)
2511 gst_ogg_demux_seek (ogg, bisect);
2513 } else if (ret == GST_FLOW_OK) {
2514 /* found offset of next ogg page */
2516 GstClockTime granuletime;
2519 /* get the granulepos */
2520 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
2522 granulepos = ogg_page_granulepos (&og);
2523 if (granulepos == -1) {
2524 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2528 /* get the stream */
2529 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2530 if (pad == NULL || pad->map.is_skeleton)
2533 /* convert granulepos to time */
2534 granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2536 if (granuletime < pad->start_time)
2539 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
2540 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2542 granuletime -= pad->start_time;
2543 granuletime += chain->begin_time;
2545 GST_DEBUG_OBJECT (ogg,
2546 "found page with granule %" G_GINT64_FORMAT " and time %"
2547 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2549 if (granuletime < target) {
2550 best = result; /* raw offset of packet with granulepos */
2551 begin = ogg->offset; /* raw offset of next page */
2552 begintime = granuletime;
2554 bisect = begin; /* *not* begin + 1 */
2556 if (bisect <= begin + 1) {
2557 end = begin; /* found it */
2559 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
2561 bisect -= CHUNKSIZE; /* an endless loop otherwise. */
2562 if (bisect <= begin)
2564 gst_ogg_demux_seek (ogg, bisect);
2567 endtime = granuletime;
2576 GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
2577 gst_ogg_demux_seek (ogg, best);
2585 GST_DEBUG_OBJECT (ogg, "got a seek error");
2591 do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2592 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2593 gint64 * p_offset, gint64 * p_timestamp)
2596 guint64 timestamp, offset;
2597 guint64 r_timestamp, r_offset;
2598 gboolean result = FALSE;
2600 target -= begintime;
2605 for (i = 0; i < chain->streams->len; i++) {
2606 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2609 if (gst_ogg_map_search_index (&pad->map, TRUE, ×tamp, &offset)) {
2610 GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
2613 if (r_offset == -1 || offset < r_offset) {
2615 r_timestamp = timestamp;
2622 *p_timestamp = r_timestamp;
2624 *p_offset = r_offset;
2630 * do seek to time @position, return FALSE or chain and TRUE
2633 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
2634 gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
2637 GstOggChain *chain = NULL;
2639 gint64 begintime, endtime;
2640 gint64 target, keytarget;
2645 gint i, pending, len;
2646 gboolean first_parsed_page = TRUE;
2648 position = segment->position;
2650 /* first find the chain to search in */
2651 total = ogg->total_time;
2652 if (ogg->chains->len == 0)
2655 for (i = ogg->chains->len - 1; i >= 0; i--) {
2656 chain = g_array_index (ogg->chains, GstOggChain *, i);
2657 total -= chain->total_time;
2658 if (position >= total)
2662 /* first step, locate page containing the required data */
2663 begin = chain->offset;
2664 end = chain->end_offset;
2665 begintime = chain->begin_time;
2666 endtime = begintime + chain->total_time;
2667 target = position - total + begintime;
2669 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
2673 /* second step: find pages for all streams, we use the keyframe_granule to keep
2674 * track of which ones we saw. If we have seen a page for each stream we can
2675 * calculate the positions of each keyframe. */
2676 GST_DEBUG_OBJECT (ogg, "find keyframes");
2677 len = pending = chain->streams->len;
2679 /* figure out where the keyframes are */
2686 GstClockTime keyframe_time, granule_time;
2688 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2689 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2691 if (ret == GST_FLOW_LIMIT) {
2692 GST_LOG_OBJECT (ogg, "reached limit");
2694 } else if (ret != GST_FLOW_OK)
2697 /* get the stream */
2698 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2702 if (pad->map.is_skeleton)
2705 granulepos = ogg_page_granulepos (&og);
2706 if (granulepos == -1 || granulepos == 0) {
2707 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2711 /* we only do this the first time we pass here */
2712 if (first_parsed_page) {
2713 /* Now that we have a time reference from the page, we can check
2714 * whether all streams still have pages from here on.
2716 * This would be more elegant before the loop, but getting the page from
2717 * there without breaking anything would be more costly */
2718 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2720 for (i = 0; i < len; i++) {
2721 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2724 /* we already know we have at least one page (the current one)
2725 * for this stream */
2728 if (granule_time > stream->map.total_time)
2729 /* we won't encounter any more pages of this stream, so we don't
2730 * try finding a key frame for it */
2733 first_parsed_page = FALSE;
2737 /* in reverse we want to go past the page with the lower timestamp */
2738 if (segment->rate < 0.0) {
2739 /* get time for this pad */
2740 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2743 GST_LOG_OBJECT (ogg,
2744 "looking at page with ts %" GST_TIME_FORMAT ", target %"
2745 GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
2746 GST_TIME_ARGS (target));
2747 if (granule_time < target)
2751 /* we've seen this pad before */
2752 if (pad->keyframe_granule != -1)
2755 /* convert granule of this pad to the granule of the keyframe */
2756 pad->keyframe_granule =
2757 gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
2758 GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
2759 pad->keyframe_granule);
2761 /* get time of the keyframe */
2763 gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
2764 GST_LOG_OBJECT (ogg,
2765 "stream %08x granule time %" GST_TIME_FORMAT,
2766 pad->map.serialno, GST_TIME_ARGS (keyframe_time));
2768 /* collect smallest value */
2769 if (keyframe_time != -1) {
2770 keyframe_time += begintime;
2771 if (keyframe_time < keytarget)
2772 keytarget = keyframe_time;
2781 /* for negative rates we will get to the keyframe backwards */
2782 if (segment->rate < 0.0)
2785 if (keytarget != target) {
2786 GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
2787 GST_TIME_ARGS (keytarget));
2789 /* last step, seek to the location of the keyframe */
2790 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
2794 /* seek back to previous position */
2795 GST_LOG_OBJECT (ogg, "keyframe on target");
2796 gst_ogg_demux_seek (ogg, best);
2801 if (segment->rate > 0.0)
2802 segment->time = keytarget;
2803 segment->position = keytarget - begintime;
2812 GST_DEBUG_OBJECT (ogg, "no chains");
2817 GST_DEBUG_OBJECT (ogg, "got a seek error");
2822 /* does not take ownership of the event */
2824 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
2826 GstOggChain *chain = NULL;
2828 gboolean flush, accurate, keyframe;
2832 GstSeekType cur_type, stop_type;
2839 GST_DEBUG_OBJECT (ogg, "seek with event");
2841 gst_event_parse_seek (event, &rate, &format, &flags,
2842 &cur_type, &cur, &stop_type, &stop);
2844 /* we can only seek on time */
2845 if (format != GST_FORMAT_TIME) {
2846 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
2849 seqnum = gst_event_get_seqnum (event);
2851 GST_DEBUG_OBJECT (ogg, "seek without event");
2855 seqnum = gst_util_seqnum_next ();
2858 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
2860 flush = flags & GST_SEEK_FLAG_FLUSH;
2861 accurate = flags & GST_SEEK_FLAG_ACCURATE;
2862 keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
2864 /* first step is to unlock the streaming thread if it is
2865 * blocked in a chain call, we do this by starting the flush. because
2866 * we cannot yet hold any streaming lock, we have to protect the chains
2867 * with their own lock. */
2871 tevent = gst_event_new_flush_start ();
2872 gst_event_set_seqnum (tevent, seqnum);
2874 gst_event_ref (tevent);
2875 gst_pad_push_event (ogg->sinkpad, tevent);
2877 GST_CHAIN_LOCK (ogg);
2878 for (i = 0; i < ogg->chains->len; i++) {
2879 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2882 for (j = 0; j < chain->streams->len; j++) {
2883 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
2885 gst_event_ref (tevent);
2886 gst_pad_push_event (GST_PAD (pad), tevent);
2889 GST_CHAIN_UNLOCK (ogg);
2891 gst_event_unref (tevent);
2893 gst_pad_pause_task (ogg->sinkpad);
2896 /* now grab the stream lock so that streaming cannot continue, for
2897 * non flushing seeks when the element is in PAUSED this could block
2899 GST_PAD_STREAM_LOCK (ogg->sinkpad);
2902 gst_segment_do_seek (&ogg->segment, rate, format, flags,
2903 cur_type, cur, stop_type, stop, &update);
2906 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
2907 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
2908 GST_TIME_ARGS (ogg->segment.stop));
2910 /* we need to stop flushing on the srcpad as we're going to use it
2911 * next. We can do this as we have the STREAM lock now. */
2913 tevent = gst_event_new_flush_stop (TRUE);
2914 gst_event_set_seqnum (tevent, seqnum);
2915 gst_pad_push_event (ogg->sinkpad, tevent);
2921 /* reset all ogg streams now, need to do this from within the lock to
2922 * make sure the streaming thread is not messing with the stream */
2923 for (i = 0; i < ogg->chains->len; i++) {
2924 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2926 gst_ogg_chain_reset (chain);
2930 /* for reverse we will already seek accurately */
2931 res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
2933 /* seek failed, make sure we continue the current chain */
2935 GST_DEBUG_OBJECT (ogg, "seek failed");
2936 chain = ogg->current_chain;
2938 GST_DEBUG_OBJECT (ogg, "seek success");
2944 /* now we have a new position, prepare for streaming again */
2949 gint64 position, begin_time;
2952 /* we have to send the flush to the old chain, not the new one */
2954 tevent = gst_event_new_flush_stop (TRUE);
2955 gst_event_set_seqnum (tevent, seqnum);
2956 gst_ogg_demux_send_event (ogg, tevent);
2959 /* we need this to see how far inside the chain we need to start */
2960 if (chain->begin_time != GST_CLOCK_TIME_NONE)
2961 begin_time = chain->begin_time;
2965 /* segment.start gives the start over all chains, we calculate the amount
2966 * of time into this chain we need to start */
2967 start = ogg->segment.start - begin_time;
2968 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2969 start += chain->segment_start;
2971 if ((stop = ogg->segment.stop) == -1)
2972 stop = ogg->segment.duration;
2974 /* segment.stop gives the stop time over all chains, calculate the amount of
2975 * time we need to stop in this chain */
2977 if (stop > begin_time)
2981 stop += chain->segment_start;
2982 /* we must stop when this chain ends and switch to the next chain to play
2983 * the remainder of the segment. */
2984 stop = MIN (stop, chain->segment_stop);
2987 position = ogg->segment.position;
2988 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2989 position += chain->segment_start;
2991 gst_segment_copy_into (&ogg->segment, &segment);
2993 /* create the segment event we are going to send out */
2994 if (ogg->segment.rate >= 0.0) {
2995 segment.start = position;
2996 segment.stop = stop;
2998 segment.start = start;
2999 segment.stop = position;
3001 event = gst_event_new_segment (&segment);
3002 gst_event_set_seqnum (event, seqnum);
3004 if (chain != ogg->current_chain) {
3005 /* switch to different chain, send segment on new chain */
3006 gst_ogg_demux_activate_chain (ogg, chain, event);
3008 /* mark discont and send segment on current chain */
3009 gst_ogg_chain_mark_discont (chain);
3010 /* This event should be sent from the streaming thread (sink pad task) */
3011 if (ogg->newsegment)
3012 gst_event_unref (ogg->newsegment);
3013 ogg->newsegment = event;
3016 /* notify start of new segment */
3017 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3018 GstMessage *message;
3020 message = gst_message_new_segment_start (GST_OBJECT (ogg),
3021 GST_FORMAT_TIME, ogg->segment.position);
3022 gst_message_set_seqnum (message, seqnum);
3024 gst_element_post_message (GST_ELEMENT (ogg), message);
3027 ogg->seqnum = seqnum;
3028 /* restart our task since it might have been stopped when we did the
3030 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3034 /* streaming can continue now */
3035 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3042 GST_DEBUG_OBJECT (ogg, "seek failed");
3047 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
3048 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3054 gst_ogg_demux_get_duration_push (GstOggDemux * ogg, int flags)
3056 /* In push mode, we get to the end of the stream to get the duration */
3061 /* A full Ogg page can be almost 64 KB. There's no guarantee that there'll be a
3062 granpos there, but it's fairly likely */
3064 ogg->push_byte_length - DURATION_CHUNK_OFFSET - EOS_AVOIDANCE_THRESHOLD;
3068 GST_DEBUG_OBJECT (ogg,
3069 "Getting duration, seeking near the end, to %" G_GINT64_FORMAT, position);
3070 ogg->push_state = PUSH_DURATION;
3071 /* do not read the last byte */
3072 sevent = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET,
3073 position, GST_SEEK_TYPE_SET, ogg->push_byte_length - 1);
3074 res = gst_pad_push_event (ogg->sinkpad, sevent);
3076 GST_DEBUG_OBJECT (ogg, "Seek succesful");
3079 GST_INFO_OBJECT (ogg, "Seek failed, duration will stay unknown");
3080 ogg->push_state = PUSH_PLAYING;
3081 ogg->push_disable_seeking = TRUE;
3087 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3090 if (ogg->push_byte_length < 0) {
3093 GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3094 if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3098 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &length);
3099 if (res && length > 0) {
3100 ogg->push_byte_length = length;
3101 GST_DEBUG_OBJECT (ogg,
3102 "File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
3104 res = gst_pad_query_duration (peer, GST_FORMAT_TIME, &length);
3105 gst_object_unref (peer);
3106 if (res && length >= 0) {
3107 ogg->push_time_length = length;
3108 GST_DEBUG_OBJECT (ogg, "File time length %" GST_TIME_FORMAT,
3109 GST_TIME_ARGS (ogg->push_time_length));
3110 } else if (!ogg->push_disable_seeking) {
3113 res = gst_ogg_demux_get_duration_push (ogg, flags);
3115 GST_DEBUG_OBJECT (ogg,
3116 "File time length unknown, trying to determine");
3117 ogg->push_mode_seek_delayed_event = NULL;
3119 GST_DEBUG_OBJECT (ogg,
3120 "Let me intercept this innocent looking seek request");
3121 ogg->push_mode_seek_delayed_event = gst_event_copy (event);
3132 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3135 gboolean res = TRUE;
3139 GstSeekType start_type, stop_type;
3143 gint64 best, best_time;
3146 GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3148 gst_event_parse_seek (event, &rate, &format, &flags,
3149 &start_type, &start, &stop_type, &stop);
3151 if (format != GST_FORMAT_TIME) {
3152 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3156 if (start_type != GST_SEEK_TYPE_SET) {
3157 GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3161 if (!(flags & GST_SEEK_FLAG_FLUSH)) {
3162 GST_DEBUG_OBJECT (ogg, "can only do flushing seeks");
3166 GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3167 GST_TIME_ARGS (start));
3169 chain = ogg->current_chain;
3171 GST_WARNING_OBJECT (ogg, "No chain to seek on");
3175 /* start accessing push_* members */
3176 GST_PUSH_LOCK (ogg);
3178 /* not if we disabled seeking (chained streams) */
3179 if (ogg->push_disable_seeking) {
3180 GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3184 /* not when we're trying to work out duration */
3185 if (ogg->push_state == PUSH_DURATION) {
3186 GST_DEBUG_OBJECT (ogg, "Busy working out duration, try again later");
3190 /* actually, not if we're doing any seeking already */
3191 if (ogg->push_state != PUSH_PLAYING) {
3192 GST_DEBUG_OBJECT (ogg, "Already doing some seeking, try again later");
3196 /* on the first seek, get length if we can */
3197 if (!gst_ogg_demux_check_duration_push (ogg, flags, event)) {
3198 GST_PUSH_UNLOCK (ogg);
3202 if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
3203 /* the index gave some result */
3204 GST_DEBUG_OBJECT (ogg,
3205 "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
3208 if (ogg->push_time_length > 0) {
3209 /* if we know the time length, we know the full segment bitrate */
3210 GST_DEBUG_OBJECT (ogg, "Using real file bitrate");
3212 gst_util_uint64_scale (ogg->push_byte_length, 8 * GST_SECOND,
3213 ogg->push_time_length);
3214 } else if (ogg->push_time_offset > 0) {
3215 /* get a first approximation using known bitrate to the current position */
3216 GST_DEBUG_OBJECT (ogg, "Using file bitrate so far");
3218 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
3219 ogg->push_time_offset);
3220 } else if (ogg->bitrate > 0) {
3221 /* nominal bitrate is better than nothing, even if it lies often */
3222 GST_DEBUG_OBJECT (ogg, "Using nominal bitrate");
3223 bitrate = ogg->bitrate;
3226 GST_DEBUG_OBJECT (ogg,
3227 "At stream start, and no nominal bitrate, using some random magic "
3229 /* the bisection, once started, should give us a better approximation */
3232 best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3235 /* offset by typical page length, and ensure our best guess is within
3236 reasonable bounds */
3240 if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3241 best = ogg->push_byte_length - 1;
3243 /* set up bisection search */
3244 ogg->push_offset0 = 0;
3245 ogg->push_offset1 = ogg->push_byte_length - 1;
3246 ogg->push_time0 = ogg->push_start_time;
3247 ogg->push_time1 = ogg->push_time_length;
3248 ogg->push_seek_time_target = start;
3249 ogg->push_seek_time_original_target = start;
3250 ogg->push_state = PUSH_BISECT1;
3252 /* reset pad push mode seeking state */
3253 for (i = 0; i < chain->streams->len; i++) {
3254 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3255 pad->push_kf_time = GST_CLOCK_TIME_NONE;
3256 pad->push_sync_time = GST_CLOCK_TIME_NONE;
3259 GST_DEBUG_OBJECT (ogg,
3260 "Setting up bisection search for %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
3261 " (time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", ogg->push_offset0,
3262 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
3263 GST_TIME_ARGS (ogg->push_time1));
3264 GST_DEBUG_OBJECT (ogg,
3265 "Target time is %" GST_TIME_FORMAT ", best first guess is %"
3266 G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target), best);
3268 ogg->push_seek_rate = rate;
3269 ogg->push_seek_flags = flags;
3270 ogg->push_mode_seek_delayed_event = NULL;
3271 ogg->push_bisection_steps[0] = 1;
3272 ogg->push_bisection_steps[1] = 0;
3273 sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
3274 start_type, best, GST_SEEK_TYPE_NONE, -1);
3276 GST_PUSH_UNLOCK (ogg);
3277 res = gst_pad_push_event (ogg->sinkpad, sevent);
3284 GST_DEBUG_OBJECT (ogg, "seek failed");
3289 GST_PUSH_UNLOCK (ogg);
3294 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
3298 if (ogg->pullmode) {
3299 res = gst_ogg_demux_perform_seek_pull (ogg, event);
3301 res = gst_ogg_demux_perform_seek_push (ogg, event);
3307 /* finds each bitstream link one at a time using a bisection search
3308 * (has to begin by knowing the offset of the lb's initial page).
3309 * Recurses for each link so it can alloc the link storage after
3310 * finding them all, then unroll and fill the cache at the same time
3312 static GstFlowReturn
3313 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
3314 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
3316 gint64 endsearched = end;
3321 GstOggChain *nextchain;
3323 GST_LOG_OBJECT (ogg,
3324 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
3325 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
3327 /* the below guards against garbage seperating the last and
3328 * first pages of two links. */
3329 while (searched < endsearched) {
3332 if (endsearched - searched < CHUNKSIZE) {
3335 bisect = (searched + endsearched) / 2;
3338 gst_ogg_demux_seek (ogg, bisect);
3339 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
3341 if (ret == GST_FLOW_EOS) {
3342 endsearched = bisect;
3343 } else if (ret == GST_FLOW_OK) {
3344 guint32 serial = ogg_page_serialno (&og);
3346 if (!gst_ogg_chain_has_stream (chain, serial)) {
3347 endsearched = bisect;
3350 searched = offset + og.header_len + og.body_len;
3356 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
3358 chain->end_offset = searched;
3359 ret = gst_ogg_demux_read_end_chain (ogg, chain);
3360 if (ret != GST_FLOW_OK)
3363 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
3365 gst_ogg_demux_seek (ogg, next);
3366 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
3367 if (ret == GST_FLOW_EOS) {
3370 GST_LOG_OBJECT (ogg, "no next chain");
3371 } else if (ret != GST_FLOW_OK)
3374 if (searched < end && nextchain != NULL) {
3375 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
3376 end, nextchain, m + 1);
3377 if (ret != GST_FLOW_OK)
3380 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
3382 g_array_insert_val (ogg->chains, 0, chain);
3388 /* read a chain from the ogg file. This code will
3389 * read all BOS pages and will create and return a GstOggChain
3390 * structure with the results.
3392 * This function will also read N pages from each stream in the
3393 * chain and submit them to the decoders. When the decoder has
3394 * decoded the first buffer, we know the timestamp of the first
3395 * page in the chain.
3397 static GstFlowReturn
3398 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
3401 GstOggChain *chain = NULL;
3402 gint64 offset = ogg->offset;
3407 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
3409 /* first read the BOS pages, do typefind on them, create
3410 * the decoders, send data to the decoders. */
3415 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3416 if (ret != GST_FLOW_OK) {
3417 if (ret == GST_FLOW_EOS) {
3418 GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
3420 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
3424 if (!ogg_page_bos (&og)) {
3425 GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
3426 /* if we did not find a chain yet, assume this is a bogus stream and
3429 GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
3435 if (chain == NULL) {
3436 chain = gst_ogg_chain_new (ogg);
3437 chain->offset = offset;
3440 serial = ogg_page_serialno (&og);
3441 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
3442 GST_WARNING_OBJECT (ogg,
3443 "found serial %08x BOS page twice, ignoring", serial);
3447 pad = gst_ogg_chain_new_stream (chain, serial);
3448 gst_ogg_pad_submit_page (pad, &og);
3451 if (ret != GST_FLOW_OK || chain == NULL) {
3452 if (ret == GST_FLOW_OK) {
3453 GST_WARNING_OBJECT (ogg, "no chain was found");
3454 ret = GST_FLOW_ERROR;
3455 } else if (ret != GST_FLOW_EOS) {
3456 GST_WARNING_OBJECT (ogg, "failed to read chain");
3458 GST_DEBUG_OBJECT (ogg, "done reading chains");
3461 gst_ogg_chain_free (chain);
3468 chain->have_bos = TRUE;
3469 GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
3471 /* now read pages until we receive a buffer from each of the
3472 * stream decoders, this will tell us the timestamp of the
3473 * first packet in the chain then */
3475 /* save the offset to the first non bos page in the chain: if searching for
3476 * pad->first_time we read past the end of the chain, we'll seek back to this
3479 offset = ogg->offset;
3484 gboolean known_serial = FALSE;
3487 serial = ogg_page_serialno (&og);
3489 for (i = 0; i < chain->streams->len; i++) {
3490 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3492 GST_LOG_OBJECT (ogg,
3493 "serial %08x time %" GST_TIME_FORMAT,
3494 pad->map.serialno, GST_TIME_ARGS (pad->start_time));
3496 if (pad->map.serialno == serial) {
3497 known_serial = TRUE;
3499 /* submit the page now, this will fill in the start_time when the
3500 * internal decoder finds it */
3501 gst_ogg_pad_submit_page (pad, &og);
3503 if (!pad->map.is_skeleton && pad->start_time == -1
3504 && ogg_page_eos (&og)) {
3505 /* got EOS on a pad before we could find its start_time.
3506 * We have no chance of finding a start_time for every pad so
3507 * stop searching for the other start_time(s).
3513 /* the timestamp will be filled in when we submit the pages */
3514 if (!pad->map.is_sparse)
3515 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
3517 GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
3520 /* we read a page not belonging to the current chain: seek back to the
3521 * beginning of the chain
3523 if (!known_serial) {
3524 GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
3525 gst_ogg_demux_seek (ogg, offset);
3530 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3531 if (ret != GST_FLOW_OK)
3535 GST_LOG_OBJECT (ogg, "done reading chain");
3536 /* now we can fill in the missing info using queries */
3537 for (i = 0; i < chain->streams->len; i++) {
3538 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3540 if (pad->map.is_skeleton)
3543 pad->mode = GST_OGG_PAD_MODE_STREAMING;
3552 /* read the last pages from the ogg stream to get the final
3555 static GstFlowReturn
3556 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
3558 gint64 begin = chain->end_offset;
3560 gint64 last_granule = -1;
3561 GstOggPad *last_pad = NULL;
3563 gboolean done = FALSE;
3572 gst_ogg_demux_seek (ogg, begin);
3574 /* now continue reading until we run out of data, if we find a page
3575 * start, we save it. It might not be the final page as there could be
3576 * another page after this one. */
3577 while (ogg->offset < end) {
3578 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
3580 if (ret == GST_FLOW_LIMIT)
3582 if (ret != GST_FLOW_OK)
3585 for (i = 0; i < chain->streams->len; i++) {
3586 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3588 if (pad->map.is_skeleton)
3591 if (pad->map.serialno == ogg_page_serialno (&og)) {
3592 gint64 granulepos = ogg_page_granulepos (&og);
3594 if (granulepos != -1) {
3595 last_granule = granulepos;
3606 chain->segment_stop =
3607 gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
3610 chain->segment_stop = GST_CLOCK_TIME_NONE;
3613 GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
3618 /* find a pad with a given serial number
3621 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
3626 /* first look in building chain if any */
3627 if (ogg->building_chain) {
3628 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
3633 /* then look in current chain if any */
3634 if (ogg->current_chain) {
3635 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
3640 for (i = 0; i < ogg->chains->len; i++) {
3641 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3643 pad = gst_ogg_chain_get_stream (chain, serialno);
3650 /* find a chain with a given serial number
3652 static GstOggChain *
3653 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
3657 pad = gst_ogg_demux_find_pad (ogg, serialno);
3664 /* returns TRUE if all streams have valid start time */
3666 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
3668 gboolean res = TRUE;
3670 chain->total_time = GST_CLOCK_TIME_NONE;
3671 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
3673 /* see if we have a start time on all streams */
3674 chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
3676 if (chain->segment_start == G_MAXUINT64) {
3677 /* not yet, stream some more data */
3679 } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
3680 /* we can calculate a total time */
3681 chain->total_time = chain->segment_stop - chain->segment_start;
3684 GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
3686 GST_DEBUG_OBJECT (ogg, "return %d", res);
3692 gst_ogg_demux_collect_info (GstOggDemux * ogg)
3696 /* collect all info */
3697 ogg->total_time = 0;
3699 for (i = 0; i < ogg->chains->len; i++) {
3700 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3702 chain->begin_time = ogg->total_time;
3704 gst_ogg_demux_collect_chain_info (ogg, chain);
3706 ogg->total_time += chain->total_time;
3708 ogg->segment.duration = ogg->total_time;
3711 /* find all the chains in the ogg file, this reads the first and
3712 * last page of the ogg stream, if they match then the ogg file has
3713 * just one chain, else we do a binary search for all chains.
3715 static GstFlowReturn
3716 gst_ogg_demux_find_chains (GstOggDemux * ogg)
3725 /* get peer to figure out length */
3726 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
3729 /* find length to read last page, we store this for later use. */
3730 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &ogg->length);
3731 gst_object_unref (peer);
3732 if (!res || ogg->length <= 0)
3735 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
3737 /* read chain from offset 0, this is the first chain of the
3739 gst_ogg_demux_seek (ogg, 0);
3740 ret = gst_ogg_demux_read_chain (ogg, &chain);
3741 if (ret != GST_FLOW_OK)
3742 goto no_first_chain;
3744 /* read page from end offset, we use this page to check if its serial
3745 * number is contained in the first chain. If this is the case then
3746 * this ogg is not a chained ogg and we can skip the scanning. */
3747 gst_ogg_demux_seek (ogg, ogg->length);
3748 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
3749 if (ret != GST_FLOW_OK)
3752 serialno = ogg_page_serialno (&og);
3754 if (!gst_ogg_chain_has_stream (chain, serialno)) {
3755 /* the last page is not in the first stream, this means we should
3756 * find all the chains in this chained ogg. */
3758 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
3761 /* we still call this function here but with an empty range so that
3762 * we can reuse the setup code in this routine. */
3764 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
3765 ogg->length, chain, 0);
3767 if (ret != GST_FLOW_OK)
3770 /* all fine, collect and print */
3771 gst_ogg_demux_collect_info (ogg);
3773 /* dump our chains and streams */
3774 gst_ogg_print (ogg);
3779 /*** error cases ***/
3782 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
3783 return GST_FLOW_NOT_LINKED;
3787 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
3788 return GST_FLOW_NOT_SUPPORTED;
3792 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
3793 return GST_FLOW_ERROR;
3797 GST_DEBUG_OBJECT (ogg, "can't get last page");
3799 gst_ogg_chain_free (chain);
3804 static GstFlowReturn
3805 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
3810 GstFlowReturn result = GST_FLOW_OK;
3812 serialno = ogg_page_serialno (page);
3813 granule = ogg_page_granulepos (page);
3815 GST_LOG_OBJECT (ogg,
3816 "processing ogg page (serial %08x, "
3817 "pageno %ld, granulepos %" G_GINT64_FORMAT ", bos %d)", serialno,
3818 ogg_page_pageno (page), granule, ogg_page_bos (page));
3820 if (ogg_page_bos (page)) {
3824 /* see if we know about the chain already */
3825 chain = gst_ogg_demux_find_chain (ogg, serialno);
3831 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3832 start = chain->segment_start;
3834 /* create the newsegment event we are going to send out */
3835 gst_segment_copy_into (&ogg->segment, &segment);
3836 segment.start = start;
3837 segment.stop = chain->segment_stop;
3838 segment.time = chain->begin_time;
3839 event = gst_event_new_segment (&segment);
3840 gst_event_set_seqnum (event, ogg->seqnum);
3842 GST_DEBUG_OBJECT (ogg,
3843 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
3844 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
3845 GST_TIME_ARGS (chain->segment_stop),
3846 GST_TIME_ARGS (chain->begin_time));
3848 /* activate it as it means we have a non-header, this will also deactivate
3849 * the currently running chain. */
3850 gst_ogg_demux_activate_chain (ogg, chain, event);
3851 pad = gst_ogg_demux_find_pad (ogg, serialno);
3853 GstClockTime chain_time;
3854 gint64 current_time;
3856 /* this can only happen in push mode */
3860 current_time = ogg->segment.position;
3862 /* time of new chain is current time */
3863 chain_time = current_time;
3865 if (ogg->building_chain == NULL) {
3866 GstOggChain *newchain;
3868 newchain = gst_ogg_chain_new (ogg);
3869 newchain->offset = 0;
3870 /* set new chain begin time aligned with end time of old chain */
3871 newchain->begin_time = chain_time;
3872 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
3873 GST_TIME_ARGS (chain_time));
3875 /* and this is the one we are building now */
3876 ogg->building_chain = newchain;
3878 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
3881 pad = gst_ogg_demux_find_pad (ogg, serialno);
3884 result = gst_ogg_pad_submit_page (pad, page);
3886 GST_PUSH_LOCK (ogg);
3887 if (!ogg->pullmode && !ogg->push_disable_seeking) {
3888 /* no pad while probing for duration, we must have a chained stream,
3889 and we don't support them, so back off */
3890 GST_INFO_OBJECT (ogg, "We seem to have a chained stream, we won't seek");
3891 if (ogg->push_state == PUSH_DURATION) {
3894 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
3895 if (res != GST_FLOW_OK)
3899 /* only once we seeked back */
3900 GST_PUSH_LOCK (ogg);
3901 ogg->push_disable_seeking = TRUE;
3903 GST_PUSH_UNLOCK (ogg);
3904 /* no pad. This means an ogg page without bos has been seen for this
3905 * serialno. we just ignore it but post a warning... */
3906 GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
3907 (NULL), ("unknown ogg pad for serial %08x detected", serialno));
3910 GST_PUSH_UNLOCK (ogg);
3917 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
3918 (NULL), ("unknown ogg chain for serial %08x detected", serialno));
3919 return GST_FLOW_ERROR;
3923 /* streaming mode, receive a buffer, parse it, create pads for
3924 * the serialno, submit pages and packets to the oggpads
3926 static GstFlowReturn
3927 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
3931 GstFlowReturn result = GST_FLOW_OK;
3933 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
3935 GST_DEBUG_OBJECT (ogg, "enter");
3936 result = gst_ogg_demux_submit_buffer (ogg, buffer);
3938 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
3941 while (result == GST_FLOW_OK) {
3944 ret = ogg_sync_pageout (&ogg->sync, &page);
3946 /* need more data */
3949 /* discontinuity in the pages */
3950 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
3952 result = gst_ogg_demux_handle_page (ogg, &page);
3954 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
3958 if (ret == 0 || result == GST_FLOW_OK) {
3959 gst_ogg_demux_sync_streams (ogg);
3961 GST_DEBUG_OBJECT (ogg, "leave with %d", result);
3966 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
3968 GstOggChain *chain = ogg->current_chain;
3969 gboolean res = TRUE;
3972 chain = ogg->building_chain;
3977 for (i = 0; i < chain->streams->len; i++) {
3978 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3980 gst_event_ref (event);
3981 GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
3982 res &= gst_pad_push_event (GST_PAD (pad), event);
3985 GST_WARNING_OBJECT (ogg, "No chain to forward event to");
3987 gst_event_unref (event);
3992 static GstFlowReturn
3993 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
3998 /* store the value */
3999 pad->last_ret = ret;
4001 /* any other error that is not-linked can be returned right
4003 if (ret != GST_FLOW_NOT_LINKED)
4006 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
4007 chain = ogg->current_chain;
4011 for (i = 0; i < chain->streams->len; i++) {
4012 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4014 ret = opad->last_ret;
4015 /* some other return value (must be SUCCESS but we can return
4016 * other values as well) */
4017 if (ret != GST_FLOW_NOT_LINKED)
4020 /* if we get here, all other pads were unlinked and we return
4021 * NOT_LINKED then */
4027 /* returns TRUE if all streams in current chain reached EOS, FALSE otherwise */
4029 gst_ogg_demux_check_eos (GstOggDemux * ogg)
4032 gboolean eos = TRUE;
4034 chain = ogg->current_chain;
4035 if (G_LIKELY (chain)) {
4038 for (i = 0; i < chain->streams->len; i++) {
4039 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4041 eos = eos && opad->is_eos;
4050 static GstFlowReturn
4051 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4056 if (ogg->offset == ogg->length) {
4057 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4058 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
4063 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4064 ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
4065 if (ret != GST_FLOW_OK) {
4066 GST_LOG_OBJECT (ogg, "Failed pull_range");
4070 ogg->offset += gst_buffer_get_size (buffer);
4072 if (G_UNLIKELY (ogg->newsegment)) {
4073 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4074 ogg->newsegment = NULL;
4077 ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
4078 if (ret != GST_FLOW_OK) {
4079 GST_LOG_OBJECT (ogg, "Failed demux_chain");
4083 /* check for the end of the segment */
4084 if (gst_ogg_demux_check_eos (ogg)) {
4085 GST_LOG_OBJECT (ogg, "got EOS");
4095 * We read the pages backwards and send the packets forwards. The first packet
4096 * in the page will be pushed with the DISCONT flag set.
4098 * Special care has to be taken for continued pages, which we can only decode
4099 * when we have the previous page(s).
4101 static GstFlowReturn
4102 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4108 if (ogg->offset == 0) {
4109 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4110 " == 0", ogg->offset);
4115 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
4116 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
4117 if (ret != GST_FLOW_OK)
4120 ogg->offset = offset;
4122 if (G_UNLIKELY (ogg->newsegment)) {
4123 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4124 ogg->newsegment = NULL;
4127 ret = gst_ogg_demux_handle_page (ogg, &page);
4128 if (ret != GST_FLOW_OK)
4131 /* check for the end of the segment */
4132 if (gst_ogg_demux_check_eos (ogg)) {
4133 GST_LOG_OBJECT (ogg, "got EOS");
4142 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4148 chain = ogg->current_chain;
4149 cur = ogg->segment.position;
4150 if (chain == NULL || cur == -1)
4153 for (i = 0; i < chain->streams->len; i++) {
4154 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4156 /* Theoretically, we should be doing this for all streams, but we're only
4157 * doing it for known-to-be-sparse streams at the moment in order not to
4158 * break things for wrongly-muxed streams (like we used to produce once) */
4159 if (stream->map.is_sparse && stream->position != GST_CLOCK_TIME_NONE) {
4161 /* Does this stream lag? Random threshold of 2 seconds */
4162 if (GST_CLOCK_DIFF (stream->position, cur) > (2 * GST_SECOND)) {
4163 GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
4164 "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
4165 GST_TIME_ARGS (stream->position), GST_TIME_ARGS (cur));
4167 stream->position = cur;
4170 ogg->segment.base += cur - stream->position;
4171 /* advance stream time (FIXME: is this right, esp. time_pos?) */
4172 gst_pad_push_event (GST_PAD_CAST (stream),
4173 gst_event_new_new_segment (TRUE, ogg->segment.rate,
4174 ogg->segment.applied_rate,
4175 GST_FORMAT_TIME, stream->position, -1, stream->position));
4182 /* random access code
4184 * - first find all the chains and streams by scanning the file.
4185 * - then get and chain buffers, just like the streaming case.
4186 * - when seeking, we can use the chain info to perform the seek.
4189 gst_ogg_demux_loop (GstOggPad * pad)
4195 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4197 if (ogg->need_chains) {
4200 /* this is the only place where we write chains and thus need to lock. */
4201 GST_CHAIN_LOCK (ogg);
4202 ret = gst_ogg_demux_find_chains (ogg);
4203 GST_CHAIN_UNLOCK (ogg);
4204 if (ret != GST_FLOW_OK)
4205 goto chain_read_failed;
4207 ogg->need_chains = FALSE;
4209 GST_OBJECT_LOCK (ogg);
4210 ogg->running = TRUE;
4213 GST_OBJECT_UNLOCK (ogg);
4215 /* and seek to configured positions without FLUSH */
4216 res = gst_ogg_demux_perform_seek_pull (ogg, event);
4218 gst_event_unref (event);
4224 if (ogg->segment.rate >= 0.0)
4225 ret = gst_ogg_demux_loop_forward (ogg);
4227 ret = gst_ogg_demux_loop_reverse (ogg);
4229 if (ret != GST_FLOW_OK)
4232 gst_ogg_demux_sync_streams (ogg);
4238 /* error was posted */
4243 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4244 ("failed to start demuxing ogg"));
4245 ret = GST_FLOW_ERROR;
4250 const gchar *reason = gst_flow_get_name (ret);
4251 GstEvent *event = NULL;
4253 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4254 gst_pad_pause_task (ogg->sinkpad);
4256 if (ret == GST_FLOW_EOS) {
4257 /* perform EOS logic */
4258 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4260 GstMessage *message;
4262 /* for segment playback we need to post when (in stream time)
4263 * we stopped, this is either stop (when set) or the duration. */
4264 if ((stop = ogg->segment.stop) == -1)
4265 stop = ogg->segment.duration;
4267 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
4269 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
4271 gst_message_set_seqnum (message, ogg->seqnum);
4273 gst_element_post_message (GST_ELEMENT (ogg), message);
4275 /* normal playback, send EOS to all linked pads */
4276 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
4277 event = gst_event_new_eos ();
4279 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
4280 GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
4281 (_("Internal data stream error.")),
4282 ("stream stopped, reason %s", reason));
4283 event = gst_event_new_eos ();
4286 /* For wrong-state we still want to pause the task and stop
4287 * but no error message or other things are necessary.
4288 * wrong-state is no real error and will be caused by flushing,
4289 * e.g. because of a flushing seek.
4292 gst_event_set_seqnum (event, ogg->seqnum);
4293 gst_ogg_demux_send_event (ogg, event);
4300 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
4304 gst_ogg_demux_deactivate_current_chain (ogg);
4306 GST_CHAIN_LOCK (ogg);
4307 for (i = 0; i < ogg->chains->len; i++) {
4308 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4310 gst_ogg_chain_free (chain);
4312 ogg->chains = g_array_set_size (ogg->chains, 0);
4313 GST_CHAIN_UNLOCK (ogg);
4316 /* this function is called when the pad is activated and should start
4319 * We check if we can do random access to decide if we work push or
4323 gst_ogg_demux_sink_activate (GstPad * sinkpad)
4328 query = gst_query_new_scheduling ();
4330 if (!gst_pad_peer_query (sinkpad, query)) {
4331 gst_query_unref (query);
4335 gst_query_parse_scheduling (query, &pull_mode, NULL, NULL, NULL, NULL, NULL);
4336 gst_query_unref (query);
4341 GST_DEBUG_OBJECT (sinkpad, "activating pull");
4342 return gst_pad_activate_pull (sinkpad, TRUE);
4346 GST_DEBUG_OBJECT (sinkpad, "activating push");
4347 return gst_pad_activate_push (sinkpad, TRUE);
4351 /* this function gets called when we activate ourselves in push mode.
4352 * We cannot seek (ourselves) in the stream */
4354 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
4358 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
4360 ogg->pullmode = FALSE;
4361 ogg->resync = FALSE;
4366 /* this function gets called when we activate ourselves in pull mode.
4367 * We can perform random access to the resource and we start a task
4368 * to start reading */
4370 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
4374 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
4377 ogg->need_chains = TRUE;
4378 ogg->pullmode = TRUE;
4380 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
4383 return gst_pad_stop_task (sinkpad);
4387 static GstStateChangeReturn
4388 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
4391 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
4393 ogg = GST_OGG_DEMUX (element);
4395 switch (transition) {
4396 case GST_STATE_CHANGE_NULL_TO_READY:
4398 ogg_sync_init (&ogg->sync);
4400 case GST_STATE_CHANGE_READY_TO_PAUSED:
4401 ogg_sync_reset (&ogg->sync);
4402 ogg->running = FALSE;
4404 ogg->total_time = -1;
4405 GST_PUSH_LOCK (ogg);
4406 ogg->push_byte_offset = 0;
4407 ogg->push_byte_length = -1;
4408 ogg->push_time_length = GST_CLOCK_TIME_NONE;
4409 ogg->push_time_offset = GST_CLOCK_TIME_NONE;
4410 ogg->push_disable_seeking = FALSE;
4411 ogg->push_state = PUSH_PLAYING;
4412 GST_PUSH_UNLOCK (ogg);
4413 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
4415 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4421 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4423 switch (transition) {
4424 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4426 case GST_STATE_CHANGE_PAUSED_TO_READY:
4427 gst_ogg_demux_clear_chains (ogg);
4428 GST_OBJECT_LOCK (ogg);
4429 ogg->running = FALSE;
4430 GST_OBJECT_UNLOCK (ogg);
4432 case GST_STATE_CHANGE_READY_TO_NULL:
4433 ogg_sync_clear (&ogg->sync);
4442 gst_ogg_demux_plugin_init (GstPlugin * plugin)
4444 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
4445 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
4446 "ogg demuxer setup stage when parsing pipeline");
4449 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
4451 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4452 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4455 return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
4456 GST_TYPE_OGG_DEMUX);
4459 /* prints all info about the element */
4460 #undef GST_CAT_DEFAULT
4461 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
4463 #ifdef GST_DISABLE_GST_DEBUG
4466 gst_ogg_print (GstOggDemux * ogg)
4471 #else /* !GST_DISABLE_GST_DEBUG */
4474 gst_ogg_print (GstOggDemux * ogg)
4478 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
4479 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4480 GST_TIME_ARGS (ogg->total_time));
4482 for (i = 0; i < ogg->chains->len; i++) {
4483 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4485 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
4486 GST_INFO_OBJECT (ogg,
4487 " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
4489 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
4490 GST_TIME_ARGS (chain->begin_time));
4491 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4492 GST_TIME_ARGS (chain->total_time));
4493 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
4494 GST_TIME_ARGS (chain->segment_start));
4495 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
4496 GST_TIME_ARGS (chain->segment_stop));
4498 for (j = 0; j < chain->streams->len; j++) {
4499 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
4501 GST_INFO_OBJECT (ogg, " stream %08x: %s", stream->map.serialno,
4502 gst_ogg_stream_get_media_type (&stream->map));
4503 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
4504 GST_TIME_ARGS (stream->start_time));
4508 #endif /* GST_DISABLE_GST_DEBUG */