2 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
4 * gstoggdemux.c: ogg stream demuxer
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 * SECTION:element-oggdemux
25 * @see_also: <link linkend="gst-plugins-base-plugins-oggmux">oggmux</link>
27 * This element demuxes ogg files into their encoded audio and video components.
29 * ## Example pipelines
31 * gst-launch-1.0 -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! autoaudiosink
33 * Decodes a vorbis audio stream stored inside an ogg container and plays it.
43 #include <glib/gi18n-lib.h>
44 #include <gst/tag/tag.h>
45 #include <gst/audio/audio.h>
47 #include "gstoggelements.h"
48 #include "gstoggdemux.h"
50 #define CHUNKSIZE (8500) /* this is out of vorbisfile */
52 /* we hope we get a granpos within this many bytes off the end */
53 #define DURATION_CHUNK_OFFSET (128*1024)
55 /* An Ogg page can not be larger than 255 segments of 255 bytes, plus
57 #define MAX_OGG_PAGE_SIZE (255 * 255 + 26)
59 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
60 #define GST_FLOW_SKIP_PUSH GST_FLOW_CUSTOM_SUCCESS_1
62 #define SEEK_GIVE_UP_THRESHOLD (3*GST_SECOND)
64 #define GST_CHAIN_LOCK(ogg) g_mutex_lock(&(ogg)->chain_lock)
65 #define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock(&(ogg)->chain_lock)
67 #define GST_PUSH_LOCK(ogg) \
69 GST_TRACE_OBJECT(ogg, "Push lock"); \
70 g_mutex_lock(&(ogg)->push_lock); \
73 #define GST_PUSH_UNLOCK(ogg) \
75 GST_TRACE_OBJECT(ogg, "Push unlock"); \
76 g_mutex_unlock(&(ogg)->push_lock); \
79 GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
80 GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
81 #define GST_CAT_DEFAULT gst_ogg_demux_debug
85 _ogg_packet_copy (const ogg_packet * packet)
87 ogg_packet *ret = g_slice_new (ogg_packet);
90 ret->packet = g_memdup2 (packet->packet, packet->bytes);
96 _ogg_packet_free (ogg_packet * packet)
98 g_free (packet->packet);
99 g_slice_free (ogg_packet, packet);
103 gst_ogg_page_copy (ogg_page * page)
105 ogg_page *p = g_slice_new (ogg_page);
107 /* make a copy of the page */
108 p->header = g_memdup2 (page->header, page->header_len);
109 p->header_len = page->header_len;
110 p->body = g_memdup2 (page->body, page->body_len);
111 p->body_len = page->body_len;
117 gst_ogg_page_free (ogg_page * page)
119 g_free (page->header);
121 g_slice_free (ogg_page, page);
124 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
125 GstOggChain * chain);
126 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
127 GstOggChain * chain, GstEvent * event);
128 static void gst_ogg_pad_mark_discont (GstOggPad * pad);
129 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
131 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
133 static gboolean gst_ogg_demux_receive_event (GstElement * element,
136 static void gst_ogg_pad_dispose (GObject * object);
137 static void gst_ogg_pad_finalize (GObject * object);
139 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstObject * parent,
141 static gboolean gst_ogg_pad_event (GstPad * pad, GstObject * parent,
143 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
146 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
147 GstOggPad * pad, GstFlowReturn ret);
148 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
150 static GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg,
151 GstCaps * caps, GList * headers);
152 static gboolean gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
153 static gboolean gst_ogg_demux_perform_seek_push (GstOggDemux * ogg,
155 static gboolean gst_ogg_demux_check_duration_push (GstOggDemux * ogg,
156 GstSeekFlags flags, GstEvent * event);
158 GType gst_ogg_pad_get_type (void);
159 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
162 gst_ogg_pad_class_init (GstOggPadClass * klass)
164 GObjectClass *gobject_class;
166 gobject_class = (GObjectClass *) klass;
168 gobject_class->dispose = gst_ogg_pad_dispose;
169 gobject_class->finalize = gst_ogg_pad_finalize;
173 gst_ogg_pad_init (GstOggPad * pad)
175 gst_pad_set_event_function (GST_PAD (pad),
176 GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
177 gst_pad_set_query_function (GST_PAD (pad),
178 GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
179 gst_pad_use_fixed_caps (GST_PAD (pad));
181 pad->current_granule = -1;
182 pad->prev_granule = -1;
183 pad->keyframe_granule = -1;
185 pad->start_time = GST_CLOCK_TIME_NONE;
187 pad->position = GST_CLOCK_TIME_NONE;
189 pad->have_type = FALSE;
190 pad->continued = NULL;
191 pad->map.headers = NULL;
192 pad->map.queued = NULL;
194 pad->map.granulerate_n = 0;
195 pad->map.granulerate_d = 0;
196 pad->map.granuleshift = -1;
200 gst_ogg_pad_dispose (GObject * object)
202 GstOggPad *pad = GST_OGG_PAD (object);
207 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
208 g_list_free (pad->map.headers);
209 pad->map.headers = NULL;
210 g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
211 g_list_free (pad->map.queued);
212 pad->map.queued = NULL;
214 g_free (pad->map.index);
215 pad->map.index = NULL;
217 /* clear continued pages */
218 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
219 g_list_free (pad->continued);
220 pad->continued = NULL;
223 gst_caps_unref (pad->map.caps);
224 pad->map.caps = NULL;
227 if (pad->map.taglist) {
228 gst_tag_list_unref (pad->map.taglist);
229 pad->map.taglist = NULL;
232 ogg_stream_reset (&pad->map.stream);
234 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
238 gst_ogg_pad_finalize (GObject * object)
240 GstOggPad *pad = GST_OGG_PAD (object);
242 ogg_stream_clear (&pad->map.stream);
244 G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
248 gst_ogg_pad_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
253 ogg = GST_OGG_DEMUX (parent);
255 switch (GST_QUERY_TYPE (query)) {
256 case GST_QUERY_POSITION:
259 GstOggPad *ogg_pad = GST_OGG_PAD (pad);
261 gst_query_parse_position (query, &format, NULL);
262 /* can only get position in time */
263 if (format != GST_FORMAT_TIME)
266 gst_query_set_position (query, format, ogg_pad->position);
269 case GST_QUERY_DURATION:
272 gint64 total_time = -1;
274 gst_query_parse_duration (query, &format, NULL);
275 /* can only get duration in time */
276 if (format != GST_FORMAT_TIME)
279 if (ogg->total_time != -1) {
280 /* we can return the total length */
281 total_time = ogg->total_time;
283 gint bitrate = ogg->bitrate;
285 /* try with length and bitrate */
289 /* ask upstream for total length in bytes */
290 uquery = gst_query_new_duration (GST_FORMAT_BYTES);
291 if (gst_pad_peer_query (ogg->sinkpad, uquery)) {
294 gst_query_parse_duration (uquery, NULL, &length);
296 /* estimate using the bitrate */
298 gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);
301 "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
302 GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
304 gst_query_unref (uquery);
308 gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
311 case GST_QUERY_SEEKING:
315 gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
316 if (format == GST_FORMAT_TIME) {
317 gboolean seekable = FALSE;
320 GST_CHAIN_LOCK (ogg);
323 stop = ogg->total_time;
324 } else if (ogg->push_disable_seeking) {
326 } else if (ogg->current_chain == NULL) {
329 /* assume we can seek if upstream is seekable in BYTES format */
330 GST_LOG_OBJECT (ogg, "no current chain, check upstream seekability");
331 squery = gst_query_new_seeking (GST_FORMAT_BYTES);
332 if (gst_pad_peer_query (ogg->sinkpad, squery))
333 gst_query_parse_seeking (squery, NULL, &seekable, NULL, NULL);
336 gst_query_unref (squery);
337 } else if (ogg->current_chain->streams->len) {
341 for (i = 0; i < ogg->current_chain->streams->len; i++) {
343 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
346 if (pad->map.index != NULL && pad->map.n_index != 0) {
348 GstClockTime idx_time;
350 idx = &pad->map.index[pad->map.n_index - 1];
352 gst_util_uint64_scale (idx->timestamp, GST_SECOND,
357 stop = MAX (idx_time, stop);
359 stop = ogg->push_time_length;
361 stop = ogg->total_time;
366 gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
367 GST_CHAIN_UNLOCK (ogg);
373 case GST_QUERY_SEGMENT:{
377 format = ogg->segment.format;
380 gst_segment_to_stream_time (&ogg->segment, format,
382 if ((stop = ogg->segment.stop) == -1)
383 stop = ogg->segment.duration;
385 stop = gst_segment_to_stream_time (&ogg->segment, format, stop);
387 gst_query_set_segment (query, ogg->segment.rate, format, start, stop);
392 res = gst_pad_query_default (pad, parent, query);
402 GST_DEBUG_OBJECT (ogg, "only query position/duration on TIME is supported");
409 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
414 ogg = GST_OGG_DEMUX (element);
416 switch (GST_EVENT_TYPE (event)) {
418 /* now do the seek */
419 res = gst_ogg_demux_perform_seek (ogg, event);
420 gst_event_unref (event);
423 GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
432 GST_DEBUG_OBJECT (ogg, "error handling event");
433 gst_event_unref (event);
439 gst_ogg_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
444 ogg = GST_OGG_DEMUX (parent);
446 switch (GST_EVENT_TYPE (event)) {
448 /* now do the seek */
449 res = gst_ogg_demux_perform_seek (ogg, event);
450 gst_event_unref (event);
452 case GST_EVENT_RECONFIGURE:
453 GST_OGG_PAD (pad)->last_ret = GST_FLOW_OK;
454 res = gst_pad_event_default (pad, parent, event);
457 res = gst_pad_event_default (pad, parent, event);
465 gst_ogg_pad_reset (GstOggPad * pad)
467 ogg_stream_reset (&pad->map.stream);
469 GST_DEBUG_OBJECT (pad, "doing reset");
471 /* clear continued pages */
472 g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
473 g_list_free (pad->continued);
474 pad->continued = NULL;
476 pad->last_ret = GST_FLOW_OK;
477 pad->position = GST_CLOCK_TIME_NONE;
478 pad->current_granule = -1;
479 pad->prev_granule = -1;
480 pad->keyframe_granule = -1;
484 /* queue data, basically takes the packet, puts it in a buffer and store the
485 * buffer in the queued list. */
487 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
489 #ifndef GST_DISABLE_GST_DEBUG
490 GstOggDemux *ogg = pad->ogg;
493 GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x",
494 pad, pad->map.serialno);
496 pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
503 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
504 gboolean push_headers)
506 GstBuffer *buf = NULL;
507 GstFlowReturn ret, cret;
508 GstOggDemux *ogg = pad->ogg;
514 GstClockTime out_timestamp, out_duration;
515 guint64 out_offset, out_offset_end;
516 gboolean delta_unit = FALSE;
518 guint64 clip_start = 0, clip_end = 0;
520 ret = cret = GST_FLOW_OK;
521 GST_DEBUG_OBJECT (pad, "Chaining %d %d %" GST_TIME_FORMAT " %d %p",
522 ogg->pullmode, ogg->push_state, GST_TIME_ARGS (ogg->push_time_length),
523 ogg->push_disable_seeking, ogg->building_chain);
525 if (G_UNLIKELY (pad->is_eos)) {
526 GST_DEBUG_OBJECT (pad, "Skipping packet on pad that is eos");
532 if (!ogg->pullmode && ogg->push_state == PUSH_PLAYING
533 && ogg->push_time_length == GST_CLOCK_TIME_NONE
534 && !ogg->push_disable_seeking) {
535 if (!ogg->building_chain) {
536 /* we got all headers, now try to get duration */
537 if (!gst_ogg_demux_check_duration_push (ogg, GST_SEEK_FLAG_FLUSH, NULL)) {
538 GST_PUSH_UNLOCK (ogg);
542 GST_PUSH_UNLOCK (ogg);
545 GST_PUSH_UNLOCK (ogg);
547 GST_DEBUG_OBJECT (ogg,
548 "%p streaming to peer serial %08x", pad, pad->map.serialno);
550 gst_ogg_stream_update_stats (&pad->map, packet);
552 if (pad->map.is_ogm) {
556 data = packet->packet;
557 bytes = packet->bytes;
562 if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
563 /* We don't push header packets for OGM */
567 offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
568 delta_unit = (((data[0] & 0x08) >> 3) == 0);
572 /* Strip trailing \0 for subtitles */
573 if (pad->map.is_ogm_text) {
574 while (bytes && data[bytes - 1] == 0) {
579 } else if (pad->map.is_vp8) {
580 if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
582 (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) {
583 /* Request the first packet being pushed downstream to have the header
584 flag set, unblocking the keyframe_waiter_probe in decodebin3. */
585 pad->need_header_flag = TRUE;
586 /* We don't push header packets for VP8 */
591 delta_unit = !gst_ogg_stream_packet_is_key_frame (&pad->map, packet);
595 delta_unit = !gst_ogg_stream_packet_is_key_frame (&pad->map, packet);
598 /* get timing info for the packet */
599 is_header = gst_ogg_stream_packet_is_header (&pad->map, packet);
602 GST_DEBUG_OBJECT (ogg, "packet is header");
604 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
605 GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
609 /* If we get a hole at start, it might be we're catching a stream
610 * partway through. In that case, if the stream has an index, the
611 * index might be mooted. However, as it's totally valid to index
612 * a stream with a hole at start (ie, capturing a live stream and
613 * then index it), we now check whether the index references some
614 * offset beyond the byte length (if known). If this is the case,
615 * we can be reasonably sure we're getting a stream partway, with
616 * its index being now useless since we don't know how many bytes
617 * were skipped, preventing us from patching the index offsets to
618 * match the hole size. */
619 if (!is_header && ogg->check_index_overflow) {
626 if (ogg->current_chain) {
627 query = gst_query_new_duration (GST_FORMAT_BYTES);
628 if (gst_pad_peer_query (ogg->sinkpad, query)) {
629 gst_query_parse_duration (query, &format, &length);
630 if (format == GST_FORMAT_BYTES && length >= 0) {
631 for (i = 0; i < ogg->current_chain->streams->len; i++) {
633 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
634 if (!ipad->map.index)
636 beyond = ipad->map.n_index
637 && ipad->map.index[ipad->map.n_index - 1].offset >= length;
639 GST_WARNING_OBJECT (pad, "Index offsets beyond byte length");
641 /* hole - the index is most likely screwed up */
642 GST_WARNING_OBJECT (ogg, "Discarding entire index");
643 g_free (ipad->map.index);
644 ipad->map.index = NULL;
645 ipad->map.n_index = 0;
647 /* no hole - we can just clip the index if needed */
648 GST_WARNING_OBJECT (ogg, "Clipping index");
649 while (ipad->map.n_index > 0
650 && ipad->map.index[ipad->map.n_index - 1].offset >= length)
652 if (ipad->map.n_index == 0) {
653 GST_WARNING_OBJECT (ogg, "The entire index was clipped");
654 g_free (ipad->map.index);
655 ipad->map.index = NULL;
658 /* We can't trust the total time if the index goes beyond */
659 ipad->map.total_time = -1;
661 /* use total time to update the total ogg time */
662 if (ogg->total_time == -1) {
663 ogg->total_time = ipad->map.total_time;
664 } else if (ipad->map.total_time > 0) {
665 ogg->total_time = MAX (ogg->total_time, ipad->map.total_time);
671 gst_query_unref (query);
673 ogg->check_index_overflow = FALSE;
677 out_timestamp = GST_CLOCK_TIME_NONE;
678 out_duration = GST_CLOCK_TIME_NONE;
682 if (packet->granulepos > -1) {
683 gint64 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
686 GST_ERROR_OBJECT (ogg,
687 "granulepos %" G_GINT64_FORMAT " yielded granule %" G_GINT64_FORMAT,
688 (gint64) packet->granulepos, (gint64) granule);
689 return GST_FLOW_ERROR;
691 pad->current_granule = granule;
692 pad->keyframe_granule =
693 gst_ogg_stream_granulepos_to_key_granule (&pad->map,
695 GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
696 pad->current_granule);
697 } else if (pad->current_granule != -1) {
698 pad->current_granule += duration;
700 pad->keyframe_granule = pad->current_granule;
702 GST_DEBUG_OBJECT (ogg, "interpolating granule %" G_GUINT64_FORMAT,
703 pad->current_granule);
706 if (ogg->segment.rate < 0.0 && pad->current_granule == -1) {
707 /* negative rates, allow output of packets with no timestamp, let downstream reconstruct */
712 pad->prev_granule = -1;
714 /* we only push buffers after we have a valid granule. This is done so that
715 * we nicely skip packets without a timestamp after a seek. This is ok
716 * because we base our seek on the packet after the page with the smaller
718 if (pad->current_granule == -1) {
719 pad->prev_granule = -1;
723 if (pad->map.is_ogm) {
724 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
725 pad->current_granule);
726 out_duration = gst_util_uint64_scale (duration,
727 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
728 } else if (pad->map.is_sparse) {
729 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
730 pad->current_granule);
731 if (duration == GST_CLOCK_TIME_NONE) {
732 out_duration = GST_CLOCK_TIME_NONE;
734 out_duration = gst_util_uint64_scale (duration,
735 GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
738 /* The last packet may be clipped. This will be represented
739 by the last granule being smaller than what it would otherwise
740 have been, had no content been clipped. In that case, we
741 cannot calculate the PTS of the audio from the packet length
744 if (pad->prev_granule >= 0)
745 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
748 out_timestamp = GST_CLOCK_TIME_NONE;
750 if (pad->map.audio_clipping
751 && pad->current_granule < pad->prev_granule + duration) {
752 clip_end = pad->prev_granule + duration - pad->current_granule;
754 if (pad->map.audio_clipping
755 && pad->current_granule - duration < -pad->map.granule_offset) {
756 if (pad->current_granule >= -pad->map.granule_offset) {
757 guint64 already_removed =
758 pad->current_granule >
759 duration ? pad->current_granule - duration : 0;
762 -pad->map.granule_offset ? 0 : -pad->map.granule_offset -
765 clip_start = pad->current_granule;
768 out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
769 pad->current_granule - duration);
771 if (pad->map.audio_clipping
772 && pad->current_granule - duration < -pad->map.granule_offset) {
773 if (pad->current_granule >= -pad->map.granule_offset) {
774 guint64 already_removed =
775 pad->current_granule >
776 duration ? pad->current_granule - duration : 0;
779 -pad->map.granule_offset ? 0 : -pad->map.granule_offset -
782 clip_start = pad->current_granule;
786 gst_ogg_stream_granule_to_time (&pad->map,
787 pad->current_granule) - out_timestamp;
790 gst_ogg_stream_granule_to_granulepos (&pad->map,
791 pad->current_granule, pad->keyframe_granule);
793 gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
795 pad->prev_granule = pad->current_granule;
798 if (G_UNLIKELY (offset + trim > packet->bytes))
800 else if (pad->map.is_ogm_text) {
801 /* check for invalid buffer sizes */
802 if (G_UNLIKELY (offset + trim >= packet->bytes))
809 buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
811 if (pad->map.audio_clipping && (clip_start || clip_end)) {
812 GST_DEBUG_OBJECT (pad,
813 "Clipping %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT " (%"
814 G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT ")", clip_start, clip_end,
815 clip_start + clip_end, duration);
816 gst_buffer_add_audio_clipping_meta (buf, GST_FORMAT_DEFAULT, clip_start,
820 /* set delta flag for OGM content */
822 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
824 /* set header flag for buffers that are also in the streamheaders or when explicitely requested (VP8). */
825 if (is_header || pad->need_header_flag) {
826 pad->need_header_flag = FALSE;
827 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER);
830 if (packet->packet != NULL) {
831 /* copy packet in buffer */
832 gst_buffer_fill (buf, 0, packet->packet + offset,
833 packet->bytes - offset - trim);
836 GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
837 GST_BUFFER_DURATION (buf) = out_duration;
838 GST_BUFFER_OFFSET (buf) = out_offset;
839 GST_BUFFER_OFFSET_END (buf) = out_offset_end;
841 /* Mark discont on the buffer */
843 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
844 if (ogg->segment.rate < 0.0 || GST_BUFFER_TIMESTAMP_IS_VALID (buf))
845 pad->discont = FALSE;
848 /* don't push the header packets when we are asked to skip them */
849 if (!packet->b_o_s || push_headers) {
850 if (pad->last_ret == GST_FLOW_OK) {
851 GST_LOG_OBJECT (ogg, "Pushing buf %" GST_PTR_FORMAT, buf);
852 ret = gst_pad_push (GST_PAD_CAST (pad), buf);
854 GST_DEBUG_OBJECT (ogg, "not pushing buffer on error pad");
856 gst_buffer_unref (buf);
861 /* we're done with skeleton stuff */
862 if (pad->map.is_skeleton)
865 /* check if valid granulepos, then we can calculate the current
866 * position. We know the granule for each packet but we only want to update
867 * the position when we have a valid granulepos on the packet because else
868 * our time jumps around for the different streams. */
869 if (packet->granulepos < 0)
872 /* convert to time */
873 current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
876 /* convert to stream time */
877 if ((chain = pad->chain)) {
878 gint64 chain_start = 0;
880 if (chain->segment_start != GST_CLOCK_TIME_NONE)
881 chain_start = chain->segment_start;
883 current_time = current_time - chain_start + chain->begin_time;
886 /* and store as the current position */
887 ogg->segment.position = current_time;
889 GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT
890 " (%" G_GINT64_FORMAT ")", GST_TIME_ARGS (current_time), current_time);
892 pad->position = ogg->segment.position;
894 /* check stream eos */
895 if (!pad->is_eos && !delta_unit &&
896 ((ogg->segment.rate > 0.0 &&
897 ogg->segment.stop != GST_CLOCK_TIME_NONE &&
898 current_time >= ogg->segment.stop) ||
899 (ogg->segment.rate < 0.0 && current_time <= ogg->segment.start))) {
900 GST_DEBUG_OBJECT (ogg, "marking pad %p EOS", pad);
903 if (ret == GST_FLOW_OK) {
910 cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
914 gst_buffer_unref (buf);
915 /* return combined flow result */
921 GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
927 GST_DEBUG_OBJECT (ogg, "Skipping invalid packet");
933 GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
938 GST_DEBUG_OBJECT (ogg, "pad not added yet");
944 gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
947 guint64 start_time = G_MAXUINT64;
949 for (i = 0; i < chain->streams->len; i++) {
950 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
952 if (pad->map.is_skeleton)
955 /* can do this if the pad start time is not defined */
956 GST_DEBUG_OBJECT (ogg, "Pad %08x (%s) start time is %" GST_TIME_FORMAT,
957 pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map),
958 GST_TIME_ARGS (pad->start_time));
959 if (pad->start_time == GST_CLOCK_TIME_NONE) {
960 if (!pad->map.is_sparse) {
961 start_time = G_MAXUINT64;
965 start_time = MIN (start_time, pad->start_time);
972 gst_ogg_demux_collect_sync_time (GstOggDemux * ogg, GstOggChain * chain)
975 GstClockTime sync_time = GST_CLOCK_TIME_NONE;
978 GST_WARNING_OBJECT (ogg, "No chain!");
979 return GST_CLOCK_TIME_NONE;
982 for (i = 0; i < chain->streams->len; i++) {
983 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
985 if (pad->map.is_sparse)
988 if (pad->push_sync_time == GST_CLOCK_TIME_NONE) {
989 sync_time = GST_CLOCK_TIME_NONE;
992 if (sync_time == GST_CLOCK_TIME_NONE)
993 sync_time = pad->push_sync_time;
995 sync_time = MAX (sync_time, pad->push_sync_time);
1001 /* submit a packet to the oggpad, this function will run the type detection
1002 * code for the pad if this is the first packet for this stream
1004 static GstFlowReturn
1005 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
1008 GstFlowReturn ret = GST_FLOW_OK;
1010 GstOggDemux *ogg = pad->ogg;
1012 GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x",
1013 pad, pad->map.serialno);
1015 if (!pad->have_type) {
1016 pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
1017 if (!pad->have_type && !pad->map.caps) {
1018 pad->map.caps = gst_caps_new_empty_simple ("application/x-unknown");
1020 if (pad->map.is_skeleton) {
1021 GST_DEBUG_OBJECT (ogg, "we have a fishead");
1022 /* copy values over to global ogg level */
1023 ogg->basetime = pad->map.basetime;
1024 ogg->prestime = pad->map.prestime;
1026 /* use total time to update the total ogg time */
1027 if (ogg->total_time == -1) {
1028 ogg->total_time = pad->map.total_time;
1029 } else if (pad->map.total_time > 0) {
1030 ogg->total_time = MAX (ogg->total_time, pad->map.total_time);
1033 if (!pad->map.caps) {
1034 GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
1038 if (pad->map.is_skeleton) {
1040 GstOggPad *skel_pad;
1041 GstOggSkeleton type;
1043 /* try to parse the serialno first */
1044 if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
1045 &serialno, &type)) {
1047 GST_DEBUG_OBJECT (pad->ogg,
1048 "got skeleton packet for stream 0x%08x", serialno);
1050 skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
1053 case GST_OGG_SKELETON_FISBONE:
1054 /* parse the remainder of the fisbone in the pad with the serialno,
1055 * note that we ignore the start_time as this is usually wrong for
1057 gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
1058 packet->bytes, NULL);
1060 case GST_OGG_SKELETON_INDEX:
1061 gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
1063 ogg->check_index_overflow = TRUE;
1070 GST_WARNING_OBJECT (pad->ogg,
1071 "found skeleton fisbone for an unknown stream 0x%08x", serialno);
1076 GST_DEBUG_OBJECT (ogg, "%p packet has granulepos %" G_GINT64_FORMAT, pad,
1077 (gint64) packet->granulepos);
1079 gst_ogg_stream_granulepos_to_granule (&pad->map, packet->granulepos);
1081 GST_DEBUG_OBJECT (ogg, "%p has granule %" G_GINT64_FORMAT, pad, granule);
1082 pad->current_granule = granule;
1083 } else if (granule == 0) {
1085 } else if (granule != -1) {
1086 GST_ERROR_OBJECT (ogg,
1087 "granulepos %" G_GINT64_FORMAT " yielded granule %" G_GINT64_FORMAT,
1088 (gint64) packet->granulepos, (gint64) granule);
1089 return GST_FLOW_ERROR;
1092 /* restart header packet count when seeing a b_o_s page;
1093 * particularly useful following a seek or even following chain finding */
1094 if (packet->b_o_s) {
1095 GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
1096 pad->map.n_header_packets_seen = 0;
1097 if (!pad->map.have_headers) {
1098 GST_DEBUG_OBJECT (ogg, "clearing header packets");
1099 g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
1100 g_list_free (pad->map.headers);
1101 pad->map.headers = NULL;
1105 /* Overload the value of b_o_s in ogg_packet with a flag whether or
1106 * not this is a header packet. Maybe some day this could be cleaned
1108 packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
1109 if (!packet->b_o_s) {
1110 GST_DEBUG ("found non-header packet");
1111 pad->map.have_headers = TRUE;
1112 if (pad->start_time == GST_CLOCK_TIME_NONE) {
1113 gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
1114 GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
1115 if (duration != -1) {
1116 pad->map.accumulated_granule += duration;
1117 GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
1118 pad->map.accumulated_granule);
1121 if (packet->granulepos != -1) {
1122 ogg_int64_t start_granule;
1125 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
1126 packet->granulepos);
1128 GST_ERROR_OBJECT (ogg,
1129 "granulepos %" G_GINT64_FORMAT " yielded granule %"
1130 G_GINT64_FORMAT, (gint64) packet->granulepos, (gint64) granule);
1131 return GST_FLOW_ERROR;
1134 if (granule >= pad->map.accumulated_granule)
1135 start_granule = granule - pad->map.accumulated_granule;
1139 pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
1141 GST_DEBUG_OBJECT (ogg,
1142 "start time %" GST_TIME_FORMAT " (%" GST_TIME_FORMAT ") for %s "
1143 "from granpos %" G_GINT64_FORMAT " (granule %" G_GINT64_FORMAT ", "
1144 "accumulated granule %" G_GINT64_FORMAT,
1145 GST_TIME_ARGS (pad->start_time), GST_TIME_ARGS (pad->start_time),
1146 gst_ogg_stream_get_media_type (&pad->map),
1147 (gint64) packet->granulepos, granule, pad->map.accumulated_granule);
1149 packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
1150 pad->map.accumulated_granule + pad->current_granule,
1151 pad->keyframe_granule);
1155 /* look for tags in header packet (before inc header count) */
1156 gst_ogg_stream_extract_tags (&pad->map, packet);
1157 pad->map.n_header_packets_seen++;
1158 if (!pad->map.have_headers) {
1160 g_list_append (pad->map.headers, _ogg_packet_copy (packet));
1161 GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
1165 /* we know the start_time of the pad data, see if we
1166 * can activate the complete chain if this is a dynamic
1167 * chain. We need all the headers too for this. */
1168 if (pad->start_time != GST_CLOCK_TIME_NONE && pad->map.have_headers) {
1169 GstOggChain *chain = pad->chain;
1171 /* check if complete chain has start time */
1172 if (chain == ogg->building_chain) {
1173 GstEvent *event = NULL;
1178 GST_DEBUG_OBJECT (ogg, "need to resync");
1180 /* when we need to resync after a seek, we wait until we have received
1181 * timestamps on all streams */
1182 start_time = gst_ogg_demux_collect_start_time (ogg, chain);
1184 if (start_time != G_MAXUINT64) {
1185 gint64 segment_time;
1188 GST_DEBUG_OBJECT (ogg, "start_time: %" GST_TIME_FORMAT,
1189 GST_TIME_ARGS (start_time));
1191 if (chain->segment_start < start_time)
1193 (start_time - chain->segment_start) + chain->begin_time;
1195 segment_time = chain->begin_time;
1197 /* create the newsegment event we are going to send out */
1198 gst_segment_init (&segment, GST_FORMAT_TIME);
1200 GST_PUSH_LOCK (ogg);
1201 if (!ogg->pullmode && ogg->push_state == PUSH_LINEAR2) {
1202 /* if we are fast forwarding to the actual seek target,
1203 ensure previous frames are clipped */
1204 GST_DEBUG_OBJECT (ogg,
1205 "Resynced, starting segment at %" GST_TIME_FORMAT
1206 ", start_time %" GST_TIME_FORMAT,
1207 GST_TIME_ARGS (ogg->push_seek_time_original_target),
1208 GST_TIME_ARGS (start_time));
1209 segment.rate = ogg->push_seek_rate;
1210 segment.start = ogg->push_seek_time_original_target;
1211 segment.position = ogg->push_seek_time_original_target;
1212 segment.stop = ogg->push_seek_time_original_stop;
1213 segment.time = ogg->push_seek_time_original_target;
1214 segment.base = ogg->segment.base;
1215 event = gst_event_new_segment (&segment);
1216 ogg->push_state = PUSH_PLAYING;
1218 segment.rate = ogg->segment.rate;
1219 segment.applied_rate = ogg->segment.applied_rate;
1220 segment.start = start_time;
1221 segment.position = start_time;
1222 segment.stop = chain->segment_stop;
1223 segment.time = segment_time;
1224 segment.base = ogg->segment.base;
1225 event = gst_event_new_segment (&segment);
1227 GST_PUSH_UNLOCK (ogg);
1229 ogg->resync = FALSE;
1232 /* see if we have enough info to activate the chain, we have enough info
1233 * when all streams have a valid start time. */
1234 if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
1237 GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
1238 GST_TIME_ARGS (chain->segment_start));
1239 GST_DEBUG_OBJECT (ogg, "segment_stop: %" GST_TIME_FORMAT,
1240 GST_TIME_ARGS (chain->segment_stop));
1241 GST_DEBUG_OBJECT (ogg, "segment_time: %" GST_TIME_FORMAT,
1242 GST_TIME_ARGS (chain->begin_time));
1244 /* create the newsegment event we are going to send out */
1245 gst_segment_init (&segment, GST_FORMAT_TIME);
1246 segment.rate = ogg->segment.rate;
1247 segment.applied_rate = ogg->segment.applied_rate;
1248 segment.start = chain->segment_start;
1249 segment.position = chain->segment_start;
1250 segment.stop = chain->segment_stop;
1251 segment.time = chain->begin_time;
1252 segment.base = ogg->segment.base + segment.time;
1253 event = gst_event_new_segment (&segment);
1258 gst_event_set_seqnum (event, ogg->seqnum);
1260 gst_ogg_demux_activate_chain (ogg, chain, event);
1262 ogg->building_chain = NULL;
1267 /* if we are building a chain, store buffer for when we activate
1268 * it. This path is taken if we operate in streaming mode. */
1269 if (ogg->building_chain) {
1270 /* bos packets where stored in the header list so we can discard
1273 ret = gst_ogg_demux_queue_data (pad, packet);
1275 /* else we are completely streaming to the peer */
1277 ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
1282 /* flush at most @npackets from the stream layer. All packets if
1285 static GstFlowReturn
1286 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1288 GstFlowReturn result = GST_FLOW_OK;
1289 gboolean done = FALSE;
1298 ret = ogg_stream_packetout (&pad->map.stream, &packet);
1301 GST_LOG_OBJECT (ogg, "packetout done");
1305 GST_LOG_OBJECT (ogg, "packetout discont");
1306 if (!pad->map.is_sparse) {
1307 gst_ogg_chain_mark_discont (pad->chain);
1309 gst_ogg_pad_mark_discont (pad);
1313 GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1315 if (packet.granulepos < -1) {
1316 GST_WARNING_OBJECT (ogg,
1317 "Invalid granulepos (%" G_GINT64_FORMAT "), resetting stream",
1318 (gint64) packet.granulepos);
1319 gst_ogg_pad_reset (pad);
1323 if (packet.bytes > ogg->max_packet_size)
1324 ogg->max_packet_size = packet.bytes;
1325 result = gst_ogg_pad_submit_packet (pad, &packet);
1326 /* not linked is not a problem, it's possible that we are still
1327 * collecting headers and that we don't have exposed the pads yet */
1328 if (result == GST_FLOW_NOT_LINKED)
1330 else if (result <= GST_FLOW_EOS)
1331 goto could_not_submit;
1334 GST_WARNING_OBJECT (ogg,
1335 "invalid return value %d for ogg_stream_packetout, resetting stream",
1337 gst_ogg_pad_reset (pad);
1342 done = (npackets == 0);
1350 GST_WARNING_OBJECT (ogg,
1351 "could not submit packet for stream %08x, "
1352 "error: %d", pad->map.serialno, result);
1353 gst_ogg_pad_reset (pad);
1359 gst_ogg_demux_setup_first_granule (GstOggDemux * ogg, GstOggPad * pad,
1362 /* When we submit a page, we check if we have started tracking granules.
1363 * If not, we calculate the granule corresponding to the first packet
1365 gboolean valid_granule = TRUE;
1367 if (pad->current_granule == -1) {
1368 ogg_int64_t granpos = ogg_page_granulepos (page);
1371 (gint64) gst_ogg_stream_granulepos_to_granule (&pad->map, granpos);
1373 int packets = ogg_page_packets (page), n;
1374 GST_DEBUG_OBJECT (pad,
1375 "This page completes %d packets, granule %" G_GINT64_FORMAT, packets,
1379 ogg_stream_state os;
1381 int last_size = pad->map.last_size;
1383 memcpy (&os, &pad->map.stream, sizeof (os));
1384 for (n = 0; valid_granule && n < packets; ++n) {
1385 int ret = ogg_stream_packetout (&os, &op);
1387 /* This usually means a continued packet after a seek and we can't calc the first granule,
1388 * but sometimes not - so if it's ret == -1 and first packet, try again */
1389 if (ret == -1 && n == 0) {
1393 GST_DEBUG_OBJECT (pad, "Failed to read packet off first page");
1394 valid_granule = FALSE;
1398 GST_WARNING_OBJECT (pad,
1399 "Short read getting %d packets off first page", packets);
1400 valid_granule = FALSE;
1403 duration = gst_ogg_stream_get_packet_duration (&pad->map, &op);
1404 GST_DEBUG_OBJECT (pad, "Packet %d has duration %" G_GINT64_FORMAT,
1406 granule -= duration;
1408 pad->map.last_size = last_size;
1409 if (valid_granule) {
1411 pad->current_granule = granule;
1412 GST_INFO_OBJECT (pad,
1413 "Starting with first granule %" G_GINT64_FORMAT, granule);
1415 pad->current_granule = 0;
1416 GST_INFO_OBJECT (pad, "Extrapolated first granule is negative, "
1417 "used to clip samples at start");
1421 GST_WARNING_OBJECT (pad,
1422 "Ogg page finishing no packets, but a valid granule");
1429 gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg)
1431 if (ogg->push_last_seek_time >= ogg->push_seek_time_target) {
1432 GST_DEBUG_OBJECT (ogg, "We overshot by %" GST_TIME_FORMAT,
1433 GST_TIME_ARGS (ogg->push_last_seek_time - ogg->push_seek_time_target));
1434 ogg->push_offset1 = ogg->push_last_seek_offset;
1435 ogg->push_time1 = ogg->push_last_seek_time;
1436 ogg->seek_undershot = FALSE;
1438 GST_DEBUG_OBJECT (ogg, "We undershot by %" GST_TIME_FORMAT,
1439 GST_TIME_ARGS (ogg->push_seek_time_target - ogg->push_last_seek_time));
1440 ogg->push_offset0 = ogg->push_last_seek_offset;
1441 ogg->push_time0 = ogg->push_last_seek_time;
1442 ogg->seek_undershot = TRUE;
1447 gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg, float seek_quality)
1450 gint64 segment_bitrate;
1453 /* we might not know the length of the stream in time,
1454 so push_time1 might not be set */
1455 GST_DEBUG_OBJECT (ogg,
1456 "push time 1: %" GST_TIME_FORMAT ", dbytes %" G_GINT64_FORMAT,
1457 GST_TIME_ARGS (ogg->push_time1), ogg->push_offset1 - ogg->push_offset0);
1458 if (ogg->push_time1 == GST_CLOCK_TIME_NONE) {
1459 GST_DEBUG_OBJECT (ogg,
1460 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1461 ", time %" GST_TIME_FORMAT " (open ended)", ogg->push_offset0,
1462 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0));
1463 if (ogg->push_last_seek_time == ogg->push_start_time) {
1464 /* if we're at start and don't know the end time, we can't estimate
1465 bitrate, so get the nominal declared bitrate as a failsafe, or some
1466 random constant which will be discarded after we made a (probably
1467 dire) first guess */
1468 segment_bitrate = (ogg->bitrate > 0 ? ogg->bitrate : 1000);
1471 gst_util_uint64_scale (ogg->push_last_seek_offset - 0,
1472 8 * GST_SECOND, ogg->push_last_seek_time - ogg->push_start_time);
1476 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1477 segment_bitrate, 8 * GST_SECOND);
1478 ogg->seek_secant = TRUE;
1480 GST_DEBUG_OBJECT (ogg,
1481 "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1482 ", time %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, ogg->push_offset0,
1483 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
1484 GST_TIME_ARGS (ogg->push_time1));
1485 if (ogg->push_time0 == ogg->push_time1) {
1486 best = ogg->push_offset0;
1489 gst_util_uint64_scale (ogg->push_offset1 - ogg->push_offset0,
1490 8 * GST_SECOND, ogg->push_time1 - ogg->push_time0);
1491 GST_DEBUG_OBJECT (ogg,
1492 "Local bitrate on the %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1493 " segment: %" G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_time0),
1494 GST_TIME_ARGS (ogg->push_time1), segment_bitrate);
1498 gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1499 segment_bitrate, 8 * GST_SECOND);
1500 if (seek_quality < 0.5f && ogg->seek_secant) {
1501 gint64 new_best, best2 = (ogg->push_offset0 + ogg->push_offset1) / 2;
1502 /* if dire result, give as much as 25% weight to a dumb bisection guess */
1503 float secant_weight = 1.0f - ((0.5 - seek_quality) / 0.5f) * 0.25;
1504 new_best = (best * secant_weight + best2 * (1.0f - secant_weight));
1505 GST_DEBUG_OBJECT (ogg,
1506 "Secant says %" G_GINT64_FORMAT ", straight is %" G_GINT64_FORMAT
1507 ", new best %" G_GINT64_FORMAT " with secant_weight %f", best,
1508 best2, new_best, secant_weight);
1510 ogg->seek_secant = FALSE;
1512 ogg->seek_secant = TRUE;
1517 GST_DEBUG_OBJECT (ogg, "Raw best guess: %" G_GINT64_FORMAT, best);
1519 /* offset the guess down as we need to capture the start of the
1520 page we are targeting - but only do so if we did not undershoot
1521 last time, as we're likely to still do this time */
1522 if (!ogg->seek_undershot) {
1523 /* very small packets are packed on pages, so offset by at least
1524 a value which is likely to get us at least one page where the
1527 ogg->max_packet_size >
1528 ogg->max_page_size ? ogg->max_packet_size : ogg->max_page_size;
1529 GST_DEBUG_OBJECT (ogg, "Offsetting by %" G_GINT64_FORMAT, skew);
1533 /* do not seek too close to the bounds, as we stop seeking
1534 when we get to within max_packet_size before the target */
1535 if (best > ogg->push_offset1 - ogg->max_packet_size) {
1536 best = ogg->push_offset1 - ogg->max_packet_size;
1537 GST_DEBUG_OBJECT (ogg,
1538 "Too close to high bound, pushing back to %" G_GINT64_FORMAT, best);
1539 } else if (best < ogg->push_offset0 + ogg->max_packet_size) {
1540 best = ogg->push_offset0 + ogg->max_packet_size;
1541 GST_DEBUG_OBJECT (ogg,
1542 "Too close to low bound, pushing forth to %" G_GINT64_FORMAT, best);
1545 /* keep within bounds */
1546 if (best > ogg->push_offset1)
1547 best = ogg->push_offset1;
1548 if (best < ogg->push_offset0)
1549 best = ogg->push_offset0;
1551 GST_DEBUG_OBJECT (ogg, "Choosing target %" G_GINT64_FORMAT, best);
1556 gst_ogg_demux_record_keyframe_time (GstOggDemux * ogg, GstOggPad * pad,
1557 ogg_int64_t granpos)
1560 GstClockTime kf_time;
1562 kf_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, granpos);
1563 kf_time = gst_ogg_stream_granule_to_time (&pad->map, kf_granule);
1565 pad->push_kf_time = kf_time;
1568 /* returns the earliest keyframe time for all non sparse pads in the chain,
1569 * if known, and GST_CLOCK_TIME_NONE if not */
1571 gst_ogg_demux_get_earliest_keyframe_time (GstOggDemux * ogg)
1573 GstClockTime t = GST_CLOCK_TIME_NONE;
1574 GstOggChain *chain = ogg->building_chain;
1578 GST_WARNING_OBJECT (ogg, "No chain!");
1579 return GST_CLOCK_TIME_NONE;
1581 for (i = 0; i < chain->streams->len; i++) {
1582 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1584 if (pad->map.is_sparse)
1586 if (pad->push_kf_time == GST_CLOCK_TIME_NONE)
1587 return GST_CLOCK_TIME_NONE;
1588 if (t == GST_CLOCK_TIME_NONE || pad->push_kf_time < t)
1589 t = pad->push_kf_time;
1595 /* MUST be called with the push lock locked, and will unlock it
1596 regardless of return value. */
1597 static GstFlowReturn
1598 gst_ogg_demux_seek_back_after_push_duration_check_unlock (GstOggDemux * ogg)
1602 /* Get the delayed event, if any */
1603 event = ogg->push_mode_seek_delayed_event;
1604 ogg->push_mode_seek_delayed_event = NULL;
1606 /* if we haven't learnt about the total time yet, disable seeking */
1607 if (ogg->total_time == -1)
1608 ogg->push_disable_seeking = TRUE;
1610 ogg->push_state = PUSH_PLAYING;
1612 /* If there is one, perform it. Otherwise, seek back at start to start
1613 * normal playback */
1615 GST_INFO_OBJECT (ogg, "Seeking back to 0 after duration check");
1616 event = gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1617 GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
1618 GST_SEEK_TYPE_SET, 1, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
1619 /* drop everything until this seek event completed. We can't wait until the
1620 * seek thread sets this because there would be race between receiving e.g.
1621 * an EOS or any data and the seek thread actually picking up the seek. */
1622 ogg->seek_event_drop_till = gst_event_get_seqnum (event);
1624 gst_event_replace (&ogg->seek_event, event);
1625 gst_event_unref (event);
1626 GST_PUSH_UNLOCK (ogg);
1627 g_mutex_lock (&ogg->seek_event_mutex);
1628 g_cond_broadcast (&ogg->seek_event_cond);
1629 g_mutex_unlock (&ogg->seek_event_mutex);
1635 gst_ogg_demux_estimate_seek_quality (GstOggDemux * ogg)
1637 GstClockTimeDiff diff; /* how far from the goal we ended up */
1638 GstClockTimeDiff dist; /* how far we moved this iteration */
1641 if (ogg->push_prev_seek_time == GST_CLOCK_TIME_NONE) {
1642 /* for the first seek, we pretend we got a good seek,
1643 as we don't have a previous seek yet */
1647 /* We take a guess at how good the last seek was at guessing
1648 the byte target by comparing the amplitude of the last
1649 seek to the error */
1650 diff = GST_CLOCK_DIFF (ogg->push_seek_time_target, ogg->push_last_seek_time);
1653 dist = GST_CLOCK_DIFF (ogg->push_last_seek_time, ogg->push_prev_seek_time);
1657 seek_quality = (dist == 0) ? 0.0f : 1.0f / (1.0f + diff / (float) dist);
1659 GST_DEBUG_OBJECT (ogg,
1660 "We moved %" GST_STIME_FORMAT ", we're off by %" GST_STIME_FORMAT
1661 ", seek quality %f", GST_STIME_ARGS (dist), GST_STIME_ARGS (diff),
1663 return seek_quality;
1667 gst_ogg_demux_update_bisection_stats (GstOggDemux * ogg)
1671 GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps",
1672 ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]);
1674 for (n = 0; n < 2; ++n) {
1675 ogg->stats_bisection_steps[n] += ogg->push_bisection_steps[n];
1676 if (ogg->stats_bisection_max_steps[n] < ogg->push_bisection_steps[n])
1677 ogg->stats_bisection_max_steps[n] = ogg->push_bisection_steps[n];
1679 ogg->stats_nbisections++;
1681 GST_INFO_OBJECT (ogg,
1682 "So far, %.2f + %.2f bisections needed per seek (max %d + %d)",
1683 ogg->stats_bisection_steps[0] / (float) ogg->stats_nbisections,
1684 ogg->stats_bisection_steps[1] / (float) ogg->stats_nbisections,
1685 ogg->stats_bisection_max_steps[0], ogg->stats_bisection_max_steps[1]);
1689 gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
1691 GstOggDemux *ogg = pad->ogg;
1692 ogg_int64_t granpos = ogg_page_granulepos (page);
1694 GST_PUSH_LOCK (ogg);
1695 if (granpos >= 0 && pad->have_type) {
1696 if (ogg->push_start_time == GST_CLOCK_TIME_NONE) {
1697 ogg->push_start_time =
1698 gst_ogg_stream_get_start_time_for_granulepos (&pad->map, granpos);
1699 GST_DEBUG_OBJECT (ogg, "Stream start time: %" GST_TIME_FORMAT,
1700 GST_TIME_ARGS (ogg->push_start_time));
1702 ogg->push_time_offset =
1703 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1704 if (ogg->push_time_offset > 0) {
1705 GST_DEBUG_OBJECT (ogg, "Bitrate since start: %" G_GUINT64_FORMAT,
1706 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
1707 ogg->push_time_offset));
1710 if (ogg->push_state == PUSH_DURATION) {
1712 gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1714 if (ogg->total_time == GST_CLOCK_TIME_NONE || t > ogg->total_time) {
1715 GST_DEBUG_OBJECT (ogg, "New total time: %" GST_TIME_FORMAT,
1717 ogg->total_time = t;
1718 ogg->push_time_length = t;
1721 /* If we're still receiving data from before the seek segment, drop it */
1722 if (ogg->seek_event_drop_till != 0) {
1723 GST_PUSH_UNLOCK (ogg);
1724 return GST_FLOW_SKIP_PUSH;
1727 /* If we were determining the duration of the stream, we're now done,
1728 and can get back to sending the original event we delayed.
1729 We stop a bit before the end of the stream, as if we get a EOS
1730 event and there is a queue2 upstream (such as when using playbin),
1731 it will pause the task *after* we come back from the EOS handler,
1732 so we cannot prevent the pausing by issuing a seek. */
1733 if (ogg->push_byte_offset >= ogg->push_byte_length) {
1734 GstMessage *message;
1737 /* tell the pipeline we've just found out the duration */
1738 ogg->push_time_length = ogg->total_time;
1739 GST_INFO_OBJECT (ogg, "New duration found: %" GST_TIME_FORMAT,
1740 GST_TIME_ARGS (ogg->total_time));
1741 message = gst_message_new_duration_changed (GST_OBJECT (ogg));
1742 gst_element_post_message (GST_ELEMENT (ogg), message);
1744 GST_DEBUG_OBJECT (ogg,
1745 "We're close enough to the end, and we're scared "
1746 "to get too close, seeking back to start");
1748 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1749 if (res != GST_FLOW_OK)
1751 return GST_FLOW_SKIP_PUSH;
1753 GST_PUSH_UNLOCK (ogg);
1755 return GST_FLOW_SKIP_PUSH;
1759 /* if we're seeking, look at time, and decide what to do */
1760 if (ogg->push_state != PUSH_PLAYING && ogg->push_state != PUSH_LINEAR2) {
1764 gboolean close_enough;
1767 /* ignore -1 granpos when seeking, we want to sync on a real granpos */
1769 GST_PUSH_UNLOCK (ogg);
1770 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1772 if (pad->current_granule == -1)
1773 gst_ogg_demux_setup_first_granule (ogg, pad, page);
1774 return GST_FLOW_SKIP_PUSH;
1777 t = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1779 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1780 GstClockTime sync_time;
1782 if (pad->push_sync_time == GST_CLOCK_TIME_NONE)
1783 pad->push_sync_time = t;
1784 GST_DEBUG_OBJECT (ogg, "Got PTS %" GST_TIME_FORMAT " for %s",
1785 GST_TIME_ARGS (t), gst_ogg_stream_get_media_type (&pad->map));
1786 sync_time = gst_ogg_demux_collect_sync_time (ogg, ogg->building_chain);
1787 if (sync_time == GST_CLOCK_TIME_NONE) {
1788 GST_PUSH_UNLOCK (ogg);
1789 GST_DEBUG_OBJECT (ogg,
1790 "Not enough timing info collected for sync, waiting for more");
1791 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1793 if (pad->current_granule == -1)
1794 gst_ogg_demux_setup_first_granule (ogg, pad, page);
1795 return GST_FLOW_SKIP_PUSH;
1797 ogg->push_last_seek_time = sync_time;
1799 GST_DEBUG_OBJECT (ogg,
1800 "Bisection just seeked at %" G_GINT64_FORMAT ", time %"
1801 GST_TIME_FORMAT ", target was %" GST_TIME_FORMAT,
1802 ogg->push_last_seek_offset,
1803 GST_TIME_ARGS (ogg->push_last_seek_time),
1804 GST_TIME_ARGS (ogg->push_seek_time_target));
1806 if (ogg->push_time1 != GST_CLOCK_TIME_NONE) {
1807 seek_quality = gst_ogg_demux_estimate_seek_quality (ogg);
1808 GST_DEBUG_OBJECT (ogg,
1809 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1810 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1811 " (%" GST_TIME_FORMAT "), seek quality %f", ogg->push_offset0,
1812 ogg->push_offset1, ogg->push_offset1 - ogg->push_offset0,
1813 GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1),
1814 GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0), seek_quality);
1816 /* in a open ended seek, we can't do bisection, so we pretend
1817 we like our result so far */
1818 seek_quality = 1.0f;
1819 GST_DEBUG_OBJECT (ogg,
1820 "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1821 G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - unknown",
1822 ogg->push_offset0, ogg->push_offset1,
1823 ogg->push_offset1 - ogg->push_offset0,
1824 GST_TIME_ARGS (ogg->push_time0));
1826 ogg->push_prev_seek_time = ogg->push_last_seek_time;
1828 gst_ogg_demux_setup_bisection_bounds (ogg);
1830 best = gst_ogg_demux_estimate_bisection_target (ogg, seek_quality);
1832 if (ogg->push_seek_time_target == 0) {
1833 GST_DEBUG_OBJECT (ogg, "Seeking to 0, deemed close enough");
1834 close_enough = (ogg->push_last_seek_time == 0);
1836 /* TODO: make this dependent on framerate ? */
1837 GstClockTime time_threshold = GST_SECOND / 2;
1838 guint64 byte_threshold =
1839 (ogg->max_packet_size >
1840 64 * 1024 ? ogg->max_packet_size : 64 * 1024);
1842 /* We want to be within half a second before the target,
1843 or before the target and half less or equal to the max
1844 packet size left to search in */
1845 if (time_threshold > ogg->push_seek_time_target)
1846 time_threshold = ogg->push_seek_time_target;
1847 close_enough = ogg->push_last_seek_time < ogg->push_seek_time_target
1848 && (ogg->push_last_seek_time >=
1849 ogg->push_seek_time_target - time_threshold
1850 || ogg->push_offset1 <= ogg->push_offset0 + byte_threshold);
1851 GST_DEBUG_OBJECT (ogg,
1852 "testing if we're close enough: %" GST_TIME_FORMAT " <= %"
1853 GST_TIME_FORMAT " < %" GST_TIME_FORMAT ", or %" G_GUINT64_FORMAT
1854 " <= %" G_GUINT64_FORMAT " ? %s",
1855 GST_TIME_ARGS (ogg->push_seek_time_target - time_threshold),
1856 GST_TIME_ARGS (ogg->push_last_seek_time),
1857 GST_TIME_ARGS (ogg->push_seek_time_target),
1858 ogg->push_offset1 - ogg->push_offset0, byte_threshold,
1859 close_enough ? "Yes" : "No");
1862 if (close_enough || best == ogg->push_last_seek_offset) {
1863 if (ogg->push_state == PUSH_BISECT1) {
1864 /* we now know the time segment we'll have to search for
1865 the second bisection */
1866 ogg->push_time0 = ogg->push_start_time;
1867 ogg->push_offset0 = 0;
1869 GST_DEBUG_OBJECT (ogg,
1870 "Seek to %" GST_TIME_FORMAT
1871 " (%lx) done, now gathering pages for all non-sparse streams",
1872 GST_TIME_ARGS (ogg->push_seek_time_target), (long) granpos);
1873 ogg->push_state = PUSH_LINEAR1;
1875 /* If we're asked for an accurate seek, we'll go forward till
1876 we get to the original seek target time, else we'll just drop
1877 here at the keyframe */
1878 if (ogg->push_seek_flags & GST_SEEK_FLAG_ACCURATE) {
1879 GST_INFO_OBJECT (ogg,
1880 "Seek to keyframe at %" GST_TIME_FORMAT " done (we're at %"
1881 GST_TIME_FORMAT "), skipping to original target (%"
1882 GST_TIME_FORMAT ")",
1883 GST_TIME_ARGS (ogg->push_seek_time_target),
1884 GST_TIME_ARGS (sync_time),
1885 GST_TIME_ARGS (ogg->push_seek_time_original_target));
1886 ogg->push_state = PUSH_LINEAR2;
1888 GST_INFO_OBJECT (ogg, "Seek to keyframe done, playing");
1890 /* we're synced to the seek target, so flush stream and stuff
1891 any queued pages into the stream so we start decoding there */
1892 ogg->push_state = PUSH_PLAYING;
1894 gst_ogg_demux_update_bisection_stats (ogg);
1897 } else if (ogg->push_state == PUSH_LINEAR1) {
1898 if (pad->push_kf_time == GST_CLOCK_TIME_NONE) {
1899 GstClockTime earliest_keyframe_time;
1901 gst_ogg_demux_record_keyframe_time (ogg, pad, granpos);
1902 GST_DEBUG_OBJECT (ogg,
1903 "Previous keyframe for %s stream at %" GST_TIME_FORMAT,
1904 gst_ogg_stream_get_media_type (&pad->map),
1905 GST_TIME_ARGS (pad->push_kf_time));
1906 earliest_keyframe_time = gst_ogg_demux_get_earliest_keyframe_time (ogg);
1907 if (earliest_keyframe_time != GST_CLOCK_TIME_NONE) {
1908 if (earliest_keyframe_time > ogg->push_last_seek_time) {
1909 GST_INFO_OBJECT (ogg,
1910 "All non sparse streams now have a previous keyframe time, "
1911 "and we already decoded it, switching to playing");
1912 ogg->push_state = PUSH_PLAYING;
1913 gst_ogg_demux_update_bisection_stats (ogg);
1915 GST_INFO_OBJECT (ogg,
1916 "All non sparse streams now have a previous keyframe time, "
1917 "bisecting again to %" GST_TIME_FORMAT,
1918 GST_TIME_ARGS (earliest_keyframe_time));
1920 ogg->push_seek_time_target = earliest_keyframe_time;
1921 ogg->push_offset0 = 0;
1922 ogg->push_time0 = ogg->push_start_time;
1923 ogg->push_offset1 = ogg->push_last_seek_offset;
1924 ogg->push_time1 = ogg->push_last_seek_time;
1925 ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
1926 ogg->seek_secant = FALSE;
1927 ogg->seek_undershot = FALSE;
1929 ogg->push_state = PUSH_BISECT2;
1930 best = gst_ogg_demux_estimate_bisection_target (ogg, 1.0f);
1936 if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1939 ogg_sync_reset (&ogg->sync);
1940 for (i = 0; i < ogg->building_chain->streams->len; i++) {
1942 g_array_index (ogg->building_chain->streams, GstOggPad *, i);
1944 pad->push_sync_time = GST_CLOCK_TIME_NONE;
1945 ogg_stream_reset (&pad->map.stream);
1948 GST_DEBUG_OBJECT (ogg,
1949 "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, best,
1952 g_assert (best != -1);
1953 ogg->push_bisection_steps[ogg->push_state == PUSH_BISECT2 ? 1 : 0]++;
1955 gst_event_new_seek (ogg->push_seek_rate, GST_FORMAT_BYTES,
1956 ogg->push_seek_flags, GST_SEEK_TYPE_SET, best,
1957 GST_SEEK_TYPE_NONE, -1);
1958 gst_event_set_seqnum (sevent, ogg->seqnum);
1960 gst_event_replace (&ogg->seek_event, sevent);
1961 gst_event_unref (sevent);
1962 GST_PUSH_UNLOCK (ogg);
1963 g_mutex_lock (&ogg->seek_event_mutex);
1964 g_cond_broadcast (&ogg->seek_event_cond);
1965 g_mutex_unlock (&ogg->seek_event_mutex);
1966 return GST_FLOW_SKIP_PUSH;
1969 if (ogg->push_state != PUSH_PLAYING) {
1970 GST_PUSH_UNLOCK (ogg);
1971 return GST_FLOW_SKIP_PUSH;
1974 GST_PUSH_UNLOCK (ogg);
1980 GST_WARNING_OBJECT (ogg,
1981 "ogg stream choked on page (serial %08x), "
1982 "resetting stream", pad->map.serialno);
1983 gst_ogg_pad_reset (pad);
1984 /* we continue to recover */
1985 return GST_FLOW_SKIP_PUSH;
1990 gst_ogg_demux_query_duration_push (GstOggDemux * ogg)
1992 if (!ogg->pullmode && ogg->push_byte_length == -1) {
1994 gboolean seekable = FALSE;
1996 query = gst_query_new_seeking (GST_FORMAT_BYTES);
1997 if (gst_pad_peer_query (ogg->sinkpad, query))
1998 gst_query_parse_seeking (query, NULL, &seekable, NULL, NULL);
1999 gst_query_unref (query);
2003 if (!gst_element_query_duration (GST_ELEMENT (ogg), GST_FORMAT_BYTES,
2006 GST_DEBUG_OBJECT (ogg,
2007 "Unable to determine stream size, assuming live, seeking disabled");
2008 ogg->push_disable_seeking = TRUE;
2010 ogg->push_disable_seeking = FALSE;
2013 GST_DEBUG_OBJECT (ogg, "Stream is not seekable, seeking disabled");
2014 ogg->push_disable_seeking = TRUE;
2019 /* submit a page to an oggpad, this function will then submit all
2020 * the packets in the page.
2022 static GstFlowReturn
2023 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
2025 GstFlowReturn result = GST_FLOW_OK;
2027 gboolean continued = FALSE;
2031 /* for negative rates we read pages backwards and must therefore be careful
2032 * with continued pages */
2033 if (ogg->segment.rate < 0.0) {
2036 continued = ogg_page_continued (page);
2038 /* number of completed packets in the page */
2039 npackets = ogg_page_packets (page);
2041 /* page is not continued so it contains at least one packet start. It's
2042 * possible that no packet ends on this page (npackets == 0). In that
2043 * case, the next (continued) page(s) we kept contain the remainder of the
2044 * packets. We mark npackets=1 to make us start decoding the pages in the
2045 * remainder of the algorithm. */
2049 GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
2051 if (npackets == 0) {
2052 GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
2057 gst_ogg_demux_query_duration_push (ogg);
2059 /* keep track of time in push mode */
2060 if (!ogg->pullmode) {
2061 result = gst_ogg_pad_handle_push_mode_state (pad, page);
2062 if (result == GST_FLOW_SKIP_PUSH)
2064 if (result != GST_FLOW_OK)
2068 if (page->header_len + page->body_len > ogg->max_page_size)
2069 ogg->max_page_size = page->header_len + page->body_len;
2071 if (ogg_stream_pagein (&pad->map.stream, page) != 0)
2073 if (pad->current_granule == -1)
2074 gst_ogg_demux_setup_first_granule (ogg, pad, page);
2076 /* flush all packets in the stream layer, this might not give a packet if
2077 * the page had no packets finishing on the page (npackets == 0). */
2078 result = gst_ogg_pad_stream_out (pad, 0);
2080 if (pad->continued) {
2083 /* now send the continued pages to the stream layer */
2084 while (pad->continued) {
2085 ogg_page *p = (ogg_page *) pad->continued->data;
2087 GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
2088 if (ogg_stream_pagein (&pad->map.stream, p) != 0)
2091 pad->continued = g_list_delete_link (pad->continued, pad->continued);
2094 gst_ogg_page_free (p);
2097 GST_LOG_OBJECT (ogg, "flushing last continued packet");
2098 /* flush 1 continued packet in the stream layer */
2099 result = gst_ogg_pad_stream_out (pad, 1);
2101 /* flush all remaining packets, we pushed them in the previous round.
2102 * We don't use _reset() because we still want to get the discont when
2103 * we submit a next page. */
2104 while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
2108 /* keep continued pages (only in reverse mode) */
2110 ogg_page *p = gst_ogg_page_copy (page);
2112 GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
2113 pad->continued = g_list_prepend (pad->continued, p);
2120 GST_WARNING_OBJECT (ogg,
2121 "ogg stream choked on page (serial %08x), "
2122 "resetting stream", pad->map.serialno);
2123 gst_ogg_pad_reset (pad);
2124 /* we continue to recover */
2130 static GstOggChain *
2131 gst_ogg_chain_new (GstOggDemux * ogg)
2133 GstOggChain *chain = g_slice_new0 (GstOggChain);
2135 GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
2139 chain->have_bos = FALSE;
2140 chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
2141 chain->begin_time = GST_CLOCK_TIME_NONE;
2142 chain->segment_start = GST_CLOCK_TIME_NONE;
2143 chain->segment_stop = GST_CLOCK_TIME_NONE;
2144 chain->total_time = GST_CLOCK_TIME_NONE;
2150 gst_ogg_chain_free (GstOggChain * chain)
2154 for (i = 0; i < chain->streams->len; i++) {
2155 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2157 gst_object_unref (pad);
2159 g_array_free (chain->streams, TRUE);
2160 g_slice_free (GstOggChain, chain);
2164 gst_ogg_pad_mark_discont (GstOggPad * pad)
2166 GST_LOG_OBJECT (pad, "Marking discont on pad");
2167 pad->discont = TRUE;
2168 pad->map.last_size = 0;
2172 gst_ogg_chain_mark_discont (GstOggChain * chain)
2176 for (i = 0; i < chain->streams->len; i++) {
2177 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2179 gst_ogg_pad_mark_discont (pad);
2184 gst_ogg_chain_reset (GstOggChain * chain)
2188 for (i = 0; i < chain->streams->len; i++) {
2189 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2191 gst_ogg_pad_reset (pad);
2196 gst_ogg_chain_new_stream (GstOggChain * chain, guint32 serialno)
2201 GST_DEBUG_OBJECT (chain->ogg,
2202 "creating new stream %08x in chain %p", serialno, chain);
2204 name = g_strdup_printf ("src_%08x", serialno);
2205 ret = g_object_new (GST_TYPE_OGG_PAD, "name", name, NULL);
2207 /* we own this one */
2208 gst_object_ref_sink (ret);
2210 GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
2211 gst_ogg_pad_mark_discont (ret);
2214 ret->ogg = chain->ogg;
2216 ret->map.serialno = serialno;
2217 if (ogg_stream_init (&ret->map.stream, serialno) != 0)
2220 GST_DEBUG_OBJECT (chain->ogg,
2221 "created new ogg src %p for stream with serial %08x", ret, serialno);
2223 g_array_append_val (chain->streams, ret);
2224 gst_pad_set_active (GST_PAD_CAST (ret), TRUE);
2231 GST_ERROR ("Could not initialize ogg_stream struct for serial %08x",
2233 gst_object_unref (ret);
2239 gst_ogg_chain_get_stream (GstOggChain * chain, guint32 serialno)
2243 for (i = 0; i < chain->streams->len; i++) {
2244 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2246 if (pad->map.serialno == serialno)
2253 gst_ogg_chain_has_stream (GstOggChain * chain, guint32 serialno)
2255 return gst_ogg_chain_get_stream (chain, serialno) != NULL;
2258 /* signals and args */
2271 static GstStaticPadTemplate ogg_demux_src_template_factory =
2272 GST_STATIC_PAD_TEMPLATE ("src_%08x",
2275 GST_STATIC_CAPS_ANY);
2277 static GstStaticPadTemplate ogg_demux_sink_template_factory =
2278 GST_STATIC_PAD_TEMPLATE ("sink",
2281 GST_STATIC_CAPS ("application/ogg; audio/ogg; video/ogg; application/kate")
2284 static void gst_ogg_demux_finalize (GObject * object);
2286 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
2287 GstOggChain ** chain);
2288 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
2289 GstOggChain * chain);
2291 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent,
2293 static void gst_ogg_demux_loop (GstOggPad * pad);
2294 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstObject * parent,
2295 GstBuffer * buffer);
2296 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad,
2297 GstObject * parent);
2298 static gboolean gst_ogg_demux_sink_activate_mode (GstPad * sinkpad,
2299 GstObject * parent, GstPadMode mode, gboolean active);
2300 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
2301 GstStateChange transition);
2303 static void gst_ogg_print (GstOggDemux * demux);
2304 static gboolean gst_ogg_demux_plugin_init (GstPlugin * plugin);
2306 #define gst_ogg_demux_parent_class parent_class
2307 G_DEFINE_TYPE (GstOggDemux, gst_ogg_demux, GST_TYPE_ELEMENT);
2308 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (oggdemux, "oggdemux", GST_RANK_PRIMARY,
2309 GST_TYPE_OGG_DEMUX, gst_ogg_demux_plugin_init (plugin));
2312 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
2314 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
2315 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2317 gst_element_class_set_static_metadata (gstelement_class,
2318 "Ogg demuxer", "Codec/Demuxer",
2319 "demux ogg streams (info about ogg: http://xiph.org)",
2320 "Wim Taymans <wim@fluendo.com>");
2322 gst_element_class_add_static_pad_template (gstelement_class,
2323 &ogg_demux_sink_template_factory);
2324 gst_element_class_add_static_pad_template (gstelement_class,
2325 &ogg_demux_src_template_factory);
2327 gstelement_class->change_state = gst_ogg_demux_change_state;
2328 gstelement_class->send_event = gst_ogg_demux_receive_event;
2330 gobject_class->finalize = gst_ogg_demux_finalize;
2334 gst_ogg_demux_init (GstOggDemux * ogg)
2336 /* create the sink pad */
2338 gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
2341 gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
2342 gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
2343 gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
2344 gst_pad_set_activatemode_function (ogg->sinkpad,
2345 gst_ogg_demux_sink_activate_mode);
2346 gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
2348 g_mutex_init (&ogg->chain_lock);
2349 g_mutex_init (&ogg->push_lock);
2350 g_mutex_init (&ogg->seek_event_mutex);
2351 g_cond_init (&ogg->seek_event_cond);
2352 g_cond_init (&ogg->thread_started_cond);
2354 ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
2356 ogg->stats_nbisections = 0;
2357 ogg->stats_bisection_steps[0] = 0;
2358 ogg->stats_bisection_steps[1] = 0;
2359 ogg->stats_bisection_max_steps[0] = 0;
2360 ogg->stats_bisection_max_steps[1] = 0;
2362 ogg->newsegment = NULL;
2363 ogg->seqnum = GST_SEQNUM_INVALID;
2365 ogg->chunk_size = CHUNKSIZE;
2366 ogg->flowcombiner = gst_flow_combiner_new ();
2370 gst_ogg_demux_finalize (GObject * object)
2374 ogg = GST_OGG_DEMUX (object);
2376 g_array_free (ogg->chains, TRUE);
2377 g_mutex_clear (&ogg->chain_lock);
2378 g_mutex_clear (&ogg->push_lock);
2379 g_cond_clear (&ogg->seek_event_cond);
2380 g_cond_clear (&ogg->thread_started_cond);
2381 g_mutex_clear (&ogg->seek_event_mutex);
2383 ogg_sync_clear (&ogg->sync);
2385 if (ogg->newsegment)
2386 gst_event_unref (ogg->newsegment);
2388 gst_flow_combiner_free (ogg->flowcombiner);
2390 if (ogg->building_chain)
2391 gst_ogg_chain_free (ogg->building_chain);
2393 G_OBJECT_CLASS (parent_class)->finalize (object);
2397 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
2402 chain = ogg->current_chain;
2406 for (i = 0; i < chain->streams->len; i++) {
2407 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2409 stream->start_time = -1;
2410 stream->map.accumulated_granule = 0;
2411 stream->current_granule = -1;
2412 stream->keyframe_granule = -1;
2414 ogg->building_chain = chain;
2415 GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2416 ogg->current_chain = NULL;
2418 gst_ogg_chain_mark_discont (chain);
2420 ogg->chunk_size = CHUNKSIZE;
2424 gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
2429 ogg = GST_OGG_DEMUX (parent);
2431 switch (GST_EVENT_TYPE (event)) {
2432 case GST_EVENT_FLUSH_START:
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);
2439 case GST_EVENT_FLUSH_STOP:
2440 GST_DEBUG_OBJECT (ogg, "got a flush stop event");
2441 ogg_sync_reset (&ogg->sync);
2442 if (ogg->seqnum != GST_SEQNUM_INVALID) {
2443 event = gst_event_make_writable (event);
2444 gst_event_set_seqnum (event, ogg->seqnum);
2446 res = gst_ogg_demux_send_event (ogg, event);
2447 if (ogg->pullmode || ogg->push_state != PUSH_DURATION) {
2448 /* it's starting to feel reaaaally dirty :(
2449 if we're on a spliced seek to get duration, don't reset streams,
2450 we'll need them for the delayed seek */
2451 gst_ogg_demux_reset_streams (ogg);
2454 case GST_EVENT_SEGMENT:
2455 GST_DEBUG_OBJECT (ogg, "got a new segment event");
2460 gst_event_copy_segment (event, &segment);
2462 if (segment.format == GST_FORMAT_BYTES) {
2463 GST_PUSH_LOCK (ogg);
2464 ogg->push_byte_offset = segment.start;
2465 ogg->push_last_seek_offset = segment.start;
2467 if (gst_event_get_seqnum (event) == ogg->seqnum) {
2468 GstSeekType stop_type = GST_SEEK_TYPE_NONE;
2469 if (ogg->push_seek_time_original_stop != -1)
2470 stop_type = GST_SEEK_TYPE_SET;
2471 gst_segment_do_seek (&ogg->segment, ogg->push_seek_rate,
2472 GST_FORMAT_TIME, ogg->push_seek_flags, GST_SEEK_TYPE_SET,
2473 ogg->push_seek_time_original_target, stop_type,
2474 ogg->push_seek_time_original_stop, &update);
2475 } else if (ogg->seqnum == GST_SEQNUM_INVALID) {
2476 ogg->seqnum = GST_EVENT_SEQNUM (event);
2479 if (!ogg->pullmode && !(ogg->push_seek_flags & GST_SEEK_FLAG_FLUSH)) {
2481 GstOggChain *chain = ogg->current_chain;
2483 ogg->push_seek_flags = 0;
2485 /* This will happen when we bisect, as we clear the chain when
2486 we do the first seek. On subsequent ones, we just reset the
2487 ogg sync object as we already reset the chain */
2488 GST_DEBUG_OBJECT (ogg, "No chain, just resetting ogg sync");
2489 ogg_sync_reset (&ogg->sync);
2491 /* reset pad push mode seeking state */
2492 for (i = 0; i < chain->streams->len; i++) {
2493 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2494 pad->push_kf_time = GST_CLOCK_TIME_NONE;
2495 pad->push_sync_time = GST_CLOCK_TIME_NONE;
2497 ogg_sync_reset (&ogg->sync);
2498 gst_ogg_demux_reset_streams (ogg);
2502 if (!ogg->pullmode) {
2503 if (ogg->seek_event_drop_till == gst_event_get_seqnum (event)) {
2504 GST_DEBUG_OBJECT (ogg,
2505 "Got event seqnum %u, stopping dropping (ogg->seqnum:%u)",
2506 ogg->seek_event_drop_till, ogg->seqnum);
2507 ogg->seek_event_drop_till = 0;
2510 GST_PUSH_UNLOCK (ogg);
2512 GST_WARNING_OBJECT (ogg, "unexpected segment format: %s",
2513 gst_format_get_name (segment.format));
2517 gst_event_unref (event);
2522 gboolean drop = FALSE;
2523 GST_DEBUG_OBJECT (ogg, "got an EOS event");
2524 GST_PUSH_LOCK (ogg);
2525 if (ogg->push_state == PUSH_DURATION) {
2526 GST_DEBUG_OBJECT (ogg, "Got EOS while determining length");
2527 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
2528 if (res != GST_FLOW_OK) {
2529 GST_DEBUG_OBJECT (ogg, "Error seeking back after duration check: %d",
2532 gst_event_unref (event);
2536 if (ogg->seek_event_drop_till > 0) {
2537 GST_DEBUG_OBJECT (ogg, "Dropping EOS (seqnum:%u) because we have "
2538 "a pending seek (seqnum:%u)", gst_event_get_seqnum (event),
2539 ogg->seek_event_drop_till);
2542 GST_PUSH_UNLOCK (ogg);
2546 res = gst_ogg_demux_send_event (ogg, event);
2548 gst_event_unref (event);
2549 if (ogg->current_chain == NULL) {
2550 GST_WARNING_OBJECT (ogg,
2551 "EOS while trying to retrieve chain, seeking disabled");
2552 ogg->push_disable_seeking = TRUE;
2558 res = gst_pad_event_default (pad, parent, event);
2565 /* submit the given buffer to the ogg sync */
2566 static GstFlowReturn
2567 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
2571 GstFlowReturn ret = GST_FLOW_OK;
2573 size = gst_buffer_get_size (buffer);
2574 GST_DEBUG_OBJECT (ogg, "submitting %" G_GSIZE_FORMAT " bytes", size);
2575 if (G_UNLIKELY (size == 0))
2578 oggbuffer = ogg_sync_buffer (&ogg->sync, size);
2579 if (G_UNLIKELY (oggbuffer == NULL))
2582 gst_buffer_extract (buffer, 0, oggbuffer, size);
2584 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2587 if (!ogg->pullmode) {
2588 GST_PUSH_LOCK (ogg);
2589 ogg->push_byte_offset += size;
2590 GST_PUSH_UNLOCK (ogg);
2594 gst_buffer_unref (buffer);
2601 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2602 (NULL), ("failed to get ogg sync buffer"));
2603 ret = GST_FLOW_ERROR;
2608 GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2609 ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2610 ret = GST_FLOW_ERROR;
2615 /* in random access mode this code updates the current read position
2616 * and resets the ogg sync buffer so that the next read will happen
2617 * from this new location.
2620 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
2622 GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
2624 ogg->offset = offset;
2625 ogg->read_offset = offset;
2626 ogg_sync_reset (&ogg->sync);
2629 /* read more data from the current offset and submit to
2630 * the ogg sync layer.
2632 static GstFlowReturn
2633 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
2640 GST_LOG_OBJECT (ogg,
2641 "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
2642 ogg->read_offset, ogg->length, end_offset);
2644 if (end_offset > 0 && ogg->read_offset >= end_offset)
2645 goto boundary_reached;
2647 if (ogg->read_offset == ogg->length)
2650 oggbuffer = ogg_sync_buffer (&ogg->sync, ogg->chunk_size);
2651 if (G_UNLIKELY (oggbuffer == NULL))
2655 gst_buffer_new_wrapped_full (0, oggbuffer, ogg->chunk_size, 0,
2656 ogg->chunk_size, NULL, NULL);
2659 gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, ogg->chunk_size,
2661 if (ret != GST_FLOW_OK)
2664 size = gst_buffer_get_size (buffer);
2666 if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2669 ogg->read_offset += size;
2670 gst_buffer_unref (buffer);
2677 GST_LOG_OBJECT (ogg, "reached boundary");
2678 return GST_FLOW_LIMIT;
2682 GST_LOG_OBJECT (ogg, "reached EOS");
2683 return GST_FLOW_EOS;
2687 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2688 (NULL), ("failed to get ogg sync buffer"));
2689 return GST_FLOW_ERROR;
2693 GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
2694 gst_flow_get_name (ret));
2695 gst_buffer_unref (buffer);
2700 GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2701 ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2702 gst_buffer_unref (buffer);
2703 return GST_FLOW_ERROR;
2707 /* Read the next page from the current offset.
2708 * boundary: number of bytes ahead we allow looking for;
2711 * @offset will contain the offset the next page starts at when this function
2712 * returns GST_FLOW_OK.
2714 * GST_FLOW_EOS is returned on EOS.
2716 * GST_FLOW_LIMIT is returned when we did not find a page before the
2717 * boundary. If @boundary is -1, this is never returned.
2719 * Any other error returned while retrieving data from the peer is returned as
2722 static GstFlowReturn
2723 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
2724 gint64 boundary, gint64 * offset)
2726 gint64 end_offset = -1;
2729 GST_LOG_OBJECT (ogg,
2730 "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
2731 G_GINT64_FORMAT, ogg->offset, boundary);
2734 end_offset = ogg->offset + boundary;
2739 if (end_offset > 0 && ogg->offset >= end_offset)
2740 goto boundary_reached;
2742 more = ogg_sync_pageseek (&ogg->sync, og);
2744 GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
2747 /* skipped n bytes */
2748 ogg->offset -= more;
2749 GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
2751 } else if (more == 0) {
2752 /* we need more data */
2754 goto boundary_reached;
2756 GST_LOG_OBJECT (ogg, "need more data");
2757 ret = gst_ogg_demux_get_data (ogg, end_offset);
2758 if (ret != GST_FLOW_OK)
2761 gint64 res_offset = ogg->offset;
2763 /* got a page. Return the offset at the page beginning,
2764 advance the internal offset past the page end */
2766 *offset = res_offset;
2769 ogg->offset += more;
2771 GST_LOG_OBJECT (ogg,
2772 "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
2773 G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
2774 ogg_page_serialno (og), ogg->offset,
2775 (gint64) ogg_page_granulepos (og));
2779 GST_LOG_OBJECT (ogg, "returning %d", ret);
2786 GST_LOG_OBJECT (ogg,
2787 "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
2788 ogg->offset, end_offset);
2789 return GST_FLOW_LIMIT;
2793 /* from the current offset, find the previous page, seeking backwards
2794 * until we find the page.
2796 static GstFlowReturn
2797 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
2800 gint64 begin = ogg->offset;
2802 gint64 cur_offset = -1;
2804 GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
2806 while (cur_offset == -1) {
2807 begin -= ogg->chunk_size;
2811 /* seek ogg->chunk_size back */
2812 GST_LOG_OBJECT (ogg, "seeking back to %" G_GINT64_FORMAT, begin);
2813 gst_ogg_demux_seek (ogg, begin);
2815 /* now continue reading until we run out of data, if we find a page
2816 * start, we save it. It might not be the final page as there could be
2817 * another page after this one. */
2818 while (ogg->offset < end) {
2819 gint64 new_offset, boundary;
2821 /* An Ogg page cannot be more than a bit less than 64 KB, so we can
2822 bound the boundary to that size when searching backwards if we
2823 haven't found a page yet. So the most we have to look at is twice
2824 the max page size, which is the worst case if we start scanning
2825 just after a large page, after which also lies a large page. */
2826 boundary = end - ogg->offset;
2827 if (boundary > 2 * MAX_OGG_PAGE_SIZE)
2828 boundary = 2 * MAX_OGG_PAGE_SIZE;
2830 ret = gst_ogg_demux_get_next_page (ogg, og, boundary, &new_offset);
2831 /* we hit the upper limit, offset contains the last page start */
2832 if (ret == GST_FLOW_LIMIT) {
2833 GST_LOG_OBJECT (ogg, "hit limit");
2836 /* something went wrong */
2837 if (ret == GST_FLOW_EOS) {
2839 GST_LOG_OBJECT (ogg, "got unexpected");
2842 } else if (ret != GST_FLOW_OK) {
2843 GST_LOG_OBJECT (ogg, "got error %d", ret);
2847 GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
2849 /* offset is next page start */
2850 cur_offset = new_offset;
2854 GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
2856 /* we have the offset. Actually snork and hold the page now */
2857 gst_ogg_demux_seek (ogg, cur_offset);
2858 ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
2859 if (ret != GST_FLOW_OK) {
2860 GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
2862 /* this shouldn't be possible */
2867 *offset = cur_offset;
2874 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
2877 GstOggChain *chain = ogg->current_chain;
2882 GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
2884 /* send EOS on all the pads */
2885 for (i = 0; i < chain->streams->len; i++) {
2886 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2892 event = gst_event_new_eos ();
2893 gst_event_set_seqnum (event, ogg->seqnum);
2894 gst_pad_push_event (GST_PAD_CAST (pad), event);
2896 GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
2898 /* deactivate first */
2899 gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
2901 gst_flow_combiner_remove_pad (ogg->flowcombiner, GST_PAD_CAST (pad));
2903 gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2908 /* if we cannot seek back to the chain, we can destroy the chain
2910 if (!ogg->pullmode) {
2911 if (ogg->building_chain == chain)
2912 ogg->building_chain = NULL;
2913 ogg->current_chain = NULL;
2914 gst_ogg_chain_free (chain);
2921 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
2924 GstStructure *structure;
2925 GValue array = { 0 };
2927 GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
2929 if (G_UNLIKELY (!caps))
2931 if (G_UNLIKELY (!headers))
2934 caps = gst_caps_make_writable (caps);
2935 structure = gst_caps_get_structure (caps, 0);
2937 g_value_init (&array, GST_TYPE_ARRAY);
2940 GValue value = { 0 };
2942 ogg_packet *op = headers->data;
2944 buffer = gst_buffer_new_and_alloc (op->bytes);
2946 gst_buffer_fill (buffer, 0, op->packet, op->bytes);
2947 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_HEADER);
2948 g_value_init (&value, GST_TYPE_BUFFER);
2949 gst_value_take_buffer (&value, buffer);
2950 gst_value_array_append_value (&array, &value);
2951 g_value_unset (&value);
2952 headers = headers->next;
2955 gst_structure_take_value (structure, "streamheader", &array);
2956 GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
2962 gst_ogg_demux_push_queued_buffers (GstOggDemux * ogg, GstOggPad * pad)
2966 /* push queued packets */
2967 for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
2968 ogg_packet *p = walk->data;
2970 gst_ogg_demux_chain_peer (pad, p, TRUE);
2971 _ogg_packet_free (p);
2973 /* and free the queued buffers */
2974 g_list_free (pad->map.queued);
2975 pad->map.queued = NULL;
2979 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
2983 gint bitrate, idx_bitrate;
2985 g_return_val_if_fail (chain != NULL, FALSE);
2987 if (chain == ogg->current_chain) {
2989 gst_event_unref (event);
2991 for (i = 0; i < chain->streams->len; i++) {
2992 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2993 gst_ogg_demux_push_queued_buffers (ogg, pad);
2999 GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
3001 bitrate = idx_bitrate = 0;
3003 /* first add the pads */
3004 for (i = 0; i < chain->streams->len; i++) {
3009 pad = g_array_index (chain->streams, GstOggPad *, i);
3011 if (pad->map.idx_bitrate)
3012 idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
3014 bitrate += pad->map.bitrate;
3017 gst_ogg_pad_mark_discont (pad);
3018 pad->last_ret = GST_FLOW_OK;
3020 if (pad->map.is_skeleton || pad->map.is_cmml || pad->added
3024 GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
3026 /* activate first */
3027 gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
3030 gst_pad_create_stream_id_printf (GST_PAD (pad), GST_ELEMENT_CAST (ogg),
3031 "%08x", pad->map.serialno);
3033 gst_pad_get_sticky_event (ogg->sinkpad, GST_EVENT_STREAM_START, 0);
3035 if (gst_event_parse_group_id (ss_event, &ogg->group_id))
3036 ogg->have_group_id = TRUE;
3038 ogg->have_group_id = FALSE;
3039 gst_event_unref (ss_event);
3040 } else if (!ogg->have_group_id) {
3041 ogg->have_group_id = TRUE;
3042 ogg->group_id = gst_util_group_id_next ();
3044 ss_event = gst_event_new_stream_start (stream_id);
3045 if (ogg->have_group_id)
3046 gst_event_set_group_id (ss_event, ogg->group_id);
3048 gst_pad_push_event (GST_PAD (pad), ss_event);
3051 /* Set headers on caps */
3053 gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers);
3054 gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps);
3056 gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
3058 gst_flow_combiner_add_pad (ogg->flowcombiner, GST_PAD_CAST (pad));
3060 /* prefer the index bitrate over the ones encoded in the streams */
3061 ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
3063 /* after adding the new pads, remove the old pads */
3064 gst_ogg_demux_deactivate_current_chain (ogg);
3066 GST_DEBUG_OBJECT (ogg, "Setting current chain to %p", chain);
3067 ogg->current_chain = chain;
3069 /* we are finished now */
3070 gst_element_no_more_pads (GST_ELEMENT (ogg));
3072 GST_DEBUG_OBJECT (ogg, "starting chain");
3074 /* then send out any headers and queued packets */
3075 for (i = 0; i < chain->streams->len; i++) {
3080 pad = g_array_index (chain->streams, GstOggPad *, i);
3082 /* Skip pads that were not added, e.g. Skeleton streams */
3086 /* FIXME, must be sent from the streaming thread */
3088 gst_pad_push_event (GST_PAD_CAST (pad), gst_event_ref (event));
3090 /* FIXME also streaming thread */
3091 if (pad->map.taglist) {
3092 GST_DEBUG_OBJECT (ogg, "pushing tags");
3093 gst_pad_push_event (GST_PAD_CAST (pad),
3094 gst_event_new_tag (pad->map.taglist));
3095 pad->map.taglist = NULL;
3098 tags = gst_tag_list_new (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL);
3099 gst_tag_list_set_scope (tags, GST_TAG_SCOPE_GLOBAL);
3100 gst_pad_push_event (GST_PAD (pad), gst_event_new_tag (tags));
3102 GST_DEBUG_OBJECT (ogg, "pushing headers");
3104 for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
3105 ogg_packet *p = walk->data;
3107 gst_ogg_demux_chain_peer (pad, p, TRUE);
3110 GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
3111 gst_ogg_demux_push_queued_buffers (ogg, pad);
3115 gst_event_unref (event);
3121 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
3122 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
3123 gint64 * offset, gboolean only_serial_no, gint serialno)
3131 GST_DEBUG_OBJECT (ogg,
3132 "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
3134 GST_DEBUG_OBJECT (ogg,
3135 "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
3136 GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
3137 GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
3139 /* perform the seek */
3140 while (begin < end) {
3143 if ((end - begin < ogg->chunk_size) || (endtime == begintime)) {
3146 /* take a (pretty decent) guess, avoiding overflow */
3147 gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
3150 (target - begintime) / GST_MSECOND * rate + begin - ogg->chunk_size;
3152 if (bisect <= begin)
3154 GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
3156 gst_ogg_demux_seek (ogg, bisect);
3158 while (begin < end) {
3161 GST_DEBUG_OBJECT (ogg,
3162 "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
3163 ", end %" G_GINT64_FORMAT, bisect, begin, end);
3165 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
3166 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
3169 if (ret == GST_FLOW_LIMIT) {
3170 /* we hit the upper limit, go back a bit */
3171 if (bisect <= begin + 1) {
3172 end = begin; /* found it */
3177 bisect -= ogg->chunk_size;
3178 if (bisect <= begin)
3181 gst_ogg_demux_seek (ogg, bisect);
3183 } else if (ret == GST_FLOW_OK) {
3184 /* found offset of next ogg page */
3186 GstClockTime granuletime;
3189 /* get the granulepos */
3190 GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
3192 granulepos = ogg_page_granulepos (&og);
3193 if (granulepos == -1) {
3194 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
3198 /* Avoid seeking to an incorrect granuletime by only considering
3199 the stream for which we found the earliest time */
3200 if (only_serial_no && ogg_page_serialno (&og) != serialno)
3203 /* get the stream */
3204 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
3205 if (pad == NULL || pad->map.is_skeleton)
3208 /* convert granulepos to time */
3209 granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
3211 if (granuletime < pad->start_time)
3214 GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to PTS %"
3215 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
3217 granuletime -= pad->start_time;
3218 granuletime += chain->begin_time;
3220 GST_DEBUG_OBJECT (ogg,
3221 "found page with granule %" G_GINT64_FORMAT " and time %"
3222 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
3224 if (granuletime < target) {
3225 best = result; /* raw offset of packet with granulepos */
3226 begin = ogg->offset; /* raw offset of next page */
3227 begintime = granuletime;
3229 bisect = begin; /* *not* begin + 1 */
3231 if (bisect <= begin + 1) {
3232 end = begin; /* found it */
3234 if (end == ogg->offset) { /* we're pretty close - we'd be stuck in */
3236 bisect -= ogg->chunk_size; /* an endless loop otherwise. */
3237 if (bisect <= begin)
3239 gst_ogg_demux_seek (ogg, bisect);
3242 endtime = granuletime;
3251 GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
3252 gst_ogg_demux_seek (ogg, best);
3260 GST_DEBUG_OBJECT (ogg, "got a seek error");
3266 do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
3267 gint64 end, gint64 begintime, gint64 endtime, gint64 target,
3268 gint64 * p_offset, gint64 * p_timestamp)
3271 guint64 timestamp, offset;
3272 guint64 r_timestamp, r_offset;
3273 gboolean result = FALSE;
3275 target -= begintime;
3280 for (i = 0; i < chain->streams->len; i++) {
3281 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3284 if (gst_ogg_map_search_index (&pad->map, TRUE, ×tamp, &offset)) {
3285 GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
3288 if (r_offset == -1 || offset < r_offset) {
3290 r_timestamp = timestamp;
3297 *p_timestamp = r_timestamp;
3299 *p_offset = r_offset;
3305 * do seek to time @position, return FALSE or chain and TRUE
3308 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
3309 gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
3312 GstOggChain *chain = NULL;
3314 gint64 begintime, endtime;
3315 gint64 target, keytarget;
3322 gboolean found_keyframe = FALSE;
3323 GstClockTime ts, first_ts = GST_CLOCK_TIME_NONE;
3325 position = segment->position;
3327 /* first find the chain to search in */
3328 total = ogg->total_time;
3329 if (ogg->chains->len == 0)
3332 for (i = ogg->chains->len - 1; i >= 0; i--) {
3333 chain = g_array_index (ogg->chains, GstOggChain *, i);
3334 total -= chain->total_time;
3335 if (position >= total)
3339 /* first step, locate page containing the required data */
3340 begin = chain->offset;
3341 end = chain->end_offset;
3342 begintime = chain->begin_time;
3343 endtime = begintime + chain->total_time;
3344 target = position - total + begintime;
3346 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
3350 /* second step: find pages for all relevant streams. We use the
3351 * keyframe_granule to keep track of which ones we saw. If we have
3352 * seen a page for each stream we can calculate the positions of
3354 * Relevant streams are defined as those streams which are not
3355 * Skeleton (which only has header pages). Discontinuous streams
3356 * such as Kate and CMML are currently excluded, as they could
3357 * cause performance issues if there are few pages in the area.
3358 * TODO: We might want to include them on a flag, if we want to
3359 * not miss a subtitle (Kate has repeat packets for this purpose,
3360 * but a stream does not have to use them). */
3361 pending = chain->streams->len;
3362 for (i = 0; i < chain->streams->len; i++) {
3363 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3365 GST_WARNING_OBJECT (ogg, "No pad at index %d", i);
3369 if (pad->map.is_skeleton) {
3370 GST_DEBUG_OBJECT (ogg, "Not finding pages for Skeleton stream %08x",
3375 if (pad->map.is_sparse) {
3376 GST_DEBUG_OBJECT (ogg, "Not finding pages for sparse stream %08x (%s)",
3377 pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map));
3382 GST_DEBUG_OBJECT (ogg, "find keyframes for %d/%d streams", pending,
3383 chain->streams->len);
3385 /* figure out where the keyframes are */
3392 GstClockTime keyframe_time, granule_time;
3394 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
3395 GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
3397 if (ret == GST_FLOW_LIMIT) {
3398 GST_LOG_OBJECT (ogg, "reached limit");
3400 } else if (ret != GST_FLOW_OK)
3403 /* get the stream */
3404 pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
3408 if (pad->map.is_skeleton || pad->map.is_sparse)
3411 granulepos = ogg_page_granulepos (&og);
3412 if (granulepos == -1 || granulepos == 0) {
3413 GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
3417 /* We have a valid granpos, and we bail out when the time since the
3418 first seen time to the time corresponding to this granpos is larger
3419 then a threshold, to guard against some streams having large holes
3420 (eg, a stream ending early, which would cause seeking after that
3421 to fill up a queue for streams still active). */
3422 ts = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granulepos);
3423 if (GST_CLOCK_TIME_IS_VALID (ts)) {
3424 if (first_ts == GST_CLOCK_TIME_NONE) {
3425 GST_WARNING_OBJECT (pad, "Locking on pts %" GST_TIME_FORMAT,
3426 GST_TIME_ARGS (ts));
3429 if (ts - first_ts > SEEK_GIVE_UP_THRESHOLD) {
3430 GST_WARNING_OBJECT (pad,
3431 "No data found for %" GST_TIME_FORMAT ", giving up",
3432 GST_TIME_ARGS (SEEK_GIVE_UP_THRESHOLD));
3433 found_keyframe = FALSE;
3439 /* in reverse we want to go past the page with the lower timestamp */
3440 if (segment->rate < 0.0) {
3441 /* get time for this pad */
3442 granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
3445 /* Convert to stream time */
3446 granule_time -= pad->start_time;
3447 granule_time += chain->begin_time;
3449 GST_LOG_OBJECT (ogg,
3450 "looking at page with time %" GST_TIME_FORMAT ", target %"
3451 GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
3452 GST_TIME_ARGS (target));
3453 if (granule_time < target)
3457 /* we've seen this pad before */
3458 if (pad->keyframe_granule != -1)
3461 /* convert granule of this pad to the granule of the keyframe */
3462 pad->keyframe_granule =
3463 gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
3464 GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
3465 pad->keyframe_granule);
3467 /* get time of the keyframe */
3469 gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
3470 GST_LOG_OBJECT (ogg,
3471 "stream %08x keyframe granule PTS %" GST_TIME_FORMAT
3472 " target %" GST_TIME_FORMAT,
3473 pad->map.serialno, GST_TIME_ARGS (keyframe_time),
3474 GST_TIME_ARGS (keytarget));
3476 /* collect smallest value */
3477 if (keyframe_time != -1) {
3478 keyframe_time -= pad->start_time;
3479 keyframe_time += begintime;
3480 if (keyframe_time < keytarget) {
3481 serialno = pad->map.serialno;
3482 keytarget = keyframe_time;
3483 found_keyframe = TRUE;
3484 GST_LOG_OBJECT (ogg, "storing keytarget %" GST_TIME_FORMAT,
3485 GST_TIME_ARGS (keytarget));
3495 /* for negative rates we will get to the keyframe backwards */
3496 if (segment->rate < 0.0)
3499 /* No keyframe found, no need to bisect again, keytarget == target here */
3500 if (!found_keyframe)
3503 if (keytarget != target) {
3504 GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
3505 GST_TIME_ARGS (keytarget));
3507 /* last step, seek to the location of the keyframe */
3508 if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
3509 keytarget, &best, TRUE, serialno))
3512 /* seek back to previous position */
3513 GST_LOG_OBJECT (ogg, "keyframe on target");
3514 gst_ogg_demux_seek (ogg, best);
3519 if (segment->rate > 0.0)
3520 segment->time = keytarget;
3521 segment->position = keytarget - begintime;
3530 GST_DEBUG_OBJECT (ogg, "no chains");
3535 GST_DEBUG_OBJECT (ogg, "got a seek error");
3540 /* does not take ownership of the event */
3542 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
3544 GstOggChain *chain = NULL;
3546 gboolean accurate, keyframe;
3550 GstSeekType start_type, stop_type;
3556 GST_DEBUG_OBJECT (ogg, "seek with event");
3558 gst_event_parse_seek (event, &rate, &format, &flags,
3559 &start_type, &start, &stop_type, &stop);
3561 /* we can only seek on time */
3562 if (format != GST_FORMAT_TIME) {
3563 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3566 seqnum = gst_event_get_seqnum (event);
3568 GST_DEBUG_OBJECT (ogg, "seek without event");
3572 seqnum = gst_util_seqnum_next ();
3575 GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
3577 accurate = flags & GST_SEEK_FLAG_ACCURATE;
3578 keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
3580 gst_pad_pause_task (ogg->sinkpad);
3582 /* now grab the stream lock so that streaming cannot continue, for
3583 * non flushing seeks when the element is in PAUSED this could block
3585 GST_PAD_STREAM_LOCK (ogg->sinkpad);
3588 gst_segment_do_seek (&ogg->segment, rate, format, flags,
3589 start_type, start, stop_type, stop, &update);
3592 GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
3593 GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
3594 GST_TIME_ARGS (ogg->segment.stop));
3599 /* reset all ogg streams now, need to do this from within the lock to
3600 * make sure the streaming thread is not messing with the stream */
3601 for (i = 0; i < ogg->chains->len; i++) {
3602 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3604 gst_ogg_chain_reset (chain);
3608 /* for reverse we will already seek accurately */
3609 res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
3611 /* seek failed, make sure we continue the current chain */
3613 GST_DEBUG_OBJECT (ogg, "seek failed");
3614 chain = ogg->current_chain;
3616 GST_DEBUG_OBJECT (ogg, "seek success");
3622 /* now we have a new position, prepare for streaming again */
3627 gint64 position, begin_time;
3630 /* we need this to see how far inside the chain we need to start */
3631 if (chain->begin_time != GST_CLOCK_TIME_NONE)
3632 begin_time = chain->begin_time;
3636 /* segment.start gives the start over all chains, we calculate the amount
3637 * of time into this chain we need to start */
3638 start = ogg->segment.start - begin_time;
3639 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3640 start += chain->segment_start;
3642 if ((stop = ogg->segment.stop) == -1)
3643 stop = ogg->segment.duration;
3645 /* segment.stop gives the stop time over all chains, calculate the amount of
3646 * time we need to stop in this chain */
3648 if (stop > begin_time)
3652 stop += chain->segment_start;
3653 /* we must stop when this chain ends and switch to the next chain to play
3654 * the remainder of the segment. */
3655 stop = MIN (stop, chain->segment_stop);
3658 position = ogg->segment.position;
3659 if (chain->segment_start != GST_CLOCK_TIME_NONE)
3660 position += chain->segment_start;
3662 gst_segment_copy_into (&ogg->segment, &segment);
3664 /* create the segment event we are going to send out */
3665 if (ogg->segment.rate >= 0.0) {
3666 segment.start = position;
3667 segment.stop = stop;
3669 segment.start = start;
3670 segment.stop = position;
3672 event = gst_event_new_segment (&segment);
3673 gst_event_set_seqnum (event, seqnum);
3675 if (chain != ogg->current_chain) {
3676 /* switch to different chain, send segment on new chain */
3677 gst_ogg_demux_activate_chain (ogg, chain, event);
3679 /* mark discont and send segment on current chain */
3680 gst_ogg_chain_mark_discont (chain);
3681 /* This event should be sent from the streaming thread (sink pad task) */
3682 if (ogg->newsegment)
3683 gst_event_unref (ogg->newsegment);
3684 ogg->newsegment = event;
3687 /* notify start of new segment */
3688 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3689 GstMessage *message;
3691 message = gst_message_new_segment_start (GST_OBJECT (ogg),
3692 GST_FORMAT_TIME, ogg->segment.position);
3693 gst_message_set_seqnum (message, seqnum);
3695 gst_element_post_message (GST_ELEMENT (ogg), message);
3698 ogg->seqnum = seqnum;
3699 /* restart our task since it might have been stopped when we did the
3701 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3702 ogg->sinkpad, NULL);
3705 /* streaming can continue now */
3706 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3710 gst_event_unref (event);
3716 GST_DEBUG_OBJECT (ogg, "seek failed");
3722 GST_DEBUG_OBJECT (ogg, "no chain to seek in");
3723 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3730 gst_ogg_demux_get_duration_push (GstOggDemux * ogg, int flags)
3732 /* In push mode, we get to the end of the stream to get the duration */
3736 /* A full Ogg page can be almost 64 KB. There's no guarantee that there'll be a
3737 granpos there, but it's fairly likely */
3738 position = ogg->push_byte_length - DURATION_CHUNK_OFFSET;
3742 GST_DEBUG_OBJECT (ogg,
3743 "Getting duration, seeking near the end, to %" G_GINT64_FORMAT, position);
3744 ogg->push_state = PUSH_DURATION;
3745 /* do not read the last byte */
3746 sevent = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET,
3747 position, GST_SEEK_TYPE_SET, ogg->push_byte_length - 1);
3748 gst_event_replace (&ogg->seek_event, sevent);
3749 ogg->seek_event_drop_till = gst_event_get_seqnum (sevent);
3750 gst_event_unref (sevent);
3751 g_mutex_lock (&ogg->seek_event_mutex);
3752 g_cond_broadcast (&ogg->seek_event_cond);
3753 g_mutex_unlock (&ogg->seek_event_mutex);
3758 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3761 if (ogg->push_byte_length < 0) {
3764 GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3765 if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3769 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &length);
3770 if (res && length > 0) {
3771 ogg->push_byte_length = length;
3772 GST_DEBUG_OBJECT (ogg,
3773 "File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
3775 GST_DEBUG_OBJECT (ogg, "File byte length unknown, assuming live");
3776 ogg->push_disable_seeking = TRUE;
3777 gst_object_unref (peer);
3780 res = gst_pad_query_duration (peer, GST_FORMAT_TIME, &length);
3781 gst_object_unref (peer);
3782 if (res && length >= 0) {
3783 ogg->push_time_length = length;
3784 GST_DEBUG_OBJECT (ogg, "File time length %" GST_TIME_FORMAT,
3785 GST_TIME_ARGS (ogg->push_time_length));
3786 } else if (!ogg->push_disable_seeking) {
3789 res = gst_ogg_demux_get_duration_push (ogg, flags);
3791 GST_DEBUG_OBJECT (ogg,
3792 "File time length unknown, trying to determine");
3793 ogg->push_mode_seek_delayed_event = NULL;
3795 GST_DEBUG_OBJECT (ogg,
3796 "Let me intercept this innocent looking seek request");
3797 ogg->push_mode_seek_delayed_event = gst_event_copy (event);
3808 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3811 gboolean res = TRUE;
3815 GstSeekType start_type, stop_type;
3819 gint64 best, best_time;
3822 GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3824 gst_event_parse_seek (event, &rate, &format, &flags,
3825 &start_type, &start, &stop_type, &stop);
3827 if (format != GST_FORMAT_TIME) {
3828 GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3832 if (start_type != GST_SEEK_TYPE_SET) {
3833 GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3837 /* If stop is unset, make sure it is -1, as this value will be tested
3838 later to check whether stop is set or not */
3839 if (stop_type == GST_SEEK_TYPE_NONE)
3842 GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3843 GST_TIME_ARGS (start));
3845 chain = ogg->current_chain;
3847 GST_WARNING_OBJECT (ogg, "No chain to seek on");
3851 /* start accessing push_* members */
3852 GST_PUSH_LOCK (ogg);
3854 /* not if we disabled seeking (chained streams) */
3855 if (ogg->push_disable_seeking) {
3856 GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3860 /* not when we're trying to work out duration */
3861 if (ogg->push_state == PUSH_DURATION) {
3862 GST_DEBUG_OBJECT (ogg, "Busy working out duration, try again later");
3866 /* actually, not if we're doing any seeking already */
3867 if (ogg->push_state != PUSH_PLAYING) {
3868 GST_DEBUG_OBJECT (ogg, "Already doing some seeking, try again later");
3872 /* on the first seek, get length if we can */
3873 if (!gst_ogg_demux_check_duration_push (ogg, flags, event)) {
3874 GST_PUSH_UNLOCK (ogg);
3878 if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
3879 /* the index gave some result */
3880 GST_DEBUG_OBJECT (ogg,
3881 "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
3884 if (ogg->push_time_length > 0) {
3885 /* if we know the time length, we know the full segment bitrate */
3886 GST_DEBUG_OBJECT (ogg, "Using real file bitrate");
3888 gst_util_uint64_scale (ogg->push_byte_length, 8 * GST_SECOND,
3889 ogg->push_time_length);
3890 } else if (ogg->push_time_offset > 0) {
3891 /* get a first approximation using known bitrate to the current position */
3892 GST_DEBUG_OBJECT (ogg, "Using file bitrate so far");
3894 gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
3895 ogg->push_time_offset);
3896 } else if (ogg->bitrate > 0) {
3897 /* nominal bitrate is better than nothing, even if it lies often */
3898 GST_DEBUG_OBJECT (ogg, "Using nominal bitrate");
3899 bitrate = ogg->bitrate;
3902 GST_DEBUG_OBJECT (ogg,
3903 "At stream start, and no nominal bitrate, using some random magic "
3905 /* the bisection, once started, should give us a better approximation */
3908 best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3911 /* offset by typical page length, and ensure our best guess is within
3912 reasonable bounds */
3913 best -= ogg->chunk_size;
3916 if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3917 best = ogg->push_byte_length - 1;
3919 /* set up bisection search */
3920 ogg->push_offset0 = 0;
3921 ogg->push_offset1 = ogg->push_byte_length - 1;
3922 ogg->push_time0 = ogg->push_start_time;
3923 ogg->push_time1 = ogg->push_time_length;
3924 ogg->seqnum = gst_event_get_seqnum (event);
3925 ogg->push_seek_time_target = start;
3926 ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
3927 ogg->push_seek_time_original_target = start;
3928 ogg->push_seek_time_original_stop = stop;
3929 ogg->push_state = PUSH_BISECT1;
3930 ogg->seek_secant = FALSE;
3931 ogg->seek_undershot = FALSE;
3933 if (flags & GST_SEEK_FLAG_FLUSH) {
3934 /* reset pad push mode seeking state */
3935 for (i = 0; i < chain->streams->len; i++) {
3936 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3937 pad->push_kf_time = GST_CLOCK_TIME_NONE;
3938 pad->push_sync_time = GST_CLOCK_TIME_NONE;
3942 GST_DEBUG_OBJECT (ogg,
3943 "Setting up bisection search for %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
3944 " (time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", ogg->push_offset0,
3945 ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
3946 GST_TIME_ARGS (ogg->push_time1));
3947 GST_DEBUG_OBJECT (ogg,
3948 "Target time is %" GST_TIME_FORMAT ", best first guess is %"
3949 G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target), best);
3951 ogg->push_seek_rate = rate;
3952 ogg->push_seek_flags = flags;
3953 ogg->push_mode_seek_delayed_event = NULL;
3954 ogg->push_bisection_steps[0] = 1;
3955 ogg->push_bisection_steps[1] = 0;
3956 sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
3957 start_type, best, GST_SEEK_TYPE_NONE, -1);
3958 gst_event_set_seqnum (sevent, gst_event_get_seqnum (event));
3960 gst_event_replace (&ogg->seek_event, sevent);
3961 gst_event_unref (sevent);
3962 GST_PUSH_UNLOCK (ogg);
3963 g_mutex_lock (&ogg->seek_event_mutex);
3964 g_cond_broadcast (&ogg->seek_event_cond);
3965 g_mutex_unlock (&ogg->seek_event_mutex);
3972 GST_DEBUG_OBJECT (ogg, "seek failed");
3977 GST_PUSH_UNLOCK (ogg);
3982 gst_ogg_demux_setup_seek_pull (GstOggDemux * ogg, GstEvent * event)
3987 guint32 seqnum = gst_event_get_seqnum (event);
3989 GST_DEBUG_OBJECT (ogg, "Scheduling seek: %" GST_PTR_FORMAT, event);
3990 gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
3992 flush = flags & GST_SEEK_FLAG_FLUSH;
3994 /* first step is to unlock the streaming thread if it is
3995 * blocked in a chain call, we do this by starting the flush. because
3996 * we cannot yet hold any streaming lock, we have to protect the chains
3997 * with their own lock. */
4001 tevent = gst_event_new_flush_start ();
4002 gst_event_set_seqnum (tevent, seqnum);
4004 gst_event_ref (tevent);
4005 gst_pad_push_event (ogg->sinkpad, tevent);
4007 GST_CHAIN_LOCK (ogg);
4008 for (i = 0; i < ogg->chains->len; i++) {
4009 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4012 for (j = 0; j < chain->streams->len; j++) {
4013 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
4015 gst_event_ref (tevent);
4016 gst_pad_push_event (GST_PAD (pad), tevent);
4019 GST_CHAIN_UNLOCK (ogg);
4021 gst_event_unref (tevent);
4024 gst_pad_pause_task (ogg->sinkpad);
4026 /* now grab the stream lock so that streaming cannot continue, for
4027 * non flushing seeks when the element is in PAUSED this could block
4029 GST_PAD_STREAM_LOCK (ogg->sinkpad);
4031 /* we need to stop flushing on the sinkpad as we're going to use it
4032 * next. We can do this as we have the STREAM lock now. */
4034 tevent = gst_event_new_flush_stop (TRUE);
4035 gst_event_set_seqnum (tevent, seqnum);
4036 gst_pad_push_event (ogg->sinkpad, gst_event_ref (tevent));
4037 gst_ogg_demux_send_event (ogg, tevent);
4040 gst_event_replace (&ogg->seek_event, event);
4041 gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
4042 ogg->sinkpad, NULL);
4043 GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
4049 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
4053 if (ogg->pullmode) {
4054 res = gst_ogg_demux_setup_seek_pull (ogg, event);
4056 res = gst_ogg_demux_perform_seek_push (ogg, event);
4062 /* finds each bitstream link one at a time using a bisection search
4063 * (has to begin by knowing the offset of the lb's initial page).
4064 * Recurses for each link so it can alloc the link storage after
4065 * finding them all, then unroll and fill the cache at the same time
4067 static GstFlowReturn
4068 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
4069 gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
4071 gint64 endsearched = end;
4076 GstOggChain *nextchain;
4078 GST_LOG_OBJECT (ogg,
4079 "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
4080 ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
4082 /* the below guards against garbage separating the last and
4083 * first pages of two links. */
4084 while (searched < endsearched) {
4087 if (endsearched - searched < ogg->chunk_size) {
4090 bisect = (searched + endsearched) / 2;
4093 gst_ogg_demux_seek (ogg, bisect);
4094 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
4096 if (ret == GST_FLOW_EOS) {
4097 endsearched = bisect;
4098 } else if (ret == GST_FLOW_OK) {
4099 guint32 serial = ogg_page_serialno (&og);
4101 if (!gst_ogg_chain_has_stream (chain, serial)) {
4102 endsearched = bisect;
4105 searched = offset + og.header_len + og.body_len;
4111 GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
4113 chain->end_offset = searched;
4114 ret = gst_ogg_demux_read_end_chain (ogg, chain);
4115 if (ret != GST_FLOW_OK)
4118 GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
4120 gst_ogg_demux_seek (ogg, next);
4121 ret = gst_ogg_demux_read_chain (ogg, &nextchain);
4122 if (ret == GST_FLOW_EOS) {
4125 GST_LOG_OBJECT (ogg, "no next chain");
4126 } else if (ret != GST_FLOW_OK)
4129 if (searched < end && nextchain != NULL) {
4130 ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
4131 end, nextchain, m + 1);
4132 if (ret != GST_FLOW_OK)
4135 GST_LOG_OBJECT (ogg, "adding chain %p", chain);
4137 g_array_insert_val (ogg->chains, 0, chain);
4143 /* read a chain from the ogg file. This code will
4144 * read all BOS pages and will create and return a GstOggChain
4145 * structure with the results.
4147 * This function will also read N pages from each stream in the
4148 * chain and submit them to the internal ogg stream parser/mapper
4149 * until we know the timestamp of the first page in the chain.
4151 static GstFlowReturn
4152 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
4155 GstOggChain *chain = NULL;
4156 gint64 offset = ogg->offset;
4161 GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
4163 /* first read the BOS pages, detect the stream types, create the internal
4164 * stream mappers, send data to them. */
4169 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
4170 if (ret != GST_FLOW_OK) {
4171 if (ret == GST_FLOW_EOS) {
4172 GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
4174 GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
4178 if (!ogg_page_bos (&og)) {
4179 GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
4180 /* if we did not find a chain yet, assume this is a bogus stream and
4183 GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
4189 if (chain == NULL) {
4190 chain = gst_ogg_chain_new (ogg);
4191 chain->offset = offset;
4194 serial = ogg_page_serialno (&og);
4195 if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
4196 GST_WARNING_OBJECT (ogg,
4197 "found serial %08x BOS page twice, ignoring", serial);
4201 pad = gst_ogg_chain_new_stream (chain, serial);
4202 gst_ogg_pad_submit_page (pad, &og);
4205 if (ret != GST_FLOW_OK || chain == NULL) {
4206 if (ret == GST_FLOW_OK) {
4207 GST_WARNING_OBJECT (ogg, "no chain was found");
4208 ret = GST_FLOW_ERROR;
4209 } else if (ret != GST_FLOW_EOS) {
4210 GST_WARNING_OBJECT (ogg, "failed to read chain");
4212 GST_DEBUG_OBJECT (ogg, "done reading chains");
4215 gst_ogg_chain_free (chain);
4222 chain->have_bos = TRUE;
4223 GST_INFO_OBJECT (ogg, "read bos pages, ");
4225 /* now read pages until each ogg stream mapper has figured out the
4226 * timestamp of the first packet in the chain */
4228 /* save the offset to the first non bos page in the chain: if searching for
4229 * pad->first_time we read past the end of the chain, we'll seek back to this
4232 offset = ogg->offset;
4237 gboolean known_serial = FALSE;
4240 serial = ogg_page_serialno (&og);
4242 for (i = 0; i < chain->streams->len; i++) {
4243 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4245 GST_LOG_OBJECT (ogg,
4246 "serial %08x time %" GST_TIME_FORMAT,
4247 pad->map.serialno, GST_TIME_ARGS (pad->start_time));
4249 if (pad->map.serialno == serial) {
4250 known_serial = TRUE;
4252 /* submit the page now, this will fill in the start_time when the
4253 * internal stream mapper finds it */
4254 gst_ogg_pad_submit_page (pad, &og);
4256 if (!pad->map.is_skeleton && pad->start_time == -1
4257 && ogg_page_eos (&og)) {
4258 /* got EOS on a pad before we could find its start_time.
4259 * We have no chance of finding a start_time for every pad so
4260 * stop searching for the other start_time(s).
4266 /* the timestamp will be filled in when we submit the pages */
4267 if (!pad->map.is_sparse)
4268 done &= (pad->start_time != GST_CLOCK_TIME_NONE);
4270 GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
4273 /* we read a page not belonging to the current chain: seek back to the
4274 * beginning of the chain
4276 if (!known_serial) {
4277 GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
4278 gst_ogg_demux_seek (ogg, offset);
4283 ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
4284 if (ret != GST_FLOW_OK)
4288 GST_LOG_OBJECT (ogg, "done reading chain");
4296 /* read the last pages from the ogg stream to get the final
4299 static GstFlowReturn
4300 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
4302 gint64 begin = chain->end_offset;
4304 gint64 last_granule = -1;
4305 GstOggPad *last_pad = NULL;
4307 gboolean done = FALSE;
4312 begin -= ogg->chunk_size;
4316 gst_ogg_demux_seek (ogg, begin);
4318 /* now continue reading until we run out of data, if we find a page
4319 * start, we save it. It might not be the final page as there could be
4320 * another page after this one. */
4321 while (ogg->offset < end) {
4322 ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
4324 if (ret == GST_FLOW_LIMIT)
4326 if (ret != GST_FLOW_OK)
4329 for (i = 0; i < chain->streams->len; i++) {
4330 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4332 if (pad->map.is_skeleton)
4335 if (pad->map.serialno == ogg_page_serialno (&og)) {
4336 gint64 granulepos = ogg_page_granulepos (&og);
4338 if (granulepos != -1) {
4339 last_granule = granulepos;
4350 chain->segment_stop =
4351 gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
4354 chain->segment_stop = GST_CLOCK_TIME_NONE;
4357 GST_INFO ("segment stop %" G_GUINT64_FORMAT ", for last granule %"
4358 G_GUINT64_FORMAT, chain->segment_stop, last_granule);
4363 /* find a pad with a given serial number
4366 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
4371 /* first look in building chain if any */
4372 if (ogg->building_chain) {
4373 pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
4378 /* then look in current chain if any */
4379 if (ogg->current_chain) {
4380 pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
4385 for (i = 0; i < ogg->chains->len; i++) {
4386 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4388 pad = gst_ogg_chain_get_stream (chain, serialno);
4395 /* find a chain with a given serial number
4397 static GstOggChain *
4398 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
4402 pad = gst_ogg_demux_find_pad (ogg, serialno);
4409 /* returns TRUE if all streams have valid start time */
4411 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
4413 gboolean res = TRUE;
4415 chain->total_time = GST_CLOCK_TIME_NONE;
4416 GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
4418 /* see if we have a start time on all streams */
4419 chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
4421 if (chain->segment_start == G_MAXUINT64) {
4422 /* not yet, stream some more data */
4424 } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
4425 /* we can calculate a total time */
4426 chain->total_time = chain->segment_stop - chain->segment_start;
4429 GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
4431 GST_DEBUG_OBJECT (ogg, "return %d", res);
4437 gst_ogg_demux_collect_info (GstOggDemux * ogg)
4441 /* collect all info */
4442 ogg->total_time = 0;
4444 for (i = 0; i < ogg->chains->len; i++) {
4445 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4447 chain->begin_time = ogg->total_time;
4449 gst_ogg_demux_collect_chain_info (ogg, chain);
4451 ogg->total_time += chain->total_time;
4453 ogg->segment.duration = ogg->total_time;
4456 /* find all the chains in the ogg file, this reads the first and
4457 * last page of the ogg stream, if they match then the ogg file has
4458 * just one chain, else we do a binary search for all chains.
4460 static GstFlowReturn
4461 gst_ogg_demux_find_chains (GstOggDemux * ogg)
4470 /* get peer to figure out length */
4471 if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
4474 /* find length to read last page, we store this for later use. */
4475 res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &ogg->length);
4476 gst_object_unref (peer);
4477 if (!res || ogg->length <= 0)
4480 GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
4482 /* read chain from offset 0, this is the first chain of the
4484 gst_ogg_demux_seek (ogg, 0);
4485 ret = gst_ogg_demux_read_chain (ogg, &chain);
4486 if (ret != GST_FLOW_OK) {
4487 if (ret == GST_FLOW_FLUSHING)
4490 goto no_first_chain;
4493 /* read page from end offset, we use this page to check if its serial
4494 * number is contained in the first chain. If this is the case then
4495 * this ogg is not a chained ogg and we can skip the scanning. */
4496 gst_ogg_demux_seek (ogg, ogg->length);
4497 ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
4498 if (ret != GST_FLOW_OK)
4501 serialno = ogg_page_serialno (&og);
4503 if (!gst_ogg_chain_has_stream (chain, serialno)) {
4504 /* the last page is not in the first stream, this means we should
4505 * find all the chains in this chained ogg. */
4507 gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
4510 /* we still call this function here but with an empty range so that
4511 * we can reuse the setup code in this routine. */
4513 gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
4514 ogg->length, chain, 0);
4516 if (ret != GST_FLOW_OK)
4519 /* all fine, collect and print */
4520 gst_ogg_demux_collect_info (ogg);
4522 /* dump our chains and streams */
4523 gst_ogg_print (ogg);
4528 /*** error cases ***/
4531 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
4532 return GST_FLOW_NOT_LINKED;
4536 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
4537 return GST_FLOW_NOT_SUPPORTED;
4541 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
4542 return GST_FLOW_ERROR;
4546 GST_DEBUG_OBJECT (ogg, "can't get last page");
4548 gst_ogg_chain_free (chain);
4553 GST_DEBUG_OBJECT (ogg, "Flushing, can't read chain");
4554 return GST_FLOW_FLUSHING;
4559 gst_ogg_demux_update_chunk_size (GstOggDemux * ogg, ogg_page * page)
4561 long size = page->header_len + page->body_len;
4562 long chunk_size = size * 2;
4563 if (chunk_size > ogg->chunk_size) {
4564 GST_LOG_OBJECT (ogg, "Updating chunk size to %ld", chunk_size);
4565 ogg->chunk_size = chunk_size;
4569 static GstFlowReturn
4570 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page, gboolean discont)
4575 GstFlowReturn result = GST_FLOW_OK;
4577 serialno = ogg_page_serialno (page);
4578 granule = ogg_page_granulepos (page);
4580 gst_ogg_demux_update_chunk_size (ogg, page);
4582 GST_LOG_OBJECT (ogg,
4583 "processing ogg page (serial %08x, "
4584 "pageno %ld, granulepos %" G_GINT64_FORMAT ", bos %d)", serialno,
4585 ogg_page_pageno (page), granule, ogg_page_bos (page));
4587 if (ogg_page_bos (page)) {
4591 /* see if we know about the chain already */
4592 chain = gst_ogg_demux_find_chain (ogg, serialno);
4598 if (chain->segment_start != GST_CLOCK_TIME_NONE)
4599 start = chain->segment_start;
4601 /* create the newsegment event we are going to send out */
4602 gst_segment_copy_into (&ogg->segment, &segment);
4603 segment.start = start;
4604 segment.stop = chain->segment_stop;
4605 segment.time = chain->begin_time;
4606 segment.base += chain->begin_time;
4607 event = gst_event_new_segment (&segment);
4608 gst_event_set_seqnum (event, ogg->seqnum);
4610 GST_DEBUG_OBJECT (ogg,
4611 "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
4612 ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
4613 GST_TIME_ARGS (chain->segment_stop),
4614 GST_TIME_ARGS (chain->begin_time));
4616 /* activate it as it means we have a non-header, this will also deactivate
4617 * the currently running chain. */
4618 gst_ogg_demux_activate_chain (ogg, chain, event);
4619 pad = gst_ogg_demux_find_pad (ogg, serialno);
4621 GstClockTime chain_time;
4622 gint64 current_time;
4624 /* this can only happen in push mode */
4628 current_time = ogg->segment.position;
4630 /* time of new chain is current time */
4631 chain_time = current_time;
4633 if (ogg->building_chain == NULL) {
4634 GstOggChain *newchain;
4636 newchain = gst_ogg_chain_new (ogg);
4637 newchain->offset = 0;
4638 /* set new chain begin time aligned with end time of old chain */
4639 newchain->begin_time = chain_time;
4640 GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
4641 GST_TIME_ARGS (chain_time));
4643 /* and this is the one we are building now */
4644 ogg->building_chain = newchain;
4646 pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
4649 pad = gst_ogg_demux_find_pad (ogg, serialno);
4652 /* Reset granule interpolation if chaining in reverse (discont = TRUE) */
4654 pad->current_granule = -1;
4656 result = gst_ogg_pad_submit_page (pad, page);
4658 GST_PUSH_LOCK (ogg);
4659 if (!ogg->pullmode && !ogg->push_disable_seeking) {
4660 /* no pad while probing for duration, we must have a chained stream,
4661 and we don't support them, so back off */
4662 GST_INFO_OBJECT (ogg, "We seem to have a chained stream, we won't seek");
4663 if (ogg->push_state == PUSH_DURATION) {
4666 res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
4667 /* Call to function above unlocks, relock */
4668 GST_PUSH_LOCK (ogg);
4669 if (res != GST_FLOW_OK)
4673 /* only once we seeked back */
4674 ogg->push_disable_seeking = TRUE;
4676 GST_PUSH_UNLOCK (ogg);
4677 /* no pad. This means an ogg page without bos has been seen for this
4678 * serialno. we just ignore it but post a warning... */
4679 GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
4680 (NULL), ("unknown ogg pad for serial %08x detected", serialno));
4683 GST_PUSH_UNLOCK (ogg);
4690 GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
4691 (NULL), ("unknown ogg chain for serial %08x detected", serialno));
4692 return GST_FLOW_ERROR;
4696 /* streaming mode, receive a buffer, parse it, create pads for
4697 * the serialno, submit pages and packets to the oggpads
4699 static GstFlowReturn
4700 gst_ogg_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
4704 GstFlowReturn result = GST_FLOW_OK;
4707 ogg = GST_OGG_DEMUX (parent);
4709 GST_PUSH_LOCK (ogg);
4710 drop = (ogg->seek_event_drop_till > 0);
4711 GST_PUSH_UNLOCK (ogg);
4713 GST_DEBUG_OBJECT (ogg, "Dropping buffer because we have a pending seek");
4714 gst_buffer_unref (buffer);
4718 GST_DEBUG_OBJECT (ogg, "enter");
4719 result = gst_ogg_demux_submit_buffer (ogg, buffer);
4721 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
4724 while (result == GST_FLOW_OK) {
4727 ret = ogg_sync_pageout (&ogg->sync, &page);
4729 /* need more data */
4732 /* discontinuity in the pages */
4733 GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
4735 result = gst_ogg_demux_handle_page (ogg, &page, FALSE);
4737 GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
4741 if (ret == 0 || result == GST_FLOW_OK) {
4742 gst_ogg_demux_sync_streams (ogg);
4744 GST_DEBUG_OBJECT (ogg, "leave with %d", result);
4749 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
4751 GstOggChain *chain = ogg->current_chain;
4752 gboolean event_sent = FALSE;
4753 gboolean res = TRUE;
4756 chain = ogg->building_chain;
4761 for (i = 0; i < chain->streams->len; i++) {
4762 GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4764 gst_event_ref (event);
4765 GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
4766 res &= gst_pad_push_event (GST_PAD (pad), event);
4772 gst_event_unref (event);
4774 if (!event_sent && GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
4775 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4776 ("EOS before finding a chain"));
4782 static GstFlowReturn
4783 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
4786 /* store the value */
4787 pad->last_ret = ret;
4788 pad->is_eos = (ret == GST_FLOW_EOS);
4790 return gst_flow_combiner_update_pad_flow (ogg->flowcombiner,
4791 GST_PAD_CAST (pad), ret);
4794 static GstFlowReturn
4795 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4798 GstBuffer *buffer = NULL;
4800 if (ogg->offset == ogg->length) {
4801 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4802 " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
4807 GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4809 gst_pad_pull_range (ogg->sinkpad, ogg->offset, ogg->chunk_size, &buffer);
4810 if (ret != GST_FLOW_OK) {
4811 GST_LOG_OBJECT (ogg, "Failed pull_range");
4815 ogg->offset += gst_buffer_get_size (buffer);
4817 if (G_UNLIKELY (ogg->newsegment)) {
4818 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4819 ogg->newsegment = NULL;
4822 ret = gst_ogg_demux_chain (ogg->sinkpad, GST_OBJECT_CAST (ogg), buffer);
4823 if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS) {
4824 GST_LOG_OBJECT (ogg, "Failed demux_chain");
4833 * We read the pages backwards and send the packets forwards. The first packet
4834 * in the page will be pushed with the DISCONT flag set.
4836 * Special care has to be taken for continued pages, which we can only decode
4837 * when we have the previous page(s).
4839 static GstFlowReturn
4840 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4846 if (ogg->offset == 0) {
4847 GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4848 " == 0", ogg->offset);
4853 GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
4854 ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
4855 if (ret != GST_FLOW_OK)
4858 ogg->offset = offset;
4860 if (G_UNLIKELY (ogg->newsegment)) {
4861 gst_ogg_demux_send_event (ogg, ogg->newsegment);
4862 ogg->newsegment = NULL;
4865 GST_LOG_OBJECT (ogg, "Handling page at offset %" G_GINT64_FORMAT,
4867 ret = gst_ogg_demux_handle_page (ogg, &page, TRUE);
4874 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4880 chain = ogg->current_chain;
4881 cur = ogg->segment.position;
4882 if (chain == NULL || cur == -1)
4885 for (i = 0; i < chain->streams->len; i++) {
4886 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4888 /* Theoretically, we should be doing this for all streams, so we're doing
4889 * it, but it might break things break things for wrongly-muxed streams
4890 * (like we used to produce once) */
4891 if ( /*stream->map.is_sparse && */ stream->position != GST_CLOCK_TIME_NONE) {
4893 /* Does this stream lag? Random threshold of 2 seconds */
4894 if (GST_CLOCK_DIFF (stream->position, cur) > (2 * GST_SECOND)) {
4895 GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
4896 "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
4897 GST_TIME_ARGS (stream->position), GST_TIME_ARGS (cur));
4899 stream->position = cur;
4901 gst_pad_push_event (GST_PAD_CAST (stream),
4902 gst_event_new_gap (stream->position, cur - stream->position));
4908 /* random access code
4910 * - first find all the chains and streams by scanning the file.
4911 * - then get and chain buffers, just like the streaming case.
4912 * - when seeking, we can use the chain info to perform the seek.
4915 gst_ogg_demux_loop (GstOggPad * pad)
4922 ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4923 seek = ogg->seek_event;
4924 ogg->seek_event = NULL;
4926 if (ogg->need_chains) {
4928 /* this is the only place where we write chains and thus need to lock. */
4929 GST_CHAIN_LOCK (ogg);
4930 ret = gst_ogg_demux_find_chains (ogg);
4931 GST_CHAIN_UNLOCK (ogg);
4932 if (ret != GST_FLOW_OK)
4933 goto chain_read_failed;
4935 ogg->need_chains = FALSE;
4937 GST_OBJECT_LOCK (ogg);
4938 ogg->running = TRUE;
4939 GST_OBJECT_UNLOCK (ogg);
4941 /* and seek to configured positions without FLUSH */
4942 res = gst_ogg_demux_perform_seek_pull (ogg, seek);
4947 res = gst_ogg_demux_perform_seek_pull (ogg, seek);
4952 if (ogg->segment.rate >= 0.0)
4953 ret = gst_ogg_demux_loop_forward (ogg);
4955 ret = gst_ogg_demux_loop_reverse (ogg);
4957 if (ret != GST_FLOW_OK)
4960 gst_ogg_demux_sync_streams (ogg);
4966 /* error was posted */
4973 GST_OBJECT_LOCK (pad);
4974 flushing = GST_PAD_IS_FLUSHING (pad);
4975 GST_OBJECT_UNLOCK (pad);
4977 ret = GST_FLOW_FLUSHING;
4979 GST_ELEMENT_FLOW_ERROR (ogg, ret);
4980 ret = GST_FLOW_ERROR;
4986 const gchar *reason = gst_flow_get_name (ret);
4987 GstEvent *event = NULL;
4989 GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4990 gst_pad_pause_task (ogg->sinkpad);
4992 if (ret == GST_FLOW_EOS) {
4993 /* perform EOS logic */
4994 if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4996 GstMessage *message;
4998 /* for segment playback we need to post when (in stream time)
4999 * we stopped, this is either stop (when set) or the duration. */
5000 if ((stop = ogg->segment.stop) == -1)
5001 stop = ogg->segment.duration;
5003 GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
5005 gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
5007 gst_message_set_seqnum (message, ogg->seqnum);
5009 gst_element_post_message (GST_ELEMENT (ogg), message);
5011 event = gst_event_new_segment_done (GST_FORMAT_TIME, stop);
5012 gst_event_set_seqnum (event, ogg->seqnum);
5013 gst_ogg_demux_send_event (ogg, event);
5016 /* normal playback, send EOS to all linked pads */
5017 GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
5018 event = gst_event_new_eos ();
5020 } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
5021 GST_ELEMENT_FLOW_ERROR (ogg, ret);
5022 event = gst_event_new_eos ();
5025 /* For wrong-state we still want to pause the task and stop
5026 * but no error message or other things are necessary.
5027 * wrong-state is no real error and will be caused by flushing,
5028 * e.g. because of a flushing seek.
5031 /* guard against corrupt/truncated files, where one can hit EOS
5032 before prerolling is done and a chain created. If we have no
5033 chain to send the event to, error out. */
5034 if (ogg->current_chain || ogg->building_chain) {
5035 gst_event_set_seqnum (event, ogg->seqnum);
5036 gst_ogg_demux_send_event (ogg, event);
5038 gst_event_unref (event);
5039 GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
5040 ("EOS before finding a chain"));
5047 /* The sink pad task function for push mode.
5048 * It just sends any seek events queued by the streaming thread.
5051 gst_ogg_demux_loop_push (GstOggDemux * ogg)
5053 GstEvent *event = NULL;
5055 g_mutex_lock (&ogg->seek_event_mutex);
5056 /* Inform other threads that we started */
5057 ogg->seek_thread_started = TRUE;
5058 g_cond_broadcast (&ogg->thread_started_cond);
5061 while (!ogg->seek_event_thread_stop) {
5063 while (!ogg->seek_event_thread_stop) {
5064 GST_PUSH_LOCK (ogg);
5065 event = ogg->seek_event;
5066 ogg->seek_event = NULL;
5068 ogg->seek_event_drop_till = gst_event_get_seqnum (event);
5069 GST_PUSH_UNLOCK (ogg);
5074 g_cond_wait (&ogg->seek_event_cond, &ogg->seek_event_mutex);
5077 if (ogg->seek_event_thread_stop) {
5082 g_mutex_unlock (&ogg->seek_event_mutex);
5084 GST_DEBUG_OBJECT (ogg->sinkpad, "Pushing event %" GST_PTR_FORMAT, event);
5085 if (!gst_pad_push_event (ogg->sinkpad, event)) {
5086 GST_WARNING_OBJECT (ogg, "Failed to push event");
5087 GST_PUSH_LOCK (ogg);
5088 if (!ogg->pullmode) {
5089 ogg->push_state = PUSH_PLAYING;
5090 ogg->push_disable_seeking = TRUE;
5092 GST_PUSH_UNLOCK (ogg);
5094 GST_DEBUG_OBJECT (ogg->sinkpad, "Pushed event ok");
5097 g_mutex_lock (&ogg->seek_event_mutex);
5100 g_mutex_unlock (&ogg->seek_event_mutex);
5102 gst_object_unref (ogg);
5107 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
5111 gst_ogg_demux_deactivate_current_chain (ogg);
5113 GST_CHAIN_LOCK (ogg);
5114 for (i = 0; i < ogg->chains->len; i++) {
5115 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
5117 if (chain == ogg->current_chain)
5118 ogg->current_chain = NULL;
5119 if (chain == ogg->building_chain)
5120 ogg->building_chain = NULL;
5121 gst_ogg_chain_free (chain);
5123 ogg->chains = g_array_set_size (ogg->chains, 0);
5124 if (ogg->current_chain != NULL) {
5125 GST_FIXME_OBJECT (ogg, "current chain was tracked in existing chains !");
5126 gst_ogg_chain_free (ogg->current_chain);
5127 ogg->current_chain = NULL;
5129 if (ogg->building_chain != NULL) {
5130 GST_FIXME_OBJECT (ogg, "building chain was tracked in existing chains !");
5131 gst_ogg_chain_free (ogg->building_chain);
5132 ogg->building_chain = NULL;
5134 GST_CHAIN_UNLOCK (ogg);
5137 /* this function is called when the pad is activated and should start
5140 * We check if we can do random access to decide if we work push or
5144 gst_ogg_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
5149 query = gst_query_new_scheduling ();
5151 if (!gst_pad_peer_query (sinkpad, query)) {
5152 gst_query_unref (query);
5156 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
5157 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
5158 gst_query_unref (query);
5163 GST_DEBUG_OBJECT (sinkpad, "activating pull");
5164 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
5168 GST_DEBUG_OBJECT (sinkpad, "activating push");
5169 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
5174 gst_ogg_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
5175 GstPadMode mode, gboolean active)
5180 ogg = GST_OGG_DEMUX (parent);
5183 case GST_PAD_MODE_PUSH:
5184 ogg->pullmode = FALSE;
5185 ogg->resync = FALSE;
5187 ogg->seek_event_thread_stop = FALSE;
5188 ogg->seek_thread_started = FALSE;
5189 ogg->seek_event_thread = g_thread_new ("seek_event_thread",
5190 (GThreadFunc) gst_ogg_demux_loop_push, gst_object_ref (ogg));
5191 /* And wait for the thread to start.
5192 * FIXME : This is hackish. And one wonders why we need a separate thread to
5193 * seek to a certain offset */
5194 g_mutex_lock (&ogg->seek_event_mutex);
5195 while (!ogg->seek_thread_started) {
5196 g_cond_wait (&ogg->thread_started_cond, &ogg->seek_event_mutex);
5198 g_mutex_unlock (&ogg->seek_event_mutex);
5200 g_mutex_lock (&ogg->seek_event_mutex);
5201 ogg->seek_event_thread_stop = TRUE;
5202 g_cond_broadcast (&ogg->seek_event_cond);
5203 g_mutex_unlock (&ogg->seek_event_mutex);
5204 g_thread_join (ogg->seek_event_thread);
5205 ogg->seek_event_thread = NULL;
5209 case GST_PAD_MODE_PULL:
5211 ogg->need_chains = TRUE;
5212 ogg->pullmode = TRUE;
5214 res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
5217 res = gst_pad_stop_task (sinkpad);
5227 static GstStateChangeReturn
5228 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
5231 GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
5233 ogg = GST_OGG_DEMUX (element);
5235 switch (transition) {
5236 case GST_STATE_CHANGE_NULL_TO_READY:
5238 ogg_sync_init (&ogg->sync);
5240 case GST_STATE_CHANGE_READY_TO_PAUSED:
5241 ogg_sync_reset (&ogg->sync);
5242 ogg->running = FALSE;
5244 ogg->total_time = -1;
5245 GST_PUSH_LOCK (ogg);
5246 ogg->push_byte_offset = 0;
5247 ogg->push_byte_length = -1;
5248 ogg->push_time_length = GST_CLOCK_TIME_NONE;
5249 ogg->push_time_offset = GST_CLOCK_TIME_NONE;
5250 ogg->push_state = PUSH_PLAYING;
5251 ogg->have_group_id = FALSE;
5252 ogg->group_id = G_MAXUINT;
5253 ogg->seqnum = GST_SEQNUM_INVALID;
5255 ogg->push_disable_seeking = FALSE;
5256 gst_ogg_demux_query_duration_push (ogg);
5257 GST_PUSH_UNLOCK (ogg);
5258 gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
5264 result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5266 switch (transition) {
5267 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
5269 case GST_STATE_CHANGE_PAUSED_TO_READY:
5270 gst_ogg_demux_clear_chains (ogg);
5271 GST_OBJECT_LOCK (ogg);
5272 ogg->running = FALSE;
5273 gst_event_replace (&ogg->seek_event, NULL);
5274 GST_OBJECT_UNLOCK (ogg);
5276 case GST_STATE_CHANGE_READY_TO_NULL:
5277 ogg_sync_clear (&ogg->sync);
5286 gst_ogg_demux_plugin_init (GstPlugin * plugin)
5288 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
5289 GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
5290 "ogg demuxer setup stage when parsing pipeline");
5293 GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
5295 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
5296 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
5302 /* prints all info about the element */
5303 #undef GST_CAT_DEFAULT
5304 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
5306 #ifdef GST_DISABLE_GST_DEBUG
5309 gst_ogg_print (GstOggDemux * ogg)
5314 #else /* !GST_DISABLE_GST_DEBUG */
5317 gst_ogg_print (GstOggDemux * ogg)
5321 GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
5322 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
5323 GST_TIME_ARGS (ogg->total_time));
5325 for (i = 0; i < ogg->chains->len; i++) {
5326 GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
5328 GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
5329 GST_INFO_OBJECT (ogg,
5330 " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
5332 GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT,
5333 GST_TIME_ARGS (chain->begin_time));
5334 GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
5335 GST_TIME_ARGS (chain->total_time));
5336 GST_INFO_OBJECT (ogg, " segment start: %" GST_TIME_FORMAT,
5337 GST_TIME_ARGS (chain->segment_start));
5338 GST_INFO_OBJECT (ogg, " segment stop: %" GST_TIME_FORMAT,
5339 GST_TIME_ARGS (chain->segment_stop));
5341 for (j = 0; j < chain->streams->len; j++) {
5342 GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
5344 GST_INFO_OBJECT (ogg, " stream %08x: %s", stream->map.serialno,
5345 gst_ogg_stream_get_media_type (&stream->map));
5346 GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
5347 GST_TIME_ARGS (stream->start_time));
5351 #endif /* GST_DISABLE_GST_DEBUG */