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