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)
44 #include <gst/gst-i18n-plugin.h>
45 #include <gst/tag/tag.h>
47 #include "gstoggdemux.h"
49 #include "gst/glib-compat-private.h"
51 #define CHUNKSIZE (8500) /* this is out of vorbisfile */
53 /* we hope we get a granpos within this many bytes off the end */
54 #define DURATION_CHUNK_OFFSET (64*1024)
56 /* stop duration checks within this much of EOS */
57 #define EOS_AVOIDANCE_THRESHOLD 8192
59 /* An Ogg page can not be larger than 255 segments of 255 bytes, plus
61 #define MAX_OGG_PAGE_SIZE (255 * 255 + 26)
63 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
64 #define GST_FLOW_SKIP_PUSH GST_FLOW_CUSTOM_SUCCESS_1
66 #define GST_CHAIN_LOCK(ogg) g_mutex_lock((ogg)->chain_lock)
67 #define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock((ogg)->chain_lock)
69 #define GST_PUSH_LOCK(ogg) \
71 GST_TRACE_OBJECT(ogg, "Push lock"); \
72 g_mutex_lock((ogg)->push_lock); \
75 #define GST_PUSH_UNLOCK(ogg) \
77 GST_TRACE_OBJECT(ogg, "Push unlock"); \
78 g_mutex_unlock((ogg)->push_lock); \
81 GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
82 GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
83 #define GST_CAT_DEFAULT gst_ogg_demux_debug
87 _ogg_packet_copy (const ogg_packet * packet)
89 ogg_packet *ret = g_slice_new (ogg_packet);
92 ret->packet = g_memdup (packet->packet, packet->bytes);
98 _ogg_packet_free (ogg_packet * packet)
100 g_free (packet->packet);
101 g_slice_free (ogg_packet, packet);
105 gst_ogg_page_copy (ogg_page * page)
107 ogg_page *p = g_slice_new (ogg_page);
109 /* make a copy of the page */
110 p->header = g_memdup (page->header, page->header_len);
111 p->header_len = page->header_len;
112 p->body = g_memdup (page->body, page->body_len);
113 p->body_len = page->body_len;
119 gst_ogg_page_free (ogg_page * page)
121 g_free (page->header);
123 g_slice_free (ogg_page, page);
126 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
127 GstOggChain * chain);
128 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
129 GstOggChain * chain, GstEvent * event);
130 static void gst_ogg_pad_mark_discont (GstOggPad * pad);
131 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
133 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
135 static gboolean gst_ogg_demux_receive_event (GstElement * element,
138 static void gst_ogg_pad_dispose (GObject * object);
139 static void gst_ogg_pad_finalize (GObject * object);
141 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstObject * parent,
143 static gboolean gst_ogg_pad_event (GstPad * pad, GstObject * parent,
145 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
148 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
149 GstOggPad * pad, GstFlowReturn ret);
150 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
152 GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg,
153 GstCaps * caps, GList * headers);
154 static gboolean gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
155 static gboolean gst_ogg_demux_perform_seek_push (GstOggDemux * ogg,
157 static gboolean gst_ogg_demux_check_duration_push (GstOggDemux * ogg,
158 GstSeekFlags flags, GstEvent * event);
160 GType gst_ogg_pad_get_type (void);
161 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
164 gst_ogg_pad_class_init (GstOggPadClass * klass)
166 GObjectClass *gobject_class;
168 gobject_class = (GObjectClass *) klass;
170 gobject_class->dispose = gst_ogg_pad_dispose;
171 gobject_class->finalize = gst_ogg_pad_finalize;
175 gst_ogg_pad_init (GstOggPad * pad)
177 gst_pad_set_event_function (GST_PAD (pad),
178 GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
179 gst_pad_set_query_function (GST_PAD (pad),
180 GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
181 gst_pad_use_fixed_caps (GST_PAD (pad));
183 pad->current_granule = -1;
184 pad->keyframe_granule = -1;
186 pad->start_time = GST_CLOCK_TIME_NONE;
188 pad->position = GST_CLOCK_TIME_NONE;
190 pad->have_type = FALSE;
191 pad->continued = NULL;
192 pad->map.headers = NULL;
193 pad->map.queued = NULL;
195 pad->map.granulerate_n = 0;
196 pad->map.granulerate_d = 0;
197 pad->map.granuleshift = -1;
201 gst_ogg_pad_dispose (GObject * object)
203 GstOggPad *pad = GST_OGG_PAD (object);
208 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
209 g_list_free (pad->map.headers);
210 pad->map.headers = NULL;
211 g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
212 g_list_free (pad->map.queued);
213 pad->map.queued = NULL;
215 g_free (pad->map.index);
216 pad->map.index = NULL;
218 /* clear continued pages */
219 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
220 g_list_free (pad->continued);
221 pad->continued = NULL;
224 gst_caps_unref (pad->map.caps);
225 pad->map.caps = NULL;
228 if (pad->map.taglist) {
229 gst_tag_list_free (pad->map.taglist);
230 pad->map.taglist = NULL;
233 ogg_stream_reset (&pad->map.stream);
235 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
239 gst_ogg_pad_finalize (GObject * object)
241 GstOggPad *pad = GST_OGG_PAD (object);
243 ogg_stream_clear (&pad->map.stream);
245 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
249 gst_ogg_pad_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
254 ogg = GST_OGG_DEMUX (parent);
256 switch (GST_QUERY_TYPE (query)) {
257 case GST_QUERY_DURATION:
260 gint64 total_time = -1;
262 gst_query_parse_duration (query, &format, NULL);
263 /* can only get position in time */
264 if (format != GST_FORMAT_TIME)
267 if (ogg->total_time != -1) {
268 /* we can return the total length */
269 total_time = ogg->total_time;
271 gint bitrate = ogg->bitrate;
273 /* try with length and bitrate */
277 /* ask upstream for total length in bytes */
278 uquery = gst_query_new_duration (GST_FORMAT_BYTES);
279 if (gst_pad_peer_query (ogg->sinkpad, uquery)) {
282 gst_query_parse_duration (uquery, NULL, &length);
284 /* estimate using the bitrate */
286 gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);
289 "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
290 GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
292 gst_query_unref (uquery);
296 gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
299 case GST_QUERY_SEEKING:
303 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
304 if (format == GST_FORMAT_TIME) {
305 gboolean seekable = FALSE;
308 GST_CHAIN_LOCK (ogg);
311 stop = ogg->total_time;
312 } else if (ogg->push_disable_seeking) {
314 } else if (ogg->current_chain == NULL) {
317 /* assume we can seek if upstream is seekable in BYTES format */
318 GST_LOG_OBJECT (ogg, "no current chain, check upstream seekability");
319 squery = gst_query_new_seeking (GST_FORMAT_BYTES);
320 if (gst_pad_peer_query (ogg->sinkpad, squery))
321 gst_query_parse_seeking (squery, NULL, &seekable, NULL, NULL);
324 gst_query_unref (squery);
325 } else if (ogg->current_chain->streams->len) {
329 for (i = 0; i < ogg->current_chain->streams->len; i++) {
331 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
334 if (pad->map.index != NULL && pad->map.n_index != 0) {
336 GstClockTime idx_time;
338 idx = &pad->map.index[pad->map.n_index - 1];
340 gst_util_uint64_scale (idx->timestamp, GST_SECOND,
345 stop = MAX (idx_time, stop);
347 stop = -1; /* we've no clue, sadly, without seeking */
352 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
353 GST_CHAIN_UNLOCK (ogg);
361 res = gst_pad_query_default (pad, parent, query);
371 GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
378 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
383 ogg = GST_OGG_DEMUX (element);
385 switch (GST_EVENT_TYPE (event)) {
387 /* now do the seek */
388 res = gst_ogg_demux_perform_seek (ogg, event);
389 gst_event_unref (event);
392 GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
401 GST_DEBUG_OBJECT (ogg, "error handling event");
402 gst_event_unref (event);
408 gst_ogg_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
413 ogg = GST_OGG_DEMUX (parent);
415 switch (GST_EVENT_TYPE (event)) {
417 /* now do the seek */
418 res = gst_ogg_demux_perform_seek (ogg, event);
419 gst_event_unref (event);
422 res = gst_pad_event_default (pad, parent, event);
430 gst_ogg_pad_reset (GstOggPad * pad)
432 ogg_stream_reset (&pad->map.stream);
434 GST_DEBUG_OBJECT (pad, "doing reset");
436 /* clear continued pages */
437 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
438 g_list_free (pad->continued);
439 pad->continued = NULL;
441 pad->last_ret = GST_FLOW_OK;
442 pad->position = GST_CLOCK_TIME_NONE;
443 pad->current_granule = -1;
444 pad->keyframe_granule = -1;
448 /* queue data, basically takes the packet, puts it in a buffer and store the
449 * buffer in the queued list. */
451 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
453 #ifndef GST_DISABLE_GST_DEBUG
454 GstOggDemux *ogg = pad->ogg;
457 GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x",
458 pad, pad->map.serialno);
460 pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
467 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
468 gboolean push_headers)
470 GstBuffer *buf = NULL;
471 GstFlowReturn ret, cret;
472 GstOggDemux *ogg = pad->ogg;
478 GstClockTime out_timestamp, out_duration;
479 guint64 out_offset, out_offset_end;
480 gboolean delta_unit = FALSE;
484 GST_DEBUG_OBJECT (pad, "Chaining %d %d %" GST_TIME_FORMAT " %d %p",
485 ogg->pullmode, ogg->push_state, GST_TIME_ARGS (ogg->push_time_length),
486 ogg->push_disable_seeking, ogg->building_chain);
488 if (!ogg->pullmode && ogg->push_state == PUSH_PLAYING
489 && ogg->push_time_length == GST_CLOCK_TIME_NONE
490 && !ogg->push_disable_seeking) {
491 if (!ogg->building_chain) {
492 /* we got all headers, now try to get duration */
493 if (!gst_ogg_demux_check_duration_push (ogg, GST_SEEK_FLAG_FLUSH, NULL)) {
494 GST_PUSH_UNLOCK (ogg);
498 GST_PUSH_UNLOCK (ogg);
501 GST_PUSH_UNLOCK (ogg);
503 GST_DEBUG_OBJECT (ogg,
504 "%p streaming to peer serial %08x", pad, pad->map.serialno);
506 if (pad->map.is_ogm) {
510 data = packet->packet;
511 bytes = packet->bytes;
516 if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
517 /* We don't push header packets for OGM */
521 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
522 delta_unit = (((data[0] & 0x08) >> 3) == 0);
526 /* Strip trailing \0 for subtitles */
527 if (pad->map.is_ogm_text) {
528 while (bytes && data[bytes - 1] == 0) {
533 } else if (pad->map.is_vp8) {
534 if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
536 (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) {
537 /* We don't push header packets for VP8 */
547 /* get timing info for the packet */
548 if (gst_ogg_stream_packet_is_header (&pad->map, packet)) {
550 GST_DEBUG_OBJECT (ogg, "packet is header");
552 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
553 GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
557 out_timestamp = GST_CLOCK_TIME_NONE;
558 out_duration = GST_CLOCK_TIME_NONE;
562 if (packet->granulepos != -1) {
563 gint64 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
566 GST_ERROR_OBJECT (ogg,
567 "granulepos %" G_GINT64_FORMAT " yielded granule %" G_GINT64_FORMAT,
568 packet->granulepos, granule);
569 return GST_FLOW_ERROR;
571 pad->current_granule = granule;
572 pad->keyframe_granule =
573 gst_ogg_stream_granulepos_to_key_granule (&pad->map,
575 GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
576 pad->current_granule);
577 } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
578 pad->current_granule += duration;
579 if (gst_ogg_stream_packet_is_key_frame (&pad->map, packet)) {
580 pad->keyframe_granule = pad->current_granule;
582 GST_DEBUG_OBJECT (ogg, "interpolating granule %" G_GUINT64_FORMAT,
583 pad->current_granule);
585 if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
586 /* negative rates, only set timestamp on the packets with a granulepos */
592 /* we only push buffers after we have a valid granule. This is done so that
593 * we nicely skip packets without a timestamp after a seek. This is ok
594 * because we base or seek on the packet after the page with the smaller
596 if (pad->current_granule == -1)
599 if (pad->map.is_ogm) {
600 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
601 pad->current_granule);
602 out_duration = gst_util_uint64_scale (duration,
603 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
604 } else if (pad->map.is_sparse) {
605 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
606 pad->current_granule);
607 if (duration == GST_CLOCK_TIME_NONE) {
608 out_duration = GST_CLOCK_TIME_NONE;
610 out_duration = gst_util_uint64_scale (duration,
611 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
614 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
615 pad->current_granule - duration);
617 gst_ogg_stream_granule_to_time (&pad->map,
618 pad->current_granule) - out_timestamp;
621 gst_ogg_stream_granule_to_granulepos (&pad->map,
622 pad->current_granule, pad->keyframe_granule);
624 gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
628 if (pad->map.is_ogm_text) {
629 /* check for invalid buffer sizes */
630 if (G_UNLIKELY (offset + trim >= packet->bytes))
637 buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
639 /* set delta flag for OGM content */
641 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
643 if (packet->packet != NULL) {
644 /* copy packet in buffer */
645 gst_buffer_fill (buf, 0, packet->packet + offset,
646 packet->bytes - offset - trim);
649 GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
650 GST_BUFFER_DURATION (buf) = out_duration;
651 GST_BUFFER_OFFSET (buf) = out_offset;
652 GST_BUFFER_OFFSET_END (buf) = out_offset_end;
654 /* Mark discont on the buffer */
656 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
657 pad->discont = FALSE;
660 pad->position = ogg->segment.position;
662 /* don't push the header packets when we are asked to skip them */
663 if (!packet->b_o_s || push_headers) {
664 ret = gst_pad_push (GST_PAD_CAST (pad), buf);
668 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
671 /* we're done with skeleton stuff */
672 if (pad->map.is_skeleton)
675 /* check if valid granulepos, then we can calculate the current
676 * position. We know the granule for each packet but we only want to update
677 * the position when we have a valid granulepos on the packet because else
678 * our time jumps around for the different streams. */
679 if (packet->granulepos < 0)
682 /* convert to time */
683 current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
686 /* convert to stream time */
687 if ((chain = pad->chain)) {
688 gint64 chain_start = 0;
690 if (chain->segment_start != GST_CLOCK_TIME_NONE)
691 chain_start = chain->segment_start;
693 current_time = current_time - chain_start + chain->begin_time;
696 /* and store as the current position */
697 ogg->segment.position = current_time;
699 GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
700 GST_TIME_ARGS (current_time));
702 /* check stream eos */
703 if ((ogg->segment.rate > 0.0 && ogg->segment.stop != GST_CLOCK_TIME_NONE &&
704 current_time > ogg->segment.stop) ||
705 (ogg->segment.rate < 0.0 && ogg->segment.start != GST_CLOCK_TIME_NONE &&
706 current_time < ogg->segment.start)) {
707 GST_DEBUG_OBJECT (ogg, "marking pad %p EOS", pad);
713 gst_buffer_unref (buf);
714 /* return combined flow result */
720 GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
726 GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
731 GST_DEBUG_OBJECT (ogg, "pad not added yet");
737 gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
740 guint64 start_time = G_MAXUINT64;
742 for (i = 0; i < chain->streams->len; i++) {
743 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
745 if (pad->map.is_skeleton)
748 /* can do this if the pad start time is not defined */
749 GST_DEBUG_OBJECT (ogg, "Pad %08x (%s) start time is %" GST_TIME_FORMAT,
750 pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map),
751 GST_TIME_ARGS (pad->start_time));
752 if (pad->start_time == GST_CLOCK_TIME_NONE) {
753 if (!pad->map.is_sparse) {
754 start_time = G_MAXUINT64;
758 start_time = MIN (start_time, pad->start_time);
765 gst_ogg_demux_collect_sync_time (GstOggDemux * ogg, GstOggChain * chain)
768 GstClockTime sync_time = GST_CLOCK_TIME_NONE;
771 GST_WARNING_OBJECT (ogg, "No chain!");
772 return GST_CLOCK_TIME_NONE;
775 for (i = 0; i < chain->streams->len; i++) {
776 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
778 if (pad->map.is_sparse)
781 if (pad->push_sync_time == GST_CLOCK_TIME_NONE) {
782 sync_time = GST_CLOCK_TIME_NONE;
785 if (sync_time == GST_CLOCK_TIME_NONE)
786 sync_time = pad->push_sync_time;
788 sync_time = MAX (sync_time, pad->push_sync_time);
794 /* submit a packet to the oggpad, this function will run the type detection
795 * code for the pad if this is the first packet for this stream
798 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
801 GstFlowReturn ret = GST_FLOW_OK;
803 GstOggDemux *ogg = pad->ogg;
805 GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x",
806 pad, pad->map.serialno);
808 if (!pad->have_type) {
809 pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
810 if (!pad->have_type && !pad->map.caps) {
811 pad->map.caps = gst_caps_new_empty_simple ("application/x-unknown");
813 if (pad->map.is_skeleton) {
814 GST_DEBUG_OBJECT (ogg, "we have a fishead");
815 /* copy values over to global ogg level */
816 ogg->basetime = pad->map.basetime;
817 ogg->prestime = pad->map.prestime;
819 /* use total time to update the total ogg time */
820 if (ogg->total_time == -1) {
821 ogg->total_time = pad->map.total_time;
822 } else if (pad->map.total_time > 0) {
823 ogg->total_time = MAX (ogg->total_time, pad->map.total_time);
827 gst_pad_push_event (GST_PAD (pad), gst_event_new_stream_start ());
828 gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
830 GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
834 if (pad->map.is_skeleton) {
839 /* try to parse the serialno first */
840 if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
843 GST_DEBUG_OBJECT (pad->ogg,
844 "got skeleton packet for stream 0x%08x", serialno);
846 skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
849 case GST_OGG_SKELETON_FISBONE:
850 /* parse the remainder of the fisbone in the pad with the serialno,
851 * note that we ignore the start_time as this is usually wrong for
853 gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
854 packet->bytes, NULL);
856 case GST_OGG_SKELETON_INDEX:
857 gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
860 /* use total time to update the total ogg time */
861 if (ogg->total_time == -1) {
862 ogg->total_time = skel_pad->map.total_time;
863 } else if (skel_pad->map.total_time > 0) {
864 ogg->total_time = MAX (ogg->total_time, skel_pad->map.total_time);
872 GST_WARNING_OBJECT (pad->ogg,
873 "found skeleton fisbone for an unknown stream 0x%08x", serialno);
878 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
881 GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
882 pad->current_granule = granule;
883 } else if (granule != -1) {
884 GST_ERROR_OBJECT (ogg,
885 "granulepos %" G_GINT64_FORMAT " yielded granule %" G_GINT64_FORMAT,
886 packet->granulepos, granule);
887 return GST_FLOW_ERROR;
890 /* restart header packet count when seeing a b_o_s page;
891 * particularly useful following a seek or even following chain finding */
893 GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
894 pad->map.n_header_packets_seen = 0;
895 if (!pad->map.have_headers) {
896 GST_DEBUG_OBJECT (ogg, "clearing header packets");
897 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
898 g_list_free (pad->map.headers);
899 pad->map.headers = NULL;
903 /* Overload the value of b_o_s in ogg_packet with a flag whether or
904 * not this is a header packet. Maybe some day this could be cleaned
906 packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
907 if (!packet->b_o_s) {
908 GST_DEBUG ("found non-header packet");
909 pad->map.have_headers = TRUE;
910 if (pad->start_time == GST_CLOCK_TIME_NONE) {
911 gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
912 GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
913 if (duration != -1) {
914 pad->map.accumulated_granule += duration;
915 GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
916 pad->map.accumulated_granule);
919 if (packet->granulepos != -1) {
920 ogg_int64_t start_granule;
923 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
926 GST_ERROR_OBJECT (ogg,
927 "granulepos %" G_GINT64_FORMAT " yielded granule %"
928 G_GINT64_FORMAT, packet->granulepos, granule);
929 return GST_FLOW_ERROR;
932 if (granule >= pad->map.accumulated_granule)
933 start_granule = granule - pad->map.accumulated_granule;
935 if (pad->map.forbid_start_clamping) {
936 GST_ERROR_OBJECT (ogg, "Start of stream maps to negative time");
937 return GST_FLOW_ERROR;
943 pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
945 GST_DEBUG_OBJECT (ogg,
946 "start time %" GST_TIME_FORMAT " (%" GST_TIME_FORMAT ") for %s "
947 "from granpos %" G_GINT64_FORMAT " (granule %" G_GINT64_FORMAT ", "
948 "accumulated granule %" G_GINT64_FORMAT,
949 GST_TIME_ARGS (pad->start_time), GST_TIME_ARGS (pad->start_time),
950 gst_ogg_stream_get_media_type (&pad->map),
951 (gint64) packet->granulepos, granule, pad->map.accumulated_granule);
953 packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
954 pad->map.accumulated_granule, pad->keyframe_granule);
958 /* look for tags in header packet (before inc header count) */
959 gst_ogg_stream_extract_tags (&pad->map, packet);
960 pad->map.n_header_packets_seen++;
961 if (!pad->map.have_headers) {
963 g_list_append (pad->map.headers, _ogg_packet_copy (packet));
964 GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
968 /* we know the start_time of the pad data, see if we
969 * can activate the complete chain if this is a dynamic
970 * chain. We need all the headers too for this. */
971 if (pad->start_time != GST_CLOCK_TIME_NONE && pad->map.have_headers) {
972 GstOggChain *chain = pad->chain;
974 /* check if complete chain has start time */
975 if (chain == ogg->building_chain) {
976 GstEvent *event = NULL;
981 GST_DEBUG_OBJECT (ogg, "need to resync");
983 /* when we need to resync after a seek, we wait until we have received
984 * timestamps on all streams */
985 start_time = gst_ogg_demux_collect_start_time (ogg, chain);
987 if (start_time != G_MAXUINT64) {
991 GST_DEBUG_OBJECT (ogg, "start_time: %" GST_TIME_FORMAT,
992 GST_TIME_ARGS (start_time));
994 if (chain->segment_start < start_time)
996 (start_time - chain->segment_start) + chain->begin_time;
998 segment_time = chain->begin_time;
1000 /* create the newsegment event we are going to send out */
1001 gst_segment_init (&segment, GST_FORMAT_TIME);
1003 GST_PUSH_LOCK (ogg);
1004 if (!ogg->pullmode && ogg->push_state == PUSH_LINEAR2) {
1005 /* if we are fast forwarding to the actual seek target,
1006 ensure previous frames are clipped */
1007 GST_DEBUG_OBJECT (ogg,
1008 "Resynced, starting segment at %" GST_TIME_FORMAT
1009 ", start_time %" GST_TIME_FORMAT,
1010 GST_TIME_ARGS (ogg->push_seek_time_original_target),
1011 GST_TIME_ARGS (start_time));
1012 segment.rate = ogg->push_seek_rate;
1013 segment.start = ogg->push_seek_time_original_target;
1015 segment.time = ogg->push_seek_time_original_target;
1016 event = gst_event_new_segment (&segment);
1017 ogg->push_state = PUSH_PLAYING;
1019 segment.rate = ogg->segment.rate;
1020 segment.applied_rate = ogg->segment.applied_rate;
1021 segment.start = start_time;
1022 segment.stop = chain->segment_stop;
1023 segment.time = segment_time;
1024 event = gst_event_new_segment (&segment);
1026 GST_PUSH_UNLOCK (ogg);
1028 ogg->resync = FALSE;
1031 /* see if we have enough info to activate the chain, we have enough info
1032 * when all streams have a valid start time. */
1033 if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
1036 GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
1037 GST_TIME_ARGS (chain->segment_start));
1038 GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT,
1039 GST_TIME_ARGS (chain->segment_stop));
1040 GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT,
1041 GST_TIME_ARGS (chain->begin_time));
1043 /* create the newsegment event we are going to send out */
1044 gst_segment_init (&segment, GST_FORMAT_TIME);
1045 segment.rate = ogg->segment.rate;
1046 segment.applied_rate = ogg->segment.applied_rate;
1047 segment.start = chain->segment_start;
1048 segment.stop = chain->segment_stop;
1049 segment.time = chain->begin_time;
1050 event = gst_event_new_segment (&segment);
1055 gst_event_set_seqnum (event, ogg->seqnum);
1057 gst_ogg_demux_activate_chain (ogg, chain, event);
1059 ogg->building_chain = NULL;
1064 /* if we are building a chain, store buffer for when we activate
1065 * it. This path is taken if we operate in streaming mode. */
1066 if (ogg->building_chain) {
1067 /* bos packets where stored in the header list so we can discard
1070 ret = gst_ogg_demux_queue_data (pad, packet);
1072 /* else we are completely streaming to the peer */
1074 ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
1079 /* flush at most @npackets from the stream layer. All packets if
1082 static GstFlowReturn
1083 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1085 GstFlowReturn result = GST_FLOW_OK;
1086 gboolean done = FALSE;
1095 ret = ogg_stream_packetout (&pad->map.stream, &packet);
1098 GST_LOG_OBJECT (ogg, "packetout done");
1102 GST_LOG_OBJECT (ogg, "packetout discont");
1103 if (!pad->map.is_sparse) {
1104 gst_ogg_chain_mark_discont (pad->chain);
1106 gst_ogg_pad_mark_discont (pad);
1110 GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1111 if (packet.bytes > ogg->max_packet_size)
1112 ogg->max_packet_size = packet.bytes;
1113 result = gst_ogg_pad_submit_packet (pad, &packet);
1114 /* not linked is not a problem, it's possible that we are still
1115 * collecting headers and that we don't have exposed the pads yet */
1116 if (result == GST_FLOW_NOT_LINKED)
1118 else if (result <= GST_FLOW_EOS)
1119 goto could_not_submit;
1122 GST_WARNING_OBJECT (ogg,
1123 "invalid return value %d for ogg_stream_packetout, resetting stream",
1125 gst_ogg_pad_reset (pad);
1130 done = (npackets == 0);
1138 GST_WARNING_OBJECT (ogg,
1139 "could not submit packet for stream %08x, "
1140 "error: %d", pad->map.serialno, result);
1141 gst_ogg_pad_reset (pad);
1147 gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg)
1149 if (ogg->push_last_seek_time >= ogg->push_seek_time_target) {
1150 GST_DEBUG_OBJECT (ogg, "We overshot by %" GST_TIME_FORMAT,
1151 GST_TIME_ARGS (ogg->push_last_seek_time - ogg->push_seek_time_target));
1152 ogg->push_offset1 = ogg->push_last_seek_offset;
1153 ogg->push_time1 = ogg->push_last_seek_time;
1154 ogg->seek_undershot = FALSE;
1156 GST_DEBUG_OBJECT (ogg, "We undershot by %" GST_TIME_FORMAT,
1157 GST_TIME_ARGS (ogg->push_seek_time_target - ogg->push_last_seek_time));
1158 ogg->push_offset0 = ogg->push_last_seek_offset;
1159 ogg->push_time0 = ogg->push_last_seek_time;
1160 ogg->seek_undershot = TRUE;
1165 gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg, float seek_quality)
1168 gint64 segment_bitrate;
1171 /* we might not know the length of the stream in time,
1172 so push_time1 might not be set */
1173 GST_DEBUG_OBJECT (ogg,
1174 "push time 1: %" GST_TIME_FORMAT ", dbytes %" G_GINT64_FORMAT,
1175 GST_TIME_ARGS (ogg->push_time1), ogg->push_offset1 - ogg->push_offset0);
1176 if (ogg->push_time1 == GST_CLOCK_TIME_NONE) {
1177 GST_DEBUG_OBJECT (ogg,
1178 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1179 ", time %" GST_TIME_FORMAT " (open ended)", ogg->push_offset0,
1180 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0));
1181 if (ogg->push_last_seek_time == ogg->push_start_time) {
1182 /* if we're at start and don't know the end time, we can't estimate
1183 bitrate, so get the nominal declared bitrate as a failsafe, or some
1184 random constant which will be discarded after we made a (probably
1185 dire) first guess */
1186 segment_bitrate = (ogg->bitrate > 0 ? ogg->bitrate : 1000);
1189 gst_util_uint64_scale (ogg->push_last_seek_offset - 0,
1190 8 * GST_SECOND, ogg->push_last_seek_time - ogg->push_start_time);
1194 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1195 segment_bitrate, 8 * GST_SECOND);
1196 ogg->seek_secant = TRUE;
1198 GST_DEBUG_OBJECT (ogg,
1199 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1200 ", time %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, ogg->push_offset0,
1201 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
1202 GST_TIME_ARGS (ogg->push_time1));
1203 if (ogg->push_time0 == ogg->push_time1) {
1204 best = ogg->push_offset0;
1207 gst_util_uint64_scale (ogg->push_offset1 - ogg->push_offset0,
1208 8 * GST_SECOND, ogg->push_time1 - ogg->push_time0);
1209 GST_DEBUG_OBJECT (ogg,
1210 "Local bitrate on the %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1211 " segment: %" G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_time0),
1212 GST_TIME_ARGS (ogg->push_time1), segment_bitrate);
1216 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1217 segment_bitrate, 8 * GST_SECOND);
1218 if (seek_quality < 0.5f && ogg->seek_secant) {
1219 gint64 new_best, best2 = (ogg->push_offset0 + ogg->push_offset1) / 2;
1220 /* if dire result, give as much as 25% weight to a dumb bisection guess */
1221 float secant_weight = 1.0f - ((0.5 - seek_quality) / 0.5f) * 0.25;
1222 new_best = (best * secant_weight + best2 * (1.0f - secant_weight));
1223 GST_DEBUG_OBJECT (ogg,
1224 "Secant says %" G_GINT64_FORMAT ", straight is %" G_GINT64_FORMAT
1225 ", new best %" G_GINT64_FORMAT " with secant_weight %f", best,
1226 best2, new_best, secant_weight);
1228 ogg->seek_secant = FALSE;
1230 ogg->seek_secant = TRUE;
1235 GST_DEBUG_OBJECT (ogg, "Raw best guess: %" G_GINT64_FORMAT, best);
1237 /* offset the guess down as we need to capture the start of the
1238 page we are targetting - but only do so if we did not undershoot
1239 last time, as we're likely to still do this time */
1240 if (!ogg->seek_undershot) {
1241 /* very small packets are packed on pages, so offset by at least
1242 a value which is likely to get us at least one page where the
1245 ogg->max_packet_size >
1246 ogg->max_page_size ? ogg->max_packet_size : ogg->max_page_size;
1247 GST_DEBUG_OBJECT (ogg, "Offsetting by %" G_GINT64_FORMAT, skew);
1251 /* do not seek too close to the bounds, as we stop seeking
1252 when we get to within max_packet_size before the target */
1253 if (best > ogg->push_offset1 - ogg->max_packet_size) {
1254 best = ogg->push_offset1 - ogg->max_packet_size;
1255 GST_DEBUG_OBJECT (ogg,
1256 "Too close to high bound, pushing back to %" G_GINT64_FORMAT, best);
1257 } else if (best < ogg->push_offset0 + ogg->max_packet_size) {
1258 best = ogg->push_offset0 + ogg->max_packet_size;
1259 GST_DEBUG_OBJECT (ogg,
1260 "Too close to low bound, pushing forth to %" G_GINT64_FORMAT, best);
1263 /* keep within bounds */
1264 if (best > ogg->push_offset1)
1265 best = ogg->push_offset1;
1266 if (best < ogg->push_offset0)
1267 best = ogg->push_offset0;
1269 GST_DEBUG_OBJECT (ogg, "Choosing target %" G_GINT64_FORMAT, best);
1274 gst_ogg_demux_record_keyframe_time (GstOggDemux * ogg, GstOggPad * pad,
1275 ogg_int64_t granpos)
1278 GstClockTime kf_time;
1280 kf_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, granpos);
1281 kf_time = gst_ogg_stream_granule_to_time (&pad->map, kf_granule);
1283 pad->push_kf_time = kf_time;
1286 /* returns the earliest keyframe time for all non sparse pads in the chain,
1287 * if known, and GST_CLOCK_TIME_NONE if not */
1289 gst_ogg_demux_get_earliest_keyframe_time (GstOggDemux * ogg)
1291 GstClockTime t = GST_CLOCK_TIME_NONE;
1292 GstOggChain *chain = ogg->building_chain;
1296 GST_WARNING_OBJECT (ogg, "No chain!");
1297 return GST_CLOCK_TIME_NONE;
1299 for (i = 0; i < chain->streams->len; i++) {
1300 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1302 if (pad->map.is_sparse)
1304 if (pad->push_kf_time == GST_CLOCK_TIME_NONE)
1305 return GST_CLOCK_TIME_NONE;
1306 if (t == GST_CLOCK_TIME_NONE || pad->push_kf_time < t)
1307 t = pad->push_kf_time;
1313 /* MUST be called with the push lock locked, and will unlock it
1314 regardless of return value. */
1315 static GstFlowReturn
1316 gst_ogg_demux_seek_back_after_push_duration_check_unlock (GstOggDemux * ogg)
1320 /* Get the delayed event, if any */
1321 event = ogg->push_mode_seek_delayed_event;
1322 ogg->push_mode_seek_delayed_event = NULL;
1324 ogg->push_state = PUSH_PLAYING;
1326 GST_PUSH_UNLOCK (ogg);
1329 /* If there is one, perform it */
1330 gst_ogg_demux_perform_seek_push (ogg, event);
1332 /* If there wasn't, seek back at start to start normal playback */
1333 GST_INFO_OBJECT (ogg, "Seeking back to 0 after duration check");
1334 event = gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1335 GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
1336 GST_SEEK_TYPE_SET, 1, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
1337 if (!gst_pad_push_event (ogg->sinkpad, event)) {
1338 GST_WARNING_OBJECT (ogg, "Failed seeking back to start");
1339 return GST_FLOW_ERROR;
1347 gst_ogg_demux_estimate_seek_quality (GstOggDemux * ogg)
1349 gint64 diff; /* how far from the goal we ended up */
1350 gint64 dist; /* how far we moved this iteration */
1353 if (ogg->push_prev_seek_time == GST_CLOCK_TIME_NONE) {
1354 /* for the first seek, we pretend we got a good seek,
1355 as we don't have a previous seek yet */
1359 /* We take a guess at how good the last seek was at guessing
1360 the byte target by comparing the amplitude of the last
1361 seek to the error */
1362 diff = ogg->push_seek_time_target - ogg->push_last_seek_time;
1365 dist = ogg->push_last_seek_time - ogg->push_prev_seek_time;
1369 seek_quality = (dist == 0) ? 0.0f : 1.0f / (1.0f + diff / (float) dist);
1371 GST_DEBUG_OBJECT (ogg,
1372 "We moved %" GST_TIME_FORMAT ", we're off by %" GST_TIME_FORMAT
1373 ", seek quality %f", GST_TIME_ARGS (dist), GST_TIME_ARGS (diff),
1375 return seek_quality;
1379 gst_ogg_demux_update_bisection_stats (GstOggDemux * ogg)
1383 GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps",
1384 ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]);
1386 for (n = 0; n < 2; ++n) {
1387 ogg->stats_bisection_steps[n] += ogg->push_bisection_steps[n];
1388 if (ogg->stats_bisection_max_steps[n] < ogg->push_bisection_steps[n])
1389 ogg->stats_bisection_max_steps[n] = ogg->push_bisection_steps[n];
1391 ogg->stats_nbisections++;
1393 GST_INFO_OBJECT (ogg,
1394 "So far, %.2f + %.2f bisections needed per seek (max %d + %d)",
1395 ogg->stats_bisection_steps[0] / (float) ogg->stats_nbisections,
1396 ogg->stats_bisection_steps[1] / (float) ogg->stats_nbisections,
1397 ogg->stats_bisection_max_steps[0], ogg->stats_bisection_max_steps[1]);
1401 gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
1403 GstOggDemux *ogg = pad->ogg;
1404 ogg_int64_t granpos = ogg_page_granulepos (page);
1406 GST_PUSH_LOCK (ogg);
1408 if (ogg->push_start_time == GST_CLOCK_TIME_NONE) {
1409 ogg->push_start_time =
1410 gst_ogg_stream_get_start_time_for_granulepos (&pad->map, granpos);
1411 GST_DEBUG_OBJECT (ogg, "Stream start time: %" GST_TIME_FORMAT,
1412 GST_TIME_ARGS (ogg->push_start_time));
1414 ogg->push_time_offset =
1415 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1416 if (ogg->push_time_offset > 0) {
1417 GST_DEBUG_OBJECT (ogg, "Bitrate since start: %" G_GUINT64_FORMAT,
1418 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
1419 ogg->push_time_offset));
1422 if (ogg->push_state == PUSH_DURATION) {
1424 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1426 if (ogg->total_time == GST_CLOCK_TIME_NONE || t > ogg->total_time) {
1427 GST_DEBUG_OBJECT (ogg, "New total time: %" GST_TIME_FORMAT,
1429 ogg->total_time = t;
1430 ogg->push_time_length = t;
1433 /* If we were determining the duration of the stream, we're now done,
1434 and can get back to sending the original event we delayed.
1435 We stop a bit before the end of the stream, as if we get a EOS
1436 event and there is a queue2 upstream (such as when using playbin2),
1437 it will pause the task *after* we come back from the EOS handler,
1438 so we cannot prevent the pausing by issuing a seek. */
1439 if (ogg->push_byte_offset + EOS_AVOIDANCE_THRESHOLD >=
1440 ogg->push_byte_length) {
1441 GstMessage *message;
1444 /* tell the pipeline we've just found out the duration */
1445 ogg->push_time_length = ogg->total_time;
1446 GST_INFO_OBJECT (ogg, "New duration found: %" GST_TIME_FORMAT,
1447 GST_TIME_ARGS (ogg->total_time));
1449 gst_message_new_duration (GST_OBJECT (ogg), GST_FORMAT_TIME,
1451 gst_element_post_message (GST_ELEMENT (ogg), message);
1453 GST_DEBUG_OBJECT (ogg,
1454 "We're close enough to the end, and we're scared "
1455 "to get too close, seeking back to start");
1457 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1458 if (res != GST_FLOW_OK)
1460 return GST_FLOW_SKIP_PUSH;
1462 GST_PUSH_UNLOCK (ogg);
1464 return GST_FLOW_SKIP_PUSH;
1468 /* if we're seeking, look at time, and decide what to do */
1469 if (ogg->push_state != PUSH_PLAYING && ogg->push_state != PUSH_LINEAR2) {
1474 gboolean close_enough;
1477 /* ignore -1 granpos when seeking, we want to sync on a real granpos */
1479 GST_PUSH_UNLOCK (ogg);
1480 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1482 return GST_FLOW_SKIP_PUSH;
1485 t = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1487 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1488 GstClockTime sync_time;
1490 if (pad->push_sync_time == GST_CLOCK_TIME_NONE)
1491 pad->push_sync_time = t;
1492 GST_DEBUG_OBJECT (ogg, "Got timestamp %" GST_TIME_FORMAT " for %s",
1493 GST_TIME_ARGS (t), gst_ogg_stream_get_media_type (&pad->map));
1494 sync_time = gst_ogg_demux_collect_sync_time (ogg, ogg->building_chain);
1495 if (sync_time == GST_CLOCK_TIME_NONE) {
1496 GST_PUSH_UNLOCK (ogg);
1497 GST_DEBUG_OBJECT (ogg,
1498 "Not enough timing info collected for sync, waiting for more");
1499 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1501 return GST_FLOW_SKIP_PUSH;
1503 ogg->push_last_seek_time = sync_time;
1505 GST_DEBUG_OBJECT (ogg,
1506 "Bisection just seeked at %" G_GINT64_FORMAT ", time %"
1507 GST_TIME_FORMAT ", target was %" GST_TIME_FORMAT,
1508 ogg->push_last_seek_offset,
1509 GST_TIME_ARGS (ogg->push_last_seek_time),
1510 GST_TIME_ARGS (ogg->push_seek_time_target));
1512 if (ogg->push_time1 != GST_CLOCK_TIME_NONE) {
1513 seek_quality = gst_ogg_demux_estimate_seek_quality (ogg);
1514 GST_DEBUG_OBJECT (ogg,
1515 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1516 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1517 " (%" GST_TIME_FORMAT "), seek quality %f", ogg->push_offset0,
1518 ogg->push_offset1, ogg->push_offset1 - ogg->push_offset0,
1519 GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1),
1520 GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0), seek_quality);
1522 /* in a open ended seek, we can't do bisection, so we pretend
1523 we like our result so far */
1524 seek_quality = 1.0f;
1525 GST_DEBUG_OBJECT (ogg,
1526 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1527 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - unknown",
1528 ogg->push_offset0, ogg->push_offset1,
1529 ogg->push_offset1 - ogg->push_offset0,
1530 GST_TIME_ARGS (ogg->push_time0));
1532 ogg->push_prev_seek_time = ogg->push_last_seek_time;
1534 gst_ogg_demux_setup_bisection_bounds (ogg);
1536 best = gst_ogg_demux_estimate_bisection_target (ogg, seek_quality);
1538 if (ogg->push_seek_time_target == 0) {
1539 GST_DEBUG_OBJECT (ogg, "Seeking to 0, deemed close enough");
1540 close_enough = (ogg->push_last_seek_time == 0);
1542 /* TODO: make this dependent on framerate ? */
1543 GstClockTime time_threshold = GST_SECOND / 2;
1544 guint64 byte_threshold =
1545 (ogg->max_packet_size >
1546 64 * 1024 ? ogg->max_packet_size : 64 * 1024);
1548 /* We want to be within half a second before the target,
1549 or before the target and half less or equal to the max
1550 packet size left to search in */
1551 if (time_threshold > ogg->push_seek_time_target)
1552 time_threshold = ogg->push_seek_time_target;
1553 close_enough = ogg->push_last_seek_time < ogg->push_seek_time_target
1554 && (ogg->push_last_seek_time >=
1555 ogg->push_seek_time_target - time_threshold
1556 || ogg->push_offset1 <= ogg->push_offset0 + byte_threshold);
1557 GST_DEBUG_OBJECT (ogg,
1558 "testing if we're close enough: %" GST_TIME_FORMAT " <= %"
1559 GST_TIME_FORMAT " < %" GST_TIME_FORMAT ", or %" G_GUINT64_FORMAT
1560 " <= %" G_GUINT64_FORMAT " ? %s",
1561 GST_TIME_ARGS (ogg->push_seek_time_target - time_threshold),
1562 GST_TIME_ARGS (ogg->push_last_seek_time),
1563 GST_TIME_ARGS (ogg->push_seek_time_target),
1564 ogg->push_offset1 - ogg->push_offset0, byte_threshold,
1565 close_enough ? "Yes" : "No");
1568 if (close_enough || best == ogg->push_last_seek_offset) {
1569 if (ogg->push_state == PUSH_BISECT1) {
1570 /* we now know the time segment we'll have to search for
1571 the second bisection */
1572 ogg->push_time0 = ogg->push_start_time;
1573 ogg->push_offset0 = 0;
1575 GST_DEBUG_OBJECT (ogg,
1576 "Seek to %" GST_TIME_FORMAT
1577 " (%lx) done, now gathering pages for all non-sparse streams",
1578 GST_TIME_ARGS (ogg->push_seek_time_target), (long) granpos);
1579 ogg->push_state = PUSH_LINEAR1;
1581 /* If we're asked for an accurate seek, we'll go forward till
1582 we get to the original seek target time, else we'll just drop
1583 here at the keyframe */
1584 if (ogg->push_seek_flags & GST_SEEK_FLAG_ACCURATE) {
1585 GST_INFO_OBJECT (ogg,
1586 "Seek to keyframe at %" GST_TIME_FORMAT " done (we're at %"
1587 GST_TIME_FORMAT "), skipping to original target (%"
1588 GST_TIME_FORMAT ")",
1589 GST_TIME_ARGS (ogg->push_seek_time_target),
1590 GST_TIME_ARGS (sync_time),
1591 GST_TIME_ARGS (ogg->push_seek_time_original_target));
1592 ogg->push_state = PUSH_LINEAR2;
1594 GST_INFO_OBJECT (ogg, "Seek to keyframe done, playing");
1596 /* we're synced to the seek target, so flush stream and stuff
1597 any queued pages into the stream so we start decoding there */
1598 ogg->push_state = PUSH_PLAYING;
1600 gst_ogg_demux_update_bisection_stats (ogg);
1603 } else if (ogg->push_state == PUSH_LINEAR1) {
1604 if (pad->push_kf_time == GST_CLOCK_TIME_NONE) {
1605 GstClockTime earliest_keyframe_time;
1607 gst_ogg_demux_record_keyframe_time (ogg, pad, granpos);
1608 GST_DEBUG_OBJECT (ogg,
1609 "Previous keyframe for %s stream at %" GST_TIME_FORMAT,
1610 gst_ogg_stream_get_media_type (&pad->map),
1611 GST_TIME_ARGS (pad->push_kf_time));
1612 earliest_keyframe_time = gst_ogg_demux_get_earliest_keyframe_time (ogg);
1613 if (earliest_keyframe_time != GST_CLOCK_TIME_NONE) {
1614 if (earliest_keyframe_time > ogg->push_last_seek_time) {
1615 GST_INFO_OBJECT (ogg,
1616 "All non sparse streams now have a previous keyframe time, "
1617 "and we already decoded it, switching to playing");
1618 ogg->push_state = PUSH_PLAYING;
1619 gst_ogg_demux_update_bisection_stats (ogg);
1621 GST_INFO_OBJECT (ogg,
1622 "All non sparse streams now have a previous keyframe time, "
1623 "bisecting again to %" GST_TIME_FORMAT,
1624 GST_TIME_ARGS (earliest_keyframe_time));
1626 ogg->push_seek_time_target = earliest_keyframe_time;
1627 ogg->push_offset0 = 0;
1628 ogg->push_time0 = ogg->push_start_time;
1629 ogg->push_offset1 = ogg->push_last_seek_offset;
1630 ogg->push_time1 = ogg->push_last_seek_time;
1631 ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
1632 ogg->seek_secant = FALSE;
1633 ogg->seek_undershot = FALSE;
1635 ogg->push_state = PUSH_BISECT2;
1636 best = gst_ogg_demux_estimate_bisection_target (ogg, 1.0f);
1642 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1645 ogg_sync_reset (&ogg->sync);
1646 for (i = 0; i < ogg->building_chain->streams->len; i++) {
1648 g_array_index (ogg->building_chain->streams, GstOggPad *, i);
1650 pad->push_sync_time = GST_CLOCK_TIME_NONE;
1651 ogg_stream_reset (&pad->map.stream);
1654 GST_DEBUG_OBJECT (ogg,
1655 "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, best,
1658 g_assert (best != -1);
1659 ogg->push_bisection_steps[ogg->push_state == PUSH_BISECT2 ? 1 : 0]++;
1661 gst_event_new_seek (ogg->push_seek_rate, GST_FORMAT_BYTES,
1662 ogg->push_seek_flags, GST_SEEK_TYPE_SET, best,
1663 GST_SEEK_TYPE_NONE, -1);
1665 GST_PUSH_UNLOCK (ogg);
1666 res = gst_pad_push_event (ogg->sinkpad, sevent);
1668 /* We failed to send the seek event, notify the pipeline */
1669 GST_ELEMENT_ERROR (ogg, RESOURCE, SEEK, (NULL), ("Failed to seek"));
1670 return GST_FLOW_ERROR;
1672 return GST_FLOW_SKIP_PUSH;
1675 if (ogg->push_state != PUSH_PLAYING) {
1676 GST_PUSH_UNLOCK (ogg);
1677 return GST_FLOW_SKIP_PUSH;
1680 GST_PUSH_UNLOCK (ogg);
1686 GST_WARNING_OBJECT (ogg,
1687 "ogg stream choked on page (serial %08x), "
1688 "resetting stream", pad->map.serialno);
1689 gst_ogg_pad_reset (pad);
1690 /* we continue to recover */
1691 return GST_FLOW_SKIP_PUSH;
1695 /* submit a page to an oggpad, this function will then submit all
1696 * the packets in the page.
1698 static GstFlowReturn
1699 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
1701 GstFlowReturn result = GST_FLOW_OK;
1703 gboolean continued = FALSE;
1707 /* for negative rates we read pages backwards and must therefore be careful
1708 * with continued pages */
1709 if (ogg->segment.rate < 0.0) {
1712 continued = ogg_page_continued (page);
1714 /* number of completed packets in the page */
1715 npackets = ogg_page_packets (page);
1717 /* page is not continued so it contains at least one packet start. It's
1718 * possible that no packet ends on this page (npackets == 0). In that
1719 * case, the next (continued) page(s) we kept contain the remainder of the
1720 * packets. We mark npackets=1 to make us start decoding the pages in the
1721 * remainder of the algorithm. */
1725 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
1727 if (npackets == 0) {
1728 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
1733 /* keep track of time in push mode */
1734 if (!ogg->pullmode) {
1735 result = gst_ogg_pad_handle_push_mode_state (pad, page);
1736 if (result == GST_FLOW_SKIP_PUSH)
1738 if (result != GST_FLOW_OK)
1742 if (page->header_len + page->body_len > ogg->max_page_size)
1743 ogg->max_page_size = page->header_len + page->body_len;
1745 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1748 /* flush all packets in the stream layer, this might not give a packet if
1749 * the page had no packets finishing on the page (npackets == 0). */
1750 result = gst_ogg_pad_stream_out (pad, 0);
1752 if (pad->continued) {
1755 /* now send the continued pages to the stream layer */
1756 while (pad->continued) {
1757 ogg_page *p = (ogg_page *) pad->continued->data;
1759 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
1760 if (ogg_stream_pagein (&pad->map.stream, p) != 0)
1763 pad->continued = g_list_delete_link (pad->continued, pad->continued);
1766 gst_ogg_page_free (p);
1769 GST_LOG_OBJECT (ogg, "flushing last continued packet");
1770 /* flush 1 continued packet in the stream layer */
1771 result = gst_ogg_pad_stream_out (pad, 1);
1773 /* flush all remaining packets, we pushed them in the previous round.
1774 * We don't use _reset() because we still want to get the discont when
1775 * we submit a next page. */
1776 while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
1780 /* keep continued pages (only in reverse mode) */
1782 ogg_page *p = gst_ogg_page_copy (page);
1784 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
1785 pad->continued = g_list_prepend (pad->continued, p);
1792 GST_WARNING_OBJECT (ogg,
1793 "ogg stream choked on page (serial %08x), "
1794 "resetting stream", pad->map.serialno);
1795 gst_ogg_pad_reset (pad);
1796 /* we continue to recover */
1802 static GstOggChain *
1803 gst_ogg_chain_new (GstOggDemux * ogg)
1805 GstOggChain *chain = g_slice_new0 (GstOggChain);
1807 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
1811 chain->have_bos = FALSE;
1812 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
1813 chain->begin_time = GST_CLOCK_TIME_NONE;
1814 chain->segment_start = GST_CLOCK_TIME_NONE;
1815 chain->segment_stop = GST_CLOCK_TIME_NONE;
1816 chain->total_time = GST_CLOCK_TIME_NONE;
1822 gst_ogg_chain_free (GstOggChain * chain)
1826 for (i = 0; i < chain->streams->len; i++) {
1827 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1829 gst_object_unref (pad);
1831 g_array_free (chain->streams, TRUE);
1832 g_slice_free (GstOggChain, chain);
1836 gst_ogg_pad_mark_discont (GstOggPad * pad)
1838 pad->discont = TRUE;
1839 pad->map.last_size = 0;
1843 gst_ogg_chain_mark_discont (GstOggChain * chain)
1847 for (i = 0; i < chain->streams->len; i++) {
1848 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1850 gst_ogg_pad_mark_discont (pad);
1855 gst_ogg_chain_reset (GstOggChain * chain)
1859 for (i = 0; i < chain->streams->len; i++) {
1860 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1862 gst_ogg_pad_reset (pad);
1867 gst_ogg_chain_new_stream (GstOggChain * chain, guint32 serialno)
1873 GST_DEBUG_OBJECT (chain->ogg,
1874 "creating new stream %08x in chain %p", serialno, chain);
1876 name = g_strdup_printf ("src_%08x", serialno);
1877 ret = g_object_new (GST_TYPE_OGG_PAD, "name", name, NULL);
1879 /* we own this one */
1880 gst_object_ref_sink (ret);
1882 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1883 gst_ogg_pad_mark_discont (ret);
1886 ret->ogg = chain->ogg;
1888 ret->map.serialno = serialno;
1889 if (ogg_stream_init (&ret->map.stream, serialno) != 0)
1892 /* FIXME: either do something with it or remove it */
1893 list = gst_tag_list_new_empty ();
1894 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1896 gst_tag_list_free (list);
1898 GST_DEBUG_OBJECT (chain->ogg,
1899 "created new ogg src %p for stream with serial %08x", ret, serialno);
1901 g_array_append_val (chain->streams, ret);
1902 gst_pad_set_active (GST_PAD_CAST (ret), TRUE);
1909 GST_ERROR ("Could not initialize ogg_stream struct for serial %08x",
1911 gst_object_unref (ret);
1917 gst_ogg_chain_get_stream (GstOggChain * chain, guint32 serialno)
1921 for (i = 0; i < chain->streams->len; i++) {
1922 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1924 if (pad->map.serialno == serialno)
1931 gst_ogg_chain_has_stream (GstOggChain * chain, guint32 serialno)
1933 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1936 /* signals and args */
1949 static GstStaticPadTemplate ogg_demux_src_template_factory =
1950 GST_STATIC_PAD_TEMPLATE ("src_%08x",
1953 GST_STATIC_CAPS_ANY);
1955 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1956 GST_STATIC_PAD_TEMPLATE ("sink",
1959 GST_STATIC_CAPS ("application/ogg; audio/ogg; video/ogg; application/kate")
1962 static void gst_ogg_demux_finalize (GObject * object);
1964 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1965 GstOggChain ** chain);
1966 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1967 GstOggChain * chain);
1969 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent,
1971 static void gst_ogg_demux_loop (GstOggPad * pad);
1972 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstObject * parent,
1973 GstBuffer * buffer);
1974 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad,
1975 GstObject * parent);
1976 static gboolean gst_ogg_demux_sink_activate_mode (GstPad * sinkpad,
1977 GstObject * parent, GstPadMode mode, gboolean active);
1978 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1979 GstStateChange transition);
1981 static void gst_ogg_print (GstOggDemux * demux);
1983 #define gst_ogg_demux_parent_class parent_class
1984 G_DEFINE_TYPE (GstOggDemux, gst_ogg_demux, GST_TYPE_ELEMENT);
1987 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1989 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1990 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1992 gst_element_class_set_static_metadata (gstelement_class,
1993 "Ogg demuxer", "Codec/Demuxer",
1994 "demux ogg streams (info about ogg: http://xiph.org)",
1995 "Wim Taymans <wim@fluendo.com>");
1997 gst_element_class_add_pad_template (gstelement_class,
1998 gst_static_pad_template_get (&ogg_demux_sink_template_factory));
1999 gst_element_class_add_pad_template (gstelement_class,
2000 gst_static_pad_template_get (&ogg_demux_src_template_factory));
2002 gstelement_class->change_state = gst_ogg_demux_change_state;
2003 gstelement_class->send_event = gst_ogg_demux_receive_event;
2005 gobject_class->finalize = gst_ogg_demux_finalize;
2009 gst_ogg_demux_init (GstOggDemux * ogg)
2011 /* create the sink pad */
2013 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
2016 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
2017 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
2018 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
2019 gst_pad_set_activatemode_function (ogg->sinkpad,
2020 gst_ogg_demux_sink_activate_mode);
2021 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
2023 ogg->chain_lock = g_mutex_new ();
2024 ogg->push_lock = g_mutex_new ();
2025 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
2027 ogg->stats_nbisections = 0;
2028 ogg->stats_bisection_steps[0] = 0;
2029 ogg->stats_bisection_steps[1] = 0;
2030 ogg->stats_bisection_max_steps[0] = 0;
2031 ogg->stats_bisection_max_steps[1] = 0;
2033 ogg->newsegment = NULL;
2037 gst_ogg_demux_finalize (GObject * object)
2041 ogg = GST_OGG_DEMUX (object);
2043 g_array_free (ogg->chains, TRUE);
2044 g_mutex_free (ogg->chain_lock);
2045 g_mutex_free (ogg->push_lock);
2046 ogg_sync_clear (&ogg->sync);
2048 if (ogg->newsegment)
2049 gst_event_unref (ogg->newsegment);
2051 G_OBJECT_CLASS (parent_class)->finalize (object);
2055 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
2060 chain = ogg->current_chain;
2064 for (i = 0; i < chain->streams->len; i++) {
2065 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2067 stream->start_time = -1;
2068 stream->map.accumulated_granule = 0;
2070 ogg->building_chain = chain;
2071 GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2072 ogg->current_chain = NULL;
2077 gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
2082 ogg = GST_OGG_DEMUX (parent);
2084 switch (GST_EVENT_TYPE (event)) {
2085 case GST_EVENT_FLUSH_START:
2086 res = gst_ogg_demux_send_event (ogg, event);
2088 case GST_EVENT_FLUSH_STOP:
2089 GST_DEBUG_OBJECT (ogg, "got a flush stop event");
2090 ogg_sync_reset (&ogg->sync);
2091 res = gst_ogg_demux_send_event (ogg, event);
2092 if (ogg->pullmode || ogg->push_state != PUSH_DURATION) {
2093 /* it's starting to feel reaaaally dirty :(
2094 if we're on a spliced seek to get duration, don't reset streams,
2095 we'll need them for the delayed seek */
2096 gst_ogg_demux_reset_streams (ogg);
2099 case GST_EVENT_SEGMENT:
2100 GST_DEBUG_OBJECT (ogg, "got a new segment event");
2104 gst_event_copy_segment (event, &segment);
2106 if (segment.format == GST_FORMAT_BYTES) {
2107 GST_PUSH_LOCK (ogg);
2108 ogg->push_byte_offset = segment.start;
2109 ogg->push_last_seek_offset = segment.start;
2110 GST_PUSH_UNLOCK (ogg);
2112 GST_WARNING_OBJECT (ogg, "unexpected segment format: %s",
2113 gst_format_get_name (segment.format));
2116 gst_event_unref (event);
2121 GST_DEBUG_OBJECT (ogg, "got an EOS event");
2123 /* This would be what is needed (recover from EOS by going on to
2124 the next step (issue the delayed seek)), but it does not work
2125 if there is a queue2 upstream - see more details comment in
2126 gst_ogg_pad_submit_page.
2127 If I could find a way to bypass queue2 behavior, this should
2129 GST_PUSH_LOCK (ogg);
2130 if (ogg->push_state == PUSH_DURATION) {
2131 GST_DEBUG_OBJECT (ogg, "Got EOS while determining length");
2132 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
2133 if (res != GST_FLOW_OK) {
2134 GST_DEBUG_OBJECT (ogg, "Error seeking back after duration check: %d",
2139 GST_PUSH_UNLOCK (ogg);
2141 res = gst_ogg_demux_send_event (ogg, event);
2142 if (ogg->current_chain == NULL) {
2143 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
2144 ("can't get first chain"));
2149 res = gst_pad_event_default (pad, parent, event);
2156 /* submit the given buffer to the ogg sync */
2157 static GstFlowReturn
2158 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
2162 GstFlowReturn ret = GST_FLOW_OK;
2164 size = gst_buffer_get_size (buffer);
2165 GST_DEBUG_OBJECT (ogg, "submitting %" G_GSIZE_FORMAT " bytes", size);
2166 if (G_UNLIKELY (size == 0))
2169 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
2170 if (G_UNLIKELY (oggbuffer == NULL))
2173 gst_buffer_extract (buffer, 0, oggbuffer, size);
2175 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2178 if (!ogg->pullmode) {
2179 GST_PUSH_LOCK (ogg);
2180 ogg->push_byte_offset += size;
2181 GST_PUSH_UNLOCK (ogg);
2185 gst_buffer_unref (buffer);
2192 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2193 (NULL), ("failed to get ogg sync buffer"));
2194 ret = GST_FLOW_ERROR;
2199 GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2200 ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2201 ret = GST_FLOW_ERROR;
2206 /* in random access mode this code updates the current read position
2207 * and resets the ogg sync buffer so that the next read will happen
2208 * from this new location.
2211 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
2213 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
2215 ogg->offset = offset;
2216 ogg->read_offset = offset;
2217 ogg_sync_reset (&ogg->sync);
2220 /* read more data from the current offset and submit to
2221 * the ogg sync layer.
2223 static GstFlowReturn
2224 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
2231 GST_LOG_OBJECT (ogg,
2232 "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
2233 ogg->read_offset, ogg->length, end_offset);
2235 if (end_offset > 0 && ogg->read_offset >= end_offset)
2236 goto boundary_reached;
2238 if (ogg->read_offset == ogg->length)
2241 oggbuffer = ogg_sync_buffer (&ogg->sync, CHUNKSIZE);
2242 if (G_UNLIKELY (oggbuffer == NULL))
2246 gst_buffer_new_wrapped_full (0, oggbuffer, CHUNKSIZE, 0, CHUNKSIZE, NULL,
2249 ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
2250 if (ret != GST_FLOW_OK)
2253 size = gst_buffer_get_size (buffer);
2255 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2258 ogg->read_offset += size;
2259 gst_buffer_unref (buffer);
2266 GST_LOG_OBJECT (ogg, "reached boundary");
2267 return GST_FLOW_LIMIT;
2271 GST_LOG_OBJECT (ogg, "reached EOS");
2272 return GST_FLOW_EOS;
2276 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2277 (NULL), ("failed to get ogg sync buffer"));
2278 return GST_FLOW_ERROR;
2282 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
2283 gst_flow_get_name (ret));
2284 gst_buffer_unref (buffer);
2289 GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2290 ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2291 gst_buffer_unref (buffer);
2292 return GST_FLOW_ERROR;
2296 /* Read the next page from the current offset.
2297 * boundary: number of bytes ahead we allow looking for;
2300 * @offset will contain the offset the next page starts at when this function
2301 * returns GST_FLOW_OK.
2303 * GST_FLOW_EOS is returned on EOS.
2305 * GST_FLOW_LIMIT is returned when we did not find a page before the
2306 * boundary. If @boundary is -1, this is never returned.
2308 * Any other error returned while retrieving data from the peer is returned as
2311 static GstFlowReturn
2312 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
2313 gint64 boundary, gint64 * offset)
2315 gint64 end_offset = -1;
2318 GST_LOG_OBJECT (ogg,
2319 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
2320 G_GINT64_FORMAT, ogg->offset, boundary);
2323 end_offset = ogg->offset + boundary;
2328 if (end_offset > 0 && ogg->offset >= end_offset)
2329 goto boundary_reached;
2331 more = ogg_sync_pageseek (&ogg->sync, og);
2333 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
2336 /* skipped n bytes */
2337 ogg->offset -= more;
2338 GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
2340 } else if (more == 0) {
2341 /* we need more data */
2343 goto boundary_reached;
2345 GST_LOG_OBJECT (ogg, "need more data");
2346 ret = gst_ogg_demux_get_data (ogg, end_offset);
2347 if (ret != GST_FLOW_OK)
2350 gint64 res_offset = ogg->offset;
2352 /* got a page. Return the offset at the page beginning,
2353 advance the internal offset past the page end */
2355 *offset = res_offset;
2358 ogg->offset += more;
2360 GST_LOG_OBJECT (ogg,
2361 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
2362 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
2363 ogg_page_serialno (og), ogg->offset,
2364 (gint64) ogg_page_granulepos (og));
2368 GST_LOG_OBJECT (ogg, "returning %d", ret);
2375 GST_LOG_OBJECT (ogg,
2376 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
2377 ogg->offset, end_offset);
2378 return GST_FLOW_LIMIT;
2382 /* from the current offset, find the previous page, seeking backwards
2383 * until we find the page.
2385 static GstFlowReturn
2386 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
2389 gint64 begin = ogg->offset;
2391 gint64 cur_offset = -1;
2393 GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
2395 while (cur_offset == -1) {
2400 /* seek CHUNKSIZE back */
2401 gst_ogg_demux_seek (ogg, begin);
2403 /* now continue reading until we run out of data, if we find a page
2404 * start, we save it. It might not be the final page as there could be
2405 * another page after this one. */
2406 while (ogg->offset < end) {
2407 gint64 new_offset, boundary;
2409 /* An Ogg page cannot be more than a bit less than 64 KB, so we can
2410 bound the boundary to that size when searching backwards if we
2411 haven't found a page yet. So the most we have to look at is twice
2412 the max page size, which is the worst case if we start scanning
2413 just after a large page, after which also lies a large page. */
2414 boundary = end - ogg->offset;
2415 if (boundary > 2 * MAX_OGG_PAGE_SIZE)
2416 boundary = 2 * MAX_OGG_PAGE_SIZE;
2418 ret = gst_ogg_demux_get_next_page (ogg, og, boundary, &new_offset);
2419 /* we hit the upper limit, offset contains the last page start */
2420 if (ret == GST_FLOW_LIMIT) {
2421 GST_LOG_OBJECT (ogg, "hit limit");
2424 /* something went wrong */
2425 if (ret == GST_FLOW_EOS) {
2427 GST_LOG_OBJECT (ogg, "got unexpected");
2430 } else if (ret != GST_FLOW_OK) {
2431 GST_LOG_OBJECT (ogg, "got error %d", ret);
2435 GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
2437 /* offset is next page start */
2438 cur_offset = new_offset;
2442 GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
2444 /* we have the offset. Actually snork and hold the page now */
2445 gst_ogg_demux_seek (ogg, cur_offset);
2446 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
2447 if (ret != GST_FLOW_OK) {
2448 GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
2450 /* this shouldn't be possible */
2455 *offset = cur_offset;
2462 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
2465 GstOggChain *chain = ogg->current_chain;
2470 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
2472 /* send EOS on all the pads */
2473 for (i = 0; i < chain->streams->len; i++) {
2474 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2480 event = gst_event_new_eos ();
2481 gst_event_set_seqnum (event, ogg->seqnum);
2482 gst_pad_push_event (GST_PAD_CAST (pad), event);
2484 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
2486 /* deactivate first */
2487 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
2489 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2494 /* if we cannot seek back to the chain, we can destroy the chain
2496 if (!ogg->pullmode) {
2497 gst_ogg_chain_free (chain);
2504 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
2507 GstStructure *structure;
2508 GValue array = { 0 };
2510 GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
2512 if (G_UNLIKELY (!caps))
2514 if (G_UNLIKELY (!headers))
2517 caps = gst_caps_make_writable (caps);
2518 structure = gst_caps_get_structure (caps, 0);
2520 g_value_init (&array, GST_TYPE_ARRAY);
2523 GValue value = { 0 };
2525 ogg_packet *op = headers->data;
2527 buffer = gst_buffer_new_and_alloc (op->bytes);
2529 gst_buffer_fill (buffer, 0, op->packet, op->bytes);
2530 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_HEADER);
2531 g_value_init (&value, GST_TYPE_BUFFER);
2532 gst_value_take_buffer (&value, buffer);
2533 gst_value_array_append_value (&array, &value);
2534 g_value_unset (&value);
2535 headers = headers->next;
2538 gst_structure_set_value (structure, "streamheader", &array);
2539 g_value_unset (&array);
2540 GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
2546 gst_ogg_demux_push_queued_buffers (GstOggDemux * ogg, GstOggPad * pad)
2550 /* push queued packets */
2551 for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
2552 ogg_packet *p = walk->data;
2554 gst_ogg_demux_chain_peer (pad, p, TRUE);
2555 _ogg_packet_free (p);
2557 /* and free the queued buffers */
2558 g_list_free (pad->map.queued);
2559 pad->map.queued = NULL;
2563 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
2567 gint bitrate, idx_bitrate;
2569 g_return_val_if_fail (chain != NULL, FALSE);
2571 if (chain == ogg->current_chain) {
2573 gst_event_unref (event);
2575 for (i = 0; i < chain->streams->len; i++) {
2576 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2577 gst_ogg_demux_push_queued_buffers (ogg, pad);
2583 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
2585 bitrate = idx_bitrate = 0;
2587 /* first add the pads */
2588 for (i = 0; i < chain->streams->len; i++) {
2591 pad = g_array_index (chain->streams, GstOggPad *, i);
2593 if (pad->map.idx_bitrate)
2594 idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
2596 bitrate += pad->map.bitrate;
2599 gst_ogg_pad_mark_discont (pad);
2600 pad->last_ret = GST_FLOW_OK;
2602 if (pad->map.is_skeleton || pad->map.is_cmml || pad->added
2603 || !gst_pad_has_current_caps (GST_PAD_CAST (pad)))
2606 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
2608 /* activate first */
2609 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
2611 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2614 /* prefer the index bitrate over the ones encoded in the streams */
2615 ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
2617 /* after adding the new pads, remove the old pads */
2618 gst_ogg_demux_deactivate_current_chain (ogg);
2620 GST_DEBUG_OBJECT (ogg, "Setting current chain to %p", chain);
2621 ogg->current_chain = chain;
2623 /* we are finished now */
2624 gst_element_no_more_pads (GST_ELEMENT (ogg));
2626 /* FIXME, must be sent from the streaming thread */
2630 gst_ogg_demux_send_event (ogg, event);
2632 tags = gst_tag_list_new (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL);
2633 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
2634 gst_ogg_demux_send_event (ogg, gst_event_new_tag (tags));
2637 GST_DEBUG_OBJECT (ogg, "starting chain");
2639 /* then send out any headers and queued packets */
2640 for (i = 0; i < chain->streams->len; i++) {
2644 pad = g_array_index (chain->streams, GstOggPad *, i);
2646 /* FIXME also streaming thread */
2647 if (pad->map.taglist) {
2648 GST_DEBUG_OBJECT (ogg, "pushing tags");
2649 gst_pad_push_event (GST_PAD_CAST (pad),
2650 gst_event_new_tag (pad->map.taglist));
2651 pad->map.taglist = NULL;
2654 /* Set headers on caps */
2656 gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers);
2657 gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps);
2659 GST_DEBUG_OBJECT (ogg, "pushing headers");
2661 for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
2662 ogg_packet *p = walk->data;
2664 gst_ogg_demux_chain_peer (pad, p, TRUE);
2667 GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
2668 gst_ogg_demux_push_queued_buffers (ogg, pad);
2674 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2675 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2684 GST_DEBUG_OBJECT (ogg,
2685 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
2687 GST_DEBUG_OBJECT (ogg,
2688 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
2689 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
2690 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
2692 /* perform the seek */
2693 while (begin < end) {
2696 if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
2699 /* take a (pretty decent) guess, avoiding overflow */
2700 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
2702 bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
2704 if (bisect <= begin)
2706 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
2708 gst_ogg_demux_seek (ogg, bisect);
2710 while (begin < end) {
2713 GST_DEBUG_OBJECT (ogg,
2714 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
2715 ", end %" G_GINT64_FORMAT, bisect, begin, end);
2717 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2718 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2721 if (ret == GST_FLOW_LIMIT) {
2722 /* we hit the upper limit, go back a bit */
2723 if (bisect <= begin + 1) {
2724 end = begin; /* found it */
2729 bisect -= CHUNKSIZE;
2730 if (bisect <= begin)
2733 gst_ogg_demux_seek (ogg, bisect);
2735 } else if (ret == GST_FLOW_OK) {
2736 /* found offset of next ogg page */
2738 GstClockTime granuletime;
2741 /* get the granulepos */
2742 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
2744 granulepos = ogg_page_granulepos (&og);
2745 if (granulepos == -1) {
2746 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2750 /* get the stream */
2751 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2752 if (pad == NULL || pad->map.is_skeleton)
2755 /* convert granulepos to time */
2756 granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2758 if (granuletime < pad->start_time)
2761 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
2762 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2764 granuletime -= pad->start_time;
2765 granuletime += chain->begin_time;
2767 GST_DEBUG_OBJECT (ogg,
2768 "found page with granule %" G_GINT64_FORMAT " and time %"
2769 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2771 if (granuletime < target) {
2772 best = result; /* raw offset of packet with granulepos */
2773 begin = ogg->offset; /* raw offset of next page */
2774 begintime = granuletime;
2776 bisect = begin; /* *not* begin + 1 */
2778 if (bisect <= begin + 1) {
2779 end = begin; /* found it */
2781 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
2783 bisect -= CHUNKSIZE; /* an endless loop otherwise. */
2784 if (bisect <= begin)
2786 gst_ogg_demux_seek (ogg, bisect);
2789 endtime = granuletime;
2798 GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
2799 gst_ogg_demux_seek (ogg, best);
2807 GST_DEBUG_OBJECT (ogg, "got a seek error");
2813 do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2814 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2815 gint64 * p_offset, gint64 * p_timestamp)
2818 guint64 timestamp, offset;
2819 guint64 r_timestamp, r_offset;
2820 gboolean result = FALSE;
2822 target -= begintime;
2827 for (i = 0; i < chain->streams->len; i++) {
2828 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2831 if (gst_ogg_map_search_index (&pad->map, TRUE, ×tamp, &offset)) {
2832 GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
2835 if (r_offset == -1 || offset < r_offset) {
2837 r_timestamp = timestamp;
2844 *p_timestamp = r_timestamp;
2846 *p_offset = r_offset;
2852 * do seek to time @position, return FALSE or chain and TRUE
2855 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
2856 gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
2859 GstOggChain *chain = NULL;
2861 gint64 begintime, endtime;
2862 gint64 target, keytarget;
2867 gint i, pending, len;
2868 gboolean first_parsed_page = TRUE;
2870 position = segment->position;
2872 /* first find the chain to search in */
2873 total = ogg->total_time;
2874 if (ogg->chains->len == 0)
2877 for (i = ogg->chains->len - 1; i >= 0; i--) {
2878 chain = g_array_index (ogg->chains, GstOggChain *, i);
2879 total -= chain->total_time;
2880 if (position >= total)
2884 /* first step, locate page containing the required data */
2885 begin = chain->offset;
2886 end = chain->end_offset;
2887 begintime = chain->begin_time;
2888 endtime = begintime + chain->total_time;
2889 target = position - total + begintime;
2891 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
2895 /* second step: find pages for all streams, we use the keyframe_granule to keep
2896 * track of which ones we saw. If we have seen a page for each stream we can
2897 * calculate the positions of each keyframe. */
2898 GST_DEBUG_OBJECT (ogg, "find keyframes");
2899 len = pending = chain->streams->len;
2901 /* figure out where the keyframes are */
2908 GstClockTime keyframe_time, granule_time;
2910 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2911 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2913 if (ret == GST_FLOW_LIMIT) {
2914 GST_LOG_OBJECT (ogg, "reached limit");
2916 } else if (ret != GST_FLOW_OK)
2919 /* get the stream */
2920 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2924 if (pad->map.is_skeleton || pad->map.is_cmml)
2927 granulepos = ogg_page_granulepos (&og);
2928 if (granulepos == -1 || granulepos == 0) {
2929 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2933 /* we only do this the first time we pass here */
2934 if (first_parsed_page) {
2935 /* Now that we have a time reference from the page, we can check
2936 * whether all streams still have pages from here on.
2938 * This would be more elegant before the loop, but getting the page from
2939 * there without breaking anything would be more costly */
2940 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2942 for (i = 0; i < len; i++) {
2943 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2946 /* we already know we have at least one page (the current one)
2947 * for this stream */
2950 if (granule_time > stream->map.total_time)
2951 /* we won't encounter any more pages of this stream, so we don't
2952 * try finding a key frame for it */
2955 first_parsed_page = FALSE;
2959 /* in reverse we want to go past the page with the lower timestamp */
2960 if (segment->rate < 0.0) {
2961 /* get time for this pad */
2962 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2965 GST_LOG_OBJECT (ogg,
2966 "looking at page with ts %" GST_TIME_FORMAT ", target %"
2967 GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
2968 GST_TIME_ARGS (target));
2969 if (granule_time < target)
2973 /* we've seen this pad before */
2974 if (pad->keyframe_granule != -1)
2977 /* convert granule of this pad to the granule of the keyframe */
2978 pad->keyframe_granule =
2979 gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
2980 GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
2981 pad->keyframe_granule);
2983 /* get time of the keyframe */
2985 gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
2986 GST_LOG_OBJECT (ogg,
2987 "stream %08x granule time %" GST_TIME_FORMAT,
2988 pad->map.serialno, GST_TIME_ARGS (keyframe_time));
2990 /* collect smallest value */
2991 if (keyframe_time != -1) {
2992 keyframe_time += begintime;
2993 if (keyframe_time < keytarget)
2994 keytarget = keyframe_time;
3003 /* for negative rates we will get to the keyframe backwards */
3004 if (segment->rate < 0.0)
3007 if (keytarget != target) {
3008 GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
3009 GST_TIME_ARGS (keytarget));
3011 /* last step, seek to the location of the keyframe */
3012 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
3016 /* seek back to previous position */
3017 GST_LOG_OBJECT (ogg, "keyframe on target");
3018 gst_ogg_demux_seek (ogg, best);
3023 if (segment->rate > 0.0)
3024 segment->time = keytarget;
3025 segment->position = keytarget - begintime;
3034 GST_DEBUG_OBJECT (ogg, "no chains");
3039 GST_DEBUG_OBJECT (ogg, "got a seek error");
3044 /* does not take ownership of the event */
3046 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
3048 GstOggChain *chain = NULL;
3050 gboolean flush, accurate, keyframe;
3054 GstSeekType start_type, stop_type;
3061 GST_DEBUG_OBJECT (ogg, "seek with event");
3063 gst_event_parse_seek (event, &rate, &format, &flags,
3064 &start_type, &start, &stop_type, &stop);
3066 /* we can only seek on time */
3067 if (format != GST_FORMAT_TIME) {
3068 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3071 seqnum = gst_event_get_seqnum (event);
3073 GST_DEBUG_OBJECT (ogg, "seek without event");
3077 seqnum = gst_util_seqnum_next ();
3080 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
3082 flush = flags & GST_SEEK_FLAG_FLUSH;
3083 accurate = flags & GST_SEEK_FLAG_ACCURATE;
3084 keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
3086 /* first step is to unlock the streaming thread if it is
3087 * blocked in a chain call, we do this by starting the flush. because
3088 * we cannot yet hold any streaming lock, we have to protect the chains
3089 * with their own lock. */
3093 tevent = gst_event_new_flush_start ();
3094 gst_event_set_seqnum (tevent, seqnum);
3096 gst_event_ref (tevent);
3097 gst_pad_push_event (ogg->sinkpad, tevent);
3099 GST_CHAIN_LOCK (ogg);
3100 for (i = 0; i < ogg->chains->len; i++) {
3101 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3104 for (j = 0; j < chain->streams->len; j++) {
3105 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
3107 gst_event_ref (tevent);
3108 gst_pad_push_event (GST_PAD (pad), tevent);
3111 GST_CHAIN_UNLOCK (ogg);
3113 gst_event_unref (tevent);
3115 gst_pad_pause_task (ogg->sinkpad);
3118 /* now grab the stream lock so that streaming cannot continue, for
3119 * non flushing seeks when the element is in PAUSED this could block
3121 GST_PAD_STREAM_LOCK (ogg->sinkpad);
3124 gst_segment_do_seek (&ogg->segment, rate, format, flags,
3125 start_type, start, stop_type, stop, &update);
3128 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
3129 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
3130 GST_TIME_ARGS (ogg->segment.stop));
3132 /* we need to stop flushing on the srcpad as we're going to use it
3133 * next. We can do this as we have the STREAM lock now. */
3135 tevent = gst_event_new_flush_stop (TRUE);
3136 gst_event_set_seqnum (tevent, seqnum);
3137 gst_pad_push_event (ogg->sinkpad, tevent);
3143 /* reset all ogg streams now, need to do this from within the lock to
3144 * make sure the streaming thread is not messing with the stream */
3145 for (i = 0; i < ogg->chains->len; i++) {
3146 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3148 gst_ogg_chain_reset (chain);
3152 /* for reverse we will already seek accurately */
3153 res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
3155 /* seek failed, make sure we continue the current chain */
3157 GST_DEBUG_OBJECT (ogg, "seek failed");
3158 chain = ogg->current_chain;
3160 GST_DEBUG_OBJECT (ogg, "seek success");
3166 /* now we have a new position, prepare for streaming again */
3171 gint64 position, begin_time;
3174 /* we have to send the flush to the old chain, not the new one */
3176 tevent = gst_event_new_flush_stop (TRUE);
3177 gst_event_set_seqnum (tevent, seqnum);
3178 gst_ogg_demux_send_event (ogg, tevent);
3181 /* we need this to see how far inside the chain we need to start */
3182 if (chain->begin_time != GST_CLOCK_TIME_NONE)
3183 begin_time = chain->begin_time;
3187 /* segment.start gives the start over all chains, we calculate the amount
3188 * of time into this chain we need to start */
3189 start = ogg->segment.start - begin_time;
3190 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3191 start += chain->segment_start;
3193 if ((stop = ogg->segment.stop) == -1)
3194 stop = ogg->segment.duration;
3196 /* segment.stop gives the stop time over all chains, calculate the amount of
3197 * time we need to stop in this chain */
3199 if (stop > begin_time)
3203 stop += chain->segment_start;
3204 /* we must stop when this chain ends and switch to the next chain to play
3205 * the remainder of the segment. */
3206 stop = MIN (stop, chain->segment_stop);
3209 position = ogg->segment.position;
3210 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3211 position += chain->segment_start;
3213 gst_segment_copy_into (&ogg->segment, &segment);
3215 /* create the segment event we are going to send out */
3216 if (ogg->segment.rate >= 0.0) {
3217 segment.start = position;
3218 segment.stop = stop;
3220 segment.start = start;
3221 segment.stop = position;
3223 event = gst_event_new_segment (&segment);
3224 gst_event_set_seqnum (event, seqnum);
3226 if (chain != ogg->current_chain) {
3227 /* switch to different chain, send segment on new chain */
3228 gst_ogg_demux_activate_chain (ogg, chain, event);
3230 /* mark discont and send segment on current chain */
3231 gst_ogg_chain_mark_discont (chain);
3232 /* This event should be sent from the streaming thread (sink pad task) */
3233 if (ogg->newsegment)
3234 gst_event_unref (ogg->newsegment);
3235 ogg->newsegment = event;
3238 /* notify start of new segment */
3239 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3240 GstMessage *message;
3242 message = gst_message_new_segment_start (GST_OBJECT (ogg),
3243 GST_FORMAT_TIME, ogg->segment.position);
3244 gst_message_set_seqnum (message, seqnum);
3246 gst_element_post_message (GST_ELEMENT (ogg), message);
3249 ogg->seqnum = seqnum;
3250 /* restart our task since it might have been stopped when we did the
3252 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3253 ogg->sinkpad, NULL);
3256 /* streaming can continue now */
3257 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3264 GST_DEBUG_OBJECT (ogg, "seek failed");
3269 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
3270 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3276 gst_ogg_demux_get_duration_push (GstOggDemux * ogg, int flags)
3278 /* In push mode, we get to the end of the stream to get the duration */
3283 /* A full Ogg page can be almost 64 KB. There's no guarantee that there'll be a
3284 granpos there, but it's fairly likely */
3286 ogg->push_byte_length - DURATION_CHUNK_OFFSET - EOS_AVOIDANCE_THRESHOLD;
3290 GST_DEBUG_OBJECT (ogg,
3291 "Getting duration, seeking near the end, to %" G_GINT64_FORMAT, position);
3292 ogg->push_state = PUSH_DURATION;
3293 /* do not read the last byte */
3294 sevent = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET,
3295 position, GST_SEEK_TYPE_SET, ogg->push_byte_length - 1);
3296 res = gst_pad_push_event (ogg->sinkpad, sevent);
3298 GST_DEBUG_OBJECT (ogg, "Seek succesful");
3301 GST_INFO_OBJECT (ogg, "Seek failed, duration will stay unknown");
3302 ogg->push_state = PUSH_PLAYING;
3303 ogg->push_disable_seeking = TRUE;
3309 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3312 if (ogg->push_byte_length < 0) {
3315 GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3316 if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3320 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &length);
3321 if (res && length > 0) {
3322 ogg->push_byte_length = length;
3323 GST_DEBUG_OBJECT (ogg,
3324 "File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
3326 GST_DEBUG_OBJECT (ogg, "File byte length unknown, assuming live");
3327 ogg->push_disable_seeking = TRUE;
3330 res = gst_pad_query_duration (peer, GST_FORMAT_TIME, &length);
3331 gst_object_unref (peer);
3332 if (res && length >= 0) {
3333 ogg->push_time_length = length;
3334 GST_DEBUG_OBJECT (ogg, "File time length %" GST_TIME_FORMAT,
3335 GST_TIME_ARGS (ogg->push_time_length));
3336 } else if (!ogg->push_disable_seeking) {
3339 res = gst_ogg_demux_get_duration_push (ogg, flags);
3341 GST_DEBUG_OBJECT (ogg,
3342 "File time length unknown, trying to determine");
3343 ogg->push_mode_seek_delayed_event = NULL;
3345 GST_DEBUG_OBJECT (ogg,
3346 "Let me intercept this innocent looking seek request");
3347 ogg->push_mode_seek_delayed_event = gst_event_copy (event);
3358 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3361 gboolean res = TRUE;
3365 GstSeekType start_type, stop_type;
3369 gint64 best, best_time;
3372 GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3374 gst_event_parse_seek (event, &rate, &format, &flags,
3375 &start_type, &start, &stop_type, &stop);
3377 if (format != GST_FORMAT_TIME) {
3378 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3382 if (start_type != GST_SEEK_TYPE_SET) {
3383 GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3387 if (!(flags & GST_SEEK_FLAG_FLUSH)) {
3388 GST_DEBUG_OBJECT (ogg, "can only do flushing seeks");
3392 GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3393 GST_TIME_ARGS (start));
3395 chain = ogg->current_chain;
3397 GST_WARNING_OBJECT (ogg, "No chain to seek on");
3401 /* start accessing push_* members */
3402 GST_PUSH_LOCK (ogg);
3404 /* not if we disabled seeking (chained streams) */
3405 if (ogg->push_disable_seeking) {
3406 GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3410 /* not when we're trying to work out duration */
3411 if (ogg->push_state == PUSH_DURATION) {
3412 GST_DEBUG_OBJECT (ogg, "Busy working out duration, try again later");
3416 /* actually, not if we're doing any seeking already */
3417 if (ogg->push_state != PUSH_PLAYING) {
3418 GST_DEBUG_OBJECT (ogg, "Already doing some seeking, try again later");
3422 /* on the first seek, get length if we can */
3423 if (!gst_ogg_demux_check_duration_push (ogg, flags, event)) {
3424 GST_PUSH_UNLOCK (ogg);
3428 if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
3429 /* the index gave some result */
3430 GST_DEBUG_OBJECT (ogg,
3431 "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
3434 if (ogg->push_time_length > 0) {
3435 /* if we know the time length, we know the full segment bitrate */
3436 GST_DEBUG_OBJECT (ogg, "Using real file bitrate");
3438 gst_util_uint64_scale (ogg->push_byte_length, 8 * GST_SECOND,
3439 ogg->push_time_length);
3440 } else if (ogg->push_time_offset > 0) {
3441 /* get a first approximation using known bitrate to the current position */
3442 GST_DEBUG_OBJECT (ogg, "Using file bitrate so far");
3444 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
3445 ogg->push_time_offset);
3446 } else if (ogg->bitrate > 0) {
3447 /* nominal bitrate is better than nothing, even if it lies often */
3448 GST_DEBUG_OBJECT (ogg, "Using nominal bitrate");
3449 bitrate = ogg->bitrate;
3452 GST_DEBUG_OBJECT (ogg,
3453 "At stream start, and no nominal bitrate, using some random magic "
3455 /* the bisection, once started, should give us a better approximation */
3458 best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3461 /* offset by typical page length, and ensure our best guess is within
3462 reasonable bounds */
3466 if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3467 best = ogg->push_byte_length - 1;
3469 /* set up bisection search */
3470 ogg->push_offset0 = 0;
3471 ogg->push_offset1 = ogg->push_byte_length - 1;
3472 ogg->push_time0 = ogg->push_start_time;
3473 ogg->push_time1 = ogg->push_time_length;
3474 ogg->push_seek_time_target = start;
3475 ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
3476 ogg->push_seek_time_original_target = start;
3477 ogg->push_state = PUSH_BISECT1;
3478 ogg->seek_secant = FALSE;
3479 ogg->seek_undershot = FALSE;
3481 /* reset pad push mode seeking state */
3482 for (i = 0; i < chain->streams->len; i++) {
3483 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3484 pad->push_kf_time = GST_CLOCK_TIME_NONE;
3485 pad->push_sync_time = GST_CLOCK_TIME_NONE;
3488 GST_DEBUG_OBJECT (ogg,
3489 "Setting up bisection search for %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
3490 " (time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", ogg->push_offset0,
3491 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
3492 GST_TIME_ARGS (ogg->push_time1));
3493 GST_DEBUG_OBJECT (ogg,
3494 "Target time is %" GST_TIME_FORMAT ", best first guess is %"
3495 G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target), best);
3497 ogg->push_seek_rate = rate;
3498 ogg->push_seek_flags = flags;
3499 ogg->push_mode_seek_delayed_event = NULL;
3500 ogg->push_bisection_steps[0] = 1;
3501 ogg->push_bisection_steps[1] = 0;
3502 sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
3503 start_type, best, GST_SEEK_TYPE_NONE, -1);
3505 GST_PUSH_UNLOCK (ogg);
3506 res = gst_pad_push_event (ogg->sinkpad, sevent);
3513 GST_DEBUG_OBJECT (ogg, "seek failed");
3518 GST_PUSH_UNLOCK (ogg);
3523 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
3527 if (ogg->pullmode) {
3528 res = gst_ogg_demux_perform_seek_pull (ogg, event);
3530 res = gst_ogg_demux_perform_seek_push (ogg, event);
3536 /* finds each bitstream link one at a time using a bisection search
3537 * (has to begin by knowing the offset of the lb's initial page).
3538 * Recurses for each link so it can alloc the link storage after
3539 * finding them all, then unroll and fill the cache at the same time
3541 static GstFlowReturn
3542 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
3543 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
3545 gint64 endsearched = end;
3550 GstOggChain *nextchain;
3552 GST_LOG_OBJECT (ogg,
3553 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
3554 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
3556 /* the below guards against garbage seperating the last and
3557 * first pages of two links. */
3558 while (searched < endsearched) {
3561 if (endsearched - searched < CHUNKSIZE) {
3564 bisect = (searched + endsearched) / 2;
3567 gst_ogg_demux_seek (ogg, bisect);
3568 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
3570 if (ret == GST_FLOW_EOS) {
3571 endsearched = bisect;
3572 } else if (ret == GST_FLOW_OK) {
3573 guint32 serial = ogg_page_serialno (&og);
3575 if (!gst_ogg_chain_has_stream (chain, serial)) {
3576 endsearched = bisect;
3579 searched = offset + og.header_len + og.body_len;
3585 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
3587 chain->end_offset = searched;
3588 ret = gst_ogg_demux_read_end_chain (ogg, chain);
3589 if (ret != GST_FLOW_OK)
3592 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
3594 gst_ogg_demux_seek (ogg, next);
3595 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
3596 if (ret == GST_FLOW_EOS) {
3599 GST_LOG_OBJECT (ogg, "no next chain");
3600 } else if (ret != GST_FLOW_OK)
3603 if (searched < end && nextchain != NULL) {
3604 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
3605 end, nextchain, m + 1);
3606 if (ret != GST_FLOW_OK)
3609 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
3611 g_array_insert_val (ogg->chains, 0, chain);
3617 /* read a chain from the ogg file. This code will
3618 * read all BOS pages and will create and return a GstOggChain
3619 * structure with the results.
3621 * This function will also read N pages from each stream in the
3622 * chain and submit them to the internal ogg stream parser/mapper
3623 * until we know the timestamp of the first page in the chain.
3625 static GstFlowReturn
3626 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
3629 GstOggChain *chain = NULL;
3630 gint64 offset = ogg->offset;
3635 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
3637 /* first read the BOS pages, detect the stream types, create the internal
3638 * stream mappers, send data to them. */
3643 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3644 if (ret != GST_FLOW_OK) {
3645 if (ret == GST_FLOW_EOS) {
3646 GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
3648 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
3652 if (!ogg_page_bos (&og)) {
3653 GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
3654 /* if we did not find a chain yet, assume this is a bogus stream and
3657 GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
3663 if (chain == NULL) {
3664 chain = gst_ogg_chain_new (ogg);
3665 chain->offset = offset;
3668 serial = ogg_page_serialno (&og);
3669 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
3670 GST_WARNING_OBJECT (ogg,
3671 "found serial %08x BOS page twice, ignoring", serial);
3675 pad = gst_ogg_chain_new_stream (chain, serial);
3676 gst_ogg_pad_submit_page (pad, &og);
3679 if (ret != GST_FLOW_OK || chain == NULL) {
3680 if (ret == GST_FLOW_OK) {
3681 GST_WARNING_OBJECT (ogg, "no chain was found");
3682 ret = GST_FLOW_ERROR;
3683 } else if (ret != GST_FLOW_EOS) {
3684 GST_WARNING_OBJECT (ogg, "failed to read chain");
3686 GST_DEBUG_OBJECT (ogg, "done reading chains");
3689 gst_ogg_chain_free (chain);
3696 chain->have_bos = TRUE;
3697 GST_INFO_OBJECT (ogg, "read bos pages, ");
3699 /* now read pages until each ogg stream mapper has figured out the
3700 * timestamp of the first packet in the chain */
3702 /* save the offset to the first non bos page in the chain: if searching for
3703 * pad->first_time we read past the end of the chain, we'll seek back to this
3706 offset = ogg->offset;
3711 gboolean known_serial = FALSE;
3714 serial = ogg_page_serialno (&og);
3716 for (i = 0; i < chain->streams->len; i++) {
3717 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3719 GST_LOG_OBJECT (ogg,
3720 "serial %08x time %" GST_TIME_FORMAT,
3721 pad->map.serialno, GST_TIME_ARGS (pad->start_time));
3723 if (pad->map.serialno == serial) {
3724 known_serial = TRUE;
3726 /* submit the page now, this will fill in the start_time when the
3727 * internal stream mapper finds it */
3728 gst_ogg_pad_submit_page (pad, &og);
3730 if (!pad->map.is_skeleton && pad->start_time == -1
3731 && ogg_page_eos (&og)) {
3732 /* got EOS on a pad before we could find its start_time.
3733 * We have no chance of finding a start_time for every pad so
3734 * stop searching for the other start_time(s).
3740 /* the timestamp will be filled in when we submit the pages */
3741 if (!pad->map.is_sparse)
3742 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
3744 GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
3747 /* we read a page not belonging to the current chain: seek back to the
3748 * beginning of the chain
3750 if (!known_serial) {
3751 GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
3752 gst_ogg_demux_seek (ogg, offset);
3757 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3758 if (ret != GST_FLOW_OK)
3762 GST_LOG_OBJECT (ogg, "done reading chain");
3770 /* read the last pages from the ogg stream to get the final
3773 static GstFlowReturn
3774 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
3776 gint64 begin = chain->end_offset;
3778 gint64 last_granule = -1;
3779 GstOggPad *last_pad = NULL;
3781 gboolean done = FALSE;
3790 gst_ogg_demux_seek (ogg, begin);
3792 /* now continue reading until we run out of data, if we find a page
3793 * start, we save it. It might not be the final page as there could be
3794 * another page after this one. */
3795 while (ogg->offset < end) {
3796 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
3798 if (ret == GST_FLOW_LIMIT)
3800 if (ret != GST_FLOW_OK)
3803 for (i = 0; i < chain->streams->len; i++) {
3804 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3806 if (pad->map.is_skeleton)
3809 if (pad->map.serialno == ogg_page_serialno (&og)) {
3810 gint64 granulepos = ogg_page_granulepos (&og);
3812 if (granulepos != -1) {
3813 last_granule = granulepos;
3824 chain->segment_stop =
3825 gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
3828 chain->segment_stop = GST_CLOCK_TIME_NONE;
3831 GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
3836 /* find a pad with a given serial number
3839 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
3844 /* first look in building chain if any */
3845 if (ogg->building_chain) {
3846 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
3851 /* then look in current chain if any */
3852 if (ogg->current_chain) {
3853 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
3858 for (i = 0; i < ogg->chains->len; i++) {
3859 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3861 pad = gst_ogg_chain_get_stream (chain, serialno);
3868 /* find a chain with a given serial number
3870 static GstOggChain *
3871 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
3875 pad = gst_ogg_demux_find_pad (ogg, serialno);
3882 /* returns TRUE if all streams have valid start time */
3884 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
3886 gboolean res = TRUE;
3888 chain->total_time = GST_CLOCK_TIME_NONE;
3889 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
3891 /* see if we have a start time on all streams */
3892 chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
3894 if (chain->segment_start == G_MAXUINT64) {
3895 /* not yet, stream some more data */
3897 } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
3898 /* we can calculate a total time */
3899 chain->total_time = chain->segment_stop - chain->segment_start;
3902 GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
3904 GST_DEBUG_OBJECT (ogg, "return %d", res);
3910 gst_ogg_demux_collect_info (GstOggDemux * ogg)
3914 /* collect all info */
3915 ogg->total_time = 0;
3917 for (i = 0; i < ogg->chains->len; i++) {
3918 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3920 chain->begin_time = ogg->total_time;
3922 gst_ogg_demux_collect_chain_info (ogg, chain);
3924 ogg->total_time += chain->total_time;
3926 ogg->segment.duration = ogg->total_time;
3929 /* find all the chains in the ogg file, this reads the first and
3930 * last page of the ogg stream, if they match then the ogg file has
3931 * just one chain, else we do a binary search for all chains.
3933 static GstFlowReturn
3934 gst_ogg_demux_find_chains (GstOggDemux * ogg)
3943 /* get peer to figure out length */
3944 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
3947 /* find length to read last page, we store this for later use. */
3948 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &ogg->length);
3949 gst_object_unref (peer);
3950 if (!res || ogg->length <= 0)
3953 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
3955 /* read chain from offset 0, this is the first chain of the
3957 gst_ogg_demux_seek (ogg, 0);
3958 ret = gst_ogg_demux_read_chain (ogg, &chain);
3959 if (ret != GST_FLOW_OK)
3960 goto no_first_chain;
3962 /* read page from end offset, we use this page to check if its serial
3963 * number is contained in the first chain. If this is the case then
3964 * this ogg is not a chained ogg and we can skip the scanning. */
3965 gst_ogg_demux_seek (ogg, ogg->length);
3966 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
3967 if (ret != GST_FLOW_OK)
3970 serialno = ogg_page_serialno (&og);
3972 if (!gst_ogg_chain_has_stream (chain, serialno)) {
3973 /* the last page is not in the first stream, this means we should
3974 * find all the chains in this chained ogg. */
3976 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
3979 /* we still call this function here but with an empty range so that
3980 * we can reuse the setup code in this routine. */
3982 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
3983 ogg->length, chain, 0);
3985 if (ret != GST_FLOW_OK)
3988 /* all fine, collect and print */
3989 gst_ogg_demux_collect_info (ogg);
3991 /* dump our chains and streams */
3992 gst_ogg_print (ogg);
3997 /*** error cases ***/
4000 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
4001 return GST_FLOW_NOT_LINKED;
4005 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
4006 return GST_FLOW_NOT_SUPPORTED;
4010 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
4011 return GST_FLOW_ERROR;
4015 GST_DEBUG_OBJECT (ogg, "can't get last page");
4017 gst_ogg_chain_free (chain);
4022 static GstFlowReturn
4023 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
4028 GstFlowReturn result = GST_FLOW_OK;
4030 serialno = ogg_page_serialno (page);
4031 granule = ogg_page_granulepos (page);
4033 GST_LOG_OBJECT (ogg,
4034 "processing ogg page (serial %08x, "
4035 "pageno %ld, granulepos %" G_GINT64_FORMAT ", bos %d)", serialno,
4036 ogg_page_pageno (page), granule, ogg_page_bos (page));
4038 if (ogg_page_bos (page)) {
4042 /* see if we know about the chain already */
4043 chain = gst_ogg_demux_find_chain (ogg, serialno);
4049 if (chain->segment_start != GST_CLOCK_TIME_NONE)
4050 start = chain->segment_start;
4052 /* create the newsegment event we are going to send out */
4053 gst_segment_copy_into (&ogg->segment, &segment);
4054 segment.start = start;
4055 segment.stop = chain->segment_stop;
4056 segment.time = chain->begin_time;
4057 event = gst_event_new_segment (&segment);
4058 gst_event_set_seqnum (event, ogg->seqnum);
4060 GST_DEBUG_OBJECT (ogg,
4061 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
4062 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
4063 GST_TIME_ARGS (chain->segment_stop),
4064 GST_TIME_ARGS (chain->begin_time));
4066 /* activate it as it means we have a non-header, this will also deactivate
4067 * the currently running chain. */
4068 gst_ogg_demux_activate_chain (ogg, chain, event);
4069 pad = gst_ogg_demux_find_pad (ogg, serialno);
4071 GstClockTime chain_time;
4072 gint64 current_time;
4074 /* this can only happen in push mode */
4078 current_time = ogg->segment.position;
4080 /* time of new chain is current time */
4081 chain_time = current_time;
4083 if (ogg->building_chain == NULL) {
4084 GstOggChain *newchain;
4086 newchain = gst_ogg_chain_new (ogg);
4087 newchain->offset = 0;
4088 /* set new chain begin time aligned with end time of old chain */
4089 newchain->begin_time = chain_time;
4090 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
4091 GST_TIME_ARGS (chain_time));
4093 /* and this is the one we are building now */
4094 ogg->building_chain = newchain;
4096 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
4099 pad = gst_ogg_demux_find_pad (ogg, serialno);
4102 result = gst_ogg_pad_submit_page (pad, page);
4104 GST_PUSH_LOCK (ogg);
4105 if (!ogg->pullmode && !ogg->push_disable_seeking) {
4106 /* no pad while probing for duration, we must have a chained stream,
4107 and we don't support them, so back off */
4108 GST_INFO_OBJECT (ogg, "We seem to have a chained stream, we won't seek");
4109 if (ogg->push_state == PUSH_DURATION) {
4112 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
4113 if (res != GST_FLOW_OK)
4117 /* only once we seeked back */
4118 GST_PUSH_LOCK (ogg);
4119 ogg->push_disable_seeking = TRUE;
4121 GST_PUSH_UNLOCK (ogg);
4122 /* no pad. This means an ogg page without bos has been seen for this
4123 * serialno. we just ignore it but post a warning... */
4124 GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
4125 (NULL), ("unknown ogg pad for serial %08x detected", serialno));
4128 GST_PUSH_UNLOCK (ogg);
4135 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
4136 (NULL), ("unknown ogg chain for serial %08x detected", serialno));
4137 return GST_FLOW_ERROR;
4141 /* streaming mode, receive a buffer, parse it, create pads for
4142 * the serialno, submit pages and packets to the oggpads
4144 static GstFlowReturn
4145 gst_ogg_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
4149 GstFlowReturn result = GST_FLOW_OK;
4151 ogg = GST_OGG_DEMUX (parent);
4153 GST_DEBUG_OBJECT (ogg, "enter");
4154 result = gst_ogg_demux_submit_buffer (ogg, buffer);
4156 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
4159 while (result == GST_FLOW_OK) {
4162 ret = ogg_sync_pageout (&ogg->sync, &page);
4164 /* need more data */
4167 /* discontinuity in the pages */
4168 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
4170 result = gst_ogg_demux_handle_page (ogg, &page);
4172 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
4176 if (ret == 0 || result == GST_FLOW_OK) {
4177 gst_ogg_demux_sync_streams (ogg);
4179 GST_DEBUG_OBJECT (ogg, "leave with %d", result);
4184 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
4186 GstOggChain *chain = ogg->current_chain;
4187 gboolean res = TRUE;
4190 chain = ogg->building_chain;
4195 for (i = 0; i < chain->streams->len; i++) {
4196 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4198 gst_event_ref (event);
4199 GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
4200 res &= gst_pad_push_event (GST_PAD (pad), event);
4203 GST_WARNING_OBJECT (ogg, "No chain to forward event to");
4205 gst_event_unref (event);
4210 static GstFlowReturn
4211 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
4216 /* store the value */
4217 pad->last_ret = ret;
4219 /* any other error that is not-linked can be returned right
4221 if (ret != GST_FLOW_NOT_LINKED)
4224 /* only return NOT_LINKED if all other pads returned NOT_LINKED */
4225 chain = ogg->current_chain;
4229 for (i = 0; i < chain->streams->len; i++) {
4230 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4232 ret = opad->last_ret;
4233 /* some other return value (must be SUCCESS but we can return
4234 * other values as well) */
4235 if (ret != GST_FLOW_NOT_LINKED)
4238 /* if we get here, all other pads were unlinked and we return
4239 * NOT_LINKED then */
4245 /* returns TRUE if all streams in current chain reached EOS, FALSE otherwise */
4247 gst_ogg_demux_check_eos (GstOggDemux * ogg)
4250 gboolean eos = TRUE;
4252 chain = ogg->current_chain;
4253 if (G_LIKELY (chain)) {
4256 for (i = 0; i < chain->streams->len; i++) {
4257 GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4259 eos = eos && opad->is_eos;
4268 static GstFlowReturn
4269 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4272 GstBuffer *buffer = NULL;
4274 if (ogg->offset == ogg->length) {
4275 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4276 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
4281 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4282 ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
4283 if (ret != GST_FLOW_OK) {
4284 GST_LOG_OBJECT (ogg, "Failed pull_range");
4288 ogg->offset += gst_buffer_get_size (buffer);
4290 if (G_UNLIKELY (ogg->newsegment)) {
4291 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4292 ogg->newsegment = NULL;
4295 ret = gst_ogg_demux_chain (ogg->sinkpad, GST_OBJECT_CAST (ogg), buffer);
4296 if (ret != GST_FLOW_OK) {
4297 GST_LOG_OBJECT (ogg, "Failed demux_chain");
4301 /* check for the end of the segment */
4302 if (gst_ogg_demux_check_eos (ogg)) {
4303 GST_LOG_OBJECT (ogg, "got EOS");
4313 * We read the pages backwards and send the packets forwards. The first packet
4314 * in the page will be pushed with the DISCONT flag set.
4316 * Special care has to be taken for continued pages, which we can only decode
4317 * when we have the previous page(s).
4319 static GstFlowReturn
4320 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4326 if (ogg->offset == 0) {
4327 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4328 " == 0", ogg->offset);
4333 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
4334 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
4335 if (ret != GST_FLOW_OK)
4338 ogg->offset = offset;
4340 if (G_UNLIKELY (ogg->newsegment)) {
4341 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4342 ogg->newsegment = NULL;
4345 ret = gst_ogg_demux_handle_page (ogg, &page);
4346 if (ret != GST_FLOW_OK)
4349 /* check for the end of the segment */
4350 if (gst_ogg_demux_check_eos (ogg)) {
4351 GST_LOG_OBJECT (ogg, "got EOS");
4360 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4366 chain = ogg->current_chain;
4367 cur = ogg->segment.position;
4368 if (chain == NULL || cur == -1)
4371 for (i = 0; i < chain->streams->len; i++) {
4372 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4374 /* Theoretically, we should be doing this for all streams, but we're only
4375 * doing it for known-to-be-sparse streams at the moment in order not to
4376 * break things for wrongly-muxed streams (like we used to produce once) */
4377 if (stream->map.is_sparse && stream->position != GST_CLOCK_TIME_NONE) {
4379 /* Does this stream lag? Random threshold of 2 seconds */
4380 if (GST_CLOCK_DIFF (stream->position, cur) > (2 * GST_SECOND)) {
4381 GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
4382 "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
4383 GST_TIME_ARGS (stream->position), GST_TIME_ARGS (cur));
4385 stream->position = cur;
4388 ogg->segment.base += cur - stream->position;
4389 /* advance stream time (FIXME: is this right, esp. time_pos?) */
4390 gst_pad_push_event (GST_PAD_CAST (stream),
4391 gst_event_new_new_segment (TRUE, ogg->segment.rate,
4392 ogg->segment.applied_rate,
4393 GST_FORMAT_TIME, stream->position, -1, stream->position));
4400 /* random access code
4402 * - first find all the chains and streams by scanning the file.
4403 * - then get and chain buffers, just like the streaming case.
4404 * - when seeking, we can use the chain info to perform the seek.
4407 gst_ogg_demux_loop (GstOggPad * pad)
4413 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4415 if (ogg->need_chains) {
4418 /* this is the only place where we write chains and thus need to lock. */
4419 GST_CHAIN_LOCK (ogg);
4420 ret = gst_ogg_demux_find_chains (ogg);
4421 GST_CHAIN_UNLOCK (ogg);
4422 if (ret != GST_FLOW_OK)
4423 goto chain_read_failed;
4425 ogg->need_chains = FALSE;
4427 GST_OBJECT_LOCK (ogg);
4428 ogg->running = TRUE;
4431 GST_OBJECT_UNLOCK (ogg);
4433 /* and seek to configured positions without FLUSH */
4434 res = gst_ogg_demux_perform_seek_pull (ogg, event);
4436 gst_event_unref (event);
4442 if (ogg->segment.rate >= 0.0)
4443 ret = gst_ogg_demux_loop_forward (ogg);
4445 ret = gst_ogg_demux_loop_reverse (ogg);
4447 if (ret != GST_FLOW_OK)
4450 gst_ogg_demux_sync_streams (ogg);
4456 /* error was posted */
4461 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4462 ("failed to start demuxing ogg"));
4463 ret = GST_FLOW_ERROR;
4468 const gchar *reason = gst_flow_get_name (ret);
4469 GstEvent *event = NULL;
4471 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4472 gst_pad_pause_task (ogg->sinkpad);
4474 if (ret == GST_FLOW_EOS) {
4475 /* perform EOS logic */
4476 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4478 GstMessage *message;
4480 /* for segment playback we need to post when (in stream time)
4481 * we stopped, this is either stop (when set) or the duration. */
4482 if ((stop = ogg->segment.stop) == -1)
4483 stop = ogg->segment.duration;
4485 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
4487 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
4489 gst_message_set_seqnum (message, ogg->seqnum);
4491 gst_element_post_message (GST_ELEMENT (ogg), message);
4493 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
4494 gst_event_set_seqnum (event, ogg->seqnum);
4495 gst_ogg_demux_send_event (ogg, event);
4498 /* normal playback, send EOS to all linked pads */
4499 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
4500 event = gst_event_new_eos ();
4502 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
4503 GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
4504 (_("Internal data stream error.")),
4505 ("stream stopped, reason %s", reason));
4506 event = gst_event_new_eos ();
4509 /* For wrong-state we still want to pause the task and stop
4510 * but no error message or other things are necessary.
4511 * wrong-state is no real error and will be caused by flushing,
4512 * e.g. because of a flushing seek.
4515 /* guard against corrupt/truncated files, where one can hit EOS
4516 before prerolling is done and a chain created. If we have no
4517 chain to send the event to, error out. */
4518 if (ogg->current_chain || ogg->building_chain) {
4519 gst_event_set_seqnum (event, ogg->seqnum);
4520 gst_ogg_demux_send_event (ogg, event);
4522 gst_event_unref (event);
4523 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4524 ("EOS before finding a chain"));
4532 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
4536 gst_ogg_demux_deactivate_current_chain (ogg);
4538 GST_CHAIN_LOCK (ogg);
4539 for (i = 0; i < ogg->chains->len; i++) {
4540 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4542 gst_ogg_chain_free (chain);
4544 ogg->chains = g_array_set_size (ogg->chains, 0);
4545 GST_CHAIN_UNLOCK (ogg);
4548 /* this function is called when the pad is activated and should start
4551 * We check if we can do random access to decide if we work push or
4555 gst_ogg_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
4560 query = gst_query_new_scheduling ();
4562 if (!gst_pad_peer_query (sinkpad, query)) {
4563 gst_query_unref (query);
4567 pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL);
4568 gst_query_unref (query);
4573 GST_DEBUG_OBJECT (sinkpad, "activating pull");
4574 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
4578 GST_DEBUG_OBJECT (sinkpad, "activating push");
4579 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
4584 gst_ogg_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
4585 GstPadMode mode, gboolean active)
4590 ogg = GST_OGG_DEMUX (parent);
4593 case GST_PAD_MODE_PUSH:
4594 ogg->pullmode = FALSE;
4595 ogg->resync = FALSE;
4598 case GST_PAD_MODE_PULL:
4600 ogg->need_chains = TRUE;
4601 ogg->pullmode = TRUE;
4603 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
4606 res = gst_pad_stop_task (sinkpad);
4616 static GstStateChangeReturn
4617 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
4620 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
4622 ogg = GST_OGG_DEMUX (element);
4624 switch (transition) {
4625 case GST_STATE_CHANGE_NULL_TO_READY:
4627 ogg_sync_init (&ogg->sync);
4629 case GST_STATE_CHANGE_READY_TO_PAUSED:
4630 ogg_sync_reset (&ogg->sync);
4631 ogg->running = FALSE;
4633 ogg->total_time = -1;
4634 GST_PUSH_LOCK (ogg);
4635 ogg->push_byte_offset = 0;
4636 ogg->push_byte_length = -1;
4637 ogg->push_time_length = GST_CLOCK_TIME_NONE;
4638 ogg->push_time_offset = GST_CLOCK_TIME_NONE;
4639 ogg->push_state = PUSH_PLAYING;
4641 ogg->push_disable_seeking = FALSE;
4642 if (!ogg->pullmode) {
4644 if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
4646 if (!gst_pad_query_duration (peer, GST_FORMAT_BYTES, &length)
4648 GST_DEBUG_OBJECT (ogg,
4649 "Unable to determine stream size, assuming live, seeking disabled");
4650 ogg->push_disable_seeking = TRUE;
4652 gst_object_unref (peer);
4656 GST_PUSH_UNLOCK (ogg);
4657 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
4659 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4665 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4667 switch (transition) {
4668 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4670 case GST_STATE_CHANGE_PAUSED_TO_READY:
4671 gst_ogg_demux_clear_chains (ogg);
4672 GST_OBJECT_LOCK (ogg);
4673 ogg->running = FALSE;
4674 GST_OBJECT_UNLOCK (ogg);
4676 case GST_STATE_CHANGE_READY_TO_NULL:
4677 ogg_sync_clear (&ogg->sync);
4686 gst_ogg_demux_plugin_init (GstPlugin * plugin)
4688 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
4689 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
4690 "ogg demuxer setup stage when parsing pipeline");
4693 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
4695 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4696 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4699 return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
4700 GST_TYPE_OGG_DEMUX);
4703 /* prints all info about the element */
4704 #undef GST_CAT_DEFAULT
4705 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
4707 #ifdef GST_DISABLE_GST_DEBUG
4710 gst_ogg_print (GstOggDemux * ogg)
4715 #else /* !GST_DISABLE_GST_DEBUG */
4718 gst_ogg_print (GstOggDemux * ogg)
4722 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
4723 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4724 GST_TIME_ARGS (ogg->total_time));
4726 for (i = 0; i < ogg->chains->len; i++) {
4727 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4729 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
4730 GST_INFO_OBJECT (ogg,
4731 " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
4733 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
4734 GST_TIME_ARGS (chain->begin_time));
4735 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4736 GST_TIME_ARGS (chain->total_time));
4737 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
4738 GST_TIME_ARGS (chain->segment_start));
4739 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
4740 GST_TIME_ARGS (chain->segment_stop));
4742 for (j = 0; j < chain->streams->len; j++) {
4743 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
4745 GST_INFO_OBJECT (ogg, " stream %08x: %s", stream->map.serialno,
4746 gst_ogg_stream_get_media_type (&stream->map));
4747 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
4748 GST_TIME_ARGS (stream->start_time));
4752 #endif /* GST_DISABLE_GST_DEBUG */