rtsp-server:wfd: Fix build error for gcc upgrade
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / ext / ogg / gstoggdemux.c
1 /* GStreamer
2  * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
3  *
4  * gstoggdemux.c: ogg stream demuxer
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 /**
23  * SECTION:element-oggdemux
24  * @title: oggdemux
25  * @see_also: <link linkend="gst-plugins-base-plugins-oggmux">oggmux</link>
26  *
27  * This element demuxes ogg files into their encoded audio and video components.
28  *
29  * ## Example pipelines
30  * |[
31  * gst-launch-1.0 -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! autoaudiosink
32  * ]|
33  *  Decodes a vorbis audio stream stored inside an ogg container and plays it.
34  *
35  */
36
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include <string.h>
43 #include <glib/gi18n-lib.h>
44 #include <gst/tag/tag.h>
45 #include <gst/audio/audio.h>
46
47 #include "gstoggelements.h"
48 #include "gstoggdemux.h"
49
50 #define CHUNKSIZE (8500)        /* this is out of vorbisfile */
51
52 /* we hope we get a granpos within this many bytes off the end */
53 #define DURATION_CHUNK_OFFSET (128*1024)
54
55 /* An Ogg page can not be larger than 255 segments of 255 bytes, plus
56    26 bytes of header */
57 #define MAX_OGG_PAGE_SIZE (255 * 255 + 26)
58
59 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
60 #define GST_FLOW_SKIP_PUSH GST_FLOW_CUSTOM_SUCCESS_1
61
62 #define SEEK_GIVE_UP_THRESHOLD (3*GST_SECOND)
63
64 #define GST_CHAIN_LOCK(ogg)     g_mutex_lock(&(ogg)->chain_lock)
65 #define GST_CHAIN_UNLOCK(ogg)   g_mutex_unlock(&(ogg)->chain_lock)
66
67 #define GST_PUSH_LOCK(ogg)                  \
68   do {                                      \
69     GST_TRACE_OBJECT(ogg, "Push lock");     \
70     g_mutex_lock(&(ogg)->push_lock);        \
71   } while(0)
72
73 #define GST_PUSH_UNLOCK(ogg)                \
74   do {                                      \
75     GST_TRACE_OBJECT(ogg, "Push unlock");   \
76     g_mutex_unlock(&(ogg)->push_lock);      \
77   } while(0)
78
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
82
83
84 static ogg_packet *
85 _ogg_packet_copy (const ogg_packet * packet)
86 {
87   ogg_packet *ret = g_slice_new (ogg_packet);
88
89   *ret = *packet;
90   ret->packet = g_memdup2 (packet->packet, packet->bytes);
91
92   return ret;
93 }
94
95 static void
96 _ogg_packet_free (ogg_packet * packet)
97 {
98   g_free (packet->packet);
99   g_slice_free (ogg_packet, packet);
100 }
101
102 static ogg_page *
103 gst_ogg_page_copy (ogg_page * page)
104 {
105   ogg_page *p = g_slice_new (ogg_page);
106
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;
112
113   return p;
114 }
115
116 static void
117 gst_ogg_page_free (ogg_page * page)
118 {
119   g_free (page->header);
120   g_free (page->body);
121   g_slice_free (ogg_page, page);
122 }
123
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);
130
131 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
132     GstEvent * event);
133 static gboolean gst_ogg_demux_receive_event (GstElement * element,
134     GstEvent * event);
135
136 static void gst_ogg_pad_dispose (GObject * object);
137 static void gst_ogg_pad_finalize (GObject * object);
138
139 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstObject * parent,
140     GstQuery * query);
141 static gboolean gst_ogg_pad_event (GstPad * pad, GstObject * parent,
142     GstEvent * event);
143 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
144     guint32 serialno);
145
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);
149
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,
154     GstEvent * event);
155 static gboolean gst_ogg_demux_check_duration_push (GstOggDemux * ogg,
156     GstSeekFlags flags, GstEvent * event);
157
158 GType gst_ogg_pad_get_type (void);
159 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
160
161 static void
162 gst_ogg_pad_class_init (GstOggPadClass * klass)
163 {
164   GObjectClass *gobject_class;
165
166   gobject_class = (GObjectClass *) klass;
167
168   gobject_class->dispose = gst_ogg_pad_dispose;
169   gobject_class->finalize = gst_ogg_pad_finalize;
170 }
171
172 static void
173 gst_ogg_pad_init (GstOggPad * pad)
174 {
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));
180
181   pad->current_granule = -1;
182   pad->prev_granule = -1;
183   pad->keyframe_granule = -1;
184
185   pad->start_time = GST_CLOCK_TIME_NONE;
186
187   pad->position = GST_CLOCK_TIME_NONE;
188
189   pad->have_type = FALSE;
190   pad->continued = NULL;
191   pad->map.headers = NULL;
192   pad->map.queued = NULL;
193
194   pad->map.granulerate_n = 0;
195   pad->map.granulerate_d = 0;
196   pad->map.granuleshift = -1;
197 }
198
199 static void
200 gst_ogg_pad_dispose (GObject * object)
201 {
202   GstOggPad *pad = GST_OGG_PAD (object);
203
204   pad->chain = NULL;
205   pad->ogg = NULL;
206
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;
213
214   g_free (pad->map.index);
215   pad->map.index = NULL;
216
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;
221
222   if (pad->map.caps) {
223     gst_caps_unref (pad->map.caps);
224     pad->map.caps = NULL;
225   }
226
227   if (pad->map.taglist) {
228     gst_tag_list_unref (pad->map.taglist);
229     pad->map.taglist = NULL;
230   }
231
232   ogg_stream_reset (&pad->map.stream);
233
234   G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
235 }
236
237 static void
238 gst_ogg_pad_finalize (GObject * object)
239 {
240   GstOggPad *pad = GST_OGG_PAD (object);
241
242   ogg_stream_clear (&pad->map.stream);
243
244   G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
245 }
246
247 static gboolean
248 gst_ogg_pad_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
249 {
250   gboolean res = TRUE;
251   GstOggDemux *ogg;
252
253   ogg = GST_OGG_DEMUX (parent);
254
255   switch (GST_QUERY_TYPE (query)) {
256     case GST_QUERY_POSITION:
257     {
258       GstFormat format;
259       GstOggPad *ogg_pad = GST_OGG_PAD (pad);
260
261       gst_query_parse_position (query, &format, NULL);
262       /* can only get position in time */
263       if (format != GST_FORMAT_TIME)
264         goto wrong_format;
265
266       gst_query_set_position (query, format, ogg_pad->position);
267       break;
268     }
269     case GST_QUERY_DURATION:
270     {
271       GstFormat format;
272       gint64 total_time = -1;
273
274       gst_query_parse_duration (query, &format, NULL);
275       /* can only get duration in time */
276       if (format != GST_FORMAT_TIME)
277         goto wrong_format;
278
279       if (ogg->total_time != -1) {
280         /* we can return the total length */
281         total_time = ogg->total_time;
282       } else {
283         gint bitrate = ogg->bitrate;
284
285         /* try with length and bitrate */
286         if (bitrate > 0) {
287           GstQuery *uquery;
288
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)) {
292             gint64 length;
293
294             gst_query_parse_duration (uquery, NULL, &length);
295
296             /* estimate using the bitrate */
297             total_time =
298                 gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);
299
300             GST_LOG_OBJECT (ogg,
301                 "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
302                 GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
303           }
304           gst_query_unref (uquery);
305         }
306       }
307
308       gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
309       break;
310     }
311     case GST_QUERY_SEEKING:
312     {
313       GstFormat format;
314
315       gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
316       if (format == GST_FORMAT_TIME) {
317         gboolean seekable = FALSE;
318         gint64 stop = -1;
319
320         GST_CHAIN_LOCK (ogg);
321         if (ogg->pullmode) {
322           seekable = TRUE;
323           stop = ogg->total_time;
324         } else if (ogg->push_disable_seeking) {
325           seekable = FALSE;
326         } else if (ogg->current_chain == NULL) {
327           GstQuery *squery;
328
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);
334           else
335             seekable = FALSE;
336           gst_query_unref (squery);
337         } else if (ogg->current_chain->streams->len) {
338           gint i;
339
340           seekable = FALSE;
341           for (i = 0; i < ogg->current_chain->streams->len; i++) {
342             GstOggPad *pad =
343                 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
344
345             seekable = TRUE;
346             if (pad->map.index != NULL && pad->map.n_index != 0) {
347               GstOggIndex *idx;
348               GstClockTime idx_time;
349
350               idx = &pad->map.index[pad->map.n_index - 1];
351               idx_time =
352                   gst_util_uint64_scale (idx->timestamp, GST_SECOND,
353                   pad->map.kp_denom);
354               if (stop == -1)
355                 stop = idx_time;
356               else
357                 stop = MAX (idx_time, stop);
358             } else {
359               stop = ogg->push_time_length;
360               if (stop == -1)
361                 stop = ogg->total_time;
362             }
363           }
364         }
365
366         gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
367         GST_CHAIN_UNLOCK (ogg);
368       } else {
369         res = FALSE;
370       }
371       break;
372     }
373     case GST_QUERY_SEGMENT:{
374       GstFormat format;
375       gint64 start, stop;
376
377       format = ogg->segment.format;
378
379       start =
380           gst_segment_to_stream_time (&ogg->segment, format,
381           ogg->segment.start);
382       if ((stop = ogg->segment.stop) == -1)
383         stop = ogg->segment.duration;
384       else
385         stop = gst_segment_to_stream_time (&ogg->segment, format, stop);
386
387       gst_query_set_segment (query, ogg->segment.rate, format, start, stop);
388       res = TRUE;
389       break;
390     }
391     default:
392       res = gst_pad_query_default (pad, parent, query);
393       break;
394   }
395 done:
396
397   return res;
398
399   /* ERRORS */
400 wrong_format:
401   {
402     GST_DEBUG_OBJECT (ogg, "only query position/duration on TIME is supported");
403     res = FALSE;
404     goto done;
405   }
406 }
407
408 static gboolean
409 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
410 {
411   gboolean res;
412   GstOggDemux *ogg;
413
414   ogg = GST_OGG_DEMUX (element);
415
416   switch (GST_EVENT_TYPE (event)) {
417     case GST_EVENT_SEEK:
418       /* now do the seek */
419       res = gst_ogg_demux_perform_seek (ogg, event);
420       gst_event_unref (event);
421       break;
422     default:
423       GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
424       goto error;
425   }
426
427   return res;
428
429   /* ERRORS */
430 error:
431   {
432     GST_DEBUG_OBJECT (ogg, "error handling event");
433     gst_event_unref (event);
434     return FALSE;
435   }
436 }
437
438 static gboolean
439 gst_ogg_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
440 {
441   gboolean res;
442   GstOggDemux *ogg;
443
444   ogg = GST_OGG_DEMUX (parent);
445
446   switch (GST_EVENT_TYPE (event)) {
447     case GST_EVENT_SEEK:
448       /* now do the seek */
449       res = gst_ogg_demux_perform_seek (ogg, event);
450       gst_event_unref (event);
451       break;
452     case GST_EVENT_RECONFIGURE:
453       GST_OGG_PAD (pad)->last_ret = GST_FLOW_OK;
454       res = gst_pad_event_default (pad, parent, event);
455       break;
456     default:
457       res = gst_pad_event_default (pad, parent, event);
458       break;
459   }
460
461   return res;
462 }
463
464 static void
465 gst_ogg_pad_reset (GstOggPad * pad)
466 {
467   ogg_stream_reset (&pad->map.stream);
468
469   GST_DEBUG_OBJECT (pad, "doing reset");
470
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;
475
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;
481   pad->is_eos = FALSE;
482 }
483
484 /* queue data, basically takes the packet, puts it in a buffer and store the
485  * buffer in the queued list.  */
486 static GstFlowReturn
487 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
488 {
489 #ifndef GST_DISABLE_GST_DEBUG
490   GstOggDemux *ogg = pad->ogg;
491 #endif
492
493   GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x",
494       pad, pad->map.serialno);
495
496   pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
497
498   /* we are ok now */
499   return GST_FLOW_OK;
500 }
501
502 static GstFlowReturn
503 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
504     gboolean push_headers)
505 {
506   GstBuffer *buf = NULL;
507   GstFlowReturn ret, cret;
508   GstOggDemux *ogg = pad->ogg;
509   gint64 current_time;
510   GstOggChain *chain;
511   gint64 duration;
512   gint offset;
513   gint trim;
514   GstClockTime out_timestamp, out_duration;
515   guint64 out_offset, out_offset_end;
516   gboolean delta_unit = FALSE;
517   gboolean is_header;
518   guint64 clip_start = 0, clip_end = 0;
519
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);
524
525   if (G_UNLIKELY (pad->is_eos)) {
526     GST_DEBUG_OBJECT (pad, "Skipping packet on pad that is eos");
527     ret = GST_FLOW_EOS;
528     goto combine;
529   }
530
531   GST_PUSH_LOCK (ogg);
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);
539         return GST_FLOW_OK;
540       }
541     }
542     GST_PUSH_UNLOCK (ogg);
543     return GST_FLOW_OK;
544   }
545   GST_PUSH_UNLOCK (ogg);
546
547   GST_DEBUG_OBJECT (ogg,
548       "%p streaming to peer serial %08x", pad, pad->map.serialno);
549
550   gst_ogg_stream_update_stats (&pad->map, packet);
551
552   if (pad->map.is_ogm) {
553     const guint8 *data;
554     long bytes;
555
556     data = packet->packet;
557     bytes = packet->bytes;
558
559     if (bytes < 1)
560       goto empty_packet;
561
562     if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
563       /* We don't push header packets for OGM */
564       goto done;
565     }
566
567     offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
568     delta_unit = (((data[0] & 0x08) >> 3) == 0);
569
570     trim = 0;
571
572     /* Strip trailing \0 for subtitles */
573     if (pad->map.is_ogm_text) {
574       while (bytes && data[bytes - 1] == 0) {
575         trim++;
576         bytes--;
577       }
578     }
579   } else if (pad->map.is_vp8) {
580     if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
581         packet->b_o_s ||
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 */
587       goto done;
588     }
589     offset = 0;
590     trim = 0;
591     delta_unit = !gst_ogg_stream_packet_is_key_frame (&pad->map, packet);
592   } else {
593     offset = 0;
594     trim = 0;
595     delta_unit = !gst_ogg_stream_packet_is_key_frame (&pad->map, packet);
596   }
597
598   /* get timing info for the packet */
599   is_header = gst_ogg_stream_packet_is_header (&pad->map, packet);
600   if (is_header) {
601     duration = 0;
602     GST_DEBUG_OBJECT (ogg, "packet is header");
603   } else {
604     duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
605     GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
606   }
607
608
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) {
620     GstQuery *query;
621     GstFormat format;
622     int i;
623     gint64 length;
624     gboolean beyond;
625
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++) {
632             GstOggPad *ipad =
633                 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
634             if (!ipad->map.index)
635               continue;
636             beyond = ipad->map.n_index
637                 && ipad->map.index[ipad->map.n_index - 1].offset >= length;
638             if (beyond) {
639               GST_WARNING_OBJECT (pad, "Index offsets beyond byte length");
640               if (ipad->discont) {
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;
646               } else {
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)
651                   ipad->map.n_index--;
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;
656                 }
657               }
658               /* We can't trust the total time if the index goes beyond */
659               ipad->map.total_time = -1;
660             } else {
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);
666               }
667             }
668           }
669         }
670       }
671       gst_query_unref (query);
672     }
673     ogg->check_index_overflow = FALSE;
674   }
675
676   if (packet->b_o_s) {
677     out_timestamp = GST_CLOCK_TIME_NONE;
678     out_duration = GST_CLOCK_TIME_NONE;
679     out_offset = 0;
680     out_offset_end = -1;
681   } else {
682     if (packet->granulepos > -1) {
683       gint64 granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
684           packet->granulepos);
685       if (granule < 0) {
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;
690       }
691       pad->current_granule = granule;
692       pad->keyframe_granule =
693           gst_ogg_stream_granulepos_to_key_granule (&pad->map,
694           packet->granulepos);
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;
699       if (!delta_unit) {
700         pad->keyframe_granule = pad->current_granule;
701       }
702       GST_DEBUG_OBJECT (ogg, "interpolating granule %" G_GUINT64_FORMAT,
703           pad->current_granule);
704     }
705
706     if (ogg->segment.rate < 0.0 && pad->current_granule == -1) {
707       /* negative rates, allow output of packets with no timestamp, let downstream reconstruct */
708       out_timestamp = -1;
709       out_duration = -1;
710       out_offset = -1;
711       out_offset_end = -1;
712       pad->prev_granule = -1;
713     } else {
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
717        * timestamp. */
718       if (pad->current_granule == -1) {
719         pad->prev_granule = -1;
720         goto no_timestamp;
721       }
722
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;
733         } else {
734           out_duration = gst_util_uint64_scale (duration,
735               GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
736         }
737       } else {
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
742            and granule. */
743         if (packet->e_o_s) {
744           if (pad->prev_granule >= 0)
745             out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
746                 pad->prev_granule);
747           else
748             out_timestamp = GST_CLOCK_TIME_NONE;
749
750           if (pad->map.audio_clipping
751               && pad->current_granule < pad->prev_granule + duration) {
752             clip_end = pad->prev_granule + duration - pad->current_granule;
753           }
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;
760               clip_start =
761                   already_removed >
762                   -pad->map.granule_offset ? 0 : -pad->map.granule_offset -
763                   already_removed;
764             } else
765               clip_start = pad->current_granule;
766           }
767         } else {
768           out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
769               pad->current_granule - duration);
770
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;
777               clip_start =
778                   already_removed >
779                   -pad->map.granule_offset ? 0 : -pad->map.granule_offset -
780                   already_removed;
781             } else
782               clip_start = pad->current_granule;
783           }
784         }
785         out_duration =
786             gst_ogg_stream_granule_to_time (&pad->map,
787             pad->current_granule) - out_timestamp;
788       }
789       out_offset_end =
790           gst_ogg_stream_granule_to_granulepos (&pad->map,
791           pad->current_granule, pad->keyframe_granule);
792       out_offset =
793           gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
794     }
795     pad->prev_granule = pad->current_granule;
796   }
797
798   if (G_UNLIKELY (offset + trim > packet->bytes))
799     goto invalid_packet;
800   else if (pad->map.is_ogm_text) {
801     /* check for invalid buffer sizes */
802     if (G_UNLIKELY (offset + trim >= packet->bytes))
803       goto empty_packet;
804   }
805
806   if (!pad->added)
807     goto not_added;
808
809   buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
810
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,
817         clip_end);
818   }
819
820   /* set delta flag for OGM content */
821   if (delta_unit)
822     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
823
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);
828   }
829
830   if (packet->packet != NULL) {
831     /* copy packet in buffer */
832     gst_buffer_fill (buf, 0, packet->packet + offset,
833         packet->bytes - offset - trim);
834   }
835
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;
840
841   /* Mark discont on the buffer */
842   if (pad->discont) {
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;
846   }
847
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);
853     } else {
854       GST_DEBUG_OBJECT (ogg, "not pushing buffer on error pad");
855       ret = pad->last_ret;
856       gst_buffer_unref (buf);
857     }
858     buf = NULL;
859   }
860
861   /* we're done with skeleton stuff */
862   if (pad->map.is_skeleton)
863     goto combine;
864
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)
870     goto combine;
871
872   /* convert to time */
873   current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
874       packet->granulepos);
875
876   /* convert to stream time */
877   if ((chain = pad->chain)) {
878     gint64 chain_start = 0;
879
880     if (chain->segment_start != GST_CLOCK_TIME_NONE)
881       chain_start = chain->segment_start;
882
883     current_time = current_time - chain_start + chain->begin_time;
884   }
885
886   /* and store as the current position */
887   ogg->segment.position = current_time;
888
889   GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT
890       " (%" G_GINT64_FORMAT ")", GST_TIME_ARGS (current_time), current_time);
891
892   pad->position = ogg->segment.position;
893
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);
901     pad->is_eos = TRUE;
902
903     if (ret == GST_FLOW_OK) {
904       ret = GST_FLOW_EOS;
905     }
906   }
907
908 combine:
909   /* combine flows */
910   cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
911
912 done:
913   if (buf)
914     gst_buffer_unref (buf);
915   /* return combined flow result */
916   return cret;
917
918   /* special cases */
919 empty_packet:
920   {
921     GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
922     goto done;
923   }
924
925 invalid_packet:
926   {
927     GST_DEBUG_OBJECT (ogg, "Skipping invalid packet");
928     goto done;
929   }
930
931 no_timestamp:
932   {
933     GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
934     goto done;
935   }
936 not_added:
937   {
938     GST_DEBUG_OBJECT (ogg, "pad not added yet");
939     goto done;
940   }
941 }
942
943 static guint64
944 gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
945 {
946   gint i;
947   guint64 start_time = G_MAXUINT64;
948
949   for (i = 0; i < chain->streams->len; i++) {
950     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
951
952     if (pad->map.is_skeleton)
953       continue;
954
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;
962         break;
963       }
964     } else {
965       start_time = MIN (start_time, pad->start_time);
966     }
967   }
968   return start_time;
969 }
970
971 static GstClockTime
972 gst_ogg_demux_collect_sync_time (GstOggDemux * ogg, GstOggChain * chain)
973 {
974   gint i;
975   GstClockTime sync_time = GST_CLOCK_TIME_NONE;
976
977   if (!chain) {
978     GST_WARNING_OBJECT (ogg, "No chain!");
979     return GST_CLOCK_TIME_NONE;
980   }
981
982   for (i = 0; i < chain->streams->len; i++) {
983     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
984
985     if (pad->map.is_sparse)
986       continue;
987
988     if (pad->push_sync_time == GST_CLOCK_TIME_NONE) {
989       sync_time = GST_CLOCK_TIME_NONE;
990       break;
991     } else {
992       if (sync_time == GST_CLOCK_TIME_NONE)
993         sync_time = pad->push_sync_time;
994       else
995         sync_time = MAX (sync_time, pad->push_sync_time);
996     }
997   }
998   return sync_time;
999 }
1000
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
1003  */
1004 static GstFlowReturn
1005 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
1006 {
1007   gint64 granule;
1008   GstFlowReturn ret = GST_FLOW_OK;
1009
1010   GstOggDemux *ogg = pad->ogg;
1011
1012   GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x",
1013       pad, pad->map.serialno);
1014
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");
1019     }
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;
1025
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);
1031       }
1032     }
1033     if (!pad->map.caps) {
1034       GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
1035     }
1036   }
1037
1038   if (pad->map.is_skeleton) {
1039     guint32 serialno;
1040     GstOggPad *skel_pad;
1041     GstOggSkeleton type;
1042
1043     /* try to parse the serialno first */
1044     if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
1045             &serialno, &type)) {
1046
1047       GST_DEBUG_OBJECT (pad->ogg,
1048           "got skeleton packet for stream 0x%08x", serialno);
1049
1050       skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
1051       if (skel_pad) {
1052         switch (type) {
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
1056              * live streams */
1057             gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
1058                 packet->bytes, NULL);
1059             break;
1060           case GST_OGG_SKELETON_INDEX:
1061             gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
1062                 packet->bytes);
1063             ogg->check_index_overflow = TRUE;
1064             break;
1065           default:
1066             break;
1067         }
1068
1069       } else {
1070         GST_WARNING_OBJECT (pad->ogg,
1071             "found skeleton fisbone for an unknown stream 0x%08x", serialno);
1072       }
1073     }
1074   }
1075
1076   GST_DEBUG_OBJECT (ogg, "%p packet has granulepos %" G_GINT64_FORMAT, pad,
1077       (gint64) packet->granulepos);
1078   granule =
1079       gst_ogg_stream_granulepos_to_granule (&pad->map, packet->granulepos);
1080   if (granule > 0) {
1081     GST_DEBUG_OBJECT (ogg, "%p has granule %" G_GINT64_FORMAT, pad, granule);
1082     pad->current_granule = granule;
1083   } else if (granule == 0) {
1084     /* headers */
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;
1090   }
1091
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;
1102     }
1103   }
1104
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
1107    * up.  */
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);
1119       }
1120
1121       if (packet->granulepos != -1) {
1122         ogg_int64_t start_granule;
1123         gint64 granule;
1124
1125         granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
1126             packet->granulepos);
1127         if (granule < 0) {
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;
1132         }
1133
1134         if (granule >= pad->map.accumulated_granule)
1135           start_granule = granule - pad->map.accumulated_granule;
1136         else
1137           start_granule = 0;
1138
1139         pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
1140             start_granule);
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);
1148       } else {
1149         packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
1150             pad->map.accumulated_granule + pad->current_granule,
1151             pad->keyframe_granule);
1152       }
1153     }
1154   } else {
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) {
1159       pad->map.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);
1162     }
1163   }
1164
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;
1170
1171     /* check if complete chain has start time */
1172     if (chain == ogg->building_chain) {
1173       GstEvent *event = NULL;
1174
1175       if (ogg->resync) {
1176         guint64 start_time;
1177
1178         GST_DEBUG_OBJECT (ogg, "need to resync");
1179
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);
1183
1184         if (start_time != G_MAXUINT64) {
1185           gint64 segment_time;
1186           GstSegment segment;
1187
1188           GST_DEBUG_OBJECT (ogg, "start_time:  %" GST_TIME_FORMAT,
1189               GST_TIME_ARGS (start_time));
1190
1191           if (chain->segment_start < start_time)
1192             segment_time =
1193                 (start_time - chain->segment_start) + chain->begin_time;
1194           else
1195             segment_time = chain->begin_time;
1196
1197           /* create the newsegment event we are going to send out */
1198           gst_segment_init (&segment, GST_FORMAT_TIME);
1199
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;
1217           } else {
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);
1226           }
1227           GST_PUSH_UNLOCK (ogg);
1228
1229           ogg->resync = FALSE;
1230         }
1231       } else {
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)) {
1235           GstSegment segment;
1236
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));
1243
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);
1254         }
1255       }
1256
1257       if (event) {
1258         gst_event_set_seqnum (event, ogg->seqnum);
1259
1260         gst_ogg_demux_activate_chain (ogg, chain, event);
1261
1262         ogg->building_chain = NULL;
1263       }
1264     }
1265   }
1266
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
1271      * them here*/
1272     if (!packet->b_o_s)
1273       ret = gst_ogg_demux_queue_data (pad, packet);
1274   }
1275   /* else we are completely streaming to the peer */
1276   else {
1277     ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
1278   }
1279   return ret;
1280 }
1281
1282 /* flush at most @npackets from the stream layer. All packets if 
1283  * @npackets is 0;
1284  */
1285 static GstFlowReturn
1286 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1287 {
1288   GstFlowReturn result = GST_FLOW_OK;
1289   gboolean done = FALSE;
1290   GstOggDemux *ogg;
1291
1292   ogg = pad->ogg;
1293
1294   while (!done) {
1295     int ret;
1296     ogg_packet packet;
1297
1298     ret = ogg_stream_packetout (&pad->map.stream, &packet);
1299     switch (ret) {
1300       case 0:
1301         GST_LOG_OBJECT (ogg, "packetout done");
1302         done = TRUE;
1303         break;
1304       case -1:
1305         GST_LOG_OBJECT (ogg, "packetout discont");
1306         if (!pad->map.is_sparse) {
1307           gst_ogg_chain_mark_discont (pad->chain);
1308         } else {
1309           gst_ogg_pad_mark_discont (pad);
1310         }
1311         break;
1312       case 1:
1313         GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1314
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);
1320           break;
1321         }
1322
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)
1329           break;
1330         else if (result <= GST_FLOW_EOS)
1331           goto could_not_submit;
1332         break;
1333       default:
1334         GST_WARNING_OBJECT (ogg,
1335             "invalid return value %d for ogg_stream_packetout, resetting stream",
1336             ret);
1337         gst_ogg_pad_reset (pad);
1338         break;
1339     }
1340     if (npackets > 0) {
1341       npackets--;
1342       done = (npackets == 0);
1343     }
1344   }
1345   return result;
1346
1347   /* ERRORS */
1348 could_not_submit:
1349   {
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);
1354     return result;
1355   }
1356 }
1357
1358 static void
1359 gst_ogg_demux_setup_first_granule (GstOggDemux * ogg, GstOggPad * pad,
1360     ogg_page * page)
1361 {
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
1364    * on the page. */
1365   gboolean valid_granule = TRUE;
1366
1367   if (pad->current_granule == -1) {
1368     ogg_int64_t granpos = ogg_page_granulepos (page);
1369     if (granpos > 0) {
1370       gint64 granule =
1371           (gint64) gst_ogg_stream_granulepos_to_granule (&pad->map, granpos);
1372       gint64 duration;
1373       int packets = ogg_page_packets (page), n;
1374       GST_DEBUG_OBJECT (pad,
1375           "This page completes %d packets, granule %" G_GINT64_FORMAT, packets,
1376           granule);
1377
1378       if (packets > 0) {
1379         ogg_stream_state os;
1380         ogg_packet op;
1381         int last_size = pad->map.last_size;
1382
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);
1386           if (ret < 0) {
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) {
1390               n--;
1391               continue;
1392             }
1393             GST_DEBUG_OBJECT (pad, "Failed to read packet off first page");
1394             valid_granule = FALSE;
1395             break;
1396           }
1397           if (ret == 0) {
1398             GST_WARNING_OBJECT (pad,
1399                 "Short read getting %d packets off first page", packets);
1400             valid_granule = FALSE;
1401             break;
1402           }
1403           duration = gst_ogg_stream_get_packet_duration (&pad->map, &op);
1404           GST_DEBUG_OBJECT (pad, "Packet %d has duration %" G_GINT64_FORMAT,
1405               n, duration);
1406           granule -= duration;
1407         }
1408         pad->map.last_size = last_size;
1409         if (valid_granule) {
1410           if (granule >= 0) {
1411             pad->current_granule = granule;
1412             GST_INFO_OBJECT (pad,
1413                 "Starting with first granule %" G_GINT64_FORMAT, granule);
1414           } else {
1415             pad->current_granule = 0;
1416             GST_INFO_OBJECT (pad, "Extrapolated first granule is negative, "
1417                 "used to clip samples at start");
1418           }
1419         }
1420       } else {
1421         GST_WARNING_OBJECT (pad,
1422             "Ogg page finishing no packets, but a valid granule");
1423       }
1424     }
1425   }
1426 }
1427
1428 static void
1429 gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg)
1430 {
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;
1437   } else {
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;
1443   }
1444 }
1445
1446 static gint64
1447 gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg, float seek_quality)
1448 {
1449   gint64 best;
1450   gint64 segment_bitrate;
1451   gint64 skew;
1452
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);
1469     } else {
1470       segment_bitrate =
1471           gst_util_uint64_scale (ogg->push_last_seek_offset - 0,
1472           8 * GST_SECOND, ogg->push_last_seek_time - ogg->push_start_time);
1473     }
1474     best =
1475         ogg->push_offset0 +
1476         gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1477         segment_bitrate, 8 * GST_SECOND);
1478     ogg->seek_secant = TRUE;
1479   } else {
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;
1487     } else {
1488       segment_bitrate =
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);
1495
1496       best =
1497           ogg->push_offset0 +
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);
1509         best = new_best;
1510         ogg->seek_secant = FALSE;
1511       } else {
1512         ogg->seek_secant = TRUE;
1513       }
1514     }
1515   }
1516
1517   GST_DEBUG_OBJECT (ogg, "Raw best guess: %" G_GINT64_FORMAT, best);
1518
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
1525        packet starts */
1526     skew =
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);
1530     best -= skew;
1531   }
1532
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);
1543   }
1544
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;
1550
1551   GST_DEBUG_OBJECT (ogg, "Choosing target %" G_GINT64_FORMAT, best);
1552   return best;
1553 }
1554
1555 static void
1556 gst_ogg_demux_record_keyframe_time (GstOggDemux * ogg, GstOggPad * pad,
1557     ogg_int64_t granpos)
1558 {
1559   gint64 kf_granule;
1560   GstClockTime kf_time;
1561
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);
1564
1565   pad->push_kf_time = kf_time;
1566 }
1567
1568 /* returns the earliest keyframe time for all non sparse pads in the chain,
1569  * if known, and GST_CLOCK_TIME_NONE if not */
1570 static GstClockTime
1571 gst_ogg_demux_get_earliest_keyframe_time (GstOggDemux * ogg)
1572 {
1573   GstClockTime t = GST_CLOCK_TIME_NONE;
1574   GstOggChain *chain = ogg->building_chain;
1575   int i;
1576
1577   if (!chain) {
1578     GST_WARNING_OBJECT (ogg, "No chain!");
1579     return GST_CLOCK_TIME_NONE;
1580   }
1581   for (i = 0; i < chain->streams->len; i++) {
1582     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1583
1584     if (pad->map.is_sparse)
1585       continue;
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;
1590   }
1591
1592   return t;
1593 }
1594
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)
1599 {
1600   GstEvent *event;
1601
1602   /* Get the delayed event, if any */
1603   event = ogg->push_mode_seek_delayed_event;
1604   ogg->push_mode_seek_delayed_event = NULL;
1605
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;
1609
1610   ogg->push_state = PUSH_PLAYING;
1611
1612   /* If there is one, perform it. Otherwise, seek back at start to start
1613    * normal playback  */
1614   if (!event) {
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);
1623   }
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);
1630
1631   return GST_FLOW_OK;
1632 }
1633
1634 static float
1635 gst_ogg_demux_estimate_seek_quality (GstOggDemux * ogg)
1636 {
1637   GstClockTimeDiff diff;        /* how far from the goal we ended up */
1638   GstClockTimeDiff dist;        /* how far we moved this iteration */
1639   float seek_quality;
1640
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 */
1644     return 1.0f;
1645   }
1646
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);
1651   if (diff < 0)
1652     diff = -diff;
1653   dist = GST_CLOCK_DIFF (ogg->push_last_seek_time, ogg->push_prev_seek_time);
1654   if (dist < 0)
1655     dist = -dist;
1656
1657   seek_quality = (dist == 0) ? 0.0f : 1.0f / (1.0f + diff / (float) dist);
1658
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),
1662       seek_quality);
1663   return seek_quality;
1664 }
1665
1666 static void
1667 gst_ogg_demux_update_bisection_stats (GstOggDemux * ogg)
1668 {
1669   int n;
1670
1671   GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps",
1672       ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]);
1673
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];
1678   }
1679   ogg->stats_nbisections++;
1680
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]);
1686 }
1687
1688 static gboolean
1689 gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
1690 {
1691   GstOggDemux *ogg = pad->ogg;
1692   ogg_int64_t granpos = ogg_page_granulepos (page);
1693
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));
1701     }
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));
1708     }
1709
1710     if (ogg->push_state == PUSH_DURATION) {
1711       GstClockTime t =
1712           gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1713
1714       if (ogg->total_time == GST_CLOCK_TIME_NONE || t > ogg->total_time) {
1715         GST_DEBUG_OBJECT (ogg, "New total time: %" GST_TIME_FORMAT,
1716             GST_TIME_ARGS (t));
1717         ogg->total_time = t;
1718         ogg->push_time_length = t;
1719       }
1720
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;
1725       }
1726
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;
1735         GstFlowReturn res;
1736
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);
1743
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");
1747
1748         res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1749         if (res != GST_FLOW_OK)
1750           return res;
1751         return GST_FLOW_SKIP_PUSH;
1752       } else {
1753         GST_PUSH_UNLOCK (ogg);
1754       }
1755       return GST_FLOW_SKIP_PUSH;
1756     }
1757   }
1758
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) {
1761     GstClockTime t;
1762     gint64 best = -1;
1763     GstEvent *sevent;
1764     gboolean close_enough;
1765     float seek_quality;
1766
1767     /* ignore -1 granpos when seeking, we want to sync on a real granpos */
1768     if (granpos < 0) {
1769       GST_PUSH_UNLOCK (ogg);
1770       if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1771         goto choked;
1772       if (pad->current_granule == -1)
1773         gst_ogg_demux_setup_first_granule (ogg, pad, page);
1774       return GST_FLOW_SKIP_PUSH;
1775     }
1776
1777     t = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1778
1779     if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1780       GstClockTime sync_time;
1781
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)
1792           goto choked;
1793         if (pad->current_granule == -1)
1794           gst_ogg_demux_setup_first_granule (ogg, pad, page);
1795         return GST_FLOW_SKIP_PUSH;
1796       }
1797       ogg->push_last_seek_time = sync_time;
1798
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));
1805
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);
1815       } else {
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));
1825       }
1826       ogg->push_prev_seek_time = ogg->push_last_seek_time;
1827
1828       gst_ogg_demux_setup_bisection_bounds (ogg);
1829
1830       best = gst_ogg_demux_estimate_bisection_target (ogg, seek_quality);
1831
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);
1835       } else {
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);
1841
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");
1860       }
1861
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;
1868
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;
1874         } else {
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;
1887           } else {
1888             GST_INFO_OBJECT (ogg, "Seek to keyframe done, playing");
1889
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;
1893           }
1894           gst_ogg_demux_update_bisection_stats (ogg);
1895         }
1896       }
1897     } else if (ogg->push_state == PUSH_LINEAR1) {
1898       if (pad->push_kf_time == GST_CLOCK_TIME_NONE) {
1899         GstClockTime earliest_keyframe_time;
1900
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);
1914           } else {
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));
1919
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;
1928
1929             ogg->push_state = PUSH_BISECT2;
1930             best = gst_ogg_demux_estimate_bisection_target (ogg, 1.0f);
1931           }
1932         }
1933       }
1934     }
1935
1936     if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1937       gint i;
1938
1939       ogg_sync_reset (&ogg->sync);
1940       for (i = 0; i < ogg->building_chain->streams->len; i++) {
1941         GstOggPad *pad =
1942             g_array_index (ogg->building_chain->streams, GstOggPad *, i);
1943
1944         pad->push_sync_time = GST_CLOCK_TIME_NONE;
1945         ogg_stream_reset (&pad->map.stream);
1946       }
1947
1948       GST_DEBUG_OBJECT (ogg,
1949           "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, best,
1950           (gint64) - 1);
1951       /* do seek */
1952       g_assert (best != -1);
1953       ogg->push_bisection_steps[ogg->push_state == PUSH_BISECT2 ? 1 : 0]++;
1954       sevent =
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);
1959
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;
1967     }
1968
1969     if (ogg->push_state != PUSH_PLAYING) {
1970       GST_PUSH_UNLOCK (ogg);
1971       return GST_FLOW_SKIP_PUSH;
1972     }
1973   }
1974   GST_PUSH_UNLOCK (ogg);
1975
1976   return GST_FLOW_OK;
1977
1978 choked:
1979   {
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;
1986   }
1987 }
1988
1989 static void
1990 gst_ogg_demux_query_duration_push (GstOggDemux * ogg)
1991 {
1992   if (!ogg->pullmode && ogg->push_byte_length == -1) {
1993     GstQuery *query;
1994     gboolean seekable = FALSE;
1995
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);
2000
2001     if (seekable) {
2002       gint64 length = -1;
2003       if (!gst_element_query_duration (GST_ELEMENT (ogg), GST_FORMAT_BYTES,
2004               &length)
2005           || length <= 0) {
2006         GST_DEBUG_OBJECT (ogg,
2007             "Unable to determine stream size, assuming live, seeking disabled");
2008         ogg->push_disable_seeking = TRUE;
2009       } else {
2010         ogg->push_disable_seeking = FALSE;
2011       }
2012     } else {
2013       GST_DEBUG_OBJECT (ogg, "Stream is not seekable, seeking disabled");
2014       ogg->push_disable_seeking = TRUE;
2015     }
2016   }
2017 }
2018
2019 /* submit a page to an oggpad, this function will then submit all
2020  * the packets in the page.
2021  */
2022 static GstFlowReturn
2023 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
2024 {
2025   GstFlowReturn result = GST_FLOW_OK;
2026   GstOggDemux *ogg;
2027   gboolean continued = FALSE;
2028
2029   ogg = pad->ogg;
2030
2031   /* for negative rates we read pages backwards and must therefore be careful
2032    * with continued pages */
2033   if (ogg->segment.rate < 0.0) {
2034     gint npackets;
2035
2036     continued = ogg_page_continued (page);
2037
2038     /* number of completed packets in the page */
2039     npackets = ogg_page_packets (page);
2040     if (!continued) {
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. */
2046       if (npackets == 0)
2047         npackets = 1;
2048     }
2049     GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
2050
2051     if (npackets == 0) {
2052       GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
2053       goto done;
2054     }
2055   }
2056
2057   gst_ogg_demux_query_duration_push (ogg);
2058
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)
2063       return GST_FLOW_OK;
2064     if (result != GST_FLOW_OK)
2065       return result;
2066   }
2067
2068   if (page->header_len + page->body_len > ogg->max_page_size)
2069     ogg->max_page_size = page->header_len + page->body_len;
2070
2071   if (ogg_stream_pagein (&pad->map.stream, page) != 0)
2072     goto choked;
2073   if (pad->current_granule == -1)
2074     gst_ogg_demux_setup_first_granule (ogg, pad, page);
2075
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);
2079
2080   if (pad->continued) {
2081     ogg_packet packet;
2082
2083     /* now send the continued pages to the stream layer */
2084     while (pad->continued) {
2085       ogg_page *p = (ogg_page *) pad->continued->data;
2086
2087       GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
2088       if (ogg_stream_pagein (&pad->map.stream, p) != 0)
2089         goto choked;
2090
2091       pad->continued = g_list_delete_link (pad->continued, pad->continued);
2092
2093       /* free the page */
2094       gst_ogg_page_free (p);
2095     }
2096
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);
2100
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);
2105   }
2106
2107 done:
2108   /* keep continued pages (only in reverse mode) */
2109   if (continued) {
2110     ogg_page *p = gst_ogg_page_copy (page);
2111
2112     GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
2113     pad->continued = g_list_prepend (pad->continued, p);
2114   }
2115
2116   return result;
2117
2118 choked:
2119   {
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 */
2125     return GST_FLOW_OK;
2126   }
2127 }
2128
2129
2130 static GstOggChain *
2131 gst_ogg_chain_new (GstOggDemux * ogg)
2132 {
2133   GstOggChain *chain = g_slice_new0 (GstOggChain);
2134
2135   GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
2136   chain->ogg = ogg;
2137   chain->offset = -1;
2138   chain->bytes = -1;
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;
2145
2146   return chain;
2147 }
2148
2149 static void
2150 gst_ogg_chain_free (GstOggChain * chain)
2151 {
2152   gint i;
2153
2154   for (i = 0; i < chain->streams->len; i++) {
2155     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2156
2157     gst_object_unref (pad);
2158   }
2159   g_array_free (chain->streams, TRUE);
2160   g_slice_free (GstOggChain, chain);
2161 }
2162
2163 static void
2164 gst_ogg_pad_mark_discont (GstOggPad * pad)
2165 {
2166   GST_LOG_OBJECT (pad, "Marking discont on pad");
2167   pad->discont = TRUE;
2168   pad->map.last_size = 0;
2169 }
2170
2171 static void
2172 gst_ogg_chain_mark_discont (GstOggChain * chain)
2173 {
2174   gint i;
2175
2176   for (i = 0; i < chain->streams->len; i++) {
2177     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2178
2179     gst_ogg_pad_mark_discont (pad);
2180   }
2181 }
2182
2183 static void
2184 gst_ogg_chain_reset (GstOggChain * chain)
2185 {
2186   gint i;
2187
2188   for (i = 0; i < chain->streams->len; i++) {
2189     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2190
2191     gst_ogg_pad_reset (pad);
2192   }
2193 }
2194
2195 static GstOggPad *
2196 gst_ogg_chain_new_stream (GstOggChain * chain, guint32 serialno)
2197 {
2198   GstOggPad *ret;
2199   gchar *name;
2200
2201   GST_DEBUG_OBJECT (chain->ogg,
2202       "creating new stream %08x in chain %p", serialno, chain);
2203
2204   name = g_strdup_printf ("src_%08x", serialno);
2205   ret = g_object_new (GST_TYPE_OGG_PAD, "name", name, NULL);
2206   g_free (name);
2207   /* we own this one */
2208   gst_object_ref_sink (ret);
2209
2210   GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
2211   gst_ogg_pad_mark_discont (ret);
2212
2213   ret->chain = chain;
2214   ret->ogg = chain->ogg;
2215
2216   ret->map.serialno = serialno;
2217   if (ogg_stream_init (&ret->map.stream, serialno) != 0)
2218     goto init_failed;
2219
2220   GST_DEBUG_OBJECT (chain->ogg,
2221       "created new ogg src %p for stream with serial %08x", ret, serialno);
2222
2223   g_array_append_val (chain->streams, ret);
2224   gst_pad_set_active (GST_PAD_CAST (ret), TRUE);
2225
2226   return ret;
2227
2228   /* ERRORS */
2229 init_failed:
2230   {
2231     GST_ERROR ("Could not initialize ogg_stream struct for serial %08x",
2232         serialno);
2233     gst_object_unref (ret);
2234     return NULL;
2235   }
2236 }
2237
2238 static GstOggPad *
2239 gst_ogg_chain_get_stream (GstOggChain * chain, guint32 serialno)
2240 {
2241   gint i;
2242
2243   for (i = 0; i < chain->streams->len; i++) {
2244     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2245
2246     if (pad->map.serialno == serialno)
2247       return pad;
2248   }
2249   return NULL;
2250 }
2251
2252 static gboolean
2253 gst_ogg_chain_has_stream (GstOggChain * chain, guint32 serialno)
2254 {
2255   return gst_ogg_chain_get_stream (chain, serialno) != NULL;
2256 }
2257
2258 /* signals and args */
2259 enum
2260 {
2261   /* FILL ME */
2262   LAST_SIGNAL
2263 };
2264
2265 enum
2266 {
2267   ARG_0
2268       /* FILL ME */
2269 };
2270
2271 static GstStaticPadTemplate ogg_demux_src_template_factory =
2272 GST_STATIC_PAD_TEMPLATE ("src_%08x",
2273     GST_PAD_SRC,
2274     GST_PAD_SOMETIMES,
2275     GST_STATIC_CAPS_ANY);
2276
2277 static GstStaticPadTemplate ogg_demux_sink_template_factory =
2278     GST_STATIC_PAD_TEMPLATE ("sink",
2279     GST_PAD_SINK,
2280     GST_PAD_ALWAYS,
2281     GST_STATIC_CAPS ("application/ogg; audio/ogg; video/ogg; application/kate")
2282     );
2283
2284 static void gst_ogg_demux_finalize (GObject * object);
2285
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);
2290
2291 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent,
2292     GstEvent * event);
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);
2302
2303 static void gst_ogg_print (GstOggDemux * demux);
2304 static gboolean gst_ogg_demux_plugin_init (GstPlugin * plugin);
2305
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));
2310
2311 static void
2312 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
2313 {
2314   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
2315   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2316
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>");
2321
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);
2326
2327   gstelement_class->change_state = gst_ogg_demux_change_state;
2328   gstelement_class->send_event = gst_ogg_demux_receive_event;
2329
2330   gobject_class->finalize = gst_ogg_demux_finalize;
2331 }
2332
2333 static void
2334 gst_ogg_demux_init (GstOggDemux * ogg)
2335 {
2336   /* create the sink pad */
2337   ogg->sinkpad =
2338       gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
2339       "sink");
2340
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);
2347
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);
2353
2354   ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
2355
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;
2361
2362   ogg->newsegment = NULL;
2363   ogg->seqnum = GST_SEQNUM_INVALID;
2364
2365   ogg->chunk_size = CHUNKSIZE;
2366   ogg->flowcombiner = gst_flow_combiner_new ();
2367 }
2368
2369 static void
2370 gst_ogg_demux_finalize (GObject * object)
2371 {
2372   GstOggDemux *ogg;
2373
2374   ogg = GST_OGG_DEMUX (object);
2375
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);
2382
2383   ogg_sync_clear (&ogg->sync);
2384
2385   if (ogg->newsegment)
2386     gst_event_unref (ogg->newsegment);
2387
2388   gst_flow_combiner_free (ogg->flowcombiner);
2389
2390   if (ogg->building_chain)
2391     gst_ogg_chain_free (ogg->building_chain);
2392
2393   G_OBJECT_CLASS (parent_class)->finalize (object);
2394 }
2395
2396 static void
2397 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
2398 {
2399   GstOggChain *chain;
2400   guint i;
2401
2402   chain = ogg->current_chain;
2403   if (chain == NULL)
2404     return;
2405
2406   for (i = 0; i < chain->streams->len; i++) {
2407     GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2408
2409     stream->start_time = -1;
2410     stream->map.accumulated_granule = 0;
2411     stream->current_granule = -1;
2412     stream->keyframe_granule = -1;
2413   }
2414   ogg->building_chain = chain;
2415   GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2416   ogg->current_chain = NULL;
2417   ogg->resync = TRUE;
2418   gst_ogg_chain_mark_discont (chain);
2419
2420   ogg->chunk_size = CHUNKSIZE;
2421 }
2422
2423 static gboolean
2424 gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
2425 {
2426   gboolean res;
2427   GstOggDemux *ogg;
2428
2429   ogg = GST_OGG_DEMUX (parent);
2430
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);
2436       }
2437       res = gst_ogg_demux_send_event (ogg, event);
2438       break;
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);
2445       }
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);
2452       }
2453       break;
2454     case GST_EVENT_SEGMENT:
2455       GST_DEBUG_OBJECT (ogg, "got a new segment event");
2456       {
2457         GstSegment segment;
2458         gboolean update;
2459
2460         gst_event_copy_segment (event, &segment);
2461
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;
2466
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);
2477           }
2478
2479           if (!ogg->pullmode && !(ogg->push_seek_flags & GST_SEEK_FLAG_FLUSH)) {
2480             int i;
2481             GstOggChain *chain = ogg->current_chain;
2482
2483             ogg->push_seek_flags = 0;
2484             if (!chain) {
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);
2490             } else {
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;
2496               }
2497               ogg_sync_reset (&ogg->sync);
2498               gst_ogg_demux_reset_streams (ogg);
2499             }
2500           }
2501
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;
2508             }
2509           }
2510           GST_PUSH_UNLOCK (ogg);
2511         } else {
2512           GST_WARNING_OBJECT (ogg, "unexpected segment format: %s",
2513               gst_format_get_name (segment.format));
2514         }
2515       }
2516
2517       gst_event_unref (event);
2518       res = TRUE;
2519       break;
2520     case GST_EVENT_EOS:
2521     {
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",
2530               res);
2531         }
2532         gst_event_unref (event);
2533         res = TRUE;
2534         break;
2535       } else {
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);
2540           drop = TRUE;
2541         }
2542         GST_PUSH_UNLOCK (ogg);
2543         res = TRUE;
2544       }
2545       if (!drop)
2546         res = gst_ogg_demux_send_event (ogg, event);
2547       else
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;
2553         res = TRUE;
2554       }
2555       break;
2556     }
2557     default:
2558       res = gst_pad_event_default (pad, parent, event);
2559       break;
2560   }
2561
2562   return res;
2563 }
2564
2565 /* submit the given buffer to the ogg sync */
2566 static GstFlowReturn
2567 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
2568 {
2569   gsize size;
2570   gchar *oggbuffer;
2571   GstFlowReturn ret = GST_FLOW_OK;
2572
2573   size = gst_buffer_get_size (buffer);
2574   GST_DEBUG_OBJECT (ogg, "submitting %" G_GSIZE_FORMAT " bytes", size);
2575   if (G_UNLIKELY (size == 0))
2576     goto done;
2577
2578   oggbuffer = ogg_sync_buffer (&ogg->sync, size);
2579   if (G_UNLIKELY (oggbuffer == NULL))
2580     goto no_buffer;
2581
2582   gst_buffer_extract (buffer, 0, oggbuffer, size);
2583
2584   if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2585     goto write_failed;
2586
2587   if (!ogg->pullmode) {
2588     GST_PUSH_LOCK (ogg);
2589     ogg->push_byte_offset += size;
2590     GST_PUSH_UNLOCK (ogg);
2591   }
2592
2593 done:
2594   gst_buffer_unref (buffer);
2595
2596   return ret;
2597
2598   /* ERRORS */
2599 no_buffer:
2600   {
2601     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2602         (NULL), ("failed to get ogg sync buffer"));
2603     ret = GST_FLOW_ERROR;
2604     goto done;
2605   }
2606 write_failed:
2607   {
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;
2611     goto done;
2612   }
2613 }
2614
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.
2618  */
2619 static void
2620 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
2621 {
2622   GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
2623
2624   ogg->offset = offset;
2625   ogg->read_offset = offset;
2626   ogg_sync_reset (&ogg->sync);
2627 }
2628
2629 /* read more data from the current offset and submit to
2630  * the ogg sync layer.
2631  */
2632 static GstFlowReturn
2633 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
2634 {
2635   GstFlowReturn ret;
2636   GstBuffer *buffer;
2637   gchar *oggbuffer;
2638   gsize size;
2639
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);
2643
2644   if (end_offset > 0 && ogg->read_offset >= end_offset)
2645     goto boundary_reached;
2646
2647   if (ogg->read_offset == ogg->length)
2648     goto eos;
2649
2650   oggbuffer = ogg_sync_buffer (&ogg->sync, ogg->chunk_size);
2651   if (G_UNLIKELY (oggbuffer == NULL))
2652     goto no_buffer;
2653
2654   buffer =
2655       gst_buffer_new_wrapped_full (0, oggbuffer, ogg->chunk_size, 0,
2656       ogg->chunk_size, NULL, NULL);
2657
2658   ret =
2659       gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, ogg->chunk_size,
2660       &buffer);
2661   if (ret != GST_FLOW_OK)
2662     goto error;
2663
2664   size = gst_buffer_get_size (buffer);
2665
2666   if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2667     goto write_failed;
2668
2669   ogg->read_offset += size;
2670   gst_buffer_unref (buffer);
2671
2672   return ret;
2673
2674   /* ERROR */
2675 boundary_reached:
2676   {
2677     GST_LOG_OBJECT (ogg, "reached boundary");
2678     return GST_FLOW_LIMIT;
2679   }
2680 eos:
2681   {
2682     GST_LOG_OBJECT (ogg, "reached EOS");
2683     return GST_FLOW_EOS;
2684   }
2685 no_buffer:
2686   {
2687     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2688         (NULL), ("failed to get ogg sync buffer"));
2689     return GST_FLOW_ERROR;
2690   }
2691 error:
2692   {
2693     GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
2694         gst_flow_get_name (ret));
2695     gst_buffer_unref (buffer);
2696     return ret;
2697   }
2698 write_failed:
2699   {
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;
2704   }
2705 }
2706
2707 /* Read the next page from the current offset.
2708  * boundary: number of bytes ahead we allow looking for;
2709  * -1 if no boundary
2710  *
2711  * @offset will contain the offset the next page starts at when this function
2712  * returns GST_FLOW_OK.
2713  *
2714  * GST_FLOW_EOS is returned on EOS.
2715  *
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.
2718  *
2719  * Any other error returned while retrieving data from the peer is returned as
2720  * is.
2721  */
2722 static GstFlowReturn
2723 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
2724     gint64 boundary, gint64 * offset)
2725 {
2726   gint64 end_offset = -1;
2727   GstFlowReturn ret;
2728
2729   GST_LOG_OBJECT (ogg,
2730       "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
2731       G_GINT64_FORMAT, ogg->offset, boundary);
2732
2733   if (boundary >= 0)
2734     end_offset = ogg->offset + boundary;
2735
2736   while (TRUE) {
2737     glong more;
2738
2739     if (end_offset > 0 && ogg->offset >= end_offset)
2740       goto boundary_reached;
2741
2742     more = ogg_sync_pageseek (&ogg->sync, og);
2743
2744     GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
2745
2746     if (more < 0) {
2747       /* skipped n bytes */
2748       ogg->offset -= more;
2749       GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
2750           more, ogg->offset);
2751     } else if (more == 0) {
2752       /* we need more data */
2753       if (boundary == 0)
2754         goto boundary_reached;
2755
2756       GST_LOG_OBJECT (ogg, "need more data");
2757       ret = gst_ogg_demux_get_data (ogg, end_offset);
2758       if (ret != GST_FLOW_OK)
2759         break;
2760     } else {
2761       gint64 res_offset = ogg->offset;
2762
2763       /* got a page.  Return the offset at the page beginning,
2764          advance the internal offset past the page end */
2765       if (offset)
2766         *offset = res_offset;
2767       ret = GST_FLOW_OK;
2768
2769       ogg->offset += more;
2770
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));
2776       break;
2777     }
2778   }
2779   GST_LOG_OBJECT (ogg, "returning %d", ret);
2780
2781   return ret;
2782
2783   /* ERRORS */
2784 boundary_reached:
2785   {
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;
2790   }
2791 }
2792
2793 /* from the current offset, find the previous page, seeking backwards
2794  * until we find the page. 
2795  */
2796 static GstFlowReturn
2797 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
2798 {
2799   GstFlowReturn ret;
2800   gint64 begin = ogg->offset;
2801   gint64 end = begin;
2802   gint64 cur_offset = -1;
2803
2804   GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
2805
2806   while (cur_offset == -1) {
2807     begin -= ogg->chunk_size;
2808     if (begin < 0)
2809       begin = 0;
2810
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);
2814
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;
2820
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;
2829
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");
2834         break;
2835       }
2836       /* something went wrong */
2837       if (ret == GST_FLOW_EOS) {
2838         new_offset = 0;
2839         GST_LOG_OBJECT (ogg, "got unexpected");
2840         /* We hit EOS. */
2841         goto beach;
2842       } else if (ret != GST_FLOW_OK) {
2843         GST_LOG_OBJECT (ogg, "got error %d", ret);
2844         return ret;
2845       }
2846
2847       GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
2848
2849       /* offset is next page start */
2850       cur_offset = new_offset;
2851     }
2852   }
2853
2854   GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
2855
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,
2861         cur_offset);
2862     /* this shouldn't be possible */
2863     return ret;
2864   }
2865
2866   if (offset)
2867     *offset = cur_offset;
2868
2869 beach:
2870   return ret;
2871 }
2872
2873 static gboolean
2874 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
2875 {
2876   gint i;
2877   GstOggChain *chain = ogg->current_chain;
2878
2879   if (chain == NULL)
2880     return TRUE;
2881
2882   GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
2883
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);
2887     GstEvent *event;
2888
2889     if (!pad->added)
2890       continue;
2891
2892     event = gst_event_new_eos ();
2893     gst_event_set_seqnum (event, ogg->seqnum);
2894     gst_pad_push_event (GST_PAD_CAST (pad), event);
2895
2896     GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
2897
2898     /* deactivate first */
2899     gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
2900
2901     gst_flow_combiner_remove_pad (ogg->flowcombiner, GST_PAD_CAST (pad));
2902
2903     gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2904
2905     pad->added = FALSE;
2906   }
2907
2908   /* if we cannot seek back to the chain, we can destroy the chain 
2909    * completely */
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);
2915   }
2916
2917   return TRUE;
2918 }
2919
2920 static GstCaps *
2921 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
2922     GList * headers)
2923 {
2924   GstStructure *structure;
2925   GValue array = { 0 };
2926
2927   GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
2928
2929   if (G_UNLIKELY (!caps))
2930     return NULL;
2931   if (G_UNLIKELY (!headers))
2932     return caps;
2933
2934   caps = gst_caps_make_writable (caps);
2935   structure = gst_caps_get_structure (caps, 0);
2936
2937   g_value_init (&array, GST_TYPE_ARRAY);
2938
2939   while (headers) {
2940     GValue value = { 0 };
2941     GstBuffer *buffer;
2942     ogg_packet *op = headers->data;
2943     g_assert (op);
2944     buffer = gst_buffer_new_and_alloc (op->bytes);
2945     if (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;
2953   }
2954
2955   gst_structure_take_value (structure, "streamheader", &array);
2956   GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
2957
2958   return caps;
2959 }
2960
2961 static void
2962 gst_ogg_demux_push_queued_buffers (GstOggDemux * ogg, GstOggPad * pad)
2963 {
2964   GList *walk;
2965
2966   /* push queued packets */
2967   for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
2968     ogg_packet *p = walk->data;
2969
2970     gst_ogg_demux_chain_peer (pad, p, TRUE);
2971     _ogg_packet_free (p);
2972   }
2973   /* and free the queued buffers */
2974   g_list_free (pad->map.queued);
2975   pad->map.queued = NULL;
2976 }
2977
2978 static gboolean
2979 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
2980     GstEvent * event)
2981 {
2982   gint i;
2983   gint bitrate, idx_bitrate;
2984
2985   g_return_val_if_fail (chain != NULL, FALSE);
2986
2987   if (chain == ogg->current_chain) {
2988     if (event)
2989       gst_event_unref (event);
2990
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);
2994     }
2995     return TRUE;
2996   }
2997
2998
2999   GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
3000
3001   bitrate = idx_bitrate = 0;
3002
3003   /* first add the pads */
3004   for (i = 0; i < chain->streams->len; i++) {
3005     GstOggPad *pad;
3006     GstEvent *ss_event;
3007     gchar *stream_id;
3008
3009     pad = g_array_index (chain->streams, GstOggPad *, i);
3010
3011     if (pad->map.idx_bitrate)
3012       idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
3013
3014     bitrate += pad->map.bitrate;
3015
3016     /* mark discont */
3017     gst_ogg_pad_mark_discont (pad);
3018     pad->last_ret = GST_FLOW_OK;
3019
3020     if (pad->map.is_skeleton || pad->map.is_cmml || pad->added
3021         || !pad->map.caps)
3022       continue;
3023
3024     GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
3025
3026     /* activate first */
3027     gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
3028
3029     stream_id =
3030         gst_pad_create_stream_id_printf (GST_PAD (pad), GST_ELEMENT_CAST (ogg),
3031         "%08x", pad->map.serialno);
3032     ss_event =
3033         gst_pad_get_sticky_event (ogg->sinkpad, GST_EVENT_STREAM_START, 0);
3034     if (ss_event) {
3035       if (gst_event_parse_group_id (ss_event, &ogg->group_id))
3036         ogg->have_group_id = TRUE;
3037       else
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 ();
3043     }
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);
3047
3048     gst_pad_push_event (GST_PAD (pad), ss_event);
3049     g_free (stream_id);
3050
3051     /* Set headers on caps */
3052     pad->map.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);
3055
3056     gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
3057     pad->added = TRUE;
3058     gst_flow_combiner_add_pad (ogg->flowcombiner, GST_PAD_CAST (pad));
3059   }
3060   /* prefer the index bitrate over the ones encoded in the streams */
3061   ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
3062
3063   /* after adding the new pads, remove the old pads */
3064   gst_ogg_demux_deactivate_current_chain (ogg);
3065
3066   GST_DEBUG_OBJECT (ogg, "Setting current chain to %p", chain);
3067   ogg->current_chain = chain;
3068
3069   /* we are finished now */
3070   gst_element_no_more_pads (GST_ELEMENT (ogg));
3071
3072   GST_DEBUG_OBJECT (ogg, "starting chain");
3073
3074   /* then send out any headers and queued packets */
3075   for (i = 0; i < chain->streams->len; i++) {
3076     GList *walk;
3077     GstOggPad *pad;
3078     GstTagList *tags;
3079
3080     pad = g_array_index (chain->streams, GstOggPad *, i);
3081
3082     /* Skip pads that were not added, e.g. Skeleton streams */
3083     if (!pad->added)
3084       continue;
3085
3086     /* FIXME, must be sent from the streaming thread */
3087     if (event)
3088       gst_pad_push_event (GST_PAD_CAST (pad), gst_event_ref (event));
3089
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;
3096     }
3097
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));
3101
3102     GST_DEBUG_OBJECT (ogg, "pushing headers");
3103     /* push headers */
3104     for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
3105       ogg_packet *p = walk->data;
3106
3107       gst_ogg_demux_chain_peer (pad, p, TRUE);
3108     }
3109
3110     GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
3111     gst_ogg_demux_push_queued_buffers (ogg, pad);
3112   }
3113
3114   if (event)
3115     gst_event_unref (event);
3116
3117   return TRUE;
3118 }
3119
3120 static gboolean
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)
3124 {
3125   gint64 best;
3126   GstFlowReturn ret;
3127   gint64 result = 0;
3128
3129   best = begin;
3130
3131   GST_DEBUG_OBJECT (ogg,
3132       "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
3133       begin, end);
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));
3138
3139   /* perform the seek */
3140   while (begin < end) {
3141     gint64 bisect;
3142
3143     if ((end - begin < ogg->chunk_size) || (endtime == begintime)) {
3144       bisect = begin;
3145     } else {
3146       /* take a (pretty decent) guess, avoiding overflow */
3147       gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
3148
3149       bisect =
3150           (target - begintime) / GST_MSECOND * rate + begin - ogg->chunk_size;
3151
3152       if (bisect <= begin)
3153         bisect = begin;
3154       GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
3155     }
3156     gst_ogg_demux_seek (ogg, bisect);
3157
3158     while (begin < end) {
3159       ogg_page og;
3160
3161       GST_DEBUG_OBJECT (ogg,
3162           "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
3163           ", end %" G_GINT64_FORMAT, bisect, begin, end);
3164
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,
3167           result);
3168
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 */
3173         } else {
3174           if (bisect == 0)
3175             goto seek_error;
3176
3177           bisect -= ogg->chunk_size;
3178           if (bisect <= begin)
3179             bisect = begin + 1;
3180
3181           gst_ogg_demux_seek (ogg, bisect);
3182         }
3183       } else if (ret == GST_FLOW_OK) {
3184         /* found offset of next ogg page */
3185         gint64 granulepos;
3186         GstClockTime granuletime;
3187         GstOggPad *pad;
3188
3189         /* get the granulepos */
3190         GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
3191             result);
3192         granulepos = ogg_page_granulepos (&og);
3193         if (granulepos == -1) {
3194           GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
3195           continue;
3196         }
3197
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)
3201           continue;
3202
3203         /* get the stream */
3204         pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
3205         if (pad == NULL || pad->map.is_skeleton)
3206           continue;
3207
3208         /* convert granulepos to time */
3209         granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
3210             granulepos);
3211         if (granuletime < pad->start_time)
3212           continue;
3213
3214         GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to PTS %"
3215             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
3216
3217         granuletime -= pad->start_time;
3218         granuletime += chain->begin_time;
3219
3220         GST_DEBUG_OBJECT (ogg,
3221             "found page with granule %" G_GINT64_FORMAT " and time %"
3222             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
3223
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;
3228
3229           bisect = begin;       /* *not* begin + 1 */
3230         } else {
3231           if (bisect <= begin + 1) {
3232             end = begin;        /* found it */
3233           } else {
3234             if (end == ogg->offset) {   /* we're pretty close - we'd be stuck in */
3235               end = result;
3236               bisect -= ogg->chunk_size;        /* an endless loop otherwise. */
3237               if (bisect <= begin)
3238                 bisect = begin + 1;
3239               gst_ogg_demux_seek (ogg, bisect);
3240             } else {
3241               end = result;
3242               endtime = granuletime;
3243               break;
3244             }
3245           }
3246         }
3247       } else
3248         goto seek_error;
3249     }
3250   }
3251   GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
3252   gst_ogg_demux_seek (ogg, best);
3253   *offset = best;
3254
3255   return TRUE;
3256
3257   /* ERRORS */
3258 seek_error:
3259   {
3260     GST_DEBUG_OBJECT (ogg, "got a seek error");
3261     return FALSE;
3262   }
3263 }
3264
3265 static gboolean
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)
3269 {
3270   guint i;
3271   guint64 timestamp, offset;
3272   guint64 r_timestamp, r_offset;
3273   gboolean result = FALSE;
3274
3275   target -= begintime;
3276
3277   r_offset = -1;
3278   r_timestamp = -1;
3279
3280   for (i = 0; i < chain->streams->len; i++) {
3281     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3282
3283     timestamp = target;
3284     if (gst_ogg_map_search_index (&pad->map, TRUE, &timestamp, &offset)) {
3285       GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
3286           timestamp, offset);
3287
3288       if (r_offset == -1 || offset < r_offset) {
3289         r_offset = offset;
3290         r_timestamp = timestamp;
3291       }
3292       result |= TRUE;
3293     }
3294   }
3295
3296   if (p_timestamp)
3297     *p_timestamp = r_timestamp;
3298   if (p_offset)
3299     *p_offset = r_offset;
3300
3301   return result;
3302 }
3303
3304 /*
3305  * do seek to time @position, return FALSE or chain and TRUE
3306  */
3307 static gboolean
3308 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
3309     gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
3310 {
3311   guint64 position;
3312   GstOggChain *chain = NULL;
3313   gint64 begin, end;
3314   gint64 begintime, endtime;
3315   gint64 target, keytarget;
3316   gint64 best;
3317   gint64 total;
3318   gint64 result = 0;
3319   GstFlowReturn ret;
3320   gint i, pending;
3321   gint serialno = 0;
3322   gboolean found_keyframe = FALSE;
3323   GstClockTime ts, first_ts = GST_CLOCK_TIME_NONE;
3324
3325   position = segment->position;
3326
3327   /* first find the chain to search in */
3328   total = ogg->total_time;
3329   if (ogg->chains->len == 0)
3330     goto no_chains;
3331
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)
3336       break;
3337   }
3338
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;
3345
3346   if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
3347           &best, FALSE, 0))
3348     goto seek_error;
3349
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
3353    * each keyframe.
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);
3364     if (!pad) {
3365       GST_WARNING_OBJECT (ogg, "No pad at index %d", i);
3366       pending--;
3367       continue;
3368     }
3369     if (pad->map.is_skeleton) {
3370       GST_DEBUG_OBJECT (ogg, "Not finding pages for Skeleton stream %08x",
3371           pad->map.serialno);
3372       pending--;
3373       continue;
3374     }
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));
3378       pending--;
3379       continue;
3380     }
3381   }
3382   GST_DEBUG_OBJECT (ogg, "find keyframes for %d/%d streams", pending,
3383       chain->streams->len);
3384
3385   /* figure out where the keyframes are */
3386   keytarget = target;
3387
3388   while (TRUE) {
3389     ogg_page og;
3390     gint64 granulepos;
3391     GstOggPad *pad;
3392     GstClockTime keyframe_time, granule_time;
3393
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,
3396         result);
3397     if (ret == GST_FLOW_LIMIT) {
3398       GST_LOG_OBJECT (ogg, "reached limit");
3399       break;
3400     } else if (ret != GST_FLOW_OK)
3401       goto seek_error;
3402
3403     /* get the stream */
3404     pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
3405     if (pad == NULL)
3406       continue;
3407
3408     if (pad->map.is_skeleton || pad->map.is_sparse)
3409       goto next;
3410
3411     granulepos = ogg_page_granulepos (&og);
3412     if (granulepos == -1 || granulepos == 0) {
3413       GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
3414       continue;
3415     }
3416
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));
3427         first_ts = ts;
3428       }
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;
3434         keytarget = target;
3435         break;
3436       }
3437     }
3438
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,
3443           granulepos);
3444
3445       /* Convert to stream time */
3446       granule_time -= pad->start_time;
3447       granule_time += chain->begin_time;
3448
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)
3454         continue;
3455     }
3456
3457     /* we've seen this pad before */
3458     if (pad->keyframe_granule != -1)
3459       continue;
3460
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);
3466
3467     /* get time of the keyframe */
3468     keyframe_time =
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));
3475
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));
3486       }
3487     }
3488
3489   next:
3490     pending--;
3491     if (pending == 0)
3492       break;
3493   }
3494
3495   /* for negative rates we will get to the keyframe backwards */
3496   if (segment->rate < 0.0)
3497     goto done;
3498
3499   /* No keyframe found, no need to bisect again, keytarget == target here */
3500   if (!found_keyframe)
3501     best = 0;
3502
3503   if (keytarget != target) {
3504     GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
3505         GST_TIME_ARGS (keytarget));
3506
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))
3510       goto seek_error;
3511   } else {
3512     /* seek back to previous position */
3513     GST_LOG_OBJECT (ogg, "keyframe on target");
3514     gst_ogg_demux_seek (ogg, best);
3515   }
3516
3517 done:
3518   if (keyframe) {
3519     if (segment->rate > 0.0)
3520       segment->time = keytarget;
3521     segment->position = keytarget - begintime;
3522   }
3523
3524   *rchain = chain;
3525
3526   return TRUE;
3527
3528 no_chains:
3529   {
3530     GST_DEBUG_OBJECT (ogg, "no chains");
3531     return FALSE;
3532   }
3533 seek_error:
3534   {
3535     GST_DEBUG_OBJECT (ogg, "got a seek error");
3536     return FALSE;
3537   }
3538 }
3539
3540 /* does not take ownership of the event */
3541 static gboolean
3542 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
3543 {
3544   GstOggChain *chain = NULL;
3545   gboolean res;
3546   gboolean accurate, keyframe;
3547   GstFormat format;
3548   gdouble rate;
3549   GstSeekFlags flags;
3550   GstSeekType start_type, stop_type;
3551   gint64 start, stop;
3552   gboolean update;
3553   guint32 seqnum;
3554
3555   if (event) {
3556     GST_DEBUG_OBJECT (ogg, "seek with event");
3557
3558     gst_event_parse_seek (event, &rate, &format, &flags,
3559         &start_type, &start, &stop_type, &stop);
3560
3561     /* we can only seek on time */
3562     if (format != GST_FORMAT_TIME) {
3563       GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3564       goto error;
3565     }
3566     seqnum = gst_event_get_seqnum (event);
3567   } else {
3568     GST_DEBUG_OBJECT (ogg, "seek without event");
3569
3570     flags = 0;
3571     rate = 1.0;
3572     seqnum = gst_util_seqnum_next ();
3573   }
3574
3575   GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
3576
3577   accurate = flags & GST_SEEK_FLAG_ACCURATE;
3578   keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
3579
3580   gst_pad_pause_task (ogg->sinkpad);
3581
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
3584    * forever. */
3585   GST_PAD_STREAM_LOCK (ogg->sinkpad);
3586
3587   if (event) {
3588     gst_segment_do_seek (&ogg->segment, rate, format, flags,
3589         start_type, start, stop_type, stop, &update);
3590   }
3591
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));
3595
3596   {
3597     gint i;
3598
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);
3603
3604       gst_ogg_chain_reset (chain);
3605     }
3606   }
3607
3608   /* for reverse we will already seek accurately */
3609   res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
3610
3611   /* seek failed, make sure we continue the current chain */
3612   if (!res) {
3613     GST_DEBUG_OBJECT (ogg, "seek failed");
3614     chain = ogg->current_chain;
3615   } else {
3616     GST_DEBUG_OBJECT (ogg, "seek success");
3617   }
3618
3619   if (!chain)
3620     goto no_chain;
3621
3622   /* now we have a new position, prepare for streaming again */
3623   {
3624     GstEvent *event;
3625     gint64 stop;
3626     gint64 start;
3627     gint64 position, begin_time;
3628     GstSegment segment;
3629
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;
3633     else
3634       begin_time = 0;
3635
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;
3641
3642     if ((stop = ogg->segment.stop) == -1)
3643       stop = ogg->segment.duration;
3644
3645     /* segment.stop gives the stop time over all chains, calculate the amount of
3646      * time we need to stop in this chain */
3647     if (stop != -1) {
3648       if (stop > begin_time)
3649         stop -= begin_time;
3650       else
3651         stop = 0;
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);
3656     }
3657
3658     position = ogg->segment.position;
3659     if (chain->segment_start != GST_CLOCK_TIME_NONE)
3660       position += chain->segment_start;
3661
3662     gst_segment_copy_into (&ogg->segment, &segment);
3663
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;
3668     } else {
3669       segment.start = start;
3670       segment.stop = position;
3671     }
3672     event = gst_event_new_segment (&segment);
3673     gst_event_set_seqnum (event, seqnum);
3674
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);
3678     } else {
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;
3685     }
3686
3687     /* notify start of new segment */
3688     if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3689       GstMessage *message;
3690
3691       message = gst_message_new_segment_start (GST_OBJECT (ogg),
3692           GST_FORMAT_TIME, ogg->segment.position);
3693       gst_message_set_seqnum (message, seqnum);
3694
3695       gst_element_post_message (GST_ELEMENT (ogg), message);
3696     }
3697
3698     ogg->seqnum = seqnum;
3699     /* restart our task since it might have been stopped when we did the 
3700      * flush. */
3701     gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3702         ogg->sinkpad, NULL);
3703   }
3704
3705   /* streaming can continue now */
3706   GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3707
3708 done:
3709   if (event)
3710     gst_event_unref (event);
3711   return res;
3712
3713   /* ERRORS */
3714 error:
3715   {
3716     GST_DEBUG_OBJECT (ogg, "seek failed");
3717     res = FALSE;
3718     goto done;
3719   }
3720 no_chain:
3721   {
3722     GST_DEBUG_OBJECT (ogg, "no chain to seek in");
3723     GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3724     res = FALSE;
3725     goto done;
3726   }
3727 }
3728
3729 static gboolean
3730 gst_ogg_demux_get_duration_push (GstOggDemux * ogg, int flags)
3731 {
3732   /* In push mode, we get to the end of the stream to get the duration */
3733   gint64 position;
3734   GstEvent *sevent;
3735
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;
3739   if (position < 0)
3740     position = 0;
3741
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);
3754   return TRUE;
3755 }
3756
3757 static gboolean
3758 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3759     GstEvent * event)
3760 {
3761   if (ogg->push_byte_length < 0) {
3762     GstPad *peer;
3763
3764     GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3765     if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3766       gint64 length;
3767       int res;
3768
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);
3774       } else {
3775         GST_DEBUG_OBJECT (ogg, "File byte length unknown, assuming live");
3776         ogg->push_disable_seeking = TRUE;
3777         gst_object_unref (peer);
3778         return TRUE;
3779       }
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) {
3787         gboolean res;
3788
3789         res = gst_ogg_demux_get_duration_push (ogg, flags);
3790         if (res) {
3791           GST_DEBUG_OBJECT (ogg,
3792               "File time length unknown, trying to determine");
3793           ogg->push_mode_seek_delayed_event = NULL;
3794           if (event) {
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);
3798           }
3799           return FALSE;
3800         }
3801       }
3802     }
3803   }
3804   return TRUE;
3805 }
3806
3807 static gboolean
3808 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3809 {
3810   gint bitrate;
3811   gboolean res = TRUE;
3812   GstFormat format;
3813   gdouble rate;
3814   GstSeekFlags flags;
3815   GstSeekType start_type, stop_type;
3816   gint64 start, stop;
3817   GstEvent *sevent;
3818   GstOggChain *chain;
3819   gint64 best, best_time;
3820   gint i;
3821
3822   GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3823
3824   gst_event_parse_seek (event, &rate, &format, &flags,
3825       &start_type, &start, &stop_type, &stop);
3826
3827   if (format != GST_FORMAT_TIME) {
3828     GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3829     goto error;
3830   }
3831
3832   if (start_type != GST_SEEK_TYPE_SET) {
3833     GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3834     goto error;
3835   }
3836
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)
3840     stop = -1;
3841
3842   GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3843       GST_TIME_ARGS (start));
3844
3845   chain = ogg->current_chain;
3846   if (!chain) {
3847     GST_WARNING_OBJECT (ogg, "No chain to seek on");
3848     goto error;
3849   }
3850
3851   /* start accessing push_* members */
3852   GST_PUSH_LOCK (ogg);
3853
3854   /* not if we disabled seeking (chained streams) */
3855   if (ogg->push_disable_seeking) {
3856     GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3857     goto error_locked;
3858   }
3859
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");
3863     goto error_locked;
3864   }
3865
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");
3869     goto error_locked;
3870   }
3871
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);
3875     return FALSE;
3876   }
3877
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,
3882         best, best_time);
3883   } else {
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");
3887       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");
3893       bitrate =
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;
3900     } else {
3901       /* meh */
3902       GST_DEBUG_OBJECT (ogg,
3903           "At stream start, and no nominal bitrate, using some random magic "
3904           "number to seed");
3905       /* the bisection, once started, should give us a better approximation */
3906       bitrate = 1000;
3907     }
3908     best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3909   }
3910
3911   /* offset by typical page length, and ensure our best guess is within
3912      reasonable bounds */
3913   best -= ogg->chunk_size;
3914   if (best < 0)
3915     best = 0;
3916   if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3917     best = ogg->push_byte_length - 1;
3918
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;
3932
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;
3939     }
3940   }
3941
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);
3950
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));
3959
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);
3966
3967   return res;
3968
3969   /* ERRORS */
3970 error:
3971   {
3972     GST_DEBUG_OBJECT (ogg, "seek failed");
3973     return FALSE;
3974   }
3975
3976 error_locked:
3977   GST_PUSH_UNLOCK (ogg);
3978   goto error;
3979 }
3980
3981 static gboolean
3982 gst_ogg_demux_setup_seek_pull (GstOggDemux * ogg, GstEvent * event)
3983 {
3984   gboolean flush;
3985   GstSeekFlags flags;
3986   GstEvent *tevent;
3987   guint32 seqnum = gst_event_get_seqnum (event);
3988
3989   GST_DEBUG_OBJECT (ogg, "Scheduling seek: %" GST_PTR_FORMAT, event);
3990   gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
3991
3992   flush = flags & GST_SEEK_FLAG_FLUSH;
3993
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. */
3998   if (flush) {
3999     gint i;
4000
4001     tevent = gst_event_new_flush_start ();
4002     gst_event_set_seqnum (tevent, seqnum);
4003
4004     gst_event_ref (tevent);
4005     gst_pad_push_event (ogg->sinkpad, tevent);
4006
4007     GST_CHAIN_LOCK (ogg);
4008     for (i = 0; i < ogg->chains->len; i++) {
4009       GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4010       gint j;
4011
4012       for (j = 0; j < chain->streams->len; j++) {
4013         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
4014
4015         gst_event_ref (tevent);
4016         gst_pad_push_event (GST_PAD (pad), tevent);
4017       }
4018     }
4019     GST_CHAIN_UNLOCK (ogg);
4020
4021     gst_event_unref (tevent);
4022   }
4023
4024   gst_pad_pause_task (ogg->sinkpad);
4025
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
4028    * forever. */
4029   GST_PAD_STREAM_LOCK (ogg->sinkpad);
4030
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. */
4033   if (flush) {
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);
4038   }
4039
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);
4044
4045   return TRUE;
4046 }
4047
4048 static gboolean
4049 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
4050 {
4051   gboolean res;
4052
4053   if (ogg->pullmode) {
4054     res = gst_ogg_demux_setup_seek_pull (ogg, event);
4055   } else {
4056     res = gst_ogg_demux_perform_seek_push (ogg, event);
4057   }
4058   return res;
4059 }
4060
4061
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
4066  */
4067 static GstFlowReturn
4068 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
4069     gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
4070 {
4071   gint64 endsearched = end;
4072   gint64 next = end;
4073   ogg_page og;
4074   GstFlowReturn ret;
4075   gint64 offset;
4076   GstOggChain *nextchain;
4077
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);
4081
4082   /* the below guards against garbage separating the last and
4083    * first pages of two links. */
4084   while (searched < endsearched) {
4085     gint64 bisect;
4086
4087     if (endsearched - searched < ogg->chunk_size) {
4088       bisect = searched;
4089     } else {
4090       bisect = (searched + endsearched) / 2;
4091     }
4092
4093     gst_ogg_demux_seek (ogg, bisect);
4094     ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
4095
4096     if (ret == GST_FLOW_EOS) {
4097       endsearched = bisect;
4098     } else if (ret == GST_FLOW_OK) {
4099       guint32 serial = ogg_page_serialno (&og);
4100
4101       if (!gst_ogg_chain_has_stream (chain, serial)) {
4102         endsearched = bisect;
4103         next = offset;
4104       } else {
4105         searched = offset + og.header_len + og.body_len;
4106       }
4107     } else
4108       return ret;
4109   }
4110
4111   GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
4112
4113   chain->end_offset = searched;
4114   ret = gst_ogg_demux_read_end_chain (ogg, chain);
4115   if (ret != GST_FLOW_OK)
4116     return ret;
4117
4118   GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
4119
4120   gst_ogg_demux_seek (ogg, next);
4121   ret = gst_ogg_demux_read_chain (ogg, &nextchain);
4122   if (ret == GST_FLOW_EOS) {
4123     nextchain = NULL;
4124     ret = GST_FLOW_OK;
4125     GST_LOG_OBJECT (ogg, "no next chain");
4126   } else if (ret != GST_FLOW_OK)
4127     goto done;
4128
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)
4133       goto done;
4134   }
4135   GST_LOG_OBJECT (ogg, "adding chain %p", chain);
4136
4137   g_array_insert_val (ogg->chains, 0, chain);
4138
4139 done:
4140   return ret;
4141 }
4142
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. 
4146  * 
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.
4150  */
4151 static GstFlowReturn
4152 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
4153 {
4154   GstFlowReturn ret;
4155   GstOggChain *chain = NULL;
4156   gint64 offset = ogg->offset;
4157   ogg_page og;
4158   gboolean done;
4159   gint i;
4160
4161   GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
4162
4163   /* first read the BOS pages, detect the stream types, create the internal
4164    * stream mappers, send data to them. */
4165   while (TRUE) {
4166     GstOggPad *pad;
4167     guint32 serial;
4168
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");
4173       } else {
4174         GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
4175       }
4176       break;
4177     }
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
4181        * ignore it */
4182       if (!chain) {
4183         GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
4184         ret = GST_FLOW_EOS;
4185       }
4186       break;
4187     }
4188
4189     if (chain == NULL) {
4190       chain = gst_ogg_chain_new (ogg);
4191       chain->offset = offset;
4192     }
4193
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);
4198       continue;
4199     }
4200
4201     pad = gst_ogg_chain_new_stream (chain, serial);
4202     gst_ogg_pad_submit_page (pad, &og);
4203   }
4204
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");
4211     } else {
4212       GST_DEBUG_OBJECT (ogg, "done reading chains");
4213     }
4214     if (chain) {
4215       gst_ogg_chain_free (chain);
4216     }
4217     if (res_chain)
4218       *res_chain = NULL;
4219     return ret;
4220   }
4221
4222   chain->have_bos = TRUE;
4223   GST_INFO_OBJECT (ogg, "read bos pages, ");
4224
4225   /* now read pages until each ogg stream mapper has figured out the
4226    * timestamp of the first packet in the chain */
4227
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
4230    * position
4231    */
4232   offset = ogg->offset;
4233
4234   done = FALSE;
4235   while (!done) {
4236     guint32 serial;
4237     gboolean known_serial = FALSE;
4238     GstFlowReturn ret;
4239
4240     serial = ogg_page_serialno (&og);
4241     done = TRUE;
4242     for (i = 0; i < chain->streams->len; i++) {
4243       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4244
4245       GST_LOG_OBJECT (ogg,
4246           "serial %08x time %" GST_TIME_FORMAT,
4247           pad->map.serialno, GST_TIME_ARGS (pad->start_time));
4248
4249       if (pad->map.serialno == serial) {
4250         known_serial = TRUE;
4251
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);
4255
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).
4261            */
4262           done = TRUE;
4263           break;
4264         }
4265       }
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);
4269
4270       GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
4271     }
4272
4273     /* we read a page not belonging to the current chain: seek back to the
4274      * beginning of the chain
4275      */
4276     if (!known_serial) {
4277       GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
4278       gst_ogg_demux_seek (ogg, offset);
4279       break;
4280     }
4281
4282     if (!done) {
4283       ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
4284       if (ret != GST_FLOW_OK)
4285         break;
4286     }
4287   }
4288   GST_LOG_OBJECT (ogg, "done reading chain");
4289
4290   if (res_chain)
4291     *res_chain = chain;
4292
4293   return GST_FLOW_OK;
4294 }
4295
4296 /* read the last pages from the ogg stream to get the final
4297  * page end_offsets.
4298  */
4299 static GstFlowReturn
4300 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
4301 {
4302   gint64 begin = chain->end_offset;
4303   gint64 end = begin;
4304   gint64 last_granule = -1;
4305   GstOggPad *last_pad = NULL;
4306   GstFlowReturn ret;
4307   gboolean done = FALSE;
4308   ogg_page og;
4309   gint i;
4310
4311   while (!done) {
4312     begin -= ogg->chunk_size;
4313     if (begin < 0)
4314       begin = 0;
4315
4316     gst_ogg_demux_seek (ogg, begin);
4317
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);
4323
4324       if (ret == GST_FLOW_LIMIT)
4325         break;
4326       if (ret != GST_FLOW_OK)
4327         return ret;
4328
4329       for (i = 0; i < chain->streams->len; i++) {
4330         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4331
4332         if (pad->map.is_skeleton)
4333           continue;
4334
4335         if (pad->map.serialno == ogg_page_serialno (&og)) {
4336           gint64 granulepos = ogg_page_granulepos (&og);
4337
4338           if (granulepos != -1) {
4339             last_granule = granulepos;
4340             last_pad = pad;
4341             done = TRUE;
4342           }
4343           break;
4344         }
4345       }
4346     }
4347   }
4348
4349   if (last_pad) {
4350     chain->segment_stop =
4351         gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
4352         last_granule);
4353   } else {
4354     chain->segment_stop = GST_CLOCK_TIME_NONE;
4355   }
4356
4357   GST_INFO ("segment stop %" G_GUINT64_FORMAT ", for last granule %"
4358       G_GUINT64_FORMAT, chain->segment_stop, last_granule);
4359
4360   return GST_FLOW_OK;
4361 }
4362
4363 /* find a pad with a given serial number
4364  */
4365 static GstOggPad *
4366 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
4367 {
4368   GstOggPad *pad;
4369   gint i;
4370
4371   /* first look in building chain if any */
4372   if (ogg->building_chain) {
4373     pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
4374     if (pad)
4375       return pad;
4376   }
4377
4378   /* then look in current chain if any */
4379   if (ogg->current_chain) {
4380     pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
4381     if (pad)
4382       return pad;
4383   }
4384
4385   for (i = 0; i < ogg->chains->len; i++) {
4386     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4387
4388     pad = gst_ogg_chain_get_stream (chain, serialno);
4389     if (pad)
4390       return pad;
4391   }
4392   return NULL;
4393 }
4394
4395 /* find a chain with a given serial number
4396  */
4397 static GstOggChain *
4398 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
4399 {
4400   GstOggPad *pad;
4401
4402   pad = gst_ogg_demux_find_pad (ogg, serialno);
4403   if (pad) {
4404     return pad->chain;
4405   }
4406   return NULL;
4407 }
4408
4409 /* returns TRUE if all streams have valid start time */
4410 static gboolean
4411 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
4412 {
4413   gboolean res = TRUE;
4414
4415   chain->total_time = GST_CLOCK_TIME_NONE;
4416   GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
4417
4418   /* see if we have a start time on all streams */
4419   chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
4420
4421   if (chain->segment_start == G_MAXUINT64) {
4422     /* not yet, stream some more data */
4423     res = FALSE;
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;
4427   }
4428
4429   GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
4430
4431   GST_DEBUG_OBJECT (ogg, "return %d", res);
4432
4433   return res;
4434 }
4435
4436 static void
4437 gst_ogg_demux_collect_info (GstOggDemux * ogg)
4438 {
4439   gint i;
4440
4441   /* collect all info */
4442   ogg->total_time = 0;
4443
4444   for (i = 0; i < ogg->chains->len; i++) {
4445     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4446
4447     chain->begin_time = ogg->total_time;
4448
4449     gst_ogg_demux_collect_chain_info (ogg, chain);
4450
4451     ogg->total_time += chain->total_time;
4452   }
4453   ogg->segment.duration = ogg->total_time;
4454 }
4455
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.
4459  */
4460 static GstFlowReturn
4461 gst_ogg_demux_find_chains (GstOggDemux * ogg)
4462 {
4463   ogg_page og;
4464   GstPad *peer;
4465   gboolean res;
4466   guint32 serialno;
4467   GstOggChain *chain;
4468   GstFlowReturn ret;
4469
4470   /* get peer to figure out length */
4471   if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
4472     goto no_peer;
4473
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)
4478     goto no_length;
4479
4480   GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
4481
4482   /* read chain from offset 0, this is the first chain of the
4483    * ogg file. */
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)
4488       goto flushing;
4489     else
4490       goto no_first_chain;
4491   }
4492
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)
4499     goto no_last_page;
4500
4501   serialno = ogg_page_serialno (&og);
4502
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. */
4506     ret =
4507         gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
4508         0);
4509   } else {
4510     /* we still call this function here but with an empty range so that
4511      * we can reuse the setup code in this routine. */
4512     ret =
4513         gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
4514         ogg->length, chain, 0);
4515   }
4516   if (ret != GST_FLOW_OK)
4517     goto done;
4518
4519   /* all fine, collect and print */
4520   gst_ogg_demux_collect_info (ogg);
4521
4522   /* dump our chains and streams */
4523   gst_ogg_print (ogg);
4524
4525 done:
4526   return ret;
4527
4528   /*** error cases ***/
4529 no_peer:
4530   {
4531     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
4532     return GST_FLOW_NOT_LINKED;
4533   }
4534 no_length:
4535   {
4536     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
4537     return GST_FLOW_NOT_SUPPORTED;
4538   }
4539 no_first_chain:
4540   {
4541     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
4542     return GST_FLOW_ERROR;
4543   }
4544 no_last_page:
4545   {
4546     GST_DEBUG_OBJECT (ogg, "can't get last page");
4547     if (chain)
4548       gst_ogg_chain_free (chain);
4549     return ret;
4550   }
4551 flushing:
4552   {
4553     GST_DEBUG_OBJECT (ogg, "Flushing, can't read chain");
4554     return GST_FLOW_FLUSHING;
4555   }
4556 }
4557
4558 static void
4559 gst_ogg_demux_update_chunk_size (GstOggDemux * ogg, ogg_page * page)
4560 {
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;
4566   }
4567 }
4568
4569 static GstFlowReturn
4570 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page, gboolean discont)
4571 {
4572   GstOggPad *pad;
4573   gint64 granule;
4574   guint32 serialno;
4575   GstFlowReturn result = GST_FLOW_OK;
4576
4577   serialno = ogg_page_serialno (page);
4578   granule = ogg_page_granulepos (page);
4579
4580   gst_ogg_demux_update_chunk_size (ogg, page);
4581
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));
4586
4587   if (ogg_page_bos (page)) {
4588     GstOggChain *chain;
4589
4590     /* first page */
4591     /* see if we know about the chain already */
4592     chain = gst_ogg_demux_find_chain (ogg, serialno);
4593     if (chain) {
4594       GstEvent *event;
4595       gint64 start = 0;
4596       GstSegment segment;
4597
4598       if (chain->segment_start != GST_CLOCK_TIME_NONE)
4599         start = chain->segment_start;
4600
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);
4609
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));
4615
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);
4620     } else {
4621       GstClockTime chain_time;
4622       gint64 current_time;
4623
4624       /* this can only happen in push mode */
4625       if (ogg->pullmode)
4626         goto unknown_chain;
4627
4628       current_time = ogg->segment.position;
4629
4630       /* time of new chain is current time */
4631       chain_time = current_time;
4632
4633       if (ogg->building_chain == NULL) {
4634         GstOggChain *newchain;
4635
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));
4642
4643         /* and this is the one we are building now */
4644         ogg->building_chain = newchain;
4645       }
4646       pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
4647     }
4648   } else {
4649     pad = gst_ogg_demux_find_pad (ogg, serialno);
4650   }
4651   if (pad) {
4652     /* Reset granule interpolation if chaining in reverse (discont = TRUE) */
4653     if (discont)
4654       pad->current_granule = -1;
4655
4656     result = gst_ogg_pad_submit_page (pad, page);
4657   } else {
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) {
4664         GstFlowReturn res;
4665
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)
4670           return res;
4671       }
4672
4673       /* only once we seeked back */
4674       ogg->push_disable_seeking = TRUE;
4675     } else {
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));
4681       return GST_FLOW_OK;
4682     }
4683     GST_PUSH_UNLOCK (ogg);
4684   }
4685   return result;
4686
4687   /* ERRORS */
4688 unknown_chain:
4689   {
4690     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
4691         (NULL), ("unknown ogg chain for serial %08x detected", serialno));
4692     return GST_FLOW_ERROR;
4693   }
4694 }
4695
4696 /* streaming mode, receive a buffer, parse it, create pads for
4697  * the serialno, submit pages and packets to the oggpads
4698  */
4699 static GstFlowReturn
4700 gst_ogg_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
4701 {
4702   GstOggDemux *ogg;
4703   gint ret = 0;
4704   GstFlowReturn result = GST_FLOW_OK;
4705   gboolean drop;
4706
4707   ogg = GST_OGG_DEMUX (parent);
4708
4709   GST_PUSH_LOCK (ogg);
4710   drop = (ogg->seek_event_drop_till > 0);
4711   GST_PUSH_UNLOCK (ogg);
4712   if (drop) {
4713     GST_DEBUG_OBJECT (ogg, "Dropping buffer because we have a pending seek");
4714     gst_buffer_unref (buffer);
4715     return GST_FLOW_OK;
4716   }
4717
4718   GST_DEBUG_OBJECT (ogg, "enter");
4719   result = gst_ogg_demux_submit_buffer (ogg, buffer);
4720   if (result < 0) {
4721     GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
4722   }
4723
4724   while (result == GST_FLOW_OK) {
4725     ogg_page page;
4726
4727     ret = ogg_sync_pageout (&ogg->sync, &page);
4728     if (ret == 0)
4729       /* need more data */
4730       break;
4731     if (ret == -1) {
4732       /* discontinuity in the pages */
4733       GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
4734     } else {
4735       result = gst_ogg_demux_handle_page (ogg, &page, FALSE);
4736       if (result < 0) {
4737         GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
4738       }
4739     }
4740   }
4741   if (ret == 0 || result == GST_FLOW_OK) {
4742     gst_ogg_demux_sync_streams (ogg);
4743   }
4744   GST_DEBUG_OBJECT (ogg, "leave with %d", result);
4745   return result;
4746 }
4747
4748 static gboolean
4749 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
4750 {
4751   GstOggChain *chain = ogg->current_chain;
4752   gboolean event_sent = FALSE;
4753   gboolean res = TRUE;
4754
4755   if (!chain)
4756     chain = ogg->building_chain;
4757
4758   if (chain) {
4759     gint i;
4760
4761     for (i = 0; i < chain->streams->len; i++) {
4762       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4763
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);
4767       if (pad->added)
4768         event_sent = TRUE;
4769     }
4770   }
4771
4772   gst_event_unref (event);
4773
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"));
4777   }
4778
4779   return res;
4780 }
4781
4782 static GstFlowReturn
4783 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
4784     GstFlowReturn ret)
4785 {
4786   /* store the value */
4787   pad->last_ret = ret;
4788   pad->is_eos = (ret == GST_FLOW_EOS);
4789
4790   return gst_flow_combiner_update_pad_flow (ogg->flowcombiner,
4791       GST_PAD_CAST (pad), ret);
4792 }
4793
4794 static GstFlowReturn
4795 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4796 {
4797   GstFlowReturn ret;
4798   GstBuffer *buffer = NULL;
4799
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);
4803     ret = GST_FLOW_EOS;
4804     goto done;
4805   }
4806
4807   GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4808   ret =
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");
4812     goto done;
4813   }
4814
4815   ogg->offset += gst_buffer_get_size (buffer);
4816
4817   if (G_UNLIKELY (ogg->newsegment)) {
4818     gst_ogg_demux_send_event (ogg, ogg->newsegment);
4819     ogg->newsegment = NULL;
4820   }
4821
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");
4825   }
4826
4827 done:
4828   return ret;
4829 }
4830
4831 /* reverse mode.
4832  *
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.
4835  *
4836  * Special care has to be taken for continued pages, which we can only decode
4837  * when we have the previous page(s).
4838  */
4839 static GstFlowReturn
4840 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4841 {
4842   GstFlowReturn ret;
4843   ogg_page page;
4844   gint64 offset;
4845
4846   if (ogg->offset == 0) {
4847     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4848         " == 0", ogg->offset);
4849     ret = GST_FLOW_EOS;
4850     goto done;
4851   }
4852
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)
4856     goto done;
4857
4858   ogg->offset = offset;
4859
4860   if (G_UNLIKELY (ogg->newsegment)) {
4861     gst_ogg_demux_send_event (ogg, ogg->newsegment);
4862     ogg->newsegment = NULL;
4863   }
4864
4865   GST_LOG_OBJECT (ogg, "Handling page at offset %" G_GINT64_FORMAT,
4866       ogg->offset);
4867   ret = gst_ogg_demux_handle_page (ogg, &page, TRUE);
4868
4869 done:
4870   return ret;
4871 }
4872
4873 static void
4874 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4875 {
4876   GstClockTime cur;
4877   GstOggChain *chain;
4878   guint i;
4879
4880   chain = ogg->current_chain;
4881   cur = ogg->segment.position;
4882   if (chain == NULL || cur == -1)
4883     return;
4884
4885   for (i = 0; i < chain->streams->len; i++) {
4886     GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4887
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) {
4892
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));
4898
4899         stream->position = cur;
4900
4901         gst_pad_push_event (GST_PAD_CAST (stream),
4902             gst_event_new_gap (stream->position, cur - stream->position));
4903       }
4904     }
4905   }
4906 }
4907
4908 /* random access code
4909  *
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.
4913  */
4914 static void
4915 gst_ogg_demux_loop (GstOggPad * pad)
4916 {
4917   GstOggDemux *ogg;
4918   gboolean res;
4919   GstFlowReturn ret;
4920   GstEvent *seek;
4921
4922   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4923   seek = ogg->seek_event;
4924   ogg->seek_event = NULL;
4925
4926   if (ogg->need_chains) {
4927
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;
4934
4935     ogg->need_chains = FALSE;
4936
4937     GST_OBJECT_LOCK (ogg);
4938     ogg->running = TRUE;
4939     GST_OBJECT_UNLOCK (ogg);
4940
4941     /* and seek to configured positions without FLUSH */
4942     res = gst_ogg_demux_perform_seek_pull (ogg, seek);
4943
4944     if (!res)
4945       goto seek_failed;
4946   } else if (seek) {
4947     res = gst_ogg_demux_perform_seek_pull (ogg, seek);
4948     if (!res)
4949       goto seek_failed;
4950   }
4951
4952   if (ogg->segment.rate >= 0.0)
4953     ret = gst_ogg_demux_loop_forward (ogg);
4954   else
4955     ret = gst_ogg_demux_loop_reverse (ogg);
4956
4957   if (ret != GST_FLOW_OK)
4958     goto pause;
4959
4960   gst_ogg_demux_sync_streams (ogg);
4961   return;
4962
4963   /* ERRORS */
4964 chain_read_failed:
4965   {
4966     /* error was posted */
4967     goto pause;
4968   }
4969 seek_failed:
4970   {
4971     gboolean flushing;
4972
4973     GST_OBJECT_LOCK (pad);
4974     flushing = GST_PAD_IS_FLUSHING (pad);
4975     GST_OBJECT_UNLOCK (pad);
4976     if (flushing) {
4977       ret = GST_FLOW_FLUSHING;
4978     } else {
4979       GST_ELEMENT_FLOW_ERROR (ogg, ret);
4980       ret = GST_FLOW_ERROR;
4981     }
4982     goto pause;
4983   }
4984 pause:
4985   {
4986     const gchar *reason = gst_flow_get_name (ret);
4987     GstEvent *event = NULL;
4988
4989     GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4990     gst_pad_pause_task (ogg->sinkpad);
4991
4992     if (ret == GST_FLOW_EOS) {
4993       /* perform EOS logic */
4994       if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4995         gint64 stop;
4996         GstMessage *message;
4997
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;
5002
5003         GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
5004         message =
5005             gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
5006             stop);
5007         gst_message_set_seqnum (message, ogg->seqnum);
5008
5009         gst_element_post_message (GST_ELEMENT (ogg), message);
5010
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);
5014         event = NULL;
5015       } else {
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 ();
5019       }
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 ();
5023     }
5024
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.
5029      */
5030     if (event) {
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);
5037       } else {
5038         gst_event_unref (event);
5039         GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
5040             ("EOS before finding a chain"));
5041       }
5042     }
5043     return;
5044   }
5045 }
5046
5047 /* The sink pad task function for push mode.
5048  * It just sends any seek events queued by the streaming thread.
5049  */
5050 static gpointer
5051 gst_ogg_demux_loop_push (GstOggDemux * ogg)
5052 {
5053   GstEvent *event = NULL;
5054
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);
5059
5060
5061   while (!ogg->seek_event_thread_stop) {
5062
5063     while (!ogg->seek_event_thread_stop) {
5064       GST_PUSH_LOCK (ogg);
5065       event = ogg->seek_event;
5066       ogg->seek_event = NULL;
5067       if (event)
5068         ogg->seek_event_drop_till = gst_event_get_seqnum (event);
5069       GST_PUSH_UNLOCK (ogg);
5070
5071       if (event)
5072         break;
5073
5074       g_cond_wait (&ogg->seek_event_cond, &ogg->seek_event_mutex);
5075     }
5076
5077     if (ogg->seek_event_thread_stop) {
5078       break;
5079     }
5080     g_assert (event);
5081
5082     g_mutex_unlock (&ogg->seek_event_mutex);
5083
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;
5091       }
5092       GST_PUSH_UNLOCK (ogg);
5093     } else {
5094       GST_DEBUG_OBJECT (ogg->sinkpad, "Pushed event ok");
5095     }
5096
5097     g_mutex_lock (&ogg->seek_event_mutex);
5098   }
5099
5100   g_mutex_unlock (&ogg->seek_event_mutex);
5101
5102   gst_object_unref (ogg);
5103   return NULL;
5104 }
5105
5106 static void
5107 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
5108 {
5109   gint i;
5110
5111   gst_ogg_demux_deactivate_current_chain (ogg);
5112
5113   GST_CHAIN_LOCK (ogg);
5114   for (i = 0; i < ogg->chains->len; i++) {
5115     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
5116
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);
5122   }
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;
5128   }
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;
5133   }
5134   GST_CHAIN_UNLOCK (ogg);
5135 }
5136
5137 /* this function is called when the pad is activated and should start
5138  * processing data.
5139  *
5140  * We check if we can do random access to decide if we work push or
5141  * pull based.
5142  */
5143 static gboolean
5144 gst_ogg_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
5145 {
5146   GstQuery *query;
5147   gboolean pull_mode;
5148
5149   query = gst_query_new_scheduling ();
5150
5151   if (!gst_pad_peer_query (sinkpad, query)) {
5152     gst_query_unref (query);
5153     goto activate_push;
5154   }
5155
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);
5159
5160   if (!pull_mode)
5161     goto activate_push;
5162
5163   GST_DEBUG_OBJECT (sinkpad, "activating pull");
5164   return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
5165
5166 activate_push:
5167   {
5168     GST_DEBUG_OBJECT (sinkpad, "activating push");
5169     return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
5170   }
5171 }
5172
5173 static gboolean
5174 gst_ogg_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
5175     GstPadMode mode, gboolean active)
5176 {
5177   gboolean res;
5178   GstOggDemux *ogg;
5179
5180   ogg = GST_OGG_DEMUX (parent);
5181
5182   switch (mode) {
5183     case GST_PAD_MODE_PUSH:
5184       ogg->pullmode = FALSE;
5185       ogg->resync = FALSE;
5186       if (active) {
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);
5197         }
5198         g_mutex_unlock (&ogg->seek_event_mutex);
5199       } else {
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;
5206       }
5207       res = TRUE;
5208       break;
5209     case GST_PAD_MODE_PULL:
5210       if (active) {
5211         ogg->need_chains = TRUE;
5212         ogg->pullmode = TRUE;
5213
5214         res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
5215             sinkpad, NULL);
5216       } else {
5217         res = gst_pad_stop_task (sinkpad);
5218       }
5219       break;
5220     default:
5221       res = FALSE;
5222       break;
5223   }
5224   return res;
5225 }
5226
5227 static GstStateChangeReturn
5228 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
5229 {
5230   GstOggDemux *ogg;
5231   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
5232
5233   ogg = GST_OGG_DEMUX (element);
5234
5235   switch (transition) {
5236     case GST_STATE_CHANGE_NULL_TO_READY:
5237       ogg->basetime = 0;
5238       ogg_sync_init (&ogg->sync);
5239       break;
5240     case GST_STATE_CHANGE_READY_TO_PAUSED:
5241       ogg_sync_reset (&ogg->sync);
5242       ogg->running = FALSE;
5243       ogg->bitrate = 0;
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;
5254
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);
5259       break;
5260     default:
5261       break;
5262   }
5263
5264   result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5265
5266   switch (transition) {
5267     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
5268       break;
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);
5275       break;
5276     case GST_STATE_CHANGE_READY_TO_NULL:
5277       ogg_sync_clear (&ogg->sync);
5278       break;
5279     default:
5280       break;
5281   }
5282   return result;
5283 }
5284
5285 static gboolean
5286 gst_ogg_demux_plugin_init (GstPlugin * plugin)
5287 {
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");
5291
5292 #ifdef ENABLE_NLS
5293   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
5294       LOCALEDIR);
5295   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
5296   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
5297 #endif
5298
5299   return TRUE;
5300 }
5301
5302 /* prints all info about the element */
5303 #undef GST_CAT_DEFAULT
5304 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
5305
5306 #ifdef GST_DISABLE_GST_DEBUG
5307
5308 static void
5309 gst_ogg_print (GstOggDemux * ogg)
5310 {
5311   /* NOP */
5312 }
5313
5314 #else /* !GST_DISABLE_GST_DEBUG */
5315
5316 static void
5317 gst_ogg_print (GstOggDemux * ogg)
5318 {
5319   guint j, i;
5320
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));
5324
5325   for (i = 0; i < ogg->chains->len; i++) {
5326     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
5327
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,
5331         chain->end_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));
5340
5341     for (j = 0; j < chain->streams->len; j++) {
5342       GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
5343
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));
5348     }
5349   }
5350 }
5351 #endif /* GST_DISABLE_GST_DEBUG */