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 <gst/gst-i18n-plugin.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);
1615 gst_event_replace (&ogg->seek_event, event);
1616 gst_event_unref (event);
1617 GST_PUSH_UNLOCK (ogg);
1618 g_mutex_lock (&ogg->seek_event_mutex);
1619 g_cond_broadcast (&ogg->seek_event_cond);
1620 g_mutex_unlock (&ogg->seek_event_mutex);
1626 gst_ogg_demux_estimate_seek_quality (GstOggDemux * ogg)
1628 GstClockTimeDiff diff; /* how far from the goal we ended up */
1629 GstClockTimeDiff dist; /* how far we moved this iteration */
1632 if (ogg->push_prev_seek_time == GST_CLOCK_TIME_NONE) {
1633 /* for the first seek, we pretend we got a good seek,
1634 as we don't have a previous seek yet */
1638 /* We take a guess at how good the last seek was at guessing
1639 the byte target by comparing the amplitude of the last
1640 seek to the error */
1641 diff = GST_CLOCK_DIFF (ogg->push_seek_time_target, ogg->push_last_seek_time);
1644 dist = GST_CLOCK_DIFF (ogg->push_last_seek_time, ogg->push_prev_seek_time);
1648 seek_quality = (dist == 0) ? 0.0f : 1.0f / (1.0f + diff / (float) dist);
1650 GST_DEBUG_OBJECT (ogg,
1651 "We moved %" GST_STIME_FORMAT ", we're off by %" GST_STIME_FORMAT
1652 ", seek quality %f", GST_STIME_ARGS (dist), GST_STIME_ARGS (diff),
1654 return seek_quality;
1658 gst_ogg_demux_update_bisection_stats (GstOggDemux * ogg)
1662 GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps",
1663 ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]);
1665 for (n = 0; n < 2; ++n) {
1666 ogg->stats_bisection_steps[n] += ogg->push_bisection_steps[n];
1667 if (ogg->stats_bisection_max_steps[n] < ogg->push_bisection_steps[n])
1668 ogg->stats_bisection_max_steps[n] = ogg->push_bisection_steps[n];
1670 ogg->stats_nbisections++;
1672 GST_INFO_OBJECT (ogg,
1673 "So far, %.2f + %.2f bisections needed per seek (max %d + %d)",
1674 ogg->stats_bisection_steps[0] / (float) ogg->stats_nbisections,
1675 ogg->stats_bisection_steps[1] / (float) ogg->stats_nbisections,
1676 ogg->stats_bisection_max_steps[0], ogg->stats_bisection_max_steps[1]);
1680 gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
1682 GstOggDemux *ogg = pad->ogg;
1683 ogg_int64_t granpos = ogg_page_granulepos (page);
1685 GST_PUSH_LOCK (ogg);
1686 if (granpos >= 0 && pad->have_type) {
1687 if (ogg->push_start_time == GST_CLOCK_TIME_NONE) {
1688 ogg->push_start_time =
1689 gst_ogg_stream_get_start_time_for_granulepos (&pad->map, granpos);
1690 GST_DEBUG_OBJECT (ogg, "Stream start time: %" GST_TIME_FORMAT,
1691 GST_TIME_ARGS (ogg->push_start_time));
1693 ogg->push_time_offset =
1694 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1695 if (ogg->push_time_offset > 0) {
1696 GST_DEBUG_OBJECT (ogg, "Bitrate since start: %" G_GUINT64_FORMAT,
1697 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
1698 ogg->push_time_offset));
1701 if (ogg->push_state == PUSH_DURATION) {
1703 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1705 if (ogg->total_time == GST_CLOCK_TIME_NONE || t > ogg->total_time) {
1706 GST_DEBUG_OBJECT (ogg, "New total time: %" GST_TIME_FORMAT,
1708 ogg->total_time = t;
1709 ogg->push_time_length = t;
1712 /* If we're still receiving data from before the seek segment, drop it */
1713 if (ogg->seek_event_drop_till != 0) {
1714 GST_PUSH_UNLOCK (ogg);
1715 return GST_FLOW_SKIP_PUSH;
1718 /* If we were determining the duration of the stream, we're now done,
1719 and can get back to sending the original event we delayed.
1720 We stop a bit before the end of the stream, as if we get a EOS
1721 event and there is a queue2 upstream (such as when using playbin),
1722 it will pause the task *after* we come back from the EOS handler,
1723 so we cannot prevent the pausing by issuing a seek. */
1724 if (ogg->push_byte_offset >= ogg->push_byte_length) {
1725 GstMessage *message;
1728 /* tell the pipeline we've just found out the duration */
1729 ogg->push_time_length = ogg->total_time;
1730 GST_INFO_OBJECT (ogg, "New duration found: %" GST_TIME_FORMAT,
1731 GST_TIME_ARGS (ogg->total_time));
1732 message = gst_message_new_duration_changed (GST_OBJECT (ogg));
1733 gst_element_post_message (GST_ELEMENT (ogg), message);
1735 GST_DEBUG_OBJECT (ogg,
1736 "We're close enough to the end, and we're scared "
1737 "to get too close, seeking back to start");
1739 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1740 if (res != GST_FLOW_OK)
1742 return GST_FLOW_SKIP_PUSH;
1744 GST_PUSH_UNLOCK (ogg);
1746 return GST_FLOW_SKIP_PUSH;
1750 /* if we're seeking, look at time, and decide what to do */
1751 if (ogg->push_state != PUSH_PLAYING && ogg->push_state != PUSH_LINEAR2) {
1755 gboolean close_enough;
1758 /* ignore -1 granpos when seeking, we want to sync on a real granpos */
1760 GST_PUSH_UNLOCK (ogg);
1761 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1763 if (pad->current_granule == -1)
1764 gst_ogg_demux_setup_first_granule (ogg, pad, page);
1765 return GST_FLOW_SKIP_PUSH;
1768 t = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1770 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1771 GstClockTime sync_time;
1773 if (pad->push_sync_time == GST_CLOCK_TIME_NONE)
1774 pad->push_sync_time = t;
1775 GST_DEBUG_OBJECT (ogg, "Got PTS %" GST_TIME_FORMAT " for %s",
1776 GST_TIME_ARGS (t), gst_ogg_stream_get_media_type (&pad->map));
1777 sync_time = gst_ogg_demux_collect_sync_time (ogg, ogg->building_chain);
1778 if (sync_time == GST_CLOCK_TIME_NONE) {
1779 GST_PUSH_UNLOCK (ogg);
1780 GST_DEBUG_OBJECT (ogg,
1781 "Not enough timing info collected for sync, waiting for more");
1782 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1784 if (pad->current_granule == -1)
1785 gst_ogg_demux_setup_first_granule (ogg, pad, page);
1786 return GST_FLOW_SKIP_PUSH;
1788 ogg->push_last_seek_time = sync_time;
1790 GST_DEBUG_OBJECT (ogg,
1791 "Bisection just seeked at %" G_GINT64_FORMAT ", time %"
1792 GST_TIME_FORMAT ", target was %" GST_TIME_FORMAT,
1793 ogg->push_last_seek_offset,
1794 GST_TIME_ARGS (ogg->push_last_seek_time),
1795 GST_TIME_ARGS (ogg->push_seek_time_target));
1797 if (ogg->push_time1 != GST_CLOCK_TIME_NONE) {
1798 seek_quality = gst_ogg_demux_estimate_seek_quality (ogg);
1799 GST_DEBUG_OBJECT (ogg,
1800 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1801 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1802 " (%" GST_TIME_FORMAT "), seek quality %f", ogg->push_offset0,
1803 ogg->push_offset1, ogg->push_offset1 - ogg->push_offset0,
1804 GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1),
1805 GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0), seek_quality);
1807 /* in a open ended seek, we can't do bisection, so we pretend
1808 we like our result so far */
1809 seek_quality = 1.0f;
1810 GST_DEBUG_OBJECT (ogg,
1811 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1812 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - unknown",
1813 ogg->push_offset0, ogg->push_offset1,
1814 ogg->push_offset1 - ogg->push_offset0,
1815 GST_TIME_ARGS (ogg->push_time0));
1817 ogg->push_prev_seek_time = ogg->push_last_seek_time;
1819 gst_ogg_demux_setup_bisection_bounds (ogg);
1821 best = gst_ogg_demux_estimate_bisection_target (ogg, seek_quality);
1823 if (ogg->push_seek_time_target == 0) {
1824 GST_DEBUG_OBJECT (ogg, "Seeking to 0, deemed close enough");
1825 close_enough = (ogg->push_last_seek_time == 0);
1827 /* TODO: make this dependent on framerate ? */
1828 GstClockTime time_threshold = GST_SECOND / 2;
1829 guint64 byte_threshold =
1830 (ogg->max_packet_size >
1831 64 * 1024 ? ogg->max_packet_size : 64 * 1024);
1833 /* We want to be within half a second before the target,
1834 or before the target and half less or equal to the max
1835 packet size left to search in */
1836 if (time_threshold > ogg->push_seek_time_target)
1837 time_threshold = ogg->push_seek_time_target;
1838 close_enough = ogg->push_last_seek_time < ogg->push_seek_time_target
1839 && (ogg->push_last_seek_time >=
1840 ogg->push_seek_time_target - time_threshold
1841 || ogg->push_offset1 <= ogg->push_offset0 + byte_threshold);
1842 GST_DEBUG_OBJECT (ogg,
1843 "testing if we're close enough: %" GST_TIME_FORMAT " <= %"
1844 GST_TIME_FORMAT " < %" GST_TIME_FORMAT ", or %" G_GUINT64_FORMAT
1845 " <= %" G_GUINT64_FORMAT " ? %s",
1846 GST_TIME_ARGS (ogg->push_seek_time_target - time_threshold),
1847 GST_TIME_ARGS (ogg->push_last_seek_time),
1848 GST_TIME_ARGS (ogg->push_seek_time_target),
1849 ogg->push_offset1 - ogg->push_offset0, byte_threshold,
1850 close_enough ? "Yes" : "No");
1853 if (close_enough || best == ogg->push_last_seek_offset) {
1854 if (ogg->push_state == PUSH_BISECT1) {
1855 /* we now know the time segment we'll have to search for
1856 the second bisection */
1857 ogg->push_time0 = ogg->push_start_time;
1858 ogg->push_offset0 = 0;
1860 GST_DEBUG_OBJECT (ogg,
1861 "Seek to %" GST_TIME_FORMAT
1862 " (%lx) done, now gathering pages for all non-sparse streams",
1863 GST_TIME_ARGS (ogg->push_seek_time_target), (long) granpos);
1864 ogg->push_state = PUSH_LINEAR1;
1866 /* If we're asked for an accurate seek, we'll go forward till
1867 we get to the original seek target time, else we'll just drop
1868 here at the keyframe */
1869 if (ogg->push_seek_flags & GST_SEEK_FLAG_ACCURATE) {
1870 GST_INFO_OBJECT (ogg,
1871 "Seek to keyframe at %" GST_TIME_FORMAT " done (we're at %"
1872 GST_TIME_FORMAT "), skipping to original target (%"
1873 GST_TIME_FORMAT ")",
1874 GST_TIME_ARGS (ogg->push_seek_time_target),
1875 GST_TIME_ARGS (sync_time),
1876 GST_TIME_ARGS (ogg->push_seek_time_original_target));
1877 ogg->push_state = PUSH_LINEAR2;
1879 GST_INFO_OBJECT (ogg, "Seek to keyframe done, playing");
1881 /* we're synced to the seek target, so flush stream and stuff
1882 any queued pages into the stream so we start decoding there */
1883 ogg->push_state = PUSH_PLAYING;
1885 gst_ogg_demux_update_bisection_stats (ogg);
1888 } else if (ogg->push_state == PUSH_LINEAR1) {
1889 if (pad->push_kf_time == GST_CLOCK_TIME_NONE) {
1890 GstClockTime earliest_keyframe_time;
1892 gst_ogg_demux_record_keyframe_time (ogg, pad, granpos);
1893 GST_DEBUG_OBJECT (ogg,
1894 "Previous keyframe for %s stream at %" GST_TIME_FORMAT,
1895 gst_ogg_stream_get_media_type (&pad->map),
1896 GST_TIME_ARGS (pad->push_kf_time));
1897 earliest_keyframe_time = gst_ogg_demux_get_earliest_keyframe_time (ogg);
1898 if (earliest_keyframe_time != GST_CLOCK_TIME_NONE) {
1899 if (earliest_keyframe_time > ogg->push_last_seek_time) {
1900 GST_INFO_OBJECT (ogg,
1901 "All non sparse streams now have a previous keyframe time, "
1902 "and we already decoded it, switching to playing");
1903 ogg->push_state = PUSH_PLAYING;
1904 gst_ogg_demux_update_bisection_stats (ogg);
1906 GST_INFO_OBJECT (ogg,
1907 "All non sparse streams now have a previous keyframe time, "
1908 "bisecting again to %" GST_TIME_FORMAT,
1909 GST_TIME_ARGS (earliest_keyframe_time));
1911 ogg->push_seek_time_target = earliest_keyframe_time;
1912 ogg->push_offset0 = 0;
1913 ogg->push_time0 = ogg->push_start_time;
1914 ogg->push_offset1 = ogg->push_last_seek_offset;
1915 ogg->push_time1 = ogg->push_last_seek_time;
1916 ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
1917 ogg->seek_secant = FALSE;
1918 ogg->seek_undershot = FALSE;
1920 ogg->push_state = PUSH_BISECT2;
1921 best = gst_ogg_demux_estimate_bisection_target (ogg, 1.0f);
1927 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1930 ogg_sync_reset (&ogg->sync);
1931 for (i = 0; i < ogg->building_chain->streams->len; i++) {
1933 g_array_index (ogg->building_chain->streams, GstOggPad *, i);
1935 pad->push_sync_time = GST_CLOCK_TIME_NONE;
1936 ogg_stream_reset (&pad->map.stream);
1939 GST_DEBUG_OBJECT (ogg,
1940 "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, best,
1943 g_assert (best != -1);
1944 ogg->push_bisection_steps[ogg->push_state == PUSH_BISECT2 ? 1 : 0]++;
1946 gst_event_new_seek (ogg->push_seek_rate, GST_FORMAT_BYTES,
1947 ogg->push_seek_flags, GST_SEEK_TYPE_SET, best,
1948 GST_SEEK_TYPE_NONE, -1);
1949 gst_event_set_seqnum (sevent, ogg->seqnum);
1951 gst_event_replace (&ogg->seek_event, sevent);
1952 gst_event_unref (sevent);
1953 GST_PUSH_UNLOCK (ogg);
1954 g_mutex_lock (&ogg->seek_event_mutex);
1955 g_cond_broadcast (&ogg->seek_event_cond);
1956 g_mutex_unlock (&ogg->seek_event_mutex);
1957 return GST_FLOW_SKIP_PUSH;
1960 if (ogg->push_state != PUSH_PLAYING) {
1961 GST_PUSH_UNLOCK (ogg);
1962 return GST_FLOW_SKIP_PUSH;
1965 GST_PUSH_UNLOCK (ogg);
1971 GST_WARNING_OBJECT (ogg,
1972 "ogg stream choked on page (serial %08x), "
1973 "resetting stream", pad->map.serialno);
1974 gst_ogg_pad_reset (pad);
1975 /* we continue to recover */
1976 return GST_FLOW_SKIP_PUSH;
1981 gst_ogg_demux_query_duration_push (GstOggDemux * ogg)
1983 if (!ogg->pullmode && ogg->push_byte_length == -1) {
1985 gboolean seekable = FALSE;
1987 query = gst_query_new_seeking (GST_FORMAT_BYTES);
1988 if (gst_pad_peer_query (ogg->sinkpad, query))
1989 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
1990 gst_query_unref (query);
1994 if (!gst_element_query_duration (GST_ELEMENT (ogg), GST_FORMAT_BYTES,
1997 GST_DEBUG_OBJECT (ogg,
1998 "Unable to determine stream size, assuming live, seeking disabled");
1999 ogg->push_disable_seeking = TRUE;
2001 ogg->push_disable_seeking = FALSE;
2004 GST_DEBUG_OBJECT (ogg, "Stream is not seekable, seeking disabled");
2005 ogg->push_disable_seeking = TRUE;
2010 /* submit a page to an oggpad, this function will then submit all
2011 * the packets in the page.
2013 static GstFlowReturn
2014 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
2016 GstFlowReturn result = GST_FLOW_OK;
2018 gboolean continued = FALSE;
2022 /* for negative rates we read pages backwards and must therefore be careful
2023 * with continued pages */
2024 if (ogg->segment.rate < 0.0) {
2027 continued = ogg_page_continued (page);
2029 /* number of completed packets in the page */
2030 npackets = ogg_page_packets (page);
2032 /* page is not continued so it contains at least one packet start. It's
2033 * possible that no packet ends on this page (npackets == 0). In that
2034 * case, the next (continued) page(s) we kept contain the remainder of the
2035 * packets. We mark npackets=1 to make us start decoding the pages in the
2036 * remainder of the algorithm. */
2040 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
2042 if (npackets == 0) {
2043 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
2048 gst_ogg_demux_query_duration_push (ogg);
2050 /* keep track of time in push mode */
2051 if (!ogg->pullmode) {
2052 result = gst_ogg_pad_handle_push_mode_state (pad, page);
2053 if (result == GST_FLOW_SKIP_PUSH)
2055 if (result != GST_FLOW_OK)
2059 if (page->header_len + page->body_len > ogg->max_page_size)
2060 ogg->max_page_size = page->header_len + page->body_len;
2062 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
2064 if (pad->current_granule == -1)
2065 gst_ogg_demux_setup_first_granule (ogg, pad, page);
2067 /* flush all packets in the stream layer, this might not give a packet if
2068 * the page had no packets finishing on the page (npackets == 0). */
2069 result = gst_ogg_pad_stream_out (pad, 0);
2071 if (pad->continued) {
2074 /* now send the continued pages to the stream layer */
2075 while (pad->continued) {
2076 ogg_page *p = (ogg_page *) pad->continued->data;
2078 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
2079 if (ogg_stream_pagein (&pad->map.stream, p) != 0)
2082 pad->continued = g_list_delete_link (pad->continued, pad->continued);
2085 gst_ogg_page_free (p);
2088 GST_LOG_OBJECT (ogg, "flushing last continued packet");
2089 /* flush 1 continued packet in the stream layer */
2090 result = gst_ogg_pad_stream_out (pad, 1);
2092 /* flush all remaining packets, we pushed them in the previous round.
2093 * We don't use _reset() because we still want to get the discont when
2094 * we submit a next page. */
2095 while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
2099 /* keep continued pages (only in reverse mode) */
2101 ogg_page *p = gst_ogg_page_copy (page);
2103 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
2104 pad->continued = g_list_prepend (pad->continued, p);
2111 GST_WARNING_OBJECT (ogg,
2112 "ogg stream choked on page (serial %08x), "
2113 "resetting stream", pad->map.serialno);
2114 gst_ogg_pad_reset (pad);
2115 /* we continue to recover */
2121 static GstOggChain *
2122 gst_ogg_chain_new (GstOggDemux * ogg)
2124 GstOggChain *chain = g_slice_new0 (GstOggChain);
2126 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
2130 chain->have_bos = FALSE;
2131 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
2132 chain->begin_time = GST_CLOCK_TIME_NONE;
2133 chain->segment_start = GST_CLOCK_TIME_NONE;
2134 chain->segment_stop = GST_CLOCK_TIME_NONE;
2135 chain->total_time = GST_CLOCK_TIME_NONE;
2141 gst_ogg_chain_free (GstOggChain * chain)
2145 for (i = 0; i < chain->streams->len; i++) {
2146 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2148 gst_object_unref (pad);
2150 g_array_free (chain->streams, TRUE);
2151 g_slice_free (GstOggChain, chain);
2155 gst_ogg_pad_mark_discont (GstOggPad * pad)
2157 GST_LOG_OBJECT (pad, "Marking discont on pad");
2158 pad->discont = TRUE;
2159 pad->map.last_size = 0;
2163 gst_ogg_chain_mark_discont (GstOggChain * chain)
2167 for (i = 0; i < chain->streams->len; i++) {
2168 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2170 gst_ogg_pad_mark_discont (pad);
2175 gst_ogg_chain_reset (GstOggChain * chain)
2179 for (i = 0; i < chain->streams->len; i++) {
2180 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2182 gst_ogg_pad_reset (pad);
2187 gst_ogg_chain_new_stream (GstOggChain * chain, guint32 serialno)
2192 GST_DEBUG_OBJECT (chain->ogg,
2193 "creating new stream %08x in chain %p", serialno, chain);
2195 name = g_strdup_printf ("src_%08x", serialno);
2196 ret = g_object_new (GST_TYPE_OGG_PAD, "name", name, NULL);
2198 /* we own this one */
2199 gst_object_ref_sink (ret);
2201 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
2202 gst_ogg_pad_mark_discont (ret);
2205 ret->ogg = chain->ogg;
2207 ret->map.serialno = serialno;
2208 if (ogg_stream_init (&ret->map.stream, serialno) != 0)
2211 GST_DEBUG_OBJECT (chain->ogg,
2212 "created new ogg src %p for stream with serial %08x", ret, serialno);
2214 g_array_append_val (chain->streams, ret);
2215 gst_pad_set_active (GST_PAD_CAST (ret), TRUE);
2222 GST_ERROR ("Could not initialize ogg_stream struct for serial %08x",
2224 gst_object_unref (ret);
2230 gst_ogg_chain_get_stream (GstOggChain * chain, guint32 serialno)
2234 for (i = 0; i < chain->streams->len; i++) {
2235 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2237 if (pad->map.serialno == serialno)
2244 gst_ogg_chain_has_stream (GstOggChain * chain, guint32 serialno)
2246 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
2249 /* signals and args */
2262 static GstStaticPadTemplate ogg_demux_src_template_factory =
2263 GST_STATIC_PAD_TEMPLATE ("src_%08x",
2266 GST_STATIC_CAPS_ANY);
2268 static GstStaticPadTemplate ogg_demux_sink_template_factory =
2269 GST_STATIC_PAD_TEMPLATE ("sink",
2272 GST_STATIC_CAPS ("application/ogg; audio/ogg; video/ogg; application/kate")
2275 static void gst_ogg_demux_finalize (GObject * object);
2277 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
2278 GstOggChain ** chain);
2279 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
2280 GstOggChain * chain);
2282 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent,
2284 static void gst_ogg_demux_loop (GstOggPad * pad);
2285 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstObject * parent,
2286 GstBuffer * buffer);
2287 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad,
2288 GstObject * parent);
2289 static gboolean gst_ogg_demux_sink_activate_mode (GstPad * sinkpad,
2290 GstObject * parent, GstPadMode mode, gboolean active);
2291 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
2292 GstStateChange transition);
2294 static void gst_ogg_print (GstOggDemux * demux);
2295 static gboolean gst_ogg_demux_plugin_init (GstPlugin * plugin);
2297 #define gst_ogg_demux_parent_class parent_class
2298 G_DEFINE_TYPE (GstOggDemux, gst_ogg_demux, GST_TYPE_ELEMENT);
2299 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (oggdemux, "oggdemux", GST_RANK_PRIMARY,
2300 GST_TYPE_OGG_DEMUX, gst_ogg_demux_plugin_init (plugin));
2303 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
2305 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
2306 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2308 gst_element_class_set_static_metadata (gstelement_class,
2309 "Ogg demuxer", "Codec/Demuxer",
2310 "demux ogg streams (info about ogg: http://xiph.org)",
2311 "Wim Taymans <wim@fluendo.com>");
2313 gst_element_class_add_static_pad_template (gstelement_class,
2314 &ogg_demux_sink_template_factory);
2315 gst_element_class_add_static_pad_template (gstelement_class,
2316 &ogg_demux_src_template_factory);
2318 gstelement_class->change_state = gst_ogg_demux_change_state;
2319 gstelement_class->send_event = gst_ogg_demux_receive_event;
2321 gobject_class->finalize = gst_ogg_demux_finalize;
2325 gst_ogg_demux_init (GstOggDemux * ogg)
2327 /* create the sink pad */
2329 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
2332 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
2333 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
2334 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
2335 gst_pad_set_activatemode_function (ogg->sinkpad,
2336 gst_ogg_demux_sink_activate_mode);
2337 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
2339 g_mutex_init (&ogg->chain_lock);
2340 g_mutex_init (&ogg->push_lock);
2341 g_mutex_init (&ogg->seek_event_mutex);
2342 g_cond_init (&ogg->seek_event_cond);
2343 g_cond_init (&ogg->thread_started_cond);
2345 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
2347 ogg->stats_nbisections = 0;
2348 ogg->stats_bisection_steps[0] = 0;
2349 ogg->stats_bisection_steps[1] = 0;
2350 ogg->stats_bisection_max_steps[0] = 0;
2351 ogg->stats_bisection_max_steps[1] = 0;
2353 ogg->newsegment = NULL;
2354 ogg->seqnum = GST_SEQNUM_INVALID;
2356 ogg->chunk_size = CHUNKSIZE;
2357 ogg->flowcombiner = gst_flow_combiner_new ();
2361 gst_ogg_demux_finalize (GObject * object)
2365 ogg = GST_OGG_DEMUX (object);
2367 g_array_free (ogg->chains, TRUE);
2368 g_mutex_clear (&ogg->chain_lock);
2369 g_mutex_clear (&ogg->push_lock);
2370 g_cond_clear (&ogg->seek_event_cond);
2371 g_cond_clear (&ogg->thread_started_cond);
2372 g_mutex_clear (&ogg->seek_event_mutex);
2374 ogg_sync_clear (&ogg->sync);
2376 if (ogg->newsegment)
2377 gst_event_unref (ogg->newsegment);
2379 gst_flow_combiner_free (ogg->flowcombiner);
2381 if (ogg->building_chain)
2382 gst_ogg_chain_free (ogg->building_chain);
2384 G_OBJECT_CLASS (parent_class)->finalize (object);
2388 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
2393 chain = ogg->current_chain;
2397 for (i = 0; i < chain->streams->len; i++) {
2398 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2400 stream->start_time = -1;
2401 stream->map.accumulated_granule = 0;
2402 stream->current_granule = -1;
2403 stream->keyframe_granule = -1;
2405 ogg->building_chain = chain;
2406 GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2407 ogg->current_chain = NULL;
2409 gst_ogg_chain_mark_discont (chain);
2411 ogg->chunk_size = CHUNKSIZE;
2415 gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
2420 ogg = GST_OGG_DEMUX (parent);
2422 switch (GST_EVENT_TYPE (event)) {
2423 case GST_EVENT_FLUSH_START:
2424 if (ogg->seqnum != GST_SEQNUM_INVALID) {
2425 event = gst_event_make_writable (event);
2426 gst_event_set_seqnum (event, ogg->seqnum);
2428 res = gst_ogg_demux_send_event (ogg, event);
2430 case GST_EVENT_FLUSH_STOP:
2431 GST_DEBUG_OBJECT (ogg, "got a flush stop event");
2432 ogg_sync_reset (&ogg->sync);
2433 if (ogg->seqnum != GST_SEQNUM_INVALID) {
2434 event = gst_event_make_writable (event);
2435 gst_event_set_seqnum (event, ogg->seqnum);
2437 res = gst_ogg_demux_send_event (ogg, event);
2438 if (ogg->pullmode || ogg->push_state != PUSH_DURATION) {
2439 /* it's starting to feel reaaaally dirty :(
2440 if we're on a spliced seek to get duration, don't reset streams,
2441 we'll need them for the delayed seek */
2442 gst_ogg_demux_reset_streams (ogg);
2445 case GST_EVENT_SEGMENT:
2446 GST_DEBUG_OBJECT (ogg, "got a new segment event");
2451 gst_event_copy_segment (event, &segment);
2453 if (segment.format == GST_FORMAT_BYTES) {
2454 GST_PUSH_LOCK (ogg);
2455 ogg->push_byte_offset = segment.start;
2456 ogg->push_last_seek_offset = segment.start;
2458 if (gst_event_get_seqnum (event) == ogg->seqnum) {
2459 GstSeekType stop_type = GST_SEEK_TYPE_NONE;
2460 if (ogg->push_seek_time_original_stop != -1)
2461 stop_type = GST_SEEK_TYPE_SET;
2462 gst_segment_do_seek (&ogg->segment, ogg->push_seek_rate,
2463 GST_FORMAT_TIME, ogg->push_seek_flags, GST_SEEK_TYPE_SET,
2464 ogg->push_seek_time_original_target, stop_type,
2465 ogg->push_seek_time_original_stop, &update);
2466 } else if (ogg->seqnum == GST_SEQNUM_INVALID) {
2467 ogg->seqnum = GST_EVENT_SEQNUM (event);
2470 if (!ogg->pullmode && !(ogg->push_seek_flags & GST_SEEK_FLAG_FLUSH)) {
2472 GstOggChain *chain = ogg->current_chain;
2474 ogg->push_seek_flags = 0;
2476 /* This will happen when we bisect, as we clear the chain when
2477 we do the first seek. On subsequent ones, we just reset the
2478 ogg sync object as we already reset the chain */
2479 GST_DEBUG_OBJECT (ogg, "No chain, just resetting ogg sync");
2480 ogg_sync_reset (&ogg->sync);
2482 /* reset pad push mode seeking state */
2483 for (i = 0; i < chain->streams->len; i++) {
2484 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2485 pad->push_kf_time = GST_CLOCK_TIME_NONE;
2486 pad->push_sync_time = GST_CLOCK_TIME_NONE;
2488 ogg_sync_reset (&ogg->sync);
2489 gst_ogg_demux_reset_streams (ogg);
2493 if (!ogg->pullmode) {
2494 if (ogg->seek_event_drop_till == gst_event_get_seqnum (event)) {
2495 GST_DEBUG_OBJECT (ogg,
2496 "Got event seqnum %u, stopping dropping (ogg->seqnum:%u)",
2497 ogg->seek_event_drop_till, ogg->seqnum);
2498 ogg->seek_event_drop_till = 0;
2501 GST_PUSH_UNLOCK (ogg);
2503 GST_WARNING_OBJECT (ogg, "unexpected segment format: %s",
2504 gst_format_get_name (segment.format));
2508 gst_event_unref (event);
2513 GST_DEBUG_OBJECT (ogg, "got an EOS event");
2514 GST_PUSH_LOCK (ogg);
2515 if (ogg->push_state == PUSH_DURATION) {
2516 GST_DEBUG_OBJECT (ogg, "Got EOS while determining length");
2517 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
2518 if (res != GST_FLOW_OK) {
2519 GST_DEBUG_OBJECT (ogg, "Error seeking back after duration check: %d",
2524 GST_PUSH_UNLOCK (ogg);
2525 res = gst_ogg_demux_send_event (ogg, event);
2526 if (ogg->current_chain == NULL) {
2527 GST_WARNING_OBJECT (ogg,
2528 "EOS while trying to retrieve chain, seeking disabled");
2529 ogg->push_disable_seeking = TRUE;
2535 res = gst_pad_event_default (pad, parent, event);
2542 /* submit the given buffer to the ogg sync */
2543 static GstFlowReturn
2544 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
2548 GstFlowReturn ret = GST_FLOW_OK;
2550 size = gst_buffer_get_size (buffer);
2551 GST_DEBUG_OBJECT (ogg, "submitting %" G_GSIZE_FORMAT " bytes", size);
2552 if (G_UNLIKELY (size == 0))
2555 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
2556 if (G_UNLIKELY (oggbuffer == NULL))
2559 gst_buffer_extract (buffer, 0, oggbuffer, size);
2561 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2564 if (!ogg->pullmode) {
2565 GST_PUSH_LOCK (ogg);
2566 ogg->push_byte_offset += size;
2567 GST_PUSH_UNLOCK (ogg);
2571 gst_buffer_unref (buffer);
2578 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2579 (NULL), ("failed to get ogg sync buffer"));
2580 ret = GST_FLOW_ERROR;
2585 GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2586 ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2587 ret = GST_FLOW_ERROR;
2592 /* in random access mode this code updates the current read position
2593 * and resets the ogg sync buffer so that the next read will happen
2594 * from this new location.
2597 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
2599 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
2601 ogg->offset = offset;
2602 ogg->read_offset = offset;
2603 ogg_sync_reset (&ogg->sync);
2606 /* read more data from the current offset and submit to
2607 * the ogg sync layer.
2609 static GstFlowReturn
2610 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
2617 GST_LOG_OBJECT (ogg,
2618 "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
2619 ogg->read_offset, ogg->length, end_offset);
2621 if (end_offset > 0 && ogg->read_offset >= end_offset)
2622 goto boundary_reached;
2624 if (ogg->read_offset == ogg->length)
2627 oggbuffer = ogg_sync_buffer (&ogg->sync, ogg->chunk_size);
2628 if (G_UNLIKELY (oggbuffer == NULL))
2632 gst_buffer_new_wrapped_full (0, oggbuffer, ogg->chunk_size, 0,
2633 ogg->chunk_size, NULL, NULL);
2636 gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, ogg->chunk_size,
2638 if (ret != GST_FLOW_OK)
2641 size = gst_buffer_get_size (buffer);
2643 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2646 ogg->read_offset += size;
2647 gst_buffer_unref (buffer);
2654 GST_LOG_OBJECT (ogg, "reached boundary");
2655 return GST_FLOW_LIMIT;
2659 GST_LOG_OBJECT (ogg, "reached EOS");
2660 return GST_FLOW_EOS;
2664 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2665 (NULL), ("failed to get ogg sync buffer"));
2666 return GST_FLOW_ERROR;
2670 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
2671 gst_flow_get_name (ret));
2672 gst_buffer_unref (buffer);
2677 GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2678 ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2679 gst_buffer_unref (buffer);
2680 return GST_FLOW_ERROR;
2684 /* Read the next page from the current offset.
2685 * boundary: number of bytes ahead we allow looking for;
2688 * @offset will contain the offset the next page starts at when this function
2689 * returns GST_FLOW_OK.
2691 * GST_FLOW_EOS is returned on EOS.
2693 * GST_FLOW_LIMIT is returned when we did not find a page before the
2694 * boundary. If @boundary is -1, this is never returned.
2696 * Any other error returned while retrieving data from the peer is returned as
2699 static GstFlowReturn
2700 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
2701 gint64 boundary, gint64 * offset)
2703 gint64 end_offset = -1;
2706 GST_LOG_OBJECT (ogg,
2707 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
2708 G_GINT64_FORMAT, ogg->offset, boundary);
2711 end_offset = ogg->offset + boundary;
2716 if (end_offset > 0 && ogg->offset >= end_offset)
2717 goto boundary_reached;
2719 more = ogg_sync_pageseek (&ogg->sync, og);
2721 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
2724 /* skipped n bytes */
2725 ogg->offset -= more;
2726 GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
2728 } else if (more == 0) {
2729 /* we need more data */
2731 goto boundary_reached;
2733 GST_LOG_OBJECT (ogg, "need more data");
2734 ret = gst_ogg_demux_get_data (ogg, end_offset);
2735 if (ret != GST_FLOW_OK)
2738 gint64 res_offset = ogg->offset;
2740 /* got a page. Return the offset at the page beginning,
2741 advance the internal offset past the page end */
2743 *offset = res_offset;
2746 ogg->offset += more;
2748 GST_LOG_OBJECT (ogg,
2749 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
2750 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
2751 ogg_page_serialno (og), ogg->offset,
2752 (gint64) ogg_page_granulepos (og));
2756 GST_LOG_OBJECT (ogg, "returning %d", ret);
2763 GST_LOG_OBJECT (ogg,
2764 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
2765 ogg->offset, end_offset);
2766 return GST_FLOW_LIMIT;
2770 /* from the current offset, find the previous page, seeking backwards
2771 * until we find the page.
2773 static GstFlowReturn
2774 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
2777 gint64 begin = ogg->offset;
2779 gint64 cur_offset = -1;
2781 GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
2783 while (cur_offset == -1) {
2784 begin -= ogg->chunk_size;
2788 /* seek ogg->chunk_size back */
2789 GST_LOG_OBJECT (ogg, "seeking back to %" G_GINT64_FORMAT, begin);
2790 gst_ogg_demux_seek (ogg, begin);
2792 /* now continue reading until we run out of data, if we find a page
2793 * start, we save it. It might not be the final page as there could be
2794 * another page after this one. */
2795 while (ogg->offset < end) {
2796 gint64 new_offset, boundary;
2798 /* An Ogg page cannot be more than a bit less than 64 KB, so we can
2799 bound the boundary to that size when searching backwards if we
2800 haven't found a page yet. So the most we have to look at is twice
2801 the max page size, which is the worst case if we start scanning
2802 just after a large page, after which also lies a large page. */
2803 boundary = end - ogg->offset;
2804 if (boundary > 2 * MAX_OGG_PAGE_SIZE)
2805 boundary = 2 * MAX_OGG_PAGE_SIZE;
2807 ret = gst_ogg_demux_get_next_page (ogg, og, boundary, &new_offset);
2808 /* we hit the upper limit, offset contains the last page start */
2809 if (ret == GST_FLOW_LIMIT) {
2810 GST_LOG_OBJECT (ogg, "hit limit");
2813 /* something went wrong */
2814 if (ret == GST_FLOW_EOS) {
2816 GST_LOG_OBJECT (ogg, "got unexpected");
2819 } else if (ret != GST_FLOW_OK) {
2820 GST_LOG_OBJECT (ogg, "got error %d", ret);
2824 GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
2826 /* offset is next page start */
2827 cur_offset = new_offset;
2831 GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
2833 /* we have the offset. Actually snork and hold the page now */
2834 gst_ogg_demux_seek (ogg, cur_offset);
2835 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
2836 if (ret != GST_FLOW_OK) {
2837 GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
2839 /* this shouldn't be possible */
2844 *offset = cur_offset;
2851 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
2854 GstOggChain *chain = ogg->current_chain;
2859 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
2861 /* send EOS on all the pads */
2862 for (i = 0; i < chain->streams->len; i++) {
2863 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2869 event = gst_event_new_eos ();
2870 gst_event_set_seqnum (event, ogg->seqnum);
2871 gst_pad_push_event (GST_PAD_CAST (pad), event);
2873 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
2875 /* deactivate first */
2876 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
2878 gst_flow_combiner_remove_pad (ogg->flowcombiner, GST_PAD_CAST (pad));
2880 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2885 /* if we cannot seek back to the chain, we can destroy the chain
2887 if (!ogg->pullmode) {
2888 if (ogg->building_chain == chain)
2889 ogg->building_chain = NULL;
2890 ogg->current_chain = NULL;
2891 gst_ogg_chain_free (chain);
2898 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
2901 GstStructure *structure;
2902 GValue array = { 0 };
2904 GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
2906 if (G_UNLIKELY (!caps))
2908 if (G_UNLIKELY (!headers))
2911 caps = gst_caps_make_writable (caps);
2912 structure = gst_caps_get_structure (caps, 0);
2914 g_value_init (&array, GST_TYPE_ARRAY);
2917 GValue value = { 0 };
2919 ogg_packet *op = headers->data;
2921 buffer = gst_buffer_new_and_alloc (op->bytes);
2923 gst_buffer_fill (buffer, 0, op->packet, op->bytes);
2924 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_HEADER);
2925 g_value_init (&value, GST_TYPE_BUFFER);
2926 gst_value_take_buffer (&value, buffer);
2927 gst_value_array_append_value (&array, &value);
2928 g_value_unset (&value);
2929 headers = headers->next;
2932 gst_structure_take_value (structure, "streamheader", &array);
2933 GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
2939 gst_ogg_demux_push_queued_buffers (GstOggDemux * ogg, GstOggPad * pad)
2943 /* push queued packets */
2944 for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
2945 ogg_packet *p = walk->data;
2947 gst_ogg_demux_chain_peer (pad, p, TRUE);
2948 _ogg_packet_free (p);
2950 /* and free the queued buffers */
2951 g_list_free (pad->map.queued);
2952 pad->map.queued = NULL;
2956 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
2960 gint bitrate, idx_bitrate;
2962 g_return_val_if_fail (chain != NULL, FALSE);
2964 if (chain == ogg->current_chain) {
2966 gst_event_unref (event);
2968 for (i = 0; i < chain->streams->len; i++) {
2969 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2970 gst_ogg_demux_push_queued_buffers (ogg, pad);
2976 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
2978 bitrate = idx_bitrate = 0;
2980 /* first add the pads */
2981 for (i = 0; i < chain->streams->len; i++) {
2986 pad = g_array_index (chain->streams, GstOggPad *, i);
2988 if (pad->map.idx_bitrate)
2989 idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
2991 bitrate += pad->map.bitrate;
2994 gst_ogg_pad_mark_discont (pad);
2995 pad->last_ret = GST_FLOW_OK;
2997 if (pad->map.is_skeleton || pad->map.is_cmml || pad->added
3001 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
3003 /* activate first */
3004 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
3007 gst_pad_create_stream_id_printf (GST_PAD (pad), GST_ELEMENT_CAST (ogg),
3008 "%08x", pad->map.serialno);
3010 gst_pad_get_sticky_event (ogg->sinkpad, GST_EVENT_STREAM_START, 0);
3012 if (gst_event_parse_group_id (ss_event, &ogg->group_id))
3013 ogg->have_group_id = TRUE;
3015 ogg->have_group_id = FALSE;
3016 gst_event_unref (ss_event);
3017 } else if (!ogg->have_group_id) {
3018 ogg->have_group_id = TRUE;
3019 ogg->group_id = gst_util_group_id_next ();
3021 ss_event = gst_event_new_stream_start (stream_id);
3022 if (ogg->have_group_id)
3023 gst_event_set_group_id (ss_event, ogg->group_id);
3025 gst_pad_push_event (GST_PAD (pad), ss_event);
3028 /* Set headers on caps */
3030 gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers);
3031 gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps);
3033 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
3035 gst_flow_combiner_add_pad (ogg->flowcombiner, GST_PAD_CAST (pad));
3037 /* prefer the index bitrate over the ones encoded in the streams */
3038 ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
3040 /* after adding the new pads, remove the old pads */
3041 gst_ogg_demux_deactivate_current_chain (ogg);
3043 GST_DEBUG_OBJECT (ogg, "Setting current chain to %p", chain);
3044 ogg->current_chain = chain;
3046 /* we are finished now */
3047 gst_element_no_more_pads (GST_ELEMENT (ogg));
3049 GST_DEBUG_OBJECT (ogg, "starting chain");
3051 /* then send out any headers and queued packets */
3052 for (i = 0; i < chain->streams->len; i++) {
3057 pad = g_array_index (chain->streams, GstOggPad *, i);
3059 /* Skip pads that were not added, e.g. Skeleton streams */
3063 /* FIXME, must be sent from the streaming thread */
3065 gst_pad_push_event (GST_PAD_CAST (pad), gst_event_ref (event));
3067 /* FIXME also streaming thread */
3068 if (pad->map.taglist) {
3069 GST_DEBUG_OBJECT (ogg, "pushing tags");
3070 gst_pad_push_event (GST_PAD_CAST (pad),
3071 gst_event_new_tag (pad->map.taglist));
3072 pad->map.taglist = NULL;
3075 tags = gst_tag_list_new (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL);
3076 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
3077 gst_pad_push_event (GST_PAD (pad), gst_event_new_tag (tags));
3079 GST_DEBUG_OBJECT (ogg, "pushing headers");
3081 for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
3082 ogg_packet *p = walk->data;
3084 gst_ogg_demux_chain_peer (pad, p, TRUE);
3087 GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
3088 gst_ogg_demux_push_queued_buffers (ogg, pad);
3092 gst_event_unref (event);
3098 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
3099 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
3100 gint64 * offset, gboolean only_serial_no, gint serialno)
3108 GST_DEBUG_OBJECT (ogg,
3109 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
3111 GST_DEBUG_OBJECT (ogg,
3112 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
3113 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
3114 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
3116 /* perform the seek */
3117 while (begin < end) {
3120 if ((end - begin < ogg->chunk_size) || (endtime == begintime)) {
3123 /* take a (pretty decent) guess, avoiding overflow */
3124 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
3127 (target - begintime) / GST_MSECOND * rate + begin - ogg->chunk_size;
3129 if (bisect <= begin)
3131 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
3133 gst_ogg_demux_seek (ogg, bisect);
3135 while (begin < end) {
3138 GST_DEBUG_OBJECT (ogg,
3139 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
3140 ", end %" G_GINT64_FORMAT, bisect, begin, end);
3142 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
3143 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
3146 if (ret == GST_FLOW_LIMIT) {
3147 /* we hit the upper limit, go back a bit */
3148 if (bisect <= begin + 1) {
3149 end = begin; /* found it */
3154 bisect -= ogg->chunk_size;
3155 if (bisect <= begin)
3158 gst_ogg_demux_seek (ogg, bisect);
3160 } else if (ret == GST_FLOW_OK) {
3161 /* found offset of next ogg page */
3163 GstClockTime granuletime;
3166 /* get the granulepos */
3167 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
3169 granulepos = ogg_page_granulepos (&og);
3170 if (granulepos == -1) {
3171 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
3175 /* Avoid seeking to an incorrect granuletime by only considering
3176 the stream for which we found the earliest time */
3177 if (only_serial_no && ogg_page_serialno (&og) != serialno)
3180 /* get the stream */
3181 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
3182 if (pad == NULL || pad->map.is_skeleton)
3185 /* convert granulepos to time */
3186 granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
3188 if (granuletime < pad->start_time)
3191 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to PTS %"
3192 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
3194 granuletime -= pad->start_time;
3195 granuletime += chain->begin_time;
3197 GST_DEBUG_OBJECT (ogg,
3198 "found page with granule %" G_GINT64_FORMAT " and time %"
3199 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
3201 if (granuletime < target) {
3202 best = result; /* raw offset of packet with granulepos */
3203 begin = ogg->offset; /* raw offset of next page */
3204 begintime = granuletime;
3206 bisect = begin; /* *not* begin + 1 */
3208 if (bisect <= begin + 1) {
3209 end = begin; /* found it */
3211 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
3213 bisect -= ogg->chunk_size; /* an endless loop otherwise. */
3214 if (bisect <= begin)
3216 gst_ogg_demux_seek (ogg, bisect);
3219 endtime = granuletime;
3228 GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
3229 gst_ogg_demux_seek (ogg, best);
3237 GST_DEBUG_OBJECT (ogg, "got a seek error");
3243 do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
3244 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
3245 gint64 * p_offset, gint64 * p_timestamp)
3248 guint64 timestamp, offset;
3249 guint64 r_timestamp, r_offset;
3250 gboolean result = FALSE;
3252 target -= begintime;
3257 for (i = 0; i < chain->streams->len; i++) {
3258 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3261 if (gst_ogg_map_search_index (&pad->map, TRUE, ×tamp, &offset)) {
3262 GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
3265 if (r_offset == -1 || offset < r_offset) {
3267 r_timestamp = timestamp;
3274 *p_timestamp = r_timestamp;
3276 *p_offset = r_offset;
3282 * do seek to time @position, return FALSE or chain and TRUE
3285 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
3286 gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
3289 GstOggChain *chain = NULL;
3291 gint64 begintime, endtime;
3292 gint64 target, keytarget;
3299 gboolean found_keyframe = FALSE;
3300 GstClockTime ts, first_ts = GST_CLOCK_TIME_NONE;
3302 position = segment->position;
3304 /* first find the chain to search in */
3305 total = ogg->total_time;
3306 if (ogg->chains->len == 0)
3309 for (i = ogg->chains->len - 1; i >= 0; i--) {
3310 chain = g_array_index (ogg->chains, GstOggChain *, i);
3311 total -= chain->total_time;
3312 if (position >= total)
3316 /* first step, locate page containing the required data */
3317 begin = chain->offset;
3318 end = chain->end_offset;
3319 begintime = chain->begin_time;
3320 endtime = begintime + chain->total_time;
3321 target = position - total + begintime;
3323 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
3327 /* second step: find pages for all relevant streams. We use the
3328 * keyframe_granule to keep track of which ones we saw. If we have
3329 * seen a page for each stream we can calculate the positions of
3331 * Relevant streams are defined as those streams which are not
3332 * Skeleton (which only has header pages). Discontinuous streams
3333 * such as Kate and CMML are currently excluded, as they could
3334 * cause performance issues if there are few pages in the area.
3335 * TODO: We might want to include them on a flag, if we want to
3336 * not miss a subtitle (Kate has repeat packets for this purpose,
3337 * but a stream does not have to use them). */
3338 pending = chain->streams->len;
3339 for (i = 0; i < chain->streams->len; i++) {
3340 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3342 GST_WARNING_OBJECT (ogg, "No pad at index %d", i);
3346 if (pad->map.is_skeleton) {
3347 GST_DEBUG_OBJECT (ogg, "Not finding pages for Skeleton stream %08x",
3352 if (pad->map.is_sparse) {
3353 GST_DEBUG_OBJECT (ogg, "Not finding pages for sparse stream %08x (%s)",
3354 pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map));
3359 GST_DEBUG_OBJECT (ogg, "find keyframes for %d/%d streams", pending,
3360 chain->streams->len);
3362 /* figure out where the keyframes are */
3369 GstClockTime keyframe_time, granule_time;
3371 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
3372 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
3374 if (ret == GST_FLOW_LIMIT) {
3375 GST_LOG_OBJECT (ogg, "reached limit");
3377 } else if (ret != GST_FLOW_OK)
3380 /* get the stream */
3381 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
3385 if (pad->map.is_skeleton || pad->map.is_sparse)
3388 granulepos = ogg_page_granulepos (&og);
3389 if (granulepos == -1 || granulepos == 0) {
3390 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
3394 /* We have a valid granpos, and we bail out when the time since the
3395 first seen time to the time corresponding to this granpos is larger
3396 then a threshold, to guard against some streams having large holes
3397 (eg, a stream ending early, which would cause seeking after that
3398 to fill up a queue for streams still active). */
3399 ts = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granulepos);
3400 if (GST_CLOCK_TIME_IS_VALID (ts)) {
3401 if (first_ts == GST_CLOCK_TIME_NONE) {
3402 GST_WARNING_OBJECT (pad, "Locking on pts %" GST_TIME_FORMAT,
3403 GST_TIME_ARGS (ts));
3406 if (ts - first_ts > SEEK_GIVE_UP_THRESHOLD) {
3407 GST_WARNING_OBJECT (pad,
3408 "No data found for %" GST_TIME_FORMAT ", giving up",
3409 GST_TIME_ARGS (SEEK_GIVE_UP_THRESHOLD));
3410 found_keyframe = FALSE;
3416 /* in reverse we want to go past the page with the lower timestamp */
3417 if (segment->rate < 0.0) {
3418 /* get time for this pad */
3419 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
3422 /* Convert to stream time */
3423 granule_time -= pad->start_time;
3424 granule_time += chain->begin_time;
3426 GST_LOG_OBJECT (ogg,
3427 "looking at page with time %" GST_TIME_FORMAT ", target %"
3428 GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
3429 GST_TIME_ARGS (target));
3430 if (granule_time < target)
3434 /* we've seen this pad before */
3435 if (pad->keyframe_granule != -1)
3438 /* convert granule of this pad to the granule of the keyframe */
3439 pad->keyframe_granule =
3440 gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
3441 GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
3442 pad->keyframe_granule);
3444 /* get time of the keyframe */
3446 gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
3447 GST_LOG_OBJECT (ogg,
3448 "stream %08x keyframe granule PTS %" GST_TIME_FORMAT
3449 " target %" GST_TIME_FORMAT,
3450 pad->map.serialno, GST_TIME_ARGS (keyframe_time),
3451 GST_TIME_ARGS (keytarget));
3453 /* collect smallest value */
3454 if (keyframe_time != -1) {
3455 keyframe_time -= pad->start_time;
3456 keyframe_time += begintime;
3457 if (keyframe_time < keytarget) {
3458 serialno = pad->map.serialno;
3459 keytarget = keyframe_time;
3460 found_keyframe = TRUE;
3461 GST_LOG_OBJECT (ogg, "storing keytarget %" GST_TIME_FORMAT,
3462 GST_TIME_ARGS (keytarget));
3472 /* for negative rates we will get to the keyframe backwards */
3473 if (segment->rate < 0.0)
3476 /* No keyframe found, no need to bisect again, keytarget == target here */
3477 if (!found_keyframe)
3480 if (keytarget != target) {
3481 GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
3482 GST_TIME_ARGS (keytarget));
3484 /* last step, seek to the location of the keyframe */
3485 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
3486 keytarget, &best, TRUE, serialno))
3489 /* seek back to previous position */
3490 GST_LOG_OBJECT (ogg, "keyframe on target");
3491 gst_ogg_demux_seek (ogg, best);
3496 if (segment->rate > 0.0)
3497 segment->time = keytarget;
3498 segment->position = keytarget - begintime;
3507 GST_DEBUG_OBJECT (ogg, "no chains");
3512 GST_DEBUG_OBJECT (ogg, "got a seek error");
3517 /* does not take ownership of the event */
3519 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
3521 GstOggChain *chain = NULL;
3523 gboolean accurate, keyframe;
3527 GstSeekType start_type, stop_type;
3533 GST_DEBUG_OBJECT (ogg, "seek with event");
3535 gst_event_parse_seek (event, &rate, &format, &flags,
3536 &start_type, &start, &stop_type, &stop);
3538 /* we can only seek on time */
3539 if (format != GST_FORMAT_TIME) {
3540 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3543 seqnum = gst_event_get_seqnum (event);
3545 GST_DEBUG_OBJECT (ogg, "seek without event");
3549 seqnum = gst_util_seqnum_next ();
3552 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
3554 accurate = flags & GST_SEEK_FLAG_ACCURATE;
3555 keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
3557 gst_pad_pause_task (ogg->sinkpad);
3559 /* now grab the stream lock so that streaming cannot continue, for
3560 * non flushing seeks when the element is in PAUSED this could block
3562 GST_PAD_STREAM_LOCK (ogg->sinkpad);
3565 gst_segment_do_seek (&ogg->segment, rate, format, flags,
3566 start_type, start, stop_type, stop, &update);
3569 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
3570 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
3571 GST_TIME_ARGS (ogg->segment.stop));
3576 /* reset all ogg streams now, need to do this from within the lock to
3577 * make sure the streaming thread is not messing with the stream */
3578 for (i = 0; i < ogg->chains->len; i++) {
3579 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3581 gst_ogg_chain_reset (chain);
3585 /* for reverse we will already seek accurately */
3586 res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
3588 /* seek failed, make sure we continue the current chain */
3590 GST_DEBUG_OBJECT (ogg, "seek failed");
3591 chain = ogg->current_chain;
3593 GST_DEBUG_OBJECT (ogg, "seek success");
3599 /* now we have a new position, prepare for streaming again */
3604 gint64 position, begin_time;
3607 /* we need this to see how far inside the chain we need to start */
3608 if (chain->begin_time != GST_CLOCK_TIME_NONE)
3609 begin_time = chain->begin_time;
3613 /* segment.start gives the start over all chains, we calculate the amount
3614 * of time into this chain we need to start */
3615 start = ogg->segment.start - begin_time;
3616 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3617 start += chain->segment_start;
3619 if ((stop = ogg->segment.stop) == -1)
3620 stop = ogg->segment.duration;
3622 /* segment.stop gives the stop time over all chains, calculate the amount of
3623 * time we need to stop in this chain */
3625 if (stop > begin_time)
3629 stop += chain->segment_start;
3630 /* we must stop when this chain ends and switch to the next chain to play
3631 * the remainder of the segment. */
3632 stop = MIN (stop, chain->segment_stop);
3635 position = ogg->segment.position;
3636 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3637 position += chain->segment_start;
3639 gst_segment_copy_into (&ogg->segment, &segment);
3641 /* create the segment event we are going to send out */
3642 if (ogg->segment.rate >= 0.0) {
3643 segment.start = position;
3644 segment.stop = stop;
3646 segment.start = start;
3647 segment.stop = position;
3649 event = gst_event_new_segment (&segment);
3650 gst_event_set_seqnum (event, seqnum);
3652 if (chain != ogg->current_chain) {
3653 /* switch to different chain, send segment on new chain */
3654 gst_ogg_demux_activate_chain (ogg, chain, event);
3656 /* mark discont and send segment on current chain */
3657 gst_ogg_chain_mark_discont (chain);
3658 /* This event should be sent from the streaming thread (sink pad task) */
3659 if (ogg->newsegment)
3660 gst_event_unref (ogg->newsegment);
3661 ogg->newsegment = event;
3664 /* notify start of new segment */
3665 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3666 GstMessage *message;
3668 message = gst_message_new_segment_start (GST_OBJECT (ogg),
3669 GST_FORMAT_TIME, ogg->segment.position);
3670 gst_message_set_seqnum (message, seqnum);
3672 gst_element_post_message (GST_ELEMENT (ogg), message);
3675 ogg->seqnum = seqnum;
3676 /* restart our task since it might have been stopped when we did the
3678 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3679 ogg->sinkpad, NULL);
3682 /* streaming can continue now */
3683 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3687 gst_event_unref (event);
3693 GST_DEBUG_OBJECT (ogg, "seek failed");
3699 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
3700 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3707 gst_ogg_demux_get_duration_push (GstOggDemux * ogg, int flags)
3709 /* In push mode, we get to the end of the stream to get the duration */
3713 /* A full Ogg page can be almost 64 KB. There's no guarantee that there'll be a
3714 granpos there, but it's fairly likely */
3715 position = ogg->push_byte_length - DURATION_CHUNK_OFFSET;
3719 GST_DEBUG_OBJECT (ogg,
3720 "Getting duration, seeking near the end, to %" G_GINT64_FORMAT, position);
3721 ogg->push_state = PUSH_DURATION;
3722 /* do not read the last byte */
3723 sevent = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET,
3724 position, GST_SEEK_TYPE_SET, ogg->push_byte_length - 1);
3725 gst_event_replace (&ogg->seek_event, sevent);
3726 gst_event_unref (sevent);
3727 g_mutex_lock (&ogg->seek_event_mutex);
3728 g_cond_broadcast (&ogg->seek_event_cond);
3729 g_mutex_unlock (&ogg->seek_event_mutex);
3734 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3737 if (ogg->push_byte_length < 0) {
3740 GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3741 if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3745 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &length);
3746 if (res && length > 0) {
3747 ogg->push_byte_length = length;
3748 GST_DEBUG_OBJECT (ogg,
3749 "File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
3751 GST_DEBUG_OBJECT (ogg, "File byte length unknown, assuming live");
3752 ogg->push_disable_seeking = TRUE;
3753 gst_object_unref (peer);
3756 res = gst_pad_query_duration (peer, GST_FORMAT_TIME, &length);
3757 gst_object_unref (peer);
3758 if (res && length >= 0) {
3759 ogg->push_time_length = length;
3760 GST_DEBUG_OBJECT (ogg, "File time length %" GST_TIME_FORMAT,
3761 GST_TIME_ARGS (ogg->push_time_length));
3762 } else if (!ogg->push_disable_seeking) {
3765 res = gst_ogg_demux_get_duration_push (ogg, flags);
3767 GST_DEBUG_OBJECT (ogg,
3768 "File time length unknown, trying to determine");
3769 ogg->push_mode_seek_delayed_event = NULL;
3771 GST_DEBUG_OBJECT (ogg,
3772 "Let me intercept this innocent looking seek request");
3773 ogg->push_mode_seek_delayed_event = gst_event_copy (event);
3784 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3787 gboolean res = TRUE;
3791 GstSeekType start_type, stop_type;
3795 gint64 best, best_time;
3798 GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3800 gst_event_parse_seek (event, &rate, &format, &flags,
3801 &start_type, &start, &stop_type, &stop);
3803 if (format != GST_FORMAT_TIME) {
3804 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3808 if (start_type != GST_SEEK_TYPE_SET) {
3809 GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3813 /* If stop is unset, make sure it is -1, as this value will be tested
3814 later to check whether stop is set or not */
3815 if (stop_type == GST_SEEK_TYPE_NONE)
3818 GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3819 GST_TIME_ARGS (start));
3821 chain = ogg->current_chain;
3823 GST_WARNING_OBJECT (ogg, "No chain to seek on");
3827 /* start accessing push_* members */
3828 GST_PUSH_LOCK (ogg);
3830 /* not if we disabled seeking (chained streams) */
3831 if (ogg->push_disable_seeking) {
3832 GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3836 /* not when we're trying to work out duration */
3837 if (ogg->push_state == PUSH_DURATION) {
3838 GST_DEBUG_OBJECT (ogg, "Busy working out duration, try again later");
3842 /* actually, not if we're doing any seeking already */
3843 if (ogg->push_state != PUSH_PLAYING) {
3844 GST_DEBUG_OBJECT (ogg, "Already doing some seeking, try again later");
3848 /* on the first seek, get length if we can */
3849 if (!gst_ogg_demux_check_duration_push (ogg, flags, event)) {
3850 GST_PUSH_UNLOCK (ogg);
3854 if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
3855 /* the index gave some result */
3856 GST_DEBUG_OBJECT (ogg,
3857 "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
3860 if (ogg->push_time_length > 0) {
3861 /* if we know the time length, we know the full segment bitrate */
3862 GST_DEBUG_OBJECT (ogg, "Using real file bitrate");
3864 gst_util_uint64_scale (ogg->push_byte_length, 8 * GST_SECOND,
3865 ogg->push_time_length);
3866 } else if (ogg->push_time_offset > 0) {
3867 /* get a first approximation using known bitrate to the current position */
3868 GST_DEBUG_OBJECT (ogg, "Using file bitrate so far");
3870 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
3871 ogg->push_time_offset);
3872 } else if (ogg->bitrate > 0) {
3873 /* nominal bitrate is better than nothing, even if it lies often */
3874 GST_DEBUG_OBJECT (ogg, "Using nominal bitrate");
3875 bitrate = ogg->bitrate;
3878 GST_DEBUG_OBJECT (ogg,
3879 "At stream start, and no nominal bitrate, using some random magic "
3881 /* the bisection, once started, should give us a better approximation */
3884 best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3887 /* offset by typical page length, and ensure our best guess is within
3888 reasonable bounds */
3889 best -= ogg->chunk_size;
3892 if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3893 best = ogg->push_byte_length - 1;
3895 /* set up bisection search */
3896 ogg->push_offset0 = 0;
3897 ogg->push_offset1 = ogg->push_byte_length - 1;
3898 ogg->push_time0 = ogg->push_start_time;
3899 ogg->push_time1 = ogg->push_time_length;
3900 ogg->seqnum = gst_event_get_seqnum (event);
3901 ogg->push_seek_time_target = start;
3902 ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
3903 ogg->push_seek_time_original_target = start;
3904 ogg->push_seek_time_original_stop = stop;
3905 ogg->push_state = PUSH_BISECT1;
3906 ogg->seek_secant = FALSE;
3907 ogg->seek_undershot = FALSE;
3909 if (flags & GST_SEEK_FLAG_FLUSH) {
3910 /* reset pad push mode seeking state */
3911 for (i = 0; i < chain->streams->len; i++) {
3912 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3913 pad->push_kf_time = GST_CLOCK_TIME_NONE;
3914 pad->push_sync_time = GST_CLOCK_TIME_NONE;
3918 GST_DEBUG_OBJECT (ogg,
3919 "Setting up bisection search for %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
3920 " (time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", ogg->push_offset0,
3921 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
3922 GST_TIME_ARGS (ogg->push_time1));
3923 GST_DEBUG_OBJECT (ogg,
3924 "Target time is %" GST_TIME_FORMAT ", best first guess is %"
3925 G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target), best);
3927 ogg->push_seek_rate = rate;
3928 ogg->push_seek_flags = flags;
3929 ogg->push_mode_seek_delayed_event = NULL;
3930 ogg->push_bisection_steps[0] = 1;
3931 ogg->push_bisection_steps[1] = 0;
3932 sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
3933 start_type, best, GST_SEEK_TYPE_NONE, -1);
3934 gst_event_set_seqnum (sevent, gst_event_get_seqnum (event));
3936 gst_event_replace (&ogg->seek_event, sevent);
3937 gst_event_unref (sevent);
3938 GST_PUSH_UNLOCK (ogg);
3939 g_mutex_lock (&ogg->seek_event_mutex);
3940 g_cond_broadcast (&ogg->seek_event_cond);
3941 g_mutex_unlock (&ogg->seek_event_mutex);
3948 GST_DEBUG_OBJECT (ogg, "seek failed");
3953 GST_PUSH_UNLOCK (ogg);
3958 gst_ogg_demux_setup_seek_pull (GstOggDemux * ogg, GstEvent * event)
3963 guint32 seqnum = gst_event_get_seqnum (event);
3965 GST_DEBUG_OBJECT (ogg, "Scheduling seek: %" GST_PTR_FORMAT, event);
3966 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
3968 flush = flags & GST_SEEK_FLAG_FLUSH;
3970 /* first step is to unlock the streaming thread if it is
3971 * blocked in a chain call, we do this by starting the flush. because
3972 * we cannot yet hold any streaming lock, we have to protect the chains
3973 * with their own lock. */
3977 tevent = gst_event_new_flush_start ();
3978 gst_event_set_seqnum (tevent, seqnum);
3980 gst_event_ref (tevent);
3981 gst_pad_push_event (ogg->sinkpad, tevent);
3983 GST_CHAIN_LOCK (ogg);
3984 for (i = 0; i < ogg->chains->len; i++) {
3985 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3988 for (j = 0; j < chain->streams->len; j++) {
3989 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
3991 gst_event_ref (tevent);
3992 gst_pad_push_event (GST_PAD (pad), tevent);
3995 GST_CHAIN_UNLOCK (ogg);
3997 gst_event_unref (tevent);
4000 gst_pad_pause_task (ogg->sinkpad);
4002 /* now grab the stream lock so that streaming cannot continue, for
4003 * non flushing seeks when the element is in PAUSED this could block
4005 GST_PAD_STREAM_LOCK (ogg->sinkpad);
4007 /* we need to stop flushing on the sinkpad as we're going to use it
4008 * next. We can do this as we have the STREAM lock now. */
4010 tevent = gst_event_new_flush_stop (TRUE);
4011 gst_event_set_seqnum (tevent, seqnum);
4012 gst_pad_push_event (ogg->sinkpad, gst_event_ref (tevent));
4013 gst_ogg_demux_send_event (ogg, tevent);
4016 gst_event_replace (&ogg->seek_event, event);
4017 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
4018 ogg->sinkpad, NULL);
4019 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
4025 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
4029 if (ogg->pullmode) {
4030 res = gst_ogg_demux_setup_seek_pull (ogg, event);
4032 res = gst_ogg_demux_perform_seek_push (ogg, event);
4038 /* finds each bitstream link one at a time using a bisection search
4039 * (has to begin by knowing the offset of the lb's initial page).
4040 * Recurses for each link so it can alloc the link storage after
4041 * finding them all, then unroll and fill the cache at the same time
4043 static GstFlowReturn
4044 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
4045 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
4047 gint64 endsearched = end;
4052 GstOggChain *nextchain;
4054 GST_LOG_OBJECT (ogg,
4055 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
4056 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
4058 /* the below guards against garbage separating the last and
4059 * first pages of two links. */
4060 while (searched < endsearched) {
4063 if (endsearched - searched < ogg->chunk_size) {
4066 bisect = (searched + endsearched) / 2;
4069 gst_ogg_demux_seek (ogg, bisect);
4070 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
4072 if (ret == GST_FLOW_EOS) {
4073 endsearched = bisect;
4074 } else if (ret == GST_FLOW_OK) {
4075 guint32 serial = ogg_page_serialno (&og);
4077 if (!gst_ogg_chain_has_stream (chain, serial)) {
4078 endsearched = bisect;
4081 searched = offset + og.header_len + og.body_len;
4087 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
4089 chain->end_offset = searched;
4090 ret = gst_ogg_demux_read_end_chain (ogg, chain);
4091 if (ret != GST_FLOW_OK)
4094 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
4096 gst_ogg_demux_seek (ogg, next);
4097 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
4098 if (ret == GST_FLOW_EOS) {
4101 GST_LOG_OBJECT (ogg, "no next chain");
4102 } else if (ret != GST_FLOW_OK)
4105 if (searched < end && nextchain != NULL) {
4106 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
4107 end, nextchain, m + 1);
4108 if (ret != GST_FLOW_OK)
4111 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
4113 g_array_insert_val (ogg->chains, 0, chain);
4119 /* read a chain from the ogg file. This code will
4120 * read all BOS pages and will create and return a GstOggChain
4121 * structure with the results.
4123 * This function will also read N pages from each stream in the
4124 * chain and submit them to the internal ogg stream parser/mapper
4125 * until we know the timestamp of the first page in the chain.
4127 static GstFlowReturn
4128 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
4131 GstOggChain *chain = NULL;
4132 gint64 offset = ogg->offset;
4137 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
4139 /* first read the BOS pages, detect the stream types, create the internal
4140 * stream mappers, send data to them. */
4145 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
4146 if (ret != GST_FLOW_OK) {
4147 if (ret == GST_FLOW_EOS) {
4148 GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
4150 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
4154 if (!ogg_page_bos (&og)) {
4155 GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
4156 /* if we did not find a chain yet, assume this is a bogus stream and
4159 GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
4165 if (chain == NULL) {
4166 chain = gst_ogg_chain_new (ogg);
4167 chain->offset = offset;
4170 serial = ogg_page_serialno (&og);
4171 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
4172 GST_WARNING_OBJECT (ogg,
4173 "found serial %08x BOS page twice, ignoring", serial);
4177 pad = gst_ogg_chain_new_stream (chain, serial);
4178 gst_ogg_pad_submit_page (pad, &og);
4181 if (ret != GST_FLOW_OK || chain == NULL) {
4182 if (ret == GST_FLOW_OK) {
4183 GST_WARNING_OBJECT (ogg, "no chain was found");
4184 ret = GST_FLOW_ERROR;
4185 } else if (ret != GST_FLOW_EOS) {
4186 GST_WARNING_OBJECT (ogg, "failed to read chain");
4188 GST_DEBUG_OBJECT (ogg, "done reading chains");
4191 gst_ogg_chain_free (chain);
4198 chain->have_bos = TRUE;
4199 GST_INFO_OBJECT (ogg, "read bos pages, ");
4201 /* now read pages until each ogg stream mapper has figured out the
4202 * timestamp of the first packet in the chain */
4204 /* save the offset to the first non bos page in the chain: if searching for
4205 * pad->first_time we read past the end of the chain, we'll seek back to this
4208 offset = ogg->offset;
4213 gboolean known_serial = FALSE;
4216 serial = ogg_page_serialno (&og);
4218 for (i = 0; i < chain->streams->len; i++) {
4219 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4221 GST_LOG_OBJECT (ogg,
4222 "serial %08x time %" GST_TIME_FORMAT,
4223 pad->map.serialno, GST_TIME_ARGS (pad->start_time));
4225 if (pad->map.serialno == serial) {
4226 known_serial = TRUE;
4228 /* submit the page now, this will fill in the start_time when the
4229 * internal stream mapper finds it */
4230 gst_ogg_pad_submit_page (pad, &og);
4232 if (!pad->map.is_skeleton && pad->start_time == -1
4233 && ogg_page_eos (&og)) {
4234 /* got EOS on a pad before we could find its start_time.
4235 * We have no chance of finding a start_time for every pad so
4236 * stop searching for the other start_time(s).
4242 /* the timestamp will be filled in when we submit the pages */
4243 if (!pad->map.is_sparse)
4244 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
4246 GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
4249 /* we read a page not belonging to the current chain: seek back to the
4250 * beginning of the chain
4252 if (!known_serial) {
4253 GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
4254 gst_ogg_demux_seek (ogg, offset);
4259 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
4260 if (ret != GST_FLOW_OK)
4264 GST_LOG_OBJECT (ogg, "done reading chain");
4272 /* read the last pages from the ogg stream to get the final
4275 static GstFlowReturn
4276 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
4278 gint64 begin = chain->end_offset;
4280 gint64 last_granule = -1;
4281 GstOggPad *last_pad = NULL;
4283 gboolean done = FALSE;
4288 begin -= ogg->chunk_size;
4292 gst_ogg_demux_seek (ogg, begin);
4294 /* now continue reading until we run out of data, if we find a page
4295 * start, we save it. It might not be the final page as there could be
4296 * another page after this one. */
4297 while (ogg->offset < end) {
4298 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
4300 if (ret == GST_FLOW_LIMIT)
4302 if (ret != GST_FLOW_OK)
4305 for (i = 0; i < chain->streams->len; i++) {
4306 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4308 if (pad->map.is_skeleton)
4311 if (pad->map.serialno == ogg_page_serialno (&og)) {
4312 gint64 granulepos = ogg_page_granulepos (&og);
4314 if (granulepos != -1) {
4315 last_granule = granulepos;
4326 chain->segment_stop =
4327 gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
4330 chain->segment_stop = GST_CLOCK_TIME_NONE;
4333 GST_INFO ("segment stop %" G_GUINT64_FORMAT ", for last granule %"
4334 G_GUINT64_FORMAT, chain->segment_stop, last_granule);
4339 /* find a pad with a given serial number
4342 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
4347 /* first look in building chain if any */
4348 if (ogg->building_chain) {
4349 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
4354 /* then look in current chain if any */
4355 if (ogg->current_chain) {
4356 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
4361 for (i = 0; i < ogg->chains->len; i++) {
4362 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4364 pad = gst_ogg_chain_get_stream (chain, serialno);
4371 /* find a chain with a given serial number
4373 static GstOggChain *
4374 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
4378 pad = gst_ogg_demux_find_pad (ogg, serialno);
4385 /* returns TRUE if all streams have valid start time */
4387 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
4389 gboolean res = TRUE;
4391 chain->total_time = GST_CLOCK_TIME_NONE;
4392 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
4394 /* see if we have a start time on all streams */
4395 chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
4397 if (chain->segment_start == G_MAXUINT64) {
4398 /* not yet, stream some more data */
4400 } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
4401 /* we can calculate a total time */
4402 chain->total_time = chain->segment_stop - chain->segment_start;
4405 GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
4407 GST_DEBUG_OBJECT (ogg, "return %d", res);
4413 gst_ogg_demux_collect_info (GstOggDemux * ogg)
4417 /* collect all info */
4418 ogg->total_time = 0;
4420 for (i = 0; i < ogg->chains->len; i++) {
4421 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4423 chain->begin_time = ogg->total_time;
4425 gst_ogg_demux_collect_chain_info (ogg, chain);
4427 ogg->total_time += chain->total_time;
4429 ogg->segment.duration = ogg->total_time;
4432 /* find all the chains in the ogg file, this reads the first and
4433 * last page of the ogg stream, if they match then the ogg file has
4434 * just one chain, else we do a binary search for all chains.
4436 static GstFlowReturn
4437 gst_ogg_demux_find_chains (GstOggDemux * ogg)
4446 /* get peer to figure out length */
4447 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
4450 /* find length to read last page, we store this for later use. */
4451 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &ogg->length);
4452 gst_object_unref (peer);
4453 if (!res || ogg->length <= 0)
4456 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
4458 /* read chain from offset 0, this is the first chain of the
4460 gst_ogg_demux_seek (ogg, 0);
4461 ret = gst_ogg_demux_read_chain (ogg, &chain);
4462 if (ret != GST_FLOW_OK) {
4463 if (ret == GST_FLOW_FLUSHING)
4466 goto no_first_chain;
4469 /* read page from end offset, we use this page to check if its serial
4470 * number is contained in the first chain. If this is the case then
4471 * this ogg is not a chained ogg and we can skip the scanning. */
4472 gst_ogg_demux_seek (ogg, ogg->length);
4473 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
4474 if (ret != GST_FLOW_OK)
4477 serialno = ogg_page_serialno (&og);
4479 if (!gst_ogg_chain_has_stream (chain, serialno)) {
4480 /* the last page is not in the first stream, this means we should
4481 * find all the chains in this chained ogg. */
4483 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
4486 /* we still call this function here but with an empty range so that
4487 * we can reuse the setup code in this routine. */
4489 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
4490 ogg->length, chain, 0);
4492 if (ret != GST_FLOW_OK)
4495 /* all fine, collect and print */
4496 gst_ogg_demux_collect_info (ogg);
4498 /* dump our chains and streams */
4499 gst_ogg_print (ogg);
4504 /*** error cases ***/
4507 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
4508 return GST_FLOW_NOT_LINKED;
4512 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
4513 return GST_FLOW_NOT_SUPPORTED;
4517 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
4518 return GST_FLOW_ERROR;
4522 GST_DEBUG_OBJECT (ogg, "can't get last page");
4524 gst_ogg_chain_free (chain);
4529 GST_DEBUG_OBJECT (ogg, "Flushing, can't read chain");
4530 return GST_FLOW_FLUSHING;
4535 gst_ogg_demux_update_chunk_size (GstOggDemux * ogg, ogg_page * page)
4537 long size = page->header_len + page->body_len;
4538 long chunk_size = size * 2;
4539 if (chunk_size > ogg->chunk_size) {
4540 GST_LOG_OBJECT (ogg, "Updating chunk size to %ld", chunk_size);
4541 ogg->chunk_size = chunk_size;
4545 static GstFlowReturn
4546 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page, gboolean discont)
4551 GstFlowReturn result = GST_FLOW_OK;
4553 serialno = ogg_page_serialno (page);
4554 granule = ogg_page_granulepos (page);
4556 gst_ogg_demux_update_chunk_size (ogg, page);
4558 GST_LOG_OBJECT (ogg,
4559 "processing ogg page (serial %08x, "
4560 "pageno %ld, granulepos %" G_GINT64_FORMAT ", bos %d)", serialno,
4561 ogg_page_pageno (page), granule, ogg_page_bos (page));
4563 if (ogg_page_bos (page)) {
4567 /* see if we know about the chain already */
4568 chain = gst_ogg_demux_find_chain (ogg, serialno);
4574 if (chain->segment_start != GST_CLOCK_TIME_NONE)
4575 start = chain->segment_start;
4577 /* create the newsegment event we are going to send out */
4578 gst_segment_copy_into (&ogg->segment, &segment);
4579 segment.start = start;
4580 segment.stop = chain->segment_stop;
4581 segment.time = chain->begin_time;
4582 segment.base += chain->begin_time;
4583 event = gst_event_new_segment (&segment);
4584 gst_event_set_seqnum (event, ogg->seqnum);
4586 GST_DEBUG_OBJECT (ogg,
4587 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
4588 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
4589 GST_TIME_ARGS (chain->segment_stop),
4590 GST_TIME_ARGS (chain->begin_time));
4592 /* activate it as it means we have a non-header, this will also deactivate
4593 * the currently running chain. */
4594 gst_ogg_demux_activate_chain (ogg, chain, event);
4595 pad = gst_ogg_demux_find_pad (ogg, serialno);
4597 GstClockTime chain_time;
4598 gint64 current_time;
4600 /* this can only happen in push mode */
4604 current_time = ogg->segment.position;
4606 /* time of new chain is current time */
4607 chain_time = current_time;
4609 if (ogg->building_chain == NULL) {
4610 GstOggChain *newchain;
4612 newchain = gst_ogg_chain_new (ogg);
4613 newchain->offset = 0;
4614 /* set new chain begin time aligned with end time of old chain */
4615 newchain->begin_time = chain_time;
4616 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
4617 GST_TIME_ARGS (chain_time));
4619 /* and this is the one we are building now */
4620 ogg->building_chain = newchain;
4622 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
4625 pad = gst_ogg_demux_find_pad (ogg, serialno);
4628 /* Reset granule interpolation if chaining in reverse (discont = TRUE) */
4630 pad->current_granule = -1;
4632 result = gst_ogg_pad_submit_page (pad, page);
4634 GST_PUSH_LOCK (ogg);
4635 if (!ogg->pullmode && !ogg->push_disable_seeking) {
4636 /* no pad while probing for duration, we must have a chained stream,
4637 and we don't support them, so back off */
4638 GST_INFO_OBJECT (ogg, "We seem to have a chained stream, we won't seek");
4639 if (ogg->push_state == PUSH_DURATION) {
4642 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
4643 /* Call to function above unlocks, relock */
4644 GST_PUSH_LOCK (ogg);
4645 if (res != GST_FLOW_OK)
4649 /* only once we seeked back */
4650 ogg->push_disable_seeking = TRUE;
4652 GST_PUSH_UNLOCK (ogg);
4653 /* no pad. This means an ogg page without bos has been seen for this
4654 * serialno. we just ignore it but post a warning... */
4655 GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
4656 (NULL), ("unknown ogg pad for serial %08x detected", serialno));
4659 GST_PUSH_UNLOCK (ogg);
4666 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
4667 (NULL), ("unknown ogg chain for serial %08x detected", serialno));
4668 return GST_FLOW_ERROR;
4672 /* streaming mode, receive a buffer, parse it, create pads for
4673 * the serialno, submit pages and packets to the oggpads
4675 static GstFlowReturn
4676 gst_ogg_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
4680 GstFlowReturn result = GST_FLOW_OK;
4683 ogg = GST_OGG_DEMUX (parent);
4685 GST_PUSH_LOCK (ogg);
4686 drop = (ogg->seek_event_drop_till > 0);
4687 GST_PUSH_UNLOCK (ogg);
4689 GST_DEBUG_OBJECT (ogg, "Dropping buffer because we have a pending seek");
4690 gst_buffer_unref (buffer);
4694 GST_DEBUG_OBJECT (ogg, "enter");
4695 result = gst_ogg_demux_submit_buffer (ogg, buffer);
4697 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
4700 while (result == GST_FLOW_OK) {
4703 ret = ogg_sync_pageout (&ogg->sync, &page);
4705 /* need more data */
4708 /* discontinuity in the pages */
4709 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
4711 result = gst_ogg_demux_handle_page (ogg, &page, FALSE);
4713 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
4717 if (ret == 0 || result == GST_FLOW_OK) {
4718 gst_ogg_demux_sync_streams (ogg);
4720 GST_DEBUG_OBJECT (ogg, "leave with %d", result);
4725 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
4727 GstOggChain *chain = ogg->current_chain;
4728 gboolean event_sent = FALSE;
4729 gboolean res = TRUE;
4732 chain = ogg->building_chain;
4737 for (i = 0; i < chain->streams->len; i++) {
4738 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4740 gst_event_ref (event);
4741 GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
4742 res &= gst_pad_push_event (GST_PAD (pad), event);
4748 gst_event_unref (event);
4750 if (!event_sent && GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
4751 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4752 ("EOS before finding a chain"));
4758 static GstFlowReturn
4759 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
4762 /* store the value */
4763 pad->last_ret = ret;
4764 pad->is_eos = (ret == GST_FLOW_EOS);
4766 return gst_flow_combiner_update_pad_flow (ogg->flowcombiner,
4767 GST_PAD_CAST (pad), ret);
4770 static GstFlowReturn
4771 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4774 GstBuffer *buffer = NULL;
4776 if (ogg->offset == ogg->length) {
4777 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4778 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
4783 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4785 gst_pad_pull_range (ogg->sinkpad, ogg->offset, ogg->chunk_size, &buffer);
4786 if (ret != GST_FLOW_OK) {
4787 GST_LOG_OBJECT (ogg, "Failed pull_range");
4791 ogg->offset += gst_buffer_get_size (buffer);
4793 if (G_UNLIKELY (ogg->newsegment)) {
4794 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4795 ogg->newsegment = NULL;
4798 ret = gst_ogg_demux_chain (ogg->sinkpad, GST_OBJECT_CAST (ogg), buffer);
4799 if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS) {
4800 GST_LOG_OBJECT (ogg, "Failed demux_chain");
4809 * We read the pages backwards and send the packets forwards. The first packet
4810 * in the page will be pushed with the DISCONT flag set.
4812 * Special care has to be taken for continued pages, which we can only decode
4813 * when we have the previous page(s).
4815 static GstFlowReturn
4816 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4822 if (ogg->offset == 0) {
4823 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4824 " == 0", ogg->offset);
4829 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
4830 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
4831 if (ret != GST_FLOW_OK)
4834 ogg->offset = offset;
4836 if (G_UNLIKELY (ogg->newsegment)) {
4837 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4838 ogg->newsegment = NULL;
4841 GST_LOG_OBJECT (ogg, "Handling page at offset %" G_GINT64_FORMAT,
4843 ret = gst_ogg_demux_handle_page (ogg, &page, TRUE);
4850 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4856 chain = ogg->current_chain;
4857 cur = ogg->segment.position;
4858 if (chain == NULL || cur == -1)
4861 for (i = 0; i < chain->streams->len; i++) {
4862 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4864 /* Theoretically, we should be doing this for all streams, so we're doing
4865 * it, but it might break things break things for wrongly-muxed streams
4866 * (like we used to produce once) */
4867 if ( /*stream->map.is_sparse && */ stream->position != GST_CLOCK_TIME_NONE) {
4869 /* Does this stream lag? Random threshold of 2 seconds */
4870 if (GST_CLOCK_DIFF (stream->position, cur) > (2 * GST_SECOND)) {
4871 GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
4872 "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
4873 GST_TIME_ARGS (stream->position), GST_TIME_ARGS (cur));
4875 stream->position = cur;
4877 gst_pad_push_event (GST_PAD_CAST (stream),
4878 gst_event_new_gap (stream->position, cur - stream->position));
4884 /* random access code
4886 * - first find all the chains and streams by scanning the file.
4887 * - then get and chain buffers, just like the streaming case.
4888 * - when seeking, we can use the chain info to perform the seek.
4891 gst_ogg_demux_loop (GstOggPad * pad)
4898 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4899 seek = ogg->seek_event;
4900 ogg->seek_event = NULL;
4902 if (ogg->need_chains) {
4904 /* this is the only place where we write chains and thus need to lock. */
4905 GST_CHAIN_LOCK (ogg);
4906 ret = gst_ogg_demux_find_chains (ogg);
4907 GST_CHAIN_UNLOCK (ogg);
4908 if (ret != GST_FLOW_OK)
4909 goto chain_read_failed;
4911 ogg->need_chains = FALSE;
4913 GST_OBJECT_LOCK (ogg);
4914 ogg->running = TRUE;
4915 GST_OBJECT_UNLOCK (ogg);
4917 /* and seek to configured positions without FLUSH */
4918 res = gst_ogg_demux_perform_seek_pull (ogg, seek);
4923 res = gst_ogg_demux_perform_seek_pull (ogg, seek);
4928 if (ogg->segment.rate >= 0.0)
4929 ret = gst_ogg_demux_loop_forward (ogg);
4931 ret = gst_ogg_demux_loop_reverse (ogg);
4933 if (ret != GST_FLOW_OK)
4936 gst_ogg_demux_sync_streams (ogg);
4942 /* error was posted */
4949 GST_OBJECT_LOCK (pad);
4950 flushing = GST_PAD_IS_FLUSHING (pad);
4951 GST_OBJECT_UNLOCK (pad);
4953 ret = GST_FLOW_FLUSHING;
4955 GST_ELEMENT_FLOW_ERROR (ogg, ret);
4956 ret = GST_FLOW_ERROR;
4962 const gchar *reason = gst_flow_get_name (ret);
4963 GstEvent *event = NULL;
4965 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4966 gst_pad_pause_task (ogg->sinkpad);
4968 if (ret == GST_FLOW_EOS) {
4969 /* perform EOS logic */
4970 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4972 GstMessage *message;
4974 /* for segment playback we need to post when (in stream time)
4975 * we stopped, this is either stop (when set) or the duration. */
4976 if ((stop = ogg->segment.stop) == -1)
4977 stop = ogg->segment.duration;
4979 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
4981 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
4983 gst_message_set_seqnum (message, ogg->seqnum);
4985 gst_element_post_message (GST_ELEMENT (ogg), message);
4987 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
4988 gst_event_set_seqnum (event, ogg->seqnum);
4989 gst_ogg_demux_send_event (ogg, event);
4992 /* normal playback, send EOS to all linked pads */
4993 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
4994 event = gst_event_new_eos ();
4996 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
4997 GST_ELEMENT_FLOW_ERROR (ogg, ret);
4998 event = gst_event_new_eos ();
5001 /* For wrong-state we still want to pause the task and stop
5002 * but no error message or other things are necessary.
5003 * wrong-state is no real error and will be caused by flushing,
5004 * e.g. because of a flushing seek.
5007 /* guard against corrupt/truncated files, where one can hit EOS
5008 before prerolling is done and a chain created. If we have no
5009 chain to send the event to, error out. */
5010 if (ogg->current_chain || ogg->building_chain) {
5011 gst_event_set_seqnum (event, ogg->seqnum);
5012 gst_ogg_demux_send_event (ogg, event);
5014 gst_event_unref (event);
5015 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
5016 ("EOS before finding a chain"));
5023 /* The sink pad task function for push mode.
5024 * It just sends any seek events queued by the streaming thread.
5027 gst_ogg_demux_loop_push (GstOggDemux * ogg)
5029 GstEvent *event = NULL;
5031 g_mutex_lock (&ogg->seek_event_mutex);
5032 /* Inform other threads that we started */
5033 ogg->seek_thread_started = TRUE;
5034 g_cond_broadcast (&ogg->thread_started_cond);
5037 while (!ogg->seek_event_thread_stop) {
5039 while (!ogg->seek_event_thread_stop) {
5040 GST_PUSH_LOCK (ogg);
5041 event = ogg->seek_event;
5042 ogg->seek_event = NULL;
5044 ogg->seek_event_drop_till = gst_event_get_seqnum (event);
5045 GST_PUSH_UNLOCK (ogg);
5050 g_cond_wait (&ogg->seek_event_cond, &ogg->seek_event_mutex);
5053 if (ogg->seek_event_thread_stop) {
5058 g_mutex_unlock (&ogg->seek_event_mutex);
5060 GST_DEBUG_OBJECT (ogg->sinkpad, "Pushing event %" GST_PTR_FORMAT, event);
5061 if (!gst_pad_push_event (ogg->sinkpad, event)) {
5062 GST_WARNING_OBJECT (ogg, "Failed to push event");
5063 GST_PUSH_LOCK (ogg);
5064 if (!ogg->pullmode) {
5065 ogg->push_state = PUSH_PLAYING;
5066 ogg->push_disable_seeking = TRUE;
5068 GST_PUSH_UNLOCK (ogg);
5070 GST_DEBUG_OBJECT (ogg->sinkpad, "Pushed event ok");
5073 g_mutex_lock (&ogg->seek_event_mutex);
5076 g_mutex_unlock (&ogg->seek_event_mutex);
5078 gst_object_unref (ogg);
5083 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
5087 gst_ogg_demux_deactivate_current_chain (ogg);
5089 GST_CHAIN_LOCK (ogg);
5090 for (i = 0; i < ogg->chains->len; i++) {
5091 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
5093 if (chain == ogg->current_chain)
5094 ogg->current_chain = NULL;
5095 if (chain == ogg->building_chain)
5096 ogg->building_chain = NULL;
5097 gst_ogg_chain_free (chain);
5099 ogg->chains = g_array_set_size (ogg->chains, 0);
5100 if (ogg->current_chain != NULL) {
5101 GST_FIXME_OBJECT (ogg, "current chain was tracked in existing chains !");
5102 gst_ogg_chain_free (ogg->current_chain);
5103 ogg->current_chain = NULL;
5105 if (ogg->building_chain != NULL) {
5106 GST_FIXME_OBJECT (ogg, "building chain was tracked in existing chains !");
5107 gst_ogg_chain_free (ogg->building_chain);
5108 ogg->building_chain = NULL;
5110 GST_CHAIN_UNLOCK (ogg);
5113 /* this function is called when the pad is activated and should start
5116 * We check if we can do random access to decide if we work push or
5120 gst_ogg_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
5125 query = gst_query_new_scheduling ();
5127 if (!gst_pad_peer_query (sinkpad, query)) {
5128 gst_query_unref (query);
5132 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
5133 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
5134 gst_query_unref (query);
5139 GST_DEBUG_OBJECT (sinkpad, "activating pull");
5140 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
5144 GST_DEBUG_OBJECT (sinkpad, "activating push");
5145 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
5150 gst_ogg_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
5151 GstPadMode mode, gboolean active)
5156 ogg = GST_OGG_DEMUX (parent);
5159 case GST_PAD_MODE_PUSH:
5160 ogg->pullmode = FALSE;
5161 ogg->resync = FALSE;
5163 ogg->seek_event_thread_stop = FALSE;
5164 ogg->seek_thread_started = FALSE;
5165 ogg->seek_event_thread = g_thread_new ("seek_event_thread",
5166 (GThreadFunc) gst_ogg_demux_loop_push, gst_object_ref (ogg));
5167 /* And wait for the thread to start.
5168 * FIXME : This is hackish. And one wonders why we need a separate thread to
5169 * seek to a certain offset */
5170 g_mutex_lock (&ogg->seek_event_mutex);
5171 while (!ogg->seek_thread_started) {
5172 g_cond_wait (&ogg->thread_started_cond, &ogg->seek_event_mutex);
5174 g_mutex_unlock (&ogg->seek_event_mutex);
5176 g_mutex_lock (&ogg->seek_event_mutex);
5177 ogg->seek_event_thread_stop = TRUE;
5178 g_cond_broadcast (&ogg->seek_event_cond);
5179 g_mutex_unlock (&ogg->seek_event_mutex);
5180 g_thread_join (ogg->seek_event_thread);
5181 ogg->seek_event_thread = NULL;
5185 case GST_PAD_MODE_PULL:
5187 ogg->need_chains = TRUE;
5188 ogg->pullmode = TRUE;
5190 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
5193 res = gst_pad_stop_task (sinkpad);
5203 static GstStateChangeReturn
5204 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
5207 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
5209 ogg = GST_OGG_DEMUX (element);
5211 switch (transition) {
5212 case GST_STATE_CHANGE_NULL_TO_READY:
5214 ogg_sync_init (&ogg->sync);
5216 case GST_STATE_CHANGE_READY_TO_PAUSED:
5217 ogg_sync_reset (&ogg->sync);
5218 ogg->running = FALSE;
5220 ogg->total_time = -1;
5221 GST_PUSH_LOCK (ogg);
5222 ogg->push_byte_offset = 0;
5223 ogg->push_byte_length = -1;
5224 ogg->push_time_length = GST_CLOCK_TIME_NONE;
5225 ogg->push_time_offset = GST_CLOCK_TIME_NONE;
5226 ogg->push_state = PUSH_PLAYING;
5227 ogg->have_group_id = FALSE;
5228 ogg->group_id = G_MAXUINT;
5229 ogg->seqnum = GST_SEQNUM_INVALID;
5231 ogg->push_disable_seeking = FALSE;
5232 gst_ogg_demux_query_duration_push (ogg);
5233 GST_PUSH_UNLOCK (ogg);
5234 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
5240 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5242 switch (transition) {
5243 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
5245 case GST_STATE_CHANGE_PAUSED_TO_READY:
5246 gst_ogg_demux_clear_chains (ogg);
5247 GST_OBJECT_LOCK (ogg);
5248 ogg->running = FALSE;
5249 GST_OBJECT_UNLOCK (ogg);
5251 case GST_STATE_CHANGE_READY_TO_NULL:
5252 ogg_sync_clear (&ogg->sync);
5261 gst_ogg_demux_plugin_init (GstPlugin * plugin)
5263 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
5264 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
5265 "ogg demuxer setup stage when parsing pipeline");
5268 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
5270 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
5271 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
5277 /* prints all info about the element */
5278 #undef GST_CAT_DEFAULT
5279 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
5281 #ifdef GST_DISABLE_GST_DEBUG
5284 gst_ogg_print (GstOggDemux * ogg)
5289 #else /* !GST_DISABLE_GST_DEBUG */
5292 gst_ogg_print (GstOggDemux * ogg)
5296 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
5297 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
5298 GST_TIME_ARGS (ogg->total_time));
5300 for (i = 0; i < ogg->chains->len; i++) {
5301 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
5303 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
5304 GST_INFO_OBJECT (ogg,
5305 " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
5307 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
5308 GST_TIME_ARGS (chain->begin_time));
5309 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
5310 GST_TIME_ARGS (chain->total_time));
5311 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
5312 GST_TIME_ARGS (chain->segment_start));
5313 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
5314 GST_TIME_ARGS (chain->segment_stop));
5316 for (j = 0; j < chain->streams->len; j++) {
5317 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
5319 GST_INFO_OBJECT (ogg, " stream %08x: %s", stream->map.serialno,
5320 gst_ogg_stream_get_media_type (&stream->map));
5321 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
5322 GST_TIME_ARGS (stream->start_time));
5326 #endif /* GST_DISABLE_GST_DEBUG */