Suppress deprecation warnings in selected files, for g_static_rec_mutex_* mostly
[platform/upstream/gstreamer.git] / 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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:element-oggdemux
24  * @see_also: <link linkend="gst-plugins-base-plugins-oggmux">oggmux</link>
25  *
26  * This element demuxes ogg files into their encoded audio and video components.
27  *
28  * <refsect2>
29  * <title>Example pipelines</title>
30  * |[
31  * gst-launch -v filesrc location=test.ogg ! oggdemux ! vorbisdec ! audioconvert ! alsasink
32  * ]| Decodes the vorbis audio stored inside an ogg container.
33  * </refsect2>
34  *
35  * Last reviewed on 2006-12-30 (0.10.5)
36  */
37
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
44  * with newer GLib versions (>= 2.31.0) */
45 #define GLIB_DISABLE_DEPRECATION_WARNINGS
46
47 #include <string.h>
48 #include <gst/gst-i18n-plugin.h>
49 #include <gst/tag/tag.h>
50
51 #include "gstoggdemux.h"
52
53 #include "gst/glib-compat-private.h"
54
55 #define CHUNKSIZE (8500)        /* this is out of vorbisfile */
56
57 /* we hope we get a granpos within this many bytes off the end */
58 #define DURATION_CHUNK_OFFSET (64*1024)
59
60 /* stop duration checks within this much of EOS */
61 #define EOS_AVOIDANCE_THRESHOLD 8192
62
63 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
64 #define GST_FLOW_SKIP_PUSH GST_FLOW_CUSTOM_SUCCESS_1
65
66 #define GST_CHAIN_LOCK(ogg)     g_mutex_lock((ogg)->chain_lock)
67 #define GST_CHAIN_UNLOCK(ogg)   g_mutex_unlock((ogg)->chain_lock)
68
69 #define GST_PUSH_LOCK(ogg)                  \
70   do {                                      \
71     GST_TRACE_OBJECT(ogg, "Push lock");     \
72     g_mutex_lock((ogg)->push_lock);         \
73   } while(0)
74
75 #define GST_PUSH_UNLOCK(ogg)                \
76   do {                                      \
77     GST_TRACE_OBJECT(ogg, "Push unlock");   \
78     g_mutex_unlock((ogg)->push_lock);       \
79   } while(0)
80
81 GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
82 GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
83 #define GST_CAT_DEFAULT gst_ogg_demux_debug
84
85
86 static ogg_packet *
87 _ogg_packet_copy (const ogg_packet * packet)
88 {
89   ogg_packet *ret = g_slice_new (ogg_packet);
90
91   *ret = *packet;
92   ret->packet = g_memdup (packet->packet, packet->bytes);
93
94   return ret;
95 }
96
97 static void
98 _ogg_packet_free (ogg_packet * packet)
99 {
100   g_free (packet->packet);
101   g_slice_free (ogg_packet, packet);
102 }
103
104 static ogg_page *
105 gst_ogg_page_copy (ogg_page * page)
106 {
107   ogg_page *p = g_slice_new (ogg_page);
108
109   /* make a copy of the page */
110   p->header = g_memdup (page->header, page->header_len);
111   p->header_len = page->header_len;
112   p->body = g_memdup (page->body, page->body_len);
113   p->body_len = page->body_len;
114
115   return p;
116 }
117
118 static void
119 gst_ogg_page_free (ogg_page * page)
120 {
121   g_free (page->header);
122   g_free (page->body);
123   g_slice_free (ogg_page, page);
124 }
125
126 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
127     GstOggChain * chain);
128 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
129     GstOggChain * chain, GstEvent * event);
130 static void gst_ogg_pad_mark_discont (GstOggPad * pad);
131 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
132
133 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
134     GstEvent * event);
135 static gboolean gst_ogg_demux_receive_event (GstElement * element,
136     GstEvent * event);
137
138 static void gst_ogg_pad_dispose (GObject * object);
139 static void gst_ogg_pad_finalize (GObject * object);
140
141 static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
142 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
143 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
144 static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
145 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
146     guint32 serialno);
147
148 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
149     GstOggPad * pad, GstFlowReturn ret);
150 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
151
152 GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg,
153     GstCaps * caps, GList * headers);
154 static gboolean gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
155 static gboolean gst_ogg_demux_perform_seek_push (GstOggDemux * ogg,
156     GstEvent * event);
157 static gboolean gst_ogg_demux_check_duration_push (GstOggDemux * ogg,
158     GstSeekFlags flags, GstEvent * event);
159
160 GType gst_ogg_pad_get_type (void);
161 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
162
163 static void
164 gst_ogg_pad_class_init (GstOggPadClass * klass)
165 {
166   GObjectClass *gobject_class;
167
168   gobject_class = (GObjectClass *) klass;
169
170   gobject_class->dispose = gst_ogg_pad_dispose;
171   gobject_class->finalize = gst_ogg_pad_finalize;
172 }
173
174 static void
175 gst_ogg_pad_init (GstOggPad * pad)
176 {
177   gst_pad_set_event_function (GST_PAD (pad),
178       GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
179   gst_pad_set_getcaps_function (GST_PAD (pad),
180       GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps));
181   gst_pad_set_query_type_function (GST_PAD (pad),
182       GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types));
183   gst_pad_set_query_function (GST_PAD (pad),
184       GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
185
186   pad->mode = GST_OGG_PAD_MODE_INIT;
187
188   pad->current_granule = -1;
189   pad->keyframe_granule = -1;
190
191   pad->start_time = GST_CLOCK_TIME_NONE;
192
193   pad->last_stop = GST_CLOCK_TIME_NONE;
194
195   pad->have_type = FALSE;
196   pad->continued = NULL;
197   pad->map.headers = NULL;
198   pad->map.queued = NULL;
199
200   pad->map.granulerate_n = 0;
201   pad->map.granulerate_d = 0;
202   pad->map.granuleshift = -1;
203 }
204
205 static void
206 gst_ogg_pad_dispose (GObject * object)
207 {
208   GstOggPad *pad = GST_OGG_PAD (object);
209
210   pad->chain = NULL;
211   pad->ogg = NULL;
212
213   g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
214   g_list_free (pad->map.headers);
215   pad->map.headers = NULL;
216   g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
217   g_list_free (pad->map.queued);
218   pad->map.queued = NULL;
219
220   g_free (pad->map.index);
221   pad->map.index = NULL;
222
223   /* clear continued pages */
224   g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
225   g_list_free (pad->continued);
226   pad->continued = NULL;
227
228   if (pad->map.caps) {
229     gst_caps_unref (pad->map.caps);
230     pad->map.caps = NULL;
231   }
232
233   if (pad->map.taglist) {
234     gst_tag_list_free (pad->map.taglist);
235     pad->map.taglist = NULL;
236   }
237
238   ogg_stream_reset (&pad->map.stream);
239
240   G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
241 }
242
243 static void
244 gst_ogg_pad_finalize (GObject * object)
245 {
246   GstOggPad *pad = GST_OGG_PAD (object);
247
248   ogg_stream_clear (&pad->map.stream);
249
250   G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
251 }
252
253 static const GstQueryType *
254 gst_ogg_pad_query_types (GstPad * pad)
255 {
256   static const GstQueryType query_types[] = {
257     GST_QUERY_DURATION,
258     GST_QUERY_SEEKING,
259     0
260   };
261
262   return query_types;
263 }
264
265 static GstCaps *
266 gst_ogg_pad_getcaps (GstPad * pad)
267 {
268   return gst_caps_ref (GST_PAD_CAPS (pad));
269 }
270
271 static gboolean
272 gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
273 {
274   gboolean res = TRUE;
275   GstOggDemux *ogg;
276
277   ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
278
279   switch (GST_QUERY_TYPE (query)) {
280     case GST_QUERY_DURATION:
281     {
282       GstFormat format;
283       gint64 total_time = -1;
284
285       gst_query_parse_duration (query, &format, NULL);
286       /* can only get position in time */
287       if (format != GST_FORMAT_TIME)
288         goto wrong_format;
289
290       if (ogg->total_time != -1) {
291         /* we can return the total length */
292         total_time = ogg->total_time;
293       } else {
294         gint bitrate = ogg->bitrate;
295
296         /* try with length and bitrate */
297         if (bitrate > 0) {
298           GstQuery *uquery;
299
300           /* ask upstream for total length in bytes */
301           uquery = gst_query_new_duration (GST_FORMAT_BYTES);
302           if (gst_pad_peer_query (ogg->sinkpad, uquery)) {
303             gint64 length;
304
305             gst_query_parse_duration (uquery, NULL, &length);
306
307             /* estimate using the bitrate */
308             total_time =
309                 gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);
310
311             GST_LOG_OBJECT (ogg,
312                 "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
313                 GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
314           }
315           gst_query_unref (uquery);
316         }
317       }
318
319       gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
320       break;
321     }
322     case GST_QUERY_SEEKING:
323     {
324       GstFormat format;
325
326       gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
327       if (format == GST_FORMAT_TIME) {
328         gboolean seekable = FALSE;
329         gint64 stop = -1;
330
331         if (ogg->pullmode) {
332           seekable = TRUE;
333           stop = ogg->total_time;
334         } else if (ogg->current_chain->streams->len) {
335           gint i;
336
337           seekable = FALSE;
338           for (i = 0; i < ogg->current_chain->streams->len; i++) {
339             GstOggPad *pad =
340                 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
341
342             seekable = TRUE;
343             if (pad->map.index != NULL && pad->map.n_index != 0) {
344               GstOggIndex *idx;
345               GstClockTime idx_time;
346
347               idx = &pad->map.index[pad->map.n_index - 1];
348               idx_time =
349                   gst_util_uint64_scale (idx->timestamp, GST_SECOND,
350                   pad->map.kp_denom);
351               if (stop == -1)
352                 stop = idx_time;
353               else
354                 stop = MAX (idx_time, stop);
355             } else {
356               stop = -1;        /* we've no clue, sadly, without seeking */
357             }
358           }
359         }
360
361         gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
362       } else {
363         res = FALSE;
364       }
365       break;
366     }
367
368     default:
369       res = gst_pad_query_default (pad, query);
370       break;
371   }
372 done:
373   gst_object_unref (ogg);
374
375   return res;
376
377   /* ERRORS */
378 wrong_format:
379   {
380     GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
381     res = FALSE;
382     goto done;
383   }
384 }
385
386 static gboolean
387 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
388 {
389   gboolean res;
390   GstOggDemux *ogg;
391
392   ogg = GST_OGG_DEMUX (element);
393
394   switch (GST_EVENT_TYPE (event)) {
395     case GST_EVENT_SEEK:
396       /* now do the seek */
397       res = gst_ogg_demux_perform_seek (ogg, event);
398       gst_event_unref (event);
399       break;
400     default:
401       GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
402       goto error;
403   }
404
405   return res;
406
407   /* ERRORS */
408 error:
409   {
410     GST_DEBUG_OBJECT (ogg, "error handling event");
411     gst_event_unref (event);
412     return FALSE;
413   }
414 }
415
416 static gboolean
417 gst_ogg_pad_event (GstPad * pad, GstEvent * event)
418 {
419   gboolean res;
420   GstOggDemux *ogg;
421
422   ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
423
424   switch (GST_EVENT_TYPE (event)) {
425     case GST_EVENT_SEEK:
426       /* now do the seek */
427       res = gst_ogg_demux_perform_seek (ogg, event);
428       gst_event_unref (event);
429       break;
430     default:
431       res = gst_pad_event_default (pad, event);
432       break;
433   }
434   gst_object_unref (ogg);
435
436   return res;
437 }
438
439 static void
440 gst_ogg_pad_reset (GstOggPad * pad)
441 {
442   ogg_stream_reset (&pad->map.stream);
443
444   GST_DEBUG_OBJECT (pad, "doing reset");
445
446   /* clear continued pages */
447   g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
448   g_list_free (pad->continued);
449   pad->continued = NULL;
450
451   pad->last_ret = GST_FLOW_OK;
452   pad->last_stop = GST_CLOCK_TIME_NONE;
453   pad->current_granule = -1;
454   pad->keyframe_granule = -1;
455   pad->is_eos = FALSE;
456 }
457
458 /* queue data, basically takes the packet, puts it in a buffer and store the
459  * buffer in the queued list.  */
460 static GstFlowReturn
461 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
462 {
463 #ifndef GST_DISABLE_GST_DEBUG
464   GstOggDemux *ogg = pad->ogg;
465 #endif
466
467   GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x",
468       pad, pad->map.serialno);
469
470   pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
471
472   /* we are ok now */
473   return GST_FLOW_OK;
474 }
475
476 static GstFlowReturn
477 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
478     gboolean push_headers)
479 {
480   GstBuffer *buf = NULL;
481   GstFlowReturn ret, cret;
482   GstOggDemux *ogg = pad->ogg;
483   gint64 current_time;
484   GstOggChain *chain;
485   gint64 duration;
486   gint offset;
487   gint trim;
488   GstClockTime out_timestamp, out_duration;
489   guint64 out_offset, out_offset_end;
490   gboolean delta_unit = FALSE;
491
492   cret = GST_FLOW_OK;
493
494   GST_DEBUG_OBJECT (pad, "Chaining %d %d %" GST_TIME_FORMAT " %d %p",
495       ogg->pullmode, ogg->push_state, GST_TIME_ARGS (ogg->push_time_length),
496       ogg->push_disable_seeking, ogg->building_chain);
497   GST_PUSH_LOCK (ogg);
498   if (!ogg->pullmode && ogg->push_state == PUSH_PLAYING
499       && ogg->push_time_length == GST_CLOCK_TIME_NONE
500       && !ogg->push_disable_seeking) {
501     if (!ogg->building_chain) {
502       /* we got all headers, now try to get duration */
503       if (!gst_ogg_demux_check_duration_push (ogg, GST_SEEK_FLAG_FLUSH, NULL)) {
504         GST_PUSH_UNLOCK (ogg);
505         return GST_FLOW_OK;
506       }
507     }
508     GST_PUSH_UNLOCK (ogg);
509     return GST_FLOW_OK;
510   }
511   GST_PUSH_UNLOCK (ogg);
512
513   GST_DEBUG_OBJECT (ogg,
514       "%p streaming to peer serial %08x", pad, pad->map.serialno);
515
516   if (pad->map.is_ogm) {
517     const guint8 *data;
518     long bytes;
519
520     data = packet->packet;
521     bytes = packet->bytes;
522
523     if (bytes < 1)
524       goto empty_packet;
525
526     if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
527       /* We don't push header packets for OGM */
528       goto done;
529     }
530
531     offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
532     delta_unit = (((data[0] & 0x08) >> 3) == 0);
533
534     trim = 0;
535
536     /* Strip trailing \0 for subtitles */
537     if (pad->map.is_ogm_text) {
538       while (bytes && data[bytes - 1] == 0) {
539         trim++;
540         bytes--;
541       }
542     }
543   } else if (pad->map.is_vp8) {
544     if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
545         packet->b_o_s ||
546         (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) {
547       /* We don't push header packets for VP8 */
548       goto done;
549     }
550     offset = 0;
551     trim = 0;
552   } else {
553     offset = 0;
554     trim = 0;
555   }
556
557   /* get timing info for the packet */
558   if (gst_ogg_stream_packet_is_header (&pad->map, packet)) {
559     duration = 0;
560     GST_DEBUG_OBJECT (ogg, "packet is header");
561   } else {
562     duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
563     GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
564   }
565
566   if (packet->b_o_s) {
567     out_timestamp = GST_CLOCK_TIME_NONE;
568     out_duration = GST_CLOCK_TIME_NONE;
569     out_offset = 0;
570     out_offset_end = -1;
571   } else {
572     if (packet->granulepos != -1) {
573       pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
574           packet->granulepos);
575       pad->keyframe_granule =
576           gst_ogg_stream_granulepos_to_key_granule (&pad->map,
577           packet->granulepos);
578       GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
579           pad->current_granule);
580     } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
581       pad->current_granule += duration;
582       GST_DEBUG_OBJECT (ogg, "interpolating granule %" G_GUINT64_FORMAT,
583           pad->current_granule);
584     }
585     if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
586       /* negative rates, only set timestamp on the packets with a granulepos */
587       out_timestamp = -1;
588       out_duration = -1;
589       out_offset = -1;
590       out_offset_end = -1;
591     } else {
592       /* we only push buffers after we have a valid granule. This is done so that
593        * we nicely skip packets without a timestamp after a seek. This is ok
594        * because we base or seek on the packet after the page with the smaller
595        * timestamp. */
596       if (pad->current_granule == -1)
597         goto no_timestamp;
598
599       if (pad->map.is_ogm) {
600         out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
601             pad->current_granule);
602         out_duration = gst_util_uint64_scale (duration,
603             GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
604       } else if (pad->map.is_sparse) {
605         out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
606             pad->current_granule);
607         if (duration == GST_CLOCK_TIME_NONE) {
608           out_duration = GST_CLOCK_TIME_NONE;
609         } else {
610           out_duration = gst_util_uint64_scale (duration,
611               GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
612         }
613       } else {
614         out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
615             pad->current_granule - duration);
616         out_duration =
617             gst_ogg_stream_granule_to_time (&pad->map,
618             pad->current_granule) - out_timestamp;
619       }
620       out_offset_end =
621           gst_ogg_stream_granule_to_granulepos (&pad->map,
622           pad->current_granule, pad->keyframe_granule);
623       out_offset =
624           gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
625     }
626   }
627
628   if (pad->map.is_ogm_text) {
629     /* check for invalid buffer sizes */
630     if (G_UNLIKELY (offset + trim >= packet->bytes))
631       goto empty_packet;
632   }
633
634   if (!pad->added)
635     goto not_added;
636
637   buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
638   gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
639
640   /* set delta flag for OGM content */
641   if (delta_unit)
642     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
643
644   /* copy packet in buffer */
645   memcpy (buf->data, packet->packet + offset, packet->bytes - offset - trim);
646
647   GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
648   GST_BUFFER_DURATION (buf) = out_duration;
649   GST_BUFFER_OFFSET (buf) = out_offset;
650   GST_BUFFER_OFFSET_END (buf) = out_offset_end;
651
652   /* Mark discont on the buffer */
653   if (pad->discont) {
654     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
655     pad->discont = FALSE;
656   }
657
658   pad->last_stop = ogg->segment.last_stop;
659
660   /* don't push the header packets when we are asked to skip them */
661   if (!packet->b_o_s || push_headers) {
662     ret = gst_pad_push (GST_PAD_CAST (pad), buf);
663     buf = NULL;
664
665     /* combine flows */
666     cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
667   }
668
669   /* we're done with skeleton stuff */
670   if (pad->map.is_skeleton)
671     goto done;
672
673   /* check if valid granulepos, then we can calculate the current
674    * position. We know the granule for each packet but we only want to update
675    * the last_stop when we have a valid granulepos on the packet because else
676    * our time jumps around for the different streams. */
677   if (packet->granulepos < 0)
678     goto done;
679
680   /* convert to time */
681   current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
682       packet->granulepos);
683
684   /* convert to stream time */
685   if ((chain = pad->chain)) {
686     gint64 chain_start = 0;
687
688     if (chain->segment_start != GST_CLOCK_TIME_NONE)
689       chain_start = chain->segment_start;
690
691     current_time = current_time - chain_start + chain->begin_time;
692   }
693
694   /* and store as the current position */
695   gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time);
696
697   GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
698       GST_TIME_ARGS (current_time));
699
700   /* check stream eos */
701   if ((ogg->segment.rate > 0.0 && ogg->segment.stop != GST_CLOCK_TIME_NONE &&
702           current_time > ogg->segment.stop) ||
703       (ogg->segment.rate < 0.0 && ogg->segment.start != GST_CLOCK_TIME_NONE &&
704           current_time < ogg->segment.start)) {
705     GST_DEBUG_OBJECT (ogg, "marking pad %p EOS", pad);
706     pad->is_eos = TRUE;
707   }
708
709 done:
710   if (buf)
711     gst_buffer_unref (buf);
712   /* return combined flow result */
713   return cret;
714
715   /* special cases */
716 empty_packet:
717   {
718     GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
719     goto done;
720   }
721
722 no_timestamp:
723   {
724     GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
725     goto done;
726   }
727 not_added:
728   {
729     GST_DEBUG_OBJECT (ogg, "pad not added yet");
730     goto done;
731   }
732 }
733
734 static guint64
735 gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
736 {
737   gint i;
738   guint64 start_time = G_MAXUINT64;
739
740   for (i = 0; i < chain->streams->len; i++) {
741     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
742
743     if (pad->map.is_skeleton)
744       continue;
745
746     /*  can do this if the pad start time is not defined */
747     GST_DEBUG_OBJECT (ogg, "Pad %08x (%s) start time is %" GST_TIME_FORMAT,
748         pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map),
749         GST_TIME_ARGS (pad->start_time));
750     if (pad->start_time == GST_CLOCK_TIME_NONE) {
751       if (!pad->map.is_sparse) {
752         start_time = G_MAXUINT64;
753         break;
754       }
755     } else {
756       start_time = MIN (start_time, pad->start_time);
757     }
758   }
759   return start_time;
760 }
761
762 static GstClockTime
763 gst_ogg_demux_collect_sync_time (GstOggDemux * ogg, GstOggChain * chain)
764 {
765   gint i;
766   GstClockTime sync_time = GST_CLOCK_TIME_NONE;
767
768   if (!chain) {
769     GST_WARNING_OBJECT (ogg, "No chain!");
770     return GST_CLOCK_TIME_NONE;
771   }
772
773   for (i = 0; i < chain->streams->len; i++) {
774     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
775
776     if (pad->map.is_sparse)
777       continue;
778
779     if (pad->push_sync_time == GST_CLOCK_TIME_NONE) {
780       sync_time = GST_CLOCK_TIME_NONE;
781       break;
782     } else {
783       if (sync_time == GST_CLOCK_TIME_NONE)
784         sync_time = pad->push_sync_time;
785       else
786         sync_time = MAX (sync_time, pad->push_sync_time);
787     }
788   }
789   return sync_time;
790 }
791
792 /* submit a packet to the oggpad, this function will run the
793  * typefind code for the pad if this is the first packet for this
794  * stream 
795  */
796 static GstFlowReturn
797 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
798 {
799   gint64 granule;
800   GstFlowReturn ret = GST_FLOW_OK;
801
802   GstOggDemux *ogg = pad->ogg;
803
804   GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x",
805       pad, pad->map.serialno);
806
807   if (!pad->have_type) {
808     pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
809     if (!pad->have_type) {
810       pad->map.caps = gst_caps_new_simple ("application/x-unknown", NULL);
811     }
812     if (pad->map.is_skeleton) {
813       GST_DEBUG_OBJECT (ogg, "we have a fishead");
814       /* copy values over to global ogg level */
815       ogg->basetime = pad->map.basetime;
816       ogg->prestime = pad->map.prestime;
817
818       /* use total time to update the total ogg time */
819       if (ogg->total_time == -1) {
820         ogg->total_time = pad->map.total_time;
821       } else if (pad->map.total_time > 0) {
822         ogg->total_time = MAX (ogg->total_time, pad->map.total_time);
823       }
824     }
825     if (pad->map.caps) {
826       gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
827     } else {
828       GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
829     }
830   }
831
832   if (pad->map.is_skeleton) {
833     guint32 serialno;
834     GstOggPad *skel_pad;
835     GstOggSkeleton type;
836
837     /* try to parse the serialno first */
838     if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
839             &serialno, &type)) {
840
841       GST_DEBUG_OBJECT (pad->ogg,
842           "got skeleton packet for stream 0x%08x", serialno);
843
844       skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
845       if (skel_pad) {
846         switch (type) {
847           case GST_OGG_SKELETON_FISBONE:
848             /* parse the remainder of the fisbone in the pad with the serialno,
849              * note that we ignore the start_time as this is usually wrong for
850              * live streams */
851             gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
852                 packet->bytes, NULL);
853             break;
854           case GST_OGG_SKELETON_INDEX:
855             gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
856                 packet->bytes);
857
858             /* use total time to update the total ogg time */
859             if (ogg->total_time == -1) {
860               ogg->total_time = skel_pad->map.total_time;
861             } else if (skel_pad->map.total_time > 0) {
862               ogg->total_time = MAX (ogg->total_time, skel_pad->map.total_time);
863             }
864             break;
865           default:
866             break;
867         }
868
869       } else {
870         GST_WARNING_OBJECT (pad->ogg,
871             "found skeleton fisbone for an unknown stream 0x%08x", serialno);
872       }
873     }
874   }
875
876   granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
877       packet->granulepos);
878   if (granule != -1) {
879     GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
880     pad->current_granule = granule;
881   }
882
883   /* restart header packet count when seeing a b_o_s page;
884    * particularly useful following a seek or even following chain finding */
885   if (packet->b_o_s) {
886     GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
887     pad->map.n_header_packets_seen = 0;
888     if (!pad->map.have_headers) {
889       GST_DEBUG_OBJECT (ogg, "clearing header packets");
890       g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
891       g_list_free (pad->map.headers);
892       pad->map.headers = NULL;
893     }
894   }
895
896   /* Overload the value of b_o_s in ogg_packet with a flag whether or
897    * not this is a header packet.  Maybe some day this could be cleaned
898    * up.  */
899   packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
900   if (!packet->b_o_s) {
901     GST_DEBUG ("found non-header packet");
902     pad->map.have_headers = TRUE;
903     if (pad->start_time == GST_CLOCK_TIME_NONE) {
904       gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
905       GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
906       if (duration != -1) {
907         pad->map.accumulated_granule += duration;
908         GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
909             pad->map.accumulated_granule);
910       }
911
912       if (packet->granulepos != -1) {
913         ogg_int64_t start_granule;
914         gint64 granule;
915
916         granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
917             packet->granulepos);
918
919         if (granule > pad->map.accumulated_granule)
920           start_granule = granule - pad->map.accumulated_granule;
921         else
922           start_granule = 0;
923
924         pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
925             start_granule);
926         GST_DEBUG_OBJECT (ogg,
927             "start time %" GST_TIME_FORMAT " (%" GST_TIME_FORMAT ") for %s "
928             "from granpos %" G_GINT64_FORMAT " (granule %" G_GINT64_FORMAT ", "
929             "accumulated granule %" G_GINT64_FORMAT,
930             GST_TIME_ARGS (pad->start_time), GST_TIME_ARGS (pad->start_time),
931             gst_ogg_stream_get_media_type (&pad->map),
932             (gint64) packet->granulepos, granule, pad->map.accumulated_granule);
933       } else {
934         packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
935             pad->map.accumulated_granule, pad->keyframe_granule);
936       }
937     }
938   } else {
939     /* look for tags in header packet (before inc header count) */
940     gst_ogg_stream_extract_tags (&pad->map, packet);
941     pad->map.n_header_packets_seen++;
942     if (!pad->map.have_headers) {
943       pad->map.headers =
944           g_list_append (pad->map.headers, _ogg_packet_copy (packet));
945       GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
946     }
947   }
948
949   /* we know the start_time of the pad data, see if we
950    * can activate the complete chain if this is a dynamic
951    * chain. We need all the headers too for this. */
952   if (pad->start_time != GST_CLOCK_TIME_NONE && pad->map.have_headers) {
953     GstOggChain *chain = pad->chain;
954
955     /* check if complete chain has start time */
956     if (chain == ogg->building_chain) {
957       GstEvent *event = NULL;
958
959       if (ogg->resync) {
960         guint64 start_time;
961
962         GST_DEBUG_OBJECT (ogg, "need to resync");
963
964         /* when we need to resync after a seek, we wait until we have received
965          * timestamps on all streams */
966         start_time = gst_ogg_demux_collect_start_time (ogg, chain);
967
968         if (start_time != G_MAXUINT64) {
969           gint64 segment_time;
970
971           GST_DEBUG_OBJECT (ogg, "start_time:  %" GST_TIME_FORMAT,
972               GST_TIME_ARGS (start_time));
973
974           if (chain->segment_start < start_time)
975             segment_time =
976                 (start_time - chain->segment_start) + chain->begin_time;
977           else
978             segment_time = chain->begin_time;
979
980           /* create the newsegment event we are going to send out */
981           GST_PUSH_LOCK (ogg);
982           if (!ogg->pullmode && ogg->push_state == PUSH_LINEAR2) {
983             /* if we are fast forwarding to the actual seek target,
984                ensure previous frames are clipped */
985             GST_DEBUG_OBJECT (ogg,
986                 "Resynced, starting segment at %" GST_TIME_FORMAT
987                 ", start_time %" GST_TIME_FORMAT,
988                 GST_TIME_ARGS (ogg->push_seek_time_original_target),
989                 GST_TIME_ARGS (start_time));
990             event =
991                 gst_event_new_new_segment (FALSE, ogg->push_seek_rate,
992                 GST_FORMAT_TIME, ogg->push_seek_time_original_target, -1,
993                 ogg->push_seek_time_original_target);
994             ogg->push_state = PUSH_PLAYING;
995           } else {
996             event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
997                 GST_FORMAT_TIME, start_time, chain->segment_stop, segment_time);
998           }
999           GST_PUSH_UNLOCK (ogg);
1000
1001           ogg->resync = FALSE;
1002         }
1003       } else {
1004         /* see if we have enough info to activate the chain, we have enough info
1005          * when all streams have a valid start time. */
1006         if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
1007
1008           GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
1009               GST_TIME_ARGS (chain->segment_start));
1010           GST_DEBUG_OBJECT (ogg, "segment_stop:  %" GST_TIME_FORMAT,
1011               GST_TIME_ARGS (chain->segment_stop));
1012           GST_DEBUG_OBJECT (ogg, "segment_time:  %" GST_TIME_FORMAT,
1013               GST_TIME_ARGS (chain->begin_time));
1014
1015           /* create the newsegment event we are going to send out */
1016           event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
1017               GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
1018               chain->begin_time);
1019         }
1020       }
1021
1022       if (event) {
1023         gst_event_set_seqnum (event, ogg->seqnum);
1024
1025         gst_ogg_demux_activate_chain (ogg, chain, event);
1026
1027         ogg->building_chain = NULL;
1028       }
1029     }
1030   }
1031
1032   /* if we are building a chain, store buffer for when we activate
1033    * it. This path is taken if we operate in streaming mode. */
1034   if (ogg->building_chain) {
1035     /* bos packets where stored in the header list so we can discard
1036      * them here*/
1037     if (!packet->b_o_s)
1038       ret = gst_ogg_demux_queue_data (pad, packet);
1039   }
1040   /* else we are completely streaming to the peer */
1041   else {
1042     ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
1043   }
1044   return ret;
1045 }
1046
1047 /* flush at most @npackets from the stream layer. All packets if 
1048  * @npackets is 0;
1049  */
1050 static GstFlowReturn
1051 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1052 {
1053   GstFlowReturn result = GST_FLOW_OK;
1054   gboolean done = FALSE;
1055   GstOggDemux *ogg;
1056
1057   ogg = pad->ogg;
1058
1059   while (!done) {
1060     int ret;
1061     ogg_packet packet;
1062
1063     ret = ogg_stream_packetout (&pad->map.stream, &packet);
1064     switch (ret) {
1065       case 0:
1066         GST_LOG_OBJECT (ogg, "packetout done");
1067         done = TRUE;
1068         break;
1069       case -1:
1070         GST_LOG_OBJECT (ogg, "packetout discont");
1071         if (!pad->map.is_sparse) {
1072           gst_ogg_chain_mark_discont (pad->chain);
1073         } else {
1074           gst_ogg_pad_mark_discont (pad);
1075         }
1076         break;
1077       case 1:
1078         GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1079         if (packet.bytes > ogg->max_packet_size)
1080           ogg->max_packet_size = packet.bytes;
1081         result = gst_ogg_pad_submit_packet (pad, &packet);
1082         /* not linked is not a problem, it's possible that we are still
1083          * collecting headers and that we don't have exposed the pads yet */
1084         if (result == GST_FLOW_NOT_LINKED)
1085           break;
1086         else if (result <= GST_FLOW_UNEXPECTED)
1087           goto could_not_submit;
1088         break;
1089       default:
1090         GST_WARNING_OBJECT (ogg,
1091             "invalid return value %d for ogg_stream_packetout, resetting stream",
1092             ret);
1093         gst_ogg_pad_reset (pad);
1094         break;
1095     }
1096     if (npackets > 0) {
1097       npackets--;
1098       done = (npackets == 0);
1099     }
1100   }
1101   return result;
1102
1103   /* ERRORS */
1104 could_not_submit:
1105   {
1106     GST_WARNING_OBJECT (ogg,
1107         "could not submit packet for stream %08x, "
1108         "error: %d", pad->map.serialno, result);
1109     gst_ogg_pad_reset (pad);
1110     return result;
1111   }
1112 }
1113
1114 static void
1115 gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg)
1116 {
1117   if (ogg->push_last_seek_time >= ogg->push_seek_time_target) {
1118     GST_DEBUG_OBJECT (ogg, "We overshot by %" GST_TIME_FORMAT,
1119         GST_TIME_ARGS (ogg->push_last_seek_time - ogg->push_seek_time_target));
1120     ogg->push_offset1 = ogg->push_last_seek_offset;
1121     ogg->push_time1 = ogg->push_last_seek_time;
1122     ogg->seek_undershot = FALSE;
1123   } else {
1124     GST_DEBUG_OBJECT (ogg, "We undershot by %" GST_TIME_FORMAT,
1125         GST_TIME_ARGS (ogg->push_seek_time_target - ogg->push_last_seek_time));
1126     ogg->push_offset0 = ogg->push_last_seek_offset;
1127     ogg->push_time0 = ogg->push_last_seek_time;
1128     ogg->seek_undershot = TRUE;
1129   }
1130 }
1131
1132 static gint64
1133 gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg, float seek_quality)
1134 {
1135   gint64 best;
1136   gint64 segment_bitrate;
1137   gint64 skew;
1138
1139   /* we might not know the length of the stream in time,
1140      so push_time1 might not be set */
1141   GST_DEBUG_OBJECT (ogg,
1142       "push time 1: %" GST_TIME_FORMAT ", dbytes %" G_GINT64_FORMAT,
1143       GST_TIME_ARGS (ogg->push_time1), ogg->push_offset1 - ogg->push_offset0);
1144   if (ogg->push_time1 == GST_CLOCK_TIME_NONE) {
1145     GST_DEBUG_OBJECT (ogg,
1146         "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1147         ", time %" GST_TIME_FORMAT " (open ended)", ogg->push_offset0,
1148         ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0));
1149     if (ogg->push_last_seek_time == ogg->push_start_time) {
1150       /* if we're at start and don't know the end time, we can't estimate
1151          bitrate, so get the nominal declared bitrate as a failsafe, or some
1152          random constant which will be discarded after we made a (probably
1153          dire) first guess */
1154       segment_bitrate = (ogg->bitrate > 0 ? ogg->bitrate : 1000);
1155     } else {
1156       segment_bitrate =
1157           gst_util_uint64_scale (ogg->push_last_seek_offset - 0,
1158           8 * GST_SECOND, ogg->push_last_seek_time - ogg->push_start_time);
1159     }
1160     best =
1161         ogg->push_offset0 +
1162         gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1163         segment_bitrate, 8 * GST_SECOND);
1164     ogg->seek_secant = TRUE;
1165   } else {
1166     GST_DEBUG_OBJECT (ogg,
1167         "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1168         ", time %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, ogg->push_offset0,
1169         ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
1170         GST_TIME_ARGS (ogg->push_time1));
1171     if (ogg->push_time0 == ogg->push_time1) {
1172       best = ogg->push_offset0;
1173     } else {
1174       segment_bitrate =
1175           gst_util_uint64_scale (ogg->push_offset1 - ogg->push_offset0,
1176           8 * GST_SECOND, ogg->push_time1 - ogg->push_time0);
1177       GST_DEBUG_OBJECT (ogg,
1178           "Local bitrate on the %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1179           " segment: %" G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_time0),
1180           GST_TIME_ARGS (ogg->push_time1), segment_bitrate);
1181
1182       best =
1183           ogg->push_offset0 +
1184           gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1185           segment_bitrate, 8 * GST_SECOND);
1186       if (seek_quality < 0.5f && ogg->seek_secant) {
1187         gint64 new_best, best2 = (ogg->push_offset0 + ogg->push_offset1) / 2;
1188         /* if dire result, give as much as 25% weight to a dumb bisection guess */
1189         float secant_weight = 1.0f - ((0.5 - seek_quality) / 0.5f) * 0.25;
1190         new_best = (best * secant_weight + best2 * (1.0f - secant_weight));
1191         GST_DEBUG_OBJECT (ogg,
1192             "Secant says %" G_GINT64_FORMAT ", straight is %" G_GINT64_FORMAT
1193             ", new best %" G_GINT64_FORMAT " with secant_weight %f", best,
1194             best2, new_best, secant_weight);
1195         best = new_best;
1196         ogg->seek_secant = FALSE;
1197       } else {
1198         ogg->seek_secant = TRUE;
1199       }
1200     }
1201   }
1202
1203   GST_DEBUG_OBJECT (ogg, "Raw best guess: %" G_GINT64_FORMAT, best);
1204
1205   /* offset the guess down as we need to capture the start of the
1206      page we are targetting - but only do so if we did not undershoot
1207      last time, as we're likely to still do this time */
1208   if (!ogg->seek_undershot) {
1209     /* very small packets are packed on pages, so offset by at least
1210        a value which is likely to get us at least one page where the
1211        packet starts */
1212     skew =
1213         ogg->max_packet_size >
1214         ogg->max_page_size ? ogg->max_packet_size : ogg->max_page_size;
1215     GST_DEBUG_OBJECT (ogg, "Offsetting by %" G_GINT64_FORMAT, skew);
1216     best -= skew;
1217   }
1218
1219   /* do not seek too close to the bounds, as we stop seeking
1220      when we get to within max_packet_size before the target */
1221   if (best > ogg->push_offset1 - ogg->max_packet_size) {
1222     best = ogg->push_offset1 - ogg->max_packet_size;
1223     GST_DEBUG_OBJECT (ogg,
1224         "Too close to high bound, pushing back to %" G_GINT64_FORMAT, best);
1225   } else if (best < ogg->push_offset0 + ogg->max_packet_size) {
1226     best = ogg->push_offset0 + ogg->max_packet_size;
1227     GST_DEBUG_OBJECT (ogg,
1228         "Too close to low bound, pushing forth to %" G_GINT64_FORMAT, best);
1229   }
1230
1231   /* keep within bounds */
1232   if (best > ogg->push_offset1)
1233     best = ogg->push_offset1;
1234   if (best < ogg->push_offset0)
1235     best = ogg->push_offset0;
1236
1237   GST_DEBUG_OBJECT (ogg, "Choosing target %" G_GINT64_FORMAT, best);
1238   return best;
1239 }
1240
1241 static void
1242 gst_ogg_demux_record_keyframe_time (GstOggDemux * ogg, GstOggPad * pad,
1243     ogg_int64_t granpos)
1244 {
1245   gint64 kf_granule;
1246   GstClockTime kf_time;
1247
1248   kf_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, granpos);
1249   kf_time = gst_ogg_stream_granule_to_time (&pad->map, kf_granule);
1250
1251   pad->push_kf_time = kf_time;
1252 }
1253
1254 /* returns the earliest keyframe time for all non sparse pads in the chain,
1255  * if known, and GST_CLOCK_TIME_NONE if not */
1256 static GstClockTime
1257 gst_ogg_demux_get_earliest_keyframe_time (GstOggDemux * ogg)
1258 {
1259   GstClockTime t = GST_CLOCK_TIME_NONE;
1260   GstOggChain *chain = ogg->building_chain;
1261   int i;
1262
1263   if (!chain) {
1264     GST_WARNING_OBJECT (ogg, "No chain!");
1265     return GST_CLOCK_TIME_NONE;
1266   }
1267   for (i = 0; i < chain->streams->len; i++) {
1268     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1269
1270     if (pad->map.is_sparse)
1271       continue;
1272     if (pad->push_kf_time == GST_CLOCK_TIME_NONE)
1273       return GST_CLOCK_TIME_NONE;
1274     if (t == GST_CLOCK_TIME_NONE || pad->push_kf_time < t)
1275       t = pad->push_kf_time;
1276   }
1277
1278   return t;
1279 }
1280
1281 /* MUST be called with the push lock locked, and will unlock it
1282    regardless of return value. */
1283 static GstFlowReturn
1284 gst_ogg_demux_seek_back_after_push_duration_check_unlock (GstOggDemux * ogg)
1285 {
1286   GstEvent *event;
1287
1288   /* Get the delayed event, if any */
1289   event = ogg->push_mode_seek_delayed_event;
1290   ogg->push_mode_seek_delayed_event = NULL;
1291
1292   ogg->push_state = PUSH_PLAYING;
1293
1294   GST_PUSH_UNLOCK (ogg);
1295
1296   if (event) {
1297     /* If there is one, perform it */
1298     gst_ogg_demux_perform_seek_push (ogg, event);
1299   } else {
1300     /* If there wasn't, seek back at start to start normal playback */
1301     GST_INFO_OBJECT (ogg, "Seeking back to 0 after duration check");
1302     event = gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1303         GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
1304         GST_SEEK_TYPE_SET, 1, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
1305     if (!gst_pad_push_event (ogg->sinkpad, event)) {
1306       GST_WARNING_OBJECT (ogg, "Failed seeking back to start");
1307       return GST_FLOW_ERROR;
1308     }
1309   }
1310
1311   return GST_FLOW_OK;
1312 }
1313
1314 static float
1315 gst_ogg_demux_estimate_seek_quality (GstOggDemux * ogg)
1316 {
1317   gint64 diff;                  /* how far from the goal we ended up */
1318   gint64 dist;                  /* how far we moved this iteration */
1319   float seek_quality;
1320
1321   if (ogg->push_prev_seek_time == GST_CLOCK_TIME_NONE) {
1322     /* for the first seek, we pretend we got a good seek,
1323        as we don't have a previous seek yet */
1324     return 1.0f;
1325   }
1326
1327   /* We take a guess at how good the last seek was at guessing
1328      the byte target by comparing the amplitude of the last
1329      seek to the error */
1330   diff = ogg->push_seek_time_target - ogg->push_last_seek_time;
1331   if (diff < 0)
1332     diff = -diff;
1333   dist = ogg->push_last_seek_time - ogg->push_prev_seek_time;
1334   if (dist < 0)
1335     dist = -dist;
1336
1337   seek_quality = (dist == 0) ? 0.0f : 1.0f / (1.0f + diff / (float) dist);
1338
1339   GST_DEBUG_OBJECT (ogg,
1340       "We moved %" GST_TIME_FORMAT ", we're off by %" GST_TIME_FORMAT
1341       ", seek quality %f", GST_TIME_ARGS (dist), GST_TIME_ARGS (diff),
1342       seek_quality);
1343   return seek_quality;
1344 }
1345
1346 static void
1347 gst_ogg_demux_update_bisection_stats (GstOggDemux * ogg)
1348 {
1349   int n;
1350
1351   GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps",
1352       ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]);
1353
1354   for (n = 0; n < 2; ++n) {
1355     ogg->stats_bisection_steps[n] += ogg->push_bisection_steps[n];
1356     if (ogg->stats_bisection_max_steps[n] < ogg->push_bisection_steps[n])
1357       ogg->stats_bisection_max_steps[n] = ogg->push_bisection_steps[n];
1358   }
1359   ogg->stats_nbisections++;
1360
1361   GST_INFO_OBJECT (ogg,
1362       "So far, %.2f + %.2f bisections needed per seek (max %d + %d)",
1363       ogg->stats_bisection_steps[0] / (float) ogg->stats_nbisections,
1364       ogg->stats_bisection_steps[1] / (float) ogg->stats_nbisections,
1365       ogg->stats_bisection_max_steps[0], ogg->stats_bisection_max_steps[1]);
1366 }
1367
1368 static gboolean
1369 gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
1370 {
1371   GstOggDemux *ogg = pad->ogg;
1372   ogg_int64_t granpos = ogg_page_granulepos (page);
1373
1374   GST_PUSH_LOCK (ogg);
1375   if (granpos >= 0) {
1376     if (ogg->push_start_time == GST_CLOCK_TIME_NONE) {
1377       ogg->push_start_time =
1378           gst_ogg_stream_get_start_time_for_granulepos (&pad->map, granpos);
1379       GST_DEBUG_OBJECT (ogg, "Stream start time: %" GST_TIME_FORMAT,
1380           GST_TIME_ARGS (ogg->push_start_time));
1381     }
1382     ogg->push_time_offset =
1383         gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1384     if (ogg->push_time_offset > 0) {
1385       GST_DEBUG_OBJECT (ogg, "Bitrate since start: %" G_GUINT64_FORMAT,
1386           gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
1387               ogg->push_time_offset));
1388     }
1389
1390     if (ogg->push_state == PUSH_DURATION) {
1391       GstClockTime t =
1392           gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1393
1394       if (ogg->total_time == GST_CLOCK_TIME_NONE || t > ogg->total_time) {
1395         GST_DEBUG_OBJECT (ogg, "New total time: %" GST_TIME_FORMAT,
1396             GST_TIME_ARGS (t));
1397         ogg->total_time = t;
1398         ogg->push_time_length = t;
1399       }
1400
1401       /* If we were determining the duration of the stream, we're now done,
1402          and can get back to sending the original event we delayed.
1403          We stop a bit before the end of the stream, as if we get a EOS
1404          event and there is a queue2 upstream (such as when using playbin2),
1405          it will pause the task *after* we come back from the EOS handler,
1406          so we cannot prevent the pausing by issuing a seek. */
1407       if (ogg->push_byte_offset + EOS_AVOIDANCE_THRESHOLD >=
1408           ogg->push_byte_length) {
1409         GstMessage *message;
1410         GstFlowReturn res;
1411
1412         /* tell the pipeline we've just found out the duration */
1413         ogg->push_time_length = ogg->total_time;
1414         GST_INFO_OBJECT (ogg, "New duration found: %" GST_TIME_FORMAT,
1415             GST_TIME_ARGS (ogg->total_time));
1416         message =
1417             gst_message_new_duration (GST_OBJECT (ogg), GST_FORMAT_TIME,
1418             ogg->total_time);
1419         gst_element_post_message (GST_ELEMENT (ogg), message);
1420
1421         GST_DEBUG_OBJECT (ogg,
1422             "We're close enough to the end, and we're scared "
1423             "to get too close, seeking back to start");
1424
1425         res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1426         if (res != GST_FLOW_OK)
1427           return res;
1428         return GST_FLOW_SKIP_PUSH;
1429       } else {
1430         GST_PUSH_UNLOCK (ogg);
1431       }
1432       return GST_FLOW_SKIP_PUSH;
1433     }
1434   }
1435
1436   /* if we're seeking, look at time, and decide what to do */
1437   if (ogg->push_state != PUSH_PLAYING && ogg->push_state != PUSH_LINEAR2) {
1438     GstClockTime t;
1439     gint64 best = -1;
1440     GstEvent *sevent;
1441     int res;
1442     gboolean close_enough;
1443     float seek_quality;
1444
1445     /* ignore -1 granpos when seeking, we want to sync on a real granpos */
1446     if (granpos < 0) {
1447       GST_PUSH_UNLOCK (ogg);
1448       if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1449         goto choked;
1450       return GST_FLOW_SKIP_PUSH;
1451     }
1452
1453     t = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1454
1455     if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1456       GstClockTime sync_time;
1457
1458       if (pad->push_sync_time == GST_CLOCK_TIME_NONE)
1459         pad->push_sync_time = t;
1460       GST_DEBUG_OBJECT (ogg, "Got timestamp %" GST_TIME_FORMAT " for %s",
1461           GST_TIME_ARGS (t), gst_ogg_stream_get_media_type (&pad->map));
1462       sync_time = gst_ogg_demux_collect_sync_time (ogg, ogg->building_chain);
1463       if (sync_time == GST_CLOCK_TIME_NONE) {
1464         GST_PUSH_UNLOCK (ogg);
1465         GST_DEBUG_OBJECT (ogg,
1466             "Not enough timing info collected for sync, waiting for more");
1467         if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1468           goto choked;
1469         return GST_FLOW_SKIP_PUSH;
1470       }
1471       ogg->push_last_seek_time = sync_time;
1472
1473       GST_DEBUG_OBJECT (ogg,
1474           "Bisection just seeked at %" G_GINT64_FORMAT ", time %"
1475           GST_TIME_FORMAT ", target was %" GST_TIME_FORMAT,
1476           ogg->push_last_seek_offset,
1477           GST_TIME_ARGS (ogg->push_last_seek_time),
1478           GST_TIME_ARGS (ogg->push_seek_time_target));
1479
1480       if (ogg->push_time1 != GST_CLOCK_TIME_NONE) {
1481         seek_quality = gst_ogg_demux_estimate_seek_quality (ogg);
1482         GST_DEBUG_OBJECT (ogg,
1483             "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1484             G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1485             " (%" GST_TIME_FORMAT "), seek quality %f", ogg->push_offset0,
1486             ogg->push_offset1, ogg->push_offset1 - ogg->push_offset0,
1487             GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1),
1488             GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0), seek_quality);
1489       } else {
1490         /* in a open ended seek, we can't do bisection, so we pretend
1491            we like our result so far */
1492         seek_quality = 1.0f;
1493         GST_DEBUG_OBJECT (ogg,
1494             "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1495             G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - unknown",
1496             ogg->push_offset0, ogg->push_offset1,
1497             ogg->push_offset1 - ogg->push_offset0,
1498             GST_TIME_ARGS (ogg->push_time0));
1499       }
1500       ogg->push_prev_seek_time = ogg->push_last_seek_time;
1501
1502       gst_ogg_demux_setup_bisection_bounds (ogg);
1503
1504       best = gst_ogg_demux_estimate_bisection_target (ogg, seek_quality);
1505
1506       if (ogg->push_seek_time_target == 0) {
1507         GST_DEBUG_OBJECT (ogg, "Seeking to 0, deemed close enough");
1508         close_enough = (ogg->push_last_seek_time == 0);
1509       } else {
1510         /* TODO: make this dependent on framerate ? */
1511         GstClockTime time_threshold = GST_SECOND / 2;
1512         guint64 byte_threshold =
1513             (ogg->max_packet_size >
1514             64 * 1024 ? ogg->max_packet_size : 64 * 1024);
1515
1516         /* We want to be within half a second before the target,
1517            or before the target and half less or equal to the max
1518            packet size left to search in */
1519         if (time_threshold > ogg->push_seek_time_target)
1520           time_threshold = ogg->push_seek_time_target;
1521         close_enough = ogg->push_last_seek_time < ogg->push_seek_time_target
1522             && (ogg->push_last_seek_time >=
1523             ogg->push_seek_time_target - time_threshold
1524             || ogg->push_offset1 <= ogg->push_offset0 + byte_threshold);
1525         GST_DEBUG_OBJECT (ogg,
1526             "testing if we're close enough: %" GST_TIME_FORMAT " <= %"
1527             GST_TIME_FORMAT " < %" GST_TIME_FORMAT ", or %" G_GUINT64_FORMAT
1528             " <= %" G_GUINT64_FORMAT " ? %s",
1529             GST_TIME_ARGS (ogg->push_seek_time_target - time_threshold),
1530             GST_TIME_ARGS (ogg->push_last_seek_time),
1531             GST_TIME_ARGS (ogg->push_seek_time_target),
1532             ogg->push_offset1 - ogg->push_offset0, byte_threshold,
1533             close_enough ? "Yes" : "No");
1534       }
1535
1536       if (close_enough || best == ogg->push_last_seek_offset) {
1537         if (ogg->push_state == PUSH_BISECT1) {
1538           /* we now know the time segment we'll have to search for
1539              the second bisection */
1540           ogg->push_time0 = ogg->push_start_time;
1541           ogg->push_offset0 = 0;
1542
1543           GST_DEBUG_OBJECT (ogg,
1544               "Seek to %" GST_TIME_FORMAT
1545               " (%lx) done, now gathering pages for all non-sparse streams",
1546               GST_TIME_ARGS (ogg->push_seek_time_target), (long) granpos);
1547           ogg->push_state = PUSH_LINEAR1;
1548         } else {
1549           /* If we're asked for an accurate seek, we'll go forward till
1550              we get to the original seek target time, else we'll just drop
1551              here at the keyframe */
1552           if (ogg->push_seek_flags & GST_SEEK_FLAG_ACCURATE) {
1553             GST_INFO_OBJECT (ogg,
1554                 "Seek to keyframe at %" GST_TIME_FORMAT " done (we're at %"
1555                 GST_TIME_FORMAT "), skipping to original target (%"
1556                 GST_TIME_FORMAT ")",
1557                 GST_TIME_ARGS (ogg->push_seek_time_target),
1558                 GST_TIME_ARGS (sync_time),
1559                 GST_TIME_ARGS (ogg->push_seek_time_original_target));
1560             ogg->push_state = PUSH_LINEAR2;
1561           } else {
1562             GST_INFO_OBJECT (ogg, "Seek to keyframe done, playing");
1563
1564             /* we're synced to the seek target, so flush stream and stuff
1565                any queued pages into the stream so we start decoding there */
1566             ogg->push_state = PUSH_PLAYING;
1567           }
1568           gst_ogg_demux_update_bisection_stats (ogg);
1569         }
1570       }
1571     } else if (ogg->push_state == PUSH_LINEAR1) {
1572       if (pad->push_kf_time == GST_CLOCK_TIME_NONE) {
1573         GstClockTime earliest_keyframe_time;
1574
1575         gst_ogg_demux_record_keyframe_time (ogg, pad, granpos);
1576         GST_DEBUG_OBJECT (ogg,
1577             "Previous keyframe for %s stream at %" GST_TIME_FORMAT,
1578             gst_ogg_stream_get_media_type (&pad->map),
1579             GST_TIME_ARGS (pad->push_kf_time));
1580         earliest_keyframe_time = gst_ogg_demux_get_earliest_keyframe_time (ogg);
1581         if (earliest_keyframe_time != GST_CLOCK_TIME_NONE) {
1582           if (earliest_keyframe_time > ogg->push_last_seek_time) {
1583             GST_INFO_OBJECT (ogg,
1584                 "All non sparse streams now have a previous keyframe time, "
1585                 "and we already decoded it, switching to playing");
1586             ogg->push_state = PUSH_PLAYING;
1587             gst_ogg_demux_update_bisection_stats (ogg);
1588           } else {
1589             GST_INFO_OBJECT (ogg,
1590                 "All non sparse streams now have a previous keyframe time, "
1591                 "bisecting again to %" GST_TIME_FORMAT,
1592                 GST_TIME_ARGS (earliest_keyframe_time));
1593
1594             ogg->push_seek_time_target = earliest_keyframe_time;
1595             ogg->push_offset0 = 0;
1596             ogg->push_time0 = ogg->push_start_time;
1597             ogg->push_offset1 = ogg->push_last_seek_offset;
1598             ogg->push_time1 = ogg->push_last_seek_time;
1599             ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
1600             ogg->seek_secant = FALSE;
1601             ogg->seek_undershot = FALSE;
1602
1603             ogg->push_state = PUSH_BISECT2;
1604             best = gst_ogg_demux_estimate_bisection_target (ogg, 1.0f);
1605           }
1606         }
1607       }
1608     }
1609
1610     if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1611       gint i;
1612
1613       ogg_sync_reset (&ogg->sync);
1614       for (i = 0; i < ogg->building_chain->streams->len; i++) {
1615         GstOggPad *pad =
1616             g_array_index (ogg->building_chain->streams, GstOggPad *, i);
1617
1618         pad->push_sync_time = GST_CLOCK_TIME_NONE;
1619         ogg_stream_reset (&pad->map.stream);
1620       }
1621
1622       GST_DEBUG_OBJECT (ogg,
1623           "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, best,
1624           (gint64) - 1);
1625       /* do seek */
1626       g_assert (best != -1);
1627       ogg->push_bisection_steps[ogg->push_state == PUSH_BISECT2 ? 1 : 0]++;
1628       sevent =
1629           gst_event_new_seek (ogg->push_seek_rate, GST_FORMAT_BYTES,
1630           ogg->push_seek_flags, GST_SEEK_TYPE_SET, best,
1631           GST_SEEK_TYPE_NONE, -1);
1632
1633       GST_PUSH_UNLOCK (ogg);
1634       res = gst_pad_push_event (ogg->sinkpad, sevent);
1635       if (!res) {
1636         /* We failed to send the seek event, notify the pipeline */
1637         GST_ELEMENT_ERROR (ogg, RESOURCE, SEEK, (NULL), ("Failed to seek"));
1638         return GST_FLOW_ERROR;
1639       }
1640       return GST_FLOW_SKIP_PUSH;
1641     }
1642
1643     if (ogg->push_state != PUSH_PLAYING) {
1644       GST_PUSH_UNLOCK (ogg);
1645       return GST_FLOW_SKIP_PUSH;
1646     }
1647   }
1648   GST_PUSH_UNLOCK (ogg);
1649
1650   return GST_FLOW_OK;
1651
1652 choked:
1653   {
1654     GST_WARNING_OBJECT (ogg,
1655         "ogg stream choked on page (serial %08x), "
1656         "resetting stream", pad->map.serialno);
1657     gst_ogg_pad_reset (pad);
1658     /* we continue to recover */
1659     return GST_FLOW_SKIP_PUSH;
1660   }
1661 }
1662
1663 /* submit a page to an oggpad, this function will then submit all
1664  * the packets in the page.
1665  */
1666 static GstFlowReturn
1667 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
1668 {
1669   GstFlowReturn result = GST_FLOW_OK;
1670   GstOggDemux *ogg;
1671   gboolean continued = FALSE;
1672
1673   ogg = pad->ogg;
1674
1675   /* for negative rates we read pages backwards and must therefore be careful
1676    * with continued pages */
1677   if (ogg->segment.rate < 0.0) {
1678     gint npackets;
1679
1680     continued = ogg_page_continued (page);
1681
1682     /* number of completed packets in the page */
1683     npackets = ogg_page_packets (page);
1684     if (!continued) {
1685       /* page is not continued so it contains at least one packet start. It's
1686        * possible that no packet ends on this page (npackets == 0). In that
1687        * case, the next (continued) page(s) we kept contain the remainder of the
1688        * packets. We mark npackets=1 to make us start decoding the pages in the
1689        * remainder of the algorithm. */
1690       if (npackets == 0)
1691         npackets = 1;
1692     }
1693     GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
1694
1695     if (npackets == 0) {
1696       GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
1697       goto done;
1698     }
1699   }
1700
1701   /* keep track of time in push mode */
1702   if (!ogg->pullmode) {
1703     result = gst_ogg_pad_handle_push_mode_state (pad, page);
1704     if (result == GST_FLOW_SKIP_PUSH)
1705       return GST_FLOW_OK;
1706     if (result != GST_FLOW_OK)
1707       return result;
1708   }
1709
1710   if (page->header_len + page->body_len > ogg->max_page_size)
1711     ogg->max_page_size = page->header_len + page->body_len;
1712
1713   if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1714     goto choked;
1715
1716   /* flush all packets in the stream layer, this might not give a packet if
1717    * the page had no packets finishing on the page (npackets == 0). */
1718   result = gst_ogg_pad_stream_out (pad, 0);
1719
1720   if (pad->continued) {
1721     ogg_packet packet;
1722
1723     /* now send the continued pages to the stream layer */
1724     while (pad->continued) {
1725       ogg_page *p = (ogg_page *) pad->continued->data;
1726
1727       GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
1728       if (ogg_stream_pagein (&pad->map.stream, p) != 0)
1729         goto choked;
1730
1731       pad->continued = g_list_delete_link (pad->continued, pad->continued);
1732
1733       /* free the page */
1734       gst_ogg_page_free (p);
1735     }
1736
1737     GST_LOG_OBJECT (ogg, "flushing last continued packet");
1738     /* flush 1 continued packet in the stream layer */
1739     result = gst_ogg_pad_stream_out (pad, 1);
1740
1741     /* flush all remaining packets, we pushed them in the previous round.
1742      * We don't use _reset() because we still want to get the discont when
1743      * we submit a next page. */
1744     while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
1745   }
1746
1747 done:
1748   /* keep continued pages (only in reverse mode) */
1749   if (continued) {
1750     ogg_page *p = gst_ogg_page_copy (page);
1751
1752     GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
1753     pad->continued = g_list_prepend (pad->continued, p);
1754   }
1755
1756   return result;
1757
1758 choked:
1759   {
1760     GST_WARNING_OBJECT (ogg,
1761         "ogg stream choked on page (serial %08x), "
1762         "resetting stream", pad->map.serialno);
1763     gst_ogg_pad_reset (pad);
1764     /* we continue to recover */
1765     return GST_FLOW_OK;
1766   }
1767 }
1768
1769
1770 static GstOggChain *
1771 gst_ogg_chain_new (GstOggDemux * ogg)
1772 {
1773   GstOggChain *chain = g_slice_new0 (GstOggChain);
1774
1775   GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
1776   chain->ogg = ogg;
1777   chain->offset = -1;
1778   chain->bytes = -1;
1779   chain->have_bos = FALSE;
1780   chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
1781   chain->begin_time = GST_CLOCK_TIME_NONE;
1782   chain->segment_start = GST_CLOCK_TIME_NONE;
1783   chain->segment_stop = GST_CLOCK_TIME_NONE;
1784   chain->total_time = GST_CLOCK_TIME_NONE;
1785
1786   return chain;
1787 }
1788
1789 static void
1790 gst_ogg_chain_free (GstOggChain * chain)
1791 {
1792   gint i;
1793
1794   for (i = 0; i < chain->streams->len; i++) {
1795     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1796
1797     gst_object_unref (pad);
1798   }
1799   g_array_free (chain->streams, TRUE);
1800   g_slice_free (GstOggChain, chain);
1801 }
1802
1803 static void
1804 gst_ogg_pad_mark_discont (GstOggPad * pad)
1805 {
1806   pad->discont = TRUE;
1807   pad->map.last_size = 0;
1808 }
1809
1810 static void
1811 gst_ogg_chain_mark_discont (GstOggChain * chain)
1812 {
1813   gint i;
1814
1815   for (i = 0; i < chain->streams->len; i++) {
1816     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1817
1818     gst_ogg_pad_mark_discont (pad);
1819   }
1820 }
1821
1822 static void
1823 gst_ogg_chain_reset (GstOggChain * chain)
1824 {
1825   gint i;
1826
1827   for (i = 0; i < chain->streams->len; i++) {
1828     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1829
1830     gst_ogg_pad_reset (pad);
1831   }
1832 }
1833
1834 static GstOggPad *
1835 gst_ogg_chain_new_stream (GstOggChain * chain, guint32 serialno)
1836 {
1837   GstOggPad *ret;
1838   GstTagList *list;
1839   gchar *name;
1840
1841   GST_DEBUG_OBJECT (chain->ogg,
1842       "creating new stream %08x in chain %p", serialno, chain);
1843
1844   name = g_strdup_printf ("serial_%08x", serialno);
1845   ret = g_object_new (GST_TYPE_OGG_PAD, "name", name, NULL);
1846   g_free (name);
1847   /* we own this one */
1848   gst_object_ref (ret);
1849   gst_object_sink (ret);
1850
1851   GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1852   gst_ogg_pad_mark_discont (ret);
1853
1854   ret->chain = chain;
1855   ret->ogg = chain->ogg;
1856
1857   ret->map.serialno = serialno;
1858   if (ogg_stream_init (&ret->map.stream, serialno) != 0)
1859     goto init_failed;
1860
1861   /* FIXME: either do something with it or remove it */
1862   list = gst_tag_list_new ();
1863   gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1864       NULL);
1865   gst_tag_list_free (list);
1866
1867   GST_DEBUG_OBJECT (chain->ogg,
1868       "created new ogg src %p for stream with serial %08x", ret, serialno);
1869
1870   g_array_append_val (chain->streams, ret);
1871
1872   return ret;
1873
1874   /* ERRORS */
1875 init_failed:
1876   {
1877     GST_ERROR ("Could not initialize ogg_stream struct for serial %08x",
1878         serialno);
1879     gst_object_unref (ret);
1880     return NULL;
1881   }
1882 }
1883
1884 static GstOggPad *
1885 gst_ogg_chain_get_stream (GstOggChain * chain, guint32 serialno)
1886 {
1887   gint i;
1888
1889   for (i = 0; i < chain->streams->len; i++) {
1890     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1891
1892     if (pad->map.serialno == serialno)
1893       return pad;
1894   }
1895   return NULL;
1896 }
1897
1898 static gboolean
1899 gst_ogg_chain_has_stream (GstOggChain * chain, guint32 serialno)
1900 {
1901   return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1902 }
1903
1904 /* signals and args */
1905 enum
1906 {
1907   /* FILL ME */
1908   LAST_SIGNAL
1909 };
1910
1911 enum
1912 {
1913   ARG_0
1914       /* FILL ME */
1915 };
1916
1917 static GstStaticPadTemplate ogg_demux_src_template_factory =
1918 GST_STATIC_PAD_TEMPLATE ("src_%d",
1919     GST_PAD_SRC,
1920     GST_PAD_SOMETIMES,
1921     GST_STATIC_CAPS_ANY);
1922
1923 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1924     GST_STATIC_PAD_TEMPLATE ("sink",
1925     GST_PAD_SINK,
1926     GST_PAD_ALWAYS,
1927     GST_STATIC_CAPS ("application/ogg; application/x-annodex")
1928     );
1929
1930 static void gst_ogg_demux_finalize (GObject * object);
1931
1932 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1933     GstOggChain ** chain);
1934 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1935     GstOggChain * chain);
1936
1937 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
1938 static void gst_ogg_demux_loop (GstOggPad * pad);
1939 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
1940 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
1941 static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
1942     gboolean active);
1943 static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
1944     gboolean active);
1945 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1946     GstStateChange transition);
1947
1948 static void gst_ogg_print (GstOggDemux * demux);
1949
1950 GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT);
1951
1952 static void
1953 gst_ogg_demux_base_init (gpointer g_class)
1954 {
1955   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1956
1957   gst_element_class_set_details_simple (element_class,
1958       "Ogg demuxer", "Codec/Demuxer",
1959       "demux ogg streams (info about ogg: http://xiph.org)",
1960       "Wim Taymans <wim@fluendo.com>");
1961
1962   gst_element_class_add_static_pad_template (element_class,
1963       &ogg_demux_sink_template_factory);
1964   gst_element_class_add_static_pad_template (element_class,
1965       &ogg_demux_src_template_factory);
1966 }
1967
1968 static void
1969 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1970 {
1971   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1972   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1973
1974   gstelement_class->change_state = gst_ogg_demux_change_state;
1975   gstelement_class->send_event = gst_ogg_demux_receive_event;
1976
1977   gobject_class->finalize = gst_ogg_demux_finalize;
1978 }
1979
1980 static void
1981 gst_ogg_demux_init (GstOggDemux * ogg, GstOggDemuxClass * g_class)
1982 {
1983   /* create the sink pad */
1984   ogg->sinkpad =
1985       gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
1986       "sink");
1987
1988   gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
1989   gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
1990   gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
1991   gst_pad_set_activatepull_function (ogg->sinkpad,
1992       gst_ogg_demux_sink_activate_pull);
1993   gst_pad_set_activatepush_function (ogg->sinkpad,
1994       gst_ogg_demux_sink_activate_push);
1995   gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
1996
1997   ogg->chain_lock = g_mutex_new ();
1998   ogg->push_lock = g_mutex_new ();
1999   ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
2000
2001   ogg->stats_nbisections = 0;
2002   ogg->stats_bisection_steps[0] = 0;
2003   ogg->stats_bisection_steps[1] = 0;
2004   ogg->stats_bisection_max_steps[0] = 0;
2005   ogg->stats_bisection_max_steps[1] = 0;
2006
2007   ogg->newsegment = NULL;
2008 }
2009
2010 static void
2011 gst_ogg_demux_finalize (GObject * object)
2012 {
2013   GstOggDemux *ogg;
2014
2015   ogg = GST_OGG_DEMUX (object);
2016
2017   g_array_free (ogg->chains, TRUE);
2018   g_mutex_free (ogg->chain_lock);
2019   g_mutex_free (ogg->push_lock);
2020   ogg_sync_clear (&ogg->sync);
2021
2022   if (ogg->newsegment)
2023     gst_event_unref (ogg->newsegment);
2024
2025   G_OBJECT_CLASS (parent_class)->finalize (object);
2026 }
2027
2028 static void
2029 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
2030 {
2031   GstOggChain *chain;
2032   guint i;
2033
2034   chain = ogg->current_chain;
2035   if (chain == NULL)
2036     return;
2037
2038   for (i = 0; i < chain->streams->len; i++) {
2039     GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2040
2041     stream->start_time = -1;
2042     stream->map.accumulated_granule = 0;
2043   }
2044   ogg->building_chain = chain;
2045   GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2046   ogg->current_chain = NULL;
2047   ogg->resync = TRUE;
2048 }
2049
2050 static gboolean
2051 gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
2052 {
2053   gboolean res;
2054   GstOggDemux *ogg;
2055
2056   ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
2057
2058   switch (GST_EVENT_TYPE (event)) {
2059     case GST_EVENT_FLUSH_START:
2060       res = gst_ogg_demux_send_event (ogg, event);
2061       break;
2062     case GST_EVENT_FLUSH_STOP:
2063       GST_DEBUG_OBJECT (ogg, "got a flush stop event");
2064       ogg_sync_reset (&ogg->sync);
2065       res = gst_ogg_demux_send_event (ogg, event);
2066       if (ogg->pullmode || ogg->push_state != PUSH_DURATION) {
2067         /* it's starting to feel reaaaally dirty :(
2068            if we're on a spliced seek to get duration, don't reset streams,
2069            we'll need them for the delayed seek */
2070         gst_ogg_demux_reset_streams (ogg);
2071       }
2072       break;
2073     case GST_EVENT_NEWSEGMENT:
2074       GST_DEBUG_OBJECT (ogg, "got a new segment event");
2075       {
2076         gboolean update;
2077         GstFormat format;
2078         gdouble rate, arate;
2079         gint64 start, stop, time;
2080
2081         gst_event_parse_new_segment_full (event, &update, &rate, &arate,
2082             &format, &start, &stop, &time);
2083         if (format == GST_FORMAT_BYTES) {
2084           GST_PUSH_LOCK (ogg);
2085           ogg->push_byte_offset = start;
2086           ogg->push_last_seek_offset = start;
2087           GST_PUSH_UNLOCK (ogg);
2088         } else {
2089           GST_WARNING_OBJECT (ogg, "unexpected segment format: %s",
2090               gst_format_get_name (format));
2091         }
2092       }
2093       gst_event_unref (event);
2094       res = TRUE;
2095       break;
2096     case GST_EVENT_EOS:
2097     {
2098       GST_DEBUG_OBJECT (ogg, "got an EOS event");
2099 #if 0
2100       /* This would be what is needed (recover from EOS by going on to
2101          the next step (issue the delayed seek)), but it does not work
2102          if there is a queue2 upstream - see more details comment in
2103          gst_ogg_pad_submit_page.
2104          If I could find a way to bypass queue2 behavior, this should
2105          be enabled. */
2106       GST_PUSH_LOCK (ogg);
2107       if (ogg->push_state == PUSH_DURATION) {
2108         GST_DEBUG_OBJECT (ogg, "Got EOS while determining length");
2109         res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
2110         if (res != GST_FLOW_OK) {
2111           GST_DEBUG_OBJECT (ogg, "Error seeking back after duration check: %d",
2112               res);
2113         }
2114         break;
2115       }
2116       GST_PUSH_UNLOCK (ogg);
2117 #endif
2118       res = gst_ogg_demux_send_event (ogg, event);
2119       if (ogg->current_chain == NULL) {
2120         GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
2121             ("can't get first chain"));
2122       }
2123       break;
2124     }
2125     default:
2126       res = gst_ogg_demux_send_event (ogg, event);
2127       break;
2128   }
2129   gst_object_unref (ogg);
2130
2131   return res;
2132 }
2133
2134 /* submit the given buffer to the ogg sync */
2135 static GstFlowReturn
2136 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
2137 {
2138   gint size;
2139   guint8 *data;
2140   gchar *oggbuffer;
2141   GstFlowReturn ret = GST_FLOW_OK;
2142
2143   size = GST_BUFFER_SIZE (buffer);
2144   data = GST_BUFFER_DATA (buffer);
2145
2146   GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
2147   if (G_UNLIKELY (size == 0))
2148     goto done;
2149
2150   oggbuffer = ogg_sync_buffer (&ogg->sync, size);
2151   if (G_UNLIKELY (oggbuffer == NULL))
2152     goto no_buffer;
2153
2154   memcpy (oggbuffer, data, size);
2155   if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2156     goto write_failed;
2157
2158   if (!ogg->pullmode) {
2159     GST_PUSH_LOCK (ogg);
2160     ogg->push_byte_offset += size;
2161     GST_PUSH_UNLOCK (ogg);
2162   }
2163
2164 done:
2165   gst_buffer_unref (buffer);
2166
2167   return ret;
2168
2169   /* ERRORS */
2170 no_buffer:
2171   {
2172     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2173         (NULL), ("failed to get ogg sync buffer"));
2174     ret = GST_FLOW_ERROR;
2175     goto done;
2176   }
2177 write_failed:
2178   {
2179     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2180         (NULL), ("failed to write %d bytes to the sync buffer", size));
2181     ret = GST_FLOW_ERROR;
2182     goto done;
2183   }
2184 }
2185
2186 /* in random access mode this code updates the current read position
2187  * and resets the ogg sync buffer so that the next read will happen
2188  * from this new location.
2189  */
2190 static void
2191 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
2192 {
2193   GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
2194
2195   ogg->offset = offset;
2196   ogg->read_offset = offset;
2197   ogg_sync_reset (&ogg->sync);
2198 }
2199
2200 /* read more data from the current offset and submit to
2201  * the ogg sync layer.
2202  */
2203 static GstFlowReturn
2204 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
2205 {
2206   GstFlowReturn ret;
2207   GstBuffer *buffer;
2208
2209   GST_LOG_OBJECT (ogg,
2210       "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
2211       ogg->read_offset, ogg->length, end_offset);
2212
2213   if (end_offset > 0 && ogg->read_offset >= end_offset)
2214     goto boundary_reached;
2215
2216   if (ogg->read_offset == ogg->length)
2217     goto eos;
2218
2219   ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
2220   if (ret != GST_FLOW_OK)
2221     goto error;
2222
2223   ogg->read_offset += GST_BUFFER_SIZE (buffer);
2224
2225   ret = gst_ogg_demux_submit_buffer (ogg, buffer);
2226
2227   return ret;
2228
2229   /* ERROR */
2230 boundary_reached:
2231   {
2232     GST_LOG_OBJECT (ogg, "reached boundary");
2233     return GST_FLOW_LIMIT;
2234   }
2235 eos:
2236   {
2237     GST_LOG_OBJECT (ogg, "reached EOS");
2238     return GST_FLOW_UNEXPECTED;
2239   }
2240 error:
2241   {
2242     GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
2243         gst_flow_get_name (ret));
2244     return ret;
2245   }
2246 }
2247
2248 /* Read the next page from the current offset.
2249  * boundary: number of bytes ahead we allow looking for;
2250  * -1 if no boundary
2251  *
2252  * @offset will contain the offset the next page starts at when this function
2253  * returns GST_FLOW_OK.
2254  *
2255  * GST_FLOW_UNEXPECTED is returned on EOS.
2256  *
2257  * GST_FLOW_LIMIT is returned when we did not find a page before the
2258  * boundary. If @boundary is -1, this is never returned.
2259  *
2260  * Any other error returned while retrieving data from the peer is returned as
2261  * is.
2262  */
2263 static GstFlowReturn
2264 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
2265     gint64 boundary, gint64 * offset)
2266 {
2267   gint64 end_offset = -1;
2268   GstFlowReturn ret;
2269
2270   GST_LOG_OBJECT (ogg,
2271       "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
2272       G_GINT64_FORMAT, ogg->offset, boundary);
2273
2274   if (boundary >= 0)
2275     end_offset = ogg->offset + boundary;
2276
2277   while (TRUE) {
2278     glong more;
2279
2280     if (end_offset > 0 && ogg->offset >= end_offset)
2281       goto boundary_reached;
2282
2283     more = ogg_sync_pageseek (&ogg->sync, og);
2284
2285     GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
2286
2287     if (more < 0) {
2288       /* skipped n bytes */
2289       ogg->offset -= more;
2290       GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
2291           more, ogg->offset);
2292     } else if (more == 0) {
2293       /* we need more data */
2294       if (boundary == 0)
2295         goto boundary_reached;
2296
2297       GST_LOG_OBJECT (ogg, "need more data");
2298       ret = gst_ogg_demux_get_data (ogg, end_offset);
2299       if (ret != GST_FLOW_OK)
2300         break;
2301     } else {
2302       gint64 res_offset = ogg->offset;
2303
2304       /* got a page.  Return the offset at the page beginning,
2305          advance the internal offset past the page end */
2306       if (offset)
2307         *offset = res_offset;
2308       ret = GST_FLOW_OK;
2309
2310       ogg->offset += more;
2311
2312       GST_LOG_OBJECT (ogg,
2313           "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
2314           G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
2315           ogg_page_serialno (og), ogg->offset,
2316           (gint64) ogg_page_granulepos (og));
2317       break;
2318     }
2319   }
2320   GST_LOG_OBJECT (ogg, "returning %d", ret);
2321
2322   return ret;
2323
2324   /* ERRORS */
2325 boundary_reached:
2326   {
2327     GST_LOG_OBJECT (ogg,
2328         "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
2329         ogg->offset, end_offset);
2330     return GST_FLOW_LIMIT;
2331   }
2332 }
2333
2334 /* from the current offset, find the previous page, seeking backwards
2335  * until we find the page. 
2336  */
2337 static GstFlowReturn
2338 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
2339 {
2340   GstFlowReturn ret;
2341   gint64 begin = ogg->offset;
2342   gint64 end = begin;
2343   gint64 cur_offset = -1;
2344
2345   GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
2346
2347   while (cur_offset == -1) {
2348     begin -= CHUNKSIZE;
2349     if (begin < 0)
2350       begin = 0;
2351
2352     /* seek CHUNKSIZE back */
2353     gst_ogg_demux_seek (ogg, begin);
2354
2355     /* now continue reading until we run out of data, if we find a page
2356      * start, we save it. It might not be the final page as there could be
2357      * another page after this one. */
2358     while (ogg->offset < end) {
2359       gint64 new_offset;
2360
2361       ret =
2362           gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
2363       /* we hit the upper limit, offset contains the last page start */
2364       if (ret == GST_FLOW_LIMIT) {
2365         GST_LOG_OBJECT (ogg, "hit limit");
2366         break;
2367       }
2368       /* something went wrong */
2369       if (ret == GST_FLOW_UNEXPECTED) {
2370         new_offset = 0;
2371         GST_LOG_OBJECT (ogg, "got unexpected");
2372       } else if (ret != GST_FLOW_OK) {
2373         GST_LOG_OBJECT (ogg, "got error %d", ret);
2374         return ret;
2375       }
2376
2377       GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
2378
2379       /* offset is next page start */
2380       cur_offset = new_offset;
2381     }
2382   }
2383
2384   GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
2385
2386   /* we have the offset.  Actually snork and hold the page now */
2387   gst_ogg_demux_seek (ogg, cur_offset);
2388   ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
2389   if (ret != GST_FLOW_OK) {
2390     GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
2391         cur_offset);
2392     /* this shouldn't be possible */
2393     return ret;
2394   }
2395
2396   if (offset)
2397     *offset = cur_offset;
2398
2399   return ret;
2400 }
2401
2402 static gboolean
2403 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
2404 {
2405   gint i;
2406   GstOggChain *chain = ogg->current_chain;
2407
2408   if (chain == NULL)
2409     return TRUE;
2410
2411   GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
2412
2413   /* send EOS on all the pads */
2414   for (i = 0; i < chain->streams->len; i++) {
2415     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2416     GstEvent *event;
2417
2418     if (!pad->added)
2419       continue;
2420
2421     event = gst_event_new_eos ();
2422     gst_event_set_seqnum (event, ogg->seqnum);
2423     gst_pad_push_event (GST_PAD_CAST (pad), event);
2424
2425     GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
2426
2427     /* deactivate first */
2428     gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
2429
2430     gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2431
2432     pad->added = FALSE;
2433   }
2434   /* With push mode seeking implemented, we can now seek back to the chain,
2435      so we do not destroy it */
2436   GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2437   ogg->current_chain = NULL;
2438
2439   return TRUE;
2440 }
2441
2442 GstCaps *
2443 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
2444     GList * headers)
2445 {
2446   GstStructure *structure;
2447   GValue array = { 0 };
2448
2449   GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
2450
2451   if (G_UNLIKELY (!caps))
2452     return NULL;
2453   if (G_UNLIKELY (!headers))
2454     return NULL;
2455
2456   caps = gst_caps_make_writable (caps);
2457   structure = gst_caps_get_structure (caps, 0);
2458
2459   g_value_init (&array, GST_TYPE_ARRAY);
2460
2461   while (headers) {
2462     GValue value = { 0 };
2463     GstBuffer *buffer;
2464     ogg_packet *op = headers->data;
2465     g_assert (op);
2466     buffer = gst_buffer_new_and_alloc (op->bytes);
2467     memcpy (GST_BUFFER_DATA (buffer), op->packet, op->bytes);
2468     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS);
2469     g_value_init (&value, GST_TYPE_BUFFER);
2470     gst_value_take_buffer (&value, buffer);
2471     gst_value_array_append_value (&array, &value);
2472     g_value_unset (&value);
2473     headers = headers->next;
2474   }
2475
2476   gst_structure_set_value (structure, "streamheader", &array);
2477   g_value_unset (&array);
2478   GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
2479
2480   return caps;
2481 }
2482
2483 static void
2484 gst_ogg_demux_push_queued_buffers (GstOggDemux * ogg, GstOggPad * pad)
2485 {
2486   GList *walk;
2487
2488   /* push queued packets */
2489   for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
2490     ogg_packet *p = walk->data;
2491
2492     gst_ogg_demux_chain_peer (pad, p, TRUE);
2493     _ogg_packet_free (p);
2494   }
2495   /* and free the queued buffers */
2496   g_list_free (pad->map.queued);
2497   pad->map.queued = NULL;
2498 }
2499
2500 static gboolean
2501 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
2502     GstEvent * event)
2503 {
2504   gint i;
2505   gint bitrate, idx_bitrate;
2506
2507   g_return_val_if_fail (chain != NULL, FALSE);
2508
2509   if (chain == ogg->current_chain) {
2510     if (event)
2511       gst_event_unref (event);
2512
2513     for (i = 0; i < chain->streams->len; i++) {
2514       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2515       gst_ogg_demux_push_queued_buffers (ogg, pad);
2516     }
2517     return TRUE;
2518   }
2519
2520
2521   GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
2522
2523   bitrate = idx_bitrate = 0;
2524
2525   /* first add the pads */
2526   for (i = 0; i < chain->streams->len; i++) {
2527     GstOggPad *pad;
2528
2529     pad = g_array_index (chain->streams, GstOggPad *, i);
2530
2531     if (pad->map.idx_bitrate)
2532       idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
2533
2534     bitrate += pad->map.bitrate;
2535
2536     /* mark discont */
2537     gst_ogg_pad_mark_discont (pad);
2538     pad->last_ret = GST_FLOW_OK;
2539
2540     if (pad->map.is_skeleton || pad->added || GST_PAD_CAPS (pad) == NULL)
2541       continue;
2542
2543     GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
2544
2545     /* activate first */
2546     gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
2547
2548     gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2549     pad->added = TRUE;
2550   }
2551   /* prefer the index bitrate over the ones encoded in the streams */
2552   ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
2553
2554   /* after adding the new pads, remove the old pads */
2555   gst_ogg_demux_deactivate_current_chain (ogg);
2556
2557   GST_DEBUG_OBJECT (ogg, "Setting current chain to %p", chain);
2558   ogg->current_chain = chain;
2559
2560   /* we are finished now */
2561   gst_element_no_more_pads (GST_ELEMENT (ogg));
2562
2563   /* FIXME, must be sent from the streaming thread */
2564   if (event) {
2565     gst_ogg_demux_send_event (ogg, event);
2566
2567     gst_element_found_tags (GST_ELEMENT_CAST (ogg),
2568         gst_tag_list_new_full (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL));
2569   }
2570
2571   GST_DEBUG_OBJECT (ogg, "starting chain");
2572
2573   /* then send out any headers and queued packets */
2574   for (i = 0; i < chain->streams->len; i++) {
2575     GList *walk;
2576     GstOggPad *pad;
2577
2578     pad = g_array_index (chain->streams, GstOggPad *, i);
2579
2580     /* FIXME also streaming thread */
2581     if (pad->map.taglist) {
2582       GST_DEBUG_OBJECT (ogg, "pushing tags");
2583       gst_element_found_tags_for_pad (GST_ELEMENT_CAST (ogg),
2584           GST_PAD_CAST (pad), pad->map.taglist);
2585       pad->map.taglist = NULL;
2586     }
2587
2588     /* Set headers on caps */
2589     pad->map.caps =
2590         gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers);
2591     gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps);
2592
2593     GST_DEBUG_OBJECT (ogg, "pushing headers");
2594     /* push headers */
2595     for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
2596       ogg_packet *p = walk->data;
2597
2598       gst_ogg_demux_chain_peer (pad, p, TRUE);
2599     }
2600
2601     GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
2602     gst_ogg_demux_push_queued_buffers (ogg, pad);
2603   }
2604   return TRUE;
2605 }
2606
2607 static gboolean
2608 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2609     gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2610     gint64 * offset)
2611 {
2612   gint64 best;
2613   GstFlowReturn ret;
2614   gint64 result = 0;
2615
2616   best = begin;
2617
2618   GST_DEBUG_OBJECT (ogg,
2619       "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
2620       begin, end);
2621   GST_DEBUG_OBJECT (ogg,
2622       "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
2623       GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
2624   GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
2625
2626   /* perform the seek */
2627   while (begin < end) {
2628     gint64 bisect;
2629
2630     if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
2631       bisect = begin;
2632     } else {
2633       /* take a (pretty decent) guess, avoiding overflow */
2634       gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
2635
2636       bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
2637
2638       if (bisect <= begin)
2639         bisect = begin;
2640       GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
2641     }
2642     gst_ogg_demux_seek (ogg, bisect);
2643
2644     while (begin < end) {
2645       ogg_page og;
2646
2647       GST_DEBUG_OBJECT (ogg,
2648           "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
2649           ", end %" G_GINT64_FORMAT, bisect, begin, end);
2650
2651       ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2652       GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2653           result);
2654
2655       if (ret == GST_FLOW_LIMIT) {
2656         /* we hit the upper limit, go back a bit */
2657         if (bisect <= begin + 1) {
2658           end = begin;          /* found it */
2659         } else {
2660           if (bisect == 0)
2661             goto seek_error;
2662
2663           bisect -= CHUNKSIZE;
2664           if (bisect <= begin)
2665             bisect = begin + 1;
2666
2667           gst_ogg_demux_seek (ogg, bisect);
2668         }
2669       } else if (ret == GST_FLOW_OK) {
2670         /* found offset of next ogg page */
2671         gint64 granulepos;
2672         GstClockTime granuletime;
2673         GstOggPad *pad;
2674
2675         /* get the granulepos */
2676         GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
2677             result);
2678         granulepos = ogg_page_granulepos (&og);
2679         if (granulepos == -1) {
2680           GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2681           continue;
2682         }
2683
2684         /* get the stream */
2685         pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2686         if (pad == NULL || pad->map.is_skeleton)
2687           continue;
2688
2689         /* convert granulepos to time */
2690         granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2691             granulepos);
2692         if (granuletime < pad->start_time)
2693           continue;
2694
2695         GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
2696             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2697
2698         granuletime -= pad->start_time;
2699         granuletime += chain->begin_time;
2700
2701         GST_DEBUG_OBJECT (ogg,
2702             "found page with granule %" G_GINT64_FORMAT " and time %"
2703             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2704
2705         if (granuletime < target) {
2706           best = result;        /* raw offset of packet with granulepos */
2707           begin = ogg->offset;  /* raw offset of next page */
2708           begintime = granuletime;
2709
2710           bisect = begin;       /* *not* begin + 1 */
2711         } else {
2712           if (bisect <= begin + 1) {
2713             end = begin;        /* found it */
2714           } else {
2715             if (end == ogg->offset) {   /* we're pretty close - we'd be stuck in */
2716               end = result;
2717               bisect -= CHUNKSIZE;      /* an endless loop otherwise. */
2718               if (bisect <= begin)
2719                 bisect = begin + 1;
2720               gst_ogg_demux_seek (ogg, bisect);
2721             } else {
2722               end = result;
2723               endtime = granuletime;
2724               break;
2725             }
2726           }
2727         }
2728       } else
2729         goto seek_error;
2730     }
2731   }
2732   GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
2733   gst_ogg_demux_seek (ogg, best);
2734   *offset = best;
2735
2736   return TRUE;
2737
2738   /* ERRORS */
2739 seek_error:
2740   {
2741     GST_DEBUG_OBJECT (ogg, "got a seek error");
2742     return FALSE;
2743   }
2744 }
2745
2746 static gboolean
2747 do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2748     gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2749     gint64 * p_offset, gint64 * p_timestamp)
2750 {
2751   guint i;
2752   guint64 timestamp, offset;
2753   guint64 r_timestamp, r_offset;
2754   gboolean result = FALSE;
2755
2756   target -= begintime;
2757
2758   r_offset = -1;
2759   r_timestamp = -1;
2760
2761   for (i = 0; i < chain->streams->len; i++) {
2762     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2763
2764     timestamp = target;
2765     if (gst_ogg_map_search_index (&pad->map, TRUE, &timestamp, &offset)) {
2766       GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
2767           timestamp, offset);
2768
2769       if (r_offset == -1 || offset < r_offset) {
2770         r_offset = offset;
2771         r_timestamp = timestamp;
2772       }
2773       result |= TRUE;
2774     }
2775   }
2776
2777   if (p_timestamp)
2778     *p_timestamp = r_timestamp;
2779   if (p_offset)
2780     *p_offset = r_offset;
2781
2782   return result;
2783 }
2784
2785 /*
2786  * do seek to time @position, return FALSE or chain and TRUE
2787  */
2788 static gboolean
2789 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
2790     gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
2791 {
2792   guint64 position;
2793   GstOggChain *chain = NULL;
2794   gint64 begin, end;
2795   gint64 begintime, endtime;
2796   gint64 target, keytarget;
2797   gint64 best;
2798   gint64 total;
2799   gint64 result = 0;
2800   GstFlowReturn ret;
2801   gint i, pending, len;
2802   gboolean first_parsed_page = TRUE;
2803
2804   position = segment->last_stop;
2805
2806   /* first find the chain to search in */
2807   total = ogg->total_time;
2808   if (ogg->chains->len == 0)
2809     goto no_chains;
2810
2811   for (i = ogg->chains->len - 1; i >= 0; i--) {
2812     chain = g_array_index (ogg->chains, GstOggChain *, i);
2813     total -= chain->total_time;
2814     if (position >= total)
2815       break;
2816   }
2817
2818   /* first step, locate page containing the required data */
2819   begin = chain->offset;
2820   end = chain->end_offset;
2821   begintime = chain->begin_time;
2822   endtime = begintime + chain->total_time;
2823   target = position - total + begintime;
2824
2825   if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
2826           &best))
2827     goto seek_error;
2828
2829   /* second step: find pages for all streams, we use the keyframe_granule to keep
2830    * track of which ones we saw. If we have seen a page for each stream we can
2831    * calculate the positions of each keyframe. */
2832   GST_DEBUG_OBJECT (ogg, "find keyframes");
2833   len = pending = chain->streams->len;
2834
2835   /* figure out where the keyframes are */
2836   keytarget = target;
2837
2838   while (TRUE) {
2839     ogg_page og;
2840     gint64 granulepos;
2841     GstOggPad *pad;
2842     GstClockTime keyframe_time, granule_time;
2843
2844     ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2845     GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2846         result);
2847     if (ret == GST_FLOW_LIMIT) {
2848       GST_LOG_OBJECT (ogg, "reached limit");
2849       break;
2850     } else if (ret != GST_FLOW_OK)
2851       goto seek_error;
2852
2853     /* get the stream */
2854     pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2855     if (pad == NULL)
2856       continue;
2857
2858     if (pad->map.is_skeleton)
2859       goto next;
2860
2861     granulepos = ogg_page_granulepos (&og);
2862     if (granulepos == -1 || granulepos == 0) {
2863       GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2864       continue;
2865     }
2866
2867     /* we only do this the first time we pass here */
2868     if (first_parsed_page) {
2869       /* Now that we have a time reference from the page, we can check
2870        * whether all streams still have pages from here on.
2871        *
2872        * This would be more elegant before the loop, but getting the page from
2873        * there without breaking anything would be more costly */
2874       granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2875           granulepos);
2876       for (i = 0; i < len; i++) {
2877         GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2878
2879         if (stream == pad)
2880           /* we already know we have at least one page (the current one)
2881            * for this stream */
2882           continue;
2883
2884         if (granule_time > stream->map.total_time)
2885           /* we won't encounter any more pages of this stream, so we don't
2886            * try finding a key frame for it */
2887           pending--;
2888       }
2889       first_parsed_page = FALSE;
2890     }
2891
2892
2893     /* in reverse we want to go past the page with the lower timestamp */
2894     if (segment->rate < 0.0) {
2895       /* get time for this pad */
2896       granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2897           granulepos);
2898
2899       GST_LOG_OBJECT (ogg,
2900           "looking at page with ts %" GST_TIME_FORMAT ", target %"
2901           GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
2902           GST_TIME_ARGS (target));
2903       if (granule_time < target)
2904         continue;
2905     }
2906
2907     /* we've seen this pad before */
2908     if (pad->keyframe_granule != -1)
2909       continue;
2910
2911     /* convert granule of this pad to the granule of the keyframe */
2912     pad->keyframe_granule =
2913         gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
2914     GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
2915         pad->keyframe_granule);
2916
2917     /* get time of the keyframe */
2918     keyframe_time =
2919         gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
2920     GST_LOG_OBJECT (ogg,
2921         "stream %08x granule time %" GST_TIME_FORMAT,
2922         pad->map.serialno, GST_TIME_ARGS (keyframe_time));
2923
2924     /* collect smallest value */
2925     if (keyframe_time != -1) {
2926       keyframe_time += begintime;
2927       if (keyframe_time < keytarget)
2928         keytarget = keyframe_time;
2929     }
2930
2931   next:
2932     pending--;
2933     if (pending == 0)
2934       break;
2935   }
2936
2937   /* for negative rates we will get to the keyframe backwards */
2938   if (segment->rate < 0.0)
2939     goto done;
2940
2941   if (keytarget != target) {
2942     GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
2943         GST_TIME_ARGS (keytarget));
2944
2945     /* last step, seek to the location of the keyframe */
2946     if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
2947             keytarget, &best))
2948       goto seek_error;
2949   } else {
2950     /* seek back to previous position */
2951     GST_LOG_OBJECT (ogg, "keyframe on target");
2952     gst_ogg_demux_seek (ogg, best);
2953   }
2954
2955 done:
2956   if (keyframe) {
2957     if (segment->rate > 0.0)
2958       segment->time = keytarget;
2959     segment->last_stop = keytarget - begintime;
2960   }
2961
2962   *rchain = chain;
2963
2964   return TRUE;
2965
2966 no_chains:
2967   {
2968     GST_DEBUG_OBJECT (ogg, "no chains");
2969     return FALSE;
2970   }
2971 seek_error:
2972   {
2973     GST_DEBUG_OBJECT (ogg, "got a seek error");
2974     return FALSE;
2975   }
2976 }
2977
2978 /* does not take ownership of the event */
2979 static gboolean
2980 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
2981 {
2982   GstOggChain *chain = NULL;
2983   gboolean res;
2984   gboolean flush, accurate, keyframe;
2985   GstFormat format;
2986   gdouble rate;
2987   GstSeekFlags flags;
2988   GstSeekType cur_type, stop_type;
2989   gint64 cur, stop;
2990   gboolean update;
2991   guint32 seqnum;
2992   GstEvent *tevent;
2993
2994   if (event) {
2995     GST_DEBUG_OBJECT (ogg, "seek with event");
2996
2997     gst_event_parse_seek (event, &rate, &format, &flags,
2998         &cur_type, &cur, &stop_type, &stop);
2999
3000     /* we can only seek on time */
3001     if (format != GST_FORMAT_TIME) {
3002       GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3003       goto error;
3004     }
3005     seqnum = gst_event_get_seqnum (event);
3006   } else {
3007     GST_DEBUG_OBJECT (ogg, "seek without event");
3008
3009     flags = 0;
3010     rate = 1.0;
3011     seqnum = gst_util_seqnum_next ();
3012   }
3013
3014   GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
3015
3016   flush = flags & GST_SEEK_FLAG_FLUSH;
3017   accurate = flags & GST_SEEK_FLAG_ACCURATE;
3018   keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
3019
3020   /* first step is to unlock the streaming thread if it is
3021    * blocked in a chain call, we do this by starting the flush. because
3022    * we cannot yet hold any streaming lock, we have to protect the chains
3023    * with their own lock. */
3024   if (flush) {
3025     gint i;
3026
3027     tevent = gst_event_new_flush_start ();
3028     gst_event_set_seqnum (tevent, seqnum);
3029
3030     gst_event_ref (tevent);
3031     gst_pad_push_event (ogg->sinkpad, tevent);
3032
3033     GST_CHAIN_LOCK (ogg);
3034     for (i = 0; i < ogg->chains->len; i++) {
3035       GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3036       gint j;
3037
3038       for (j = 0; j < chain->streams->len; j++) {
3039         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
3040
3041         gst_event_ref (tevent);
3042         gst_pad_push_event (GST_PAD (pad), tevent);
3043       }
3044     }
3045     GST_CHAIN_UNLOCK (ogg);
3046
3047     gst_event_unref (tevent);
3048   } else {
3049     gst_pad_pause_task (ogg->sinkpad);
3050   }
3051
3052   /* now grab the stream lock so that streaming cannot continue, for
3053    * non flushing seeks when the element is in PAUSED this could block
3054    * forever. */
3055   GST_PAD_STREAM_LOCK (ogg->sinkpad);
3056
3057   if (ogg->segment_running && !flush) {
3058     /* create the segment event to close the current segment */
3059     if ((chain = ogg->current_chain)) {
3060       GstEvent *newseg;
3061       gint64 chain_start = 0;
3062
3063       if (chain->segment_start != GST_CLOCK_TIME_NONE)
3064         chain_start = chain->segment_start;
3065
3066       newseg = gst_event_new_new_segment (TRUE, ogg->segment.rate,
3067           GST_FORMAT_TIME, ogg->segment.start + chain_start,
3068           ogg->segment.last_stop + chain_start, ogg->segment.time);
3069       /* set the seqnum of the running segment */
3070       gst_event_set_seqnum (newseg, ogg->seqnum);
3071
3072       /* send segment on old chain, FIXME, must be sent from streaming thread. */
3073       gst_ogg_demux_send_event (ogg, newseg);
3074     }
3075   }
3076
3077   if (event) {
3078     gst_segment_set_seek (&ogg->segment, rate, format, flags,
3079         cur_type, cur, stop_type, stop, &update);
3080   }
3081
3082   GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
3083       GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
3084       GST_TIME_ARGS (ogg->segment.stop));
3085
3086   /* we need to stop flushing on the srcpad as we're going to use it
3087    * next. We can do this as we have the STREAM lock now. */
3088   if (flush) {
3089     tevent = gst_event_new_flush_stop ();
3090     gst_event_set_seqnum (tevent, seqnum);
3091     gst_pad_push_event (ogg->sinkpad, tevent);
3092   }
3093
3094   {
3095     gint i;
3096
3097     /* reset all ogg streams now, need to do this from within the lock to
3098      * make sure the streaming thread is not messing with the stream */
3099     for (i = 0; i < ogg->chains->len; i++) {
3100       GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3101
3102       gst_ogg_chain_reset (chain);
3103     }
3104   }
3105
3106   /* for reverse we will already seek accurately */
3107   res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
3108
3109   /* seek failed, make sure we continue the current chain */
3110   if (!res) {
3111     GST_DEBUG_OBJECT (ogg, "seek failed");
3112     chain = ogg->current_chain;
3113   } else {
3114     GST_DEBUG_OBJECT (ogg, "seek success");
3115   }
3116
3117   if (!chain)
3118     goto no_chain;
3119
3120   /* now we have a new position, prepare for streaming again */
3121   {
3122     GstEvent *event;
3123     gint64 stop;
3124     gint64 start;
3125     gint64 last_stop, begin_time;
3126
3127     /* we have to send the flush to the old chain, not the new one */
3128     if (flush) {
3129       tevent = gst_event_new_flush_stop ();
3130       gst_event_set_seqnum (tevent, seqnum);
3131       gst_ogg_demux_send_event (ogg, tevent);
3132     }
3133
3134     /* we need this to see how far inside the chain we need to start */
3135     if (chain->begin_time != GST_CLOCK_TIME_NONE)
3136       begin_time = chain->begin_time;
3137     else
3138       begin_time = 0;
3139
3140     /* segment.start gives the start over all chains, we calculate the amount
3141      * of time into this chain we need to start */
3142     start = ogg->segment.start - begin_time;
3143     if (chain->segment_start != GST_CLOCK_TIME_NONE)
3144       start += chain->segment_start;
3145
3146     if ((stop = ogg->segment.stop) == -1)
3147       stop = ogg->segment.duration;
3148
3149     /* segment.stop gives the stop time over all chains, calculate the amount of
3150      * time we need to stop in this chain */
3151     if (stop != -1) {
3152       if (stop > begin_time)
3153         stop -= begin_time;
3154       else
3155         stop = 0;
3156       stop += chain->segment_start;
3157       /* we must stop when this chain ends and switch to the next chain to play
3158        * the remainder of the segment. */
3159       stop = MIN (stop, chain->segment_stop);
3160     }
3161
3162     last_stop = ogg->segment.last_stop;
3163     if (chain->segment_start != GST_CLOCK_TIME_NONE)
3164       last_stop += chain->segment_start;
3165
3166     /* create the segment event we are going to send out */
3167     if (ogg->segment.rate >= 0.0)
3168       event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
3169           ogg->segment.format, last_stop, stop, ogg->segment.time);
3170     else
3171       event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
3172           ogg->segment.format, start, last_stop, ogg->segment.time);
3173
3174     gst_event_set_seqnum (event, seqnum);
3175
3176     if (chain != ogg->current_chain) {
3177       /* switch to different chain, send segment on new chain */
3178       gst_ogg_demux_activate_chain (ogg, chain, event);
3179     } else {
3180       /* mark discont and send segment on current chain */
3181       gst_ogg_chain_mark_discont (chain);
3182       /* This event should be sent from the streaming thread (sink pad task) */
3183       if (ogg->newsegment)
3184         gst_event_unref (ogg->newsegment);
3185       ogg->newsegment = event;
3186     }
3187
3188     /* notify start of new segment */
3189     if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3190       GstMessage *message;
3191
3192       message = gst_message_new_segment_start (GST_OBJECT (ogg),
3193           GST_FORMAT_TIME, ogg->segment.last_stop);
3194       gst_message_set_seqnum (message, seqnum);
3195
3196       gst_element_post_message (GST_ELEMENT (ogg), message);
3197     }
3198
3199     ogg->segment_running = TRUE;
3200     ogg->seqnum = seqnum;
3201     /* restart our task since it might have been stopped when we did the 
3202      * flush. */
3203     gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3204         ogg->sinkpad);
3205   }
3206
3207   /* streaming can continue now */
3208   GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3209
3210   return res;
3211
3212   /* ERRORS */
3213 error:
3214   {
3215     GST_DEBUG_OBJECT (ogg, "seek failed");
3216     return FALSE;
3217   }
3218 no_chain:
3219   {
3220     GST_DEBUG_OBJECT (ogg, "no chain to seek in");
3221     GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3222     return FALSE;
3223   }
3224 }
3225
3226 static gboolean
3227 gst_ogg_demux_get_duration_push (GstOggDemux * ogg, int flags)
3228 {
3229   /* In push mode, we get to the end of the stream to get the duration */
3230   gint64 position;
3231   GstEvent *sevent;
3232   gboolean res;
3233
3234   /* A full Ogg page can be almost 64 KB. There's no guarantee that there'll be a
3235      granpos there, but it's fairly likely */
3236   position =
3237       ogg->push_byte_length - DURATION_CHUNK_OFFSET - EOS_AVOIDANCE_THRESHOLD;
3238   if (position < 0)
3239     position = 0;
3240
3241   GST_DEBUG_OBJECT (ogg,
3242       "Getting duration, seeking near the end, to %" G_GINT64_FORMAT, position);
3243   ogg->push_state = PUSH_DURATION;
3244   /* do not read the last byte */
3245   sevent = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET,
3246       position, GST_SEEK_TYPE_SET, ogg->push_byte_length - 1);
3247   res = gst_pad_push_event (ogg->sinkpad, sevent);
3248   if (res) {
3249     GST_DEBUG_OBJECT (ogg, "Seek succesful");
3250     return TRUE;
3251   } else {
3252     GST_INFO_OBJECT (ogg, "Seek failed, duration will stay unknown");
3253     ogg->push_state = PUSH_PLAYING;
3254     ogg->push_disable_seeking = TRUE;
3255     return FALSE;
3256   }
3257 }
3258
3259 static gboolean
3260 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3261     GstEvent * event)
3262 {
3263   if (ogg->push_byte_length < 0) {
3264     GstPad *peer;
3265
3266     GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3267     if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3268       GstFormat format = GST_FORMAT_BYTES;
3269       gint64 length;
3270       int res;
3271
3272       res = gst_pad_query_duration (peer, &format, &length);
3273       if (res && length > 0) {
3274         ogg->push_byte_length = length;
3275         GST_DEBUG_OBJECT (ogg,
3276             "File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
3277       }
3278       format = GST_FORMAT_TIME;
3279       res = gst_pad_query_duration (peer, &format, &length);
3280       gst_object_unref (peer);
3281       if (res && length >= 0) {
3282         ogg->push_time_length = length;
3283         GST_DEBUG_OBJECT (ogg, "File time length %" GST_TIME_FORMAT,
3284             GST_TIME_ARGS (ogg->push_time_length));
3285       } else if (!ogg->push_disable_seeking) {
3286         gboolean res;
3287
3288         res = gst_ogg_demux_get_duration_push (ogg, flags);
3289         if (res) {
3290           GST_DEBUG_OBJECT (ogg,
3291               "File time length unknown, trying to determine");
3292           ogg->push_mode_seek_delayed_event = NULL;
3293           if (event) {
3294             GST_DEBUG_OBJECT (ogg,
3295                 "Let me intercept this innocent looking seek request");
3296             ogg->push_mode_seek_delayed_event = gst_event_copy (event);
3297           }
3298           return FALSE;
3299         }
3300       }
3301     }
3302   }
3303   return TRUE;
3304 }
3305
3306 static gboolean
3307 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3308 {
3309   gint bitrate;
3310   gboolean res = TRUE;
3311   GstFormat format;
3312   gdouble rate;
3313   GstSeekFlags flags;
3314   GstSeekType start_type, stop_type;
3315   gint64 start, stop;
3316   GstEvent *sevent;
3317   GstOggChain *chain;
3318   gint64 best, best_time;
3319   gint i;
3320
3321   GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3322
3323   gst_event_parse_seek (event, &rate, &format, &flags,
3324       &start_type, &start, &stop_type, &stop);
3325
3326   if (format != GST_FORMAT_TIME) {
3327     GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3328     goto error;
3329   }
3330
3331   if (start_type != GST_SEEK_TYPE_SET) {
3332     GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3333     goto error;
3334   }
3335
3336   if (!(flags & GST_SEEK_FLAG_FLUSH)) {
3337     GST_DEBUG_OBJECT (ogg, "can only do flushing seeks");
3338     goto error;
3339   }
3340
3341   GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3342       GST_TIME_ARGS (start));
3343
3344   chain = ogg->current_chain;
3345   if (!chain) {
3346     GST_WARNING_OBJECT (ogg, "No chain to seek on");
3347     goto error;
3348   }
3349
3350   /* start accessing push_* members */
3351   GST_PUSH_LOCK (ogg);
3352
3353   /* not if we disabled seeking (chained streams) */
3354   if (ogg->push_disable_seeking) {
3355     GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3356     goto error_locked;
3357   }
3358
3359   /* not when we're trying to work out duration */
3360   if (ogg->push_state == PUSH_DURATION) {
3361     GST_DEBUG_OBJECT (ogg, "Busy working out duration, try again later");
3362     goto error_locked;
3363   }
3364
3365   /* actually, not if we're doing any seeking already */
3366   if (ogg->push_state != PUSH_PLAYING) {
3367     GST_DEBUG_OBJECT (ogg, "Already doing some seeking, try again later");
3368     goto error_locked;
3369   }
3370
3371   /* on the first seek, get length if we can */
3372   if (!gst_ogg_demux_check_duration_push (ogg, flags, event)) {
3373     GST_PUSH_UNLOCK (ogg);
3374     return FALSE;
3375   }
3376
3377   if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
3378     /* the index gave some result */
3379     GST_DEBUG_OBJECT (ogg,
3380         "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
3381         best, best_time);
3382   } else {
3383     if (ogg->push_time_length > 0) {
3384       /* if we know the time length, we know the full segment bitrate */
3385       GST_DEBUG_OBJECT (ogg, "Using real file bitrate");
3386       bitrate =
3387           gst_util_uint64_scale (ogg->push_byte_length, 8 * GST_SECOND,
3388           ogg->push_time_length);
3389     } else if (ogg->push_time_offset > 0) {
3390       /* get a first approximation using known bitrate to the current position */
3391       GST_DEBUG_OBJECT (ogg, "Using file bitrate so far");
3392       bitrate =
3393           gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
3394           ogg->push_time_offset);
3395     } else if (ogg->bitrate > 0) {
3396       /* nominal bitrate is better than nothing, even if it lies often */
3397       GST_DEBUG_OBJECT (ogg, "Using nominal bitrate");
3398       bitrate = ogg->bitrate;
3399     } else {
3400       /* meh */
3401       GST_DEBUG_OBJECT (ogg,
3402           "At stream start, and no nominal bitrate, using some random magic "
3403           "number to seed");
3404       /* the bisection, once started, should give us a better approximation */
3405       bitrate = 1000;
3406     }
3407     best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3408   }
3409
3410   /* offset by typical page length, and ensure our best guess is within
3411      reasonable bounds */
3412   best -= CHUNKSIZE;
3413   if (best < 0)
3414     best = 0;
3415   if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3416     best = ogg->push_byte_length - 1;
3417
3418   /* set up bisection search */
3419   ogg->push_offset0 = 0;
3420   ogg->push_offset1 = ogg->push_byte_length - 1;
3421   ogg->push_time0 = ogg->push_start_time;
3422   ogg->push_time1 = ogg->push_time_length;
3423   ogg->push_seek_time_target = start;
3424   ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
3425   ogg->push_seek_time_original_target = start;
3426   ogg->push_state = PUSH_BISECT1;
3427   ogg->seek_secant = FALSE;
3428   ogg->seek_undershot = FALSE;
3429
3430   /* reset pad push mode seeking state */
3431   for (i = 0; i < chain->streams->len; i++) {
3432     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3433     pad->push_kf_time = GST_CLOCK_TIME_NONE;
3434     pad->push_sync_time = GST_CLOCK_TIME_NONE;
3435   }
3436
3437   GST_DEBUG_OBJECT (ogg,
3438       "Setting up bisection search for %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
3439       " (time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", ogg->push_offset0,
3440       ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
3441       GST_TIME_ARGS (ogg->push_time1));
3442   GST_DEBUG_OBJECT (ogg,
3443       "Target time is %" GST_TIME_FORMAT ", best first guess is %"
3444       G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target), best);
3445
3446   ogg->push_seek_rate = rate;
3447   ogg->push_seek_flags = flags;
3448   ogg->push_mode_seek_delayed_event = NULL;
3449   ogg->push_bisection_steps[0] = 1;
3450   ogg->push_bisection_steps[1] = 0;
3451   sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
3452       start_type, best, GST_SEEK_TYPE_NONE, -1);
3453
3454   GST_PUSH_UNLOCK (ogg);
3455   res = gst_pad_push_event (ogg->sinkpad, sevent);
3456
3457   return res;
3458
3459   /* ERRORS */
3460 error:
3461   {
3462     GST_DEBUG_OBJECT (ogg, "seek failed");
3463     return FALSE;
3464   }
3465
3466 error_locked:
3467   GST_PUSH_UNLOCK (ogg);
3468   goto error;
3469 }
3470
3471 static gboolean
3472 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
3473 {
3474   gboolean res;
3475
3476   if (ogg->pullmode) {
3477     res = gst_ogg_demux_perform_seek_pull (ogg, event);
3478   } else {
3479     res = gst_ogg_demux_perform_seek_push (ogg, event);
3480   }
3481   return res;
3482 }
3483
3484
3485 /* finds each bitstream link one at a time using a bisection search
3486  * (has to begin by knowing the offset of the lb's initial page).
3487  * Recurses for each link so it can alloc the link storage after
3488  * finding them all, then unroll and fill the cache at the same time
3489  */
3490 static GstFlowReturn
3491 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
3492     gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
3493 {
3494   gint64 endsearched = end;
3495   gint64 next = end;
3496   ogg_page og;
3497   GstFlowReturn ret;
3498   gint64 offset;
3499   GstOggChain *nextchain;
3500
3501   GST_LOG_OBJECT (ogg,
3502       "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
3503       ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
3504
3505   /* the below guards against garbage seperating the last and
3506    * first pages of two links. */
3507   while (searched < endsearched) {
3508     gint64 bisect;
3509
3510     if (endsearched - searched < CHUNKSIZE) {
3511       bisect = searched;
3512     } else {
3513       bisect = (searched + endsearched) / 2;
3514     }
3515
3516     gst_ogg_demux_seek (ogg, bisect);
3517     ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
3518
3519     if (ret == GST_FLOW_UNEXPECTED) {
3520       endsearched = bisect;
3521     } else if (ret == GST_FLOW_OK) {
3522       guint32 serial = ogg_page_serialno (&og);
3523
3524       if (!gst_ogg_chain_has_stream (chain, serial)) {
3525         endsearched = bisect;
3526         next = offset;
3527       } else {
3528         searched = offset + og.header_len + og.body_len;
3529       }
3530     } else
3531       return ret;
3532   }
3533
3534   GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
3535
3536   chain->end_offset = searched;
3537   ret = gst_ogg_demux_read_end_chain (ogg, chain);
3538   if (ret != GST_FLOW_OK)
3539     return ret;
3540
3541   GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
3542
3543   gst_ogg_demux_seek (ogg, next);
3544   ret = gst_ogg_demux_read_chain (ogg, &nextchain);
3545   if (ret == GST_FLOW_UNEXPECTED) {
3546     nextchain = NULL;
3547     ret = GST_FLOW_OK;
3548     GST_LOG_OBJECT (ogg, "no next chain");
3549   } else if (ret != GST_FLOW_OK)
3550     goto done;
3551
3552   if (searched < end && nextchain != NULL) {
3553     ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
3554         end, nextchain, m + 1);
3555     if (ret != GST_FLOW_OK)
3556       goto done;
3557   }
3558   GST_LOG_OBJECT (ogg, "adding chain %p", chain);
3559
3560   g_array_insert_val (ogg->chains, 0, chain);
3561
3562 done:
3563   return ret;
3564 }
3565
3566 /* read a chain from the ogg file. This code will
3567  * read all BOS pages and will create and return a GstOggChain 
3568  * structure with the results. 
3569  * 
3570  * This function will also read N pages from each stream in the
3571  * chain and submit them to the decoders. When the decoder has
3572  * decoded the first buffer, we know the timestamp of the first
3573  * page in the chain.
3574  */
3575 static GstFlowReturn
3576 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
3577 {
3578   GstFlowReturn ret;
3579   GstOggChain *chain = NULL;
3580   gint64 offset = ogg->offset;
3581   ogg_page og;
3582   gboolean done;
3583   gint i;
3584
3585   GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
3586
3587   /* first read the BOS pages, do typefind on them, create
3588    * the decoders, send data to the decoders. */
3589   while (TRUE) {
3590     GstOggPad *pad;
3591     guint32 serial;
3592
3593     ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3594     if (ret != GST_FLOW_OK) {
3595       if (ret == GST_FLOW_UNEXPECTED) {
3596         GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
3597       } else {
3598         GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
3599       }
3600       break;
3601     }
3602     if (!ogg_page_bos (&og)) {
3603       GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
3604       /* if we did not find a chain yet, assume this is a bogus stream and
3605        * ignore it */
3606       if (!chain) {
3607         GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
3608         ret = GST_FLOW_UNEXPECTED;
3609       }
3610       break;
3611     }
3612
3613     if (chain == NULL) {
3614       chain = gst_ogg_chain_new (ogg);
3615       chain->offset = offset;
3616     }
3617
3618     serial = ogg_page_serialno (&og);
3619     if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
3620       GST_WARNING_OBJECT (ogg,
3621           "found serial %08x BOS page twice, ignoring", serial);
3622       continue;
3623     }
3624
3625     pad = gst_ogg_chain_new_stream (chain, serial);
3626     gst_ogg_pad_submit_page (pad, &og);
3627   }
3628
3629   if (ret != GST_FLOW_OK || chain == NULL) {
3630     if (ret == GST_FLOW_OK) {
3631       GST_WARNING_OBJECT (ogg, "no chain was found");
3632       ret = GST_FLOW_ERROR;
3633     } else if (ret != GST_FLOW_UNEXPECTED) {
3634       GST_WARNING_OBJECT (ogg, "failed to read chain");
3635     } else {
3636       GST_DEBUG_OBJECT (ogg, "done reading chains");
3637     }
3638     if (chain) {
3639       gst_ogg_chain_free (chain);
3640     }
3641     if (res_chain)
3642       *res_chain = NULL;
3643     return ret;
3644   }
3645
3646   chain->have_bos = TRUE;
3647   GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
3648
3649   /* now read pages until we receive a buffer from each of the
3650    * stream decoders, this will tell us the timestamp of the
3651    * first packet in the chain then */
3652
3653   /* save the offset to the first non bos page in the chain: if searching for
3654    * pad->first_time we read past the end of the chain, we'll seek back to this
3655    * position
3656    */
3657   offset = ogg->offset;
3658
3659   done = FALSE;
3660   while (!done) {
3661     guint32 serial;
3662     gboolean known_serial = FALSE;
3663     GstFlowReturn ret;
3664
3665     serial = ogg_page_serialno (&og);
3666     done = TRUE;
3667     for (i = 0; i < chain->streams->len; i++) {
3668       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3669
3670       GST_LOG_OBJECT (ogg,
3671           "serial %08x time %" GST_TIME_FORMAT,
3672           pad->map.serialno, GST_TIME_ARGS (pad->start_time));
3673
3674       if (pad->map.serialno == serial) {
3675         known_serial = TRUE;
3676
3677         /* submit the page now, this will fill in the start_time when the
3678          * internal decoder finds it */
3679         gst_ogg_pad_submit_page (pad, &og);
3680
3681         if (!pad->map.is_skeleton && pad->start_time == -1
3682             && ogg_page_eos (&og)) {
3683           /* got EOS on a pad before we could find its start_time.
3684            * We have no chance of finding a start_time for every pad so
3685            * stop searching for the other start_time(s).
3686            */
3687           done = TRUE;
3688           break;
3689         }
3690       }
3691       /* the timestamp will be filled in when we submit the pages */
3692       if (!pad->map.is_sparse)
3693         done &= (pad->start_time != GST_CLOCK_TIME_NONE);
3694
3695       GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
3696     }
3697
3698     /* we read a page not belonging to the current chain: seek back to the
3699      * beginning of the chain
3700      */
3701     if (!known_serial) {
3702       GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
3703       gst_ogg_demux_seek (ogg, offset);
3704       break;
3705     }
3706
3707     if (!done) {
3708       ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3709       if (ret != GST_FLOW_OK)
3710         break;
3711     }
3712   }
3713   GST_LOG_OBJECT (ogg, "done reading chain");
3714   /* now we can fill in the missing info using queries */
3715   for (i = 0; i < chain->streams->len; i++) {
3716     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3717
3718     if (pad->map.is_skeleton)
3719       continue;
3720
3721     pad->mode = GST_OGG_PAD_MODE_STREAMING;
3722   }
3723
3724   if (res_chain)
3725     *res_chain = chain;
3726
3727   return GST_FLOW_OK;
3728 }
3729
3730 /* read the last pages from the ogg stream to get the final
3731  * page end_offsets.
3732  */
3733 static GstFlowReturn
3734 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
3735 {
3736   gint64 begin = chain->end_offset;
3737   gint64 end = begin;
3738   gint64 last_granule = -1;
3739   GstOggPad *last_pad = NULL;
3740   GstFlowReturn ret;
3741   gboolean done = FALSE;
3742   ogg_page og;
3743   gint i;
3744
3745   while (!done) {
3746     begin -= CHUNKSIZE;
3747     if (begin < 0)
3748       begin = 0;
3749
3750     gst_ogg_demux_seek (ogg, begin);
3751
3752     /* now continue reading until we run out of data, if we find a page
3753      * start, we save it. It might not be the final page as there could be
3754      * another page after this one. */
3755     while (ogg->offset < end) {
3756       ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
3757
3758       if (ret == GST_FLOW_LIMIT)
3759         break;
3760       if (ret != GST_FLOW_OK)
3761         return ret;
3762
3763       for (i = 0; i < chain->streams->len; i++) {
3764         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3765
3766         if (pad->map.is_skeleton)
3767           continue;
3768
3769         if (pad->map.serialno == ogg_page_serialno (&og)) {
3770           gint64 granulepos = ogg_page_granulepos (&og);
3771
3772           if (granulepos != -1) {
3773             last_granule = granulepos;
3774             last_pad = pad;
3775             done = TRUE;
3776           }
3777           break;
3778         }
3779       }
3780     }
3781   }
3782
3783   if (last_pad) {
3784     chain->segment_stop =
3785         gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
3786         last_granule);
3787   } else {
3788     chain->segment_stop = GST_CLOCK_TIME_NONE;
3789   }
3790
3791   GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
3792
3793   return GST_FLOW_OK;
3794 }
3795
3796 /* find a pad with a given serial number
3797  */
3798 static GstOggPad *
3799 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
3800 {
3801   GstOggPad *pad;
3802   gint i;
3803
3804   /* first look in building chain if any */
3805   if (ogg->building_chain) {
3806     pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
3807     if (pad)
3808       return pad;
3809   }
3810
3811   /* then look in current chain if any */
3812   if (ogg->current_chain) {
3813     pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
3814     if (pad)
3815       return pad;
3816   }
3817
3818   for (i = 0; i < ogg->chains->len; i++) {
3819     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3820
3821     pad = gst_ogg_chain_get_stream (chain, serialno);
3822     if (pad)
3823       return pad;
3824   }
3825   return NULL;
3826 }
3827
3828 /* find a chain with a given serial number
3829  */
3830 static GstOggChain *
3831 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
3832 {
3833   GstOggPad *pad;
3834
3835   pad = gst_ogg_demux_find_pad (ogg, serialno);
3836   if (pad) {
3837     return pad->chain;
3838   }
3839   return NULL;
3840 }
3841
3842 /* returns TRUE if all streams have valid start time */
3843 static gboolean
3844 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
3845 {
3846   gboolean res = TRUE;
3847
3848   chain->total_time = GST_CLOCK_TIME_NONE;
3849   GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
3850
3851   /* see if we have a start time on all streams */
3852   chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
3853
3854   if (chain->segment_start == G_MAXUINT64) {
3855     /* not yet, stream some more data */
3856     res = FALSE;
3857   } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
3858     /* we can calculate a total time */
3859     chain->total_time = chain->segment_stop - chain->segment_start;
3860   }
3861
3862   GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
3863
3864   GST_DEBUG_OBJECT (ogg, "return %d", res);
3865
3866   return res;
3867 }
3868
3869 static void
3870 gst_ogg_demux_collect_info (GstOggDemux * ogg)
3871 {
3872   gint i;
3873
3874   /* collect all info */
3875   ogg->total_time = 0;
3876
3877   for (i = 0; i < ogg->chains->len; i++) {
3878     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3879
3880     chain->begin_time = ogg->total_time;
3881
3882     gst_ogg_demux_collect_chain_info (ogg, chain);
3883
3884     ogg->total_time += chain->total_time;
3885   }
3886   gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time);
3887 }
3888
3889 /* find all the chains in the ogg file, this reads the first and
3890  * last page of the ogg stream, if they match then the ogg file has
3891  * just one chain, else we do a binary search for all chains.
3892  */
3893 static GstFlowReturn
3894 gst_ogg_demux_find_chains (GstOggDemux * ogg)
3895 {
3896   ogg_page og;
3897   GstPad *peer;
3898   GstFormat format;
3899   gboolean res;
3900   guint32 serialno;
3901   GstOggChain *chain;
3902   GstFlowReturn ret;
3903
3904   /* get peer to figure out length */
3905   if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
3906     goto no_peer;
3907
3908   /* find length to read last page, we store this for later use. */
3909   format = GST_FORMAT_BYTES;
3910   res = gst_pad_query_duration (peer, &format, &ogg->length);
3911   gst_object_unref (peer);
3912   if (!res || ogg->length <= 0)
3913     goto no_length;
3914
3915   GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
3916
3917   /* read chain from offset 0, this is the first chain of the
3918    * ogg file. */
3919   gst_ogg_demux_seek (ogg, 0);
3920   ret = gst_ogg_demux_read_chain (ogg, &chain);
3921   if (ret != GST_FLOW_OK)
3922     goto no_first_chain;
3923
3924   /* read page from end offset, we use this page to check if its serial
3925    * number is contained in the first chain. If this is the case then
3926    * this ogg is not a chained ogg and we can skip the scanning. */
3927   gst_ogg_demux_seek (ogg, ogg->length);
3928   ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
3929   if (ret != GST_FLOW_OK)
3930     goto no_last_page;
3931
3932   serialno = ogg_page_serialno (&og);
3933
3934   if (!gst_ogg_chain_has_stream (chain, serialno)) {
3935     /* the last page is not in the first stream, this means we should
3936      * find all the chains in this chained ogg. */
3937     ret =
3938         gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
3939         0);
3940   } else {
3941     /* we still call this function here but with an empty range so that
3942      * we can reuse the setup code in this routine. */
3943     ret =
3944         gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
3945         ogg->length, chain, 0);
3946   }
3947   if (ret != GST_FLOW_OK)
3948     goto done;
3949
3950   /* all fine, collect and print */
3951   gst_ogg_demux_collect_info (ogg);
3952
3953   /* dump our chains and streams */
3954   gst_ogg_print (ogg);
3955
3956 done:
3957   return ret;
3958
3959   /*** error cases ***/
3960 no_peer:
3961   {
3962     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
3963     return GST_FLOW_NOT_LINKED;
3964   }
3965 no_length:
3966   {
3967     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
3968     return GST_FLOW_NOT_SUPPORTED;
3969   }
3970 no_first_chain:
3971   {
3972     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
3973     return GST_FLOW_ERROR;
3974   }
3975 no_last_page:
3976   {
3977     GST_DEBUG_OBJECT (ogg, "can't get last page");
3978     if (chain)
3979       gst_ogg_chain_free (chain);
3980     return ret;
3981   }
3982 }
3983
3984 static GstFlowReturn
3985 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
3986 {
3987   GstOggPad *pad;
3988   gint64 granule;
3989   guint32 serialno;
3990   GstFlowReturn result = GST_FLOW_OK;
3991
3992   serialno = ogg_page_serialno (page);
3993   granule = ogg_page_granulepos (page);
3994
3995   GST_LOG_OBJECT (ogg,
3996       "processing ogg page (serial %08x, "
3997       "pageno %ld, granulepos %" G_GINT64_FORMAT ", bos %d)", serialno,
3998       ogg_page_pageno (page), granule, ogg_page_bos (page));
3999
4000   if (ogg_page_bos (page)) {
4001     GstOggChain *chain;
4002
4003     /* first page */
4004     /* see if we know about the chain already */
4005     chain = gst_ogg_demux_find_chain (ogg, serialno);
4006     if (chain) {
4007       GstEvent *event;
4008       gint64 start = 0;
4009
4010       if (chain->segment_start != GST_CLOCK_TIME_NONE)
4011         start = chain->segment_start;
4012
4013       /* create the newsegment event we are going to send out */
4014       event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
4015           GST_FORMAT_TIME, start, chain->segment_stop, chain->begin_time);
4016       gst_event_set_seqnum (event, ogg->seqnum);
4017
4018       GST_DEBUG_OBJECT (ogg,
4019           "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
4020           ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
4021           GST_TIME_ARGS (chain->segment_stop),
4022           GST_TIME_ARGS (chain->begin_time));
4023
4024       /* activate it as it means we have a non-header, this will also deactivate
4025        * the currently running chain. */
4026       gst_ogg_demux_activate_chain (ogg, chain, event);
4027       pad = gst_ogg_demux_find_pad (ogg, serialno);
4028     } else {
4029       GstClockTime chain_time;
4030       gint64 current_time;
4031
4032       /* this can only happen in push mode */
4033       if (ogg->pullmode)
4034         goto unknown_chain;
4035
4036       current_time = ogg->segment.last_stop;
4037
4038       /* time of new chain is current time */
4039       chain_time = current_time;
4040
4041       if (ogg->building_chain == NULL) {
4042         GstOggChain *newchain;
4043
4044         newchain = gst_ogg_chain_new (ogg);
4045         newchain->offset = 0;
4046         /* set new chain begin time aligned with end time of old chain */
4047         newchain->begin_time = chain_time;
4048         GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
4049             GST_TIME_ARGS (chain_time));
4050
4051         /* and this is the one we are building now */
4052         ogg->building_chain = newchain;
4053       }
4054       pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
4055     }
4056   } else {
4057     pad = gst_ogg_demux_find_pad (ogg, serialno);
4058   }
4059   if (pad) {
4060     result = gst_ogg_pad_submit_page (pad, page);
4061   } else {
4062     GST_PUSH_LOCK (ogg);
4063     if (!ogg->pullmode && !ogg->push_disable_seeking) {
4064       /* no pad while probing for duration, we must have a chained stream,
4065          and we don't support them, so back off */
4066       GST_INFO_OBJECT (ogg, "We seem to have a chained stream, we won't seek");
4067       if (ogg->push_state == PUSH_DURATION) {
4068         GstFlowReturn res;
4069
4070         res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
4071         if (res != GST_FLOW_OK)
4072           return res;
4073       }
4074
4075       /* only once we seeked back */
4076       GST_PUSH_LOCK (ogg);
4077       ogg->push_disable_seeking = TRUE;
4078     } else {
4079       GST_PUSH_UNLOCK (ogg);
4080       /* no pad. This means an ogg page without bos has been seen for this
4081        * serialno. we just ignore it but post a warning... */
4082       GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
4083           (NULL), ("unknown ogg pad for serial %08x detected", serialno));
4084       return GST_FLOW_OK;
4085     }
4086     GST_PUSH_UNLOCK (ogg);
4087   }
4088   return result;
4089
4090   /* ERRORS */
4091 unknown_chain:
4092   {
4093     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
4094         (NULL), ("unknown ogg chain for serial %08x detected", serialno));
4095     return GST_FLOW_ERROR;
4096   }
4097 }
4098
4099 /* streaming mode, receive a buffer, parse it, create pads for
4100  * the serialno, submit pages and packets to the oggpads
4101  */
4102 static GstFlowReturn
4103 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
4104 {
4105   GstOggDemux *ogg;
4106   gint ret = 0;
4107   GstFlowReturn result = GST_FLOW_OK;
4108
4109   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4110
4111   GST_DEBUG_OBJECT (ogg, "enter");
4112   result = gst_ogg_demux_submit_buffer (ogg, buffer);
4113   if (result < 0) {
4114     GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
4115   }
4116
4117   while (result == GST_FLOW_OK) {
4118     ogg_page page;
4119
4120     ret = ogg_sync_pageout (&ogg->sync, &page);
4121     if (ret == 0)
4122       /* need more data */
4123       break;
4124     if (ret == -1) {
4125       /* discontinuity in the pages */
4126       GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
4127     } else {
4128       result = gst_ogg_demux_handle_page (ogg, &page);
4129       if (result < 0) {
4130         GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
4131       }
4132     }
4133   }
4134   if (ret == 0 || result == GST_FLOW_OK) {
4135     gst_ogg_demux_sync_streams (ogg);
4136   }
4137   GST_DEBUG_OBJECT (ogg, "leave with %d", result);
4138   return result;
4139 }
4140
4141 static gboolean
4142 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
4143 {
4144   GstOggChain *chain = ogg->current_chain;
4145   gboolean res = TRUE;
4146
4147   if (!chain)
4148     chain = ogg->building_chain;
4149
4150   if (chain) {
4151     gint i;
4152
4153     for (i = 0; i < chain->streams->len; i++) {
4154       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4155
4156       gst_event_ref (event);
4157       GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
4158       res &= gst_pad_push_event (GST_PAD (pad), event);
4159     }
4160   } else {
4161     GST_WARNING_OBJECT (ogg, "No chain to forward event to");
4162   }
4163   gst_event_unref (event);
4164
4165   return res;
4166 }
4167
4168 static GstFlowReturn
4169 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
4170     GstFlowReturn ret)
4171 {
4172   GstOggChain *chain;
4173
4174   /* store the value */
4175   pad->last_ret = ret;
4176
4177   /* any other error that is not-linked can be returned right
4178    * away */
4179   if (ret != GST_FLOW_NOT_LINKED)
4180     goto done;
4181
4182   /* only return NOT_LINKED if all other pads returned NOT_LINKED */
4183   chain = ogg->current_chain;
4184   if (chain) {
4185     gint i;
4186
4187     for (i = 0; i < chain->streams->len; i++) {
4188       GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4189
4190       ret = opad->last_ret;
4191       /* some other return value (must be SUCCESS but we can return
4192        * other values as well) */
4193       if (ret != GST_FLOW_NOT_LINKED)
4194         goto done;
4195     }
4196     /* if we get here, all other pads were unlinked and we return
4197      * NOT_LINKED then */
4198   }
4199 done:
4200   return ret;
4201 }
4202
4203 /* returns TRUE if all streams in current chain reached EOS, FALSE otherwise */
4204 static gboolean
4205 gst_ogg_demux_check_eos (GstOggDemux * ogg)
4206 {
4207   GstOggChain *chain;
4208   gboolean eos = TRUE;
4209
4210   chain = ogg->current_chain;
4211   if (G_LIKELY (chain)) {
4212     gint i;
4213
4214     for (i = 0; i < chain->streams->len; i++) {
4215       GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4216
4217       eos = eos && opad->is_eos;
4218     }
4219   } else {
4220     eos = FALSE;
4221   }
4222
4223   return eos;
4224 }
4225
4226 static GstFlowReturn
4227 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4228 {
4229   GstFlowReturn ret;
4230   GstBuffer *buffer;
4231
4232   if (ogg->offset == ogg->length) {
4233     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4234         " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
4235     ret = GST_FLOW_UNEXPECTED;
4236     goto done;
4237   }
4238
4239   GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4240   ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
4241   if (ret != GST_FLOW_OK) {
4242     GST_LOG_OBJECT (ogg, "Failed pull_range");
4243     goto done;
4244   }
4245
4246   ogg->offset += GST_BUFFER_SIZE (buffer);
4247
4248   if (G_UNLIKELY (ogg->newsegment)) {
4249     gst_ogg_demux_send_event (ogg, ogg->newsegment);
4250     ogg->newsegment = NULL;
4251   }
4252
4253   ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
4254   if (ret != GST_FLOW_OK) {
4255     GST_LOG_OBJECT (ogg, "Failed demux_chain");
4256     goto done;
4257   }
4258
4259   /* check for the end of the segment */
4260   if (gst_ogg_demux_check_eos (ogg)) {
4261     GST_LOG_OBJECT (ogg, "got EOS");
4262     ret = GST_FLOW_UNEXPECTED;
4263     goto done;
4264   }
4265 done:
4266   return ret;
4267 }
4268
4269 /* reverse mode.
4270  *
4271  * We read the pages backwards and send the packets forwards. The first packet
4272  * in the page will be pushed with the DISCONT flag set.
4273  *
4274  * Special care has to be taken for continued pages, which we can only decode
4275  * when we have the previous page(s).
4276  */
4277 static GstFlowReturn
4278 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4279 {
4280   GstFlowReturn ret;
4281   ogg_page page;
4282   gint64 offset;
4283
4284   if (ogg->offset == 0) {
4285     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4286         " == 0", ogg->offset);
4287     ret = GST_FLOW_UNEXPECTED;
4288     goto done;
4289   }
4290
4291   GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
4292   ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
4293   if (ret != GST_FLOW_OK)
4294     goto done;
4295
4296   ogg->offset = offset;
4297
4298   if (G_UNLIKELY (ogg->newsegment)) {
4299     gst_ogg_demux_send_event (ogg, ogg->newsegment);
4300     ogg->newsegment = NULL;
4301   }
4302
4303   ret = gst_ogg_demux_handle_page (ogg, &page);
4304   if (ret != GST_FLOW_OK)
4305     goto done;
4306
4307   /* check for the end of the segment */
4308   if (gst_ogg_demux_check_eos (ogg)) {
4309     GST_LOG_OBJECT (ogg, "got EOS");
4310     ret = GST_FLOW_UNEXPECTED;
4311     goto done;
4312   }
4313 done:
4314   return ret;
4315 }
4316
4317 static void
4318 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4319 {
4320   GstClockTime cur;
4321   GstOggChain *chain;
4322   guint i;
4323
4324   chain = ogg->current_chain;
4325   cur = ogg->segment.last_stop;
4326   if (chain == NULL || cur == -1)
4327     return;
4328
4329   for (i = 0; i < chain->streams->len; i++) {
4330     GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4331
4332     /* Theoretically, we should be doing this for all streams, but we're only
4333      * doing it for known-to-be-sparse streams at the moment in order not to
4334      * break things for wrongly-muxed streams (like we used to produce once) */
4335     if (stream->map.is_sparse && stream->last_stop != GST_CLOCK_TIME_NONE) {
4336
4337       /* Does this stream lag? Random threshold of 2 seconds */
4338       if (GST_CLOCK_DIFF (stream->last_stop, cur) > (2 * GST_SECOND)) {
4339         GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
4340             "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
4341             GST_TIME_ARGS (stream->last_stop), GST_TIME_ARGS (cur));
4342         stream->last_stop = cur;
4343         /* advance stream time (FIXME: is this right, esp. time_pos?) */
4344         gst_pad_push_event (GST_PAD_CAST (stream),
4345             gst_event_new_new_segment (TRUE, ogg->segment.rate,
4346                 GST_FORMAT_TIME, stream->last_stop, -1, stream->last_stop));
4347       }
4348     }
4349   }
4350 }
4351
4352 /* random access code
4353  *
4354  * - first find all the chains and streams by scanning the file.
4355  * - then get and chain buffers, just like the streaming case.
4356  * - when seeking, we can use the chain info to perform the seek.
4357  */
4358 static void
4359 gst_ogg_demux_loop (GstOggPad * pad)
4360 {
4361   GstOggDemux *ogg;
4362   GstFlowReturn ret;
4363   GstEvent *event;
4364
4365   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4366
4367   if (ogg->need_chains) {
4368     gboolean res;
4369
4370     /* this is the only place where we write chains and thus need to lock. */
4371     GST_CHAIN_LOCK (ogg);
4372     ret = gst_ogg_demux_find_chains (ogg);
4373     GST_CHAIN_UNLOCK (ogg);
4374     if (ret != GST_FLOW_OK)
4375       goto chain_read_failed;
4376
4377     ogg->need_chains = FALSE;
4378
4379     GST_OBJECT_LOCK (ogg);
4380     ogg->running = TRUE;
4381     event = ogg->event;
4382     ogg->event = NULL;
4383     GST_OBJECT_UNLOCK (ogg);
4384
4385     /* and seek to configured positions without FLUSH */
4386     res = gst_ogg_demux_perform_seek_pull (ogg, event);
4387     if (event)
4388       gst_event_unref (event);
4389
4390     if (!res)
4391       goto seek_failed;
4392   }
4393
4394   if (ogg->segment.rate >= 0.0)
4395     ret = gst_ogg_demux_loop_forward (ogg);
4396   else
4397     ret = gst_ogg_demux_loop_reverse (ogg);
4398
4399   if (ret != GST_FLOW_OK)
4400     goto pause;
4401
4402   gst_ogg_demux_sync_streams (ogg);
4403   return;
4404
4405   /* ERRORS */
4406 chain_read_failed:
4407   {
4408     /* error was posted */
4409     goto pause;
4410   }
4411 seek_failed:
4412   {
4413     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4414         ("failed to start demuxing ogg"));
4415     ret = GST_FLOW_ERROR;
4416     goto pause;
4417   }
4418 pause:
4419   {
4420     const gchar *reason = gst_flow_get_name (ret);
4421     GstEvent *event = NULL;
4422
4423     GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4424     ogg->segment_running = FALSE;
4425     gst_pad_pause_task (ogg->sinkpad);
4426
4427     if (ret == GST_FLOW_UNEXPECTED) {
4428       /* perform EOS logic */
4429       if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4430         gint64 stop;
4431         GstMessage *message;
4432
4433         /* for segment playback we need to post when (in stream time)
4434          * we stopped, this is either stop (when set) or the duration. */
4435         if ((stop = ogg->segment.stop) == -1)
4436           stop = ogg->segment.duration;
4437
4438         GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
4439         message =
4440             gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
4441             stop);
4442         gst_message_set_seqnum (message, ogg->seqnum);
4443
4444         gst_element_post_message (GST_ELEMENT (ogg), message);
4445       } else {
4446         /* normal playback, send EOS to all linked pads */
4447         GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
4448         event = gst_event_new_eos ();
4449       }
4450     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) {
4451       GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
4452           (_("Internal data stream error.")),
4453           ("stream stopped, reason %s", reason));
4454       event = gst_event_new_eos ();
4455     }
4456
4457     /* For wrong-state we still want to pause the task and stop
4458      * but no error message or other things are necessary.
4459      * wrong-state is no real error and will be caused by flushing,
4460      * e.g. because of a flushing seek.
4461      */
4462     if (event) {
4463       gst_event_set_seqnum (event, ogg->seqnum);
4464       gst_ogg_demux_send_event (ogg, event);
4465     }
4466     return;
4467   }
4468 }
4469
4470 static void
4471 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
4472 {
4473   gint i;
4474
4475   gst_ogg_demux_deactivate_current_chain (ogg);
4476
4477   GST_CHAIN_LOCK (ogg);
4478   for (i = 0; i < ogg->chains->len; i++) {
4479     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4480
4481     gst_ogg_chain_free (chain);
4482   }
4483   ogg->chains = g_array_set_size (ogg->chains, 0);
4484   GST_CHAIN_UNLOCK (ogg);
4485 }
4486
4487 /* this function is called when the pad is activated and should start
4488  * processing data.
4489  *
4490  * We check if we can do random access to decide if we work push or
4491  * pull based.
4492  */
4493 static gboolean
4494 gst_ogg_demux_sink_activate (GstPad * sinkpad)
4495 {
4496   if (gst_pad_check_pull_range (sinkpad)) {
4497     GST_DEBUG_OBJECT (sinkpad, "activating pull");
4498     return gst_pad_activate_pull (sinkpad, TRUE);
4499   } else {
4500     GST_DEBUG_OBJECT (sinkpad, "activating push");
4501     return gst_pad_activate_push (sinkpad, TRUE);
4502   }
4503 }
4504
4505 /* this function gets called when we activate ourselves in push mode.
4506  * We cannot seek (ourselves) in the stream */
4507 static gboolean
4508 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
4509 {
4510   GstOggDemux *ogg;
4511
4512   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
4513
4514   ogg->pullmode = FALSE;
4515   ogg->resync = FALSE;
4516
4517   return TRUE;
4518 }
4519
4520 /* this function gets called when we activate ourselves in pull mode.
4521  * We can perform  random access to the resource and we start a task
4522  * to start reading */
4523 static gboolean
4524 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
4525 {
4526   GstOggDemux *ogg;
4527
4528   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
4529
4530   if (active) {
4531     ogg->need_chains = TRUE;
4532     ogg->pullmode = TRUE;
4533
4534     return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
4535         sinkpad);
4536   } else {
4537     return gst_pad_stop_task (sinkpad);
4538   }
4539 }
4540
4541 static GstStateChangeReturn
4542 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
4543 {
4544   GstOggDemux *ogg;
4545   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
4546
4547   ogg = GST_OGG_DEMUX (element);
4548
4549   switch (transition) {
4550     case GST_STATE_CHANGE_NULL_TO_READY:
4551       ogg->basetime = 0;
4552       ogg_sync_init (&ogg->sync);
4553       break;
4554     case GST_STATE_CHANGE_READY_TO_PAUSED:
4555       ogg_sync_reset (&ogg->sync);
4556       ogg->running = FALSE;
4557       ogg->bitrate = 0;
4558       ogg->segment_running = FALSE;
4559       ogg->total_time = -1;
4560       GST_PUSH_LOCK (ogg);
4561       ogg->push_byte_offset = 0;
4562       ogg->push_byte_length = -1;
4563       ogg->push_time_length = GST_CLOCK_TIME_NONE;
4564       ogg->push_time_offset = GST_CLOCK_TIME_NONE;
4565       ogg->push_disable_seeking = FALSE;
4566       ogg->push_state = PUSH_PLAYING;
4567       GST_PUSH_UNLOCK (ogg);
4568       gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
4569       break;
4570     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4571       break;
4572     default:
4573       break;
4574   }
4575
4576   result = parent_class->change_state (element, transition);
4577
4578   switch (transition) {
4579     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4580       break;
4581     case GST_STATE_CHANGE_PAUSED_TO_READY:
4582       gst_ogg_demux_clear_chains (ogg);
4583       GST_OBJECT_LOCK (ogg);
4584       ogg->running = FALSE;
4585       ogg->segment_running = FALSE;
4586       GST_OBJECT_UNLOCK (ogg);
4587       break;
4588     case GST_STATE_CHANGE_READY_TO_NULL:
4589       ogg_sync_clear (&ogg->sync);
4590       break;
4591     default:
4592       break;
4593   }
4594   return result;
4595 }
4596
4597 gboolean
4598 gst_ogg_demux_plugin_init (GstPlugin * plugin)
4599 {
4600   GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
4601   GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
4602       "ogg demuxer setup stage when parsing pipeline");
4603
4604 #ifdef ENABLE_NLS
4605   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
4606       LOCALEDIR);
4607   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4608   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4609 #endif
4610
4611   return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
4612       GST_TYPE_OGG_DEMUX);
4613 }
4614
4615 /* prints all info about the element */
4616 #undef GST_CAT_DEFAULT
4617 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
4618
4619 #ifdef GST_DISABLE_GST_DEBUG
4620
4621 static void
4622 gst_ogg_print (GstOggDemux * ogg)
4623 {
4624   /* NOP */
4625 }
4626
4627 #else /* !GST_DISABLE_GST_DEBUG */
4628
4629 static void
4630 gst_ogg_print (GstOggDemux * ogg)
4631 {
4632   guint j, i;
4633
4634   GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
4635   GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4636       GST_TIME_ARGS (ogg->total_time));
4637
4638   for (i = 0; i < ogg->chains->len; i++) {
4639     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4640
4641     GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
4642     GST_INFO_OBJECT (ogg,
4643         "  offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
4644         chain->end_offset);
4645     GST_INFO_OBJECT (ogg, "  begin time: %" GST_TIME_FORMAT,
4646         GST_TIME_ARGS (chain->begin_time));
4647     GST_INFO_OBJECT (ogg, "  total time: %" GST_TIME_FORMAT,
4648         GST_TIME_ARGS (chain->total_time));
4649     GST_INFO_OBJECT (ogg, "  segment start: %" GST_TIME_FORMAT,
4650         GST_TIME_ARGS (chain->segment_start));
4651     GST_INFO_OBJECT (ogg, "  segment stop:  %" GST_TIME_FORMAT,
4652         GST_TIME_ARGS (chain->segment_stop));
4653
4654     for (j = 0; j < chain->streams->len; j++) {
4655       GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
4656
4657       GST_INFO_OBJECT (ogg, "  stream %08x: %s", stream->map.serialno,
4658           gst_ogg_stream_get_media_type (&stream->map));
4659       GST_INFO_OBJECT (ogg, "   start time:       %" GST_TIME_FORMAT,
4660           GST_TIME_ARGS (stream->start_time));
4661     }
4662   }
4663 }
4664 #endif /* GST_DISABLE_GST_DEBUG */