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., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 * SECTION:element-oggdemux
25 * @see_also: <link linkend="gst-plugins-base-plugins-oggmux">oggmux</link>
27 * This element demuxes ogg files into their encoded audio and video components.
29 * ## Example pipelines
31 * gst-launch-1.0 -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! autoaudiosink
33 * Decodes a vorbis audio stream stored inside an ogg container and plays it.
43 #include <glib/gi18n-lib.h>
44 #include <gst/tag/tag.h>
45 #include <gst/audio/audio.h>
47 #include "gstoggelements.h"
48 #include "gstoggdemux.h"
50 #define CHUNKSIZE (8500) /* this is out of vorbisfile */
52 /* we hope we get a granpos within this many bytes off the end */
53 #define DURATION_CHUNK_OFFSET (128*1024)
55 /* An Ogg page can not be larger than 255 segments of 255 bytes, plus
57 #define MAX_OGG_PAGE_SIZE (255 * 255 + 26)
59 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
60 #define GST_FLOW_SKIP_PUSH GST_FLOW_CUSTOM_SUCCESS_1
62 #define SEEK_GIVE_UP_THRESHOLD (3*GST_SECOND)
64 #define GST_CHAIN_LOCK(ogg) g_mutex_lock(&(ogg)->chain_lock)
65 #define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock(&(ogg)->chain_lock)
67 #define GST_PUSH_LOCK(ogg) \
69 GST_TRACE_OBJECT(ogg, "Push lock"); \
70 g_mutex_lock(&(ogg)->push_lock); \
73 #define GST_PUSH_UNLOCK(ogg) \
75 GST_TRACE_OBJECT(ogg, "Push unlock"); \
76 g_mutex_unlock(&(ogg)->push_lock); \
79 GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
80 GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
81 #define GST_CAT_DEFAULT gst_ogg_demux_debug
85 _ogg_packet_copy (const ogg_packet * packet)
87 ogg_packet *ret = g_slice_new (ogg_packet);
90 ret->packet = g_memdup2 (packet->packet, packet->bytes);
96 _ogg_packet_free (ogg_packet * packet)
98 g_free (packet->packet);
99 g_slice_free (ogg_packet, packet);
103 gst_ogg_page_copy (ogg_page * page)
105 ogg_page *p = g_slice_new (ogg_page);
107 /* make a copy of the page */
108 p->header = g_memdup2 (page->header, page->header_len);
109 p->header_len = page->header_len;
110 p->body = g_memdup2 (page->body, page->body_len);
111 p->body_len = page->body_len;
117 gst_ogg_page_free (ogg_page * page)
119 g_free (page->header);
121 g_slice_free (ogg_page, page);
124 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
125 GstOggChain * chain);
126 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
127 GstOggChain * chain, GstEvent * event);
128 static void gst_ogg_pad_mark_discont (GstOggPad * pad);
129 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
131 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
133 static gboolean gst_ogg_demux_receive_event (GstElement * element,
136 static void gst_ogg_pad_dispose (GObject * object);
137 static void gst_ogg_pad_finalize (GObject * object);
139 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstObject * parent,
141 static gboolean gst_ogg_pad_event (GstPad * pad, GstObject * parent,
143 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
146 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
147 GstOggPad * pad, GstFlowReturn ret);
148 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
150 static GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg,
151 GstCaps * caps, GList * headers);
152 static gboolean gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
153 static gboolean gst_ogg_demux_perform_seek_push (GstOggDemux * ogg,
155 static gboolean gst_ogg_demux_check_duration_push (GstOggDemux * ogg,
156 GstSeekFlags flags, GstEvent * event);
158 GType gst_ogg_pad_get_type (void);
159 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
162 gst_ogg_pad_class_init (GstOggPadClass * klass)
164 GObjectClass *gobject_class;
166 gobject_class = (GObjectClass *) klass;
168 gobject_class->dispose = gst_ogg_pad_dispose;
169 gobject_class->finalize = gst_ogg_pad_finalize;
173 gst_ogg_pad_init (GstOggPad * pad)
175 gst_pad_set_event_function (GST_PAD (pad),
176 GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
177 gst_pad_set_query_function (GST_PAD (pad),
178 GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
179 gst_pad_use_fixed_caps (GST_PAD (pad));
181 pad->current_granule = -1;
182 pad->prev_granule = -1;
183 pad->keyframe_granule = -1;
185 pad->start_time = GST_CLOCK_TIME_NONE;
187 pad->position = GST_CLOCK_TIME_NONE;
189 pad->have_type = FALSE;
190 pad->continued = NULL;
191 pad->map.headers = NULL;
192 pad->map.queued = NULL;
194 pad->map.granulerate_n = 0;
195 pad->map.granulerate_d = 0;
196 pad->map.granuleshift = -1;
200 gst_ogg_pad_dispose (GObject * object)
202 GstOggPad *pad = GST_OGG_PAD (object);
207 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
208 g_list_free (pad->map.headers);
209 pad->map.headers = NULL;
210 g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
211 g_list_free (pad->map.queued);
212 pad->map.queued = NULL;
214 g_free (pad->map.index);
215 pad->map.index = NULL;
217 /* clear continued pages */
218 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
219 g_list_free (pad->continued);
220 pad->continued = NULL;
223 gst_caps_unref (pad->map.caps);
224 pad->map.caps = NULL;
227 if (pad->map.taglist) {
228 gst_tag_list_unref (pad->map.taglist);
229 pad->map.taglist = NULL;
232 ogg_stream_reset (&pad->map.stream);
234 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
238 gst_ogg_pad_finalize (GObject * object)
240 GstOggPad *pad = GST_OGG_PAD (object);
242 ogg_stream_clear (&pad->map.stream);
244 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
248 gst_ogg_pad_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
253 ogg = GST_OGG_DEMUX (parent);
255 switch (GST_QUERY_TYPE (query)) {
256 case GST_QUERY_POSITION:
259 GstOggPad *ogg_pad = GST_OGG_PAD (pad);
261 gst_query_parse_position (query, &format, NULL);
262 /* can only get position in time */
263 if (format != GST_FORMAT_TIME)
266 gst_query_set_position (query, format, ogg_pad->position);
269 case GST_QUERY_DURATION:
272 gint64 total_time = -1;
274 gst_query_parse_duration (query, &format, NULL);
275 /* can only get duration in time */
276 if (format != GST_FORMAT_TIME)
279 if (ogg->total_time != -1) {
280 /* we can return the total length */
281 total_time = ogg->total_time;
283 gint bitrate = ogg->bitrate;
285 /* try with length and bitrate */
289 /* ask upstream for total length in bytes */
290 uquery = gst_query_new_duration (GST_FORMAT_BYTES);
291 if (gst_pad_peer_query (ogg->sinkpad, uquery)) {
294 gst_query_parse_duration (uquery, NULL, &length);
296 /* estimate using the bitrate */
298 gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);
301 "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
302 GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
304 gst_query_unref (uquery);
308 gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
311 case GST_QUERY_SEEKING:
315 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
316 if (format == GST_FORMAT_TIME) {
317 gboolean seekable = FALSE;
320 GST_CHAIN_LOCK (ogg);
323 stop = ogg->total_time;
324 } else if (ogg->push_disable_seeking) {
326 } else if (ogg->current_chain == NULL) {
329 /* assume we can seek if upstream is seekable in BYTES format */
330 GST_LOG_OBJECT (ogg, "no current chain, check upstream seekability");
331 squery = gst_query_new_seeking (GST_FORMAT_BYTES);
332 if (gst_pad_peer_query (ogg->sinkpad, squery))
333 gst_query_parse_seeking (squery, NULL, &seekable, NULL, NULL);
336 gst_query_unref (squery);
337 } else if (ogg->current_chain->streams->len) {
341 for (i = 0; i < ogg->current_chain->streams->len; i++) {
343 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
346 if (pad->map.index != NULL && pad->map.n_index != 0) {
348 GstClockTime idx_time;
350 idx = &pad->map.index[pad->map.n_index - 1];
352 gst_util_uint64_scale (idx->timestamp, GST_SECOND,
357 stop = MAX (idx_time, stop);
359 stop = ogg->push_time_length;
361 stop = ogg->total_time;
366 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
367 GST_CHAIN_UNLOCK (ogg);
373 case GST_QUERY_SEGMENT:{
377 format = ogg->segment.format;
380 gst_segment_to_stream_time (&ogg->segment, format,
382 if ((stop = ogg->segment.stop) == -1)
383 stop = ogg->segment.duration;
385 stop = gst_segment_to_stream_time (&ogg->segment, format, stop);
387 gst_query_set_segment (query, ogg->segment.rate, format, start, stop);
392 res = gst_pad_query_default (pad, parent, query);
402 GST_DEBUG_OBJECT (ogg, "only query position/duration on TIME is supported");
409 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
414 ogg = GST_OGG_DEMUX (element);
416 switch (GST_EVENT_TYPE (event)) {
418 /* now do the seek */
419 res = gst_ogg_demux_perform_seek (ogg, event);
420 gst_event_unref (event);
423 GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
432 GST_DEBUG_OBJECT (ogg, "error handling event");
433 gst_event_unref (event);
439 gst_ogg_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
444 ogg = GST_OGG_DEMUX (parent);
446 switch (GST_EVENT_TYPE (event)) {
448 /* now do the seek */
449 res = gst_ogg_demux_perform_seek (ogg, event);
450 gst_event_unref (event);
452 case GST_EVENT_RECONFIGURE:
453 GST_OGG_PAD (pad)->last_ret = GST_FLOW_OK;
454 res = gst_pad_event_default (pad, parent, event);
457 res = gst_pad_event_default (pad, parent, event);
465 gst_ogg_pad_reset (GstOggPad * pad)
467 ogg_stream_reset (&pad->map.stream);
469 GST_DEBUG_OBJECT (pad, "doing reset");
471 /* clear continued pages */
472 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
473 g_list_free (pad->continued);
474 pad->continued = NULL;
476 pad->last_ret = GST_FLOW_OK;
477 pad->position = GST_CLOCK_TIME_NONE;
478 pad->current_granule = -1;
479 pad->prev_granule = -1;
480 pad->keyframe_granule = -1;
484 /* queue data, basically takes the packet, puts it in a buffer and store the
485 * buffer in the queued list. */
487 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
489 #ifndef GST_DISABLE_GST_DEBUG
490 GstOggDemux *ogg = pad->ogg;
493 GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x",
494 pad, pad->map.serialno);
496 pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
503 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
504 gboolean push_headers)
506 GstBuffer *buf = NULL;
507 GstFlowReturn ret, cret;
508 GstOggDemux *ogg = pad->ogg;
514 GstClockTime out_timestamp, out_duration;
515 guint64 out_offset, out_offset_end;
516 gboolean delta_unit = FALSE;
518 guint64 clip_start = 0, clip_end = 0;
520 ret = cret = GST_FLOW_OK;
521 GST_DEBUG_OBJECT (pad, "Chaining %d %d %" GST_TIME_FORMAT " %d %p",
522 ogg->pullmode, ogg->push_state, GST_TIME_ARGS (ogg->push_time_length),
523 ogg->push_disable_seeking, ogg->building_chain);
525 if (G_UNLIKELY (pad->is_eos)) {
526 GST_DEBUG_OBJECT (pad, "Skipping packet on pad that is eos");
532 if (!ogg->pullmode && ogg->push_state == PUSH_PLAYING
533 && ogg->push_time_length == GST_CLOCK_TIME_NONE
534 && !ogg->push_disable_seeking) {
535 if (!ogg->building_chain) {
536 /* we got all headers, now try to get duration */
537 if (!gst_ogg_demux_check_duration_push (ogg, GST_SEEK_FLAG_FLUSH, NULL)) {
538 GST_PUSH_UNLOCK (ogg);
542 GST_PUSH_UNLOCK (ogg);
545 GST_PUSH_UNLOCK (ogg);
547 GST_DEBUG_OBJECT (ogg,
548 "%p streaming to peer serial %08x", pad, pad->map.serialno);
550 gst_ogg_stream_update_stats (&pad->map, packet);
552 if (pad->map.is_ogm) {
556 data = packet->packet;
557 bytes = packet->bytes;
562 if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
563 /* We don't push header packets for OGM */
567 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
568 delta_unit = (((data[0] & 0x08) >> 3) == 0);
572 /* Strip trailing \0 for subtitles */
573 if (pad->map.is_ogm_text) {
574 while (bytes && data[bytes - 1] == 0) {
579 } else if (pad->map.is_vp8) {
580 if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
582 (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) {
583 /* We don't push header packets for VP8 */
588 delta_unit = !gst_ogg_stream_packet_is_key_frame (&pad->map, packet);
592 delta_unit = !gst_ogg_stream_packet_is_key_frame (&pad->map, packet);
595 /* get timing info for the packet */
596 is_header = gst_ogg_stream_packet_is_header (&pad->map, packet);
599 GST_DEBUG_OBJECT (ogg, "packet is header");
601 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
602 GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
606 /* If we get a hole at start, it might be we're catching a stream
607 * partway through. In that case, if the stream has an index, the
608 * index might be mooted. However, as it's totally valid to index
609 * a stream with a hole at start (ie, capturing a live stream and
610 * then index it), we now check whether the index references some
611 * offset beyond the byte length (if known). If this is the case,
612 * we can be reasonably sure we're getting a stream partway, with
613 * its index being now useless since we don't know how many bytes
614 * were skipped, preventing us from patching the index offsets to
615 * match the hole size. */
616 if (!is_header && ogg->check_index_overflow) {
623 if (ogg->current_chain) {
624 query = gst_query_new_duration (GST_FORMAT_BYTES);
625 if (gst_pad_peer_query (ogg->sinkpad, query)) {
626 gst_query_parse_duration (query, &format, &length);
627 if (format == GST_FORMAT_BYTES && length >= 0) {
628 for (i = 0; i < ogg->current_chain->streams->len; i++) {
630 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
631 if (!ipad->map.index)
633 beyond = ipad->map.n_index
634 && ipad->map.index[ipad->map.n_index - 1].offset >= length;
636 GST_WARNING_OBJECT (pad, "Index offsets beyond byte length");
638 /* hole - the index is most likely screwed up */
639 GST_WARNING_OBJECT (ogg, "Discarding entire index");
640 g_free (ipad->map.index);
641 ipad->map.index = NULL;
642 ipad->map.n_index = 0;
644 /* no hole - we can just clip the index if needed */
645 GST_WARNING_OBJECT (ogg, "Clipping index");
646 while (ipad->map.n_index > 0
647 && ipad->map.index[ipad->map.n_index - 1].offset >= length)
649 if (ipad->map.n_index == 0) {
650 GST_WARNING_OBJECT (ogg, "The entire index was clipped");
651 g_free (ipad->map.index);
652 ipad->map.index = NULL;
655 /* We can't trust the total time if the index goes beyond */
656 ipad->map.total_time = -1;
658 /* use total time to update the total ogg time */
659 if (ogg->total_time == -1) {
660 ogg->total_time = ipad->map.total_time;
661 } else if (ipad->map.total_time > 0) {
662 ogg->total_time = MAX (ogg->total_time, ipad->map.total_time);
668 gst_query_unref (query);
670 ogg->check_index_overflow = FALSE;
674 out_timestamp = GST_CLOCK_TIME_NONE;
675 out_duration = GST_CLOCK_TIME_NONE;
679 if (packet->granulepos > -1) {
680 gint64 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
683 GST_ERROR_OBJECT (ogg,
684 "granulepos %" G_GINT64_FORMAT " yielded granule %" G_GINT64_FORMAT,
685 (gint64) packet->granulepos, (gint64) granule);
686 return GST_FLOW_ERROR;
688 pad->current_granule = granule;
689 pad->keyframe_granule =
690 gst_ogg_stream_granulepos_to_key_granule (&pad->map,
692 GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
693 pad->current_granule);
694 } else if (pad->current_granule != -1) {
695 pad->current_granule += duration;
697 pad->keyframe_granule = pad->current_granule;
699 GST_DEBUG_OBJECT (ogg, "interpolating granule %" G_GUINT64_FORMAT,
700 pad->current_granule);
703 if (ogg->segment.rate < 0.0 && pad->current_granule == -1) {
704 /* negative rates, allow output of packets with no timestamp, let downstream reconstruct */
709 pad->prev_granule = -1;
711 /* we only push buffers after we have a valid granule. This is done so that
712 * we nicely skip packets without a timestamp after a seek. This is ok
713 * because we base our seek on the packet after the page with the smaller
715 if (pad->current_granule == -1) {
716 pad->prev_granule = -1;
720 if (pad->map.is_ogm) {
721 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
722 pad->current_granule);
723 out_duration = gst_util_uint64_scale (duration,
724 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
725 } else if (pad->map.is_sparse) {
726 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
727 pad->current_granule);
728 if (duration == GST_CLOCK_TIME_NONE) {
729 out_duration = GST_CLOCK_TIME_NONE;
731 out_duration = gst_util_uint64_scale (duration,
732 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
735 /* The last packet may be clipped. This will be represented
736 by the last granule being smaller than what it would otherwise
737 have been, had no content been clipped. In that case, we
738 cannot calculate the PTS of the audio from the packet length
741 if (pad->prev_granule >= 0)
742 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
745 out_timestamp = GST_CLOCK_TIME_NONE;
747 if (pad->map.audio_clipping
748 && pad->current_granule < pad->prev_granule + duration) {
749 clip_end = pad->prev_granule + duration - pad->current_granule;
751 if (pad->map.audio_clipping
752 && pad->current_granule - duration < -pad->map.granule_offset) {
753 if (pad->current_granule >= -pad->map.granule_offset) {
754 guint64 already_removed =
755 pad->current_granule >
756 duration ? pad->current_granule - duration : 0;
759 -pad->map.granule_offset ? 0 : -pad->map.granule_offset -
762 clip_start = pad->current_granule;
765 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
766 pad->current_granule - duration);
768 if (pad->map.audio_clipping
769 && pad->current_granule - duration < -pad->map.granule_offset) {
770 if (pad->current_granule >= -pad->map.granule_offset) {
771 guint64 already_removed =
772 pad->current_granule >
773 duration ? pad->current_granule - duration : 0;
776 -pad->map.granule_offset ? 0 : -pad->map.granule_offset -
779 clip_start = pad->current_granule;
783 gst_ogg_stream_granule_to_time (&pad->map,
784 pad->current_granule) - out_timestamp;
787 gst_ogg_stream_granule_to_granulepos (&pad->map,
788 pad->current_granule, pad->keyframe_granule);
790 gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
792 pad->prev_granule = pad->current_granule;
795 if (G_UNLIKELY (offset + trim > packet->bytes))
797 else if (pad->map.is_ogm_text) {
798 /* check for invalid buffer sizes */
799 if (G_UNLIKELY (offset + trim >= packet->bytes))
806 buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
808 if (pad->map.audio_clipping && (clip_start || clip_end)) {
809 GST_DEBUG_OBJECT (pad,
810 "Clipping %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT " (%"
811 G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT ")", clip_start, clip_end,
812 clip_start + clip_end, duration);
813 gst_buffer_add_audio_clipping_meta (buf, GST_FORMAT_DEFAULT, clip_start,
817 /* set delta flag for OGM content */
819 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
821 /* set header flag for buffers that are also in the streamheaders */
823 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
825 if (packet->packet != NULL) {
826 /* copy packet in buffer */
827 gst_buffer_fill (buf, 0, packet->packet + offset,
828 packet->bytes - offset - trim);
831 GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
832 GST_BUFFER_DURATION (buf) = out_duration;
833 GST_BUFFER_OFFSET (buf) = out_offset;
834 GST_BUFFER_OFFSET_END (buf) = out_offset_end;
836 /* Mark discont on the buffer */
838 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
839 if (ogg->segment.rate < 0.0 || GST_BUFFER_TIMESTAMP_IS_VALID (buf))
840 pad->discont = FALSE;
843 /* don't push the header packets when we are asked to skip them */
844 if (!packet->b_o_s || push_headers) {
845 if (pad->last_ret == GST_FLOW_OK) {
846 GST_LOG_OBJECT (ogg, "Pushing buf %" GST_PTR_FORMAT, buf);
847 ret = gst_pad_push (GST_PAD_CAST (pad), buf);
849 GST_DEBUG_OBJECT (ogg, "not pushing buffer on error pad");
851 gst_buffer_unref (buf);
856 /* we're done with skeleton stuff */
857 if (pad->map.is_skeleton)
860 /* check if valid granulepos, then we can calculate the current
861 * position. We know the granule for each packet but we only want to update
862 * the position when we have a valid granulepos on the packet because else
863 * our time jumps around for the different streams. */
864 if (packet->granulepos < 0)
867 /* convert to time */
868 current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
871 /* convert to stream time */
872 if ((chain = pad->chain)) {
873 gint64 chain_start = 0;
875 if (chain->segment_start != GST_CLOCK_TIME_NONE)
876 chain_start = chain->segment_start;
878 current_time = current_time - chain_start + chain->begin_time;
881 /* and store as the current position */
882 ogg->segment.position = current_time;
884 GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT
885 " (%" G_GINT64_FORMAT ")", GST_TIME_ARGS (current_time), current_time);
887 pad->position = ogg->segment.position;
889 /* check stream eos */
890 if (!pad->is_eos && !delta_unit &&
891 ((ogg->segment.rate > 0.0 &&
892 ogg->segment.stop != GST_CLOCK_TIME_NONE &&
893 current_time >= ogg->segment.stop) ||
894 (ogg->segment.rate < 0.0 && current_time <= ogg->segment.start))) {
895 GST_DEBUG_OBJECT (ogg, "marking pad %p EOS", pad);
898 if (ret == GST_FLOW_OK) {
905 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
909 gst_buffer_unref (buf);
910 /* return combined flow result */
916 GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
922 GST_DEBUG_OBJECT (ogg, "Skipping invalid packet");
928 GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
933 GST_DEBUG_OBJECT (ogg, "pad not added yet");
939 gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
942 guint64 start_time = G_MAXUINT64;
944 for (i = 0; i < chain->streams->len; i++) {
945 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
947 if (pad->map.is_skeleton)
950 /* can do this if the pad start time is not defined */
951 GST_DEBUG_OBJECT (ogg, "Pad %08x (%s) start time is %" GST_TIME_FORMAT,
952 pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map),
953 GST_TIME_ARGS (pad->start_time));
954 if (pad->start_time == GST_CLOCK_TIME_NONE) {
955 if (!pad->map.is_sparse) {
956 start_time = G_MAXUINT64;
960 start_time = MIN (start_time, pad->start_time);
967 gst_ogg_demux_collect_sync_time (GstOggDemux * ogg, GstOggChain * chain)
970 GstClockTime sync_time = GST_CLOCK_TIME_NONE;
973 GST_WARNING_OBJECT (ogg, "No chain!");
974 return GST_CLOCK_TIME_NONE;
977 for (i = 0; i < chain->streams->len; i++) {
978 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
980 if (pad->map.is_sparse)
983 if (pad->push_sync_time == GST_CLOCK_TIME_NONE) {
984 sync_time = GST_CLOCK_TIME_NONE;
987 if (sync_time == GST_CLOCK_TIME_NONE)
988 sync_time = pad->push_sync_time;
990 sync_time = MAX (sync_time, pad->push_sync_time);
996 /* submit a packet to the oggpad, this function will run the type detection
997 * code for the pad if this is the first packet for this stream
1000 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
1003 GstFlowReturn ret = GST_FLOW_OK;
1005 GstOggDemux *ogg = pad->ogg;
1007 GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x",
1008 pad, pad->map.serialno);
1010 if (!pad->have_type) {
1011 pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
1012 if (!pad->have_type && !pad->map.caps) {
1013 pad->map.caps = gst_caps_new_empty_simple ("application/x-unknown");
1015 if (pad->map.is_skeleton) {
1016 GST_DEBUG_OBJECT (ogg, "we have a fishead");
1017 /* copy values over to global ogg level */
1018 ogg->basetime = pad->map.basetime;
1019 ogg->prestime = pad->map.prestime;
1021 /* use total time to update the total ogg time */
1022 if (ogg->total_time == -1) {
1023 ogg->total_time = pad->map.total_time;
1024 } else if (pad->map.total_time > 0) {
1025 ogg->total_time = MAX (ogg->total_time, pad->map.total_time);
1028 if (!pad->map.caps) {
1029 GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
1033 if (pad->map.is_skeleton) {
1035 GstOggPad *skel_pad;
1036 GstOggSkeleton type;
1038 /* try to parse the serialno first */
1039 if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
1040 &serialno, &type)) {
1042 GST_DEBUG_OBJECT (pad->ogg,
1043 "got skeleton packet for stream 0x%08x", serialno);
1045 skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
1048 case GST_OGG_SKELETON_FISBONE:
1049 /* parse the remainder of the fisbone in the pad with the serialno,
1050 * note that we ignore the start_time as this is usually wrong for
1052 gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
1053 packet->bytes, NULL);
1055 case GST_OGG_SKELETON_INDEX:
1056 gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
1058 ogg->check_index_overflow = TRUE;
1065 GST_WARNING_OBJECT (pad->ogg,
1066 "found skeleton fisbone for an unknown stream 0x%08x", serialno);
1071 GST_DEBUG_OBJECT (ogg, "%p packet has granulepos %" G_GINT64_FORMAT, pad,
1072 (gint64) packet->granulepos);
1074 gst_ogg_stream_granulepos_to_granule (&pad->map, packet->granulepos);
1076 GST_DEBUG_OBJECT (ogg, "%p has granule %" G_GINT64_FORMAT, pad, granule);
1077 pad->current_granule = granule;
1078 } else if (granule == 0) {
1080 } else if (granule != -1) {
1081 GST_ERROR_OBJECT (ogg,
1082 "granulepos %" G_GINT64_FORMAT " yielded granule %" G_GINT64_FORMAT,
1083 (gint64) packet->granulepos, (gint64) granule);
1084 return GST_FLOW_ERROR;
1087 /* restart header packet count when seeing a b_o_s page;
1088 * particularly useful following a seek or even following chain finding */
1089 if (packet->b_o_s) {
1090 GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
1091 pad->map.n_header_packets_seen = 0;
1092 if (!pad->map.have_headers) {
1093 GST_DEBUG_OBJECT (ogg, "clearing header packets");
1094 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
1095 g_list_free (pad->map.headers);
1096 pad->map.headers = NULL;
1100 /* Overload the value of b_o_s in ogg_packet with a flag whether or
1101 * not this is a header packet. Maybe some day this could be cleaned
1103 packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
1104 if (!packet->b_o_s) {
1105 GST_DEBUG ("found non-header packet");
1106 pad->map.have_headers = TRUE;
1107 if (pad->start_time == GST_CLOCK_TIME_NONE) {
1108 gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
1109 GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
1110 if (duration != -1) {
1111 pad->map.accumulated_granule += duration;
1112 GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
1113 pad->map.accumulated_granule);
1116 if (packet->granulepos != -1) {
1117 ogg_int64_t start_granule;
1120 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
1121 packet->granulepos);
1123 GST_ERROR_OBJECT (ogg,
1124 "granulepos %" G_GINT64_FORMAT " yielded granule %"
1125 G_GINT64_FORMAT, (gint64) packet->granulepos, (gint64) granule);
1126 return GST_FLOW_ERROR;
1129 if (granule >= pad->map.accumulated_granule)
1130 start_granule = granule - pad->map.accumulated_granule;
1134 pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
1136 GST_DEBUG_OBJECT (ogg,
1137 "start time %" GST_TIME_FORMAT " (%" GST_TIME_FORMAT ") for %s "
1138 "from granpos %" G_GINT64_FORMAT " (granule %" G_GINT64_FORMAT ", "
1139 "accumulated granule %" G_GINT64_FORMAT,
1140 GST_TIME_ARGS (pad->start_time), GST_TIME_ARGS (pad->start_time),
1141 gst_ogg_stream_get_media_type (&pad->map),
1142 (gint64) packet->granulepos, granule, pad->map.accumulated_granule);
1144 packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
1145 pad->map.accumulated_granule + pad->current_granule,
1146 pad->keyframe_granule);
1150 /* look for tags in header packet (before inc header count) */
1151 gst_ogg_stream_extract_tags (&pad->map, packet);
1152 pad->map.n_header_packets_seen++;
1153 if (!pad->map.have_headers) {
1155 g_list_append (pad->map.headers, _ogg_packet_copy (packet));
1156 GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
1160 /* we know the start_time of the pad data, see if we
1161 * can activate the complete chain if this is a dynamic
1162 * chain. We need all the headers too for this. */
1163 if (pad->start_time != GST_CLOCK_TIME_NONE && pad->map.have_headers) {
1164 GstOggChain *chain = pad->chain;
1166 /* check if complete chain has start time */
1167 if (chain == ogg->building_chain) {
1168 GstEvent *event = NULL;
1173 GST_DEBUG_OBJECT (ogg, "need to resync");
1175 /* when we need to resync after a seek, we wait until we have received
1176 * timestamps on all streams */
1177 start_time = gst_ogg_demux_collect_start_time (ogg, chain);
1179 if (start_time != G_MAXUINT64) {
1180 gint64 segment_time;
1183 GST_DEBUG_OBJECT (ogg, "start_time: %" GST_TIME_FORMAT,
1184 GST_TIME_ARGS (start_time));
1186 if (chain->segment_start < start_time)
1188 (start_time - chain->segment_start) + chain->begin_time;
1190 segment_time = chain->begin_time;
1192 /* create the newsegment event we are going to send out */
1193 gst_segment_init (&segment, GST_FORMAT_TIME);
1195 GST_PUSH_LOCK (ogg);
1196 if (!ogg->pullmode && ogg->push_state == PUSH_LINEAR2) {
1197 /* if we are fast forwarding to the actual seek target,
1198 ensure previous frames are clipped */
1199 GST_DEBUG_OBJECT (ogg,
1200 "Resynced, starting segment at %" GST_TIME_FORMAT
1201 ", start_time %" GST_TIME_FORMAT,
1202 GST_TIME_ARGS (ogg->push_seek_time_original_target),
1203 GST_TIME_ARGS (start_time));
1204 segment.rate = ogg->push_seek_rate;
1205 segment.start = ogg->push_seek_time_original_target;
1206 segment.position = ogg->push_seek_time_original_target;
1207 segment.stop = ogg->push_seek_time_original_stop;
1208 segment.time = ogg->push_seek_time_original_target;
1209 segment.base = ogg->segment.base;
1210 event = gst_event_new_segment (&segment);
1211 ogg->push_state = PUSH_PLAYING;
1213 segment.rate = ogg->segment.rate;
1214 segment.applied_rate = ogg->segment.applied_rate;
1215 segment.start = start_time;
1216 segment.position = start_time;
1217 segment.stop = chain->segment_stop;
1218 segment.time = segment_time;
1219 segment.base = ogg->segment.base;
1220 event = gst_event_new_segment (&segment);
1222 GST_PUSH_UNLOCK (ogg);
1224 ogg->resync = FALSE;
1227 /* see if we have enough info to activate the chain, we have enough info
1228 * when all streams have a valid start time. */
1229 if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
1232 GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
1233 GST_TIME_ARGS (chain->segment_start));
1234 GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT,
1235 GST_TIME_ARGS (chain->segment_stop));
1236 GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT,
1237 GST_TIME_ARGS (chain->begin_time));
1239 /* create the newsegment event we are going to send out */
1240 gst_segment_init (&segment, GST_FORMAT_TIME);
1241 segment.rate = ogg->segment.rate;
1242 segment.applied_rate = ogg->segment.applied_rate;
1243 segment.start = chain->segment_start;
1244 segment.position = chain->segment_start;
1245 segment.stop = chain->segment_stop;
1246 segment.time = chain->begin_time;
1247 segment.base = ogg->segment.base + segment.time;
1248 event = gst_event_new_segment (&segment);
1253 gst_event_set_seqnum (event, ogg->seqnum);
1255 gst_ogg_demux_activate_chain (ogg, chain, event);
1257 ogg->building_chain = NULL;
1262 /* if we are building a chain, store buffer for when we activate
1263 * it. This path is taken if we operate in streaming mode. */
1264 if (ogg->building_chain) {
1265 /* bos packets where stored in the header list so we can discard
1268 ret = gst_ogg_demux_queue_data (pad, packet);
1270 /* else we are completely streaming to the peer */
1272 ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
1277 /* flush at most @npackets from the stream layer. All packets if
1280 static GstFlowReturn
1281 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1283 GstFlowReturn result = GST_FLOW_OK;
1284 gboolean done = FALSE;
1293 ret = ogg_stream_packetout (&pad->map.stream, &packet);
1296 GST_LOG_OBJECT (ogg, "packetout done");
1300 GST_LOG_OBJECT (ogg, "packetout discont");
1301 if (!pad->map.is_sparse) {
1302 gst_ogg_chain_mark_discont (pad->chain);
1304 gst_ogg_pad_mark_discont (pad);
1308 GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1310 if (packet.granulepos < -1) {
1311 GST_WARNING_OBJECT (ogg,
1312 "Invalid granulepos (%" G_GINT64_FORMAT "), resetting stream",
1313 (gint64) packet.granulepos);
1314 gst_ogg_pad_reset (pad);
1318 if (packet.bytes > ogg->max_packet_size)
1319 ogg->max_packet_size = packet.bytes;
1320 result = gst_ogg_pad_submit_packet (pad, &packet);
1321 /* not linked is not a problem, it's possible that we are still
1322 * collecting headers and that we don't have exposed the pads yet */
1323 if (result == GST_FLOW_NOT_LINKED)
1325 else if (result <= GST_FLOW_EOS)
1326 goto could_not_submit;
1329 GST_WARNING_OBJECT (ogg,
1330 "invalid return value %d for ogg_stream_packetout, resetting stream",
1332 gst_ogg_pad_reset (pad);
1337 done = (npackets == 0);
1345 GST_WARNING_OBJECT (ogg,
1346 "could not submit packet for stream %08x, "
1347 "error: %d", pad->map.serialno, result);
1348 gst_ogg_pad_reset (pad);
1354 gst_ogg_demux_setup_first_granule (GstOggDemux * ogg, GstOggPad * pad,
1357 /* When we submit a page, we check if we have started tracking granules.
1358 * If not, we calculate the granule corresponding to the first packet
1360 gboolean valid_granule = TRUE;
1362 if (pad->current_granule == -1) {
1363 ogg_int64_t granpos = ogg_page_granulepos (page);
1366 (gint64) gst_ogg_stream_granulepos_to_granule (&pad->map, granpos);
1368 int packets = ogg_page_packets (page), n;
1369 GST_DEBUG_OBJECT (pad,
1370 "This page completes %d packets, granule %" G_GINT64_FORMAT, packets,
1374 ogg_stream_state os;
1376 int last_size = pad->map.last_size;
1378 memcpy (&os, &pad->map.stream, sizeof (os));
1379 for (n = 0; valid_granule && n < packets; ++n) {
1380 int ret = ogg_stream_packetout (&os, &op);
1382 /* This usually means a continued packet after a seek and we can't calc the first granule,
1383 * but sometimes not - so if it's ret == -1 and first packet, try again */
1384 if (ret == -1 && n == 0) {
1388 GST_DEBUG_OBJECT (pad, "Failed to read packet off first page");
1389 valid_granule = FALSE;
1393 GST_WARNING_OBJECT (pad,
1394 "Short read getting %d packets off first page", packets);
1395 valid_granule = FALSE;
1398 duration = gst_ogg_stream_get_packet_duration (&pad->map, &op);
1399 GST_DEBUG_OBJECT (pad, "Packet %d has duration %" G_GINT64_FORMAT,
1401 granule -= duration;
1403 pad->map.last_size = last_size;
1404 if (valid_granule) {
1406 pad->current_granule = granule;
1407 GST_INFO_OBJECT (pad,
1408 "Starting with first granule %" G_GINT64_FORMAT, granule);
1410 pad->current_granule = 0;
1411 GST_INFO_OBJECT (pad, "Extrapolated first granule is negative, "
1412 "used to clip samples at start");
1416 GST_WARNING_OBJECT (pad,
1417 "Ogg page finishing no packets, but a valid granule");
1424 gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg)
1426 if (ogg->push_last_seek_time >= ogg->push_seek_time_target) {
1427 GST_DEBUG_OBJECT (ogg, "We overshot by %" GST_TIME_FORMAT,
1428 GST_TIME_ARGS (ogg->push_last_seek_time - ogg->push_seek_time_target));
1429 ogg->push_offset1 = ogg->push_last_seek_offset;
1430 ogg->push_time1 = ogg->push_last_seek_time;
1431 ogg->seek_undershot = FALSE;
1433 GST_DEBUG_OBJECT (ogg, "We undershot by %" GST_TIME_FORMAT,
1434 GST_TIME_ARGS (ogg->push_seek_time_target - ogg->push_last_seek_time));
1435 ogg->push_offset0 = ogg->push_last_seek_offset;
1436 ogg->push_time0 = ogg->push_last_seek_time;
1437 ogg->seek_undershot = TRUE;
1442 gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg, float seek_quality)
1445 gint64 segment_bitrate;
1448 /* we might not know the length of the stream in time,
1449 so push_time1 might not be set */
1450 GST_DEBUG_OBJECT (ogg,
1451 "push time 1: %" GST_TIME_FORMAT ", dbytes %" G_GINT64_FORMAT,
1452 GST_TIME_ARGS (ogg->push_time1), ogg->push_offset1 - ogg->push_offset0);
1453 if (ogg->push_time1 == GST_CLOCK_TIME_NONE) {
1454 GST_DEBUG_OBJECT (ogg,
1455 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1456 ", time %" GST_TIME_FORMAT " (open ended)", ogg->push_offset0,
1457 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0));
1458 if (ogg->push_last_seek_time == ogg->push_start_time) {
1459 /* if we're at start and don't know the end time, we can't estimate
1460 bitrate, so get the nominal declared bitrate as a failsafe, or some
1461 random constant which will be discarded after we made a (probably
1462 dire) first guess */
1463 segment_bitrate = (ogg->bitrate > 0 ? ogg->bitrate : 1000);
1466 gst_util_uint64_scale (ogg->push_last_seek_offset - 0,
1467 8 * GST_SECOND, ogg->push_last_seek_time - ogg->push_start_time);
1471 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1472 segment_bitrate, 8 * GST_SECOND);
1473 ogg->seek_secant = TRUE;
1475 GST_DEBUG_OBJECT (ogg,
1476 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1477 ", time %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, ogg->push_offset0,
1478 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
1479 GST_TIME_ARGS (ogg->push_time1));
1480 if (ogg->push_time0 == ogg->push_time1) {
1481 best = ogg->push_offset0;
1484 gst_util_uint64_scale (ogg->push_offset1 - ogg->push_offset0,
1485 8 * GST_SECOND, ogg->push_time1 - ogg->push_time0);
1486 GST_DEBUG_OBJECT (ogg,
1487 "Local bitrate on the %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1488 " segment: %" G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_time0),
1489 GST_TIME_ARGS (ogg->push_time1), segment_bitrate);
1493 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1494 segment_bitrate, 8 * GST_SECOND);
1495 if (seek_quality < 0.5f && ogg->seek_secant) {
1496 gint64 new_best, best2 = (ogg->push_offset0 + ogg->push_offset1) / 2;
1497 /* if dire result, give as much as 25% weight to a dumb bisection guess */
1498 float secant_weight = 1.0f - ((0.5 - seek_quality) / 0.5f) * 0.25;
1499 new_best = (best * secant_weight + best2 * (1.0f - secant_weight));
1500 GST_DEBUG_OBJECT (ogg,
1501 "Secant says %" G_GINT64_FORMAT ", straight is %" G_GINT64_FORMAT
1502 ", new best %" G_GINT64_FORMAT " with secant_weight %f", best,
1503 best2, new_best, secant_weight);
1505 ogg->seek_secant = FALSE;
1507 ogg->seek_secant = TRUE;
1512 GST_DEBUG_OBJECT (ogg, "Raw best guess: %" G_GINT64_FORMAT, best);
1514 /* offset the guess down as we need to capture the start of the
1515 page we are targeting - but only do so if we did not undershoot
1516 last time, as we're likely to still do this time */
1517 if (!ogg->seek_undershot) {
1518 /* very small packets are packed on pages, so offset by at least
1519 a value which is likely to get us at least one page where the
1522 ogg->max_packet_size >
1523 ogg->max_page_size ? ogg->max_packet_size : ogg->max_page_size;
1524 GST_DEBUG_OBJECT (ogg, "Offsetting by %" G_GINT64_FORMAT, skew);
1528 /* do not seek too close to the bounds, as we stop seeking
1529 when we get to within max_packet_size before the target */
1530 if (best > ogg->push_offset1 - ogg->max_packet_size) {
1531 best = ogg->push_offset1 - ogg->max_packet_size;
1532 GST_DEBUG_OBJECT (ogg,
1533 "Too close to high bound, pushing back to %" G_GINT64_FORMAT, best);
1534 } else if (best < ogg->push_offset0 + ogg->max_packet_size) {
1535 best = ogg->push_offset0 + ogg->max_packet_size;
1536 GST_DEBUG_OBJECT (ogg,
1537 "Too close to low bound, pushing forth to %" G_GINT64_FORMAT, best);
1540 /* keep within bounds */
1541 if (best > ogg->push_offset1)
1542 best = ogg->push_offset1;
1543 if (best < ogg->push_offset0)
1544 best = ogg->push_offset0;
1546 GST_DEBUG_OBJECT (ogg, "Choosing target %" G_GINT64_FORMAT, best);
1551 gst_ogg_demux_record_keyframe_time (GstOggDemux * ogg, GstOggPad * pad,
1552 ogg_int64_t granpos)
1555 GstClockTime kf_time;
1557 kf_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, granpos);
1558 kf_time = gst_ogg_stream_granule_to_time (&pad->map, kf_granule);
1560 pad->push_kf_time = kf_time;
1563 /* returns the earliest keyframe time for all non sparse pads in the chain,
1564 * if known, and GST_CLOCK_TIME_NONE if not */
1566 gst_ogg_demux_get_earliest_keyframe_time (GstOggDemux * ogg)
1568 GstClockTime t = GST_CLOCK_TIME_NONE;
1569 GstOggChain *chain = ogg->building_chain;
1573 GST_WARNING_OBJECT (ogg, "No chain!");
1574 return GST_CLOCK_TIME_NONE;
1576 for (i = 0; i < chain->streams->len; i++) {
1577 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1579 if (pad->map.is_sparse)
1581 if (pad->push_kf_time == GST_CLOCK_TIME_NONE)
1582 return GST_CLOCK_TIME_NONE;
1583 if (t == GST_CLOCK_TIME_NONE || pad->push_kf_time < t)
1584 t = pad->push_kf_time;
1590 /* MUST be called with the push lock locked, and will unlock it
1591 regardless of return value. */
1592 static GstFlowReturn
1593 gst_ogg_demux_seek_back_after_push_duration_check_unlock (GstOggDemux * ogg)
1597 /* Get the delayed event, if any */
1598 event = ogg->push_mode_seek_delayed_event;
1599 ogg->push_mode_seek_delayed_event = NULL;
1601 /* if we haven't learnt about the total time yet, disable seeking */
1602 if (ogg->total_time == -1)
1603 ogg->push_disable_seeking = TRUE;
1605 ogg->push_state = PUSH_PLAYING;
1607 /* If there is one, perform it. Otherwise, seek back at start to start
1608 * normal playback */
1610 GST_INFO_OBJECT (ogg, "Seeking back to 0 after duration check");
1611 event = gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1612 GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
1613 GST_SEEK_TYPE_SET, 1, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
1614 /* drop everything until this seek event completed. We can't wait until the
1615 * seek thread sets this because there would be race between receiving e.g.
1616 * an EOS or any data and the seek thread actually picking up the seek. */
1617 ogg->seek_event_drop_till = gst_event_get_seqnum (event);
1619 gst_event_replace (&ogg->seek_event, event);
1620 gst_event_unref (event);
1621 GST_PUSH_UNLOCK (ogg);
1622 g_mutex_lock (&ogg->seek_event_mutex);
1623 g_cond_broadcast (&ogg->seek_event_cond);
1624 g_mutex_unlock (&ogg->seek_event_mutex);
1630 gst_ogg_demux_estimate_seek_quality (GstOggDemux * ogg)
1632 GstClockTimeDiff diff; /* how far from the goal we ended up */
1633 GstClockTimeDiff dist; /* how far we moved this iteration */
1636 if (ogg->push_prev_seek_time == GST_CLOCK_TIME_NONE) {
1637 /* for the first seek, we pretend we got a good seek,
1638 as we don't have a previous seek yet */
1642 /* We take a guess at how good the last seek was at guessing
1643 the byte target by comparing the amplitude of the last
1644 seek to the error */
1645 diff = GST_CLOCK_DIFF (ogg->push_seek_time_target, ogg->push_last_seek_time);
1648 dist = GST_CLOCK_DIFF (ogg->push_last_seek_time, ogg->push_prev_seek_time);
1652 seek_quality = (dist == 0) ? 0.0f : 1.0f / (1.0f + diff / (float) dist);
1654 GST_DEBUG_OBJECT (ogg,
1655 "We moved %" GST_STIME_FORMAT ", we're off by %" GST_STIME_FORMAT
1656 ", seek quality %f", GST_STIME_ARGS (dist), GST_STIME_ARGS (diff),
1658 return seek_quality;
1662 gst_ogg_demux_update_bisection_stats (GstOggDemux * ogg)
1666 GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps",
1667 ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]);
1669 for (n = 0; n < 2; ++n) {
1670 ogg->stats_bisection_steps[n] += ogg->push_bisection_steps[n];
1671 if (ogg->stats_bisection_max_steps[n] < ogg->push_bisection_steps[n])
1672 ogg->stats_bisection_max_steps[n] = ogg->push_bisection_steps[n];
1674 ogg->stats_nbisections++;
1676 GST_INFO_OBJECT (ogg,
1677 "So far, %.2f + %.2f bisections needed per seek (max %d + %d)",
1678 ogg->stats_bisection_steps[0] / (float) ogg->stats_nbisections,
1679 ogg->stats_bisection_steps[1] / (float) ogg->stats_nbisections,
1680 ogg->stats_bisection_max_steps[0], ogg->stats_bisection_max_steps[1]);
1684 gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
1686 GstOggDemux *ogg = pad->ogg;
1687 ogg_int64_t granpos = ogg_page_granulepos (page);
1689 GST_PUSH_LOCK (ogg);
1690 if (granpos >= 0 && pad->have_type) {
1691 if (ogg->push_start_time == GST_CLOCK_TIME_NONE) {
1692 ogg->push_start_time =
1693 gst_ogg_stream_get_start_time_for_granulepos (&pad->map, granpos);
1694 GST_DEBUG_OBJECT (ogg, "Stream start time: %" GST_TIME_FORMAT,
1695 GST_TIME_ARGS (ogg->push_start_time));
1697 ogg->push_time_offset =
1698 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1699 if (ogg->push_time_offset > 0) {
1700 GST_DEBUG_OBJECT (ogg, "Bitrate since start: %" G_GUINT64_FORMAT,
1701 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
1702 ogg->push_time_offset));
1705 if (ogg->push_state == PUSH_DURATION) {
1707 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1709 if (ogg->total_time == GST_CLOCK_TIME_NONE || t > ogg->total_time) {
1710 GST_DEBUG_OBJECT (ogg, "New total time: %" GST_TIME_FORMAT,
1712 ogg->total_time = t;
1713 ogg->push_time_length = t;
1716 /* If we're still receiving data from before the seek segment, drop it */
1717 if (ogg->seek_event_drop_till != 0) {
1718 GST_PUSH_UNLOCK (ogg);
1719 return GST_FLOW_SKIP_PUSH;
1722 /* If we were determining the duration of the stream, we're now done,
1723 and can get back to sending the original event we delayed.
1724 We stop a bit before the end of the stream, as if we get a EOS
1725 event and there is a queue2 upstream (such as when using playbin),
1726 it will pause the task *after* we come back from the EOS handler,
1727 so we cannot prevent the pausing by issuing a seek. */
1728 if (ogg->push_byte_offset >= ogg->push_byte_length) {
1729 GstMessage *message;
1732 /* tell the pipeline we've just found out the duration */
1733 ogg->push_time_length = ogg->total_time;
1734 GST_INFO_OBJECT (ogg, "New duration found: %" GST_TIME_FORMAT,
1735 GST_TIME_ARGS (ogg->total_time));
1736 message = gst_message_new_duration_changed (GST_OBJECT (ogg));
1737 gst_element_post_message (GST_ELEMENT (ogg), message);
1739 GST_DEBUG_OBJECT (ogg,
1740 "We're close enough to the end, and we're scared "
1741 "to get too close, seeking back to start");
1743 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1744 if (res != GST_FLOW_OK)
1746 return GST_FLOW_SKIP_PUSH;
1748 GST_PUSH_UNLOCK (ogg);
1750 return GST_FLOW_SKIP_PUSH;
1754 /* if we're seeking, look at time, and decide what to do */
1755 if (ogg->push_state != PUSH_PLAYING && ogg->push_state != PUSH_LINEAR2) {
1759 gboolean close_enough;
1762 /* ignore -1 granpos when seeking, we want to sync on a real granpos */
1764 GST_PUSH_UNLOCK (ogg);
1765 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1767 if (pad->current_granule == -1)
1768 gst_ogg_demux_setup_first_granule (ogg, pad, page);
1769 return GST_FLOW_SKIP_PUSH;
1772 t = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1774 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1775 GstClockTime sync_time;
1777 if (pad->push_sync_time == GST_CLOCK_TIME_NONE)
1778 pad->push_sync_time = t;
1779 GST_DEBUG_OBJECT (ogg, "Got PTS %" GST_TIME_FORMAT " for %s",
1780 GST_TIME_ARGS (t), gst_ogg_stream_get_media_type (&pad->map));
1781 sync_time = gst_ogg_demux_collect_sync_time (ogg, ogg->building_chain);
1782 if (sync_time == GST_CLOCK_TIME_NONE) {
1783 GST_PUSH_UNLOCK (ogg);
1784 GST_DEBUG_OBJECT (ogg,
1785 "Not enough timing info collected for sync, waiting for more");
1786 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1788 if (pad->current_granule == -1)
1789 gst_ogg_demux_setup_first_granule (ogg, pad, page);
1790 return GST_FLOW_SKIP_PUSH;
1792 ogg->push_last_seek_time = sync_time;
1794 GST_DEBUG_OBJECT (ogg,
1795 "Bisection just seeked at %" G_GINT64_FORMAT ", time %"
1796 GST_TIME_FORMAT ", target was %" GST_TIME_FORMAT,
1797 ogg->push_last_seek_offset,
1798 GST_TIME_ARGS (ogg->push_last_seek_time),
1799 GST_TIME_ARGS (ogg->push_seek_time_target));
1801 if (ogg->push_time1 != GST_CLOCK_TIME_NONE) {
1802 seek_quality = gst_ogg_demux_estimate_seek_quality (ogg);
1803 GST_DEBUG_OBJECT (ogg,
1804 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1805 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1806 " (%" GST_TIME_FORMAT "), seek quality %f", ogg->push_offset0,
1807 ogg->push_offset1, ogg->push_offset1 - ogg->push_offset0,
1808 GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1),
1809 GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0), seek_quality);
1811 /* in a open ended seek, we can't do bisection, so we pretend
1812 we like our result so far */
1813 seek_quality = 1.0f;
1814 GST_DEBUG_OBJECT (ogg,
1815 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1816 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - unknown",
1817 ogg->push_offset0, ogg->push_offset1,
1818 ogg->push_offset1 - ogg->push_offset0,
1819 GST_TIME_ARGS (ogg->push_time0));
1821 ogg->push_prev_seek_time = ogg->push_last_seek_time;
1823 gst_ogg_demux_setup_bisection_bounds (ogg);
1825 best = gst_ogg_demux_estimate_bisection_target (ogg, seek_quality);
1827 if (ogg->push_seek_time_target == 0) {
1828 GST_DEBUG_OBJECT (ogg, "Seeking to 0, deemed close enough");
1829 close_enough = (ogg->push_last_seek_time == 0);
1831 /* TODO: make this dependent on framerate ? */
1832 GstClockTime time_threshold = GST_SECOND / 2;
1833 guint64 byte_threshold =
1834 (ogg->max_packet_size >
1835 64 * 1024 ? ogg->max_packet_size : 64 * 1024);
1837 /* We want to be within half a second before the target,
1838 or before the target and half less or equal to the max
1839 packet size left to search in */
1840 if (time_threshold > ogg->push_seek_time_target)
1841 time_threshold = ogg->push_seek_time_target;
1842 close_enough = ogg->push_last_seek_time < ogg->push_seek_time_target
1843 && (ogg->push_last_seek_time >=
1844 ogg->push_seek_time_target - time_threshold
1845 || ogg->push_offset1 <= ogg->push_offset0 + byte_threshold);
1846 GST_DEBUG_OBJECT (ogg,
1847 "testing if we're close enough: %" GST_TIME_FORMAT " <= %"
1848 GST_TIME_FORMAT " < %" GST_TIME_FORMAT ", or %" G_GUINT64_FORMAT
1849 " <= %" G_GUINT64_FORMAT " ? %s",
1850 GST_TIME_ARGS (ogg->push_seek_time_target - time_threshold),
1851 GST_TIME_ARGS (ogg->push_last_seek_time),
1852 GST_TIME_ARGS (ogg->push_seek_time_target),
1853 ogg->push_offset1 - ogg->push_offset0, byte_threshold,
1854 close_enough ? "Yes" : "No");
1857 if (close_enough || best == ogg->push_last_seek_offset) {
1858 if (ogg->push_state == PUSH_BISECT1) {
1859 /* we now know the time segment we'll have to search for
1860 the second bisection */
1861 ogg->push_time0 = ogg->push_start_time;
1862 ogg->push_offset0 = 0;
1864 GST_DEBUG_OBJECT (ogg,
1865 "Seek to %" GST_TIME_FORMAT
1866 " (%lx) done, now gathering pages for all non-sparse streams",
1867 GST_TIME_ARGS (ogg->push_seek_time_target), (long) granpos);
1868 ogg->push_state = PUSH_LINEAR1;
1870 /* If we're asked for an accurate seek, we'll go forward till
1871 we get to the original seek target time, else we'll just drop
1872 here at the keyframe */
1873 if (ogg->push_seek_flags & GST_SEEK_FLAG_ACCURATE) {
1874 GST_INFO_OBJECT (ogg,
1875 "Seek to keyframe at %" GST_TIME_FORMAT " done (we're at %"
1876 GST_TIME_FORMAT "), skipping to original target (%"
1877 GST_TIME_FORMAT ")",
1878 GST_TIME_ARGS (ogg->push_seek_time_target),
1879 GST_TIME_ARGS (sync_time),
1880 GST_TIME_ARGS (ogg->push_seek_time_original_target));
1881 ogg->push_state = PUSH_LINEAR2;
1883 GST_INFO_OBJECT (ogg, "Seek to keyframe done, playing");
1885 /* we're synced to the seek target, so flush stream and stuff
1886 any queued pages into the stream so we start decoding there */
1887 ogg->push_state = PUSH_PLAYING;
1889 gst_ogg_demux_update_bisection_stats (ogg);
1892 } else if (ogg->push_state == PUSH_LINEAR1) {
1893 if (pad->push_kf_time == GST_CLOCK_TIME_NONE) {
1894 GstClockTime earliest_keyframe_time;
1896 gst_ogg_demux_record_keyframe_time (ogg, pad, granpos);
1897 GST_DEBUG_OBJECT (ogg,
1898 "Previous keyframe for %s stream at %" GST_TIME_FORMAT,
1899 gst_ogg_stream_get_media_type (&pad->map),
1900 GST_TIME_ARGS (pad->push_kf_time));
1901 earliest_keyframe_time = gst_ogg_demux_get_earliest_keyframe_time (ogg);
1902 if (earliest_keyframe_time != GST_CLOCK_TIME_NONE) {
1903 if (earliest_keyframe_time > ogg->push_last_seek_time) {
1904 GST_INFO_OBJECT (ogg,
1905 "All non sparse streams now have a previous keyframe time, "
1906 "and we already decoded it, switching to playing");
1907 ogg->push_state = PUSH_PLAYING;
1908 gst_ogg_demux_update_bisection_stats (ogg);
1910 GST_INFO_OBJECT (ogg,
1911 "All non sparse streams now have a previous keyframe time, "
1912 "bisecting again to %" GST_TIME_FORMAT,
1913 GST_TIME_ARGS (earliest_keyframe_time));
1915 ogg->push_seek_time_target = earliest_keyframe_time;
1916 ogg->push_offset0 = 0;
1917 ogg->push_time0 = ogg->push_start_time;
1918 ogg->push_offset1 = ogg->push_last_seek_offset;
1919 ogg->push_time1 = ogg->push_last_seek_time;
1920 ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
1921 ogg->seek_secant = FALSE;
1922 ogg->seek_undershot = FALSE;
1924 ogg->push_state = PUSH_BISECT2;
1925 best = gst_ogg_demux_estimate_bisection_target (ogg, 1.0f);
1931 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1934 ogg_sync_reset (&ogg->sync);
1935 for (i = 0; i < ogg->building_chain->streams->len; i++) {
1937 g_array_index (ogg->building_chain->streams, GstOggPad *, i);
1939 pad->push_sync_time = GST_CLOCK_TIME_NONE;
1940 ogg_stream_reset (&pad->map.stream);
1943 GST_DEBUG_OBJECT (ogg,
1944 "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, best,
1947 g_assert (best != -1);
1948 ogg->push_bisection_steps[ogg->push_state == PUSH_BISECT2 ? 1 : 0]++;
1950 gst_event_new_seek (ogg->push_seek_rate, GST_FORMAT_BYTES,
1951 ogg->push_seek_flags, GST_SEEK_TYPE_SET, best,
1952 GST_SEEK_TYPE_NONE, -1);
1953 gst_event_set_seqnum (sevent, ogg->seqnum);
1955 gst_event_replace (&ogg->seek_event, sevent);
1956 gst_event_unref (sevent);
1957 GST_PUSH_UNLOCK (ogg);
1958 g_mutex_lock (&ogg->seek_event_mutex);
1959 g_cond_broadcast (&ogg->seek_event_cond);
1960 g_mutex_unlock (&ogg->seek_event_mutex);
1961 return GST_FLOW_SKIP_PUSH;
1964 if (ogg->push_state != PUSH_PLAYING) {
1965 GST_PUSH_UNLOCK (ogg);
1966 return GST_FLOW_SKIP_PUSH;
1969 GST_PUSH_UNLOCK (ogg);
1975 GST_WARNING_OBJECT (ogg,
1976 "ogg stream choked on page (serial %08x), "
1977 "resetting stream", pad->map.serialno);
1978 gst_ogg_pad_reset (pad);
1979 /* we continue to recover */
1980 return GST_FLOW_SKIP_PUSH;
1985 gst_ogg_demux_query_duration_push (GstOggDemux * ogg)
1987 if (!ogg->pullmode && ogg->push_byte_length == -1) {
1989 gboolean seekable = FALSE;
1991 query = gst_query_new_seeking (GST_FORMAT_BYTES);
1992 if (gst_pad_peer_query (ogg->sinkpad, query))
1993 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
1994 gst_query_unref (query);
1998 if (!gst_element_query_duration (GST_ELEMENT (ogg), GST_FORMAT_BYTES,
2001 GST_DEBUG_OBJECT (ogg,
2002 "Unable to determine stream size, assuming live, seeking disabled");
2003 ogg->push_disable_seeking = TRUE;
2005 ogg->push_disable_seeking = FALSE;
2008 GST_DEBUG_OBJECT (ogg, "Stream is not seekable, seeking disabled");
2009 ogg->push_disable_seeking = TRUE;
2014 /* submit a page to an oggpad, this function will then submit all
2015 * the packets in the page.
2017 static GstFlowReturn
2018 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
2020 GstFlowReturn result = GST_FLOW_OK;
2022 gboolean continued = FALSE;
2026 /* for negative rates we read pages backwards and must therefore be careful
2027 * with continued pages */
2028 if (ogg->segment.rate < 0.0) {
2031 continued = ogg_page_continued (page);
2033 /* number of completed packets in the page */
2034 npackets = ogg_page_packets (page);
2036 /* page is not continued so it contains at least one packet start. It's
2037 * possible that no packet ends on this page (npackets == 0). In that
2038 * case, the next (continued) page(s) we kept contain the remainder of the
2039 * packets. We mark npackets=1 to make us start decoding the pages in the
2040 * remainder of the algorithm. */
2044 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
2046 if (npackets == 0) {
2047 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
2052 gst_ogg_demux_query_duration_push (ogg);
2054 /* keep track of time in push mode */
2055 if (!ogg->pullmode) {
2056 result = gst_ogg_pad_handle_push_mode_state (pad, page);
2057 if (result == GST_FLOW_SKIP_PUSH)
2059 if (result != GST_FLOW_OK)
2063 if (page->header_len + page->body_len > ogg->max_page_size)
2064 ogg->max_page_size = page->header_len + page->body_len;
2066 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
2068 if (pad->current_granule == -1)
2069 gst_ogg_demux_setup_first_granule (ogg, pad, page);
2071 /* flush all packets in the stream layer, this might not give a packet if
2072 * the page had no packets finishing on the page (npackets == 0). */
2073 result = gst_ogg_pad_stream_out (pad, 0);
2075 if (pad->continued) {
2078 /* now send the continued pages to the stream layer */
2079 while (pad->continued) {
2080 ogg_page *p = (ogg_page *) pad->continued->data;
2082 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
2083 if (ogg_stream_pagein (&pad->map.stream, p) != 0)
2086 pad->continued = g_list_delete_link (pad->continued, pad->continued);
2089 gst_ogg_page_free (p);
2092 GST_LOG_OBJECT (ogg, "flushing last continued packet");
2093 /* flush 1 continued packet in the stream layer */
2094 result = gst_ogg_pad_stream_out (pad, 1);
2096 /* flush all remaining packets, we pushed them in the previous round.
2097 * We don't use _reset() because we still want to get the discont when
2098 * we submit a next page. */
2099 while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
2103 /* keep continued pages (only in reverse mode) */
2105 ogg_page *p = gst_ogg_page_copy (page);
2107 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
2108 pad->continued = g_list_prepend (pad->continued, p);
2115 GST_WARNING_OBJECT (ogg,
2116 "ogg stream choked on page (serial %08x), "
2117 "resetting stream", pad->map.serialno);
2118 gst_ogg_pad_reset (pad);
2119 /* we continue to recover */
2125 static GstOggChain *
2126 gst_ogg_chain_new (GstOggDemux * ogg)
2128 GstOggChain *chain = g_slice_new0 (GstOggChain);
2130 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
2134 chain->have_bos = FALSE;
2135 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
2136 chain->begin_time = GST_CLOCK_TIME_NONE;
2137 chain->segment_start = GST_CLOCK_TIME_NONE;
2138 chain->segment_stop = GST_CLOCK_TIME_NONE;
2139 chain->total_time = GST_CLOCK_TIME_NONE;
2145 gst_ogg_chain_free (GstOggChain * chain)
2149 for (i = 0; i < chain->streams->len; i++) {
2150 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2152 gst_object_unref (pad);
2154 g_array_free (chain->streams, TRUE);
2155 g_slice_free (GstOggChain, chain);
2159 gst_ogg_pad_mark_discont (GstOggPad * pad)
2161 GST_LOG_OBJECT (pad, "Marking discont on pad");
2162 pad->discont = TRUE;
2163 pad->map.last_size = 0;
2167 gst_ogg_chain_mark_discont (GstOggChain * chain)
2171 for (i = 0; i < chain->streams->len; i++) {
2172 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2174 gst_ogg_pad_mark_discont (pad);
2179 gst_ogg_chain_reset (GstOggChain * chain)
2183 for (i = 0; i < chain->streams->len; i++) {
2184 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2186 gst_ogg_pad_reset (pad);
2191 gst_ogg_chain_new_stream (GstOggChain * chain, guint32 serialno)
2196 GST_DEBUG_OBJECT (chain->ogg,
2197 "creating new stream %08x in chain %p", serialno, chain);
2199 name = g_strdup_printf ("src_%08x", serialno);
2200 ret = g_object_new (GST_TYPE_OGG_PAD, "name", name, NULL);
2202 /* we own this one */
2203 gst_object_ref_sink (ret);
2205 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
2206 gst_ogg_pad_mark_discont (ret);
2209 ret->ogg = chain->ogg;
2211 ret->map.serialno = serialno;
2212 if (ogg_stream_init (&ret->map.stream, serialno) != 0)
2215 GST_DEBUG_OBJECT (chain->ogg,
2216 "created new ogg src %p for stream with serial %08x", ret, serialno);
2218 g_array_append_val (chain->streams, ret);
2219 gst_pad_set_active (GST_PAD_CAST (ret), TRUE);
2226 GST_ERROR ("Could not initialize ogg_stream struct for serial %08x",
2228 gst_object_unref (ret);
2234 gst_ogg_chain_get_stream (GstOggChain * chain, guint32 serialno)
2238 for (i = 0; i < chain->streams->len; i++) {
2239 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2241 if (pad->map.serialno == serialno)
2248 gst_ogg_chain_has_stream (GstOggChain * chain, guint32 serialno)
2250 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
2253 /* signals and args */
2266 static GstStaticPadTemplate ogg_demux_src_template_factory =
2267 GST_STATIC_PAD_TEMPLATE ("src_%08x",
2270 GST_STATIC_CAPS_ANY);
2272 static GstStaticPadTemplate ogg_demux_sink_template_factory =
2273 GST_STATIC_PAD_TEMPLATE ("sink",
2276 GST_STATIC_CAPS ("application/ogg; audio/ogg; video/ogg; application/kate")
2279 static void gst_ogg_demux_finalize (GObject * object);
2281 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
2282 GstOggChain ** chain);
2283 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
2284 GstOggChain * chain);
2286 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent,
2288 static void gst_ogg_demux_loop (GstOggPad * pad);
2289 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstObject * parent,
2290 GstBuffer * buffer);
2291 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad,
2292 GstObject * parent);
2293 static gboolean gst_ogg_demux_sink_activate_mode (GstPad * sinkpad,
2294 GstObject * parent, GstPadMode mode, gboolean active);
2295 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
2296 GstStateChange transition);
2298 static void gst_ogg_print (GstOggDemux * demux);
2299 static gboolean gst_ogg_demux_plugin_init (GstPlugin * plugin);
2301 #define gst_ogg_demux_parent_class parent_class
2302 G_DEFINE_TYPE (GstOggDemux, gst_ogg_demux, GST_TYPE_ELEMENT);
2303 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (oggdemux, "oggdemux", GST_RANK_PRIMARY,
2304 GST_TYPE_OGG_DEMUX, gst_ogg_demux_plugin_init (plugin));
2307 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
2309 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
2310 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2312 gst_element_class_set_static_metadata (gstelement_class,
2313 "Ogg demuxer", "Codec/Demuxer",
2314 "demux ogg streams (info about ogg: http://xiph.org)",
2315 "Wim Taymans <wim@fluendo.com>");
2317 gst_element_class_add_static_pad_template (gstelement_class,
2318 &ogg_demux_sink_template_factory);
2319 gst_element_class_add_static_pad_template (gstelement_class,
2320 &ogg_demux_src_template_factory);
2322 gstelement_class->change_state = gst_ogg_demux_change_state;
2323 gstelement_class->send_event = gst_ogg_demux_receive_event;
2325 gobject_class->finalize = gst_ogg_demux_finalize;
2329 gst_ogg_demux_init (GstOggDemux * ogg)
2331 /* create the sink pad */
2333 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
2336 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
2337 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
2338 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
2339 gst_pad_set_activatemode_function (ogg->sinkpad,
2340 gst_ogg_demux_sink_activate_mode);
2341 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
2343 g_mutex_init (&ogg->chain_lock);
2344 g_mutex_init (&ogg->push_lock);
2345 g_mutex_init (&ogg->seek_event_mutex);
2346 g_cond_init (&ogg->seek_event_cond);
2347 g_cond_init (&ogg->thread_started_cond);
2349 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
2351 ogg->stats_nbisections = 0;
2352 ogg->stats_bisection_steps[0] = 0;
2353 ogg->stats_bisection_steps[1] = 0;
2354 ogg->stats_bisection_max_steps[0] = 0;
2355 ogg->stats_bisection_max_steps[1] = 0;
2357 ogg->newsegment = NULL;
2358 ogg->seqnum = GST_SEQNUM_INVALID;
2360 ogg->chunk_size = CHUNKSIZE;
2361 ogg->flowcombiner = gst_flow_combiner_new ();
2365 gst_ogg_demux_finalize (GObject * object)
2369 ogg = GST_OGG_DEMUX (object);
2371 g_array_free (ogg->chains, TRUE);
2372 g_mutex_clear (&ogg->chain_lock);
2373 g_mutex_clear (&ogg->push_lock);
2374 g_cond_clear (&ogg->seek_event_cond);
2375 g_cond_clear (&ogg->thread_started_cond);
2376 g_mutex_clear (&ogg->seek_event_mutex);
2378 ogg_sync_clear (&ogg->sync);
2380 if (ogg->newsegment)
2381 gst_event_unref (ogg->newsegment);
2383 gst_flow_combiner_free (ogg->flowcombiner);
2385 if (ogg->building_chain)
2386 gst_ogg_chain_free (ogg->building_chain);
2388 G_OBJECT_CLASS (parent_class)->finalize (object);
2392 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
2397 chain = ogg->current_chain;
2401 for (i = 0; i < chain->streams->len; i++) {
2402 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2404 stream->start_time = -1;
2405 stream->map.accumulated_granule = 0;
2406 stream->current_granule = -1;
2407 stream->keyframe_granule = -1;
2409 ogg->building_chain = chain;
2410 GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2411 ogg->current_chain = NULL;
2413 gst_ogg_chain_mark_discont (chain);
2415 ogg->chunk_size = CHUNKSIZE;
2419 gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
2424 ogg = GST_OGG_DEMUX (parent);
2426 switch (GST_EVENT_TYPE (event)) {
2427 case GST_EVENT_FLUSH_START:
2428 if (ogg->seqnum != GST_SEQNUM_INVALID) {
2429 event = gst_event_make_writable (event);
2430 gst_event_set_seqnum (event, ogg->seqnum);
2432 res = gst_ogg_demux_send_event (ogg, event);
2434 case GST_EVENT_FLUSH_STOP:
2435 GST_DEBUG_OBJECT (ogg, "got a flush stop event");
2436 ogg_sync_reset (&ogg->sync);
2437 if (ogg->seqnum != GST_SEQNUM_INVALID) {
2438 event = gst_event_make_writable (event);
2439 gst_event_set_seqnum (event, ogg->seqnum);
2441 res = gst_ogg_demux_send_event (ogg, event);
2442 if (ogg->pullmode || ogg->push_state != PUSH_DURATION) {
2443 /* it's starting to feel reaaaally dirty :(
2444 if we're on a spliced seek to get duration, don't reset streams,
2445 we'll need them for the delayed seek */
2446 gst_ogg_demux_reset_streams (ogg);
2449 case GST_EVENT_SEGMENT:
2450 GST_DEBUG_OBJECT (ogg, "got a new segment event");
2455 gst_event_copy_segment (event, &segment);
2457 if (segment.format == GST_FORMAT_BYTES) {
2458 GST_PUSH_LOCK (ogg);
2459 ogg->push_byte_offset = segment.start;
2460 ogg->push_last_seek_offset = segment.start;
2462 if (gst_event_get_seqnum (event) == ogg->seqnum) {
2463 GstSeekType stop_type = GST_SEEK_TYPE_NONE;
2464 if (ogg->push_seek_time_original_stop != -1)
2465 stop_type = GST_SEEK_TYPE_SET;
2466 gst_segment_do_seek (&ogg->segment, ogg->push_seek_rate,
2467 GST_FORMAT_TIME, ogg->push_seek_flags, GST_SEEK_TYPE_SET,
2468 ogg->push_seek_time_original_target, stop_type,
2469 ogg->push_seek_time_original_stop, &update);
2470 } else if (ogg->seqnum == GST_SEQNUM_INVALID) {
2471 ogg->seqnum = GST_EVENT_SEQNUM (event);
2474 if (!ogg->pullmode && !(ogg->push_seek_flags & GST_SEEK_FLAG_FLUSH)) {
2476 GstOggChain *chain = ogg->current_chain;
2478 ogg->push_seek_flags = 0;
2480 /* This will happen when we bisect, as we clear the chain when
2481 we do the first seek. On subsequent ones, we just reset the
2482 ogg sync object as we already reset the chain */
2483 GST_DEBUG_OBJECT (ogg, "No chain, just resetting ogg sync");
2484 ogg_sync_reset (&ogg->sync);
2486 /* reset pad push mode seeking state */
2487 for (i = 0; i < chain->streams->len; i++) {
2488 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2489 pad->push_kf_time = GST_CLOCK_TIME_NONE;
2490 pad->push_sync_time = GST_CLOCK_TIME_NONE;
2492 ogg_sync_reset (&ogg->sync);
2493 gst_ogg_demux_reset_streams (ogg);
2497 if (!ogg->pullmode) {
2498 if (ogg->seek_event_drop_till == gst_event_get_seqnum (event)) {
2499 GST_DEBUG_OBJECT (ogg,
2500 "Got event seqnum %u, stopping dropping (ogg->seqnum:%u)",
2501 ogg->seek_event_drop_till, ogg->seqnum);
2502 ogg->seek_event_drop_till = 0;
2505 GST_PUSH_UNLOCK (ogg);
2507 GST_WARNING_OBJECT (ogg, "unexpected segment format: %s",
2508 gst_format_get_name (segment.format));
2512 gst_event_unref (event);
2517 gboolean drop = FALSE;
2518 GST_DEBUG_OBJECT (ogg, "got an EOS event");
2519 GST_PUSH_LOCK (ogg);
2520 if (ogg->push_state == PUSH_DURATION) {
2521 GST_DEBUG_OBJECT (ogg, "Got EOS while determining length");
2522 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
2523 if (res != GST_FLOW_OK) {
2524 GST_DEBUG_OBJECT (ogg, "Error seeking back after duration check: %d",
2527 gst_event_unref (event);
2531 if (ogg->seek_event_drop_till > 0) {
2532 GST_DEBUG_OBJECT (ogg, "Dropping EOS (seqnum:%u) because we have "
2533 "a pending seek (seqnum:%u)", gst_event_get_seqnum (event),
2534 ogg->seek_event_drop_till);
2537 GST_PUSH_UNLOCK (ogg);
2541 res = gst_ogg_demux_send_event (ogg, event);
2543 gst_event_unref (event);
2544 if (ogg->current_chain == NULL) {
2545 GST_WARNING_OBJECT (ogg,
2546 "EOS while trying to retrieve chain, seeking disabled");
2547 ogg->push_disable_seeking = TRUE;
2553 res = gst_pad_event_default (pad, parent, event);
2560 /* submit the given buffer to the ogg sync */
2561 static GstFlowReturn
2562 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
2566 GstFlowReturn ret = GST_FLOW_OK;
2568 size = gst_buffer_get_size (buffer);
2569 GST_DEBUG_OBJECT (ogg, "submitting %" G_GSIZE_FORMAT " bytes", size);
2570 if (G_UNLIKELY (size == 0))
2573 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
2574 if (G_UNLIKELY (oggbuffer == NULL))
2577 gst_buffer_extract (buffer, 0, oggbuffer, size);
2579 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2582 if (!ogg->pullmode) {
2583 GST_PUSH_LOCK (ogg);
2584 ogg->push_byte_offset += size;
2585 GST_PUSH_UNLOCK (ogg);
2589 gst_buffer_unref (buffer);
2596 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2597 (NULL), ("failed to get ogg sync buffer"));
2598 ret = GST_FLOW_ERROR;
2603 GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2604 ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2605 ret = GST_FLOW_ERROR;
2610 /* in random access mode this code updates the current read position
2611 * and resets the ogg sync buffer so that the next read will happen
2612 * from this new location.
2615 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
2617 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
2619 ogg->offset = offset;
2620 ogg->read_offset = offset;
2621 ogg_sync_reset (&ogg->sync);
2624 /* read more data from the current offset and submit to
2625 * the ogg sync layer.
2627 static GstFlowReturn
2628 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
2635 GST_LOG_OBJECT (ogg,
2636 "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
2637 ogg->read_offset, ogg->length, end_offset);
2639 if (end_offset > 0 && ogg->read_offset >= end_offset)
2640 goto boundary_reached;
2642 if (ogg->read_offset == ogg->length)
2645 oggbuffer = ogg_sync_buffer (&ogg->sync, ogg->chunk_size);
2646 if (G_UNLIKELY (oggbuffer == NULL))
2650 gst_buffer_new_wrapped_full (0, oggbuffer, ogg->chunk_size, 0,
2651 ogg->chunk_size, NULL, NULL);
2654 gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, ogg->chunk_size,
2656 if (ret != GST_FLOW_OK)
2659 size = gst_buffer_get_size (buffer);
2661 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2664 ogg->read_offset += size;
2665 gst_buffer_unref (buffer);
2672 GST_LOG_OBJECT (ogg, "reached boundary");
2673 return GST_FLOW_LIMIT;
2677 GST_LOG_OBJECT (ogg, "reached EOS");
2678 return GST_FLOW_EOS;
2682 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2683 (NULL), ("failed to get ogg sync buffer"));
2684 return GST_FLOW_ERROR;
2688 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
2689 gst_flow_get_name (ret));
2690 gst_buffer_unref (buffer);
2695 GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2696 ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2697 gst_buffer_unref (buffer);
2698 return GST_FLOW_ERROR;
2702 /* Read the next page from the current offset.
2703 * boundary: number of bytes ahead we allow looking for;
2706 * @offset will contain the offset the next page starts at when this function
2707 * returns GST_FLOW_OK.
2709 * GST_FLOW_EOS is returned on EOS.
2711 * GST_FLOW_LIMIT is returned when we did not find a page before the
2712 * boundary. If @boundary is -1, this is never returned.
2714 * Any other error returned while retrieving data from the peer is returned as
2717 static GstFlowReturn
2718 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
2719 gint64 boundary, gint64 * offset)
2721 gint64 end_offset = -1;
2724 GST_LOG_OBJECT (ogg,
2725 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
2726 G_GINT64_FORMAT, ogg->offset, boundary);
2729 end_offset = ogg->offset + boundary;
2734 if (end_offset > 0 && ogg->offset >= end_offset)
2735 goto boundary_reached;
2737 more = ogg_sync_pageseek (&ogg->sync, og);
2739 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
2742 /* skipped n bytes */
2743 ogg->offset -= more;
2744 GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
2746 } else if (more == 0) {
2747 /* we need more data */
2749 goto boundary_reached;
2751 GST_LOG_OBJECT (ogg, "need more data");
2752 ret = gst_ogg_demux_get_data (ogg, end_offset);
2753 if (ret != GST_FLOW_OK)
2756 gint64 res_offset = ogg->offset;
2758 /* got a page. Return the offset at the page beginning,
2759 advance the internal offset past the page end */
2761 *offset = res_offset;
2764 ogg->offset += more;
2766 GST_LOG_OBJECT (ogg,
2767 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
2768 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
2769 ogg_page_serialno (og), ogg->offset,
2770 (gint64) ogg_page_granulepos (og));
2774 GST_LOG_OBJECT (ogg, "returning %d", ret);
2781 GST_LOG_OBJECT (ogg,
2782 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
2783 ogg->offset, end_offset);
2784 return GST_FLOW_LIMIT;
2788 /* from the current offset, find the previous page, seeking backwards
2789 * until we find the page.
2791 static GstFlowReturn
2792 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
2795 gint64 begin = ogg->offset;
2797 gint64 cur_offset = -1;
2799 GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
2801 while (cur_offset == -1) {
2802 begin -= ogg->chunk_size;
2806 /* seek ogg->chunk_size back */
2807 GST_LOG_OBJECT (ogg, "seeking back to %" G_GINT64_FORMAT, begin);
2808 gst_ogg_demux_seek (ogg, begin);
2810 /* now continue reading until we run out of data, if we find a page
2811 * start, we save it. It might not be the final page as there could be
2812 * another page after this one. */
2813 while (ogg->offset < end) {
2814 gint64 new_offset, boundary;
2816 /* An Ogg page cannot be more than a bit less than 64 KB, so we can
2817 bound the boundary to that size when searching backwards if we
2818 haven't found a page yet. So the most we have to look at is twice
2819 the max page size, which is the worst case if we start scanning
2820 just after a large page, after which also lies a large page. */
2821 boundary = end - ogg->offset;
2822 if (boundary > 2 * MAX_OGG_PAGE_SIZE)
2823 boundary = 2 * MAX_OGG_PAGE_SIZE;
2825 ret = gst_ogg_demux_get_next_page (ogg, og, boundary, &new_offset);
2826 /* we hit the upper limit, offset contains the last page start */
2827 if (ret == GST_FLOW_LIMIT) {
2828 GST_LOG_OBJECT (ogg, "hit limit");
2831 /* something went wrong */
2832 if (ret == GST_FLOW_EOS) {
2834 GST_LOG_OBJECT (ogg, "got unexpected");
2837 } else if (ret != GST_FLOW_OK) {
2838 GST_LOG_OBJECT (ogg, "got error %d", ret);
2842 GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
2844 /* offset is next page start */
2845 cur_offset = new_offset;
2849 GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
2851 /* we have the offset. Actually snork and hold the page now */
2852 gst_ogg_demux_seek (ogg, cur_offset);
2853 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
2854 if (ret != GST_FLOW_OK) {
2855 GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
2857 /* this shouldn't be possible */
2862 *offset = cur_offset;
2869 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
2872 GstOggChain *chain = ogg->current_chain;
2877 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
2879 /* send EOS on all the pads */
2880 for (i = 0; i < chain->streams->len; i++) {
2881 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2887 event = gst_event_new_eos ();
2888 gst_event_set_seqnum (event, ogg->seqnum);
2889 gst_pad_push_event (GST_PAD_CAST (pad), event);
2891 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
2893 /* deactivate first */
2894 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
2896 gst_flow_combiner_remove_pad (ogg->flowcombiner, GST_PAD_CAST (pad));
2898 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2903 /* if we cannot seek back to the chain, we can destroy the chain
2905 if (!ogg->pullmode) {
2906 if (ogg->building_chain == chain)
2907 ogg->building_chain = NULL;
2908 ogg->current_chain = NULL;
2909 gst_ogg_chain_free (chain);
2916 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
2919 GstStructure *structure;
2920 GValue array = { 0 };
2922 GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
2924 if (G_UNLIKELY (!caps))
2926 if (G_UNLIKELY (!headers))
2929 caps = gst_caps_make_writable (caps);
2930 structure = gst_caps_get_structure (caps, 0);
2932 g_value_init (&array, GST_TYPE_ARRAY);
2935 GValue value = { 0 };
2937 ogg_packet *op = headers->data;
2939 buffer = gst_buffer_new_and_alloc (op->bytes);
2941 gst_buffer_fill (buffer, 0, op->packet, op->bytes);
2942 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_HEADER);
2943 g_value_init (&value, GST_TYPE_BUFFER);
2944 gst_value_take_buffer (&value, buffer);
2945 gst_value_array_append_value (&array, &value);
2946 g_value_unset (&value);
2947 headers = headers->next;
2950 gst_structure_take_value (structure, "streamheader", &array);
2951 GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
2957 gst_ogg_demux_push_queued_buffers (GstOggDemux * ogg, GstOggPad * pad)
2961 /* push queued packets */
2962 for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
2963 ogg_packet *p = walk->data;
2965 gst_ogg_demux_chain_peer (pad, p, TRUE);
2966 _ogg_packet_free (p);
2968 /* and free the queued buffers */
2969 g_list_free (pad->map.queued);
2970 pad->map.queued = NULL;
2974 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
2978 gint bitrate, idx_bitrate;
2980 g_return_val_if_fail (chain != NULL, FALSE);
2982 if (chain == ogg->current_chain) {
2984 gst_event_unref (event);
2986 for (i = 0; i < chain->streams->len; i++) {
2987 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2988 gst_ogg_demux_push_queued_buffers (ogg, pad);
2994 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
2996 bitrate = idx_bitrate = 0;
2998 /* first add the pads */
2999 for (i = 0; i < chain->streams->len; i++) {
3004 pad = g_array_index (chain->streams, GstOggPad *, i);
3006 if (pad->map.idx_bitrate)
3007 idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
3009 bitrate += pad->map.bitrate;
3012 gst_ogg_pad_mark_discont (pad);
3013 pad->last_ret = GST_FLOW_OK;
3015 if (pad->map.is_skeleton || pad->map.is_cmml || pad->added
3019 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
3021 /* activate first */
3022 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
3025 gst_pad_create_stream_id_printf (GST_PAD (pad), GST_ELEMENT_CAST (ogg),
3026 "%08x", pad->map.serialno);
3028 gst_pad_get_sticky_event (ogg->sinkpad, GST_EVENT_STREAM_START, 0);
3030 if (gst_event_parse_group_id (ss_event, &ogg->group_id))
3031 ogg->have_group_id = TRUE;
3033 ogg->have_group_id = FALSE;
3034 gst_event_unref (ss_event);
3035 } else if (!ogg->have_group_id) {
3036 ogg->have_group_id = TRUE;
3037 ogg->group_id = gst_util_group_id_next ();
3039 ss_event = gst_event_new_stream_start (stream_id);
3040 if (ogg->have_group_id)
3041 gst_event_set_group_id (ss_event, ogg->group_id);
3043 gst_pad_push_event (GST_PAD (pad), ss_event);
3046 /* Set headers on caps */
3048 gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers);
3049 gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps);
3051 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
3053 gst_flow_combiner_add_pad (ogg->flowcombiner, GST_PAD_CAST (pad));
3055 /* prefer the index bitrate over the ones encoded in the streams */
3056 ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
3058 /* after adding the new pads, remove the old pads */
3059 gst_ogg_demux_deactivate_current_chain (ogg);
3061 GST_DEBUG_OBJECT (ogg, "Setting current chain to %p", chain);
3062 ogg->current_chain = chain;
3064 /* we are finished now */
3065 gst_element_no_more_pads (GST_ELEMENT (ogg));
3067 GST_DEBUG_OBJECT (ogg, "starting chain");
3069 /* then send out any headers and queued packets */
3070 for (i = 0; i < chain->streams->len; i++) {
3075 pad = g_array_index (chain->streams, GstOggPad *, i);
3077 /* Skip pads that were not added, e.g. Skeleton streams */
3081 /* FIXME, must be sent from the streaming thread */
3083 gst_pad_push_event (GST_PAD_CAST (pad), gst_event_ref (event));
3085 /* FIXME also streaming thread */
3086 if (pad->map.taglist) {
3087 GST_DEBUG_OBJECT (ogg, "pushing tags");
3088 gst_pad_push_event (GST_PAD_CAST (pad),
3089 gst_event_new_tag (pad->map.taglist));
3090 pad->map.taglist = NULL;
3093 tags = gst_tag_list_new (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL);
3094 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
3095 gst_pad_push_event (GST_PAD (pad), gst_event_new_tag (tags));
3097 GST_DEBUG_OBJECT (ogg, "pushing headers");
3099 for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
3100 ogg_packet *p = walk->data;
3102 gst_ogg_demux_chain_peer (pad, p, TRUE);
3105 GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
3106 gst_ogg_demux_push_queued_buffers (ogg, pad);
3110 gst_event_unref (event);
3116 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
3117 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
3118 gint64 * offset, gboolean only_serial_no, gint serialno)
3126 GST_DEBUG_OBJECT (ogg,
3127 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
3129 GST_DEBUG_OBJECT (ogg,
3130 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
3131 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
3132 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
3134 /* perform the seek */
3135 while (begin < end) {
3138 if ((end - begin < ogg->chunk_size) || (endtime == begintime)) {
3141 /* take a (pretty decent) guess, avoiding overflow */
3142 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
3145 (target - begintime) / GST_MSECOND * rate + begin - ogg->chunk_size;
3147 if (bisect <= begin)
3149 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
3151 gst_ogg_demux_seek (ogg, bisect);
3153 while (begin < end) {
3156 GST_DEBUG_OBJECT (ogg,
3157 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
3158 ", end %" G_GINT64_FORMAT, bisect, begin, end);
3160 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
3161 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
3164 if (ret == GST_FLOW_LIMIT) {
3165 /* we hit the upper limit, go back a bit */
3166 if (bisect <= begin + 1) {
3167 end = begin; /* found it */
3172 bisect -= ogg->chunk_size;
3173 if (bisect <= begin)
3176 gst_ogg_demux_seek (ogg, bisect);
3178 } else if (ret == GST_FLOW_OK) {
3179 /* found offset of next ogg page */
3181 GstClockTime granuletime;
3184 /* get the granulepos */
3185 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
3187 granulepos = ogg_page_granulepos (&og);
3188 if (granulepos == -1) {
3189 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
3193 /* Avoid seeking to an incorrect granuletime by only considering
3194 the stream for which we found the earliest time */
3195 if (only_serial_no && ogg_page_serialno (&og) != serialno)
3198 /* get the stream */
3199 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
3200 if (pad == NULL || pad->map.is_skeleton)
3203 /* convert granulepos to time */
3204 granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
3206 if (granuletime < pad->start_time)
3209 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to PTS %"
3210 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
3212 granuletime -= pad->start_time;
3213 granuletime += chain->begin_time;
3215 GST_DEBUG_OBJECT (ogg,
3216 "found page with granule %" G_GINT64_FORMAT " and time %"
3217 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
3219 if (granuletime < target) {
3220 best = result; /* raw offset of packet with granulepos */
3221 begin = ogg->offset; /* raw offset of next page */
3222 begintime = granuletime;
3224 bisect = begin; /* *not* begin + 1 */
3226 if (bisect <= begin + 1) {
3227 end = begin; /* found it */
3229 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
3231 bisect -= ogg->chunk_size; /* an endless loop otherwise. */
3232 if (bisect <= begin)
3234 gst_ogg_demux_seek (ogg, bisect);
3237 endtime = granuletime;
3246 GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
3247 gst_ogg_demux_seek (ogg, best);
3255 GST_DEBUG_OBJECT (ogg, "got a seek error");
3261 do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
3262 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
3263 gint64 * p_offset, gint64 * p_timestamp)
3266 guint64 timestamp, offset;
3267 guint64 r_timestamp, r_offset;
3268 gboolean result = FALSE;
3270 target -= begintime;
3275 for (i = 0; i < chain->streams->len; i++) {
3276 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3279 if (gst_ogg_map_search_index (&pad->map, TRUE, ×tamp, &offset)) {
3280 GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
3283 if (r_offset == -1 || offset < r_offset) {
3285 r_timestamp = timestamp;
3292 *p_timestamp = r_timestamp;
3294 *p_offset = r_offset;
3300 * do seek to time @position, return FALSE or chain and TRUE
3303 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
3304 gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
3307 GstOggChain *chain = NULL;
3309 gint64 begintime, endtime;
3310 gint64 target, keytarget;
3317 gboolean found_keyframe = FALSE;
3318 GstClockTime ts, first_ts = GST_CLOCK_TIME_NONE;
3320 position = segment->position;
3322 /* first find the chain to search in */
3323 total = ogg->total_time;
3324 if (ogg->chains->len == 0)
3327 for (i = ogg->chains->len - 1; i >= 0; i--) {
3328 chain = g_array_index (ogg->chains, GstOggChain *, i);
3329 total -= chain->total_time;
3330 if (position >= total)
3334 /* first step, locate page containing the required data */
3335 begin = chain->offset;
3336 end = chain->end_offset;
3337 begintime = chain->begin_time;
3338 endtime = begintime + chain->total_time;
3339 target = position - total + begintime;
3341 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
3345 /* second step: find pages for all relevant streams. We use the
3346 * keyframe_granule to keep track of which ones we saw. If we have
3347 * seen a page for each stream we can calculate the positions of
3349 * Relevant streams are defined as those streams which are not
3350 * Skeleton (which only has header pages). Discontinuous streams
3351 * such as Kate and CMML are currently excluded, as they could
3352 * cause performance issues if there are few pages in the area.
3353 * TODO: We might want to include them on a flag, if we want to
3354 * not miss a subtitle (Kate has repeat packets for this purpose,
3355 * but a stream does not have to use them). */
3356 pending = chain->streams->len;
3357 for (i = 0; i < chain->streams->len; i++) {
3358 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3360 GST_WARNING_OBJECT (ogg, "No pad at index %d", i);
3364 if (pad->map.is_skeleton) {
3365 GST_DEBUG_OBJECT (ogg, "Not finding pages for Skeleton stream %08x",
3370 if (pad->map.is_sparse) {
3371 GST_DEBUG_OBJECT (ogg, "Not finding pages for sparse stream %08x (%s)",
3372 pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map));
3377 GST_DEBUG_OBJECT (ogg, "find keyframes for %d/%d streams", pending,
3378 chain->streams->len);
3380 /* figure out where the keyframes are */
3387 GstClockTime keyframe_time, granule_time;
3389 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
3390 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
3392 if (ret == GST_FLOW_LIMIT) {
3393 GST_LOG_OBJECT (ogg, "reached limit");
3395 } else if (ret != GST_FLOW_OK)
3398 /* get the stream */
3399 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
3403 if (pad->map.is_skeleton || pad->map.is_sparse)
3406 granulepos = ogg_page_granulepos (&og);
3407 if (granulepos == -1 || granulepos == 0) {
3408 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
3412 /* We have a valid granpos, and we bail out when the time since the
3413 first seen time to the time corresponding to this granpos is larger
3414 then a threshold, to guard against some streams having large holes
3415 (eg, a stream ending early, which would cause seeking after that
3416 to fill up a queue for streams still active). */
3417 ts = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granulepos);
3418 if (GST_CLOCK_TIME_IS_VALID (ts)) {
3419 if (first_ts == GST_CLOCK_TIME_NONE) {
3420 GST_WARNING_OBJECT (pad, "Locking on pts %" GST_TIME_FORMAT,
3421 GST_TIME_ARGS (ts));
3424 if (ts - first_ts > SEEK_GIVE_UP_THRESHOLD) {
3425 GST_WARNING_OBJECT (pad,
3426 "No data found for %" GST_TIME_FORMAT ", giving up",
3427 GST_TIME_ARGS (SEEK_GIVE_UP_THRESHOLD));
3428 found_keyframe = FALSE;
3434 /* in reverse we want to go past the page with the lower timestamp */
3435 if (segment->rate < 0.0) {
3436 /* get time for this pad */
3437 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
3440 /* Convert to stream time */
3441 granule_time -= pad->start_time;
3442 granule_time += chain->begin_time;
3444 GST_LOG_OBJECT (ogg,
3445 "looking at page with time %" GST_TIME_FORMAT ", target %"
3446 GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
3447 GST_TIME_ARGS (target));
3448 if (granule_time < target)
3452 /* we've seen this pad before */
3453 if (pad->keyframe_granule != -1)
3456 /* convert granule of this pad to the granule of the keyframe */
3457 pad->keyframe_granule =
3458 gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
3459 GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
3460 pad->keyframe_granule);
3462 /* get time of the keyframe */
3464 gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
3465 GST_LOG_OBJECT (ogg,
3466 "stream %08x keyframe granule PTS %" GST_TIME_FORMAT
3467 " target %" GST_TIME_FORMAT,
3468 pad->map.serialno, GST_TIME_ARGS (keyframe_time),
3469 GST_TIME_ARGS (keytarget));
3471 /* collect smallest value */
3472 if (keyframe_time != -1) {
3473 keyframe_time -= pad->start_time;
3474 keyframe_time += begintime;
3475 if (keyframe_time < keytarget) {
3476 serialno = pad->map.serialno;
3477 keytarget = keyframe_time;
3478 found_keyframe = TRUE;
3479 GST_LOG_OBJECT (ogg, "storing keytarget %" GST_TIME_FORMAT,
3480 GST_TIME_ARGS (keytarget));
3490 /* for negative rates we will get to the keyframe backwards */
3491 if (segment->rate < 0.0)
3494 /* No keyframe found, no need to bisect again, keytarget == target here */
3495 if (!found_keyframe)
3498 if (keytarget != target) {
3499 GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
3500 GST_TIME_ARGS (keytarget));
3502 /* last step, seek to the location of the keyframe */
3503 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
3504 keytarget, &best, TRUE, serialno))
3507 /* seek back to previous position */
3508 GST_LOG_OBJECT (ogg, "keyframe on target");
3509 gst_ogg_demux_seek (ogg, best);
3514 if (segment->rate > 0.0)
3515 segment->time = keytarget;
3516 segment->position = keytarget - begintime;
3525 GST_DEBUG_OBJECT (ogg, "no chains");
3530 GST_DEBUG_OBJECT (ogg, "got a seek error");
3535 /* does not take ownership of the event */
3537 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
3539 GstOggChain *chain = NULL;
3541 gboolean accurate, keyframe;
3545 GstSeekType start_type, stop_type;
3551 GST_DEBUG_OBJECT (ogg, "seek with event");
3553 gst_event_parse_seek (event, &rate, &format, &flags,
3554 &start_type, &start, &stop_type, &stop);
3556 /* we can only seek on time */
3557 if (format != GST_FORMAT_TIME) {
3558 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3561 seqnum = gst_event_get_seqnum (event);
3563 GST_DEBUG_OBJECT (ogg, "seek without event");
3567 seqnum = gst_util_seqnum_next ();
3570 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
3572 accurate = flags & GST_SEEK_FLAG_ACCURATE;
3573 keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
3575 gst_pad_pause_task (ogg->sinkpad);
3577 /* now grab the stream lock so that streaming cannot continue, for
3578 * non flushing seeks when the element is in PAUSED this could block
3580 GST_PAD_STREAM_LOCK (ogg->sinkpad);
3583 gst_segment_do_seek (&ogg->segment, rate, format, flags,
3584 start_type, start, stop_type, stop, &update);
3587 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
3588 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
3589 GST_TIME_ARGS (ogg->segment.stop));
3594 /* reset all ogg streams now, need to do this from within the lock to
3595 * make sure the streaming thread is not messing with the stream */
3596 for (i = 0; i < ogg->chains->len; i++) {
3597 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3599 gst_ogg_chain_reset (chain);
3603 /* for reverse we will already seek accurately */
3604 res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
3606 /* seek failed, make sure we continue the current chain */
3608 GST_DEBUG_OBJECT (ogg, "seek failed");
3609 chain = ogg->current_chain;
3611 GST_DEBUG_OBJECT (ogg, "seek success");
3617 /* now we have a new position, prepare for streaming again */
3622 gint64 position, begin_time;
3625 /* we need this to see how far inside the chain we need to start */
3626 if (chain->begin_time != GST_CLOCK_TIME_NONE)
3627 begin_time = chain->begin_time;
3631 /* segment.start gives the start over all chains, we calculate the amount
3632 * of time into this chain we need to start */
3633 start = ogg->segment.start - begin_time;
3634 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3635 start += chain->segment_start;
3637 if ((stop = ogg->segment.stop) == -1)
3638 stop = ogg->segment.duration;
3640 /* segment.stop gives the stop time over all chains, calculate the amount of
3641 * time we need to stop in this chain */
3643 if (stop > begin_time)
3647 stop += chain->segment_start;
3648 /* we must stop when this chain ends and switch to the next chain to play
3649 * the remainder of the segment. */
3650 stop = MIN (stop, chain->segment_stop);
3653 position = ogg->segment.position;
3654 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3655 position += chain->segment_start;
3657 gst_segment_copy_into (&ogg->segment, &segment);
3659 /* create the segment event we are going to send out */
3660 if (ogg->segment.rate >= 0.0) {
3661 segment.start = position;
3662 segment.stop = stop;
3664 segment.start = start;
3665 segment.stop = position;
3667 event = gst_event_new_segment (&segment);
3668 gst_event_set_seqnum (event, seqnum);
3670 if (chain != ogg->current_chain) {
3671 /* switch to different chain, send segment on new chain */
3672 gst_ogg_demux_activate_chain (ogg, chain, event);
3674 /* mark discont and send segment on current chain */
3675 gst_ogg_chain_mark_discont (chain);
3676 /* This event should be sent from the streaming thread (sink pad task) */
3677 if (ogg->newsegment)
3678 gst_event_unref (ogg->newsegment);
3679 ogg->newsegment = event;
3682 /* notify start of new segment */
3683 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3684 GstMessage *message;
3686 message = gst_message_new_segment_start (GST_OBJECT (ogg),
3687 GST_FORMAT_TIME, ogg->segment.position);
3688 gst_message_set_seqnum (message, seqnum);
3690 gst_element_post_message (GST_ELEMENT (ogg), message);
3693 ogg->seqnum = seqnum;
3694 /* restart our task since it might have been stopped when we did the
3696 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3697 ogg->sinkpad, NULL);
3700 /* streaming can continue now */
3701 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3705 gst_event_unref (event);
3711 GST_DEBUG_OBJECT (ogg, "seek failed");
3717 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
3718 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3725 gst_ogg_demux_get_duration_push (GstOggDemux * ogg, int flags)
3727 /* In push mode, we get to the end of the stream to get the duration */
3731 /* A full Ogg page can be almost 64 KB. There's no guarantee that there'll be a
3732 granpos there, but it's fairly likely */
3733 position = ogg->push_byte_length - DURATION_CHUNK_OFFSET;
3737 GST_DEBUG_OBJECT (ogg,
3738 "Getting duration, seeking near the end, to %" G_GINT64_FORMAT, position);
3739 ogg->push_state = PUSH_DURATION;
3740 /* do not read the last byte */
3741 sevent = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET,
3742 position, GST_SEEK_TYPE_SET, ogg->push_byte_length - 1);
3743 gst_event_replace (&ogg->seek_event, sevent);
3744 ogg->seek_event_drop_till = gst_event_get_seqnum (sevent);
3745 gst_event_unref (sevent);
3746 g_mutex_lock (&ogg->seek_event_mutex);
3747 g_cond_broadcast (&ogg->seek_event_cond);
3748 g_mutex_unlock (&ogg->seek_event_mutex);
3753 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3756 if (ogg->push_byte_length < 0) {
3759 GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3760 if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3764 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &length);
3765 if (res && length > 0) {
3766 ogg->push_byte_length = length;
3767 GST_DEBUG_OBJECT (ogg,
3768 "File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
3770 GST_DEBUG_OBJECT (ogg, "File byte length unknown, assuming live");
3771 ogg->push_disable_seeking = TRUE;
3772 gst_object_unref (peer);
3775 res = gst_pad_query_duration (peer, GST_FORMAT_TIME, &length);
3776 gst_object_unref (peer);
3777 if (res && length >= 0) {
3778 ogg->push_time_length = length;
3779 GST_DEBUG_OBJECT (ogg, "File time length %" GST_TIME_FORMAT,
3780 GST_TIME_ARGS (ogg->push_time_length));
3781 } else if (!ogg->push_disable_seeking) {
3784 res = gst_ogg_demux_get_duration_push (ogg, flags);
3786 GST_DEBUG_OBJECT (ogg,
3787 "File time length unknown, trying to determine");
3788 ogg->push_mode_seek_delayed_event = NULL;
3790 GST_DEBUG_OBJECT (ogg,
3791 "Let me intercept this innocent looking seek request");
3792 ogg->push_mode_seek_delayed_event = gst_event_copy (event);
3803 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3806 gboolean res = TRUE;
3810 GstSeekType start_type, stop_type;
3814 gint64 best, best_time;
3817 GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3819 gst_event_parse_seek (event, &rate, &format, &flags,
3820 &start_type, &start, &stop_type, &stop);
3822 if (format != GST_FORMAT_TIME) {
3823 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3827 if (start_type != GST_SEEK_TYPE_SET) {
3828 GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3832 /* If stop is unset, make sure it is -1, as this value will be tested
3833 later to check whether stop is set or not */
3834 if (stop_type == GST_SEEK_TYPE_NONE)
3837 GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3838 GST_TIME_ARGS (start));
3840 chain = ogg->current_chain;
3842 GST_WARNING_OBJECT (ogg, "No chain to seek on");
3846 /* start accessing push_* members */
3847 GST_PUSH_LOCK (ogg);
3849 /* not if we disabled seeking (chained streams) */
3850 if (ogg->push_disable_seeking) {
3851 GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3855 /* not when we're trying to work out duration */
3856 if (ogg->push_state == PUSH_DURATION) {
3857 GST_DEBUG_OBJECT (ogg, "Busy working out duration, try again later");
3861 /* actually, not if we're doing any seeking already */
3862 if (ogg->push_state != PUSH_PLAYING) {
3863 GST_DEBUG_OBJECT (ogg, "Already doing some seeking, try again later");
3867 /* on the first seek, get length if we can */
3868 if (!gst_ogg_demux_check_duration_push (ogg, flags, event)) {
3869 GST_PUSH_UNLOCK (ogg);
3873 if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
3874 /* the index gave some result */
3875 GST_DEBUG_OBJECT (ogg,
3876 "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
3879 if (ogg->push_time_length > 0) {
3880 /* if we know the time length, we know the full segment bitrate */
3881 GST_DEBUG_OBJECT (ogg, "Using real file bitrate");
3883 gst_util_uint64_scale (ogg->push_byte_length, 8 * GST_SECOND,
3884 ogg->push_time_length);
3885 } else if (ogg->push_time_offset > 0) {
3886 /* get a first approximation using known bitrate to the current position */
3887 GST_DEBUG_OBJECT (ogg, "Using file bitrate so far");
3889 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
3890 ogg->push_time_offset);
3891 } else if (ogg->bitrate > 0) {
3892 /* nominal bitrate is better than nothing, even if it lies often */
3893 GST_DEBUG_OBJECT (ogg, "Using nominal bitrate");
3894 bitrate = ogg->bitrate;
3897 GST_DEBUG_OBJECT (ogg,
3898 "At stream start, and no nominal bitrate, using some random magic "
3900 /* the bisection, once started, should give us a better approximation */
3903 best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3906 /* offset by typical page length, and ensure our best guess is within
3907 reasonable bounds */
3908 best -= ogg->chunk_size;
3911 if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3912 best = ogg->push_byte_length - 1;
3914 /* set up bisection search */
3915 ogg->push_offset0 = 0;
3916 ogg->push_offset1 = ogg->push_byte_length - 1;
3917 ogg->push_time0 = ogg->push_start_time;
3918 ogg->push_time1 = ogg->push_time_length;
3919 ogg->seqnum = gst_event_get_seqnum (event);
3920 ogg->push_seek_time_target = start;
3921 ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
3922 ogg->push_seek_time_original_target = start;
3923 ogg->push_seek_time_original_stop = stop;
3924 ogg->push_state = PUSH_BISECT1;
3925 ogg->seek_secant = FALSE;
3926 ogg->seek_undershot = FALSE;
3928 if (flags & GST_SEEK_FLAG_FLUSH) {
3929 /* reset pad push mode seeking state */
3930 for (i = 0; i < chain->streams->len; i++) {
3931 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3932 pad->push_kf_time = GST_CLOCK_TIME_NONE;
3933 pad->push_sync_time = GST_CLOCK_TIME_NONE;
3937 GST_DEBUG_OBJECT (ogg,
3938 "Setting up bisection search for %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
3939 " (time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", ogg->push_offset0,
3940 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
3941 GST_TIME_ARGS (ogg->push_time1));
3942 GST_DEBUG_OBJECT (ogg,
3943 "Target time is %" GST_TIME_FORMAT ", best first guess is %"
3944 G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target), best);
3946 ogg->push_seek_rate = rate;
3947 ogg->push_seek_flags = flags;
3948 ogg->push_mode_seek_delayed_event = NULL;
3949 ogg->push_bisection_steps[0] = 1;
3950 ogg->push_bisection_steps[1] = 0;
3951 sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
3952 start_type, best, GST_SEEK_TYPE_NONE, -1);
3953 gst_event_set_seqnum (sevent, gst_event_get_seqnum (event));
3955 gst_event_replace (&ogg->seek_event, sevent);
3956 gst_event_unref (sevent);
3957 GST_PUSH_UNLOCK (ogg);
3958 g_mutex_lock (&ogg->seek_event_mutex);
3959 g_cond_broadcast (&ogg->seek_event_cond);
3960 g_mutex_unlock (&ogg->seek_event_mutex);
3967 GST_DEBUG_OBJECT (ogg, "seek failed");
3972 GST_PUSH_UNLOCK (ogg);
3977 gst_ogg_demux_setup_seek_pull (GstOggDemux * ogg, GstEvent * event)
3982 guint32 seqnum = gst_event_get_seqnum (event);
3984 GST_DEBUG_OBJECT (ogg, "Scheduling seek: %" GST_PTR_FORMAT, event);
3985 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
3987 flush = flags & GST_SEEK_FLAG_FLUSH;
3989 /* first step is to unlock the streaming thread if it is
3990 * blocked in a chain call, we do this by starting the flush. because
3991 * we cannot yet hold any streaming lock, we have to protect the chains
3992 * with their own lock. */
3996 tevent = gst_event_new_flush_start ();
3997 gst_event_set_seqnum (tevent, seqnum);
3999 gst_event_ref (tevent);
4000 gst_pad_push_event (ogg->sinkpad, tevent);
4002 GST_CHAIN_LOCK (ogg);
4003 for (i = 0; i < ogg->chains->len; i++) {
4004 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4007 for (j = 0; j < chain->streams->len; j++) {
4008 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
4010 gst_event_ref (tevent);
4011 gst_pad_push_event (GST_PAD (pad), tevent);
4014 GST_CHAIN_UNLOCK (ogg);
4016 gst_event_unref (tevent);
4019 gst_pad_pause_task (ogg->sinkpad);
4021 /* now grab the stream lock so that streaming cannot continue, for
4022 * non flushing seeks when the element is in PAUSED this could block
4024 GST_PAD_STREAM_LOCK (ogg->sinkpad);
4026 /* we need to stop flushing on the sinkpad as we're going to use it
4027 * next. We can do this as we have the STREAM lock now. */
4029 tevent = gst_event_new_flush_stop (TRUE);
4030 gst_event_set_seqnum (tevent, seqnum);
4031 gst_pad_push_event (ogg->sinkpad, gst_event_ref (tevent));
4032 gst_ogg_demux_send_event (ogg, tevent);
4035 gst_event_replace (&ogg->seek_event, event);
4036 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
4037 ogg->sinkpad, NULL);
4038 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
4044 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
4048 if (ogg->pullmode) {
4049 res = gst_ogg_demux_setup_seek_pull (ogg, event);
4051 res = gst_ogg_demux_perform_seek_push (ogg, event);
4057 /* finds each bitstream link one at a time using a bisection search
4058 * (has to begin by knowing the offset of the lb's initial page).
4059 * Recurses for each link so it can alloc the link storage after
4060 * finding them all, then unroll and fill the cache at the same time
4062 static GstFlowReturn
4063 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
4064 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
4066 gint64 endsearched = end;
4071 GstOggChain *nextchain;
4073 GST_LOG_OBJECT (ogg,
4074 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
4075 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
4077 /* the below guards against garbage separating the last and
4078 * first pages of two links. */
4079 while (searched < endsearched) {
4082 if (endsearched - searched < ogg->chunk_size) {
4085 bisect = (searched + endsearched) / 2;
4088 gst_ogg_demux_seek (ogg, bisect);
4089 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
4091 if (ret == GST_FLOW_EOS) {
4092 endsearched = bisect;
4093 } else if (ret == GST_FLOW_OK) {
4094 guint32 serial = ogg_page_serialno (&og);
4096 if (!gst_ogg_chain_has_stream (chain, serial)) {
4097 endsearched = bisect;
4100 searched = offset + og.header_len + og.body_len;
4106 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
4108 chain->end_offset = searched;
4109 ret = gst_ogg_demux_read_end_chain (ogg, chain);
4110 if (ret != GST_FLOW_OK)
4113 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
4115 gst_ogg_demux_seek (ogg, next);
4116 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
4117 if (ret == GST_FLOW_EOS) {
4120 GST_LOG_OBJECT (ogg, "no next chain");
4121 } else if (ret != GST_FLOW_OK)
4124 if (searched < end && nextchain != NULL) {
4125 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
4126 end, nextchain, m + 1);
4127 if (ret != GST_FLOW_OK)
4130 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
4132 g_array_insert_val (ogg->chains, 0, chain);
4138 /* read a chain from the ogg file. This code will
4139 * read all BOS pages and will create and return a GstOggChain
4140 * structure with the results.
4142 * This function will also read N pages from each stream in the
4143 * chain and submit them to the internal ogg stream parser/mapper
4144 * until we know the timestamp of the first page in the chain.
4146 static GstFlowReturn
4147 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
4150 GstOggChain *chain = NULL;
4151 gint64 offset = ogg->offset;
4156 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
4158 /* first read the BOS pages, detect the stream types, create the internal
4159 * stream mappers, send data to them. */
4164 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
4165 if (ret != GST_FLOW_OK) {
4166 if (ret == GST_FLOW_EOS) {
4167 GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
4169 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
4173 if (!ogg_page_bos (&og)) {
4174 GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
4175 /* if we did not find a chain yet, assume this is a bogus stream and
4178 GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
4184 if (chain == NULL) {
4185 chain = gst_ogg_chain_new (ogg);
4186 chain->offset = offset;
4189 serial = ogg_page_serialno (&og);
4190 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
4191 GST_WARNING_OBJECT (ogg,
4192 "found serial %08x BOS page twice, ignoring", serial);
4196 pad = gst_ogg_chain_new_stream (chain, serial);
4197 gst_ogg_pad_submit_page (pad, &og);
4200 if (ret != GST_FLOW_OK || chain == NULL) {
4201 if (ret == GST_FLOW_OK) {
4202 GST_WARNING_OBJECT (ogg, "no chain was found");
4203 ret = GST_FLOW_ERROR;
4204 } else if (ret != GST_FLOW_EOS) {
4205 GST_WARNING_OBJECT (ogg, "failed to read chain");
4207 GST_DEBUG_OBJECT (ogg, "done reading chains");
4210 gst_ogg_chain_free (chain);
4217 chain->have_bos = TRUE;
4218 GST_INFO_OBJECT (ogg, "read bos pages, ");
4220 /* now read pages until each ogg stream mapper has figured out the
4221 * timestamp of the first packet in the chain */
4223 /* save the offset to the first non bos page in the chain: if searching for
4224 * pad->first_time we read past the end of the chain, we'll seek back to this
4227 offset = ogg->offset;
4232 gboolean known_serial = FALSE;
4235 serial = ogg_page_serialno (&og);
4237 for (i = 0; i < chain->streams->len; i++) {
4238 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4240 GST_LOG_OBJECT (ogg,
4241 "serial %08x time %" GST_TIME_FORMAT,
4242 pad->map.serialno, GST_TIME_ARGS (pad->start_time));
4244 if (pad->map.serialno == serial) {
4245 known_serial = TRUE;
4247 /* submit the page now, this will fill in the start_time when the
4248 * internal stream mapper finds it */
4249 gst_ogg_pad_submit_page (pad, &og);
4251 if (!pad->map.is_skeleton && pad->start_time == -1
4252 && ogg_page_eos (&og)) {
4253 /* got EOS on a pad before we could find its start_time.
4254 * We have no chance of finding a start_time for every pad so
4255 * stop searching for the other start_time(s).
4261 /* the timestamp will be filled in when we submit the pages */
4262 if (!pad->map.is_sparse)
4263 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
4265 GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
4268 /* we read a page not belonging to the current chain: seek back to the
4269 * beginning of the chain
4271 if (!known_serial) {
4272 GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
4273 gst_ogg_demux_seek (ogg, offset);
4278 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
4279 if (ret != GST_FLOW_OK)
4283 GST_LOG_OBJECT (ogg, "done reading chain");
4291 /* read the last pages from the ogg stream to get the final
4294 static GstFlowReturn
4295 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
4297 gint64 begin = chain->end_offset;
4299 gint64 last_granule = -1;
4300 GstOggPad *last_pad = NULL;
4302 gboolean done = FALSE;
4307 begin -= ogg->chunk_size;
4311 gst_ogg_demux_seek (ogg, begin);
4313 /* now continue reading until we run out of data, if we find a page
4314 * start, we save it. It might not be the final page as there could be
4315 * another page after this one. */
4316 while (ogg->offset < end) {
4317 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
4319 if (ret == GST_FLOW_LIMIT)
4321 if (ret != GST_FLOW_OK)
4324 for (i = 0; i < chain->streams->len; i++) {
4325 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4327 if (pad->map.is_skeleton)
4330 if (pad->map.serialno == ogg_page_serialno (&og)) {
4331 gint64 granulepos = ogg_page_granulepos (&og);
4333 if (granulepos != -1) {
4334 last_granule = granulepos;
4345 chain->segment_stop =
4346 gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
4349 chain->segment_stop = GST_CLOCK_TIME_NONE;
4352 GST_INFO ("segment stop %" G_GUINT64_FORMAT ", for last granule %"
4353 G_GUINT64_FORMAT, chain->segment_stop, last_granule);
4358 /* find a pad with a given serial number
4361 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
4366 /* first look in building chain if any */
4367 if (ogg->building_chain) {
4368 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
4373 /* then look in current chain if any */
4374 if (ogg->current_chain) {
4375 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
4380 for (i = 0; i < ogg->chains->len; i++) {
4381 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4383 pad = gst_ogg_chain_get_stream (chain, serialno);
4390 /* find a chain with a given serial number
4392 static GstOggChain *
4393 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
4397 pad = gst_ogg_demux_find_pad (ogg, serialno);
4404 /* returns TRUE if all streams have valid start time */
4406 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
4408 gboolean res = TRUE;
4410 chain->total_time = GST_CLOCK_TIME_NONE;
4411 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
4413 /* see if we have a start time on all streams */
4414 chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
4416 if (chain->segment_start == G_MAXUINT64) {
4417 /* not yet, stream some more data */
4419 } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
4420 /* we can calculate a total time */
4421 chain->total_time = chain->segment_stop - chain->segment_start;
4424 GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
4426 GST_DEBUG_OBJECT (ogg, "return %d", res);
4432 gst_ogg_demux_collect_info (GstOggDemux * ogg)
4436 /* collect all info */
4437 ogg->total_time = 0;
4439 for (i = 0; i < ogg->chains->len; i++) {
4440 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4442 chain->begin_time = ogg->total_time;
4444 gst_ogg_demux_collect_chain_info (ogg, chain);
4446 ogg->total_time += chain->total_time;
4448 ogg->segment.duration = ogg->total_time;
4451 /* find all the chains in the ogg file, this reads the first and
4452 * last page of the ogg stream, if they match then the ogg file has
4453 * just one chain, else we do a binary search for all chains.
4455 static GstFlowReturn
4456 gst_ogg_demux_find_chains (GstOggDemux * ogg)
4465 /* get peer to figure out length */
4466 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
4469 /* find length to read last page, we store this for later use. */
4470 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &ogg->length);
4471 gst_object_unref (peer);
4472 if (!res || ogg->length <= 0)
4475 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
4477 /* read chain from offset 0, this is the first chain of the
4479 gst_ogg_demux_seek (ogg, 0);
4480 ret = gst_ogg_demux_read_chain (ogg, &chain);
4481 if (ret != GST_FLOW_OK) {
4482 if (ret == GST_FLOW_FLUSHING)
4485 goto no_first_chain;
4488 /* read page from end offset, we use this page to check if its serial
4489 * number is contained in the first chain. If this is the case then
4490 * this ogg is not a chained ogg and we can skip the scanning. */
4491 gst_ogg_demux_seek (ogg, ogg->length);
4492 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
4493 if (ret != GST_FLOW_OK)
4496 serialno = ogg_page_serialno (&og);
4498 if (!gst_ogg_chain_has_stream (chain, serialno)) {
4499 /* the last page is not in the first stream, this means we should
4500 * find all the chains in this chained ogg. */
4502 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
4505 /* we still call this function here but with an empty range so that
4506 * we can reuse the setup code in this routine. */
4508 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
4509 ogg->length, chain, 0);
4511 if (ret != GST_FLOW_OK)
4514 /* all fine, collect and print */
4515 gst_ogg_demux_collect_info (ogg);
4517 /* dump our chains and streams */
4518 gst_ogg_print (ogg);
4523 /*** error cases ***/
4526 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
4527 return GST_FLOW_NOT_LINKED;
4531 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
4532 return GST_FLOW_NOT_SUPPORTED;
4536 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
4537 return GST_FLOW_ERROR;
4541 GST_DEBUG_OBJECT (ogg, "can't get last page");
4543 gst_ogg_chain_free (chain);
4548 GST_DEBUG_OBJECT (ogg, "Flushing, can't read chain");
4549 return GST_FLOW_FLUSHING;
4554 gst_ogg_demux_update_chunk_size (GstOggDemux * ogg, ogg_page * page)
4556 long size = page->header_len + page->body_len;
4557 long chunk_size = size * 2;
4558 if (chunk_size > ogg->chunk_size) {
4559 GST_LOG_OBJECT (ogg, "Updating chunk size to %ld", chunk_size);
4560 ogg->chunk_size = chunk_size;
4564 static GstFlowReturn
4565 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page, gboolean discont)
4570 GstFlowReturn result = GST_FLOW_OK;
4572 serialno = ogg_page_serialno (page);
4573 granule = ogg_page_granulepos (page);
4575 gst_ogg_demux_update_chunk_size (ogg, page);
4577 GST_LOG_OBJECT (ogg,
4578 "processing ogg page (serial %08x, "
4579 "pageno %ld, granulepos %" G_GINT64_FORMAT ", bos %d)", serialno,
4580 ogg_page_pageno (page), granule, ogg_page_bos (page));
4582 if (ogg_page_bos (page)) {
4586 /* see if we know about the chain already */
4587 chain = gst_ogg_demux_find_chain (ogg, serialno);
4593 if (chain->segment_start != GST_CLOCK_TIME_NONE)
4594 start = chain->segment_start;
4596 /* create the newsegment event we are going to send out */
4597 gst_segment_copy_into (&ogg->segment, &segment);
4598 segment.start = start;
4599 segment.stop = chain->segment_stop;
4600 segment.time = chain->begin_time;
4601 segment.base += chain->begin_time;
4602 event = gst_event_new_segment (&segment);
4603 gst_event_set_seqnum (event, ogg->seqnum);
4605 GST_DEBUG_OBJECT (ogg,
4606 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
4607 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
4608 GST_TIME_ARGS (chain->segment_stop),
4609 GST_TIME_ARGS (chain->begin_time));
4611 /* activate it as it means we have a non-header, this will also deactivate
4612 * the currently running chain. */
4613 gst_ogg_demux_activate_chain (ogg, chain, event);
4614 pad = gst_ogg_demux_find_pad (ogg, serialno);
4616 GstClockTime chain_time;
4617 gint64 current_time;
4619 /* this can only happen in push mode */
4623 current_time = ogg->segment.position;
4625 /* time of new chain is current time */
4626 chain_time = current_time;
4628 if (ogg->building_chain == NULL) {
4629 GstOggChain *newchain;
4631 newchain = gst_ogg_chain_new (ogg);
4632 newchain->offset = 0;
4633 /* set new chain begin time aligned with end time of old chain */
4634 newchain->begin_time = chain_time;
4635 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
4636 GST_TIME_ARGS (chain_time));
4638 /* and this is the one we are building now */
4639 ogg->building_chain = newchain;
4641 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
4644 pad = gst_ogg_demux_find_pad (ogg, serialno);
4647 /* Reset granule interpolation if chaining in reverse (discont = TRUE) */
4649 pad->current_granule = -1;
4651 result = gst_ogg_pad_submit_page (pad, page);
4653 GST_PUSH_LOCK (ogg);
4654 if (!ogg->pullmode && !ogg->push_disable_seeking) {
4655 /* no pad while probing for duration, we must have a chained stream,
4656 and we don't support them, so back off */
4657 GST_INFO_OBJECT (ogg, "We seem to have a chained stream, we won't seek");
4658 if (ogg->push_state == PUSH_DURATION) {
4661 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
4662 /* Call to function above unlocks, relock */
4663 GST_PUSH_LOCK (ogg);
4664 if (res != GST_FLOW_OK)
4668 /* only once we seeked back */
4669 ogg->push_disable_seeking = TRUE;
4671 GST_PUSH_UNLOCK (ogg);
4672 /* no pad. This means an ogg page without bos has been seen for this
4673 * serialno. we just ignore it but post a warning... */
4674 GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
4675 (NULL), ("unknown ogg pad for serial %08x detected", serialno));
4678 GST_PUSH_UNLOCK (ogg);
4685 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
4686 (NULL), ("unknown ogg chain for serial %08x detected", serialno));
4687 return GST_FLOW_ERROR;
4691 /* streaming mode, receive a buffer, parse it, create pads for
4692 * the serialno, submit pages and packets to the oggpads
4694 static GstFlowReturn
4695 gst_ogg_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
4699 GstFlowReturn result = GST_FLOW_OK;
4702 ogg = GST_OGG_DEMUX (parent);
4704 GST_PUSH_LOCK (ogg);
4705 drop = (ogg->seek_event_drop_till > 0);
4706 GST_PUSH_UNLOCK (ogg);
4708 GST_DEBUG_OBJECT (ogg, "Dropping buffer because we have a pending seek");
4709 gst_buffer_unref (buffer);
4713 GST_DEBUG_OBJECT (ogg, "enter");
4714 result = gst_ogg_demux_submit_buffer (ogg, buffer);
4716 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
4719 while (result == GST_FLOW_OK) {
4722 ret = ogg_sync_pageout (&ogg->sync, &page);
4724 /* need more data */
4727 /* discontinuity in the pages */
4728 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
4730 result = gst_ogg_demux_handle_page (ogg, &page, FALSE);
4732 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
4736 if (ret == 0 || result == GST_FLOW_OK) {
4737 gst_ogg_demux_sync_streams (ogg);
4739 GST_DEBUG_OBJECT (ogg, "leave with %d", result);
4744 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
4746 GstOggChain *chain = ogg->current_chain;
4747 gboolean event_sent = FALSE;
4748 gboolean res = TRUE;
4751 chain = ogg->building_chain;
4756 for (i = 0; i < chain->streams->len; i++) {
4757 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4759 gst_event_ref (event);
4760 GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
4761 res &= gst_pad_push_event (GST_PAD (pad), event);
4767 gst_event_unref (event);
4769 if (!event_sent && GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
4770 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4771 ("EOS before finding a chain"));
4777 static GstFlowReturn
4778 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
4781 /* store the value */
4782 pad->last_ret = ret;
4783 pad->is_eos = (ret == GST_FLOW_EOS);
4785 return gst_flow_combiner_update_pad_flow (ogg->flowcombiner,
4786 GST_PAD_CAST (pad), ret);
4789 static GstFlowReturn
4790 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4793 GstBuffer *buffer = NULL;
4795 if (ogg->offset == ogg->length) {
4796 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4797 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
4802 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4804 gst_pad_pull_range (ogg->sinkpad, ogg->offset, ogg->chunk_size, &buffer);
4805 if (ret != GST_FLOW_OK) {
4806 GST_LOG_OBJECT (ogg, "Failed pull_range");
4810 ogg->offset += gst_buffer_get_size (buffer);
4812 if (G_UNLIKELY (ogg->newsegment)) {
4813 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4814 ogg->newsegment = NULL;
4817 ret = gst_ogg_demux_chain (ogg->sinkpad, GST_OBJECT_CAST (ogg), buffer);
4818 if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS) {
4819 GST_LOG_OBJECT (ogg, "Failed demux_chain");
4828 * We read the pages backwards and send the packets forwards. The first packet
4829 * in the page will be pushed with the DISCONT flag set.
4831 * Special care has to be taken for continued pages, which we can only decode
4832 * when we have the previous page(s).
4834 static GstFlowReturn
4835 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4841 if (ogg->offset == 0) {
4842 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4843 " == 0", ogg->offset);
4848 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
4849 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
4850 if (ret != GST_FLOW_OK)
4853 ogg->offset = offset;
4855 if (G_UNLIKELY (ogg->newsegment)) {
4856 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4857 ogg->newsegment = NULL;
4860 GST_LOG_OBJECT (ogg, "Handling page at offset %" G_GINT64_FORMAT,
4862 ret = gst_ogg_demux_handle_page (ogg, &page, TRUE);
4869 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4875 chain = ogg->current_chain;
4876 cur = ogg->segment.position;
4877 if (chain == NULL || cur == -1)
4880 for (i = 0; i < chain->streams->len; i++) {
4881 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4883 /* Theoretically, we should be doing this for all streams, so we're doing
4884 * it, but it might break things break things for wrongly-muxed streams
4885 * (like we used to produce once) */
4886 if ( /*stream->map.is_sparse && */ stream->position != GST_CLOCK_TIME_NONE) {
4888 /* Does this stream lag? Random threshold of 2 seconds */
4889 if (GST_CLOCK_DIFF (stream->position, cur) > (2 * GST_SECOND)) {
4890 GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
4891 "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
4892 GST_TIME_ARGS (stream->position), GST_TIME_ARGS (cur));
4894 stream->position = cur;
4896 gst_pad_push_event (GST_PAD_CAST (stream),
4897 gst_event_new_gap (stream->position, cur - stream->position));
4903 /* random access code
4905 * - first find all the chains and streams by scanning the file.
4906 * - then get and chain buffers, just like the streaming case.
4907 * - when seeking, we can use the chain info to perform the seek.
4910 gst_ogg_demux_loop (GstOggPad * pad)
4917 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4918 seek = ogg->seek_event;
4919 ogg->seek_event = NULL;
4921 if (ogg->need_chains) {
4923 /* this is the only place where we write chains and thus need to lock. */
4924 GST_CHAIN_LOCK (ogg);
4925 ret = gst_ogg_demux_find_chains (ogg);
4926 GST_CHAIN_UNLOCK (ogg);
4927 if (ret != GST_FLOW_OK)
4928 goto chain_read_failed;
4930 ogg->need_chains = FALSE;
4932 GST_OBJECT_LOCK (ogg);
4933 ogg->running = TRUE;
4934 GST_OBJECT_UNLOCK (ogg);
4936 /* and seek to configured positions without FLUSH */
4937 res = gst_ogg_demux_perform_seek_pull (ogg, seek);
4942 res = gst_ogg_demux_perform_seek_pull (ogg, seek);
4947 if (ogg->segment.rate >= 0.0)
4948 ret = gst_ogg_demux_loop_forward (ogg);
4950 ret = gst_ogg_demux_loop_reverse (ogg);
4952 if (ret != GST_FLOW_OK)
4955 gst_ogg_demux_sync_streams (ogg);
4961 /* error was posted */
4968 GST_OBJECT_LOCK (pad);
4969 flushing = GST_PAD_IS_FLUSHING (pad);
4970 GST_OBJECT_UNLOCK (pad);
4972 ret = GST_FLOW_FLUSHING;
4974 GST_ELEMENT_FLOW_ERROR (ogg, ret);
4975 ret = GST_FLOW_ERROR;
4981 const gchar *reason = gst_flow_get_name (ret);
4982 GstEvent *event = NULL;
4984 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4985 gst_pad_pause_task (ogg->sinkpad);
4987 if (ret == GST_FLOW_EOS) {
4988 /* perform EOS logic */
4989 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4991 GstMessage *message;
4993 /* for segment playback we need to post when (in stream time)
4994 * we stopped, this is either stop (when set) or the duration. */
4995 if ((stop = ogg->segment.stop) == -1)
4996 stop = ogg->segment.duration;
4998 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
5000 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
5002 gst_message_set_seqnum (message, ogg->seqnum);
5004 gst_element_post_message (GST_ELEMENT (ogg), message);
5006 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
5007 gst_event_set_seqnum (event, ogg->seqnum);
5008 gst_ogg_demux_send_event (ogg, event);
5011 /* normal playback, send EOS to all linked pads */
5012 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
5013 event = gst_event_new_eos ();
5015 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
5016 GST_ELEMENT_FLOW_ERROR (ogg, ret);
5017 event = gst_event_new_eos ();
5020 /* For wrong-state we still want to pause the task and stop
5021 * but no error message or other things are necessary.
5022 * wrong-state is no real error and will be caused by flushing,
5023 * e.g. because of a flushing seek.
5026 /* guard against corrupt/truncated files, where one can hit EOS
5027 before prerolling is done and a chain created. If we have no
5028 chain to send the event to, error out. */
5029 if (ogg->current_chain || ogg->building_chain) {
5030 gst_event_set_seqnum (event, ogg->seqnum);
5031 gst_ogg_demux_send_event (ogg, event);
5033 gst_event_unref (event);
5034 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
5035 ("EOS before finding a chain"));
5042 /* The sink pad task function for push mode.
5043 * It just sends any seek events queued by the streaming thread.
5046 gst_ogg_demux_loop_push (GstOggDemux * ogg)
5048 GstEvent *event = NULL;
5050 g_mutex_lock (&ogg->seek_event_mutex);
5051 /* Inform other threads that we started */
5052 ogg->seek_thread_started = TRUE;
5053 g_cond_broadcast (&ogg->thread_started_cond);
5056 while (!ogg->seek_event_thread_stop) {
5058 while (!ogg->seek_event_thread_stop) {
5059 GST_PUSH_LOCK (ogg);
5060 event = ogg->seek_event;
5061 ogg->seek_event = NULL;
5063 ogg->seek_event_drop_till = gst_event_get_seqnum (event);
5064 GST_PUSH_UNLOCK (ogg);
5069 g_cond_wait (&ogg->seek_event_cond, &ogg->seek_event_mutex);
5072 if (ogg->seek_event_thread_stop) {
5077 g_mutex_unlock (&ogg->seek_event_mutex);
5079 GST_DEBUG_OBJECT (ogg->sinkpad, "Pushing event %" GST_PTR_FORMAT, event);
5080 if (!gst_pad_push_event (ogg->sinkpad, event)) {
5081 GST_WARNING_OBJECT (ogg, "Failed to push event");
5082 GST_PUSH_LOCK (ogg);
5083 if (!ogg->pullmode) {
5084 ogg->push_state = PUSH_PLAYING;
5085 ogg->push_disable_seeking = TRUE;
5087 GST_PUSH_UNLOCK (ogg);
5089 GST_DEBUG_OBJECT (ogg->sinkpad, "Pushed event ok");
5092 g_mutex_lock (&ogg->seek_event_mutex);
5095 g_mutex_unlock (&ogg->seek_event_mutex);
5097 gst_object_unref (ogg);
5102 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
5106 gst_ogg_demux_deactivate_current_chain (ogg);
5108 GST_CHAIN_LOCK (ogg);
5109 for (i = 0; i < ogg->chains->len; i++) {
5110 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
5112 if (chain == ogg->current_chain)
5113 ogg->current_chain = NULL;
5114 if (chain == ogg->building_chain)
5115 ogg->building_chain = NULL;
5116 gst_ogg_chain_free (chain);
5118 ogg->chains = g_array_set_size (ogg->chains, 0);
5119 if (ogg->current_chain != NULL) {
5120 GST_FIXME_OBJECT (ogg, "current chain was tracked in existing chains !");
5121 gst_ogg_chain_free (ogg->current_chain);
5122 ogg->current_chain = NULL;
5124 if (ogg->building_chain != NULL) {
5125 GST_FIXME_OBJECT (ogg, "building chain was tracked in existing chains !");
5126 gst_ogg_chain_free (ogg->building_chain);
5127 ogg->building_chain = NULL;
5129 GST_CHAIN_UNLOCK (ogg);
5132 /* this function is called when the pad is activated and should start
5135 * We check if we can do random access to decide if we work push or
5139 gst_ogg_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
5144 query = gst_query_new_scheduling ();
5146 if (!gst_pad_peer_query (sinkpad, query)) {
5147 gst_query_unref (query);
5151 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
5152 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
5153 gst_query_unref (query);
5158 GST_DEBUG_OBJECT (sinkpad, "activating pull");
5159 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
5163 GST_DEBUG_OBJECT (sinkpad, "activating push");
5164 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
5169 gst_ogg_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
5170 GstPadMode mode, gboolean active)
5175 ogg = GST_OGG_DEMUX (parent);
5178 case GST_PAD_MODE_PUSH:
5179 ogg->pullmode = FALSE;
5180 ogg->resync = FALSE;
5182 ogg->seek_event_thread_stop = FALSE;
5183 ogg->seek_thread_started = FALSE;
5184 ogg->seek_event_thread = g_thread_new ("seek_event_thread",
5185 (GThreadFunc) gst_ogg_demux_loop_push, gst_object_ref (ogg));
5186 /* And wait for the thread to start.
5187 * FIXME : This is hackish. And one wonders why we need a separate thread to
5188 * seek to a certain offset */
5189 g_mutex_lock (&ogg->seek_event_mutex);
5190 while (!ogg->seek_thread_started) {
5191 g_cond_wait (&ogg->thread_started_cond, &ogg->seek_event_mutex);
5193 g_mutex_unlock (&ogg->seek_event_mutex);
5195 g_mutex_lock (&ogg->seek_event_mutex);
5196 ogg->seek_event_thread_stop = TRUE;
5197 g_cond_broadcast (&ogg->seek_event_cond);
5198 g_mutex_unlock (&ogg->seek_event_mutex);
5199 g_thread_join (ogg->seek_event_thread);
5200 ogg->seek_event_thread = NULL;
5204 case GST_PAD_MODE_PULL:
5206 ogg->need_chains = TRUE;
5207 ogg->pullmode = TRUE;
5209 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
5212 res = gst_pad_stop_task (sinkpad);
5222 static GstStateChangeReturn
5223 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
5226 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
5228 ogg = GST_OGG_DEMUX (element);
5230 switch (transition) {
5231 case GST_STATE_CHANGE_NULL_TO_READY:
5233 ogg_sync_init (&ogg->sync);
5235 case GST_STATE_CHANGE_READY_TO_PAUSED:
5236 ogg_sync_reset (&ogg->sync);
5237 ogg->running = FALSE;
5239 ogg->total_time = -1;
5240 GST_PUSH_LOCK (ogg);
5241 ogg->push_byte_offset = 0;
5242 ogg->push_byte_length = -1;
5243 ogg->push_time_length = GST_CLOCK_TIME_NONE;
5244 ogg->push_time_offset = GST_CLOCK_TIME_NONE;
5245 ogg->push_state = PUSH_PLAYING;
5246 ogg->have_group_id = FALSE;
5247 ogg->group_id = G_MAXUINT;
5248 ogg->seqnum = GST_SEQNUM_INVALID;
5250 ogg->push_disable_seeking = FALSE;
5251 gst_ogg_demux_query_duration_push (ogg);
5252 GST_PUSH_UNLOCK (ogg);
5253 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
5259 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5261 switch (transition) {
5262 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
5264 case GST_STATE_CHANGE_PAUSED_TO_READY:
5265 gst_ogg_demux_clear_chains (ogg);
5266 GST_OBJECT_LOCK (ogg);
5267 ogg->running = FALSE;
5268 gst_event_replace (&ogg->seek_event, NULL);
5269 GST_OBJECT_UNLOCK (ogg);
5271 case GST_STATE_CHANGE_READY_TO_NULL:
5272 ogg_sync_clear (&ogg->sync);
5281 gst_ogg_demux_plugin_init (GstPlugin * plugin)
5283 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
5284 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
5285 "ogg demuxer setup stage when parsing pipeline");
5288 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
5290 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
5291 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
5297 /* prints all info about the element */
5298 #undef GST_CAT_DEFAULT
5299 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
5301 #ifdef GST_DISABLE_GST_DEBUG
5304 gst_ogg_print (GstOggDemux * ogg)
5309 #else /* !GST_DISABLE_GST_DEBUG */
5312 gst_ogg_print (GstOggDemux * ogg)
5316 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
5317 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
5318 GST_TIME_ARGS (ogg->total_time));
5320 for (i = 0; i < ogg->chains->len; i++) {
5321 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
5323 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
5324 GST_INFO_OBJECT (ogg,
5325 " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
5327 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
5328 GST_TIME_ARGS (chain->begin_time));
5329 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
5330 GST_TIME_ARGS (chain->total_time));
5331 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
5332 GST_TIME_ARGS (chain->segment_start));
5333 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
5334 GST_TIME_ARGS (chain->segment_stop));
5336 for (j = 0; j < chain->streams->len; j++) {
5337 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
5339 GST_INFO_OBJECT (ogg, " stream %08x: %s", stream->map.serialno,
5340 gst_ogg_stream_get_media_type (&stream->map));
5341 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
5342 GST_TIME_ARGS (stream->start_time));
5346 #endif /* GST_DISABLE_GST_DEBUG */