tizen 2.3 release
[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     if (ogg->push_byte_length > EOS_AVOIDANCE_THRESHOLD)
3250       position = ogg->push_byte_length - (EOS_AVOIDANCE_THRESHOLD / 2);
3251    else
3252       position = 0;
3253   }
3254
3255   GST_DEBUG_OBJECT (ogg,
3256       "Getting duration, seeking near the end, to %" G_GINT64_FORMAT, position);
3257   ogg->push_state = PUSH_DURATION;
3258   /* do not read the last byte */
3259   sevent = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET,
3260       position, GST_SEEK_TYPE_SET, ogg->push_byte_length - 1);
3261   res = gst_pad_push_event (ogg->sinkpad, sevent);
3262   if (res) {
3263     GST_DEBUG_OBJECT (ogg, "Seek succesful");
3264     return TRUE;
3265   } else {
3266     GST_INFO_OBJECT (ogg, "Seek failed, duration will stay unknown");
3267     ogg->push_state = PUSH_PLAYING;
3268     ogg->push_disable_seeking = TRUE;
3269     return FALSE;
3270   }
3271 }
3272
3273 static gboolean
3274 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3275     GstEvent * event)
3276 {
3277   if (ogg->push_byte_length < 0) {
3278     GstPad *peer;
3279
3280     GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3281     if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3282       GstFormat format = GST_FORMAT_BYTES;
3283       gint64 length;
3284       int res;
3285
3286       res = gst_pad_query_duration (peer, &format, &length);
3287       if (res && length > 0) {
3288         ogg->push_byte_length = length;
3289         GST_DEBUG_OBJECT (ogg,
3290             "File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
3291       } else {
3292         GST_DEBUG_OBJECT (ogg, "File byte length unknown, assuming live");
3293         ogg->push_disable_seeking = TRUE;
3294         return TRUE;
3295       }
3296       format = GST_FORMAT_TIME;
3297       res = gst_pad_query_duration (peer, &format, &length);
3298       gst_object_unref (peer);
3299       if (res && length >= 0) {
3300         ogg->push_time_length = length;
3301         GST_DEBUG_OBJECT (ogg, "File time length %" GST_TIME_FORMAT,
3302             GST_TIME_ARGS (ogg->push_time_length));
3303       } else if (!ogg->push_disable_seeking) {
3304         gboolean res;
3305
3306         res = gst_ogg_demux_get_duration_push (ogg, flags);
3307         if (res) {
3308           GST_DEBUG_OBJECT (ogg,
3309               "File time length unknown, trying to determine");
3310           ogg->push_mode_seek_delayed_event = NULL;
3311           if (event) {
3312             GST_DEBUG_OBJECT (ogg,
3313                 "Let me intercept this innocent looking seek request");
3314             ogg->push_mode_seek_delayed_event = gst_event_copy (event);
3315           }
3316           return FALSE;
3317         }
3318       }
3319     }
3320   }
3321   return TRUE;
3322 }
3323
3324 static gboolean
3325 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3326 {
3327   gint bitrate;
3328   gboolean res = TRUE;
3329   GstFormat format;
3330   gdouble rate;
3331   GstSeekFlags flags;
3332   GstSeekType start_type, stop_type;
3333   gint64 start, stop;
3334   GstEvent *sevent;
3335   GstOggChain *chain;
3336   gint64 best, best_time;
3337   gint i;
3338
3339   GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3340
3341   gst_event_parse_seek (event, &rate, &format, &flags,
3342       &start_type, &start, &stop_type, &stop);
3343
3344   if (format != GST_FORMAT_TIME) {
3345     GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3346     goto error;
3347   }
3348
3349   if (start_type != GST_SEEK_TYPE_SET) {
3350     GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3351     goto error;
3352   }
3353
3354   if (!(flags & GST_SEEK_FLAG_FLUSH)) {
3355     GST_DEBUG_OBJECT (ogg, "can only do flushing seeks");
3356     goto error;
3357   }
3358
3359   GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3360       GST_TIME_ARGS (start));
3361
3362   chain = ogg->current_chain;
3363   if (!chain) {
3364     GST_WARNING_OBJECT (ogg, "No chain to seek on");
3365     goto error;
3366   }
3367
3368   /* start accessing push_* members */
3369   GST_PUSH_LOCK (ogg);
3370
3371   /* not if we disabled seeking (chained streams) */
3372   if (ogg->push_disable_seeking) {
3373     GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3374     goto error_locked;
3375   }
3376
3377   /* not when we're trying to work out duration */
3378   if (ogg->push_state == PUSH_DURATION) {
3379     GST_DEBUG_OBJECT (ogg, "Busy working out duration, try again later");
3380     goto error_locked;
3381   }
3382
3383   /* actually, not if we're doing any seeking already */
3384   if (ogg->push_state != PUSH_PLAYING) {
3385     GST_DEBUG_OBJECT (ogg, "Already doing some seeking, try again later");
3386     goto error_locked;
3387   }
3388
3389   /* on the first seek, get length if we can */
3390   if (!gst_ogg_demux_check_duration_push (ogg, flags, event)) {
3391     GST_PUSH_UNLOCK (ogg);
3392     return FALSE;
3393   }
3394
3395   if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
3396     /* the index gave some result */
3397     GST_DEBUG_OBJECT (ogg,
3398         "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
3399         best, best_time);
3400   } else {
3401     if (ogg->push_time_length > 0) {
3402       /* if we know the time length, we know the full segment bitrate */
3403       GST_DEBUG_OBJECT (ogg, "Using real file bitrate");
3404       bitrate =
3405           gst_util_uint64_scale (ogg->push_byte_length, 8 * GST_SECOND,
3406           ogg->push_time_length);
3407     } else if (ogg->push_time_offset > 0) {
3408       /* get a first approximation using known bitrate to the current position */
3409       GST_DEBUG_OBJECT (ogg, "Using file bitrate so far");
3410       bitrate =
3411           gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
3412           ogg->push_time_offset);
3413     } else if (ogg->bitrate > 0) {
3414       /* nominal bitrate is better than nothing, even if it lies often */
3415       GST_DEBUG_OBJECT (ogg, "Using nominal bitrate");
3416       bitrate = ogg->bitrate;
3417     } else {
3418       /* meh */
3419       GST_DEBUG_OBJECT (ogg,
3420           "At stream start, and no nominal bitrate, using some random magic "
3421           "number to seed");
3422       /* the bisection, once started, should give us a better approximation */
3423       bitrate = 1000;
3424     }
3425     best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3426   }
3427
3428   /* offset by typical page length, and ensure our best guess is within
3429      reasonable bounds */
3430   best -= CHUNKSIZE;
3431   if (best < 0)
3432     best = 0;
3433   if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3434     best = ogg->push_byte_length - 1;
3435
3436   /* set up bisection search */
3437   ogg->push_offset0 = 0;
3438   ogg->push_offset1 = ogg->push_byte_length - 1;
3439   ogg->push_time0 = ogg->push_start_time;
3440   ogg->push_time1 = ogg->push_time_length;
3441   ogg->push_seek_time_target = start;
3442   ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
3443   ogg->push_seek_time_original_target = start;
3444   ogg->push_state = PUSH_BISECT1;
3445   ogg->seek_secant = FALSE;
3446   ogg->seek_undershot = FALSE;
3447
3448   /* reset pad push mode seeking state */
3449   for (i = 0; i < chain->streams->len; i++) {
3450     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3451     pad->push_kf_time = GST_CLOCK_TIME_NONE;
3452     pad->push_sync_time = GST_CLOCK_TIME_NONE;
3453   }
3454
3455   GST_DEBUG_OBJECT (ogg,
3456       "Setting up bisection search for %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
3457       " (time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", ogg->push_offset0,
3458       ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
3459       GST_TIME_ARGS (ogg->push_time1));
3460   GST_DEBUG_OBJECT (ogg,
3461       "Target time is %" GST_TIME_FORMAT ", best first guess is %"
3462       G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target), best);
3463
3464   ogg->push_seek_rate = rate;
3465   ogg->push_seek_flags = flags;
3466   ogg->push_mode_seek_delayed_event = NULL;
3467   ogg->push_bisection_steps[0] = 1;
3468   ogg->push_bisection_steps[1] = 0;
3469   sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
3470       start_type, best, GST_SEEK_TYPE_NONE, -1);
3471
3472   GST_PUSH_UNLOCK (ogg);
3473   res = gst_pad_push_event (ogg->sinkpad, sevent);
3474
3475   return res;
3476
3477   /* ERRORS */
3478 error:
3479   {
3480     GST_DEBUG_OBJECT (ogg, "seek failed");
3481     return FALSE;
3482   }
3483
3484 error_locked:
3485   GST_PUSH_UNLOCK (ogg);
3486   goto error;
3487 }
3488
3489 static gboolean
3490 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
3491 {
3492   gboolean res;
3493
3494   if (ogg->pullmode) {
3495     res = gst_ogg_demux_perform_seek_pull (ogg, event);
3496   } else {
3497     res = gst_ogg_demux_perform_seek_push (ogg, event);
3498   }
3499   return res;
3500 }
3501
3502
3503 /* finds each bitstream link one at a time using a bisection search
3504  * (has to begin by knowing the offset of the lb's initial page).
3505  * Recurses for each link so it can alloc the link storage after
3506  * finding them all, then unroll and fill the cache at the same time
3507  */
3508 static GstFlowReturn
3509 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
3510     gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
3511 {
3512   gint64 endsearched = end;
3513   gint64 next = end;
3514   ogg_page og;
3515   GstFlowReturn ret;
3516   gint64 offset;
3517   GstOggChain *nextchain;
3518
3519   GST_LOG_OBJECT (ogg,
3520       "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
3521       ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
3522
3523   /* the below guards against garbage seperating the last and
3524    * first pages of two links. */
3525   while (searched < endsearched) {
3526     gint64 bisect;
3527
3528     if (endsearched - searched < CHUNKSIZE) {
3529       bisect = searched;
3530     } else {
3531       bisect = (searched + endsearched) / 2;
3532     }
3533
3534     gst_ogg_demux_seek (ogg, bisect);
3535     ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
3536
3537     if (ret == GST_FLOW_UNEXPECTED) {
3538       endsearched = bisect;
3539     } else if (ret == GST_FLOW_OK) {
3540       guint32 serial = ogg_page_serialno (&og);
3541
3542       if (!gst_ogg_chain_has_stream (chain, serial)) {
3543         endsearched = bisect;
3544         next = offset;
3545       } else {
3546         searched = offset + og.header_len + og.body_len;
3547       }
3548     } else
3549       return ret;
3550   }
3551
3552   GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
3553
3554   chain->end_offset = searched;
3555   ret = gst_ogg_demux_read_end_chain (ogg, chain);
3556   if (ret != GST_FLOW_OK)
3557     return ret;
3558
3559   GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
3560
3561   gst_ogg_demux_seek (ogg, next);
3562   ret = gst_ogg_demux_read_chain (ogg, &nextchain);
3563   if (ret == GST_FLOW_UNEXPECTED) {
3564     nextchain = NULL;
3565     ret = GST_FLOW_OK;
3566     GST_LOG_OBJECT (ogg, "no next chain");
3567   } else if (ret != GST_FLOW_OK)
3568     goto done;
3569
3570   if (searched < end && nextchain != NULL) {
3571     ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
3572         end, nextchain, m + 1);
3573     if (ret != GST_FLOW_OK)
3574       goto done;
3575   }
3576   GST_LOG_OBJECT (ogg, "adding chain %p", chain);
3577
3578   g_array_insert_val (ogg->chains, 0, chain);
3579
3580 done:
3581   return ret;
3582 }
3583
3584 /* read a chain from the ogg file. This code will
3585  * read all BOS pages and will create and return a GstOggChain 
3586  * structure with the results. 
3587  * 
3588  * This function will also read N pages from each stream in the
3589  * chain and submit them to the decoders. When the decoder has
3590  * decoded the first buffer, we know the timestamp of the first
3591  * page in the chain.
3592  */
3593 static GstFlowReturn
3594 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
3595 {
3596   GstFlowReturn ret;
3597   GstOggChain *chain = NULL;
3598   gint64 offset = ogg->offset;
3599   ogg_page og;
3600   gboolean done;
3601   gint i;
3602
3603   GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
3604
3605   /* first read the BOS pages, do typefind on them, create
3606    * the decoders, send data to the decoders. */
3607   while (TRUE) {
3608     GstOggPad *pad;
3609     guint32 serial;
3610
3611     ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3612     if (ret != GST_FLOW_OK) {
3613       if (ret == GST_FLOW_UNEXPECTED) {
3614         GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
3615       } else {
3616         GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
3617       }
3618       break;
3619     }
3620     if (!ogg_page_bos (&og)) {
3621       GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
3622       /* if we did not find a chain yet, assume this is a bogus stream and
3623        * ignore it */
3624       if (!chain) {
3625         GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
3626         ret = GST_FLOW_UNEXPECTED;
3627       }
3628       break;
3629     }
3630
3631     if (chain == NULL) {
3632       chain = gst_ogg_chain_new (ogg);
3633       chain->offset = offset;
3634     }
3635
3636     serial = ogg_page_serialno (&og);
3637     if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
3638       GST_WARNING_OBJECT (ogg,
3639           "found serial %08x BOS page twice, ignoring", serial);
3640       continue;
3641     }
3642
3643     pad = gst_ogg_chain_new_stream (chain, serial);
3644     gst_ogg_pad_submit_page (pad, &og);
3645   }
3646
3647   if (ret != GST_FLOW_OK || chain == NULL) {
3648     if (ret == GST_FLOW_OK) {
3649       GST_WARNING_OBJECT (ogg, "no chain was found");
3650       ret = GST_FLOW_ERROR;
3651     } else if (ret != GST_FLOW_UNEXPECTED) {
3652       GST_WARNING_OBJECT (ogg, "failed to read chain");
3653     } else {
3654       GST_DEBUG_OBJECT (ogg, "done reading chains");
3655     }
3656     if (chain) {
3657       gst_ogg_chain_free (chain);
3658     }
3659     if (res_chain)
3660       *res_chain = NULL;
3661     return ret;
3662   }
3663
3664   chain->have_bos = TRUE;
3665   GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
3666
3667   /* now read pages until we receive a buffer from each of the
3668    * stream decoders, this will tell us the timestamp of the
3669    * first packet in the chain then */
3670
3671   /* save the offset to the first non bos page in the chain: if searching for
3672    * pad->first_time we read past the end of the chain, we'll seek back to this
3673    * position
3674    */
3675   offset = ogg->offset;
3676
3677   done = FALSE;
3678   while (!done) {
3679     guint32 serial;
3680     gboolean known_serial = FALSE;
3681     GstFlowReturn ret;
3682
3683     serial = ogg_page_serialno (&og);
3684     done = TRUE;
3685     for (i = 0; i < chain->streams->len; i++) {
3686       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3687
3688       GST_LOG_OBJECT (ogg,
3689           "serial %08x time %" GST_TIME_FORMAT,
3690           pad->map.serialno, GST_TIME_ARGS (pad->start_time));
3691
3692       if (pad->map.serialno == serial) {
3693         known_serial = TRUE;
3694
3695         /* submit the page now, this will fill in the start_time when the
3696          * internal decoder finds it */
3697         gst_ogg_pad_submit_page (pad, &og);
3698
3699         if (!pad->map.is_skeleton && pad->start_time == -1
3700             && ogg_page_eos (&og)) {
3701           /* got EOS on a pad before we could find its start_time.
3702            * We have no chance of finding a start_time for every pad so
3703            * stop searching for the other start_time(s).
3704            */
3705           done = TRUE;
3706           break;
3707         }
3708       }
3709       /* the timestamp will be filled in when we submit the pages */
3710       if (!pad->map.is_sparse)
3711         done &= (pad->start_time != GST_CLOCK_TIME_NONE);
3712
3713       GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
3714     }
3715
3716     /* we read a page not belonging to the current chain: seek back to the
3717      * beginning of the chain
3718      */
3719     if (!known_serial) {
3720       GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
3721       gst_ogg_demux_seek (ogg, offset);
3722       break;
3723     }
3724
3725     if (!done) {
3726       ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3727       if (ret != GST_FLOW_OK)
3728         break;
3729     }
3730   }
3731   GST_LOG_OBJECT (ogg, "done reading chain");
3732   /* now we can fill in the missing info using queries */
3733   for (i = 0; i < chain->streams->len; i++) {
3734     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3735
3736     if (pad->map.is_skeleton)
3737       continue;
3738
3739     pad->mode = GST_OGG_PAD_MODE_STREAMING;
3740   }
3741
3742   if (res_chain)
3743     *res_chain = chain;
3744
3745   return GST_FLOW_OK;
3746 }
3747
3748 /* read the last pages from the ogg stream to get the final
3749  * page end_offsets.
3750  */
3751 static GstFlowReturn
3752 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
3753 {
3754   gint64 begin = chain->end_offset;
3755   gint64 end = begin;
3756   gint64 last_granule = -1;
3757   GstOggPad *last_pad = NULL;
3758   GstFlowReturn ret;
3759   gboolean done = FALSE;
3760   ogg_page og;
3761   gint i;
3762
3763   while (!done) {
3764     begin -= CHUNKSIZE;
3765     if (begin < 0)
3766       begin = 0;
3767
3768     gst_ogg_demux_seek (ogg, begin);
3769
3770     /* now continue reading until we run out of data, if we find a page
3771      * start, we save it. It might not be the final page as there could be
3772      * another page after this one. */
3773     while (ogg->offset < end) {
3774       ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
3775
3776       if (ret == GST_FLOW_LIMIT)
3777         break;
3778       if (ret != GST_FLOW_OK)
3779         return ret;
3780
3781       for (i = 0; i < chain->streams->len; i++) {
3782         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3783
3784         if (pad->map.is_skeleton)
3785           continue;
3786
3787         if (pad->map.serialno == ogg_page_serialno (&og)) {
3788           gint64 granulepos = ogg_page_granulepos (&og);
3789
3790           if (granulepos != -1) {
3791             last_granule = granulepos;
3792             last_pad = pad;
3793             done = TRUE;
3794           }
3795           break;
3796         }
3797       }
3798     }
3799   }
3800
3801   if (last_pad) {
3802     chain->segment_stop =
3803         gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
3804         last_granule);
3805   } else {
3806     chain->segment_stop = GST_CLOCK_TIME_NONE;
3807   }
3808
3809   GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
3810
3811   return GST_FLOW_OK;
3812 }
3813
3814 /* find a pad with a given serial number
3815  */
3816 static GstOggPad *
3817 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
3818 {
3819   GstOggPad *pad;
3820   gint i;
3821
3822   /* first look in building chain if any */
3823   if (ogg->building_chain) {
3824     pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
3825     if (pad)
3826       return pad;
3827   }
3828
3829   /* then look in current chain if any */
3830   if (ogg->current_chain) {
3831     pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
3832     if (pad)
3833       return pad;
3834   }
3835
3836   for (i = 0; i < ogg->chains->len; i++) {
3837     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3838
3839     pad = gst_ogg_chain_get_stream (chain, serialno);
3840     if (pad)
3841       return pad;
3842   }
3843   return NULL;
3844 }
3845
3846 /* find a chain with a given serial number
3847  */
3848 static GstOggChain *
3849 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
3850 {
3851   GstOggPad *pad;
3852
3853   pad = gst_ogg_demux_find_pad (ogg, serialno);
3854   if (pad) {
3855     return pad->chain;
3856   }
3857   return NULL;
3858 }
3859
3860 /* returns TRUE if all streams have valid start time */
3861 static gboolean
3862 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
3863 {
3864   gboolean res = TRUE;
3865
3866   chain->total_time = GST_CLOCK_TIME_NONE;
3867   GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
3868
3869   /* see if we have a start time on all streams */
3870   chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
3871
3872   if (chain->segment_start == G_MAXUINT64) {
3873     /* not yet, stream some more data */
3874     res = FALSE;
3875   } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
3876     /* we can calculate a total time */
3877     chain->total_time = chain->segment_stop - chain->segment_start;
3878   }
3879
3880   GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
3881
3882   GST_DEBUG_OBJECT (ogg, "return %d", res);
3883
3884   return res;
3885 }
3886
3887 static void
3888 gst_ogg_demux_collect_info (GstOggDemux * ogg)
3889 {
3890   gint i;
3891
3892   /* collect all info */
3893   ogg->total_time = 0;
3894
3895   for (i = 0; i < ogg->chains->len; i++) {
3896     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3897
3898     chain->begin_time = ogg->total_time;
3899
3900     gst_ogg_demux_collect_chain_info (ogg, chain);
3901
3902     ogg->total_time += chain->total_time;
3903   }
3904   gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time);
3905 }
3906
3907 /* find all the chains in the ogg file, this reads the first and
3908  * last page of the ogg stream, if they match then the ogg file has
3909  * just one chain, else we do a binary search for all chains.
3910  */
3911 static GstFlowReturn
3912 gst_ogg_demux_find_chains (GstOggDemux * ogg)
3913 {
3914   ogg_page og;
3915   GstPad *peer;
3916   GstFormat format;
3917   gboolean res;
3918   guint32 serialno;
3919   GstOggChain *chain;
3920   GstFlowReturn ret;
3921
3922   /* get peer to figure out length */
3923   if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
3924     goto no_peer;
3925
3926   /* find length to read last page, we store this for later use. */
3927   format = GST_FORMAT_BYTES;
3928   res = gst_pad_query_duration (peer, &format, &ogg->length);
3929   gst_object_unref (peer);
3930   if (!res || ogg->length <= 0)
3931     goto no_length;
3932
3933   GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
3934
3935   /* read chain from offset 0, this is the first chain of the
3936    * ogg file. */
3937   gst_ogg_demux_seek (ogg, 0);
3938   ret = gst_ogg_demux_read_chain (ogg, &chain);
3939   if (ret != GST_FLOW_OK)
3940     goto no_first_chain;
3941
3942   /* read page from end offset, we use this page to check if its serial
3943    * number is contained in the first chain. If this is the case then
3944    * this ogg is not a chained ogg and we can skip the scanning. */
3945   gst_ogg_demux_seek (ogg, ogg->length);
3946   ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
3947   if (ret != GST_FLOW_OK)
3948     goto no_last_page;
3949
3950   serialno = ogg_page_serialno (&og);
3951
3952   if (!gst_ogg_chain_has_stream (chain, serialno)) {
3953     /* the last page is not in the first stream, this means we should
3954      * find all the chains in this chained ogg. */
3955     ret =
3956         gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
3957         0);
3958   } else {
3959     /* we still call this function here but with an empty range so that
3960      * we can reuse the setup code in this routine. */
3961     ret =
3962         gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
3963         ogg->length, chain, 0);
3964   }
3965   if (ret != GST_FLOW_OK)
3966     goto done;
3967
3968   /* all fine, collect and print */
3969   gst_ogg_demux_collect_info (ogg);
3970
3971   /* dump our chains and streams */
3972   gst_ogg_print (ogg);
3973
3974 done:
3975   return ret;
3976
3977   /*** error cases ***/
3978 no_peer:
3979   {
3980     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
3981     return GST_FLOW_NOT_LINKED;
3982   }
3983 no_length:
3984   {
3985     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
3986     return GST_FLOW_NOT_SUPPORTED;
3987   }
3988 no_first_chain:
3989   {
3990     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
3991     return GST_FLOW_ERROR;
3992   }
3993 no_last_page:
3994   {
3995     GST_DEBUG_OBJECT (ogg, "can't get last page");
3996     if (chain)
3997       gst_ogg_chain_free (chain);
3998     return ret;
3999   }
4000 }
4001
4002 static GstFlowReturn
4003 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
4004 {
4005   GstOggPad *pad;
4006   gint64 granule;
4007   guint32 serialno;
4008   GstFlowReturn result = GST_FLOW_OK;
4009
4010   serialno = ogg_page_serialno (page);
4011   granule = ogg_page_granulepos (page);
4012
4013   GST_LOG_OBJECT (ogg,
4014       "processing ogg page (serial %08x, "
4015       "pageno %ld, granulepos %" G_GINT64_FORMAT ", bos %d)", serialno,
4016       ogg_page_pageno (page), granule, ogg_page_bos (page));
4017
4018   if (ogg_page_bos (page)) {
4019     GstOggChain *chain;
4020
4021     /* first page */
4022     /* see if we know about the chain already */
4023     chain = gst_ogg_demux_find_chain (ogg, serialno);
4024     if (chain) {
4025       GstEvent *event;
4026       gint64 start = 0;
4027
4028       if (chain->segment_start != GST_CLOCK_TIME_NONE)
4029         start = chain->segment_start;
4030
4031       /* create the newsegment event we are going to send out */
4032       event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
4033           GST_FORMAT_TIME, start, chain->segment_stop, chain->begin_time);
4034       gst_event_set_seqnum (event, ogg->seqnum);
4035
4036       GST_DEBUG_OBJECT (ogg,
4037           "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
4038           ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
4039           GST_TIME_ARGS (chain->segment_stop),
4040           GST_TIME_ARGS (chain->begin_time));
4041
4042       /* activate it as it means we have a non-header, this will also deactivate
4043        * the currently running chain. */
4044       gst_ogg_demux_activate_chain (ogg, chain, event);
4045       pad = gst_ogg_demux_find_pad (ogg, serialno);
4046     } else {
4047       GstClockTime chain_time;
4048       gint64 current_time;
4049
4050       /* this can only happen in push mode */
4051       if (ogg->pullmode)
4052         goto unknown_chain;
4053
4054       current_time = ogg->segment.last_stop;
4055
4056       /* time of new chain is current time */
4057       chain_time = current_time;
4058
4059       if (ogg->building_chain == NULL) {
4060         GstOggChain *newchain;
4061
4062         newchain = gst_ogg_chain_new (ogg);
4063         newchain->offset = 0;
4064         /* set new chain begin time aligned with end time of old chain */
4065         newchain->begin_time = chain_time;
4066         GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
4067             GST_TIME_ARGS (chain_time));
4068
4069         /* and this is the one we are building now */
4070         ogg->building_chain = newchain;
4071       }
4072       pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
4073     }
4074   } else {
4075     pad = gst_ogg_demux_find_pad (ogg, serialno);
4076   }
4077   if (pad) {
4078     result = gst_ogg_pad_submit_page (pad, page);
4079   } else {
4080     GST_PUSH_LOCK (ogg);
4081     if (!ogg->pullmode && !ogg->push_disable_seeking) {
4082       /* no pad while probing for duration, we must have a chained stream,
4083          and we don't support them, so back off */
4084       GST_INFO_OBJECT (ogg, "We seem to have a chained stream, we won't seek");
4085       if (ogg->push_state == PUSH_DURATION) {
4086         GstFlowReturn res;
4087
4088         res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
4089         if (res != GST_FLOW_OK)
4090           return res;
4091       }
4092
4093       /* only once we seeked back */
4094       GST_PUSH_LOCK (ogg);
4095       ogg->push_disable_seeking = TRUE;
4096     } else {
4097       GST_PUSH_UNLOCK (ogg);
4098       /* no pad. This means an ogg page without bos has been seen for this
4099        * serialno. we just ignore it but post a warning... */
4100       GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
4101           (NULL), ("unknown ogg pad for serial %08x detected", serialno));
4102       return GST_FLOW_OK;
4103     }
4104     GST_PUSH_UNLOCK (ogg);
4105   }
4106   return result;
4107
4108   /* ERRORS */
4109 unknown_chain:
4110   {
4111     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
4112         (NULL), ("unknown ogg chain for serial %08x detected", serialno));
4113     return GST_FLOW_ERROR;
4114   }
4115 }
4116
4117 /* streaming mode, receive a buffer, parse it, create pads for
4118  * the serialno, submit pages and packets to the oggpads
4119  */
4120 static GstFlowReturn
4121 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
4122 {
4123   GstOggDemux *ogg;
4124   gint ret = 0;
4125   GstFlowReturn result = GST_FLOW_OK;
4126
4127   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4128
4129   GST_DEBUG_OBJECT (ogg, "enter");
4130   result = gst_ogg_demux_submit_buffer (ogg, buffer);
4131   if (result < 0) {
4132     GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
4133   }
4134
4135   while (result == GST_FLOW_OK) {
4136     ogg_page page;
4137
4138     ret = ogg_sync_pageout (&ogg->sync, &page);
4139     if (ret == 0)
4140       /* need more data */
4141       break;
4142     if (ret == -1) {
4143       /* discontinuity in the pages */
4144       GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
4145     } else {
4146       result = gst_ogg_demux_handle_page (ogg, &page);
4147       if (result < 0) {
4148         GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
4149       }
4150     }
4151   }
4152   if (ret == 0 || result == GST_FLOW_OK) {
4153     gst_ogg_demux_sync_streams (ogg);
4154   }
4155   GST_DEBUG_OBJECT (ogg, "leave with %d", result);
4156   return result;
4157 }
4158
4159 static gboolean
4160 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
4161 {
4162   GstOggChain *chain = ogg->current_chain;
4163   gboolean res = TRUE;
4164
4165   if (!chain)
4166     chain = ogg->building_chain;
4167
4168   if (chain) {
4169     gint i;
4170
4171     for (i = 0; i < chain->streams->len; i++) {
4172       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4173
4174       gst_event_ref (event);
4175       GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
4176       res &= gst_pad_push_event (GST_PAD (pad), event);
4177     }
4178   } else {
4179     GST_WARNING_OBJECT (ogg, "No chain to forward event to");
4180   }
4181   gst_event_unref (event);
4182
4183   return res;
4184 }
4185
4186 static GstFlowReturn
4187 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
4188     GstFlowReturn ret)
4189 {
4190   GstOggChain *chain;
4191
4192   /* store the value */
4193   pad->last_ret = ret;
4194
4195   /* any other error that is not-linked can be returned right
4196    * away */
4197   if (ret != GST_FLOW_NOT_LINKED)
4198     goto done;
4199
4200   /* only return NOT_LINKED if all other pads returned NOT_LINKED */
4201   chain = ogg->current_chain;
4202   if (chain) {
4203     gint i;
4204
4205     for (i = 0; i < chain->streams->len; i++) {
4206       GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4207
4208       ret = opad->last_ret;
4209       /* some other return value (must be SUCCESS but we can return
4210        * other values as well) */
4211       if (ret != GST_FLOW_NOT_LINKED)
4212         goto done;
4213     }
4214     /* if we get here, all other pads were unlinked and we return
4215      * NOT_LINKED then */
4216   }
4217 done:
4218   return ret;
4219 }
4220
4221 /* returns TRUE if all streams in current chain reached EOS, FALSE otherwise */
4222 static gboolean
4223 gst_ogg_demux_check_eos (GstOggDemux * ogg)
4224 {
4225   GstOggChain *chain;
4226   gboolean eos = TRUE;
4227
4228   chain = ogg->current_chain;
4229   if (G_LIKELY (chain)) {
4230     gint i;
4231
4232     for (i = 0; i < chain->streams->len; i++) {
4233       GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4234
4235       eos = eos && opad->is_eos;
4236     }
4237   } else {
4238     eos = FALSE;
4239   }
4240
4241   return eos;
4242 }
4243
4244 static GstFlowReturn
4245 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4246 {
4247   GstFlowReturn ret;
4248   GstBuffer *buffer;
4249
4250   if (ogg->offset == ogg->length) {
4251     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4252         " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
4253     ret = GST_FLOW_UNEXPECTED;
4254     goto done;
4255   }
4256
4257   GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4258   ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
4259   if (ret != GST_FLOW_OK) {
4260     GST_LOG_OBJECT (ogg, "Failed pull_range");
4261     goto done;
4262   }
4263
4264   ogg->offset += GST_BUFFER_SIZE (buffer);
4265
4266   if (G_UNLIKELY (ogg->newsegment)) {
4267     gst_ogg_demux_send_event (ogg, ogg->newsegment);
4268     ogg->newsegment = NULL;
4269   }
4270
4271   ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
4272   if (ret != GST_FLOW_OK) {
4273     GST_LOG_OBJECT (ogg, "Failed demux_chain");
4274     goto done;
4275   }
4276
4277   /* check for the end of the segment */
4278   if (gst_ogg_demux_check_eos (ogg)) {
4279     GST_LOG_OBJECT (ogg, "got EOS");
4280     ret = GST_FLOW_UNEXPECTED;
4281     goto done;
4282   }
4283 done:
4284   return ret;
4285 }
4286
4287 /* reverse mode.
4288  *
4289  * We read the pages backwards and send the packets forwards. The first packet
4290  * in the page will be pushed with the DISCONT flag set.
4291  *
4292  * Special care has to be taken for continued pages, which we can only decode
4293  * when we have the previous page(s).
4294  */
4295 static GstFlowReturn
4296 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4297 {
4298   GstFlowReturn ret;
4299   ogg_page page;
4300   gint64 offset;
4301
4302   if (ogg->offset == 0) {
4303     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4304         " == 0", ogg->offset);
4305     ret = GST_FLOW_UNEXPECTED;
4306     goto done;
4307   }
4308
4309   GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
4310   ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
4311   if (ret != GST_FLOW_OK)
4312     goto done;
4313
4314   ogg->offset = offset;
4315
4316   if (G_UNLIKELY (ogg->newsegment)) {
4317     gst_ogg_demux_send_event (ogg, ogg->newsegment);
4318     ogg->newsegment = NULL;
4319   }
4320
4321   ret = gst_ogg_demux_handle_page (ogg, &page);
4322   if (ret != GST_FLOW_OK)
4323     goto done;
4324
4325   /* check for the end of the segment */
4326   if (gst_ogg_demux_check_eos (ogg)) {
4327     GST_LOG_OBJECT (ogg, "got EOS");
4328     ret = GST_FLOW_UNEXPECTED;
4329     goto done;
4330   }
4331 done:
4332   return ret;
4333 }
4334
4335 static void
4336 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4337 {
4338   GstClockTime cur;
4339   GstOggChain *chain;
4340   guint i;
4341
4342   chain = ogg->current_chain;
4343   cur = ogg->segment.last_stop;
4344   if (chain == NULL || cur == -1)
4345     return;
4346
4347   for (i = 0; i < chain->streams->len; i++) {
4348     GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4349
4350     /* Theoretically, we should be doing this for all streams, but we're only
4351      * doing it for known-to-be-sparse streams at the moment in order not to
4352      * break things for wrongly-muxed streams (like we used to produce once) */
4353     if (stream->map.is_sparse && stream->last_stop != GST_CLOCK_TIME_NONE) {
4354
4355       /* Does this stream lag? Random threshold of 2 seconds */
4356       if (GST_CLOCK_DIFF (stream->last_stop, cur) > (2 * GST_SECOND)) {
4357         GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
4358             "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
4359             GST_TIME_ARGS (stream->last_stop), GST_TIME_ARGS (cur));
4360         stream->last_stop = cur;
4361         /* advance stream time (FIXME: is this right, esp. time_pos?) */
4362         gst_pad_push_event (GST_PAD_CAST (stream),
4363             gst_event_new_new_segment (TRUE, ogg->segment.rate,
4364                 GST_FORMAT_TIME, stream->last_stop, -1, stream->last_stop));
4365       }
4366     }
4367   }
4368 }
4369
4370 /* random access code
4371  *
4372  * - first find all the chains and streams by scanning the file.
4373  * - then get and chain buffers, just like the streaming case.
4374  * - when seeking, we can use the chain info to perform the seek.
4375  */
4376 static void
4377 gst_ogg_demux_loop (GstOggPad * pad)
4378 {
4379   GstOggDemux *ogg;
4380   GstFlowReturn ret;
4381   GstEvent *event;
4382
4383   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4384
4385   if (ogg->need_chains) {
4386     gboolean res;
4387
4388     /* this is the only place where we write chains and thus need to lock. */
4389     GST_CHAIN_LOCK (ogg);
4390     ret = gst_ogg_demux_find_chains (ogg);
4391     GST_CHAIN_UNLOCK (ogg);
4392     if (ret != GST_FLOW_OK)
4393       goto chain_read_failed;
4394
4395     ogg->need_chains = FALSE;
4396
4397     GST_OBJECT_LOCK (ogg);
4398     ogg->running = TRUE;
4399     event = ogg->event;
4400     ogg->event = NULL;
4401     GST_OBJECT_UNLOCK (ogg);
4402
4403     /* and seek to configured positions without FLUSH */
4404     res = gst_ogg_demux_perform_seek_pull (ogg, event);
4405     if (event)
4406       gst_event_unref (event);
4407
4408     if (!res)
4409       goto seek_failed;
4410   }
4411
4412   if (ogg->segment.rate >= 0.0)
4413     ret = gst_ogg_demux_loop_forward (ogg);
4414   else
4415     ret = gst_ogg_demux_loop_reverse (ogg);
4416
4417   if (ret != GST_FLOW_OK)
4418     goto pause;
4419
4420   gst_ogg_demux_sync_streams (ogg);
4421   return;
4422
4423   /* ERRORS */
4424 chain_read_failed:
4425   {
4426     /* error was posted */
4427     goto pause;
4428   }
4429 seek_failed:
4430   {
4431     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4432         ("failed to start demuxing ogg"));
4433     ret = GST_FLOW_ERROR;
4434     goto pause;
4435   }
4436 pause:
4437   {
4438     const gchar *reason = gst_flow_get_name (ret);
4439     GstEvent *event = NULL;
4440
4441     GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4442     ogg->segment_running = FALSE;
4443     gst_pad_pause_task (ogg->sinkpad);
4444
4445     if (ret == GST_FLOW_UNEXPECTED) {
4446       /* perform EOS logic */
4447       if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4448         gint64 stop;
4449         GstMessage *message;
4450
4451         /* for segment playback we need to post when (in stream time)
4452          * we stopped, this is either stop (when set) or the duration. */
4453         if ((stop = ogg->segment.stop) == -1)
4454           stop = ogg->segment.duration;
4455
4456         GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
4457         message =
4458             gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
4459             stop);
4460         gst_message_set_seqnum (message, ogg->seqnum);
4461
4462         gst_element_post_message (GST_ELEMENT (ogg), message);
4463       } else {
4464         /* normal playback, send EOS to all linked pads */
4465         GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
4466         event = gst_event_new_eos ();
4467       }
4468     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_UNEXPECTED) {
4469       GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
4470           (_("Internal data stream error.")),
4471           ("stream stopped, reason %s", reason));
4472       event = gst_event_new_eos ();
4473     }
4474
4475     /* For wrong-state we still want to pause the task and stop
4476      * but no error message or other things are necessary.
4477      * wrong-state is no real error and will be caused by flushing,
4478      * e.g. because of a flushing seek.
4479      */
4480     if (event) {
4481       /* guard against corrupt/truncated files, where one can hit EOS
4482          before prerolling is done and a chain created. If we have no
4483          chain to send the event to, error out. */
4484       if (ogg->current_chain || ogg->building_chain) {
4485         gst_event_set_seqnum (event, ogg->seqnum);
4486         gst_ogg_demux_send_event (ogg, event);
4487       } else {
4488         gst_event_unref (event);
4489         GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4490             ("EOS before finding a chain"));
4491       }
4492     }
4493     return;
4494   }
4495 }
4496
4497 static void
4498 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
4499 {
4500   gint i;
4501
4502   gst_ogg_demux_deactivate_current_chain (ogg);
4503
4504   GST_CHAIN_LOCK (ogg);
4505   for (i = 0; i < ogg->chains->len; i++) {
4506     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4507
4508     gst_ogg_chain_free (chain);
4509   }
4510   ogg->chains = g_array_set_size (ogg->chains, 0);
4511   GST_CHAIN_UNLOCK (ogg);
4512 }
4513
4514 /* this function is called when the pad is activated and should start
4515  * processing data.
4516  *
4517  * We check if we can do random access to decide if we work push or
4518  * pull based.
4519  */
4520 static gboolean
4521 gst_ogg_demux_sink_activate (GstPad * sinkpad)
4522 {
4523   if (gst_pad_check_pull_range (sinkpad)) {
4524     GST_DEBUG_OBJECT (sinkpad, "activating pull");
4525     return gst_pad_activate_pull (sinkpad, TRUE);
4526   } else {
4527     GST_DEBUG_OBJECT (sinkpad, "activating push");
4528     return gst_pad_activate_push (sinkpad, TRUE);
4529   }
4530 }
4531
4532 /* this function gets called when we activate ourselves in push mode.
4533  * We cannot seek (ourselves) in the stream */
4534 static gboolean
4535 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
4536 {
4537   GstOggDemux *ogg;
4538
4539   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
4540
4541   ogg->pullmode = FALSE;
4542   ogg->resync = FALSE;
4543
4544   return TRUE;
4545 }
4546
4547 /* this function gets called when we activate ourselves in pull mode.
4548  * We can perform  random access to the resource and we start a task
4549  * to start reading */
4550 static gboolean
4551 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
4552 {
4553   GstOggDemux *ogg;
4554
4555   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
4556
4557   if (active) {
4558     ogg->need_chains = TRUE;
4559     ogg->pullmode = TRUE;
4560
4561     return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
4562         sinkpad);
4563   } else {
4564     return gst_pad_stop_task (sinkpad);
4565   }
4566 }
4567
4568 static GstStateChangeReturn
4569 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
4570 {
4571   GstOggDemux *ogg;
4572   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
4573
4574   ogg = GST_OGG_DEMUX (element);
4575
4576   switch (transition) {
4577     case GST_STATE_CHANGE_NULL_TO_READY:
4578       ogg->basetime = 0;
4579       ogg_sync_init (&ogg->sync);
4580       break;
4581     case GST_STATE_CHANGE_READY_TO_PAUSED:
4582       ogg_sync_reset (&ogg->sync);
4583       ogg->running = FALSE;
4584       ogg->bitrate = 0;
4585       ogg->segment_running = FALSE;
4586       ogg->total_time = -1;
4587       GST_PUSH_LOCK (ogg);
4588       ogg->push_byte_offset = 0;
4589       ogg->push_byte_length = -1;
4590       ogg->push_time_length = GST_CLOCK_TIME_NONE;
4591       ogg->push_time_offset = GST_CLOCK_TIME_NONE;
4592       ogg->push_state = PUSH_PLAYING;
4593
4594       ogg->push_disable_seeking = FALSE;
4595       if (!ogg->pullmode) {
4596         GstPad *peer;
4597         if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
4598           GstFormat format = GST_FORMAT_BYTES;
4599           gint64 length = -1;
4600           if (!gst_pad_query_duration (peer, &format, &length) || length <= 0) {
4601             GST_DEBUG_OBJECT (ogg,
4602                 "Unable to determine stream size, assuming live, seeking disabled");
4603             ogg->push_disable_seeking = TRUE;
4604           }
4605           gst_object_unref (peer);
4606         }
4607       }
4608
4609       GST_PUSH_UNLOCK (ogg);
4610       gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
4611       break;
4612     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4613       break;
4614     default:
4615       break;
4616   }
4617
4618   result = parent_class->change_state (element, transition);
4619
4620   switch (transition) {
4621     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4622       break;
4623     case GST_STATE_CHANGE_PAUSED_TO_READY:
4624       gst_ogg_demux_clear_chains (ogg);
4625       GST_OBJECT_LOCK (ogg);
4626       ogg->running = FALSE;
4627       ogg->segment_running = FALSE;
4628       GST_OBJECT_UNLOCK (ogg);
4629       break;
4630     case GST_STATE_CHANGE_READY_TO_NULL:
4631       ogg_sync_clear (&ogg->sync);
4632       break;
4633     default:
4634       break;
4635   }
4636   return result;
4637 }
4638
4639 gboolean
4640 gst_ogg_demux_plugin_init (GstPlugin * plugin)
4641 {
4642   GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
4643   GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
4644       "ogg demuxer setup stage when parsing pipeline");
4645
4646 #ifdef ENABLE_NLS
4647   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
4648       LOCALEDIR);
4649   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4650   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4651 #endif
4652
4653   return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
4654       GST_TYPE_OGG_DEMUX);
4655 }
4656
4657 /* prints all info about the element */
4658 #undef GST_CAT_DEFAULT
4659 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
4660
4661 #ifdef GST_DISABLE_GST_DEBUG
4662
4663 static void
4664 gst_ogg_print (GstOggDemux * ogg)
4665 {
4666   /* NOP */
4667 }
4668
4669 #else /* !GST_DISABLE_GST_DEBUG */
4670
4671 static void
4672 gst_ogg_print (GstOggDemux * ogg)
4673 {
4674   guint j, i;
4675
4676   GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
4677   GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4678       GST_TIME_ARGS (ogg->total_time));
4679
4680   for (i = 0; i < ogg->chains->len; i++) {
4681     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4682
4683     GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
4684     GST_INFO_OBJECT (ogg,
4685         "  offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
4686         chain->end_offset);
4687     GST_INFO_OBJECT (ogg, "  begin time: %" GST_TIME_FORMAT,
4688         GST_TIME_ARGS (chain->begin_time));
4689     GST_INFO_OBJECT (ogg, "  total time: %" GST_TIME_FORMAT,
4690         GST_TIME_ARGS (chain->total_time));
4691     GST_INFO_OBJECT (ogg, "  segment start: %" GST_TIME_FORMAT,
4692         GST_TIME_ARGS (chain->segment_start));
4693     GST_INFO_OBJECT (ogg, "  segment stop:  %" GST_TIME_FORMAT,
4694         GST_TIME_ARGS (chain->segment_stop));
4695
4696     for (j = 0; j < chain->streams->len; j++) {
4697       GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
4698
4699       GST_INFO_OBJECT (ogg, "  stream %08x: %s", stream->map.serialno,
4700           gst_ogg_stream_get_media_type (&stream->map));
4701       GST_INFO_OBJECT (ogg, "   start time:       %" GST_TIME_FORMAT,
4702           GST_TIME_ARGS (stream->start_time));
4703     }
4704   }
4705 }
4706 #endif /* GST_DISABLE_GST_DEBUG */