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