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