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 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
52 #define GST_CHAIN_LOCK(ogg) g_mutex_lock((ogg)->chain_lock)
53 #define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock((ogg)->chain_lock)
55 GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
56 GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
57 #define GST_CAT_DEFAULT gst_ogg_demux_debug
61 _ogg_packet_copy (const ogg_packet * packet)
63 ogg_packet *ret = g_new0 (ogg_packet, 1);
66 ret->packet = g_memdup (packet->packet, packet->bytes);
72 _ogg_packet_free (ogg_packet * packet)
74 g_free (packet->packet);
79 gst_ogg_page_copy (ogg_page * page)
81 ogg_page *p = g_new0 (ogg_page, 1);
83 /* make a copy of the page */
84 p->header = g_memdup (page->header, page->header_len);
85 p->header_len = page->header_len;
86 p->body = g_memdup (page->body, page->body_len);
87 p->body_len = page->body_len;
93 gst_ogg_page_free (ogg_page * page)
95 g_free (page->header);
100 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
101 GstOggChain * chain);
102 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
103 GstOggChain * chain, GstEvent * event);
104 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
106 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
108 static gboolean gst_ogg_demux_receive_event (GstElement * element,
111 static void gst_ogg_pad_dispose (GObject * object);
112 static void gst_ogg_pad_finalize (GObject * object);
114 static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
115 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
116 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
117 static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
118 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
121 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
122 GstOggPad * pad, GstFlowReturn ret);
123 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
125 GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg,
126 GstCaps * caps, GList * headers);
128 GType gst_ogg_pad_get_type (void);
129 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
132 gst_ogg_pad_class_init (GstOggPadClass * klass)
134 GObjectClass *gobject_class;
136 gobject_class = (GObjectClass *) klass;
138 gobject_class->dispose = gst_ogg_pad_dispose;
139 gobject_class->finalize = gst_ogg_pad_finalize;
143 gst_ogg_pad_init (GstOggPad * pad)
145 gst_pad_set_event_function (GST_PAD (pad),
146 GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
147 gst_pad_set_getcaps_function (GST_PAD (pad),
148 GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps));
149 gst_pad_set_query_type_function (GST_PAD (pad),
150 GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types));
151 gst_pad_set_query_function (GST_PAD (pad),
152 GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
154 pad->mode = GST_OGG_PAD_MODE_INIT;
156 pad->current_granule = -1;
157 pad->keyframe_granule = -1;
159 pad->start_time = GST_CLOCK_TIME_NONE;
161 pad->last_stop = GST_CLOCK_TIME_NONE;
163 pad->have_type = FALSE;
164 pad->continued = NULL;
165 pad->map.headers = NULL;
166 pad->map.queued = NULL;
170 gst_ogg_pad_dispose (GObject * object)
172 GstOggPad *pad = GST_OGG_PAD (object);
177 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
178 g_list_free (pad->map.headers);
179 pad->map.headers = NULL;
180 g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
181 g_list_free (pad->map.queued);
182 pad->map.queued = NULL;
184 g_free (pad->map.index);
185 pad->map.index = NULL;
187 /* clear continued pages */
188 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
189 g_list_free (pad->continued);
190 pad->continued = NULL;
193 gst_caps_unref (pad->map.caps);
194 pad->map.caps = NULL;
197 if (pad->map.taglist) {
198 gst_tag_list_free (pad->map.taglist);
199 pad->map.taglist = NULL;
202 ogg_stream_reset (&pad->map.stream);
204 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
208 gst_ogg_pad_finalize (GObject * object)
210 GstOggPad *pad = GST_OGG_PAD (object);
212 ogg_stream_clear (&pad->map.stream);
214 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
217 static const GstQueryType *
218 gst_ogg_pad_query_types (GstPad * pad)
220 static const GstQueryType query_types[] = {
230 gst_ogg_pad_getcaps (GstPad * pad)
232 return gst_caps_ref (GST_PAD_CAPS (pad));
236 gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
241 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
243 switch (GST_QUERY_TYPE (query)) {
244 case GST_QUERY_DURATION:
247 gint64 total_time = -1;
249 gst_query_parse_duration (query, &format, NULL);
250 /* can only get position in time */
251 if (format != GST_FORMAT_TIME)
254 if (ogg->total_time != -1) {
255 /* we can return the total length */
256 total_time = ogg->total_time;
258 gint bitrate = ogg->bitrate;
260 /* try with length and bitrate */
264 /* ask upstream for total length in bytes */
265 uquery = gst_query_new_duration (GST_FORMAT_BYTES);
266 if (gst_pad_peer_query (ogg->sinkpad, uquery)) {
269 gst_query_parse_duration (uquery, NULL, &length);
271 /* estimate using the bitrate */
273 gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);
276 "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
277 GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
279 gst_query_unref (uquery);
283 gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
286 case GST_QUERY_SEEKING:
290 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
291 if (format == GST_FORMAT_TIME) {
292 gboolean seekable = FALSE;
297 stop = ogg->total_time;
298 } else if (ogg->current_chain->streams->len) {
302 for (i = 0; i < ogg->current_chain->streams->len; i++) {
304 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
306 seekable |= (pad->map.index != NULL && pad->map.n_index != 0);
308 if (pad->map.index != NULL && pad->map.n_index != 0) {
310 GstClockTime idx_time;
312 idx = &pad->map.index[pad->map.n_index - 1];
314 gst_util_uint64_scale (idx->timestamp, GST_SECOND,
319 stop = MAX (idx_time, stop);
324 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
332 res = gst_pad_query_default (pad, query);
336 gst_object_unref (ogg);
343 GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
350 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
355 ogg = GST_OGG_DEMUX (element);
357 switch (GST_EVENT_TYPE (event)) {
359 /* now do the seek */
360 res = gst_ogg_demux_perform_seek (ogg, event);
361 gst_event_unref (event);
364 GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
373 GST_DEBUG_OBJECT (ogg, "error handling event");
374 gst_event_unref (event);
380 gst_ogg_pad_event (GstPad * pad, GstEvent * event)
385 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
387 switch (GST_EVENT_TYPE (event)) {
389 /* now do the seek */
390 res = gst_ogg_demux_perform_seek (ogg, event);
391 gst_event_unref (event);
394 res = gst_pad_event_default (pad, event);
397 gst_object_unref (ogg);
403 gst_ogg_pad_reset (GstOggPad * pad)
405 ogg_stream_reset (&pad->map.stream);
407 GST_DEBUG_OBJECT (pad, "doing reset");
409 /* clear continued pages */
410 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
411 g_list_free (pad->continued);
412 pad->continued = NULL;
414 pad->last_ret = GST_FLOW_OK;
415 pad->last_stop = GST_CLOCK_TIME_NONE;
416 pad->current_granule = -1;
417 pad->keyframe_granule = -1;
421 /* queue data, basically takes the packet, puts it in a buffer and store the
422 * buffer in the queued list. */
424 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
426 #ifndef GST_DISABLE_GST_DEBUG
427 GstOggDemux *ogg = pad->ogg;
430 GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08lx", pad,
433 pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
440 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
441 gboolean push_headers)
443 GstBuffer *buf = NULL;
444 GstFlowReturn ret, cret;
445 GstOggDemux *ogg = pad->ogg;
451 GstClockTime out_timestamp, out_duration;
452 guint64 out_offset, out_offset_end;
453 gboolean delta_unit = FALSE;
457 GST_DEBUG_OBJECT (ogg,
458 "%p streaming to peer serial %08lx", pad, pad->map.serialno);
460 if (pad->map.is_ogm) {
464 data = packet->packet;
465 bytes = packet->bytes;
470 if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
471 /* We don't push header packets for OGM */
475 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
476 delta_unit = (((data[0] & 0x08) >> 3) == 0);
480 /* Strip trailing \0 for subtitles */
481 if (pad->map.is_ogm_text) {
482 while (bytes && data[bytes - 1] == 0) {
487 } else if (pad->map.is_vp8) {
488 if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
490 (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) {
491 /* We don't push header packets for VP8 */
501 /* get timing info for the packet */
502 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
503 GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
506 out_timestamp = GST_CLOCK_TIME_NONE;
507 out_duration = GST_CLOCK_TIME_NONE;
511 if (packet->granulepos != -1) {
512 pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
514 pad->keyframe_granule =
515 gst_ogg_stream_granulepos_to_key_granule (&pad->map,
517 GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
518 pad->current_granule);
519 } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
520 pad->current_granule += duration;
521 GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT,
522 pad->current_granule);
524 if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
525 /* negative rates, only set timestamp on the packets with a granulepos */
531 /* we only push buffers after we have a valid granule. This is done so that
532 * we nicely skip packets without a timestamp after a seek. This is ok
533 * because we base or seek on the packet after the page with the smaller
535 if (pad->current_granule == -1)
538 if (pad->map.is_ogm) {
539 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
540 pad->current_granule);
541 out_duration = gst_util_uint64_scale (duration,
542 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
543 } else if (pad->map.is_sparse) {
544 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
545 pad->current_granule);
546 if (duration == GST_CLOCK_TIME_NONE) {
547 out_duration = GST_CLOCK_TIME_NONE;
549 out_duration = gst_util_uint64_scale (duration,
550 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
553 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
554 pad->current_granule - duration);
556 gst_ogg_stream_granule_to_time (&pad->map,
557 pad->current_granule) - out_timestamp;
560 gst_ogg_stream_granule_to_granulepos (&pad->map,
561 pad->current_granule, pad->keyframe_granule);
563 gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
567 if (pad->map.is_ogm_text) {
568 /* check for invalid buffer sizes */
569 if (G_UNLIKELY (offset + trim >= packet->bytes))
576 buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
577 gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
579 /* set delta flag for OGM content */
581 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
583 /* copy packet in buffer */
584 memcpy (buf->data, packet->packet + offset, packet->bytes - offset - trim);
586 GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
587 GST_BUFFER_DURATION (buf) = out_duration;
588 GST_BUFFER_OFFSET (buf) = out_offset;
589 GST_BUFFER_OFFSET_END (buf) = out_offset_end;
591 /* Mark discont on the buffer */
593 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
594 pad->discont = FALSE;
597 pad->last_stop = ogg->segment.last_stop;
599 /* don't push the header packets when we are asked to skip them */
600 if (!packet->b_o_s || push_headers) {
601 ret = gst_pad_push (GST_PAD_CAST (pad), buf);
605 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
608 /* we're done with skeleton stuff */
609 if (pad->map.is_skeleton)
612 /* check if valid granulepos, then we can calculate the current
613 * position. We know the granule for each packet but we only want to update
614 * the last_stop when we have a valid granulepos on the packet because else
615 * our time jumps around for the different streams. */
616 if (packet->granulepos < 0)
619 /* convert to time */
620 current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
623 /* convert to stream time */
624 if ((chain = pad->chain)) {
625 gint64 chain_start = 0;
627 if (chain->segment_start != GST_CLOCK_TIME_NONE)
628 chain_start = chain->segment_start;
630 current_time = current_time - chain_start + chain->begin_time;
633 /* and store as the current position */
634 gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time);
636 GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
637 GST_TIME_ARGS (current_time));
639 /* check stream eos */
640 if ((ogg->segment.rate > 0.0 && ogg->segment.stop != GST_CLOCK_TIME_NONE &&
641 current_time > ogg->segment.stop) ||
642 (ogg->segment.rate < 0.0 && ogg->segment.start != GST_CLOCK_TIME_NONE &&
643 current_time < ogg->segment.start)) {
644 GST_DEBUG_OBJECT (ogg, "marking pad %p EOS", pad);
650 gst_buffer_unref (buf);
651 /* return combined flow result */
657 GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
663 GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
668 GST_DEBUG_OBJECT (ogg, "pad not added yet");
674 gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
677 guint64 start_time = G_MAXUINT64;
679 for (i = 0; i < chain->streams->len; i++) {
680 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
682 if (pad->map.is_sparse)
685 /* can do this if the pad start time is not defined */
686 if (pad->start_time == GST_CLOCK_TIME_NONE) {
687 start_time = G_MAXUINT64;
690 start_time = MIN (start_time, pad->start_time);
696 /* submit a packet to the oggpad, this function will run the
697 * typefind code for the pad if this is the first packet for this
701 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
704 GstFlowReturn ret = GST_FLOW_OK;
706 GstOggDemux *ogg = pad->ogg;
708 GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08lx", pad,
711 if (!pad->have_type) {
712 pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
713 if (!pad->have_type) {
714 pad->map.caps = gst_caps_new_simple ("application/x-unknown", NULL);
716 if (pad->map.is_skeleton) {
717 GST_DEBUG_OBJECT (ogg, "we have a fishead");
718 /* copy values over to global ogg level */
719 ogg->basetime = pad->map.basetime;
720 ogg->prestime = pad->map.prestime;
722 /* use total time to update the total ogg time */
723 if (ogg->total_time == -1) {
724 ogg->total_time = pad->map.total_time;
725 } else if (pad->map.total_time > 0) {
726 ogg->total_time = MAX (ogg->total_time, pad->map.total_time);
730 gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
732 GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
736 if (pad->map.is_skeleton) {
741 /* try to parse the serialno first */
742 if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
745 GST_WARNING_OBJECT (pad->ogg,
746 "got skeleton packet for stream 0x%08x", serialno);
748 skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
751 case GST_OGG_SKELETON_FISBONE:
752 /* parse the remainder of the fisbone in the pad with the serialno,
753 * note that we ignore the start_time as this is usually wrong for
755 gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
756 packet->bytes, NULL);
758 case GST_OGG_SKELETON_INDEX:
759 gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
762 /* use total time to update the total ogg time */
763 if (ogg->total_time == -1) {
764 ogg->total_time = skel_pad->map.total_time;
765 } else if (skel_pad->map.total_time > 0) {
766 ogg->total_time = MAX (ogg->total_time, skel_pad->map.total_time);
774 GST_WARNING_OBJECT (pad->ogg,
775 "found skeleton fisbone for an unknown stream 0x%08x", serialno);
780 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
783 GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
784 pad->current_granule = granule;
787 /* restart header packet count when seeing a b_o_s page;
788 * particularly useful following a seek or even following chain finding */
790 GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
791 pad->map.n_header_packets_seen = 0;
792 if (!pad->map.have_headers) {
793 GST_DEBUG_OBJECT (ogg, "clearing header packets");
794 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
795 g_list_free (pad->map.headers);
796 pad->map.headers = NULL;
800 /* Overload the value of b_o_s in ogg_packet with a flag whether or
801 * not this is a header packet. Maybe some day this could be cleaned
803 packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
804 if (!packet->b_o_s) {
805 GST_DEBUG ("found non-header packet");
806 pad->map.have_headers = TRUE;
807 if (pad->start_time == GST_CLOCK_TIME_NONE) {
808 gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
809 GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
810 if (duration != -1) {
811 pad->map.accumulated_granule += duration;
812 GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
813 pad->map.accumulated_granule);
816 if (packet->granulepos != -1) {
817 ogg_int64_t start_granule;
820 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
823 if (granule > pad->map.accumulated_granule)
824 start_granule = granule - pad->map.accumulated_granule;
828 pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
830 GST_DEBUG ("start time %" G_GINT64_FORMAT, pad->start_time);
832 packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
833 pad->map.accumulated_granule, pad->keyframe_granule);
837 /* look for tags in header packet (before inc header count) */
838 gst_ogg_stream_extract_tags (&pad->map, packet);
839 pad->map.n_header_packets_seen++;
840 if (!pad->map.have_headers) {
842 g_list_append (pad->map.headers, _ogg_packet_copy (packet));
843 GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
847 /* we know the start_time of the pad data, see if we
848 * can activate the complete chain if this is a dynamic
849 * chain. We need all the headers too for this. */
850 if (pad->start_time != GST_CLOCK_TIME_NONE && pad->map.have_headers) {
851 GstOggChain *chain = pad->chain;
853 /* check if complete chain has start time */
854 if (chain == ogg->building_chain) {
855 GstEvent *event = NULL;
860 GST_DEBUG_OBJECT (ogg, "need to resync");
862 /* when we need to resync after a seek, we wait until we have received
863 * timestamps on all streams */
864 start_time = gst_ogg_demux_collect_start_time (ogg, chain);
866 if (start_time != G_MAXUINT64) {
869 GST_DEBUG_OBJECT (ogg, "start_time: %" GST_TIME_FORMAT,
870 GST_TIME_ARGS (start_time));
872 if (chain->segment_start < start_time)
874 (start_time - chain->segment_start) + chain->begin_time;
876 segment_time = chain->begin_time;
878 /* create the newsegment event we are going to send out */
879 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
880 GST_FORMAT_TIME, start_time, chain->segment_stop, segment_time);
885 /* see if we have enough info to activate the chain, we have enough info
886 * when all streams have a valid start time. */
887 if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
889 GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
890 GST_TIME_ARGS (chain->segment_start));
891 GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT,
892 GST_TIME_ARGS (chain->segment_stop));
893 GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT,
894 GST_TIME_ARGS (chain->begin_time));
896 /* create the newsegment event we are going to send out */
897 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
898 GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
904 gst_event_set_seqnum (event, ogg->seqnum);
906 gst_ogg_demux_activate_chain (ogg, chain, event);
908 ogg->building_chain = NULL;
913 /* if we are building a chain, store buffer for when we activate
914 * it. This path is taken if we operate in streaming mode. */
915 if (ogg->building_chain) {
916 /* bos packets where stored in the header list so we can discard
919 ret = gst_ogg_demux_queue_data (pad, packet);
921 /* else we are completely streaming to the peer */
923 ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
928 /* flush at most @npackets from the stream layer. All packets if
932 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
934 GstFlowReturn result = GST_FLOW_OK;
935 gboolean done = FALSE;
944 ret = ogg_stream_packetout (&pad->map.stream, &packet);
947 GST_LOG_OBJECT (ogg, "packetout done");
951 GST_LOG_OBJECT (ogg, "packetout discont");
952 gst_ogg_chain_mark_discont (pad->chain);
955 GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
956 result = gst_ogg_pad_submit_packet (pad, &packet);
957 /* not linked is not a problem, it's possible that we are still
958 * collecting headers and that we don't have exposed the pads yet */
959 if (result == GST_FLOW_NOT_LINKED)
961 else if (result <= GST_FLOW_UNEXPECTED)
962 goto could_not_submit;
965 GST_WARNING_OBJECT (ogg,
966 "invalid return value %d for ogg_stream_packetout, resetting stream",
968 gst_ogg_pad_reset (pad);
973 done = (npackets == 0);
981 GST_WARNING_OBJECT (ogg,
982 "could not submit packet for stream %08lx, error: %d",
983 pad->map.serialno, result);
984 gst_ogg_pad_reset (pad);
989 /* submit a page to an oggpad, this function will then submit all
990 * the packets in the page.
993 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
995 GstFlowReturn result = GST_FLOW_OK;
997 gboolean continued = FALSE;
1001 /* for negative rates we read pages backwards and must therefore be carefull
1002 * with continued pages */
1003 if (ogg->segment.rate < 0.0) {
1006 continued = ogg_page_continued (page);
1008 /* number of completed packets in the page */
1009 npackets = ogg_page_packets (page);
1011 /* page is not continued so it contains at least one packet start. It's
1012 * possible that no packet ends on this page (npackets == 0). In that
1013 * case, the next (continued) page(s) we kept contain the remainder of the
1014 * packets. We mark npackets=1 to make us start decoding the pages in the
1015 * remainder of the algorithm. */
1019 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
1021 if (npackets == 0) {
1022 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
1027 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1030 /* flush all packets in the stream layer, this might not give a packet if
1031 * the page had no packets finishing on the page (npackets == 0). */
1032 result = gst_ogg_pad_stream_out (pad, 0);
1034 if (pad->continued) {
1037 /* now send the continued pages to the stream layer */
1038 while (pad->continued) {
1039 ogg_page *p = (ogg_page *) pad->continued->data;
1041 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
1042 if (ogg_stream_pagein (&pad->map.stream, p) != 0)
1045 pad->continued = g_list_delete_link (pad->continued, pad->continued);
1048 gst_ogg_page_free (p);
1051 GST_LOG_OBJECT (ogg, "flushing last continued packet");
1052 /* flush 1 continued packet in the stream layer */
1053 result = gst_ogg_pad_stream_out (pad, 1);
1055 /* flush all remaining packets, we pushed them in the previous round.
1056 * We don't use _reset() because we still want to get the discont when
1057 * we submit a next page. */
1058 while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
1062 /* keep continued pages (only in reverse mode) */
1064 ogg_page *p = gst_ogg_page_copy (page);
1066 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
1067 pad->continued = g_list_prepend (pad->continued, p);
1074 GST_WARNING_OBJECT (ogg,
1075 "ogg stream choked on page (serial %08lx), resetting stream",
1077 gst_ogg_pad_reset (pad);
1078 /* we continue to recover */
1084 static GstOggChain *
1085 gst_ogg_chain_new (GstOggDemux * ogg)
1087 GstOggChain *chain = g_new0 (GstOggChain, 1);
1089 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
1093 chain->have_bos = FALSE;
1094 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
1095 chain->begin_time = GST_CLOCK_TIME_NONE;
1096 chain->segment_start = GST_CLOCK_TIME_NONE;
1097 chain->segment_stop = GST_CLOCK_TIME_NONE;
1098 chain->total_time = GST_CLOCK_TIME_NONE;
1104 gst_ogg_chain_free (GstOggChain * chain)
1108 for (i = 0; i < chain->streams->len; i++) {
1109 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1111 gst_object_unref (pad);
1113 g_array_free (chain->streams, TRUE);
1118 gst_ogg_pad_mark_discont (GstOggPad * pad)
1120 pad->discont = TRUE;
1121 pad->map.last_size = 0;
1125 gst_ogg_chain_mark_discont (GstOggChain * chain)
1129 for (i = 0; i < chain->streams->len; i++) {
1130 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1132 gst_ogg_pad_mark_discont (pad);
1137 gst_ogg_chain_reset (GstOggChain * chain)
1141 for (i = 0; i < chain->streams->len; i++) {
1142 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1144 gst_ogg_pad_reset (pad);
1149 gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno)
1155 GST_DEBUG_OBJECT (chain->ogg, "creating new stream %08lx in chain %p",
1158 ret = g_object_new (GST_TYPE_OGG_PAD, NULL);
1159 /* we own this one */
1160 gst_object_ref (ret);
1161 gst_object_sink (ret);
1163 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1164 gst_ogg_pad_mark_discont (ret);
1167 ret->ogg = chain->ogg;
1169 ret->map.serialno = serialno;
1170 if (ogg_stream_init (&ret->map.stream, serialno) != 0)
1173 name = g_strdup_printf ("serial_%08lx", serialno);
1174 gst_object_set_name (GST_OBJECT (ret), name);
1177 /* FIXME: either do something with it or remove it */
1178 list = gst_tag_list_new ();
1179 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1181 gst_tag_list_free (list);
1183 GST_DEBUG_OBJECT (chain->ogg,
1184 "created new ogg src %p for stream with serial %08lx", ret, serialno);
1186 g_array_append_val (chain->streams, ret);
1193 GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
1195 gst_object_unref (ret);
1201 gst_ogg_chain_get_stream (GstOggChain * chain, glong serialno)
1205 for (i = 0; i < chain->streams->len; i++) {
1206 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1208 if (pad->map.serialno == serialno)
1215 gst_ogg_chain_has_stream (GstOggChain * chain, glong serialno)
1217 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1220 /* signals and args */
1233 static GstStaticPadTemplate ogg_demux_src_template_factory =
1234 GST_STATIC_PAD_TEMPLATE ("src_%d",
1237 GST_STATIC_CAPS_ANY);
1239 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1240 GST_STATIC_PAD_TEMPLATE ("sink",
1243 GST_STATIC_CAPS ("application/ogg; application/x-annodex")
1246 static void gst_ogg_demux_finalize (GObject * object);
1248 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1249 GstOggChain ** chain);
1250 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1251 GstOggChain * chain);
1253 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
1254 static void gst_ogg_demux_loop (GstOggPad * pad);
1255 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
1256 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
1257 static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
1259 static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
1261 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1262 GstStateChange transition);
1263 static gboolean gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
1265 static void gst_ogg_print (GstOggDemux * demux);
1267 GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT);
1270 gst_ogg_demux_base_init (gpointer g_class)
1272 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1274 gst_element_class_set_details_simple (element_class,
1275 "Ogg demuxer", "Codec/Demuxer",
1276 "demux ogg streams (info about ogg: http://xiph.org)",
1277 "Wim Taymans <wim@fluendo.com>");
1279 gst_element_class_add_pad_template (element_class,
1280 gst_static_pad_template_get (&ogg_demux_sink_template_factory));
1281 gst_element_class_add_pad_template (element_class,
1282 gst_static_pad_template_get (&ogg_demux_src_template_factory));
1286 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1288 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1289 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1291 gstelement_class->change_state = gst_ogg_demux_change_state;
1292 gstelement_class->send_event = gst_ogg_demux_receive_event;
1294 gobject_class->finalize = gst_ogg_demux_finalize;
1298 gst_ogg_demux_init (GstOggDemux * ogg, GstOggDemuxClass * g_class)
1300 /* create the sink pad */
1302 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
1305 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
1306 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
1307 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
1308 gst_pad_set_activatepull_function (ogg->sinkpad,
1309 gst_ogg_demux_sink_activate_pull);
1310 gst_pad_set_activatepush_function (ogg->sinkpad,
1311 gst_ogg_demux_sink_activate_push);
1312 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
1314 ogg->chain_lock = g_mutex_new ();
1315 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
1317 ogg->newsegment = NULL;
1321 gst_ogg_demux_finalize (GObject * object)
1325 ogg = GST_OGG_DEMUX (object);
1327 g_array_free (ogg->chains, TRUE);
1328 g_mutex_free (ogg->chain_lock);
1329 ogg_sync_clear (&ogg->sync);
1331 if (ogg->newsegment)
1332 gst_event_unref (ogg->newsegment);
1334 G_OBJECT_CLASS (parent_class)->finalize (object);
1338 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
1343 chain = ogg->current_chain;
1347 for (i = 0; i < chain->streams->len; i++) {
1348 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
1350 stream->start_time = -1;
1351 stream->map.accumulated_granule = 0;
1353 ogg->building_chain = chain;
1354 ogg->current_chain = NULL;
1359 gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
1364 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
1366 switch (GST_EVENT_TYPE (event)) {
1367 case GST_EVENT_FLUSH_START:
1368 res = gst_ogg_demux_send_event (ogg, event);
1370 case GST_EVENT_FLUSH_STOP:
1371 GST_DEBUG_OBJECT (ogg, "got a flush stop event");
1372 ogg_sync_reset (&ogg->sync);
1373 res = gst_ogg_demux_send_event (ogg, event);
1374 gst_ogg_demux_reset_streams (ogg);
1376 case GST_EVENT_NEWSEGMENT:
1377 GST_DEBUG_OBJECT (ogg, "got a new segment event");
1378 gst_event_unref (event);
1383 GST_DEBUG_OBJECT (ogg, "got an EOS event");
1384 res = gst_ogg_demux_send_event (ogg, event);
1385 if (ogg->current_chain == NULL) {
1386 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
1387 ("can't get first chain"));
1392 res = gst_ogg_demux_send_event (ogg, event);
1395 gst_object_unref (ogg);
1400 /* submit the given buffer to the ogg sync */
1401 static GstFlowReturn
1402 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
1407 GstFlowReturn ret = GST_FLOW_OK;
1409 size = GST_BUFFER_SIZE (buffer);
1410 data = GST_BUFFER_DATA (buffer);
1412 GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
1413 if (G_UNLIKELY (size == 0))
1416 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
1417 if (G_UNLIKELY (oggbuffer == NULL))
1420 memcpy (oggbuffer, data, size);
1421 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
1425 gst_buffer_unref (buffer);
1432 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1433 (NULL), ("failed to get ogg sync buffer"));
1434 ret = GST_FLOW_ERROR;
1439 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1440 (NULL), ("failed to write %d bytes to the sync buffer", size));
1441 ret = GST_FLOW_ERROR;
1446 /* in random access mode this code updates the current read position
1447 * and resets the ogg sync buffer so that the next read will happen
1448 * from this new location.
1451 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
1453 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
1455 ogg->offset = offset;
1456 ogg->read_offset = offset;
1457 ogg_sync_reset (&ogg->sync);
1460 /* read more data from the current offset and submit to
1461 * the ogg sync layer.
1463 static GstFlowReturn
1464 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
1469 GST_LOG_OBJECT (ogg,
1470 "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
1471 ogg->read_offset, ogg->length, end_offset);
1473 if (end_offset > 0 && ogg->read_offset >= end_offset)
1474 goto boundary_reached;
1476 if (ogg->read_offset == ogg->length)
1479 ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
1480 if (ret != GST_FLOW_OK)
1483 ogg->read_offset += GST_BUFFER_SIZE (buffer);
1485 ret = gst_ogg_demux_submit_buffer (ogg, buffer);
1492 GST_LOG_OBJECT (ogg, "reached boundary");
1493 return GST_FLOW_LIMIT;
1497 GST_LOG_OBJECT (ogg, "reached EOS");
1498 return GST_FLOW_UNEXPECTED;
1502 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
1503 gst_flow_get_name (ret));
1508 /* Read the next page from the current offset.
1509 * boundary: number of bytes ahead we allow looking for;
1512 * @offset will contain the offset the next page starts at when this function
1513 * returns GST_FLOW_OK.
1515 * GST_FLOW_UNEXPECTED is returned on EOS.
1517 * GST_FLOW_LIMIT is returned when we did not find a page before the
1518 * boundary. If @boundary is -1, this is never returned.
1520 * Any other error returned while retrieving data from the peer is returned as
1523 static GstFlowReturn
1524 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
1525 gint64 boundary, gint64 * offset)
1527 gint64 end_offset = -1;
1530 GST_LOG_OBJECT (ogg,
1531 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
1532 G_GINT64_FORMAT, ogg->offset, boundary);
1535 end_offset = ogg->offset + boundary;
1540 if (end_offset > 0 && ogg->offset >= end_offset)
1541 goto boundary_reached;
1543 more = ogg_sync_pageseek (&ogg->sync, og);
1545 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
1548 /* skipped n bytes */
1549 ogg->offset -= more;
1550 GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
1552 } else if (more == 0) {
1553 /* we need more data */
1555 goto boundary_reached;
1557 GST_LOG_OBJECT (ogg, "need more data");
1558 ret = gst_ogg_demux_get_data (ogg, end_offset);
1559 if (ret != GST_FLOW_OK)
1562 gint64 res_offset = ogg->offset;
1564 /* got a page. Return the offset at the page beginning,
1565 advance the internal offset past the page end */
1567 *offset = res_offset;
1570 ogg->offset += more;
1572 GST_LOG_OBJECT (ogg,
1573 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
1574 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
1575 ogg_page_serialno (og), ogg->offset,
1576 (gint64) ogg_page_granulepos (og));
1580 GST_LOG_OBJECT (ogg, "returning %d", ret);
1587 GST_LOG_OBJECT (ogg,
1588 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
1589 ogg->offset, end_offset);
1590 return GST_FLOW_LIMIT;
1594 /* from the current offset, find the previous page, seeking backwards
1595 * until we find the page.
1597 static GstFlowReturn
1598 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
1601 gint64 begin = ogg->offset;
1603 gint64 cur_offset = -1;
1605 GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
1607 while (cur_offset == -1) {
1612 /* seek CHUNKSIZE back */
1613 gst_ogg_demux_seek (ogg, begin);
1615 /* now continue reading until we run out of data, if we find a page
1616 * start, we save it. It might not be the final page as there could be
1617 * another page after this one. */
1618 while (ogg->offset < end) {
1622 gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
1623 /* we hit the upper limit, offset contains the last page start */
1624 if (ret == GST_FLOW_LIMIT) {
1625 GST_LOG_OBJECT (ogg, "hit limit");
1628 /* something went wrong */
1629 if (ret == GST_FLOW_UNEXPECTED) {
1631 GST_LOG_OBJECT (ogg, "got unexpected");
1632 } else if (ret != GST_FLOW_OK) {
1633 GST_LOG_OBJECT (ogg, "got error %d", ret);
1637 GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
1639 /* offset is next page start */
1640 cur_offset = new_offset;
1644 GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
1646 /* we have the offset. Actually snork and hold the page now */
1647 gst_ogg_demux_seek (ogg, cur_offset);
1648 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
1649 if (ret != GST_FLOW_OK) {
1650 GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
1652 /* this shouldn't be possible */
1657 *offset = cur_offset;
1663 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
1666 GstOggChain *chain = ogg->current_chain;
1671 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
1673 /* send EOS on all the pads */
1674 for (i = 0; i < chain->streams->len; i++) {
1675 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1681 event = gst_event_new_eos ();
1682 gst_event_set_seqnum (event, ogg->seqnum);
1683 gst_pad_push_event (GST_PAD_CAST (pad), event);
1685 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
1687 /* deactivate first */
1688 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
1690 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
1694 /* if we cannot seek back to the chain, we can destroy the chain
1696 if (!ogg->pullmode) {
1697 gst_ogg_chain_free (chain);
1699 ogg->current_chain = NULL;
1705 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
1708 GstStructure *structure;
1709 GValue array = { 0 };
1711 GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
1713 if (G_UNLIKELY (!caps))
1715 if (G_UNLIKELY (!headers))
1718 caps = gst_caps_make_writable (caps);
1719 structure = gst_caps_get_structure (caps, 0);
1721 g_value_init (&array, GST_TYPE_ARRAY);
1724 GValue value = { 0 };
1726 ogg_packet *op = headers->data;
1728 buffer = gst_buffer_new_and_alloc (op->bytes);
1729 memcpy (GST_BUFFER_DATA (buffer), op->packet, op->bytes);
1730 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS);
1731 g_value_init (&value, GST_TYPE_BUFFER);
1732 gst_value_take_buffer (&value, buffer);
1733 gst_value_array_append_value (&array, &value);
1734 g_value_unset (&value);
1735 headers = headers->next;
1738 gst_structure_set_value (structure, "streamheader", &array);
1739 g_value_unset (&array);
1740 GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
1746 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
1750 gint bitrate, idx_bitrate;
1752 g_return_val_if_fail (chain != NULL, FALSE);
1754 if (chain == ogg->current_chain) {
1756 gst_event_unref (event);
1761 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
1763 bitrate = idx_bitrate = 0;
1765 /* first add the pads */
1766 for (i = 0; i < chain->streams->len; i++) {
1769 pad = g_array_index (chain->streams, GstOggPad *, i);
1771 if (pad->map.idx_bitrate)
1772 idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
1774 bitrate += pad->map.bitrate;
1777 gst_ogg_pad_mark_discont (pad);
1778 pad->last_ret = GST_FLOW_OK;
1780 if (pad->map.is_skeleton || pad->added || GST_PAD_CAPS (pad) == NULL)
1783 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
1785 /* activate first */
1786 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
1788 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
1791 /* prefer the index bitrate over the ones encoded in the streams */
1792 ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
1794 /* after adding the new pads, remove the old pads */
1795 gst_ogg_demux_deactivate_current_chain (ogg);
1797 ogg->current_chain = chain;
1799 /* we are finished now */
1800 gst_element_no_more_pads (GST_ELEMENT (ogg));
1802 /* FIXME, must be sent from the streaming thread */
1804 gst_ogg_demux_send_event (ogg, event);
1806 gst_element_found_tags (GST_ELEMENT_CAST (ogg),
1807 gst_tag_list_new_full (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL));
1810 GST_DEBUG_OBJECT (ogg, "starting chain");
1812 /* then send out any headers and queued packets */
1813 for (i = 0; i < chain->streams->len; i++) {
1817 pad = g_array_index (chain->streams, GstOggPad *, i);
1819 /* FIXME also streaming thread */
1820 if (pad->map.taglist) {
1821 GST_DEBUG_OBJECT (ogg, "pushing tags");
1822 gst_element_found_tags_for_pad (GST_ELEMENT_CAST (ogg),
1823 GST_PAD_CAST (pad), pad->map.taglist);
1824 pad->map.taglist = NULL;
1827 /* Set headers on caps */
1829 gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers);
1830 gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps);
1832 GST_DEBUG_OBJECT (ogg, "pushing headers");
1834 for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
1835 ogg_packet *p = walk->data;
1837 gst_ogg_demux_chain_peer (pad, p, TRUE);
1840 GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
1841 /* push queued packets */
1842 for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
1843 ogg_packet *p = walk->data;
1845 gst_ogg_demux_chain_peer (pad, p, TRUE);
1846 _ogg_packet_free (p);
1848 /* and free the queued buffers */
1849 g_list_free (pad->map.queued);
1850 pad->map.queued = NULL;
1856 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
1857 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
1866 GST_DEBUG_OBJECT (ogg,
1867 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
1869 GST_DEBUG_OBJECT (ogg,
1870 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
1871 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
1872 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
1874 /* perform the seek */
1875 while (begin < end) {
1878 if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
1881 /* take a (pretty decent) guess, avoiding overflow */
1882 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
1884 bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
1886 if (bisect <= begin)
1888 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
1890 gst_ogg_demux_seek (ogg, bisect);
1892 while (begin < end) {
1895 GST_DEBUG_OBJECT (ogg,
1896 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
1897 ", end %" G_GINT64_FORMAT, bisect, begin, end);
1899 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
1900 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
1903 if (ret == GST_FLOW_LIMIT) {
1904 /* we hit the upper limit, go back a bit */
1905 if (bisect <= begin + 1) {
1906 end = begin; /* found it */
1911 bisect -= CHUNKSIZE;
1912 if (bisect <= begin)
1915 gst_ogg_demux_seek (ogg, bisect);
1917 } else if (ret == GST_FLOW_OK) {
1918 /* found offset of next ogg page */
1920 GstClockTime granuletime;
1923 /* get the granulepos */
1924 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
1926 granulepos = ogg_page_granulepos (&og);
1927 if (granulepos == -1) {
1928 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
1932 /* get the stream */
1933 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
1934 if (pad == NULL || pad->map.is_skeleton)
1937 /* convert granulepos to time */
1938 granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
1940 if (granuletime < pad->start_time)
1943 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
1944 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
1946 granuletime -= pad->start_time;
1947 granuletime += chain->begin_time;
1949 GST_DEBUG_OBJECT (ogg,
1950 "found page with granule %" G_GINT64_FORMAT " and time %"
1951 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
1953 if (granuletime < target) {
1954 best = result; /* raw offset of packet with granulepos */
1955 begin = ogg->offset; /* raw offset of next page */
1956 begintime = granuletime;
1958 bisect = begin; /* *not* begin + 1 */
1960 if (bisect <= begin + 1) {
1961 end = begin; /* found it */
1963 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
1965 bisect -= CHUNKSIZE; /* an endless loop otherwise. */
1966 if (bisect <= begin)
1968 gst_ogg_demux_seek (ogg, bisect);
1971 endtime = granuletime;
1980 GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
1981 gst_ogg_demux_seek (ogg, best);
1989 GST_DEBUG_OBJECT (ogg, "got a seek error");
1995 do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
1996 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
1997 gint64 * p_offset, gint64 * p_timestamp)
2000 guint64 timestamp, offset;
2001 guint64 r_timestamp, r_offset;
2002 gboolean result = FALSE;
2004 target -= begintime;
2009 for (i = 0; i < chain->streams->len; i++) {
2010 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2013 if (gst_ogg_map_search_index (&pad->map, TRUE, ×tamp, &offset)) {
2014 GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
2017 if (r_offset == -1 || offset < r_offset) {
2019 r_timestamp = timestamp;
2026 *p_timestamp = r_timestamp;
2028 *p_offset = r_offset;
2034 * do seek to time @position, return FALSE or chain and TRUE
2037 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
2038 gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
2041 GstOggChain *chain = NULL;
2043 gint64 begintime, endtime;
2044 gint64 target, keytarget;
2049 gint i, pending, len;
2050 gboolean first_parsed_page = TRUE;
2052 position = segment->last_stop;
2054 /* first find the chain to search in */
2055 total = ogg->total_time;
2056 if (ogg->chains->len == 0)
2059 for (i = ogg->chains->len - 1; i >= 0; i--) {
2060 chain = g_array_index (ogg->chains, GstOggChain *, i);
2061 total -= chain->total_time;
2062 if (position >= total)
2066 /* first step, locate page containing the required data */
2067 begin = chain->offset;
2068 end = chain->end_offset;
2069 begintime = chain->begin_time;
2070 endtime = begintime + chain->total_time;
2071 target = position - total + begintime;
2073 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
2077 /* second step: find pages for all streams, we use the keyframe_granule to keep
2078 * track of which ones we saw. If we have seen a page for each stream we can
2079 * calculate the positions of each keyframe. */
2080 GST_DEBUG_OBJECT (ogg, "find keyframes");
2081 len = pending = chain->streams->len;
2083 /* figure out where the keyframes are */
2090 GstClockTime keyframe_time, granule_time;
2092 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2093 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2095 if (ret == GST_FLOW_LIMIT) {
2096 GST_LOG_OBJECT (ogg, "reached limit");
2098 } else if (ret != GST_FLOW_OK)
2101 /* get the stream */
2102 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2106 if (pad->map.is_skeleton)
2109 granulepos = ogg_page_granulepos (&og);
2110 if (granulepos == -1 || granulepos == 0) {
2111 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2115 /* we only do this the first time we pass here */
2116 if (first_parsed_page) {
2117 /* Now that we have a time reference from the page, we can check
2118 * whether all streams still have pages from here on.
2120 * This would be more elegant before the loop, but getting the page from
2121 * there without breaking anything would be more costly */
2122 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2124 for (i = 0; i < len; i++) {
2125 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2128 /* we already know we have at least one page (the current one)
2129 * for this stream */
2132 if (granule_time > stream->map.total_time)
2133 /* we won't encounter any more pages of this stream, so we don't
2134 * try finding a key frame for it */
2137 first_parsed_page = FALSE;
2141 /* in reverse we want to go past the page with the lower timestamp */
2142 if (segment->rate < 0.0) {
2143 /* get time for this pad */
2144 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2147 GST_LOG_OBJECT (ogg,
2148 "looking at page with ts %" GST_TIME_FORMAT ", target %"
2149 GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
2150 GST_TIME_ARGS (target));
2151 if (granule_time < target)
2155 /* we've seen this pad before */
2156 if (pad->keyframe_granule != -1)
2159 /* convert granule of this pad to the granule of the keyframe */
2160 pad->keyframe_granule =
2161 gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
2162 GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
2163 pad->keyframe_granule);
2165 /* get time of the keyframe */
2167 gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
2168 GST_LOG_OBJECT (ogg, "stream %08lx granule time %" GST_TIME_FORMAT,
2169 pad->map.serialno, GST_TIME_ARGS (keyframe_time));
2171 /* collect smallest value */
2172 if (keyframe_time != -1) {
2173 keyframe_time += begintime;
2174 if (keyframe_time < keytarget)
2175 keytarget = keyframe_time;
2184 /* for negative rates we will get to the keyframe backwards */
2185 if (segment->rate < 0.0)
2188 if (keytarget != target) {
2189 GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
2190 GST_TIME_ARGS (keytarget));
2192 /* last step, seek to the location of the keyframe */
2193 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
2197 /* seek back to previous position */
2198 GST_LOG_OBJECT (ogg, "keyframe on target");
2199 gst_ogg_demux_seek (ogg, best);
2204 if (segment->rate > 0.0)
2205 segment->time = keytarget;
2206 segment->last_stop = keytarget - begintime;
2215 GST_DEBUG_OBJECT (ogg, "no chains");
2220 GST_DEBUG_OBJECT (ogg, "got a seek error");
2225 /* does not take ownership of the event */
2227 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
2229 GstOggChain *chain = NULL;
2231 gboolean flush, accurate, keyframe;
2235 GstSeekType cur_type, stop_type;
2242 GST_DEBUG_OBJECT (ogg, "seek with event");
2244 gst_event_parse_seek (event, &rate, &format, &flags,
2245 &cur_type, &cur, &stop_type, &stop);
2247 /* we can only seek on time */
2248 if (format != GST_FORMAT_TIME) {
2249 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
2252 seqnum = gst_event_get_seqnum (event);
2254 GST_DEBUG_OBJECT (ogg, "seek without event");
2258 seqnum = gst_util_seqnum_next ();
2261 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
2263 flush = flags & GST_SEEK_FLAG_FLUSH;
2264 accurate = flags & GST_SEEK_FLAG_ACCURATE;
2265 keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
2267 /* first step is to unlock the streaming thread if it is
2268 * blocked in a chain call, we do this by starting the flush. because
2269 * we cannot yet hold any streaming lock, we have to protect the chains
2270 * with their own lock. */
2274 tevent = gst_event_new_flush_start ();
2275 gst_event_set_seqnum (tevent, seqnum);
2277 gst_event_ref (tevent);
2278 gst_pad_push_event (ogg->sinkpad, tevent);
2280 GST_CHAIN_LOCK (ogg);
2281 for (i = 0; i < ogg->chains->len; i++) {
2282 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2285 for (j = 0; j < chain->streams->len; j++) {
2286 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
2288 gst_event_ref (tevent);
2289 gst_pad_push_event (GST_PAD (pad), tevent);
2292 GST_CHAIN_UNLOCK (ogg);
2294 gst_event_unref (tevent);
2296 gst_pad_pause_task (ogg->sinkpad);
2299 /* now grab the stream lock so that streaming cannot continue, for
2300 * non flushing seeks when the element is in PAUSED this could block
2302 GST_PAD_STREAM_LOCK (ogg->sinkpad);
2304 if (ogg->segment_running && !flush) {
2305 /* create the segment event to close the current segment */
2306 if ((chain = ogg->current_chain)) {
2308 gint64 chain_start = 0;
2310 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2311 chain_start = chain->segment_start;
2313 newseg = gst_event_new_new_segment (TRUE, ogg->segment.rate,
2314 GST_FORMAT_TIME, ogg->segment.start + chain_start,
2315 ogg->segment.last_stop + chain_start, ogg->segment.time);
2316 /* set the seqnum of the running segment */
2317 gst_event_set_seqnum (newseg, ogg->seqnum);
2319 /* send segment on old chain, FIXME, must be sent from streaming thread. */
2320 gst_ogg_demux_send_event (ogg, newseg);
2325 gst_segment_set_seek (&ogg->segment, rate, format, flags,
2326 cur_type, cur, stop_type, stop, &update);
2329 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
2330 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
2331 GST_TIME_ARGS (ogg->segment.stop));
2333 /* we need to stop flushing on the srcpad as we're going to use it
2334 * next. We can do this as we have the STREAM lock now. */
2336 tevent = gst_event_new_flush_stop ();
2337 gst_event_set_seqnum (tevent, seqnum);
2338 gst_pad_push_event (ogg->sinkpad, tevent);
2344 /* reset all ogg streams now, need to do this from within the lock to
2345 * make sure the streaming thread is not messing with the stream */
2346 for (i = 0; i < ogg->chains->len; i++) {
2347 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2349 gst_ogg_chain_reset (chain);
2353 /* for reverse we will already seek accurately */
2354 res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
2356 /* seek failed, make sure we continue the current chain */
2358 GST_DEBUG_OBJECT (ogg, "seek failed");
2359 chain = ogg->current_chain;
2361 GST_DEBUG_OBJECT (ogg, "seek success");
2367 /* now we have a new position, prepare for streaming again */
2372 gint64 last_stop, begin_time;
2374 /* we have to send the flush to the old chain, not the new one */
2376 tevent = gst_event_new_flush_stop ();
2377 gst_event_set_seqnum (tevent, seqnum);
2378 gst_ogg_demux_send_event (ogg, tevent);
2381 /* we need this to see how far inside the chain we need to start */
2382 if (chain->begin_time != GST_CLOCK_TIME_NONE)
2383 begin_time = chain->begin_time;
2387 /* segment.start gives the start over all chains, we calculate the amount
2388 * of time into this chain we need to start */
2389 start = ogg->segment.start - begin_time;
2390 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2391 start += chain->segment_start;
2393 if ((stop = ogg->segment.stop) == -1)
2394 stop = ogg->segment.duration;
2396 /* segment.stop gives the stop time over all chains, calculate the amount of
2397 * time we need to stop in this chain */
2399 if (stop > begin_time)
2403 stop += chain->segment_start;
2404 /* we must stop when this chain ends and switch to the next chain to play
2405 * the remainder of the segment. */
2406 stop = MIN (stop, chain->segment_stop);
2409 last_stop = ogg->segment.last_stop;
2410 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2411 last_stop += chain->segment_start;
2413 /* create the segment event we are going to send out */
2414 if (ogg->segment.rate >= 0.0)
2415 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2416 ogg->segment.format, last_stop, stop, ogg->segment.time);
2418 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2419 ogg->segment.format, start, last_stop, ogg->segment.time);
2421 gst_event_set_seqnum (event, seqnum);
2423 if (chain != ogg->current_chain) {
2424 /* switch to different chain, send segment on new chain */
2425 gst_ogg_demux_activate_chain (ogg, chain, event);
2427 /* mark discont and send segment on current chain */
2428 gst_ogg_chain_mark_discont (chain);
2429 /* This event should be sent from the streaming thread (sink pad task) */
2430 if (ogg->newsegment)
2431 gst_event_unref (ogg->newsegment);
2432 ogg->newsegment = event;
2435 /* notify start of new segment */
2436 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
2437 GstMessage *message;
2439 message = gst_message_new_segment_start (GST_OBJECT (ogg),
2440 GST_FORMAT_TIME, ogg->segment.last_stop);
2441 gst_message_set_seqnum (message, seqnum);
2443 gst_element_post_message (GST_ELEMENT (ogg), message);
2446 ogg->segment_running = TRUE;
2447 ogg->seqnum = seqnum;
2448 /* restart our task since it might have been stopped when we did the
2450 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
2454 /* streaming can continue now */
2455 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
2462 GST_DEBUG_OBJECT (ogg, "seek failed");
2467 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
2468 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
2474 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
2477 gboolean res = TRUE;
2481 GstSeekType start_type, stop_type;
2485 gint64 best, best_time;
2487 gst_event_parse_seek (event, &rate, &format, &flags,
2488 &start_type, &start, &stop_type, &stop);
2490 if (format != GST_FORMAT_TIME) {
2491 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
2495 chain = ogg->current_chain;
2499 if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
2500 /* the index gave some result */
2501 GST_DEBUG_OBJECT (ogg,
2502 "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
2505 } else if ((bitrate = ogg->bitrate) > 0) {
2506 /* try with bitrate convert the seek positions to bytes */
2507 if (start_type != GST_SEEK_TYPE_NONE) {
2508 start = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
2510 if (stop_type != GST_SEEK_TYPE_NONE) {
2511 stop = gst_util_uint64_scale (stop, bitrate, 8 * GST_SECOND);
2519 GST_DEBUG_OBJECT (ogg,
2520 "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, start, stop);
2522 sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
2523 start_type, start, stop_type, stop);
2525 res = gst_pad_push_event (ogg->sinkpad, sevent);
2533 GST_DEBUG_OBJECT (ogg, "seek failed");
2539 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
2543 if (ogg->pullmode) {
2544 res = gst_ogg_demux_perform_seek_pull (ogg, event);
2546 res = gst_ogg_demux_perform_seek_push (ogg, event);
2552 /* finds each bitstream link one at a time using a bisection search
2553 * (has to begin by knowing the offset of the lb's initial page).
2554 * Recurses for each link so it can alloc the link storage after
2555 * finding them all, then unroll and fill the cache at the same time
2557 static GstFlowReturn
2558 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
2559 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
2561 gint64 endsearched = end;
2566 GstOggChain *nextchain;
2568 GST_LOG_OBJECT (ogg,
2569 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
2570 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
2572 /* the below guards against garbage seperating the last and
2573 * first pages of two links. */
2574 while (searched < endsearched) {
2577 if (endsearched - searched < CHUNKSIZE) {
2580 bisect = (searched + endsearched) / 2;
2583 gst_ogg_demux_seek (ogg, bisect);
2584 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
2586 if (ret == GST_FLOW_UNEXPECTED) {
2587 endsearched = bisect;
2588 } else if (ret == GST_FLOW_OK) {
2589 glong serial = ogg_page_serialno (&og);
2591 if (!gst_ogg_chain_has_stream (chain, serial)) {
2592 endsearched = bisect;
2595 searched = offset + og.header_len + og.body_len;
2601 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
2603 chain->end_offset = searched;
2604 ret = gst_ogg_demux_read_end_chain (ogg, chain);
2605 if (ret != GST_FLOW_OK)
2608 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
2610 gst_ogg_demux_seek (ogg, next);
2611 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
2612 if (ret == GST_FLOW_UNEXPECTED) {
2615 GST_LOG_OBJECT (ogg, "no next chain");
2616 } else if (ret != GST_FLOW_OK)
2619 if (searched < end && nextchain != NULL) {
2620 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
2621 end, nextchain, m + 1);
2622 if (ret != GST_FLOW_OK)
2625 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
2627 g_array_insert_val (ogg->chains, 0, chain);
2633 /* read a chain from the ogg file. This code will
2634 * read all BOS pages and will create and return a GstOggChain
2635 * structure with the results.
2637 * This function will also read N pages from each stream in the
2638 * chain and submit them to the decoders. When the decoder has
2639 * decoded the first buffer, we know the timestamp of the first
2640 * page in the chain.
2642 static GstFlowReturn
2643 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
2646 GstOggChain *chain = NULL;
2647 gint64 offset = ogg->offset;
2652 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
2654 /* first read the BOS pages, do typefind on them, create
2655 * the decoders, send data to the decoders. */
2660 ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
2661 if (ret != GST_FLOW_OK) {
2662 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
2665 if (!ogg_page_bos (&op)) {
2666 GST_WARNING_OBJECT (ogg, "page is not BOS page");
2667 /* if we did not find a chain yet, assume this is a bogus stream and
2670 ret = GST_FLOW_UNEXPECTED;
2674 if (chain == NULL) {
2675 chain = gst_ogg_chain_new (ogg);
2676 chain->offset = offset;
2679 serial = ogg_page_serialno (&op);
2680 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
2681 GST_WARNING_OBJECT (ogg, "found serial %08lx BOS page twice, ignoring",
2686 pad = gst_ogg_chain_new_stream (chain, serial);
2687 gst_ogg_pad_submit_page (pad, &op);
2690 if (ret != GST_FLOW_OK || chain == NULL) {
2691 if (ret == GST_FLOW_OK) {
2692 GST_WARNING_OBJECT (ogg, "no chain was found");
2693 ret = GST_FLOW_ERROR;
2694 } else if (ret != GST_FLOW_UNEXPECTED) {
2695 GST_WARNING_OBJECT (ogg, "failed to read chain");
2697 GST_DEBUG_OBJECT (ogg, "done reading chains");
2700 gst_ogg_chain_free (chain);
2707 chain->have_bos = TRUE;
2708 GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
2710 /* now read pages until we receive a buffer from each of the
2711 * stream decoders, this will tell us the timestamp of the
2712 * first packet in the chain then */
2714 /* save the offset to the first non bos page in the chain: if searching for
2715 * pad->first_time we read past the end of the chain, we'll seek back to this
2718 offset = ogg->offset;
2723 gboolean known_serial = FALSE;
2726 serial = ogg_page_serialno (&op);
2728 for (i = 0; i < chain->streams->len; i++) {
2729 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2731 GST_LOG_OBJECT (ogg, "serial %08lx time %" GST_TIME_FORMAT,
2732 pad->map.serialno, GST_TIME_ARGS (pad->start_time));
2734 if (pad->map.serialno == serial) {
2735 known_serial = TRUE;
2737 /* submit the page now, this will fill in the start_time when the
2738 * internal decoder finds it */
2739 gst_ogg_pad_submit_page (pad, &op);
2741 if (!pad->map.is_skeleton && pad->start_time == -1
2742 && ogg_page_eos (&op)) {
2743 /* got EOS on a pad before we could find its start_time.
2744 * We have no chance of finding a start_time for every pad so
2745 * stop searching for the other start_time(s).
2751 /* the timestamp will be filled in when we submit the pages */
2752 if (!pad->map.is_sparse)
2753 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
2755 GST_LOG_OBJECT (ogg, "done %08lx now %d", pad->map.serialno, done);
2758 /* we read a page not belonging to the current chain: seek back to the
2759 * beginning of the chain
2761 if (!known_serial) {
2762 GST_LOG_OBJECT (ogg, "unknown serial %08lx", serial);
2763 gst_ogg_demux_seek (ogg, offset);
2768 ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
2769 if (ret != GST_FLOW_OK)
2773 GST_LOG_OBJECT (ogg, "done reading chain");
2774 /* now we can fill in the missing info using queries */
2775 for (i = 0; i < chain->streams->len; i++) {
2776 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2778 if (pad->map.is_skeleton)
2781 pad->mode = GST_OGG_PAD_MODE_STREAMING;
2790 /* read the last pages from the ogg stream to get the final
2793 static GstFlowReturn
2794 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
2796 gint64 begin = chain->end_offset;
2798 gint64 last_granule = -1;
2799 GstOggPad *last_pad = NULL;
2801 gboolean done = FALSE;
2810 gst_ogg_demux_seek (ogg, begin);
2812 /* now continue reading until we run out of data, if we find a page
2813 * start, we save it. It might not be the final page as there could be
2814 * another page after this one. */
2815 while (ogg->offset < end) {
2816 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
2818 if (ret == GST_FLOW_LIMIT)
2820 if (ret != GST_FLOW_OK)
2823 for (i = 0; i < chain->streams->len; i++) {
2824 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2826 if (pad->map.is_sparse)
2829 if (pad->map.serialno == ogg_page_serialno (&og)) {
2830 gint64 granulepos = ogg_page_granulepos (&og);
2832 if (granulepos != -1) {
2833 last_granule = granulepos;
2844 chain->segment_stop =
2845 gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
2848 chain->segment_stop = GST_CLOCK_TIME_NONE;
2851 GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
2856 /* find a pad with a given serial number
2859 gst_ogg_demux_find_pad (GstOggDemux * ogg, glong serialno)
2864 /* first look in building chain if any */
2865 if (ogg->building_chain) {
2866 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
2871 /* then look in current chain if any */
2872 if (ogg->current_chain) {
2873 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
2878 for (i = 0; i < ogg->chains->len; i++) {
2879 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2881 pad = gst_ogg_chain_get_stream (chain, serialno);
2888 /* find a chain with a given serial number
2890 static GstOggChain *
2891 gst_ogg_demux_find_chain (GstOggDemux * ogg, glong serialno)
2895 pad = gst_ogg_demux_find_pad (ogg, serialno);
2902 /* returns TRUE if all streams have valid start time */
2904 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
2906 gboolean res = TRUE;
2908 chain->total_time = GST_CLOCK_TIME_NONE;
2909 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
2911 /* see if we have a start time on all streams */
2912 chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
2914 if (chain->segment_start == G_MAXUINT64) {
2915 /* not yet, stream some more data */
2917 } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
2918 /* we can calculate a total time */
2919 chain->total_time = chain->segment_stop - chain->segment_start;
2922 GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
2924 GST_DEBUG_OBJECT (ogg, "return %d", res);
2930 gst_ogg_demux_collect_info (GstOggDemux * ogg)
2934 /* collect all info */
2935 ogg->total_time = 0;
2937 for (i = 0; i < ogg->chains->len; i++) {
2938 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2940 chain->begin_time = ogg->total_time;
2942 gst_ogg_demux_collect_chain_info (ogg, chain);
2944 ogg->total_time += chain->total_time;
2946 gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time);
2949 /* find all the chains in the ogg file, this reads the first and
2950 * last page of the ogg stream, if they match then the ogg file has
2951 * just one chain, else we do a binary search for all chains.
2953 static GstFlowReturn
2954 gst_ogg_demux_find_chains (GstOggDemux * ogg)
2964 /* get peer to figure out length */
2965 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
2968 /* find length to read last page, we store this for later use. */
2969 format = GST_FORMAT_BYTES;
2970 res = gst_pad_query_duration (peer, &format, &ogg->length);
2971 gst_object_unref (peer);
2972 if (!res || ogg->length <= 0)
2975 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
2977 /* read chain from offset 0, this is the first chain of the
2979 gst_ogg_demux_seek (ogg, 0);
2980 ret = gst_ogg_demux_read_chain (ogg, &chain);
2981 if (ret != GST_FLOW_OK)
2982 goto no_first_chain;
2984 /* read page from end offset, we use this page to check if its serial
2985 * number is contained in the first chain. If this is the case then
2986 * this ogg is not a chained ogg and we can skip the scanning. */
2987 gst_ogg_demux_seek (ogg, ogg->length);
2988 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
2989 if (ret != GST_FLOW_OK)
2992 serialno = ogg_page_serialno (&og);
2994 if (!gst_ogg_chain_has_stream (chain, serialno)) {
2995 /* the last page is not in the first stream, this means we should
2996 * find all the chains in this chained ogg. */
2998 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
3001 /* we still call this function here but with an empty range so that
3002 * we can reuse the setup code in this routine. */
3004 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
3005 ogg->length, chain, 0);
3007 if (ret != GST_FLOW_OK)
3010 /* all fine, collect and print */
3011 gst_ogg_demux_collect_info (ogg);
3013 /* dump our chains and streams */
3014 gst_ogg_print (ogg);
3019 /*** error cases ***/
3022 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
3023 return GST_FLOW_NOT_LINKED;
3027 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
3028 return GST_FLOW_NOT_SUPPORTED;
3032 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
3033 return GST_FLOW_ERROR;
3037 GST_DEBUG_OBJECT (ogg, "can't get last page");
3039 gst_ogg_chain_free (chain);
3044 static GstFlowReturn
3045 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
3050 GstFlowReturn result = GST_FLOW_OK;
3052 serialno = ogg_page_serialno (page);
3053 granule = ogg_page_granulepos (page);
3055 GST_LOG_OBJECT (ogg,
3056 "processing ogg page (serial %08lx, pageno %ld, granulepos %"
3057 G_GINT64_FORMAT ", bos %d)",
3058 serialno, ogg_page_pageno (page), granule, ogg_page_bos (page));
3060 if (ogg_page_bos (page)) {
3064 /* see if we know about the chain already */
3065 chain = gst_ogg_demux_find_chain (ogg, serialno);
3070 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3071 start = chain->segment_start;
3073 /* create the newsegment event we are going to send out */
3074 event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
3075 GST_FORMAT_TIME, start, chain->segment_stop, chain->begin_time);
3076 gst_event_set_seqnum (event, ogg->seqnum);
3078 GST_DEBUG_OBJECT (ogg,
3079 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
3080 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
3081 GST_TIME_ARGS (chain->segment_stop),
3082 GST_TIME_ARGS (chain->begin_time));
3084 /* activate it as it means we have a non-header, this will also deactivate
3085 * the currently running chain. */
3086 gst_ogg_demux_activate_chain (ogg, chain, event);
3087 pad = gst_ogg_demux_find_pad (ogg, serialno);
3089 GstClockTime chain_time;
3090 gint64 current_time;
3092 /* this can only happen in push mode */
3096 current_time = ogg->segment.last_stop;
3098 /* time of new chain is current time */
3099 chain_time = current_time;
3101 if (ogg->building_chain == NULL) {
3102 GstOggChain *newchain;
3104 newchain = gst_ogg_chain_new (ogg);
3105 newchain->offset = 0;
3106 /* set new chain begin time aligned with end time of old chain */
3107 newchain->begin_time = chain_time;
3108 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
3109 GST_TIME_ARGS (chain_time));
3111 /* and this is the one we are building now */
3112 ogg->building_chain = newchain;
3114 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
3117 pad = gst_ogg_demux_find_pad (ogg, serialno);
3120 result = gst_ogg_pad_submit_page (pad, page);
3122 /* no pad. This means an ogg page without bos has been seen for this
3123 * serialno. we just ignore it but post a warning... */
3124 GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
3125 (NULL), ("unknown ogg pad for serial %08lx detected", serialno));
3133 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
3134 (NULL), ("unknown ogg chain for serial %08lx detected", serialno));
3135 return GST_FLOW_ERROR;
3139 /* streaming mode, receive a buffer, parse it, create pads for
3140 * the serialno, submit pages and packets to the oggpads
3142 static GstFlowReturn
3143 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
3147 GstFlowReturn result = GST_FLOW_OK;
3149 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
3151 GST_DEBUG_OBJECT (ogg, "chain");
3152 result = gst_ogg_demux_submit_buffer (ogg, buffer);
3154 while (result == GST_FLOW_OK) {
3157 ret = ogg_sync_pageout (&ogg->sync, &page);
3159 /* need more data */
3162 /* discontinuity in the pages */
3163 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
3165 result = gst_ogg_demux_handle_page (ogg, &page);
3168 if (ret == 0 || result == GST_FLOW_OK) {
3169 gst_ogg_demux_sync_streams (ogg);
3175 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
3177 GstOggChain *chain = ogg->current_chain;
3178 gboolean res = TRUE;
3183 for (i = 0; i < chain->streams->len; i++) {
3184 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3186 gst_event_ref (event);
3187 GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
3188 res &= gst_pad_push_event (GST_PAD (pad), event);
3191 gst_event_unref (event);
3196 static GstFlowReturn
3197 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
3202 /* store the value */
3203 pad->last_ret = ret;
3205 /* any other error that is not-linked can be returned right
3207 if (ret != GST_FLOW_NOT_LINKED)
3210 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
3211 chain = ogg->current_chain;
3215 for (i = 0; i < chain->streams->len; i++) {
3216 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
3218 ret = opad->last_ret;
3219 /* some other return value (must be SUCCESS but we can return
3220 * other values as well) */
3221 if (ret != GST_FLOW_NOT_LINKED)
3224 /* if we get here, all other pads were unlinked and we return
3225 * NOT_LINKED then */
3231 /* returns TRUE if all streams in current chain reached EOS, FALSE otherwise */
3233 gst_ogg_demux_check_eos (GstOggDemux * ogg)
3236 gboolean eos = TRUE;
3238 chain = ogg->current_chain;
3239 if (G_LIKELY (chain)) {
3242 for (i = 0; i < chain->streams->len; i++) {
3243 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
3245 eos = eos && opad->is_eos;
3254 static GstFlowReturn
3255 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
3260 if (ogg->offset == ogg->length) {
3261 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
3262 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
3263 ret = GST_FLOW_UNEXPECTED;
3267 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
3268 ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
3269 if (ret != GST_FLOW_OK) {
3270 GST_LOG_OBJECT (ogg, "Failed pull_range");
3274 ogg->offset += GST_BUFFER_SIZE (buffer);
3276 if (G_UNLIKELY (ogg->newsegment)) {
3277 gst_ogg_demux_send_event (ogg, ogg->newsegment);
3278 ogg->newsegment = NULL;
3281 ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
3282 if (ret != GST_FLOW_OK) {
3283 GST_LOG_OBJECT (ogg, "Failed demux_chain");
3287 /* check for the end of the segment */
3288 if (gst_ogg_demux_check_eos (ogg)) {
3289 GST_LOG_OBJECT (ogg, "got EOS");
3290 ret = GST_FLOW_UNEXPECTED;
3299 * We read the pages backwards and send the packets forwards. The first packet
3300 * in the page will be pushed with the DISCONT flag set.
3302 * Special care has to be taken for continued pages, which we can only decode
3303 * when we have the previous page(s).
3305 static GstFlowReturn
3306 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
3312 if (ogg->offset == 0) {
3313 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
3314 " == 0", ogg->offset);
3315 ret = GST_FLOW_UNEXPECTED;
3319 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
3320 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
3321 if (ret != GST_FLOW_OK)
3324 ogg->offset = offset;
3326 if (G_UNLIKELY (ogg->newsegment)) {
3327 gst_ogg_demux_send_event (ogg, ogg->newsegment);
3328 ogg->newsegment = NULL;
3331 ret = gst_ogg_demux_handle_page (ogg, &page);
3332 if (ret != GST_FLOW_OK)
3335 /* check for the end of the segment */
3336 if (gst_ogg_demux_check_eos (ogg)) {
3337 GST_LOG_OBJECT (ogg, "got EOS");
3338 ret = GST_FLOW_UNEXPECTED;
3346 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
3352 chain = ogg->current_chain;
3353 cur = ogg->segment.last_stop;
3354 if (chain == NULL || cur == -1)
3357 for (i = 0; i < chain->streams->len; i++) {
3358 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
3360 /* Theoretically, we should be doing this for all streams, but we're only
3361 * doing it for known-to-be-sparse streams at the moment in order not to
3362 * break things for wrongly-muxed streams (like we used to produce once) */
3363 if (stream->map.is_sparse && stream->last_stop != GST_CLOCK_TIME_NONE) {
3365 /* Does this stream lag? Random threshold of 2 seconds */
3366 if (GST_CLOCK_DIFF (stream->last_stop, cur) > (2 * GST_SECOND)) {
3367 GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
3368 "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
3369 GST_TIME_ARGS (stream->last_stop), GST_TIME_ARGS (cur));
3370 stream->last_stop = cur;
3371 /* advance stream time (FIXME: is this right, esp. time_pos?) */
3372 gst_pad_push_event (GST_PAD_CAST (stream),
3373 gst_event_new_new_segment (TRUE, ogg->segment.rate,
3374 GST_FORMAT_TIME, stream->last_stop, -1, stream->last_stop));
3380 /* random access code
3382 * - first find all the chains and streams by scanning the file.
3383 * - then get and chain buffers, just like the streaming case.
3384 * - when seeking, we can use the chain info to perform the seek.
3387 gst_ogg_demux_loop (GstOggPad * pad)
3393 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
3395 if (ogg->need_chains) {
3398 /* this is the only place where we write chains and thus need to lock. */
3399 GST_CHAIN_LOCK (ogg);
3400 ret = gst_ogg_demux_find_chains (ogg);
3401 GST_CHAIN_UNLOCK (ogg);
3402 if (ret != GST_FLOW_OK)
3403 goto chain_read_failed;
3405 ogg->need_chains = FALSE;
3407 GST_OBJECT_LOCK (ogg);
3408 ogg->running = TRUE;
3411 GST_OBJECT_UNLOCK (ogg);
3413 /* and seek to configured positions without FLUSH */
3414 res = gst_ogg_demux_perform_seek_pull (ogg, event);
3416 gst_event_unref (event);
3422 if (ogg->segment.rate >= 0.0)
3423 ret = gst_ogg_demux_loop_forward (ogg);
3425 ret = gst_ogg_demux_loop_reverse (ogg);
3427 if (ret != GST_FLOW_OK)
3430 gst_ogg_demux_sync_streams (ogg);
3436 /* error was posted */
3441 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
3442 ("failed to start demuxing ogg"));
3443 ret = GST_FLOW_ERROR;
3448 const gchar *reason = gst_flow_get_name (ret);
3449 GstEvent *event = NULL;
3451 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
3452 ogg->segment_running = FALSE;
3453 gst_pad_pause_task (ogg->sinkpad);
3455 if (ret == GST_FLOW_UNEXPECTED) {
3456 /* perform EOS logic */
3457 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3459 GstMessage *message;
3461 /* for segment playback we need to post when (in stream time)
3462 * we stopped, this is either stop (when set) or the duration. */
3463 if ((stop = ogg->segment.stop) == -1)
3464 stop = ogg->segment.duration;
3466 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
3468 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
3470 gst_message_set_seqnum (message, ogg->seqnum);
3472 gst_element_post_message (GST_ELEMENT (ogg), message);
3474 /* normal playback, send EOS to all linked pads */
3475 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
3476 event = gst_event_new_eos ();
3478 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) {
3479 GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
3480 (_("Internal data stream error.")),
3481 ("stream stopped, reason %s", reason));
3482 event = gst_event_new_eos ();
3485 /* For wrong-state we still want to pause the task and stop
3486 * but no error message or other things are necessary.
3487 * wrong-state is no real error and will be caused by flushing,
3488 * e.g. because of a flushing seek.
3491 gst_event_set_seqnum (event, ogg->seqnum);
3492 gst_ogg_demux_send_event (ogg, event);
3499 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
3503 gst_ogg_demux_deactivate_current_chain (ogg);
3505 GST_CHAIN_LOCK (ogg);
3506 for (i = 0; i < ogg->chains->len; i++) {
3507 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3509 gst_ogg_chain_free (chain);
3511 ogg->chains = g_array_set_size (ogg->chains, 0);
3512 GST_CHAIN_UNLOCK (ogg);
3515 /* this function is called when the pad is activated and should start
3518 * We check if we can do random access to decide if we work push or
3522 gst_ogg_demux_sink_activate (GstPad * sinkpad)
3524 if (gst_pad_check_pull_range (sinkpad)) {
3525 GST_DEBUG_OBJECT (sinkpad, "activating pull");
3526 return gst_pad_activate_pull (sinkpad, TRUE);
3528 GST_DEBUG_OBJECT (sinkpad, "activating push");
3529 return gst_pad_activate_push (sinkpad, TRUE);
3533 /* this function gets called when we activate ourselves in push mode.
3534 * We cannot seek (ourselves) in the stream */
3536 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
3540 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
3542 ogg->pullmode = FALSE;
3543 ogg->resync = FALSE;
3548 /* this function gets called when we activate ourselves in pull mode.
3549 * We can perform random access to the resource and we start a task
3550 * to start reading */
3552 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
3556 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
3559 ogg->need_chains = TRUE;
3560 ogg->pullmode = TRUE;
3562 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3565 return gst_pad_stop_task (sinkpad);
3569 static GstStateChangeReturn
3570 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
3573 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
3575 ogg = GST_OGG_DEMUX (element);
3577 switch (transition) {
3578 case GST_STATE_CHANGE_NULL_TO_READY:
3580 ogg_sync_init (&ogg->sync);
3582 case GST_STATE_CHANGE_READY_TO_PAUSED:
3583 ogg_sync_reset (&ogg->sync);
3584 ogg->running = FALSE;
3586 ogg->segment_running = FALSE;
3587 ogg->total_time = -1;
3588 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
3590 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3596 result = parent_class->change_state (element, transition);
3598 switch (transition) {
3599 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3601 case GST_STATE_CHANGE_PAUSED_TO_READY:
3602 gst_ogg_demux_clear_chains (ogg);
3603 GST_OBJECT_LOCK (ogg);
3604 ogg->running = FALSE;
3605 ogg->segment_running = FALSE;
3606 GST_OBJECT_UNLOCK (ogg);
3608 case GST_STATE_CHANGE_READY_TO_NULL:
3609 ogg_sync_clear (&ogg->sync);
3618 gst_ogg_demux_plugin_init (GstPlugin * plugin)
3620 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
3621 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
3622 "ogg demuxer setup stage when parsing pipeline");
3625 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
3627 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
3628 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3631 return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
3632 GST_TYPE_OGG_DEMUX);
3635 /* prints all info about the element */
3636 #undef GST_CAT_DEFAULT
3637 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
3639 #ifdef GST_DISABLE_GST_DEBUG
3642 gst_ogg_print (GstOggDemux * ogg)
3647 #else /* !GST_DISABLE_GST_DEBUG */
3650 gst_ogg_print (GstOggDemux * ogg)
3654 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
3655 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
3656 GST_TIME_ARGS (ogg->total_time));
3658 for (i = 0; i < ogg->chains->len; i++) {
3659 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3661 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
3662 GST_INFO_OBJECT (ogg,
3663 " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
3665 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
3666 GST_TIME_ARGS (chain->begin_time));
3667 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
3668 GST_TIME_ARGS (chain->total_time));
3669 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
3670 GST_TIME_ARGS (chain->segment_start));
3671 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
3672 GST_TIME_ARGS (chain->segment_stop));
3674 for (j = 0; j < chain->streams->len; j++) {
3675 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
3677 GST_INFO_OBJECT (ogg, " stream %08lx:", stream->map.serialno);
3678 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
3679 GST_TIME_ARGS (stream->start_time));
3683 #endif /* GST_DISABLE_GST_DEBUG */