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 gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
135 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
136 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
139 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
140 GstOggPad * pad, GstFlowReturn ret);
141 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
143 GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg,
144 GstCaps * caps, GList * headers);
145 static gboolean gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
146 static gboolean gst_ogg_demux_perform_seek_push (GstOggDemux * ogg,
148 static gboolean gst_ogg_demux_check_duration_push (GstOggDemux * ogg,
149 GstSeekFlags flags, GstEvent * event);
151 GType gst_ogg_pad_get_type (void);
152 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
155 gst_ogg_pad_class_init (GstOggPadClass * klass)
157 GObjectClass *gobject_class;
159 gobject_class = (GObjectClass *) klass;
161 gobject_class->dispose = gst_ogg_pad_dispose;
162 gobject_class->finalize = gst_ogg_pad_finalize;
166 gst_ogg_pad_init (GstOggPad * pad)
168 gst_pad_set_event_function (GST_PAD (pad),
169 GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
170 gst_pad_set_query_function (GST_PAD (pad),
171 GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
172 gst_pad_use_fixed_caps (GST_PAD (pad));
174 pad->mode = GST_OGG_PAD_MODE_INIT;
176 pad->current_granule = -1;
177 pad->keyframe_granule = -1;
179 pad->start_time = GST_CLOCK_TIME_NONE;
181 pad->position = GST_CLOCK_TIME_NONE;
183 pad->have_type = FALSE;
184 pad->continued = NULL;
185 pad->map.headers = NULL;
186 pad->map.queued = NULL;
188 pad->map.granulerate_n = 0;
189 pad->map.granulerate_d = 0;
190 pad->map.granuleshift = -1;
194 gst_ogg_pad_dispose (GObject * object)
196 GstOggPad *pad = GST_OGG_PAD (object);
201 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
202 g_list_free (pad->map.headers);
203 pad->map.headers = NULL;
204 g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
205 g_list_free (pad->map.queued);
206 pad->map.queued = NULL;
208 g_free (pad->map.index);
209 pad->map.index = NULL;
211 /* clear continued pages */
212 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
213 g_list_free (pad->continued);
214 pad->continued = NULL;
217 gst_caps_unref (pad->map.caps);
218 pad->map.caps = NULL;
221 if (pad->map.taglist) {
222 gst_tag_list_free (pad->map.taglist);
223 pad->map.taglist = NULL;
226 ogg_stream_reset (&pad->map.stream);
228 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
232 gst_ogg_pad_finalize (GObject * object)
234 GstOggPad *pad = GST_OGG_PAD (object);
236 ogg_stream_clear (&pad->map.stream);
238 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
242 gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
247 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
249 switch (GST_QUERY_TYPE (query)) {
250 case GST_QUERY_DURATION:
253 gint64 total_time = -1;
255 gst_query_parse_duration (query, &format, NULL);
256 /* can only get position in time */
257 if (format != GST_FORMAT_TIME)
260 if (ogg->total_time != -1) {
261 /* we can return the total length */
262 total_time = ogg->total_time;
264 gint bitrate = ogg->bitrate;
266 /* try with length and bitrate */
270 /* ask upstream for total length in bytes */
271 uquery = gst_query_new_duration (GST_FORMAT_BYTES);
272 if (gst_pad_peer_query (ogg->sinkpad, uquery)) {
275 gst_query_parse_duration (uquery, NULL, &length);
277 /* estimate using the bitrate */
279 gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);
282 "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
283 GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
285 gst_query_unref (uquery);
289 gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
292 case GST_QUERY_SEEKING:
296 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
297 if (format == GST_FORMAT_TIME) {
298 gboolean seekable = FALSE;
303 stop = ogg->total_time;
304 } else if (ogg->current_chain->streams->len) {
308 for (i = 0; i < ogg->current_chain->streams->len; i++) {
310 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
313 if (pad->map.index != NULL && pad->map.n_index != 0) {
315 GstClockTime idx_time;
317 idx = &pad->map.index[pad->map.n_index - 1];
319 gst_util_uint64_scale (idx->timestamp, GST_SECOND,
324 stop = MAX (idx_time, stop);
326 stop = -1; /* we've no clue, sadly, without seeking */
331 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
339 res = gst_pad_query_default (pad, query);
343 gst_object_unref (ogg);
350 GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
357 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
362 ogg = GST_OGG_DEMUX (element);
364 switch (GST_EVENT_TYPE (event)) {
366 /* now do the seek */
367 res = gst_ogg_demux_perform_seek (ogg, event);
368 gst_event_unref (event);
371 GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
380 GST_DEBUG_OBJECT (ogg, "error handling event");
381 gst_event_unref (event);
387 gst_ogg_pad_event (GstPad * pad, GstEvent * event)
392 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
394 switch (GST_EVENT_TYPE (event)) {
396 /* now do the seek */
397 res = gst_ogg_demux_perform_seek (ogg, event);
398 gst_event_unref (event);
401 res = gst_pad_event_default (pad, event);
404 gst_object_unref (ogg);
410 gst_ogg_pad_reset (GstOggPad * pad)
412 ogg_stream_reset (&pad->map.stream);
414 GST_DEBUG_OBJECT (pad, "doing reset");
416 /* clear continued pages */
417 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
418 g_list_free (pad->continued);
419 pad->continued = NULL;
421 pad->last_ret = GST_FLOW_OK;
422 pad->position = GST_CLOCK_TIME_NONE;
423 pad->current_granule = -1;
424 pad->keyframe_granule = -1;
428 /* queue data, basically takes the packet, puts it in a buffer and store the
429 * buffer in the queued list. */
431 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
433 #ifndef GST_DISABLE_GST_DEBUG
434 GstOggDemux *ogg = pad->ogg;
437 GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x",
438 pad, pad->map.serialno);
440 pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
447 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
448 gboolean push_headers)
450 GstBuffer *buf = NULL;
451 GstFlowReturn ret, cret;
452 GstOggDemux *ogg = pad->ogg;
458 GstClockTime out_timestamp, out_duration;
459 guint64 out_offset, out_offset_end;
460 gboolean delta_unit = FALSE;
465 if (!ogg->pullmode && ogg->push_state == PUSH_PLAYING
466 && ogg->push_time_length == GST_CLOCK_TIME_NONE
467 && !ogg->push_disable_seeking) {
468 if (!ogg->building_chain) {
469 /* we got all headers, now try to get duration */
470 if (!gst_ogg_demux_check_duration_push (ogg, GST_SEEK_FLAG_FLUSH, NULL)) {
471 GST_PUSH_UNLOCK (ogg);
475 GST_PUSH_UNLOCK (ogg);
478 GST_PUSH_UNLOCK (ogg);
480 GST_DEBUG_OBJECT (ogg,
481 "%p streaming to peer serial %08x", pad, pad->map.serialno);
483 if (pad->map.is_ogm) {
487 data = packet->packet;
488 bytes = packet->bytes;
493 if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
494 /* We don't push header packets for OGM */
498 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
499 delta_unit = (((data[0] & 0x08) >> 3) == 0);
503 /* Strip trailing \0 for subtitles */
504 if (pad->map.is_ogm_text) {
505 while (bytes && data[bytes - 1] == 0) {
510 } else if (pad->map.is_vp8) {
511 if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
513 (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) {
514 /* We don't push header packets for VP8 */
524 /* get timing info for the packet */
525 if (gst_ogg_stream_packet_is_header (&pad->map, packet)) {
527 GST_DEBUG_OBJECT (ogg, "packet is header");
529 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
530 GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
534 out_timestamp = GST_CLOCK_TIME_NONE;
535 out_duration = GST_CLOCK_TIME_NONE;
539 if (packet->granulepos != -1) {
540 pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
542 pad->keyframe_granule =
543 gst_ogg_stream_granulepos_to_key_granule (&pad->map,
545 GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
546 pad->current_granule);
547 } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
548 pad->current_granule += duration;
549 GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT,
550 pad->current_granule);
552 if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
553 /* negative rates, only set timestamp on the packets with a granulepos */
559 /* we only push buffers after we have a valid granule. This is done so that
560 * we nicely skip packets without a timestamp after a seek. This is ok
561 * because we base or seek on the packet after the page with the smaller
563 if (pad->current_granule == -1)
566 if (pad->map.is_ogm) {
567 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
568 pad->current_granule);
569 out_duration = gst_util_uint64_scale (duration,
570 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
571 } else if (pad->map.is_sparse) {
572 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
573 pad->current_granule);
574 if (duration == GST_CLOCK_TIME_NONE) {
575 out_duration = GST_CLOCK_TIME_NONE;
577 out_duration = gst_util_uint64_scale (duration,
578 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
581 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
582 pad->current_granule - duration);
584 gst_ogg_stream_granule_to_time (&pad->map,
585 pad->current_granule) - out_timestamp;
588 gst_ogg_stream_granule_to_granulepos (&pad->map,
589 pad->current_granule, pad->keyframe_granule);
591 gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
595 if (pad->map.is_ogm_text) {
596 /* check for invalid buffer sizes */
597 if (G_UNLIKELY (offset + trim >= packet->bytes))
604 buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
606 /* set delta flag for OGM content */
608 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
610 /* copy packet in buffer */
611 gst_buffer_fill (buf, 0, packet->packet + offset,
612 packet->bytes - offset - trim);
614 GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
615 GST_BUFFER_DURATION (buf) = out_duration;
616 GST_BUFFER_OFFSET (buf) = out_offset;
617 GST_BUFFER_OFFSET_END (buf) = out_offset_end;
619 /* Mark discont on the buffer */
621 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
622 pad->discont = FALSE;
625 pad->position = ogg->segment.position;
627 /* don't push the header packets when we are asked to skip them */
628 if (!packet->b_o_s || push_headers) {
629 ret = gst_pad_push (GST_PAD_CAST (pad), buf);
633 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
636 /* we're done with skeleton stuff */
637 if (pad->map.is_skeleton)
640 /* check if valid granulepos, then we can calculate the current
641 * position. We know the granule for each packet but we only want to update
642 * the position when we have a valid granulepos on the packet because else
643 * our time jumps around for the different streams. */
644 if (packet->granulepos < 0)
647 /* convert to time */
648 current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
651 /* convert to stream time */
652 if ((chain = pad->chain)) {
653 gint64 chain_start = 0;
655 if (chain->segment_start != GST_CLOCK_TIME_NONE)
656 chain_start = chain->segment_start;
658 current_time = current_time - chain_start + chain->begin_time;
661 /* and store as the current position */
662 ogg->segment.position = current_time;
664 GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
665 GST_TIME_ARGS (current_time));
667 /* check stream eos */
668 if ((ogg->segment.rate > 0.0 && ogg->segment.stop != GST_CLOCK_TIME_NONE &&
669 current_time > ogg->segment.stop) ||
670 (ogg->segment.rate < 0.0 && ogg->segment.start != GST_CLOCK_TIME_NONE &&
671 current_time < ogg->segment.start)) {
672 GST_DEBUG_OBJECT (ogg, "marking pad %p EOS", pad);
678 gst_buffer_unref (buf);
679 /* return combined flow result */
685 GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
691 GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
696 GST_DEBUG_OBJECT (ogg, "pad not added yet");
702 gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
705 guint64 start_time = G_MAXUINT64;
707 for (i = 0; i < chain->streams->len; i++) {
708 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
710 if (pad->map.is_skeleton)
713 /* can do this if the pad start time is not defined */
714 GST_DEBUG_OBJECT (ogg, "Pad %08x (%s) start time is %" GST_TIME_FORMAT,
715 pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map),
716 GST_TIME_ARGS (pad->start_time));
717 if (pad->start_time == GST_CLOCK_TIME_NONE) {
718 if (!pad->map.is_sparse) {
719 start_time = G_MAXUINT64;
723 start_time = MIN (start_time, pad->start_time);
730 gst_ogg_demux_collect_sync_time (GstOggDemux * ogg, GstOggChain * chain)
733 GstClockTime sync_time = GST_CLOCK_TIME_NONE;
736 GST_WARNING_OBJECT (ogg, "No chain!");
737 return GST_CLOCK_TIME_NONE;
740 for (i = 0; i < chain->streams->len; i++) {
741 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
743 if (pad->map.is_sparse)
746 if (pad->push_sync_time == GST_CLOCK_TIME_NONE) {
747 sync_time = GST_CLOCK_TIME_NONE;
750 if (sync_time == GST_CLOCK_TIME_NONE)
751 sync_time = pad->push_sync_time;
753 sync_time = MAX (sync_time, pad->push_sync_time);
759 /* submit a packet to the oggpad, this function will run the
760 * typefind code for the pad if this is the first packet for this
764 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
767 GstFlowReturn ret = GST_FLOW_OK;
769 GstOggDemux *ogg = pad->ogg;
771 GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x",
772 pad, pad->map.serialno);
774 if (!pad->have_type) {
775 pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
776 if (!pad->have_type) {
777 pad->map.caps = gst_caps_new_empty_simple ("application/x-unknown");
779 if (pad->map.is_skeleton) {
780 GST_DEBUG_OBJECT (ogg, "we have a fishead");
781 /* copy values over to global ogg level */
782 ogg->basetime = pad->map.basetime;
783 ogg->prestime = pad->map.prestime;
785 /* use total time to update the total ogg time */
786 if (ogg->total_time == -1) {
787 ogg->total_time = pad->map.total_time;
788 } else if (pad->map.total_time > 0) {
789 ogg->total_time = MAX (ogg->total_time, pad->map.total_time);
793 gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
795 GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
799 if (pad->map.is_skeleton) {
804 /* try to parse the serialno first */
805 if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
808 GST_DEBUG_OBJECT (pad->ogg,
809 "got skeleton packet for stream 0x%08x", serialno);
811 skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
814 case GST_OGG_SKELETON_FISBONE:
815 /* parse the remainder of the fisbone in the pad with the serialno,
816 * note that we ignore the start_time as this is usually wrong for
818 gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
819 packet->bytes, NULL);
821 case GST_OGG_SKELETON_INDEX:
822 gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
825 /* use total time to update the total ogg time */
826 if (ogg->total_time == -1) {
827 ogg->total_time = skel_pad->map.total_time;
828 } else if (skel_pad->map.total_time > 0) {
829 ogg->total_time = MAX (ogg->total_time, skel_pad->map.total_time);
837 GST_WARNING_OBJECT (pad->ogg,
838 "found skeleton fisbone for an unknown stream 0x%08x", serialno);
843 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
846 GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
847 pad->current_granule = granule;
850 /* restart header packet count when seeing a b_o_s page;
851 * particularly useful following a seek or even following chain finding */
853 GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
854 pad->map.n_header_packets_seen = 0;
855 if (!pad->map.have_headers) {
856 GST_DEBUG_OBJECT (ogg, "clearing header packets");
857 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
858 g_list_free (pad->map.headers);
859 pad->map.headers = NULL;
863 /* Overload the value of b_o_s in ogg_packet with a flag whether or
864 * not this is a header packet. Maybe some day this could be cleaned
866 packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
867 if (!packet->b_o_s) {
868 GST_DEBUG ("found non-header packet");
869 pad->map.have_headers = TRUE;
870 if (pad->start_time == GST_CLOCK_TIME_NONE) {
871 gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
872 GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
873 if (duration != -1) {
874 pad->map.accumulated_granule += duration;
875 GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
876 pad->map.accumulated_granule);
879 if (packet->granulepos != -1) {
880 ogg_int64_t start_granule;
883 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
886 if (granule > pad->map.accumulated_granule)
887 start_granule = granule - pad->map.accumulated_granule;
891 pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
893 GST_DEBUG_OBJECT (ogg,
894 "start time %" GST_TIME_FORMAT " (%" GST_TIME_FORMAT ") for %s",
895 GST_TIME_ARGS (pad->start_time), GST_TIME_ARGS (pad->start_time),
896 gst_ogg_stream_get_media_type (&pad->map));
898 packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
899 pad->map.accumulated_granule, pad->keyframe_granule);
903 /* look for tags in header packet (before inc header count) */
904 gst_ogg_stream_extract_tags (&pad->map, packet);
905 pad->map.n_header_packets_seen++;
906 if (!pad->map.have_headers) {
908 g_list_append (pad->map.headers, _ogg_packet_copy (packet));
909 GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
913 /* we know the start_time of the pad data, see if we
914 * can activate the complete chain if this is a dynamic
915 * chain. We need all the headers too for this. */
916 if (pad->start_time != GST_CLOCK_TIME_NONE && pad->map.have_headers) {
917 GstOggChain *chain = pad->chain;
919 /* check if complete chain has start time */
920 if (chain == ogg->building_chain) {
921 GstEvent *event = NULL;
926 GST_DEBUG_OBJECT (ogg, "need to resync");
928 /* when we need to resync after a seek, we wait until we have received
929 * timestamps on all streams */
930 start_time = gst_ogg_demux_collect_start_time (ogg, chain);
932 if (start_time != G_MAXUINT64) {
936 GST_DEBUG_OBJECT (ogg, "start_time: %" GST_TIME_FORMAT,
937 GST_TIME_ARGS (start_time));
939 if (chain->segment_start < start_time)
941 (start_time - chain->segment_start) + chain->begin_time;
943 segment_time = chain->begin_time;
945 /* create the newsegment event we are going to send out */
946 gst_segment_init (&segment, GST_FORMAT_TIME);
949 if (!ogg->pullmode && ogg->push_state == PUSH_LINEAR2) {
950 /* if we are fast forwarding to the actual seek target,
951 ensure previous frames are clipped */
952 GST_DEBUG_OBJECT (ogg,
953 "Resynced, starting segment at %" GST_TIME_FORMAT
954 ", start_time %" GST_TIME_FORMAT,
955 GST_TIME_ARGS (ogg->push_seek_time_original_target),
956 GST_TIME_ARGS (start_time));
957 segment.rate = ogg->push_seek_rate;
958 segment.start = ogg->push_seek_time_original_target;
960 segment.time = ogg->push_seek_time_original_target;
961 event = gst_event_new_segment (&segment);
962 ogg->push_state = PUSH_PLAYING;
964 segment.rate = ogg->segment.rate;
965 segment.applied_rate = ogg->segment.applied_rate;
966 segment.start = start_time;
967 segment.stop = chain->segment_stop;
968 segment.time = segment_time;
969 event = gst_event_new_segment (&segment);
971 GST_PUSH_UNLOCK (ogg);
976 /* see if we have enough info to activate the chain, we have enough info
977 * when all streams have a valid start time. */
978 if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
981 GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
982 GST_TIME_ARGS (chain->segment_start));
983 GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT,
984 GST_TIME_ARGS (chain->segment_stop));
985 GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT,
986 GST_TIME_ARGS (chain->begin_time));
988 /* create the newsegment event we are going to send out */
989 gst_segment_init (&segment, GST_FORMAT_TIME);
990 segment.rate = ogg->segment.rate;
991 segment.applied_rate = ogg->segment.applied_rate;
992 segment.start = chain->segment_start;
993 segment.stop = chain->segment_stop;
994 segment.time = chain->begin_time;
995 event = gst_event_new_segment (&segment);
1000 gst_event_set_seqnum (event, ogg->seqnum);
1002 gst_ogg_demux_activate_chain (ogg, chain, event);
1004 ogg->building_chain = NULL;
1009 /* if we are building a chain, store buffer for when we activate
1010 * it. This path is taken if we operate in streaming mode. */
1011 if (ogg->building_chain) {
1012 /* bos packets where stored in the header list so we can discard
1015 ret = gst_ogg_demux_queue_data (pad, packet);
1017 /* else we are completely streaming to the peer */
1019 ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
1024 /* flush at most @npackets from the stream layer. All packets if
1027 static GstFlowReturn
1028 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1030 GstFlowReturn result = GST_FLOW_OK;
1031 gboolean done = FALSE;
1040 ret = ogg_stream_packetout (&pad->map.stream, &packet);
1043 GST_LOG_OBJECT (ogg, "packetout done");
1047 GST_LOG_OBJECT (ogg, "packetout discont");
1048 if (!pad->map.is_sparse) {
1049 gst_ogg_chain_mark_discont (pad->chain);
1051 gst_ogg_pad_mark_discont (pad);
1055 GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1056 result = gst_ogg_pad_submit_packet (pad, &packet);
1057 /* not linked is not a problem, it's possible that we are still
1058 * collecting headers and that we don't have exposed the pads yet */
1059 if (result == GST_FLOW_NOT_LINKED)
1061 else if (result <= GST_FLOW_EOS)
1062 goto could_not_submit;
1065 GST_WARNING_OBJECT (ogg,
1066 "invalid return value %d for ogg_stream_packetout, resetting stream",
1068 gst_ogg_pad_reset (pad);
1073 done = (npackets == 0);
1081 GST_WARNING_OBJECT (ogg,
1082 "could not submit packet for stream %08x, "
1083 "error: %d", pad->map.serialno, result);
1084 gst_ogg_pad_reset (pad);
1090 gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg)
1092 if (ogg->push_last_seek_time >= ogg->push_seek_time_target) {
1093 GST_DEBUG_OBJECT (ogg, "We overshot by %" GST_TIME_FORMAT,
1094 GST_TIME_ARGS (ogg->push_last_seek_time - ogg->push_seek_time_target));
1095 ogg->push_offset1 = ogg->push_last_seek_offset;
1096 ogg->push_time1 = ogg->push_last_seek_time;
1098 GST_DEBUG_OBJECT (ogg, "We undershot by %" GST_TIME_FORMAT,
1099 GST_TIME_ARGS (ogg->push_seek_time_target - ogg->push_last_seek_time));
1100 ogg->push_offset0 = ogg->push_last_seek_offset;
1101 ogg->push_time0 = ogg->push_last_seek_time;
1106 gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg)
1109 gint64 segment_bitrate;
1111 /* we might not know the length of the stream in time,
1112 so push_time1 might not be set */
1113 GST_DEBUG_OBJECT (ogg,
1114 "push time 1: %" GST_TIME_FORMAT ", dbytes %" G_GINT64_FORMAT,
1115 GST_TIME_ARGS (ogg->push_time1), ogg->push_offset1 - ogg->push_offset0);
1116 if (ogg->push_time1 == GST_CLOCK_TIME_NONE) {
1117 GST_DEBUG_OBJECT (ogg,
1118 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1119 ", time %" GST_TIME_FORMAT " (open ended)", ogg->push_offset0,
1120 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0));
1121 if (ogg->push_last_seek_time == ogg->push_start_time) {
1122 /* if we're at start and don't know the end time, we can't estimate
1123 bitrate, so get the nominal declared bitrate as a failsafe, or some
1124 random constant which will be discarded after we made a (probably
1125 dire) first guess */
1126 segment_bitrate = (ogg->bitrate > 0 ? ogg->bitrate : 1000);
1129 gst_util_uint64_scale (ogg->push_last_seek_offset - 0,
1130 8 * GST_SECOND, ogg->push_last_seek_time - ogg->push_start_time);
1134 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1135 segment_bitrate, 8 * GST_SECOND);
1137 GST_DEBUG_OBJECT (ogg,
1138 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1139 ", time %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, ogg->push_offset0,
1140 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
1141 GST_TIME_ARGS (ogg->push_time1));
1142 if (ogg->push_time0 == ogg->push_time1) {
1143 best = ogg->push_offset0;
1146 gst_util_uint64_scale (ogg->push_offset1 - ogg->push_offset0,
1147 8 * GST_SECOND, ogg->push_time1 - ogg->push_time0);
1148 GST_DEBUG_OBJECT (ogg,
1149 "Local bitrate on the %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1150 " segment: %" G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_time0),
1151 GST_TIME_ARGS (ogg->push_time1), segment_bitrate);
1154 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1155 segment_bitrate, 8 * GST_SECOND);
1159 /* offset by typical page size */
1161 if (best < ogg->push_offset0)
1162 best = ogg->push_offset0;
1170 gst_ogg_demux_record_keyframe_time (GstOggDemux * ogg, GstOggPad * pad,
1171 ogg_int64_t granpos)
1174 GstClockTime kf_time;
1176 kf_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, granpos);
1177 kf_time = gst_ogg_stream_granule_to_time (&pad->map, kf_granule);
1179 pad->push_kf_time = kf_time;
1182 /* returns the earliest keyframe time for all non sparse pads in the chain,
1183 * if known, and GST_CLOCK_TIME_NONE if not */
1185 gst_ogg_demux_get_earliest_keyframe_time (GstOggDemux * ogg)
1187 GstClockTime t = GST_CLOCK_TIME_NONE;
1188 GstOggChain *chain = ogg->building_chain;
1192 GST_WARNING_OBJECT (ogg, "No chain!");
1193 return GST_CLOCK_TIME_NONE;
1195 for (i = 0; i < chain->streams->len; i++) {
1196 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1198 if (pad->map.is_sparse)
1200 if (pad->push_kf_time == GST_CLOCK_TIME_NONE)
1201 return GST_CLOCK_TIME_NONE;
1202 if (t == GST_CLOCK_TIME_NONE || pad->push_kf_time < t)
1203 t = pad->push_kf_time;
1209 /* MUST be called with the push lock locked, and will unlock it
1210 regardless of return value. */
1211 static GstFlowReturn
1212 gst_ogg_demux_seek_back_after_push_duration_check_unlock (GstOggDemux * ogg)
1216 /* Get the delayed event, if any */
1217 event = ogg->push_mode_seek_delayed_event;
1218 ogg->push_mode_seek_delayed_event = NULL;
1220 ogg->push_state = PUSH_PLAYING;
1222 GST_PUSH_UNLOCK (ogg);
1225 /* If there is one, perform it */
1226 gst_ogg_demux_perform_seek_push (ogg, event);
1228 /* If there wasn't, seek back at start to start normal playback */
1229 GST_INFO_OBJECT (ogg, "Seeking back to 0 after duration check");
1230 event = gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1231 GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
1232 GST_SEEK_TYPE_SET, 1, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
1233 if (!gst_pad_push_event (ogg->sinkpad, event)) {
1234 GST_WARNING_OBJECT (ogg, "Failed seeking back to start");
1235 return GST_FLOW_ERROR;
1243 gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
1245 GstOggDemux *ogg = pad->ogg;
1246 ogg_int64_t granpos = ogg_page_granulepos (page);
1248 GST_PUSH_LOCK (ogg);
1250 if (ogg->push_start_time == GST_CLOCK_TIME_NONE) {
1251 ogg->push_start_time =
1252 gst_ogg_stream_get_start_time_for_granulepos (&pad->map, granpos);
1253 GST_DEBUG_OBJECT (ogg, "Stream start time: %" GST_TIME_FORMAT,
1254 GST_TIME_ARGS (ogg->push_start_time));
1256 ogg->push_time_offset =
1257 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1258 if (ogg->push_time_offset > 0) {
1259 GST_DEBUG_OBJECT (ogg, "Bitrate since start: %" G_GUINT64_FORMAT,
1260 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
1261 ogg->push_time_offset));
1264 if (ogg->push_state == PUSH_DURATION) {
1266 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1268 if (ogg->total_time == GST_CLOCK_TIME_NONE || t > ogg->total_time) {
1269 GST_DEBUG_OBJECT (ogg, "New total time: %" GST_TIME_FORMAT,
1271 ogg->total_time = t;
1272 ogg->push_time_length = t;
1275 /* If we were determining the duration of the stream, we're now done,
1276 and can get back to sending the original event we delayed.
1277 We stop a bit before the end of the stream, as if we get a EOS
1278 event and there is a queue2 upstream (such as when using playbin2),
1279 it will pause the task *after* we come back from the EOS handler,
1280 so we cannot prevent the pausing by issuing a seek. */
1281 if (ogg->push_byte_offset + EOS_AVOIDANCE_THRESHOLD >=
1282 ogg->push_byte_length) {
1283 GstMessage *message;
1286 /* tell the pipeline we've just found out the duration */
1287 ogg->push_time_length = ogg->total_time;
1288 GST_INFO_OBJECT (ogg, "New duration found: %" GST_TIME_FORMAT,
1289 GST_TIME_ARGS (ogg->total_time));
1291 gst_message_new_duration (GST_OBJECT (ogg), GST_FORMAT_TIME,
1293 gst_element_post_message (GST_ELEMENT (ogg), message);
1295 GST_DEBUG_OBJECT (ogg,
1296 "We're close enough to the end, and we're scared "
1297 "to get too close, seeking back to start");
1299 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1300 if (res != GST_FLOW_OK)
1302 return GST_FLOW_SKIP_PUSH;
1304 GST_PUSH_UNLOCK (ogg);
1306 return GST_FLOW_SKIP_PUSH;
1310 /* if we're seeking, look at time, and decide what to do */
1311 if (ogg->push_state != PUSH_PLAYING && ogg->push_state != PUSH_LINEAR2) {
1316 gboolean close_enough;
1318 /* ignore -1 granpos when seeking, we want to sync on a real granpos */
1320 GST_PUSH_UNLOCK (ogg);
1321 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1323 return GST_FLOW_SKIP_PUSH;
1326 t = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1328 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1329 GstClockTime sync_time;
1331 if (pad->push_sync_time == GST_CLOCK_TIME_NONE)
1332 pad->push_sync_time = t;
1333 GST_DEBUG_OBJECT (ogg, "Got timestamp %" GST_TIME_FORMAT " for %s",
1334 GST_TIME_ARGS (t), gst_ogg_stream_get_media_type (&pad->map));
1335 sync_time = gst_ogg_demux_collect_sync_time (ogg, ogg->building_chain);
1336 if (sync_time == GST_CLOCK_TIME_NONE) {
1337 GST_PUSH_UNLOCK (ogg);
1338 GST_DEBUG_OBJECT (ogg,
1339 "Not enough timing info collected for sync, waiting for more");
1340 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1342 return GST_FLOW_SKIP_PUSH;
1344 ogg->push_last_seek_time = sync_time;
1346 GST_DEBUG_OBJECT (ogg,
1347 "Bisection just seeked at %" G_GINT64_FORMAT ", time %"
1348 GST_TIME_FORMAT ", target was %" GST_TIME_FORMAT,
1349 ogg->push_last_seek_offset,
1350 GST_TIME_ARGS (ogg->push_last_seek_time),
1351 GST_TIME_ARGS (ogg->push_seek_time_target));
1353 if (ogg->push_time1 != GST_CLOCK_TIME_NONE) {
1354 GST_DEBUG_OBJECT (ogg,
1355 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1356 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1357 " (%" GST_TIME_FORMAT ")", ogg->push_offset0, ogg->push_offset1,
1358 ogg->push_offset1 - ogg->push_offset0,
1359 GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1),
1360 GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0));
1362 GST_DEBUG_OBJECT (ogg,
1363 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1364 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - unknown",
1365 ogg->push_offset0, ogg->push_offset1,
1366 ogg->push_offset1 - ogg->push_offset0,
1367 GST_TIME_ARGS (ogg->push_time0));
1370 gst_ogg_demux_setup_bisection_bounds (ogg);
1372 best = gst_ogg_demux_estimate_bisection_target (ogg);
1374 if (ogg->push_seek_time_target == 0) {
1375 GST_DEBUG_OBJECT (ogg, "Seeking to 0, deemed close enough");
1376 close_enough = (ogg->push_last_seek_time == 0);
1378 /* TODO: make this dependent on framerate ? */
1379 GstClockTime threshold = GST_SECOND / 2;
1381 /* We want to be within half a second before the target */
1382 if (threshold > ogg->push_seek_time_target)
1383 threshold = ogg->push_seek_time_target;
1384 close_enough = ogg->push_last_seek_time < ogg->push_seek_time_target
1385 && ogg->push_last_seek_time >=
1386 ogg->push_seek_time_target - threshold;
1387 GST_DEBUG_OBJECT (ogg,
1388 "testing if we're close enough: %" GST_TIME_FORMAT " <= %"
1389 GST_TIME_FORMAT " < %" GST_TIME_FORMAT " ? %s",
1390 GST_TIME_ARGS (ogg->push_seek_time_target - threshold),
1391 GST_TIME_ARGS (ogg->push_last_seek_time),
1392 GST_TIME_ARGS (ogg->push_seek_time_target),
1393 close_enough ? "Yes" : "No");
1396 if (close_enough || best == ogg->push_last_seek_offset) {
1397 if (ogg->push_state == PUSH_BISECT1) {
1398 /* we now know the time segment we'll have to search for
1399 the second bisection */
1400 ogg->push_time0 = ogg->push_start_time;
1401 ogg->push_offset0 = 0;
1403 GST_DEBUG_OBJECT (ogg,
1404 "Seek to %" GST_TIME_FORMAT
1405 " (%lx) done, now gathering pages for all non-sparse streams",
1406 GST_TIME_ARGS (ogg->push_seek_time_target), (long) granpos);
1407 ogg->push_state = PUSH_LINEAR1;
1409 /* If we're asked for an accurate seek, we'll go forward till
1410 we get to the original seek target time, else we'll just drop
1411 here at the keyframe */
1412 if (ogg->push_seek_flags & GST_SEEK_FLAG_ACCURATE) {
1413 GST_INFO_OBJECT (ogg,
1414 "Seek to keyframe at %" GST_TIME_FORMAT " done (we're at %"
1415 GST_TIME_FORMAT "), skipping to original target (%"
1416 GST_TIME_FORMAT ")",
1417 GST_TIME_ARGS (ogg->push_seek_time_target),
1418 GST_TIME_ARGS (sync_time),
1419 GST_TIME_ARGS (ogg->push_seek_time_original_target));
1420 ogg->push_state = PUSH_LINEAR2;
1422 GST_DEBUG_OBJECT (ogg, "Seek to keyframe done, playing");
1424 /* we're synced to the seek target, so flush stream and stuff
1425 any queued pages into the stream so we start decoding there */
1426 ogg->push_state = PUSH_PLAYING;
1428 GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps",
1429 ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]);
1432 } else if (ogg->push_state == PUSH_LINEAR1) {
1433 if (pad->push_kf_time == GST_CLOCK_TIME_NONE) {
1434 GstClockTime earliest_keyframe_time;
1436 gst_ogg_demux_record_keyframe_time (ogg, pad, granpos);
1437 GST_DEBUG_OBJECT (ogg,
1438 "Previous keyframe for %s stream at %" GST_TIME_FORMAT,
1439 gst_ogg_stream_get_media_type (&pad->map),
1440 GST_TIME_ARGS (pad->push_kf_time));
1441 earliest_keyframe_time = gst_ogg_demux_get_earliest_keyframe_time (ogg);
1442 if (earliest_keyframe_time != GST_CLOCK_TIME_NONE) {
1443 GST_DEBUG_OBJECT (ogg,
1444 "All non sparse streams now have a previous keyframe time,"
1445 "bisecting again to %" GST_TIME_FORMAT,
1446 GST_TIME_ARGS (earliest_keyframe_time));
1447 ogg->push_seek_time_target = earliest_keyframe_time;
1449 ogg->push_state = PUSH_BISECT2;
1450 best = gst_ogg_demux_estimate_bisection_target (ogg);
1455 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1458 ogg_sync_reset (&ogg->sync);
1459 for (i = 0; i < ogg->building_chain->streams->len; i++) {
1461 g_array_index (ogg->building_chain->streams, GstOggPad *, i);
1463 pad->push_sync_time = GST_CLOCK_TIME_NONE;
1464 ogg_stream_reset (&pad->map.stream);
1467 GST_DEBUG_OBJECT (ogg,
1468 "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, best,
1471 g_assert (best != -1);
1472 ogg->push_bisection_steps[ogg->push_state == PUSH_BISECT2 ? 1 : 0]++;
1474 gst_event_new_seek (ogg->push_seek_rate, GST_FORMAT_BYTES,
1475 ogg->push_seek_flags, GST_SEEK_TYPE_SET, best,
1476 GST_SEEK_TYPE_NONE, -1);
1478 GST_PUSH_UNLOCK (ogg);
1479 res = gst_pad_push_event (ogg->sinkpad, sevent);
1481 /* We failed to send the seek event, notify the pipeline */
1482 GST_ELEMENT_ERROR (ogg, RESOURCE, SEEK, (NULL), ("Failed to seek"));
1483 return GST_FLOW_ERROR;
1485 return GST_FLOW_SKIP_PUSH;
1488 if (ogg->push_state != PUSH_PLAYING) {
1489 GST_PUSH_UNLOCK (ogg);
1490 return GST_FLOW_SKIP_PUSH;
1493 GST_PUSH_UNLOCK (ogg);
1499 GST_WARNING_OBJECT (ogg,
1500 "ogg stream choked on page (serial %08x), "
1501 "resetting stream", pad->map.serialno);
1502 gst_ogg_pad_reset (pad);
1503 /* we continue to recover */
1504 return GST_FLOW_SKIP_PUSH;
1508 /* submit a page to an oggpad, this function will then submit all
1509 * the packets in the page.
1511 static GstFlowReturn
1512 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
1514 GstFlowReturn result = GST_FLOW_OK;
1516 gboolean continued = FALSE;
1520 /* for negative rates we read pages backwards and must therefore be careful
1521 * with continued pages */
1522 if (ogg->segment.rate < 0.0) {
1525 continued = ogg_page_continued (page);
1527 /* number of completed packets in the page */
1528 npackets = ogg_page_packets (page);
1530 /* page is not continued so it contains at least one packet start. It's
1531 * possible that no packet ends on this page (npackets == 0). In that
1532 * case, the next (continued) page(s) we kept contain the remainder of the
1533 * packets. We mark npackets=1 to make us start decoding the pages in the
1534 * remainder of the algorithm. */
1538 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
1540 if (npackets == 0) {
1541 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
1546 /* keep track of time in push mode */
1547 if (!ogg->pullmode) {
1548 result = gst_ogg_pad_handle_push_mode_state (pad, page);
1549 if (result == GST_FLOW_SKIP_PUSH)
1551 if (result != GST_FLOW_OK)
1555 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1558 /* flush all packets in the stream layer, this might not give a packet if
1559 * the page had no packets finishing on the page (npackets == 0). */
1560 result = gst_ogg_pad_stream_out (pad, 0);
1562 if (pad->continued) {
1565 /* now send the continued pages to the stream layer */
1566 while (pad->continued) {
1567 ogg_page *p = (ogg_page *) pad->continued->data;
1569 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
1570 if (ogg_stream_pagein (&pad->map.stream, p) != 0)
1573 pad->continued = g_list_delete_link (pad->continued, pad->continued);
1576 gst_ogg_page_free (p);
1579 GST_LOG_OBJECT (ogg, "flushing last continued packet");
1580 /* flush 1 continued packet in the stream layer */
1581 result = gst_ogg_pad_stream_out (pad, 1);
1583 /* flush all remaining packets, we pushed them in the previous round.
1584 * We don't use _reset() because we still want to get the discont when
1585 * we submit a next page. */
1586 while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
1590 /* keep continued pages (only in reverse mode) */
1592 ogg_page *p = gst_ogg_page_copy (page);
1594 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
1595 pad->continued = g_list_prepend (pad->continued, p);
1602 GST_WARNING_OBJECT (ogg,
1603 "ogg stream choked on page (serial %08x), "
1604 "resetting stream", pad->map.serialno);
1605 gst_ogg_pad_reset (pad);
1606 /* we continue to recover */
1612 static GstOggChain *
1613 gst_ogg_chain_new (GstOggDemux * ogg)
1615 GstOggChain *chain = g_slice_new0 (GstOggChain);
1617 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
1621 chain->have_bos = FALSE;
1622 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
1623 chain->begin_time = GST_CLOCK_TIME_NONE;
1624 chain->segment_start = GST_CLOCK_TIME_NONE;
1625 chain->segment_stop = GST_CLOCK_TIME_NONE;
1626 chain->total_time = GST_CLOCK_TIME_NONE;
1632 gst_ogg_chain_free (GstOggChain * chain)
1636 for (i = 0; i < chain->streams->len; i++) {
1637 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1639 gst_object_unref (pad);
1641 g_array_free (chain->streams, TRUE);
1642 g_slice_free (GstOggChain, chain);
1646 gst_ogg_pad_mark_discont (GstOggPad * pad)
1648 pad->discont = TRUE;
1649 pad->map.last_size = 0;
1653 gst_ogg_chain_mark_discont (GstOggChain * chain)
1657 for (i = 0; i < chain->streams->len; i++) {
1658 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1660 gst_ogg_pad_mark_discont (pad);
1665 gst_ogg_chain_reset (GstOggChain * chain)
1669 for (i = 0; i < chain->streams->len; i++) {
1670 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1672 gst_ogg_pad_reset (pad);
1677 gst_ogg_chain_new_stream (GstOggChain * chain, guint32 serialno)
1683 GST_DEBUG_OBJECT (chain->ogg,
1684 "creating new stream %08x in chain %p", serialno, chain);
1686 name = g_strdup_printf ("src_%08x", serialno);
1687 ret = g_object_new (GST_TYPE_OGG_PAD, "name", name, NULL);
1689 /* we own this one */
1690 gst_object_ref_sink (ret);
1692 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1693 gst_ogg_pad_mark_discont (ret);
1696 ret->ogg = chain->ogg;
1698 ret->map.serialno = serialno;
1699 if (ogg_stream_init (&ret->map.stream, serialno) != 0)
1702 /* FIXME: either do something with it or remove it */
1703 list = gst_tag_list_new_empty ();
1704 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1706 gst_tag_list_free (list);
1708 GST_DEBUG_OBJECT (chain->ogg,
1709 "created new ogg src %p for stream with serial %08x", ret, serialno);
1711 g_array_append_val (chain->streams, ret);
1712 gst_pad_set_active (GST_PAD_CAST (ret), TRUE);
1719 GST_ERROR ("Could not initialize ogg_stream struct for serial %08x",
1721 gst_object_unref (ret);
1727 gst_ogg_chain_get_stream (GstOggChain * chain, guint32 serialno)
1731 for (i = 0; i < chain->streams->len; i++) {
1732 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1734 if (pad->map.serialno == serialno)
1741 gst_ogg_chain_has_stream (GstOggChain * chain, guint32 serialno)
1743 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1746 /* signals and args */
1759 static GstStaticPadTemplate ogg_demux_src_template_factory =
1760 GST_STATIC_PAD_TEMPLATE ("src_%08x",
1763 GST_STATIC_CAPS_ANY);
1765 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1766 GST_STATIC_PAD_TEMPLATE ("sink",
1769 GST_STATIC_CAPS ("application/ogg; application/x-annodex")
1772 static void gst_ogg_demux_finalize (GObject * object);
1774 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1775 GstOggChain ** chain);
1776 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1777 GstOggChain * chain);
1779 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
1780 static void gst_ogg_demux_loop (GstOggPad * pad);
1781 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
1782 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
1783 static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
1785 static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
1787 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1788 GstStateChange transition);
1790 static void gst_ogg_print (GstOggDemux * demux);
1792 #define gst_ogg_demux_parent_class parent_class
1793 G_DEFINE_TYPE (GstOggDemux, gst_ogg_demux, GST_TYPE_ELEMENT);
1796 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1798 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1799 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1801 gst_element_class_set_details_simple (gstelement_class,
1802 "Ogg demuxer", "Codec/Demuxer",
1803 "demux ogg streams (info about ogg: http://xiph.org)",
1804 "Wim Taymans <wim@fluendo.com>");
1806 gst_element_class_add_pad_template (gstelement_class,
1807 gst_static_pad_template_get (&ogg_demux_sink_template_factory));
1808 gst_element_class_add_pad_template (gstelement_class,
1809 gst_static_pad_template_get (&ogg_demux_src_template_factory));
1811 gstelement_class->change_state = gst_ogg_demux_change_state;
1812 gstelement_class->send_event = gst_ogg_demux_receive_event;
1814 gobject_class->finalize = gst_ogg_demux_finalize;
1818 gst_ogg_demux_init (GstOggDemux * ogg)
1820 /* create the sink pad */
1822 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
1825 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
1826 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
1827 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
1828 gst_pad_set_activatepull_function (ogg->sinkpad,
1829 gst_ogg_demux_sink_activate_pull);
1830 gst_pad_set_activatepush_function (ogg->sinkpad,
1831 gst_ogg_demux_sink_activate_push);
1832 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
1834 ogg->chain_lock = g_mutex_new ();
1835 ogg->push_lock = g_mutex_new ();
1836 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
1838 ogg->newsegment = NULL;
1842 gst_ogg_demux_finalize (GObject * object)
1846 ogg = GST_OGG_DEMUX (object);
1848 g_array_free (ogg->chains, TRUE);
1849 g_mutex_free (ogg->chain_lock);
1850 g_mutex_free (ogg->push_lock);
1851 ogg_sync_clear (&ogg->sync);
1853 if (ogg->newsegment)
1854 gst_event_unref (ogg->newsegment);
1856 G_OBJECT_CLASS (parent_class)->finalize (object);
1860 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
1865 chain = ogg->current_chain;
1869 for (i = 0; i < chain->streams->len; i++) {
1870 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
1872 stream->start_time = -1;
1873 stream->map.accumulated_granule = 0;
1875 ogg->building_chain = chain;
1876 GST_DEBUG_OBJECT (ogg, "Resetting current chain");
1877 ogg->current_chain = NULL;
1882 gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
1887 ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
1889 switch (GST_EVENT_TYPE (event)) {
1890 case GST_EVENT_FLUSH_START:
1891 res = gst_ogg_demux_send_event (ogg, event);
1893 case GST_EVENT_FLUSH_STOP:
1894 GST_DEBUG_OBJECT (ogg, "got a flush stop event");
1895 ogg_sync_reset (&ogg->sync);
1896 res = gst_ogg_demux_send_event (ogg, event);
1897 if (ogg->pullmode || ogg->push_state != PUSH_DURATION) {
1898 /* it's starting to feel reaaaally dirty :(
1899 if we're on a spliced seek to get duration, don't reset streams,
1900 we'll need them for the delayed seek */
1901 gst_ogg_demux_reset_streams (ogg);
1904 case GST_EVENT_SEGMENT:
1905 GST_DEBUG_OBJECT (ogg, "got a new segment event");
1909 gst_event_copy_segment (event, &segment);
1911 if (segment.format == GST_FORMAT_BYTES) {
1912 GST_PUSH_LOCK (ogg);
1913 ogg->push_byte_offset = segment.start;
1914 ogg->push_last_seek_offset = segment.start;
1915 GST_PUSH_UNLOCK (ogg);
1917 GST_WARNING_OBJECT (ogg, "unexpected segment format: %s",
1918 gst_format_get_name (segment.format));
1921 gst_event_unref (event);
1926 GST_DEBUG_OBJECT (ogg, "got an EOS event");
1928 /* This would be what is needed (recover from EOS by going on to
1929 the next step (issue the delayed seek)), but it does not work
1930 if there is a queue2 upstream - see more details comment in
1931 gst_ogg_pad_submit_page.
1932 If I could find a way to bypass queue2 behavior, this should
1934 GST_PUSH_LOCK (ogg);
1935 if (ogg->push_state == PUSH_DURATION) {
1936 GST_DEBUG_OBJECT (ogg, "Got EOS while determining length");
1937 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1938 if (res != GST_FLOW_OK) {
1939 GST_DEBUG_OBJECT (ogg, "Error seeking back after duration check: %d",
1944 GST_PUSH_UNLOCK (ogg);
1946 res = gst_ogg_demux_send_event (ogg, event);
1947 if (ogg->current_chain == NULL) {
1948 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
1949 ("can't get first chain"));
1954 res = gst_ogg_demux_send_event (ogg, event);
1957 gst_object_unref (ogg);
1962 /* submit the given buffer to the ogg sync */
1963 static GstFlowReturn
1964 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
1968 GstFlowReturn ret = GST_FLOW_OK;
1970 size = gst_buffer_get_size (buffer);
1971 GST_DEBUG_OBJECT (ogg, "submitting %" G_GSIZE_FORMAT " bytes", size);
1972 if (G_UNLIKELY (size == 0))
1975 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
1976 if (G_UNLIKELY (oggbuffer == NULL))
1979 gst_buffer_extract (buffer, 0, oggbuffer, size);
1981 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
1984 if (!ogg->pullmode) {
1985 GST_PUSH_LOCK (ogg);
1986 ogg->push_byte_offset += size;
1987 GST_PUSH_UNLOCK (ogg);
1991 gst_buffer_unref (buffer);
1998 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1999 (NULL), ("failed to get ogg sync buffer"));
2000 ret = GST_FLOW_ERROR;
2005 GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2006 ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2007 ret = GST_FLOW_ERROR;
2012 /* in random access mode this code updates the current read position
2013 * and resets the ogg sync buffer so that the next read will happen
2014 * from this new location.
2017 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
2019 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
2021 ogg->offset = offset;
2022 ogg->read_offset = offset;
2023 ogg_sync_reset (&ogg->sync);
2026 /* read more data from the current offset and submit to
2027 * the ogg sync layer.
2029 static GstFlowReturn
2030 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
2035 GST_LOG_OBJECT (ogg,
2036 "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
2037 ogg->read_offset, ogg->length, end_offset);
2039 if (end_offset > 0 && ogg->read_offset >= end_offset)
2040 goto boundary_reached;
2042 if (ogg->read_offset == ogg->length)
2045 ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
2046 if (ret != GST_FLOW_OK)
2049 ogg->read_offset += gst_buffer_get_size (buffer);
2051 ret = gst_ogg_demux_submit_buffer (ogg, buffer);
2058 GST_LOG_OBJECT (ogg, "reached boundary");
2059 return GST_FLOW_LIMIT;
2063 GST_LOG_OBJECT (ogg, "reached EOS");
2064 return GST_FLOW_EOS;
2068 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
2069 gst_flow_get_name (ret));
2074 /* Read the next page from the current offset.
2075 * boundary: number of bytes ahead we allow looking for;
2078 * @offset will contain the offset the next page starts at when this function
2079 * returns GST_FLOW_OK.
2081 * GST_FLOW_EOS is returned on EOS.
2083 * GST_FLOW_LIMIT is returned when we did not find a page before the
2084 * boundary. If @boundary is -1, this is never returned.
2086 * Any other error returned while retrieving data from the peer is returned as
2089 static GstFlowReturn
2090 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
2091 gint64 boundary, gint64 * offset)
2093 gint64 end_offset = -1;
2096 GST_LOG_OBJECT (ogg,
2097 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
2098 G_GINT64_FORMAT, ogg->offset, boundary);
2101 end_offset = ogg->offset + boundary;
2106 if (end_offset > 0 && ogg->offset >= end_offset)
2107 goto boundary_reached;
2109 more = ogg_sync_pageseek (&ogg->sync, og);
2111 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
2114 /* skipped n bytes */
2115 ogg->offset -= more;
2116 GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
2118 } else if (more == 0) {
2119 /* we need more data */
2121 goto boundary_reached;
2123 GST_LOG_OBJECT (ogg, "need more data");
2124 ret = gst_ogg_demux_get_data (ogg, end_offset);
2125 if (ret != GST_FLOW_OK)
2128 gint64 res_offset = ogg->offset;
2130 /* got a page. Return the offset at the page beginning,
2131 advance the internal offset past the page end */
2133 *offset = res_offset;
2136 ogg->offset += more;
2138 GST_LOG_OBJECT (ogg,
2139 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
2140 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
2141 ogg_page_serialno (og), ogg->offset,
2142 (gint64) ogg_page_granulepos (og));
2146 GST_LOG_OBJECT (ogg, "returning %d", ret);
2153 GST_LOG_OBJECT (ogg,
2154 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
2155 ogg->offset, end_offset);
2156 return GST_FLOW_LIMIT;
2160 /* from the current offset, find the previous page, seeking backwards
2161 * until we find the page.
2163 static GstFlowReturn
2164 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
2167 gint64 begin = ogg->offset;
2169 gint64 cur_offset = -1;
2171 GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
2173 while (cur_offset == -1) {
2178 /* seek CHUNKSIZE back */
2179 gst_ogg_demux_seek (ogg, begin);
2181 /* now continue reading until we run out of data, if we find a page
2182 * start, we save it. It might not be the final page as there could be
2183 * another page after this one. */
2184 while (ogg->offset < end) {
2188 gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
2189 /* we hit the upper limit, offset contains the last page start */
2190 if (ret == GST_FLOW_LIMIT) {
2191 GST_LOG_OBJECT (ogg, "hit limit");
2194 /* something went wrong */
2195 if (ret == GST_FLOW_EOS) {
2197 GST_LOG_OBJECT (ogg, "got unexpected");
2198 } else if (ret != GST_FLOW_OK) {
2199 GST_LOG_OBJECT (ogg, "got error %d", ret);
2203 GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
2205 /* offset is next page start */
2206 cur_offset = new_offset;
2210 GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
2212 /* we have the offset. Actually snork and hold the page now */
2213 gst_ogg_demux_seek (ogg, cur_offset);
2214 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
2215 if (ret != GST_FLOW_OK) {
2216 GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
2218 /* this shouldn't be possible */
2223 *offset = cur_offset;
2229 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
2232 GstOggChain *chain = ogg->current_chain;
2237 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
2239 /* send EOS on all the pads */
2240 for (i = 0; i < chain->streams->len; i++) {
2241 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2247 event = gst_event_new_eos ();
2248 gst_event_set_seqnum (event, ogg->seqnum);
2249 gst_pad_push_event (GST_PAD_CAST (pad), event);
2251 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
2253 /* deactivate first */
2254 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
2256 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2260 /* With push mode seeking implemented, we can now seek back to the chain,
2261 so we do not destroy it */
2262 GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2263 ogg->current_chain = NULL;
2269 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
2272 GstStructure *structure;
2273 GValue array = { 0 };
2275 GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
2277 if (G_UNLIKELY (!caps))
2279 if (G_UNLIKELY (!headers))
2282 caps = gst_caps_make_writable (caps);
2283 structure = gst_caps_get_structure (caps, 0);
2285 g_value_init (&array, GST_TYPE_ARRAY);
2288 GValue value = { 0 };
2290 ogg_packet *op = headers->data;
2292 buffer = gst_buffer_new_and_alloc (op->bytes);
2293 gst_buffer_fill (buffer, 0, op->packet, op->bytes);
2294 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS);
2295 g_value_init (&value, GST_TYPE_BUFFER);
2296 gst_value_take_buffer (&value, buffer);
2297 gst_value_array_append_value (&array, &value);
2298 g_value_unset (&value);
2299 headers = headers->next;
2302 gst_structure_set_value (structure, "streamheader", &array);
2303 g_value_unset (&array);
2304 GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
2310 gst_ogg_demux_push_queued_buffers (GstOggDemux * ogg, GstOggPad * pad)
2314 /* push queued packets */
2315 for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
2316 ogg_packet *p = walk->data;
2318 gst_ogg_demux_chain_peer (pad, p, TRUE);
2319 _ogg_packet_free (p);
2321 /* and free the queued buffers */
2322 g_list_free (pad->map.queued);
2323 pad->map.queued = NULL;
2327 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
2331 gint bitrate, idx_bitrate;
2333 g_return_val_if_fail (chain != NULL, FALSE);
2335 if (chain == ogg->current_chain) {
2337 gst_event_unref (event);
2339 for (i = 0; i < chain->streams->len; i++) {
2340 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2341 gst_ogg_demux_push_queued_buffers (ogg, pad);
2347 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
2349 bitrate = idx_bitrate = 0;
2351 /* first add the pads */
2352 for (i = 0; i < chain->streams->len; i++) {
2355 pad = g_array_index (chain->streams, GstOggPad *, i);
2357 if (pad->map.idx_bitrate)
2358 idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
2360 bitrate += pad->map.bitrate;
2363 gst_ogg_pad_mark_discont (pad);
2364 pad->last_ret = GST_FLOW_OK;
2366 if (pad->map.is_skeleton || pad->added
2367 || !gst_pad_has_current_caps (GST_PAD_CAST (pad)))
2370 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
2372 /* activate first */
2373 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
2375 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2378 /* prefer the index bitrate over the ones encoded in the streams */
2379 ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
2381 /* after adding the new pads, remove the old pads */
2382 gst_ogg_demux_deactivate_current_chain (ogg);
2384 GST_DEBUG_OBJECT (ogg, "Setting current chain to %p", chain);
2385 ogg->current_chain = chain;
2387 /* we are finished now */
2388 gst_element_no_more_pads (GST_ELEMENT (ogg));
2390 /* FIXME, must be sent from the streaming thread */
2394 gst_ogg_demux_send_event (ogg, event);
2396 tags = gst_tag_list_new (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL);
2397 gst_ogg_demux_send_event (ogg, gst_event_new_tag (tags));
2400 GST_DEBUG_OBJECT (ogg, "starting chain");
2402 /* then send out any headers and queued packets */
2403 for (i = 0; i < chain->streams->len; i++) {
2407 pad = g_array_index (chain->streams, GstOggPad *, i);
2409 /* FIXME also streaming thread */
2410 if (pad->map.taglist) {
2411 GST_DEBUG_OBJECT (ogg, "pushing tags");
2412 gst_pad_push_event (GST_PAD_CAST (pad),
2413 gst_event_new_tag (pad->map.taglist));
2414 pad->map.taglist = NULL;
2417 /* Set headers on caps */
2419 gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers);
2420 gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps);
2422 GST_DEBUG_OBJECT (ogg, "pushing headers");
2424 for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
2425 ogg_packet *p = walk->data;
2427 gst_ogg_demux_chain_peer (pad, p, TRUE);
2430 GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
2431 gst_ogg_demux_push_queued_buffers (ogg, pad);
2437 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2438 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2447 GST_DEBUG_OBJECT (ogg,
2448 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
2450 GST_DEBUG_OBJECT (ogg,
2451 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
2452 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
2453 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
2455 /* perform the seek */
2456 while (begin < end) {
2459 if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
2462 /* take a (pretty decent) guess, avoiding overflow */
2463 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
2465 bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
2467 if (bisect <= begin)
2469 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
2471 gst_ogg_demux_seek (ogg, bisect);
2473 while (begin < end) {
2476 GST_DEBUG_OBJECT (ogg,
2477 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
2478 ", end %" G_GINT64_FORMAT, bisect, begin, end);
2480 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2481 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2484 if (ret == GST_FLOW_LIMIT) {
2485 /* we hit the upper limit, go back a bit */
2486 if (bisect <= begin + 1) {
2487 end = begin; /* found it */
2492 bisect -= CHUNKSIZE;
2493 if (bisect <= begin)
2496 gst_ogg_demux_seek (ogg, bisect);
2498 } else if (ret == GST_FLOW_OK) {
2499 /* found offset of next ogg page */
2501 GstClockTime granuletime;
2504 /* get the granulepos */
2505 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
2507 granulepos = ogg_page_granulepos (&og);
2508 if (granulepos == -1) {
2509 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2513 /* get the stream */
2514 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2515 if (pad == NULL || pad->map.is_skeleton)
2518 /* convert granulepos to time */
2519 granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2521 if (granuletime < pad->start_time)
2524 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
2525 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2527 granuletime -= pad->start_time;
2528 granuletime += chain->begin_time;
2530 GST_DEBUG_OBJECT (ogg,
2531 "found page with granule %" G_GINT64_FORMAT " and time %"
2532 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2534 if (granuletime < target) {
2535 best = result; /* raw offset of packet with granulepos */
2536 begin = ogg->offset; /* raw offset of next page */
2537 begintime = granuletime;
2539 bisect = begin; /* *not* begin + 1 */
2541 if (bisect <= begin + 1) {
2542 end = begin; /* found it */
2544 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
2546 bisect -= CHUNKSIZE; /* an endless loop otherwise. */
2547 if (bisect <= begin)
2549 gst_ogg_demux_seek (ogg, bisect);
2552 endtime = granuletime;
2561 GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
2562 gst_ogg_demux_seek (ogg, best);
2570 GST_DEBUG_OBJECT (ogg, "got a seek error");
2576 do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2577 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2578 gint64 * p_offset, gint64 * p_timestamp)
2581 guint64 timestamp, offset;
2582 guint64 r_timestamp, r_offset;
2583 gboolean result = FALSE;
2585 target -= begintime;
2590 for (i = 0; i < chain->streams->len; i++) {
2591 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2594 if (gst_ogg_map_search_index (&pad->map, TRUE, ×tamp, &offset)) {
2595 GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
2598 if (r_offset == -1 || offset < r_offset) {
2600 r_timestamp = timestamp;
2607 *p_timestamp = r_timestamp;
2609 *p_offset = r_offset;
2615 * do seek to time @position, return FALSE or chain and TRUE
2618 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
2619 gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
2622 GstOggChain *chain = NULL;
2624 gint64 begintime, endtime;
2625 gint64 target, keytarget;
2630 gint i, pending, len;
2631 gboolean first_parsed_page = TRUE;
2633 position = segment->position;
2635 /* first find the chain to search in */
2636 total = ogg->total_time;
2637 if (ogg->chains->len == 0)
2640 for (i = ogg->chains->len - 1; i >= 0; i--) {
2641 chain = g_array_index (ogg->chains, GstOggChain *, i);
2642 total -= chain->total_time;
2643 if (position >= total)
2647 /* first step, locate page containing the required data */
2648 begin = chain->offset;
2649 end = chain->end_offset;
2650 begintime = chain->begin_time;
2651 endtime = begintime + chain->total_time;
2652 target = position - total + begintime;
2654 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
2658 /* second step: find pages for all streams, we use the keyframe_granule to keep
2659 * track of which ones we saw. If we have seen a page for each stream we can
2660 * calculate the positions of each keyframe. */
2661 GST_DEBUG_OBJECT (ogg, "find keyframes");
2662 len = pending = chain->streams->len;
2664 /* figure out where the keyframes are */
2671 GstClockTime keyframe_time, granule_time;
2673 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2674 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2676 if (ret == GST_FLOW_LIMIT) {
2677 GST_LOG_OBJECT (ogg, "reached limit");
2679 } else if (ret != GST_FLOW_OK)
2682 /* get the stream */
2683 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2687 if (pad->map.is_skeleton)
2690 granulepos = ogg_page_granulepos (&og);
2691 if (granulepos == -1 || granulepos == 0) {
2692 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2696 /* we only do this the first time we pass here */
2697 if (first_parsed_page) {
2698 /* Now that we have a time reference from the page, we can check
2699 * whether all streams still have pages from here on.
2701 * This would be more elegant before the loop, but getting the page from
2702 * there without breaking anything would be more costly */
2703 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2705 for (i = 0; i < len; i++) {
2706 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2709 /* we already know we have at least one page (the current one)
2710 * for this stream */
2713 if (granule_time > stream->map.total_time)
2714 /* we won't encounter any more pages of this stream, so we don't
2715 * try finding a key frame for it */
2718 first_parsed_page = FALSE;
2722 /* in reverse we want to go past the page with the lower timestamp */
2723 if (segment->rate < 0.0) {
2724 /* get time for this pad */
2725 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2728 GST_LOG_OBJECT (ogg,
2729 "looking at page with ts %" GST_TIME_FORMAT ", target %"
2730 GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
2731 GST_TIME_ARGS (target));
2732 if (granule_time < target)
2736 /* we've seen this pad before */
2737 if (pad->keyframe_granule != -1)
2740 /* convert granule of this pad to the granule of the keyframe */
2741 pad->keyframe_granule =
2742 gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
2743 GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
2744 pad->keyframe_granule);
2746 /* get time of the keyframe */
2748 gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
2749 GST_LOG_OBJECT (ogg,
2750 "stream %08x granule time %" GST_TIME_FORMAT,
2751 pad->map.serialno, GST_TIME_ARGS (keyframe_time));
2753 /* collect smallest value */
2754 if (keyframe_time != -1) {
2755 keyframe_time += begintime;
2756 if (keyframe_time < keytarget)
2757 keytarget = keyframe_time;
2766 /* for negative rates we will get to the keyframe backwards */
2767 if (segment->rate < 0.0)
2770 if (keytarget != target) {
2771 GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
2772 GST_TIME_ARGS (keytarget));
2774 /* last step, seek to the location of the keyframe */
2775 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
2779 /* seek back to previous position */
2780 GST_LOG_OBJECT (ogg, "keyframe on target");
2781 gst_ogg_demux_seek (ogg, best);
2786 if (segment->rate > 0.0)
2787 segment->time = keytarget;
2788 segment->position = keytarget - begintime;
2797 GST_DEBUG_OBJECT (ogg, "no chains");
2802 GST_DEBUG_OBJECT (ogg, "got a seek error");
2807 /* does not take ownership of the event */
2809 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
2811 GstOggChain *chain = NULL;
2813 gboolean flush, accurate, keyframe;
2817 GstSeekType cur_type, stop_type;
2824 GST_DEBUG_OBJECT (ogg, "seek with event");
2826 gst_event_parse_seek (event, &rate, &format, &flags,
2827 &cur_type, &cur, &stop_type, &stop);
2829 /* we can only seek on time */
2830 if (format != GST_FORMAT_TIME) {
2831 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
2834 seqnum = gst_event_get_seqnum (event);
2836 GST_DEBUG_OBJECT (ogg, "seek without event");
2840 seqnum = gst_util_seqnum_next ();
2843 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
2845 flush = flags & GST_SEEK_FLAG_FLUSH;
2846 accurate = flags & GST_SEEK_FLAG_ACCURATE;
2847 keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
2849 /* first step is to unlock the streaming thread if it is
2850 * blocked in a chain call, we do this by starting the flush. because
2851 * we cannot yet hold any streaming lock, we have to protect the chains
2852 * with their own lock. */
2856 tevent = gst_event_new_flush_start ();
2857 gst_event_set_seqnum (tevent, seqnum);
2859 gst_event_ref (tevent);
2860 gst_pad_push_event (ogg->sinkpad, tevent);
2862 GST_CHAIN_LOCK (ogg);
2863 for (i = 0; i < ogg->chains->len; i++) {
2864 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2867 for (j = 0; j < chain->streams->len; j++) {
2868 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
2870 gst_event_ref (tevent);
2871 gst_pad_push_event (GST_PAD (pad), tevent);
2874 GST_CHAIN_UNLOCK (ogg);
2876 gst_event_unref (tevent);
2878 gst_pad_pause_task (ogg->sinkpad);
2881 /* now grab the stream lock so that streaming cannot continue, for
2882 * non flushing seeks when the element is in PAUSED this could block
2884 GST_PAD_STREAM_LOCK (ogg->sinkpad);
2887 gst_segment_do_seek (&ogg->segment, rate, format, flags,
2888 cur_type, cur, stop_type, stop, &update);
2891 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
2892 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
2893 GST_TIME_ARGS (ogg->segment.stop));
2895 /* we need to stop flushing on the srcpad as we're going to use it
2896 * next. We can do this as we have the STREAM lock now. */
2898 tevent = gst_event_new_flush_stop (TRUE);
2899 gst_event_set_seqnum (tevent, seqnum);
2900 gst_pad_push_event (ogg->sinkpad, tevent);
2906 /* reset all ogg streams now, need to do this from within the lock to
2907 * make sure the streaming thread is not messing with the stream */
2908 for (i = 0; i < ogg->chains->len; i++) {
2909 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2911 gst_ogg_chain_reset (chain);
2915 /* for reverse we will already seek accurately */
2916 res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
2918 /* seek failed, make sure we continue the current chain */
2920 GST_DEBUG_OBJECT (ogg, "seek failed");
2921 chain = ogg->current_chain;
2923 GST_DEBUG_OBJECT (ogg, "seek success");
2929 /* now we have a new position, prepare for streaming again */
2934 gint64 position, begin_time;
2937 /* we have to send the flush to the old chain, not the new one */
2939 tevent = gst_event_new_flush_stop (TRUE);
2940 gst_event_set_seqnum (tevent, seqnum);
2941 gst_ogg_demux_send_event (ogg, tevent);
2944 /* we need this to see how far inside the chain we need to start */
2945 if (chain->begin_time != GST_CLOCK_TIME_NONE)
2946 begin_time = chain->begin_time;
2950 /* segment.start gives the start over all chains, we calculate the amount
2951 * of time into this chain we need to start */
2952 start = ogg->segment.start - begin_time;
2953 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2954 start += chain->segment_start;
2956 if ((stop = ogg->segment.stop) == -1)
2957 stop = ogg->segment.duration;
2959 /* segment.stop gives the stop time over all chains, calculate the amount of
2960 * time we need to stop in this chain */
2962 if (stop > begin_time)
2966 stop += chain->segment_start;
2967 /* we must stop when this chain ends and switch to the next chain to play
2968 * the remainder of the segment. */
2969 stop = MIN (stop, chain->segment_stop);
2972 position = ogg->segment.position;
2973 if (chain->segment_start != GST_CLOCK_TIME_NONE)
2974 position += chain->segment_start;
2976 gst_segment_copy_into (&ogg->segment, &segment);
2978 /* create the segment event we are going to send out */
2979 if (ogg->segment.rate >= 0.0) {
2980 segment.start = position;
2981 segment.stop = stop;
2983 segment.start = start;
2984 segment.stop = position;
2986 event = gst_event_new_segment (&segment);
2987 gst_event_set_seqnum (event, seqnum);
2989 if (chain != ogg->current_chain) {
2990 /* switch to different chain, send segment on new chain */
2991 gst_ogg_demux_activate_chain (ogg, chain, event);
2993 /* mark discont and send segment on current chain */
2994 gst_ogg_chain_mark_discont (chain);
2995 /* This event should be sent from the streaming thread (sink pad task) */
2996 if (ogg->newsegment)
2997 gst_event_unref (ogg->newsegment);
2998 ogg->newsegment = event;
3001 /* notify start of new segment */
3002 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3003 GstMessage *message;
3005 message = gst_message_new_segment_start (GST_OBJECT (ogg),
3006 GST_FORMAT_TIME, ogg->segment.position);
3007 gst_message_set_seqnum (message, seqnum);
3009 gst_element_post_message (GST_ELEMENT (ogg), message);
3012 ogg->seqnum = seqnum;
3013 /* restart our task since it might have been stopped when we did the
3015 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3019 /* streaming can continue now */
3020 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3027 GST_DEBUG_OBJECT (ogg, "seek failed");
3032 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
3033 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3039 gst_ogg_demux_get_duration_push (GstOggDemux * ogg, int flags)
3041 /* In push mode, we get to the end of the stream to get the duration */
3046 /* A full Ogg page can be almost 64 KB. There's no guarantee that there'll be a
3047 granpos there, but it's fairly likely */
3049 ogg->push_byte_length - DURATION_CHUNK_OFFSET - EOS_AVOIDANCE_THRESHOLD;
3053 GST_DEBUG_OBJECT (ogg,
3054 "Getting duration, seeking near the end, to %" G_GINT64_FORMAT, position);
3055 ogg->push_state = PUSH_DURATION;
3056 /* do not read the last byte */
3057 sevent = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET,
3058 position, GST_SEEK_TYPE_SET, ogg->push_byte_length - 1);
3059 res = gst_pad_push_event (ogg->sinkpad, sevent);
3061 GST_DEBUG_OBJECT (ogg, "Seek succesful");
3064 GST_INFO_OBJECT (ogg, "Seek failed, duration will stay unknown");
3065 ogg->push_state = PUSH_PLAYING;
3066 ogg->push_disable_seeking = TRUE;
3072 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3075 if (ogg->push_byte_length < 0) {
3078 GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3079 if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3083 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &length);
3084 if (res && length > 0) {
3085 ogg->push_byte_length = length;
3086 GST_DEBUG_OBJECT (ogg,
3087 "File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
3089 res = gst_pad_query_duration (peer, GST_FORMAT_TIME, &length);
3090 gst_object_unref (peer);
3091 if (res && length >= 0) {
3092 ogg->push_time_length = length;
3093 GST_DEBUG_OBJECT (ogg, "File time length %" GST_TIME_FORMAT,
3094 GST_TIME_ARGS (ogg->push_time_length));
3095 } else if (!ogg->push_disable_seeking) {
3098 res = gst_ogg_demux_get_duration_push (ogg, flags);
3100 GST_DEBUG_OBJECT (ogg,
3101 "File time length unknown, trying to determine");
3102 ogg->push_mode_seek_delayed_event = NULL;
3104 GST_DEBUG_OBJECT (ogg,
3105 "Let me intercept this innocent looking seek request");
3106 ogg->push_mode_seek_delayed_event = gst_event_copy (event);
3117 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3120 gboolean res = TRUE;
3124 GstSeekType start_type, stop_type;
3128 gint64 best, best_time;
3131 GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3133 gst_event_parse_seek (event, &rate, &format, &flags,
3134 &start_type, &start, &stop_type, &stop);
3136 if (format != GST_FORMAT_TIME) {
3137 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3141 if (start_type != GST_SEEK_TYPE_SET) {
3142 GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3146 if (!(flags & GST_SEEK_FLAG_FLUSH)) {
3147 GST_DEBUG_OBJECT (ogg, "can only do flushing seeks");
3151 GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3152 GST_TIME_ARGS (start));
3154 chain = ogg->current_chain;
3156 GST_WARNING_OBJECT (ogg, "No chain to seek on");
3160 /* start accessing push_* members */
3161 GST_PUSH_LOCK (ogg);
3163 /* not if we disabled seeking (chained streams) */
3164 if (ogg->push_disable_seeking) {
3165 GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3169 /* not when we're trying to work out duration */
3170 if (ogg->push_state == PUSH_DURATION) {
3171 GST_DEBUG_OBJECT (ogg, "Busy working out duration, try again later");
3175 /* actually, not if we're doing any seeking already */
3176 if (ogg->push_state != PUSH_PLAYING) {
3177 GST_DEBUG_OBJECT (ogg, "Already doing some seeking, try again later");
3181 /* on the first seek, get length if we can */
3182 if (!gst_ogg_demux_check_duration_push (ogg, flags, event)) {
3183 GST_PUSH_UNLOCK (ogg);
3187 if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
3188 /* the index gave some result */
3189 GST_DEBUG_OBJECT (ogg,
3190 "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
3193 if (ogg->push_time_length > 0) {
3194 /* if we know the time length, we know the full segment bitrate */
3195 GST_DEBUG_OBJECT (ogg, "Using real file bitrate");
3197 gst_util_uint64_scale (ogg->push_byte_length, 8 * GST_SECOND,
3198 ogg->push_time_length);
3199 } else if (ogg->push_time_offset > 0) {
3200 /* get a first approximation using known bitrate to the current position */
3201 GST_DEBUG_OBJECT (ogg, "Using file bitrate so far");
3203 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
3204 ogg->push_time_offset);
3205 } else if (ogg->bitrate > 0) {
3206 /* nominal bitrate is better than nothing, even if it lies often */
3207 GST_DEBUG_OBJECT (ogg, "Using nominal bitrate");
3208 bitrate = ogg->bitrate;
3211 GST_DEBUG_OBJECT (ogg,
3212 "At stream start, and no nominal bitrate, using some random magic "
3214 /* the bisection, once started, should give us a better approximation */
3217 best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3220 /* offset by typical page length, and ensure our best guess is within
3221 reasonable bounds */
3225 if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3226 best = ogg->push_byte_length - 1;
3228 /* set up bisection search */
3229 ogg->push_offset0 = 0;
3230 ogg->push_offset1 = ogg->push_byte_length - 1;
3231 ogg->push_time0 = ogg->push_start_time;
3232 ogg->push_time1 = ogg->push_time_length;
3233 ogg->push_seek_time_target = start;
3234 ogg->push_seek_time_original_target = start;
3235 ogg->push_state = PUSH_BISECT1;
3237 /* reset pad push mode seeking state */
3238 for (i = 0; i < chain->streams->len; i++) {
3239 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3240 pad->push_kf_time = GST_CLOCK_TIME_NONE;
3241 pad->push_sync_time = GST_CLOCK_TIME_NONE;
3244 GST_DEBUG_OBJECT (ogg,
3245 "Setting up bisection search for %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
3246 " (time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", ogg->push_offset0,
3247 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
3248 GST_TIME_ARGS (ogg->push_time1));
3249 GST_DEBUG_OBJECT (ogg,
3250 "Target time is %" GST_TIME_FORMAT ", best first guess is %"
3251 G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target), best);
3253 ogg->push_seek_rate = rate;
3254 ogg->push_seek_flags = flags;
3255 ogg->push_mode_seek_delayed_event = NULL;
3256 ogg->push_bisection_steps[0] = 1;
3257 ogg->push_bisection_steps[1] = 0;
3258 sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
3259 start_type, best, GST_SEEK_TYPE_NONE, -1);
3261 GST_PUSH_UNLOCK (ogg);
3262 res = gst_pad_push_event (ogg->sinkpad, sevent);
3269 GST_DEBUG_OBJECT (ogg, "seek failed");
3274 GST_PUSH_UNLOCK (ogg);
3279 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
3283 if (ogg->pullmode) {
3284 res = gst_ogg_demux_perform_seek_pull (ogg, event);
3286 res = gst_ogg_demux_perform_seek_push (ogg, event);
3292 /* finds each bitstream link one at a time using a bisection search
3293 * (has to begin by knowing the offset of the lb's initial page).
3294 * Recurses for each link so it can alloc the link storage after
3295 * finding them all, then unroll and fill the cache at the same time
3297 static GstFlowReturn
3298 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
3299 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
3301 gint64 endsearched = end;
3306 GstOggChain *nextchain;
3308 GST_LOG_OBJECT (ogg,
3309 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
3310 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
3312 /* the below guards against garbage seperating the last and
3313 * first pages of two links. */
3314 while (searched < endsearched) {
3317 if (endsearched - searched < CHUNKSIZE) {
3320 bisect = (searched + endsearched) / 2;
3323 gst_ogg_demux_seek (ogg, bisect);
3324 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
3326 if (ret == GST_FLOW_EOS) {
3327 endsearched = bisect;
3328 } else if (ret == GST_FLOW_OK) {
3329 guint32 serial = ogg_page_serialno (&og);
3331 if (!gst_ogg_chain_has_stream (chain, serial)) {
3332 endsearched = bisect;
3335 searched = offset + og.header_len + og.body_len;
3341 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
3343 chain->end_offset = searched;
3344 ret = gst_ogg_demux_read_end_chain (ogg, chain);
3345 if (ret != GST_FLOW_OK)
3348 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
3350 gst_ogg_demux_seek (ogg, next);
3351 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
3352 if (ret == GST_FLOW_EOS) {
3355 GST_LOG_OBJECT (ogg, "no next chain");
3356 } else if (ret != GST_FLOW_OK)
3359 if (searched < end && nextchain != NULL) {
3360 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
3361 end, nextchain, m + 1);
3362 if (ret != GST_FLOW_OK)
3365 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
3367 g_array_insert_val (ogg->chains, 0, chain);
3373 /* read a chain from the ogg file. This code will
3374 * read all BOS pages and will create and return a GstOggChain
3375 * structure with the results.
3377 * This function will also read N pages from each stream in the
3378 * chain and submit them to the decoders. When the decoder has
3379 * decoded the first buffer, we know the timestamp of the first
3380 * page in the chain.
3382 static GstFlowReturn
3383 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
3386 GstOggChain *chain = NULL;
3387 gint64 offset = ogg->offset;
3392 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
3394 /* first read the BOS pages, do typefind on them, create
3395 * the decoders, send data to the decoders. */
3400 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3401 if (ret != GST_FLOW_OK) {
3402 if (ret == GST_FLOW_EOS) {
3403 GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
3405 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
3409 if (!ogg_page_bos (&og)) {
3410 GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
3411 /* if we did not find a chain yet, assume this is a bogus stream and
3414 GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
3420 if (chain == NULL) {
3421 chain = gst_ogg_chain_new (ogg);
3422 chain->offset = offset;
3425 serial = ogg_page_serialno (&og);
3426 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
3427 GST_WARNING_OBJECT (ogg,
3428 "found serial %08x BOS page twice, ignoring", serial);
3432 pad = gst_ogg_chain_new_stream (chain, serial);
3433 gst_ogg_pad_submit_page (pad, &og);
3436 if (ret != GST_FLOW_OK || chain == NULL) {
3437 if (ret == GST_FLOW_OK) {
3438 GST_WARNING_OBJECT (ogg, "no chain was found");
3439 ret = GST_FLOW_ERROR;
3440 } else if (ret != GST_FLOW_EOS) {
3441 GST_WARNING_OBJECT (ogg, "failed to read chain");
3443 GST_DEBUG_OBJECT (ogg, "done reading chains");
3446 gst_ogg_chain_free (chain);
3453 chain->have_bos = TRUE;
3454 GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
3456 /* now read pages until we receive a buffer from each of the
3457 * stream decoders, this will tell us the timestamp of the
3458 * first packet in the chain then */
3460 /* save the offset to the first non bos page in the chain: if searching for
3461 * pad->first_time we read past the end of the chain, we'll seek back to this
3464 offset = ogg->offset;
3469 gboolean known_serial = FALSE;
3472 serial = ogg_page_serialno (&og);
3474 for (i = 0; i < chain->streams->len; i++) {
3475 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3477 GST_LOG_OBJECT (ogg,
3478 "serial %08x time %" GST_TIME_FORMAT,
3479 pad->map.serialno, GST_TIME_ARGS (pad->start_time));
3481 if (pad->map.serialno == serial) {
3482 known_serial = TRUE;
3484 /* submit the page now, this will fill in the start_time when the
3485 * internal decoder finds it */
3486 gst_ogg_pad_submit_page (pad, &og);
3488 if (!pad->map.is_skeleton && pad->start_time == -1
3489 && ogg_page_eos (&og)) {
3490 /* got EOS on a pad before we could find its start_time.
3491 * We have no chance of finding a start_time for every pad so
3492 * stop searching for the other start_time(s).
3498 /* the timestamp will be filled in when we submit the pages */
3499 if (!pad->map.is_sparse)
3500 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
3502 GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
3505 /* we read a page not belonging to the current chain: seek back to the
3506 * beginning of the chain
3508 if (!known_serial) {
3509 GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
3510 gst_ogg_demux_seek (ogg, offset);
3515 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3516 if (ret != GST_FLOW_OK)
3520 GST_LOG_OBJECT (ogg, "done reading chain");
3521 /* now we can fill in the missing info using queries */
3522 for (i = 0; i < chain->streams->len; i++) {
3523 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3525 if (pad->map.is_skeleton)
3528 pad->mode = GST_OGG_PAD_MODE_STREAMING;
3537 /* read the last pages from the ogg stream to get the final
3540 static GstFlowReturn
3541 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
3543 gint64 begin = chain->end_offset;
3545 gint64 last_granule = -1;
3546 GstOggPad *last_pad = NULL;
3548 gboolean done = FALSE;
3557 gst_ogg_demux_seek (ogg, begin);
3559 /* now continue reading until we run out of data, if we find a page
3560 * start, we save it. It might not be the final page as there could be
3561 * another page after this one. */
3562 while (ogg->offset < end) {
3563 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
3565 if (ret == GST_FLOW_LIMIT)
3567 if (ret != GST_FLOW_OK)
3570 for (i = 0; i < chain->streams->len; i++) {
3571 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3573 if (pad->map.is_skeleton)
3576 if (pad->map.serialno == ogg_page_serialno (&og)) {
3577 gint64 granulepos = ogg_page_granulepos (&og);
3579 if (granulepos != -1) {
3580 last_granule = granulepos;
3591 chain->segment_stop =
3592 gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
3595 chain->segment_stop = GST_CLOCK_TIME_NONE;
3598 GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
3603 /* find a pad with a given serial number
3606 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
3611 /* first look in building chain if any */
3612 if (ogg->building_chain) {
3613 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
3618 /* then look in current chain if any */
3619 if (ogg->current_chain) {
3620 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
3625 for (i = 0; i < ogg->chains->len; i++) {
3626 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3628 pad = gst_ogg_chain_get_stream (chain, serialno);
3635 /* find a chain with a given serial number
3637 static GstOggChain *
3638 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
3642 pad = gst_ogg_demux_find_pad (ogg, serialno);
3649 /* returns TRUE if all streams have valid start time */
3651 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
3653 gboolean res = TRUE;
3655 chain->total_time = GST_CLOCK_TIME_NONE;
3656 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
3658 /* see if we have a start time on all streams */
3659 chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
3661 if (chain->segment_start == G_MAXUINT64) {
3662 /* not yet, stream some more data */
3664 } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
3665 /* we can calculate a total time */
3666 chain->total_time = chain->segment_stop - chain->segment_start;
3669 GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
3671 GST_DEBUG_OBJECT (ogg, "return %d", res);
3677 gst_ogg_demux_collect_info (GstOggDemux * ogg)
3681 /* collect all info */
3682 ogg->total_time = 0;
3684 for (i = 0; i < ogg->chains->len; i++) {
3685 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3687 chain->begin_time = ogg->total_time;
3689 gst_ogg_demux_collect_chain_info (ogg, chain);
3691 ogg->total_time += chain->total_time;
3693 ogg->segment.duration = ogg->total_time;
3696 /* find all the chains in the ogg file, this reads the first and
3697 * last page of the ogg stream, if they match then the ogg file has
3698 * just one chain, else we do a binary search for all chains.
3700 static GstFlowReturn
3701 gst_ogg_demux_find_chains (GstOggDemux * ogg)
3710 /* get peer to figure out length */
3711 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
3714 /* find length to read last page, we store this for later use. */
3715 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &ogg->length);
3716 gst_object_unref (peer);
3717 if (!res || ogg->length <= 0)
3720 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
3722 /* read chain from offset 0, this is the first chain of the
3724 gst_ogg_demux_seek (ogg, 0);
3725 ret = gst_ogg_demux_read_chain (ogg, &chain);
3726 if (ret != GST_FLOW_OK)
3727 goto no_first_chain;
3729 /* read page from end offset, we use this page to check if its serial
3730 * number is contained in the first chain. If this is the case then
3731 * this ogg is not a chained ogg and we can skip the scanning. */
3732 gst_ogg_demux_seek (ogg, ogg->length);
3733 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
3734 if (ret != GST_FLOW_OK)
3737 serialno = ogg_page_serialno (&og);
3739 if (!gst_ogg_chain_has_stream (chain, serialno)) {
3740 /* the last page is not in the first stream, this means we should
3741 * find all the chains in this chained ogg. */
3743 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
3746 /* we still call this function here but with an empty range so that
3747 * we can reuse the setup code in this routine. */
3749 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
3750 ogg->length, chain, 0);
3752 if (ret != GST_FLOW_OK)
3755 /* all fine, collect and print */
3756 gst_ogg_demux_collect_info (ogg);
3758 /* dump our chains and streams */
3759 gst_ogg_print (ogg);
3764 /*** error cases ***/
3767 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
3768 return GST_FLOW_NOT_LINKED;
3772 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
3773 return GST_FLOW_NOT_SUPPORTED;
3777 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
3778 return GST_FLOW_ERROR;
3782 GST_DEBUG_OBJECT (ogg, "can't get last page");
3784 gst_ogg_chain_free (chain);
3789 static GstFlowReturn
3790 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
3795 GstFlowReturn result = GST_FLOW_OK;
3797 serialno = ogg_page_serialno (page);
3798 granule = ogg_page_granulepos (page);
3800 GST_LOG_OBJECT (ogg,
3801 "processing ogg page (serial %08x, "
3802 "pageno %ld, granulepos %" G_GINT64_FORMAT ", bos %d)", serialno,
3803 ogg_page_pageno (page), granule, ogg_page_bos (page));
3805 if (ogg_page_bos (page)) {
3809 /* see if we know about the chain already */
3810 chain = gst_ogg_demux_find_chain (ogg, serialno);
3816 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3817 start = chain->segment_start;
3819 /* create the newsegment event we are going to send out */
3820 gst_segment_copy_into (&ogg->segment, &segment);
3821 segment.start = start;
3822 segment.stop = chain->segment_stop;
3823 segment.time = chain->begin_time;
3824 event = gst_event_new_segment (&segment);
3825 gst_event_set_seqnum (event, ogg->seqnum);
3827 GST_DEBUG_OBJECT (ogg,
3828 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
3829 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
3830 GST_TIME_ARGS (chain->segment_stop),
3831 GST_TIME_ARGS (chain->begin_time));
3833 /* activate it as it means we have a non-header, this will also deactivate
3834 * the currently running chain. */
3835 gst_ogg_demux_activate_chain (ogg, chain, event);
3836 pad = gst_ogg_demux_find_pad (ogg, serialno);
3838 GstClockTime chain_time;
3839 gint64 current_time;
3841 /* this can only happen in push mode */
3845 current_time = ogg->segment.position;
3847 /* time of new chain is current time */
3848 chain_time = current_time;
3850 if (ogg->building_chain == NULL) {
3851 GstOggChain *newchain;
3853 newchain = gst_ogg_chain_new (ogg);
3854 newchain->offset = 0;
3855 /* set new chain begin time aligned with end time of old chain */
3856 newchain->begin_time = chain_time;
3857 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
3858 GST_TIME_ARGS (chain_time));
3860 /* and this is the one we are building now */
3861 ogg->building_chain = newchain;
3863 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
3866 pad = gst_ogg_demux_find_pad (ogg, serialno);
3869 result = gst_ogg_pad_submit_page (pad, page);
3871 GST_PUSH_LOCK (ogg);
3872 if (!ogg->pullmode && !ogg->push_disable_seeking) {
3873 /* no pad while probing for duration, we must have a chained stream,
3874 and we don't support them, so back off */
3875 GST_INFO_OBJECT (ogg, "We seem to have a chained stream, we won't seek");
3876 if (ogg->push_state == PUSH_DURATION) {
3879 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
3880 if (res != GST_FLOW_OK)
3884 /* only once we seeked back */
3885 GST_PUSH_LOCK (ogg);
3886 ogg->push_disable_seeking = TRUE;
3888 GST_PUSH_UNLOCK (ogg);
3889 /* no pad. This means an ogg page without bos has been seen for this
3890 * serialno. we just ignore it but post a warning... */
3891 GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
3892 (NULL), ("unknown ogg pad for serial %08x detected", serialno));
3895 GST_PUSH_UNLOCK (ogg);
3902 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
3903 (NULL), ("unknown ogg chain for serial %08x detected", serialno));
3904 return GST_FLOW_ERROR;
3908 /* streaming mode, receive a buffer, parse it, create pads for
3909 * the serialno, submit pages and packets to the oggpads
3911 static GstFlowReturn
3912 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
3916 GstFlowReturn result = GST_FLOW_OK;
3918 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
3920 GST_DEBUG_OBJECT (ogg, "enter");
3921 result = gst_ogg_demux_submit_buffer (ogg, buffer);
3923 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
3926 while (result == GST_FLOW_OK) {
3929 ret = ogg_sync_pageout (&ogg->sync, &page);
3931 /* need more data */
3934 /* discontinuity in the pages */
3935 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
3937 result = gst_ogg_demux_handle_page (ogg, &page);
3939 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
3943 if (ret == 0 || result == GST_FLOW_OK) {
3944 gst_ogg_demux_sync_streams (ogg);
3946 GST_DEBUG_OBJECT (ogg, "leave with %d", result);
3951 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
3953 GstOggChain *chain = ogg->current_chain;
3954 gboolean res = TRUE;
3957 chain = ogg->building_chain;
3962 for (i = 0; i < chain->streams->len; i++) {
3963 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3965 gst_event_ref (event);
3966 GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
3967 res &= gst_pad_push_event (GST_PAD (pad), event);
3970 GST_WARNING_OBJECT (ogg, "No chain to forward event to");
3972 gst_event_unref (event);
3977 static GstFlowReturn
3978 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
3983 /* store the value */
3984 pad->last_ret = ret;
3986 /* any other error that is not-linked can be returned right
3988 if (ret != GST_FLOW_NOT_LINKED)
3991 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
3992 chain = ogg->current_chain;
3996 for (i = 0; i < chain->streams->len; i++) {
3997 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
3999 ret = opad->last_ret;
4000 /* some other return value (must be SUCCESS but we can return
4001 * other values as well) */
4002 if (ret != GST_FLOW_NOT_LINKED)
4005 /* if we get here, all other pads were unlinked and we return
4006 * NOT_LINKED then */
4012 /* returns TRUE if all streams in current chain reached EOS, FALSE otherwise */
4014 gst_ogg_demux_check_eos (GstOggDemux * ogg)
4017 gboolean eos = TRUE;
4019 chain = ogg->current_chain;
4020 if (G_LIKELY (chain)) {
4023 for (i = 0; i < chain->streams->len; i++) {
4024 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4026 eos = eos && opad->is_eos;
4035 static GstFlowReturn
4036 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4041 if (ogg->offset == ogg->length) {
4042 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4043 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
4048 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4049 ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
4050 if (ret != GST_FLOW_OK) {
4051 GST_LOG_OBJECT (ogg, "Failed pull_range");
4055 ogg->offset += gst_buffer_get_size (buffer);
4057 if (G_UNLIKELY (ogg->newsegment)) {
4058 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4059 ogg->newsegment = NULL;
4062 ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
4063 if (ret != GST_FLOW_OK) {
4064 GST_LOG_OBJECT (ogg, "Failed demux_chain");
4068 /* check for the end of the segment */
4069 if (gst_ogg_demux_check_eos (ogg)) {
4070 GST_LOG_OBJECT (ogg, "got EOS");
4080 * We read the pages backwards and send the packets forwards. The first packet
4081 * in the page will be pushed with the DISCONT flag set.
4083 * Special care has to be taken for continued pages, which we can only decode
4084 * when we have the previous page(s).
4086 static GstFlowReturn
4087 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4093 if (ogg->offset == 0) {
4094 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4095 " == 0", ogg->offset);
4100 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
4101 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
4102 if (ret != GST_FLOW_OK)
4105 ogg->offset = offset;
4107 if (G_UNLIKELY (ogg->newsegment)) {
4108 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4109 ogg->newsegment = NULL;
4112 ret = gst_ogg_demux_handle_page (ogg, &page);
4113 if (ret != GST_FLOW_OK)
4116 /* check for the end of the segment */
4117 if (gst_ogg_demux_check_eos (ogg)) {
4118 GST_LOG_OBJECT (ogg, "got EOS");
4127 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4133 chain = ogg->current_chain;
4134 cur = ogg->segment.position;
4135 if (chain == NULL || cur == -1)
4138 for (i = 0; i < chain->streams->len; i++) {
4139 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4141 /* Theoretically, we should be doing this for all streams, but we're only
4142 * doing it for known-to-be-sparse streams at the moment in order not to
4143 * break things for wrongly-muxed streams (like we used to produce once) */
4144 if (stream->map.is_sparse && stream->position != GST_CLOCK_TIME_NONE) {
4146 /* Does this stream lag? Random threshold of 2 seconds */
4147 if (GST_CLOCK_DIFF (stream->position, cur) > (2 * GST_SECOND)) {
4148 GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
4149 "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
4150 GST_TIME_ARGS (stream->position), GST_TIME_ARGS (cur));
4152 stream->position = cur;
4155 ogg->segment.base += cur - stream->position;
4156 /* advance stream time (FIXME: is this right, esp. time_pos?) */
4157 gst_pad_push_event (GST_PAD_CAST (stream),
4158 gst_event_new_new_segment (TRUE, ogg->segment.rate,
4159 ogg->segment.applied_rate,
4160 GST_FORMAT_TIME, stream->position, -1, stream->position));
4167 /* random access code
4169 * - first find all the chains and streams by scanning the file.
4170 * - then get and chain buffers, just like the streaming case.
4171 * - when seeking, we can use the chain info to perform the seek.
4174 gst_ogg_demux_loop (GstOggPad * pad)
4180 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4182 if (ogg->need_chains) {
4185 /* this is the only place where we write chains and thus need to lock. */
4186 GST_CHAIN_LOCK (ogg);
4187 ret = gst_ogg_demux_find_chains (ogg);
4188 GST_CHAIN_UNLOCK (ogg);
4189 if (ret != GST_FLOW_OK)
4190 goto chain_read_failed;
4192 ogg->need_chains = FALSE;
4194 GST_OBJECT_LOCK (ogg);
4195 ogg->running = TRUE;
4198 GST_OBJECT_UNLOCK (ogg);
4200 /* and seek to configured positions without FLUSH */
4201 res = gst_ogg_demux_perform_seek_pull (ogg, event);
4203 gst_event_unref (event);
4209 if (ogg->segment.rate >= 0.0)
4210 ret = gst_ogg_demux_loop_forward (ogg);
4212 ret = gst_ogg_demux_loop_reverse (ogg);
4214 if (ret != GST_FLOW_OK)
4217 gst_ogg_demux_sync_streams (ogg);
4223 /* error was posted */
4228 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4229 ("failed to start demuxing ogg"));
4230 ret = GST_FLOW_ERROR;
4235 const gchar *reason = gst_flow_get_name (ret);
4236 GstEvent *event = NULL;
4238 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4239 gst_pad_pause_task (ogg->sinkpad);
4241 if (ret == GST_FLOW_EOS) {
4242 /* perform EOS logic */
4243 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4245 GstMessage *message;
4247 /* for segment playback we need to post when (in stream time)
4248 * we stopped, this is either stop (when set) or the duration. */
4249 if ((stop = ogg->segment.stop) == -1)
4250 stop = ogg->segment.duration;
4252 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
4254 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
4256 gst_message_set_seqnum (message, ogg->seqnum);
4258 gst_element_post_message (GST_ELEMENT (ogg), message);
4260 /* normal playback, send EOS to all linked pads */
4261 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
4262 event = gst_event_new_eos ();
4264 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
4265 GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
4266 (_("Internal data stream error.")),
4267 ("stream stopped, reason %s", reason));
4268 event = gst_event_new_eos ();
4271 /* For wrong-state we still want to pause the task and stop
4272 * but no error message or other things are necessary.
4273 * wrong-state is no real error and will be caused by flushing,
4274 * e.g. because of a flushing seek.
4277 gst_event_set_seqnum (event, ogg->seqnum);
4278 gst_ogg_demux_send_event (ogg, event);
4285 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
4289 gst_ogg_demux_deactivate_current_chain (ogg);
4291 GST_CHAIN_LOCK (ogg);
4292 for (i = 0; i < ogg->chains->len; i++) {
4293 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4295 gst_ogg_chain_free (chain);
4297 ogg->chains = g_array_set_size (ogg->chains, 0);
4298 GST_CHAIN_UNLOCK (ogg);
4301 /* this function is called when the pad is activated and should start
4304 * We check if we can do random access to decide if we work push or
4308 gst_ogg_demux_sink_activate (GstPad * sinkpad)
4313 query = gst_query_new_scheduling ();
4315 if (!gst_pad_peer_query (sinkpad, query)) {
4316 gst_query_unref (query);
4320 gst_query_parse_scheduling (query, &pull_mode, NULL, NULL, NULL, NULL, NULL);
4321 gst_query_unref (query);
4326 GST_DEBUG_OBJECT (sinkpad, "activating pull");
4327 return gst_pad_activate_pull (sinkpad, TRUE);
4331 GST_DEBUG_OBJECT (sinkpad, "activating push");
4332 return gst_pad_activate_push (sinkpad, TRUE);
4336 /* this function gets called when we activate ourselves in push mode.
4337 * We cannot seek (ourselves) in the stream */
4339 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
4343 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
4345 ogg->pullmode = FALSE;
4346 ogg->resync = FALSE;
4351 /* this function gets called when we activate ourselves in pull mode.
4352 * We can perform random access to the resource and we start a task
4353 * to start reading */
4355 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
4359 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
4362 ogg->need_chains = TRUE;
4363 ogg->pullmode = TRUE;
4365 return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
4368 return gst_pad_stop_task (sinkpad);
4372 static GstStateChangeReturn
4373 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
4376 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
4378 ogg = GST_OGG_DEMUX (element);
4380 switch (transition) {
4381 case GST_STATE_CHANGE_NULL_TO_READY:
4383 ogg_sync_init (&ogg->sync);
4385 case GST_STATE_CHANGE_READY_TO_PAUSED:
4386 ogg_sync_reset (&ogg->sync);
4387 ogg->running = FALSE;
4389 ogg->total_time = -1;
4390 GST_PUSH_LOCK (ogg);
4391 ogg->push_byte_offset = 0;
4392 ogg->push_byte_length = -1;
4393 ogg->push_time_length = GST_CLOCK_TIME_NONE;
4394 ogg->push_time_offset = GST_CLOCK_TIME_NONE;
4395 ogg->push_disable_seeking = FALSE;
4396 ogg->push_state = PUSH_PLAYING;
4397 GST_PUSH_UNLOCK (ogg);
4398 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
4400 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4406 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4408 switch (transition) {
4409 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4411 case GST_STATE_CHANGE_PAUSED_TO_READY:
4412 gst_ogg_demux_clear_chains (ogg);
4413 GST_OBJECT_LOCK (ogg);
4414 ogg->running = FALSE;
4415 GST_OBJECT_UNLOCK (ogg);
4417 case GST_STATE_CHANGE_READY_TO_NULL:
4418 ogg_sync_clear (&ogg->sync);
4427 gst_ogg_demux_plugin_init (GstPlugin * plugin)
4429 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
4430 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
4431 "ogg demuxer setup stage when parsing pipeline");
4434 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
4436 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4437 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4440 return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
4441 GST_TYPE_OGG_DEMUX);
4444 /* prints all info about the element */
4445 #undef GST_CAT_DEFAULT
4446 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
4448 #ifdef GST_DISABLE_GST_DEBUG
4451 gst_ogg_print (GstOggDemux * ogg)
4456 #else /* !GST_DISABLE_GST_DEBUG */
4459 gst_ogg_print (GstOggDemux * ogg)
4463 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
4464 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4465 GST_TIME_ARGS (ogg->total_time));
4467 for (i = 0; i < ogg->chains->len; i++) {
4468 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4470 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
4471 GST_INFO_OBJECT (ogg,
4472 " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
4474 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
4475 GST_TIME_ARGS (chain->begin_time));
4476 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4477 GST_TIME_ARGS (chain->total_time));
4478 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
4479 GST_TIME_ARGS (chain->segment_start));
4480 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
4481 GST_TIME_ARGS (chain->segment_stop));
4483 for (j = 0; j < chain->streams->len; j++) {
4484 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
4486 GST_INFO_OBJECT (ogg, " stream %08x: %s", stream->map.serialno,
4487 gst_ogg_stream_get_media_type (&stream->map));
4488 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
4489 GST_TIME_ARGS (stream->start_time));
4493 #endif /* GST_DISABLE_GST_DEBUG */