oggdemux: granulepos is relative to its chain
[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
45 #include "gstoggdemux.h"
46
47 static const GstElementDetails gst_ogg_demux_details =
48 GST_ELEMENT_DETAILS ("Ogg demuxer",
49     "Codec/Demuxer",
50     "demux ogg streams (info about ogg: http://xiph.org)",
51     "Wim Taymans <wim@fluendo.com>");
52
53 #define CHUNKSIZE (8500)        /* this is out of vorbisfile */
54 #define SKELETON_FISHEAD_SIZE 64
55 #define SKELETON_FISBONE_MIN_SIZE 52
56
57 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
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 GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
63 GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
64 #define GST_CAT_DEFAULT gst_ogg_demux_debug
65
66 static ogg_page *
67 gst_ogg_page_copy (ogg_page * page)
68 {
69   ogg_page *p = g_new0 (ogg_page, 1);
70
71   /* make a copy of the page */
72   p->header = g_memdup (page->header, page->header_len);
73   p->header_len = page->header_len;
74   p->body = g_memdup (page->body, page->body_len);
75   p->body_len = page->body_len;
76
77   return p;
78 }
79
80 static void
81 gst_ogg_page_free (ogg_page * page)
82 {
83   g_free (page->header);
84   g_free (page->body);
85   g_free (page);
86 }
87
88 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
89     GstOggChain * chain);
90 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
91     GstOggChain * chain, GstEvent * event);
92 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
93
94 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
95     GstEvent * event);
96 static gboolean gst_ogg_demux_receive_event (GstElement * element,
97     GstEvent * event);
98
99 static void gst_ogg_pad_class_init (GstOggPadClass * klass);
100 static void gst_ogg_pad_init (GstOggPad * pad);
101 static void gst_ogg_pad_dispose (GObject * object);
102 static void gst_ogg_pad_finalize (GObject * object);
103
104 static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
105 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
106 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
107 static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
108 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
109     glong serialno);
110
111 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
112     GstOggPad * pad, GstFlowReturn ret);
113 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
114
115 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
116
117 static void
118 gst_ogg_pad_class_init (GstOggPadClass * klass)
119 {
120   GObjectClass *gobject_class;
121
122   gobject_class = (GObjectClass *) klass;
123
124   gobject_class->dispose = gst_ogg_pad_dispose;
125   gobject_class->finalize = gst_ogg_pad_finalize;
126 }
127
128 static void
129 gst_ogg_pad_init (GstOggPad * pad)
130 {
131   gst_pad_set_event_function (GST_PAD (pad),
132       GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
133   gst_pad_set_getcaps_function (GST_PAD (pad),
134       GST_DEBUG_FUNCPTR (gst_ogg_pad_getcaps));
135   gst_pad_set_query_type_function (GST_PAD (pad),
136       GST_DEBUG_FUNCPTR (gst_ogg_pad_query_types));
137   gst_pad_set_query_function (GST_PAD (pad),
138       GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
139
140   pad->mode = GST_OGG_PAD_MODE_INIT;
141
142   pad->current_granule = -1;
143   pad->keyframe_granule = -1;
144
145   pad->start_time = GST_CLOCK_TIME_NONE;
146
147   pad->last_stop = GST_CLOCK_TIME_NONE;
148
149   pad->have_type = FALSE;
150   pad->continued = NULL;
151   pad->map.headers = NULL;
152   pad->map.queued = NULL;
153 }
154
155 static void
156 gst_ogg_pad_dispose (GObject * object)
157 {
158   GstOggPad *pad = GST_OGG_PAD (object);
159
160   pad->chain = NULL;
161   pad->ogg = NULL;
162
163   g_list_foreach (pad->map.headers, (GFunc) gst_mini_object_unref, NULL);
164   g_list_free (pad->map.headers);
165   pad->map.headers = NULL;
166   g_list_foreach (pad->map.queued, (GFunc) gst_mini_object_unref, NULL);
167   g_list_free (pad->map.queued);
168   pad->map.queued = NULL;
169
170   /* clear continued pages */
171   g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
172   g_list_free (pad->continued);
173   pad->continued = NULL;
174
175   ogg_stream_reset (&pad->map.stream);
176
177   G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
178 }
179
180 static void
181 gst_ogg_pad_finalize (GObject * object)
182 {
183   GstOggPad *pad = GST_OGG_PAD (object);
184
185   ogg_stream_clear (&pad->map.stream);
186
187   G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
188 }
189
190 static const GstQueryType *
191 gst_ogg_pad_query_types (GstPad * pad)
192 {
193   static const GstQueryType query_types[] = {
194     GST_QUERY_DURATION,
195     GST_QUERY_SEEKING,
196     0
197   };
198
199   return query_types;
200 }
201
202 static GstCaps *
203 gst_ogg_pad_getcaps (GstPad * pad)
204 {
205   return gst_caps_ref (GST_PAD_CAPS (pad));
206 }
207
208 static gboolean
209 gst_ogg_pad_src_query (GstPad * pad, GstQuery * query)
210 {
211   gboolean res = TRUE;
212   GstOggDemux *ogg;
213
214   ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
215
216   switch (GST_QUERY_TYPE (query)) {
217     case GST_QUERY_DURATION:
218     {
219       GstFormat format;
220       gint64 total_time;
221
222       gst_query_parse_duration (query, &format, NULL);
223       /* can only get position in time */
224       if (format != GST_FORMAT_TIME)
225         goto wrong_format;
226
227       if (ogg->seekable) {
228         /* we must return the total seekable length */
229         total_time = ogg->total_time;
230       } else {
231         /* in non-seek mode we can answer the query and we must return -1 */
232         total_time = -1;
233       }
234
235       gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
236       break;
237     }
238     case GST_QUERY_SEEKING:
239     {
240       GstFormat format;
241
242       gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
243       if (format == GST_FORMAT_TIME) {
244         gst_query_set_seeking (query, GST_FORMAT_TIME, ogg->seekable,
245             0, ogg->total_time);
246       } else {
247         res = FALSE;
248       }
249       break;
250     }
251
252     default:
253       res = gst_pad_query_default (pad, query);
254       break;
255   }
256 done:
257   gst_object_unref (ogg);
258
259   return res;
260
261   /* ERRORS */
262 wrong_format:
263   {
264     GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
265     res = FALSE;
266     goto done;
267   }
268 }
269
270 static gboolean
271 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
272 {
273   gboolean res;
274   GstOggDemux *ogg;
275
276   ogg = GST_OGG_DEMUX (element);
277
278   switch (GST_EVENT_TYPE (event)) {
279     case GST_EVENT_SEEK:
280       /* can't seek if we are not seekable, FIXME could pass the
281        * seek query upstream after converting it to bytes using
282        * the average bitrate of the stream. */
283       if (!ogg->seekable) {
284         GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
285         goto error;
286       }
287
288       /* now do the seek */
289       res = gst_ogg_demux_perform_seek (ogg, event);
290       gst_event_unref (event);
291       break;
292     default:
293       GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
294       goto error;
295   }
296
297   return res;
298
299   /* ERRORS */
300 error:
301   {
302     GST_DEBUG_OBJECT (ogg, "error handling event");
303     gst_event_unref (event);
304     return FALSE;
305   }
306 }
307
308 static gboolean
309 gst_ogg_pad_event (GstPad * pad, GstEvent * event)
310 {
311   gboolean res;
312   GstOggDemux *ogg;
313
314   ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
315
316   switch (GST_EVENT_TYPE (event)) {
317     case GST_EVENT_SEEK:
318       /* can't seek if we are not seekable, FIXME could pass the
319        * seek query upstream after converting it to bytes using
320        * the average bitrate of the stream. */
321       if (!ogg->seekable) {
322         GST_DEBUG_OBJECT (ogg, "seek on non seekable stream");
323         goto error;
324       }
325
326       /* now do the seek */
327       res = gst_ogg_demux_perform_seek (ogg, event);
328       gst_event_unref (event);
329       break;
330     default:
331       res = gst_pad_event_default (pad, event);
332       break;
333   }
334 done:
335   gst_object_unref (ogg);
336
337   return res;
338
339   /* ERRORS */
340 error:
341   {
342     GST_DEBUG_OBJECT (ogg, "error handling event");
343     gst_event_unref (event);
344     res = FALSE;
345     goto done;
346   }
347 }
348
349 static void
350 gst_ogg_pad_reset (GstOggPad * pad)
351 {
352   ogg_stream_reset (&pad->map.stream);
353
354   GST_DEBUG_OBJECT (pad, "doing reset");
355
356   /* clear continued pages */
357   g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
358   g_list_free (pad->continued);
359   pad->continued = NULL;
360
361   pad->last_ret = GST_FLOW_OK;
362   pad->last_stop = GST_CLOCK_TIME_NONE;
363   pad->current_granule = -1;
364   pad->keyframe_granule = -1;
365 }
366
367 /* called when the skeleton fishead is found. Caller ensures the packet is
368  * precisely the correct size; we don't re-check this here. */
369 static void
370 gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet)
371 {
372   GstOggDemux *ogg = pad->ogg;
373   guint8 *data = packet->packet;
374   guint16 major, minor;
375   gint64 prestime_n, prestime_d;
376   gint64 basetime_n, basetime_d;
377
378   /* skip "fishead\0" */
379   major = GST_READ_UINT16_LE (data + 8);
380   minor = GST_READ_UINT16_LE (data + 10);
381   prestime_n = (gint64) GST_READ_UINT64_LE (data + 12);
382   prestime_d = (gint64) GST_READ_UINT64_LE (data + 20);
383   basetime_n = (gint64) GST_READ_UINT64_LE (data + 28);
384   basetime_d = (gint64) GST_READ_UINT64_LE (data + 36);
385
386   ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
387   ogg->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
388   ogg->have_fishead = TRUE;
389   pad->map.is_skeleton = TRUE;
390   pad->start_time = GST_CLOCK_TIME_NONE;
391   GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %"
392       GST_TIME_FORMAT ", prestime: %" GST_TIME_FORMAT ")",
393       GST_TIME_ARGS (ogg->basetime), GST_TIME_ARGS (ogg->prestime));
394 }
395
396 /* function called when a skeleton fisbone is found. Caller ensures that
397  * the packet length is sufficient */
398 static void
399 gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet)
400 {
401   GstOggPad *fisbone_pad;
402   gint64 start_granule;
403   guint32 serialno;
404   guint8 *data = packet->packet;
405
406   serialno = GST_READ_UINT32_LE (data + 12);
407
408   fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
409   if (fisbone_pad) {
410     if (fisbone_pad->map.have_fisbone)
411       /* already parsed */
412       return;
413
414     fisbone_pad->map.have_fisbone = TRUE;
415
416     fisbone_pad->map.granulerate_n = GST_READ_UINT64_LE (data + 20);
417     fisbone_pad->map.granulerate_d = GST_READ_UINT64_LE (data + 28);
418     start_granule = GST_READ_UINT64_LE (data + 36);
419     fisbone_pad->map.preroll = GST_READ_UINT32_LE (data + 44);
420     fisbone_pad->map.granuleshift = GST_READ_UINT8 (data + 48);
421
422     GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed "
423         "(serialno: %08x start time: %" GST_TIME_FORMAT
424         " granulerate_n: %d granulerate_d: %d "
425         " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
426         serialno, GST_TIME_ARGS (fisbone_pad->start_time),
427         fisbone_pad->map.granulerate_n, fisbone_pad->map.granulerate_d,
428         fisbone_pad->map.preroll, fisbone_pad->map.granuleshift);
429   } else {
430     GST_WARNING_OBJECT (pad->ogg,
431         "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
432         serialno);
433   }
434 }
435
436 static GstBuffer *
437 gst_ogg_demux_buffer_from_packet (ogg_packet * packet)
438 {
439   GstBuffer *buf;
440
441   buf = gst_buffer_new_and_alloc (packet->bytes);
442   memcpy (buf->data, packet->packet, packet->bytes);
443   GST_BUFFER_OFFSET (buf) = -1;
444   GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
445
446   return buf;
447 }
448
449 /* queue data, basically takes the packet, puts it in a buffer and store the
450  * buffer in the queued list.  */
451 static GstFlowReturn
452 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
453 {
454   GstBuffer *buf;
455
456 #ifndef GST_DISABLE_GST_DEBUG
457   GstOggDemux *ogg = pad->ogg;
458 #endif
459
460   GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x", pad,
461       pad->map.serialno);
462
463   buf = gst_ogg_demux_buffer_from_packet (packet);
464   pad->map.queued = g_list_append (pad->map.queued, buf);
465
466   /* we are ok now */
467   return GST_FLOW_OK;
468 }
469
470 static GstFlowReturn
471 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
472 {
473   GstBuffer *buf;
474   GstFlowReturn ret, cret;
475   GstOggDemux *ogg = pad->ogg;
476   gint64 current_time;
477   GstOggChain *chain;
478   gint64 duration;
479   gint offset;
480   gint trim;
481   GstClockTime out_timestamp, out_duration;
482   guint64 out_offset, out_offset_end;
483
484   GST_DEBUG_OBJECT (ogg,
485       "%p streaming to peer serial %08x", pad, pad->map.serialno);
486
487   if (pad->map.is_ogm) {
488     const guint8 *data;
489     long bytes;
490
491     data = packet->packet;
492     bytes = packet->bytes;
493
494     if (bytes < 1)
495       goto empty_packet;
496
497     if (data[0] & 1) {
498       /* We don't push header packets for OGM */
499       cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
500       goto done;
501     }
502
503     offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
504
505     trim = 0;
506     while (bytes && data[bytes - 1] == 0) {
507       trim++;
508       bytes--;
509     }
510   } else {
511     offset = 0;
512     trim = 0;
513   }
514
515   /* get timing info for the packet */
516   duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
517   GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
518
519   if (packet->b_o_s) {
520     out_timestamp = GST_CLOCK_TIME_NONE;
521     out_duration = GST_CLOCK_TIME_NONE;
522     out_offset = 0;
523     out_offset_end = -1;
524   } else {
525     if (packet->granulepos != -1) {
526       pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
527           packet->granulepos);
528       pad->keyframe_granule =
529           gst_ogg_stream_granulepos_to_key_granule (&pad->map,
530           packet->granulepos);
531       GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
532           pad->current_granule);
533     } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
534       pad->current_granule += duration;
535       GST_DEBUG_OBJECT (ogg, "interpollating granule %" G_GUINT64_FORMAT,
536           pad->current_granule);
537     }
538     if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
539       /* negative rates, only set timestamp on the packets with a granulepos */
540       out_timestamp = -1;
541       out_duration = -1;
542       out_offset = -1;
543       out_offset_end = -1;
544     } else {
545       /* we only push buffers after we have a valid granule. This is done so that
546        * we nicely skip packets without a timestamp after a seek. This is ok
547        * because we base or seek on the packet after the page with the smaller
548        * timestamp. */
549       if (pad->current_granule == -1)
550         goto no_timestamp;
551
552       if (pad->map.is_ogm) {
553         out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
554             pad->current_granule);
555         out_duration = gst_util_uint64_scale (duration,
556             GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
557       } else {
558         out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
559             pad->current_granule - duration);
560         out_duration =
561             gst_ogg_stream_granule_to_time (&pad->map,
562             pad->current_granule) - out_timestamp;
563       }
564       out_offset_end =
565           gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule,
566           pad->keyframe_granule);
567       out_offset =
568           gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
569     }
570   }
571
572   /* check for invalid buffer sizes */
573   if (G_UNLIKELY (offset + trim >= packet->bytes))
574     goto empty_packet;
575
576   ret =
577       gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad),
578       GST_BUFFER_OFFSET_NONE, packet->bytes - offset - trim,
579       GST_PAD_CAPS (pad), &buf);
580
581   /* combine flows */
582   cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
583   if (ret != GST_FLOW_OK)
584     goto no_buffer;
585
586   /* copy packet in buffer */
587   memcpy (buf->data, packet->packet + offset, packet->bytes - offset - trim);
588
589   GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
590   GST_BUFFER_DURATION (buf) = out_duration;
591   GST_BUFFER_OFFSET (buf) = out_offset;
592   GST_BUFFER_OFFSET_END (buf) = out_offset_end;
593
594   /* Mark discont on the buffer */
595   if (pad->discont) {
596     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
597     pad->discont = FALSE;
598   }
599
600   pad->last_stop = ogg->segment.last_stop;
601
602   ret = gst_pad_push (GST_PAD_CAST (pad), buf);
603
604   /* combine flows */
605   cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
606
607   /* we're done with skeleton stuff */
608   if (pad->map.is_skeleton)
609     goto done;
610
611   /* check if valid granulepos, then we can calculate the current
612    * position. We know the granule for each packet but we only want to update
613    * the last_stop when we have a valid granulepos on the packet because else
614    * our time jumps around for the different streams. */
615   if (packet->granulepos < 0)
616     goto done;
617
618   /* convert to time */
619   current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
620       packet->granulepos);
621
622   /* convert to stream time */
623   if ((chain = pad->chain)) {
624     gint64 chain_start = 0;
625
626     if (chain->segment_start != GST_CLOCK_TIME_NONE)
627       chain_start = chain->segment_start;
628
629     current_time = current_time - chain_start + chain->begin_time;
630   }
631
632   /* and store as the current position */
633   gst_segment_set_last_stop (&ogg->segment, GST_FORMAT_TIME, current_time);
634
635   GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
636       GST_TIME_ARGS (current_time));
637
638 done:
639   /* return combined flow result */
640   return cret;
641
642   /* special cases */
643 empty_packet:
644   {
645     GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
646     cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
647     goto done;
648   }
649 no_timestamp:
650   {
651     GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
652     cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK);
653     goto done;
654   }
655 no_buffer:
656   {
657     GST_DEBUG_OBJECT (ogg,
658         "%p could not get buffer from peer %08x, %d (%s), combined %d (%s)",
659         pad, pad->map.serialno, ret, gst_flow_get_name (ret),
660         cret, gst_flow_get_name (cret));
661     goto done;
662   }
663 }
664
665 /* submit a packet to the oggpad, this function will run the
666  * typefind code for the pad if this is the first packet for this
667  * stream 
668  */
669 static GstFlowReturn
670 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
671 {
672   gint64 granule;
673   GstFlowReturn ret = GST_FLOW_OK;
674
675   GstOggDemux *ogg = pad->ogg;
676
677   GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x", pad,
678       pad->map.serialno);
679
680   if (!pad->have_type) {
681     if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE &&
682         !memcmp (packet->packet, "fishead\0", 8)) {
683       gst_ogg_pad_parse_skeleton_fishead (pad, packet);
684     }
685     pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
686     if (!pad->have_type) {
687       pad->map.caps = gst_caps_new_simple ("application/x-unknown", NULL);
688     }
689     if (pad->map.caps) {
690       gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
691     } else {
692       GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
693     }
694   }
695
696   if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
697       !memcmp (packet->packet, "fisbone\0", 8)) {
698     gst_ogg_pad_parse_skeleton_fisbone (pad, packet);
699   }
700
701   granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
702       packet->granulepos);
703   if (granule != -1) {
704     GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
705     pad->current_granule = granule;
706   }
707
708   /* restart header packet count when seeing a b_o_s page;
709    * particularly useful following a seek or even following chain finding */
710   if (packet->b_o_s) {
711     GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
712     pad->map.n_header_packets_seen = 0;
713     if (!pad->map.have_headers) {
714       GST_DEBUG_OBJECT (ogg, "clearing header packets");
715       g_list_foreach (pad->map.headers, (GFunc) gst_mini_object_unref, NULL);
716       g_list_free (pad->map.headers);
717       pad->map.headers = NULL;
718     }
719   }
720
721   /* Overload the value of b_o_s in ogg_packet with a flag whether or
722    * not this is a header packet.  Maybe some day this could be cleaned
723    * up.  */
724   packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
725   if (!packet->b_o_s) {
726     pad->map.have_headers = TRUE;
727     if (pad->start_time == GST_CLOCK_TIME_NONE) {
728       gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
729       GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
730       if (duration != -1) {
731         pad->map.accumulated_granule += duration;
732         GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
733             pad->map.accumulated_granule);
734       }
735
736       if (packet->granulepos != -1) {
737         ogg_int64_t start_granule;
738         gint64 granule;
739
740         granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
741             packet->granulepos);
742
743         if (granule > pad->map.accumulated_granule)
744           start_granule = granule - pad->map.accumulated_granule;
745         else
746           start_granule = 0;
747
748         pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
749             start_granule);
750         GST_DEBUG ("start time %" G_GINT64_FORMAT, pad->start_time);
751       } else {
752         packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
753             pad->map.accumulated_granule, pad->keyframe_granule);
754       }
755     }
756   } else {
757     GstBuffer *buf;
758
759     pad->map.n_header_packets_seen++;
760     if (!pad->map.have_headers) {
761       buf = gst_ogg_demux_buffer_from_packet (packet);
762       pad->map.headers = g_list_append (pad->map.headers, buf);
763       GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
764     }
765   }
766
767   /* we know the start_time of the pad data, see if we
768    * can activate the complete chain if this is a dynamic
769    * chain. */
770   if (pad->start_time != GST_CLOCK_TIME_NONE) {
771     GstOggChain *chain = pad->chain;
772
773     /* check if complete chain has start time */
774     if (chain == ogg->building_chain) {
775
776       /* see if we have enough info to activate the chain, we have enough info
777        * when all streams have a valid start time. */
778       if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
779         GstEvent *event;
780
781         GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
782             GST_TIME_ARGS (chain->segment_start));
783         GST_DEBUG_OBJECT (ogg, "segment_stop:  %" GST_TIME_FORMAT,
784             GST_TIME_ARGS (chain->segment_stop));
785         GST_DEBUG_OBJECT (ogg, "segment_time:  %" GST_TIME_FORMAT,
786             GST_TIME_ARGS (chain->begin_time));
787
788         /* create the newsegment event we are going to send out */
789         event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
790             GST_FORMAT_TIME, chain->segment_start, chain->segment_stop,
791             chain->begin_time);
792         gst_event_set_seqnum (event, ogg->seqnum);
793
794         gst_ogg_demux_activate_chain (ogg, chain, event);
795
796         ogg->building_chain = NULL;
797       }
798     }
799   }
800
801   /* if we are building a chain, store buffer for when we activate
802    * it. This path is taken if we operate in streaming mode. */
803   if (ogg->building_chain) {
804     /* bos packets where stored in the header list */
805     if (!packet->b_o_s)
806       ret = gst_ogg_demux_queue_data (pad, packet);
807   }
808   /* else we are completely streaming to the peer */
809   else {
810     ret = gst_ogg_demux_chain_peer (pad, packet);
811   }
812   return ret;
813 }
814
815 /* flush at most @npackets from the stream layer. All packets if 
816  * @npackets is 0;
817  */
818 static GstFlowReturn
819 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
820 {
821   GstFlowReturn result = GST_FLOW_OK;
822   gboolean done = FALSE;
823   GstOggDemux *ogg;
824
825   ogg = pad->ogg;
826
827   while (!done) {
828     int ret;
829     ogg_packet packet;
830
831     ret = ogg_stream_packetout (&pad->map.stream, &packet);
832     switch (ret) {
833       case 0:
834         GST_LOG_OBJECT (ogg, "packetout done");
835         done = TRUE;
836         break;
837       case -1:
838         GST_LOG_OBJECT (ogg, "packetout discont");
839         gst_ogg_chain_mark_discont (pad->chain);
840         break;
841       case 1:
842         GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
843         result = gst_ogg_pad_submit_packet (pad, &packet);
844         if (GST_FLOW_IS_FATAL (result))
845           goto could_not_submit;
846         break;
847       default:
848         GST_WARNING_OBJECT (ogg,
849             "invalid return value %d for ogg_stream_packetout, resetting stream",
850             ret);
851         gst_ogg_pad_reset (pad);
852         break;
853     }
854     if (npackets > 0) {
855       npackets--;
856       done = (npackets == 0);
857     }
858   }
859   return result;
860
861   /* ERRORS */
862 could_not_submit:
863   {
864     GST_WARNING_OBJECT (ogg,
865         "could not submit packet for stream %08x, error: %d", pad->map.serialno,
866         result);
867     gst_ogg_pad_reset (pad);
868     return result;
869   }
870 }
871
872 /* submit a page to an oggpad, this function will then submit all
873  * the packets in the page.
874  */
875 static GstFlowReturn
876 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
877 {
878   GstFlowReturn result = GST_FLOW_OK;
879   GstOggDemux *ogg;
880   gboolean continued = FALSE;
881
882   ogg = pad->ogg;
883
884   /* for negative rates we read pages backwards and must therefore be carefull
885    * with continued pages */
886   if (ogg->segment.rate < 0.0) {
887     gint npackets;
888
889     continued = ogg_page_continued (page);
890
891     /* number of completed packets in the page */
892     npackets = ogg_page_packets (page);
893     if (!continued) {
894       /* page is not continued so it contains at least one packet start. It's
895        * possible that no packet ends on this page (npackets == 0). In that
896        * case, the next (continued) page(s) we kept contain the remainder of the
897        * packets. We mark npackets=1 to make us start decoding the pages in the
898        * remainder of the algorithm. */
899       if (npackets == 0)
900         npackets = 1;
901     }
902     GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
903
904     if (npackets == 0) {
905       GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
906       goto done;
907     }
908   }
909
910   if (ogg_stream_pagein (&pad->map.stream, page) != 0)
911     goto choked;
912
913   /* flush all packets in the stream layer, this might not give a packet if
914    * the page had no packets finishing on the page (npackets == 0). */
915   result = gst_ogg_pad_stream_out (pad, 0);
916
917   if (pad->continued) {
918     ogg_packet packet;
919
920     /* now send the continued pages to the stream layer */
921     while (pad->continued) {
922       ogg_page *p = (ogg_page *) pad->continued->data;
923
924       GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
925       if (ogg_stream_pagein (&pad->map.stream, p) != 0)
926         goto choked;
927
928       pad->continued = g_list_delete_link (pad->continued, pad->continued);
929
930       /* free the page */
931       gst_ogg_page_free (p);
932     }
933
934     GST_LOG_OBJECT (ogg, "flushing last continued packet");
935     /* flush 1 continued packet in the stream layer */
936     result = gst_ogg_pad_stream_out (pad, 1);
937
938     /* flush all remaining packets, we pushed them in the previous round.
939      * We don't use _reset() because we still want to get the discont when
940      * we submit a next page. */
941     while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
942   }
943
944 done:
945   /* keep continued pages (only in reverse mode) */
946   if (continued) {
947     ogg_page *p = gst_ogg_page_copy (page);
948
949     GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
950     pad->continued = g_list_prepend (pad->continued, p);
951   }
952
953   return result;
954
955 choked:
956   {
957     GST_WARNING_OBJECT (ogg,
958         "ogg stream choked on page (serial %08x), resetting stream",
959         pad->map.serialno);
960     gst_ogg_pad_reset (pad);
961     /* we continue to recover */
962     return GST_FLOW_OK;
963   }
964 }
965
966
967 static GstOggChain *
968 gst_ogg_chain_new (GstOggDemux * ogg)
969 {
970   GstOggChain *chain = g_new0 (GstOggChain, 1);
971
972   GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
973   chain->ogg = ogg;
974   chain->offset = -1;
975   chain->bytes = -1;
976   chain->have_bos = FALSE;
977   chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
978   chain->begin_time = GST_CLOCK_TIME_NONE;
979   chain->segment_start = GST_CLOCK_TIME_NONE;
980   chain->segment_stop = GST_CLOCK_TIME_NONE;
981   chain->total_time = GST_CLOCK_TIME_NONE;
982
983   return chain;
984 }
985
986 static void
987 gst_ogg_chain_free (GstOggChain * chain)
988 {
989   gint i;
990
991   for (i = 0; i < chain->streams->len; i++) {
992     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
993
994     gst_object_unref (pad);
995   }
996   g_array_free (chain->streams, TRUE);
997   g_free (chain);
998 }
999
1000 static void
1001 gst_ogg_chain_mark_discont (GstOggChain * chain)
1002 {
1003   gint i;
1004
1005   for (i = 0; i < chain->streams->len; i++) {
1006     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1007
1008     pad->discont = TRUE;
1009     pad->map.last_size = 0;
1010   }
1011 }
1012
1013 static void
1014 gst_ogg_chain_reset (GstOggChain * chain)
1015 {
1016   gint i;
1017
1018   for (i = 0; i < chain->streams->len; i++) {
1019     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1020
1021     gst_ogg_pad_reset (pad);
1022   }
1023 }
1024
1025 static GstOggPad *
1026 gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno)
1027 {
1028   GstOggPad *ret;
1029   GstTagList *list;
1030   gchar *name;
1031
1032   GST_DEBUG_OBJECT (chain->ogg, "creating new stream %08lx in chain %p",
1033       serialno, chain);
1034
1035   ret = g_object_new (GST_TYPE_OGG_PAD, NULL);
1036   /* we own this one */
1037   gst_object_ref (ret);
1038   gst_object_sink (ret);
1039
1040   GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1041   ret->discont = TRUE;
1042   ret->map.last_size = 0;
1043
1044   ret->chain = chain;
1045   ret->ogg = chain->ogg;
1046
1047   ret->map.serialno = serialno;
1048   if (ogg_stream_init (&ret->map.stream, serialno) != 0)
1049     goto init_failed;
1050
1051   name = g_strdup_printf ("serial_%08lx", serialno);
1052   gst_object_set_name (GST_OBJECT (ret), name);
1053   g_free (name);
1054
1055   /* FIXME: either do something with it or remove it */
1056   list = gst_tag_list_new ();
1057   gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1058       NULL);
1059   gst_tag_list_free (list);
1060
1061   GST_DEBUG_OBJECT (chain->ogg,
1062       "created new ogg src %p for stream with serial %08lx", ret, serialno);
1063
1064   g_array_append_val (chain->streams, ret);
1065
1066   return ret;
1067
1068   /* ERRORS */
1069 init_failed:
1070   {
1071     GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
1072         serialno);
1073     gst_object_unref (ret);
1074     return NULL;
1075   }
1076 }
1077
1078 static GstOggPad *
1079 gst_ogg_chain_get_stream (GstOggChain * chain, glong serialno)
1080 {
1081   gint i;
1082
1083   for (i = 0; i < chain->streams->len; i++) {
1084     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1085
1086     if (pad->map.serialno == serialno)
1087       return pad;
1088   }
1089   return NULL;
1090 }
1091
1092 static gboolean
1093 gst_ogg_chain_has_stream (GstOggChain * chain, glong serialno)
1094 {
1095   return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1096 }
1097
1098 #define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain))
1099
1100 /* signals and args */
1101 enum
1102 {
1103   /* FILL ME */
1104   LAST_SIGNAL
1105 };
1106
1107 enum
1108 {
1109   ARG_0
1110       /* FILL ME */
1111 };
1112
1113 static GstStaticPadTemplate ogg_demux_src_template_factory =
1114 GST_STATIC_PAD_TEMPLATE ("src_%d",
1115     GST_PAD_SRC,
1116     GST_PAD_SOMETIMES,
1117     GST_STATIC_CAPS_ANY);
1118
1119 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1120     GST_STATIC_PAD_TEMPLATE ("sink",
1121     GST_PAD_SINK,
1122     GST_PAD_ALWAYS,
1123     GST_STATIC_CAPS ("application/ogg; application/x-annodex")
1124     );
1125
1126 static void gst_ogg_demux_finalize (GObject * object);
1127
1128 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1129     GstOggChain ** chain);
1130 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1131     GstOggChain * chain);
1132
1133 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event);
1134 static void gst_ogg_demux_loop (GstOggPad * pad);
1135 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer);
1136 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad);
1137 static gboolean gst_ogg_demux_sink_activate_pull (GstPad * sinkpad,
1138     gboolean active);
1139 static gboolean gst_ogg_demux_sink_activate_push (GstPad * sinkpad,
1140     gboolean active);
1141 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1142     GstStateChange transition);
1143 static void gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
1144
1145 static void gst_ogg_print (GstOggDemux * demux);
1146
1147 GST_BOILERPLATE (GstOggDemux, gst_ogg_demux, GstElement, GST_TYPE_ELEMENT);
1148
1149 static void
1150 gst_ogg_demux_base_init (gpointer g_class)
1151 {
1152   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1153
1154   gst_element_class_set_details (element_class, &gst_ogg_demux_details);
1155
1156   gst_element_class_add_pad_template (element_class,
1157       gst_static_pad_template_get (&ogg_demux_sink_template_factory));
1158   gst_element_class_add_pad_template (element_class,
1159       gst_static_pad_template_get (&ogg_demux_src_template_factory));
1160 }
1161
1162 static void
1163 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1164 {
1165   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1166   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1167
1168   gstelement_class->change_state = gst_ogg_demux_change_state;
1169   gstelement_class->send_event = gst_ogg_demux_receive_event;
1170
1171   gobject_class->finalize = gst_ogg_demux_finalize;
1172 }
1173
1174 static void
1175 gst_ogg_demux_init (GstOggDemux * ogg, GstOggDemuxClass * g_class)
1176 {
1177   /* create the sink pad */
1178   ogg->sinkpad =
1179       gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
1180       "sink");
1181
1182   gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
1183   gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
1184   gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
1185   gst_pad_set_activatepull_function (ogg->sinkpad,
1186       gst_ogg_demux_sink_activate_pull);
1187   gst_pad_set_activatepush_function (ogg->sinkpad,
1188       gst_ogg_demux_sink_activate_push);
1189   gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
1190
1191   ogg->chain_lock = g_mutex_new ();
1192   ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
1193
1194   ogg->newsegment = NULL;
1195 }
1196
1197 static void
1198 gst_ogg_demux_finalize (GObject * object)
1199 {
1200   GstOggDemux *ogg;
1201
1202   ogg = GST_OGG_DEMUX (object);
1203
1204   g_array_free (ogg->chains, TRUE);
1205   g_mutex_free (ogg->chain_lock);
1206   ogg_sync_clear (&ogg->sync);
1207
1208   if (ogg->newsegment)
1209     gst_event_unref (ogg->newsegment);
1210
1211   G_OBJECT_CLASS (parent_class)->finalize (object);
1212 }
1213
1214 static gboolean
1215 gst_ogg_demux_sink_event (GstPad * pad, GstEvent * event)
1216 {
1217   gboolean res;
1218   GstOggDemux *ogg;
1219
1220   ogg = GST_OGG_DEMUX (gst_pad_get_parent (pad));
1221
1222   switch (GST_EVENT_TYPE (event)) {
1223     case GST_EVENT_NEWSEGMENT:
1224       /* FIXME */
1225       GST_DEBUG_OBJECT (ogg, "got a new segment event");
1226       ogg_sync_reset (&ogg->sync);
1227       gst_event_unref (event);
1228       res = TRUE;
1229       break;
1230     case GST_EVENT_EOS:
1231     {
1232       GST_DEBUG_OBJECT (ogg, "got an EOS event");
1233       res = gst_pad_event_default (pad, event);
1234       if (ogg->current_chain == NULL) {
1235         GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
1236             ("can't get first chain"));
1237       }
1238       break;
1239     }
1240     default:
1241       res = gst_pad_event_default (pad, event);
1242       break;
1243   }
1244   gst_object_unref (ogg);
1245
1246   return res;
1247 }
1248
1249 /* submit the given buffer to the ogg sync.
1250  *
1251  * Returns the number of bytes submited.
1252  */
1253 static GstFlowReturn
1254 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
1255 {
1256   gint size;
1257   guint8 *data;
1258   gchar *oggbuffer;
1259   GstFlowReturn ret = GST_FLOW_OK;
1260
1261   size = GST_BUFFER_SIZE (buffer);
1262   data = GST_BUFFER_DATA (buffer);
1263
1264   GST_DEBUG_OBJECT (ogg, "submitting %u bytes", size);
1265   if (G_UNLIKELY (size == 0))
1266     goto done;
1267
1268   oggbuffer = ogg_sync_buffer (&ogg->sync, size);
1269   if (G_UNLIKELY (oggbuffer == NULL))
1270     goto no_buffer;
1271
1272   memcpy (oggbuffer, data, size);
1273   if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
1274     goto write_failed;
1275
1276 done:
1277   gst_buffer_unref (buffer);
1278
1279   return ret;
1280
1281   /* ERRORS */
1282 no_buffer:
1283   {
1284     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1285         (NULL), ("failed to get ogg sync buffer"));
1286     ret = GST_FLOW_ERROR;
1287     goto done;
1288   }
1289 write_failed:
1290   {
1291     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
1292         (NULL), ("failed to write %d bytes to the sync buffer", size));
1293     ret = GST_FLOW_ERROR;
1294     goto done;
1295   }
1296 }
1297
1298 /* in random access mode this code updates the current read position
1299  * and resets the ogg sync buffer so that the next read will happen
1300  * from this new location.
1301  */
1302 static void
1303 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
1304 {
1305   GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
1306
1307   ogg->offset = offset;
1308   ogg->read_offset = offset;
1309   ogg_sync_reset (&ogg->sync);
1310 }
1311
1312 /* read more data from the current offset and submit to
1313  * the ogg sync layer.
1314  */
1315 static GstFlowReturn
1316 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
1317 {
1318   GstFlowReturn ret;
1319   GstBuffer *buffer;
1320
1321   GST_LOG_OBJECT (ogg,
1322       "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
1323       ogg->read_offset, ogg->length, end_offset);
1324
1325   if (end_offset > 0 && ogg->read_offset >= end_offset)
1326     goto boundary_reached;
1327
1328   if (ogg->read_offset == ogg->length)
1329     goto eos;
1330
1331   ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
1332   if (ret != GST_FLOW_OK)
1333     goto error;
1334
1335   ogg->read_offset += GST_BUFFER_SIZE (buffer);
1336
1337   ret = gst_ogg_demux_submit_buffer (ogg, buffer);
1338
1339   return ret;
1340
1341   /* ERROR */
1342 boundary_reached:
1343   {
1344     GST_LOG_OBJECT (ogg, "reached boundary");
1345     return GST_FLOW_LIMIT;
1346   }
1347 eos:
1348   {
1349     GST_LOG_OBJECT (ogg, "reached EOS");
1350     return GST_FLOW_UNEXPECTED;
1351   }
1352 error:
1353   {
1354     GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
1355         gst_flow_get_name (ret));
1356     return ret;
1357   }
1358 }
1359
1360 /* Read the next page from the current offset.
1361  * boundary: number of bytes ahead we allow looking for;
1362  * -1 if no boundary
1363  *
1364  * @offset will contain the offset the next page starts at when this function
1365  * returns GST_FLOW_OK.
1366  *
1367  * GST_FLOW_UNEXPECTED is returned on EOS.
1368  *
1369  * GST_FLOW_LIMIT is returned when we did not find a page before the
1370  * boundary. If @boundary is -1, this is never returned.
1371  *
1372  * Any other error returned while retrieving data from the peer is returned as
1373  * is.
1374  */
1375 static GstFlowReturn
1376 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary,
1377     gint64 * offset)
1378 {
1379   gint64 end_offset = -1;
1380   GstFlowReturn ret;
1381
1382   GST_LOG_OBJECT (ogg,
1383       "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
1384       G_GINT64_FORMAT, ogg->offset, boundary);
1385
1386   if (boundary >= 0)
1387     end_offset = ogg->offset + boundary;
1388
1389   while (TRUE) {
1390     glong more;
1391
1392     if (end_offset > 0 && ogg->offset >= end_offset)
1393       goto boundary_reached;
1394
1395     more = ogg_sync_pageseek (&ogg->sync, og);
1396
1397     GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
1398
1399     if (more < 0) {
1400       /* skipped n bytes */
1401       ogg->offset -= more;
1402       GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT, more,
1403           ogg->offset);
1404     } else if (more == 0) {
1405       /* we need more data */
1406       if (boundary == 0)
1407         goto boundary_reached;
1408
1409       GST_LOG_OBJECT (ogg, "need more data");
1410       ret = gst_ogg_demux_get_data (ogg, end_offset);
1411       if (ret != GST_FLOW_OK)
1412         break;
1413     } else {
1414       gint64 res_offset = ogg->offset;
1415
1416       /* got a page.  Return the offset at the page beginning,
1417          advance the internal offset past the page end */
1418       if (offset)
1419         *offset = res_offset;
1420       ret = GST_FLOW_OK;
1421
1422       ogg->offset += more;
1423
1424       GST_LOG_OBJECT (ogg,
1425           "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
1426           G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
1427           ogg_page_serialno (og), ogg->offset,
1428           (gint64) ogg_page_granulepos (og));
1429       break;
1430     }
1431   }
1432   GST_LOG_OBJECT (ogg, "returning %d", ret);
1433
1434   return ret;
1435
1436   /* ERRORS */
1437 boundary_reached:
1438   {
1439     GST_LOG_OBJECT (ogg,
1440         "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
1441         ogg->offset, end_offset);
1442     return GST_FLOW_LIMIT;
1443   }
1444 }
1445
1446 /* from the current offset, find the previous page, seeking backwards
1447  * until we find the page. 
1448  */
1449 static GstFlowReturn
1450 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
1451 {
1452   GstFlowReturn ret;
1453   gint64 begin = ogg->offset;
1454   gint64 end = begin;
1455   gint64 cur_offset = -1;
1456
1457   GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
1458
1459   while (cur_offset == -1) {
1460     begin -= CHUNKSIZE;
1461     if (begin < 0)
1462       begin = 0;
1463
1464     /* seek CHUNKSIZE back */
1465     gst_ogg_demux_seek (ogg, begin);
1466
1467     /* now continue reading until we run out of data, if we find a page
1468      * start, we save it. It might not be the final page as there could be
1469      * another page after this one. */
1470     while (ogg->offset < end) {
1471       gint64 new_offset;
1472
1473       ret =
1474           gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
1475       /* we hit the upper limit, offset contains the last page start */
1476       if (ret == GST_FLOW_LIMIT) {
1477         GST_LOG_OBJECT (ogg, "hit limit");
1478         break;
1479       }
1480       /* something went wrong */
1481       if (ret == GST_FLOW_UNEXPECTED) {
1482         new_offset = 0;
1483         GST_LOG_OBJECT (ogg, "got unexpected");
1484       } else if (ret != GST_FLOW_OK) {
1485         GST_LOG_OBJECT (ogg, "got error %d", ret);
1486         return ret;
1487       }
1488
1489       GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
1490
1491       /* offset is next page start */
1492       cur_offset = new_offset;
1493     }
1494   }
1495
1496   GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
1497
1498   /* we have the offset.  Actually snork and hold the page now */
1499   gst_ogg_demux_seek (ogg, cur_offset);
1500   ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
1501   if (ret != GST_FLOW_OK) {
1502     GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
1503         cur_offset);
1504     /* this shouldn't be possible */
1505     return ret;
1506   }
1507
1508   if (offset)
1509     *offset = cur_offset;
1510
1511   return ret;
1512 }
1513
1514 static gboolean
1515 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
1516 {
1517   gint i;
1518   GstOggChain *chain = ogg->current_chain;
1519
1520   if (chain == NULL)
1521     return TRUE;
1522
1523   GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
1524
1525   /* send EOS on all the pads */
1526   for (i = 0; i < chain->streams->len; i++) {
1527     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1528     GstEvent *event;
1529
1530     if (pad->map.is_skeleton)
1531       continue;
1532
1533     event = gst_event_new_eos ();
1534     gst_event_set_seqnum (event, ogg->seqnum);
1535     gst_pad_push_event (GST_PAD_CAST (pad), event);
1536
1537     GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
1538
1539     /* deactivate first */
1540     gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
1541
1542     gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
1543   }
1544   /* if we cannot seek back to the chain, we can destroy the chain 
1545    * completely */
1546   if (!ogg->seekable) {
1547     gst_ogg_chain_free (chain);
1548   }
1549   ogg->current_chain = NULL;
1550
1551   return TRUE;
1552 }
1553
1554 static gboolean
1555 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
1556     GstEvent * event)
1557 {
1558   gint i;
1559
1560   if (chain == ogg->current_chain) {
1561     if (event)
1562       gst_event_unref (event);
1563     return TRUE;
1564   }
1565
1566   /* FIXME, should not be called with NULL */
1567   if (chain != NULL) {
1568     GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
1569
1570     /* first add the pads */
1571     for (i = 0; i < chain->streams->len; i++) {
1572       GstOggPad *pad;
1573       GstStructure *structure;
1574
1575       pad = g_array_index (chain->streams, GstOggPad *, i);
1576
1577       if (pad->map.is_skeleton)
1578         continue;
1579
1580       GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
1581
1582       /* mark discont */
1583       pad->discont = TRUE;
1584       pad->map.last_size = 0;
1585       pad->last_ret = GST_FLOW_OK;
1586
1587       structure = gst_caps_get_structure (GST_PAD_CAPS (pad), 0);
1588       pad->is_sparse =
1589           gst_structure_has_name (structure, "application/x-ogm-text") ||
1590           gst_structure_has_name (structure, "text/x-cmml") ||
1591           gst_structure_has_name (structure, "subtitle/x-kate") ||
1592           gst_structure_has_name (structure, "application/x-kate");
1593
1594       /* activate first */
1595       gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
1596
1597       gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
1598     }
1599   }
1600
1601   /* after adding the new pads, remove the old pads */
1602   gst_ogg_demux_deactivate_current_chain (ogg);
1603
1604   ogg->current_chain = chain;
1605
1606   /* we are finished now */
1607   gst_element_no_more_pads (GST_ELEMENT (ogg));
1608
1609   /* FIXME, must be sent from the streaming thread */
1610   if (event) {
1611     gst_ogg_demux_send_event (ogg, event);
1612
1613     gst_element_found_tags (GST_ELEMENT_CAST (ogg),
1614         gst_tag_list_new_full (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL));
1615   }
1616
1617   GST_DEBUG_OBJECT (ogg, "starting chain");
1618
1619   /* then send out any headers and queued buffers */
1620   for (i = 0; i < chain->streams->len; i++) {
1621     GList *walk;
1622     GstOggPad *pad;
1623
1624     pad = g_array_index (chain->streams, GstOggPad *, i);
1625
1626     GST_DEBUG_OBJECT (ogg, "pushing headers");
1627     /* ref and push headers */
1628     for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
1629       GstBuffer *buffer = GST_BUFFER (walk->data);
1630
1631       if (pad->discont) {
1632         GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
1633         pad->discont = FALSE;
1634       }
1635       /* we don't care about the return value here */
1636       gst_pad_push (GST_PAD_CAST (pad), gst_buffer_ref (buffer));
1637     }
1638
1639     GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
1640     /* push queued buffers */
1641     for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
1642       GstBuffer *buffer = GST_BUFFER (walk->data);
1643
1644       /* we don't care about the return value here */
1645       gst_pad_push (GST_PAD_CAST (pad), buffer);
1646     }
1647     /* and free the queued buffers */
1648     g_list_free (pad->map.queued);
1649     pad->map.queued = NULL;
1650   }
1651   return TRUE;
1652 }
1653
1654 static gboolean
1655 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
1656     gint64 end, gint64 begintime, gint64 endtime, gint64 target,
1657     gint64 * offset)
1658 {
1659   gint64 best;
1660   GstFlowReturn ret;
1661   gint64 result = 0;
1662
1663   best = begin;
1664
1665   GST_DEBUG_OBJECT (ogg,
1666       "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, begin,
1667       end);
1668   GST_DEBUG_OBJECT (ogg,
1669       "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
1670       GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
1671   GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
1672
1673   /* perform the seek */
1674   while (begin < end) {
1675     gint64 bisect;
1676
1677     if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
1678       bisect = begin;
1679     } else {
1680       /* take a (pretty decent) guess, avoiding overflow */
1681       gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
1682
1683       bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
1684
1685       if (bisect <= begin)
1686         bisect = begin;
1687       GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
1688     }
1689     gst_ogg_demux_seek (ogg, bisect);
1690
1691     while (begin < end) {
1692       ogg_page og;
1693
1694       GST_DEBUG_OBJECT (ogg,
1695           "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
1696           ", end %" G_GINT64_FORMAT, bisect, begin, end);
1697
1698       ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
1699       GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
1700           result);
1701
1702       if (ret == GST_FLOW_LIMIT) {
1703         /* we hit the upper limit, go back a bit */
1704         if (bisect <= begin + 1) {
1705           end = begin;          /* found it */
1706         } else {
1707           if (bisect == 0)
1708             goto seek_error;
1709
1710           bisect -= CHUNKSIZE;
1711           if (bisect <= begin)
1712             bisect = begin + 1;
1713
1714           gst_ogg_demux_seek (ogg, bisect);
1715         }
1716       } else if (ret == GST_FLOW_OK) {
1717         /* found offset of next ogg page */
1718         gint64 granulepos;
1719         GstClockTime granuletime;
1720         GstOggPad *pad;
1721
1722         /* get the granulepos */
1723         GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
1724             result);
1725         granulepos = ogg_page_granulepos (&og);
1726         if (granulepos == -1) {
1727           GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
1728           continue;
1729         }
1730
1731         /* get the stream */
1732         pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
1733         if (pad == NULL || pad->map.is_skeleton)
1734           continue;
1735
1736         /* convert granulepos to time */
1737         granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
1738             granulepos);
1739         if (granuletime < pad->start_time)
1740           continue;
1741
1742         GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
1743             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
1744
1745         granuletime -= pad->start_time;
1746         granuletime += begintime;
1747
1748         GST_DEBUG_OBJECT (ogg,
1749             "found page with granule %" G_GINT64_FORMAT " and time %"
1750             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
1751
1752         if (granuletime < target) {
1753           best = result;        /* raw offset of packet with granulepos */
1754           begin = ogg->offset;  /* raw offset of next page */
1755           begintime = granuletime;
1756
1757           bisect = begin;       /* *not* begin + 1 */
1758         } else {
1759           if (bisect <= begin + 1) {
1760             end = begin;        /* found it */
1761           } else {
1762             if (end == ogg->offset) {   /* we're pretty close - we'd be stuck in */
1763               end = result;
1764               bisect -= CHUNKSIZE;      /* an endless loop otherwise. */
1765               if (bisect <= begin)
1766                 bisect = begin + 1;
1767               gst_ogg_demux_seek (ogg, bisect);
1768             } else {
1769               end = result;
1770               endtime = granuletime;
1771               break;
1772             }
1773           }
1774         }
1775       } else
1776         goto seek_error;
1777     }
1778   }
1779   GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
1780   gst_ogg_demux_seek (ogg, best);
1781   *offset = best;
1782
1783   return TRUE;
1784
1785   /* ERRORS */
1786 seek_error:
1787   {
1788     GST_DEBUG_OBJECT (ogg, "got a seek error");
1789     return FALSE;
1790   }
1791 }
1792
1793 /*
1794  * do seek to time @position, return FALSE or chain and TRUE
1795  */
1796 static gboolean
1797 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
1798     gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
1799 {
1800   guint64 position;
1801   GstOggChain *chain = NULL;
1802   gint64 begin, end;
1803   gint64 begintime, endtime;
1804   gint64 target, keytarget;
1805   gint64 best;
1806   gint64 total;
1807   gint64 result = 0;
1808   GstFlowReturn ret;
1809   gint i, pending, len;
1810
1811   position = segment->last_stop;
1812
1813   /* first find the chain to search in */
1814   total = ogg->total_time;
1815   if (ogg->chains->len == 0)
1816     goto no_chains;
1817
1818   for (i = ogg->chains->len - 1; i >= 0; i--) {
1819     chain = g_array_index (ogg->chains, GstOggChain *, i);
1820     total -= chain->total_time;
1821     if (position >= total)
1822       break;
1823   }
1824
1825   /* first step, locate page containing the required data */
1826   begin = chain->offset;
1827   end = chain->end_offset;
1828   begintime = chain->begin_time;
1829   endtime = begintime + chain->total_time;
1830   target = position - total + begintime;
1831
1832   if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
1833           &best))
1834     goto seek_error;
1835
1836   /* second step: find pages for all streams, we use the keyframe_granule to keep
1837    * track of which ones we saw. If we have seen a page for each stream we can
1838    * calculate the positions of each keyframe. */
1839   GST_DEBUG_OBJECT (ogg, "find keyframes");
1840   len = pending = chain->streams->len;
1841
1842   /* figure out where the keyframes are */
1843   keytarget = target;
1844
1845   while (TRUE) {
1846     ogg_page og;
1847     gint64 granulepos;
1848     GstOggPad *pad;
1849     GstClockTime keyframe_time, granule_time;
1850
1851     ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
1852     GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
1853         result);
1854     if (ret == GST_FLOW_LIMIT) {
1855       GST_LOG_OBJECT (ogg, "reached limit");
1856       break;
1857     }
1858
1859     /* get the stream */
1860     pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
1861     if (pad == NULL)
1862       continue;
1863
1864     if (pad->map.is_skeleton)
1865       goto next;
1866
1867     granulepos = ogg_page_granulepos (&og);
1868     if (granulepos == -1) {
1869       GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
1870       continue;
1871     }
1872
1873     /* in reverse we want to go past the page with the lower timestamp */
1874     if (segment->rate < 0.0) {
1875       /* get time for this pad */
1876       granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
1877           granulepos);
1878
1879       GST_LOG_OBJECT (ogg,
1880           "looking at page with ts %" GST_TIME_FORMAT ", target %"
1881           GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
1882           GST_TIME_ARGS (target));
1883       if (granule_time < target)
1884         continue;
1885     }
1886
1887     /* we've seen this pad before */
1888     if (pad->keyframe_granule != -1)
1889       continue;
1890
1891     /* convert granule of this pad to the granule of the keyframe */
1892     pad->keyframe_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map,
1893         granulepos);
1894     GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
1895         pad->keyframe_granule);
1896
1897     /* get time of the keyframe */
1898     keyframe_time =
1899         gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
1900     GST_LOG_OBJECT (ogg, "stream %08x granule time %" GST_TIME_FORMAT,
1901         pad->map.serialno, GST_TIME_ARGS (keyframe_time));
1902
1903     /* collect smallest value */
1904     if (keyframe_time != -1) {
1905       keyframe_time += begintime;
1906       if (keyframe_time < keytarget)
1907         keytarget = keyframe_time;
1908     }
1909
1910   next:
1911     pending--;
1912     if (pending == 0)
1913       break;
1914   }
1915
1916   /* for negative rates we will get to the keyframe backwards */
1917   if (segment->rate < 0.0)
1918     goto done;
1919
1920   if (keytarget != target) {
1921     GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
1922         GST_TIME_ARGS (keytarget));
1923
1924     /* last step, seek to the location of the keyframe */
1925     if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
1926             keytarget, &best))
1927       goto seek_error;
1928   } else {
1929     /* seek back to previous position */
1930     GST_LOG_OBJECT (ogg, "keyframe on target");
1931     gst_ogg_demux_seek (ogg, best);
1932   }
1933
1934 done:
1935   if (keyframe) {
1936     if (segment->rate > 0.0)
1937       segment->time = keytarget;
1938     segment->last_stop = keytarget - begintime;
1939   }
1940
1941   *rchain = chain;
1942
1943   return TRUE;
1944
1945 no_chains:
1946   {
1947     GST_DEBUG_OBJECT (ogg, "no chains");
1948     return FALSE;
1949   }
1950 seek_error:
1951   {
1952     GST_DEBUG_OBJECT (ogg, "got a seek error");
1953     return FALSE;
1954   }
1955 }
1956
1957 /* does not take ownership of the event */
1958 static gboolean
1959 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
1960 {
1961   GstOggChain *chain = NULL;
1962   gboolean res;
1963   gboolean flush, accurate, keyframe;
1964   GstFormat format;
1965   gdouble rate;
1966   GstSeekFlags flags;
1967   GstSeekType cur_type, stop_type;
1968   gint64 cur, stop;
1969   gboolean update;
1970   guint32 seqnum;
1971   GstEvent *tevent;
1972
1973   if (event) {
1974     GST_DEBUG_OBJECT (ogg, "seek with event");
1975
1976     gst_event_parse_seek (event, &rate, &format, &flags,
1977         &cur_type, &cur, &stop_type, &stop);
1978
1979     /* we can only seek on time */
1980     if (format != GST_FORMAT_TIME) {
1981       GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
1982       goto error;
1983     }
1984     seqnum = gst_event_get_seqnum (event);
1985   } else {
1986     GST_DEBUG_OBJECT (ogg, "seek without event");
1987
1988     flags = 0;
1989     rate = 1.0;
1990     seqnum = gst_util_seqnum_next ();
1991   }
1992
1993   GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
1994
1995   flush = flags & GST_SEEK_FLAG_FLUSH;
1996   accurate = flags & GST_SEEK_FLAG_ACCURATE;
1997   keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
1998
1999   /* first step is to unlock the streaming thread if it is
2000    * blocked in a chain call, we do this by starting the flush. because
2001    * we cannot yet hold any streaming lock, we have to protect the chains
2002    * with their own lock. */
2003   if (flush) {
2004     gint i;
2005
2006     tevent = gst_event_new_flush_start ();
2007     gst_event_set_seqnum (tevent, seqnum);
2008
2009     gst_event_ref (tevent);
2010     gst_pad_push_event (ogg->sinkpad, tevent);
2011
2012     GST_CHAIN_LOCK (ogg);
2013     for (i = 0; i < ogg->chains->len; i++) {
2014       GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2015       gint j;
2016
2017       for (j = 0; j < chain->streams->len; j++) {
2018         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
2019
2020         gst_event_ref (tevent);
2021         gst_pad_push_event (GST_PAD (pad), tevent);
2022       }
2023     }
2024     GST_CHAIN_UNLOCK (ogg);
2025
2026     gst_event_unref (tevent);
2027   } else {
2028     gst_pad_pause_task (ogg->sinkpad);
2029   }
2030
2031   /* now grab the stream lock so that streaming cannot continue, for
2032    * non flushing seeks when the element is in PAUSED this could block
2033    * forever. */
2034   GST_PAD_STREAM_LOCK (ogg->sinkpad);
2035
2036   if (ogg->segment_running && !flush) {
2037     /* create the segment event to close the current segment */
2038     if ((chain = ogg->current_chain)) {
2039       GstEvent *newseg;
2040       gint64 chain_start = 0;
2041
2042       if (chain->segment_start != GST_CLOCK_TIME_NONE)
2043         chain_start = chain->segment_start;
2044
2045       newseg = gst_event_new_new_segment (TRUE, ogg->segment.rate,
2046           GST_FORMAT_TIME, ogg->segment.start + chain_start,
2047           ogg->segment.last_stop + chain_start, ogg->segment.time);
2048       /* set the seqnum of the running segment */
2049       gst_event_set_seqnum (newseg, ogg->seqnum);
2050
2051       /* send segment on old chain, FIXME, must be sent from streaming thread. */
2052       gst_ogg_demux_send_event (ogg, newseg);
2053     }
2054   }
2055
2056   if (event) {
2057     gst_segment_set_seek (&ogg->segment, rate, format, flags,
2058         cur_type, cur, stop_type, stop, &update);
2059   }
2060
2061   GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
2062       GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
2063       GST_TIME_ARGS (ogg->segment.stop));
2064
2065   /* we need to stop flushing on the srcpad as we're going to use it
2066    * next. We can do this as we have the STREAM lock now. */
2067   if (flush) {
2068     tevent = gst_event_new_flush_stop ();
2069     gst_event_set_seqnum (tevent, seqnum);
2070     gst_pad_push_event (ogg->sinkpad, tevent);
2071   }
2072
2073   {
2074     gint i;
2075
2076     /* reset all ogg streams now, need to do this from within the lock to
2077      * make sure the streaming thread is not messing with the stream */
2078     for (i = 0; i < ogg->chains->len; i++) {
2079       GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2080
2081       gst_ogg_chain_reset (chain);
2082     }
2083   }
2084
2085   /* for reverse we will already seek accurately */
2086   res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
2087
2088   /* seek failed, make sure we continue the current chain */
2089   if (!res) {
2090     GST_DEBUG_OBJECT (ogg, "seek failed");
2091     chain = ogg->current_chain;
2092   } else {
2093     GST_DEBUG_OBJECT (ogg, "seek success");
2094   }
2095
2096   if (!chain)
2097     goto no_chain;
2098
2099   /* now we have a new position, prepare for streaming again */
2100   {
2101     GstEvent *event;
2102     gint64 stop;
2103     gint64 start;
2104     gint64 last_stop, begin_time;
2105
2106     /* we have to send the flush to the old chain, not the new one */
2107     if (flush) {
2108       tevent = gst_event_new_flush_stop ();
2109       gst_event_set_seqnum (tevent, seqnum);
2110       gst_ogg_demux_send_event (ogg, tevent);
2111     }
2112
2113     /* we need this to see how far inside the chain we need to start */
2114     if (chain->begin_time != GST_CLOCK_TIME_NONE)
2115       begin_time = chain->begin_time;
2116     else
2117       begin_time = 0;
2118
2119     /* segment.start gives the start over all chains, we calculate the amount
2120      * of time into this chain we need to start */
2121     start = ogg->segment.start - begin_time;
2122     if (chain->segment_start != GST_CLOCK_TIME_NONE)
2123       start += chain->segment_start;
2124
2125     if ((stop = ogg->segment.stop) == -1)
2126       stop = ogg->segment.duration;
2127
2128     /* segment.stop gives the stop time over all chains, calculate the amount of
2129      * time we need to stop in this chain */
2130     if (stop != -1) {
2131       if (stop > begin_time)
2132         stop -= begin_time;
2133       else
2134         stop = 0;
2135       stop += chain->segment_start;
2136       /* we must stop when this chain ends and switch to the next chain to play
2137        * the remainder of the segment. */
2138       stop = MIN (stop, chain->segment_stop);
2139     }
2140
2141     last_stop = ogg->segment.last_stop - begin_time;
2142     if (chain->segment_start != GST_CLOCK_TIME_NONE)
2143       last_stop += chain->segment_start;
2144
2145     /* create the segment event we are going to send out */
2146     if (ogg->segment.rate >= 0.0)
2147       event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2148           ogg->segment.format, last_stop, stop, ogg->segment.time);
2149     else
2150       event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2151           ogg->segment.format, start, last_stop, ogg->segment.time);
2152
2153     gst_event_set_seqnum (event, seqnum);
2154
2155     if (chain != ogg->current_chain) {
2156       /* switch to different chain, send segment on new chain */
2157       gst_ogg_demux_activate_chain (ogg, chain, event);
2158     } else {
2159       /* mark discont and send segment on current chain */
2160       gst_ogg_chain_mark_discont (chain);
2161       /* This event should be sent from the streaming thread (sink pad task) */
2162       if (ogg->newsegment)
2163         gst_event_unref (ogg->newsegment);
2164       ogg->newsegment = event;
2165     }
2166
2167     /* notify start of new segment */
2168     if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
2169       GstMessage *message;
2170
2171       message = gst_message_new_segment_start (GST_OBJECT (ogg),
2172           GST_FORMAT_TIME, ogg->segment.last_stop);
2173       gst_message_set_seqnum (message, seqnum);
2174
2175       gst_element_post_message (GST_ELEMENT (ogg), message);
2176     }
2177
2178     ogg->segment_running = TRUE;
2179     ogg->seqnum = seqnum;
2180     /* restart our task since it might have been stopped when we did the 
2181      * flush. */
2182     gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
2183         ogg->sinkpad);
2184   }
2185
2186   /* streaming can continue now */
2187   GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
2188
2189   return res;
2190
2191 error:
2192   {
2193     GST_DEBUG_OBJECT (ogg, "seek failed");
2194     return FALSE;
2195   }
2196 no_chain:
2197   {
2198     GST_DEBUG_OBJECT (ogg, "no chain to seek in");
2199     GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
2200     return FALSE;
2201   }
2202 }
2203
2204 /* finds each bitstream link one at a time using a bisection search
2205  * (has to begin by knowing the offset of the lb's initial page).
2206  * Recurses for each link so it can alloc the link storage after
2207  * finding them all, then unroll and fill the cache at the same time 
2208  */
2209 static GstFlowReturn
2210 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
2211     gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
2212 {
2213   gint64 endsearched = end;
2214   gint64 next = end;
2215   ogg_page og;
2216   GstFlowReturn ret;
2217   gint64 offset;
2218   GstOggChain *nextchain;
2219
2220   GST_LOG_OBJECT (ogg,
2221       "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
2222       ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
2223
2224   /* the below guards against garbage seperating the last and
2225    * first pages of two links. */
2226   while (searched < endsearched) {
2227     gint64 bisect;
2228
2229     if (endsearched - searched < CHUNKSIZE) {
2230       bisect = searched;
2231     } else {
2232       bisect = (searched + endsearched) / 2;
2233     }
2234
2235     gst_ogg_demux_seek (ogg, bisect);
2236     ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
2237
2238     if (ret == GST_FLOW_UNEXPECTED) {
2239       endsearched = bisect;
2240     } else if (ret == GST_FLOW_OK) {
2241       glong serial = ogg_page_serialno (&og);
2242
2243       if (!gst_ogg_chain_has_stream (chain, serial)) {
2244         endsearched = bisect;
2245         next = offset;
2246       } else {
2247         searched = offset + og.header_len + og.body_len;
2248       }
2249     } else
2250       return ret;
2251   }
2252
2253   GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
2254
2255   chain->end_offset = searched;
2256   ret = gst_ogg_demux_read_end_chain (ogg, chain);
2257   if (ret != GST_FLOW_OK)
2258     return ret;
2259
2260   GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
2261
2262   gst_ogg_demux_seek (ogg, next);
2263   ret = gst_ogg_demux_read_chain (ogg, &nextchain);
2264   if (ret == GST_FLOW_UNEXPECTED) {
2265     nextchain = NULL;
2266     ret = GST_FLOW_OK;
2267     GST_LOG_OBJECT (ogg, "no next chain");
2268   } else if (ret != GST_FLOW_OK)
2269     goto done;
2270
2271   if (searched < end && nextchain != NULL) {
2272     ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
2273         end, nextchain, m + 1);
2274     if (ret != GST_FLOW_OK)
2275       goto done;
2276   }
2277   GST_LOG_OBJECT (ogg, "adding chain %p", chain);
2278
2279   g_array_insert_val (ogg->chains, 0, chain);
2280
2281 done:
2282   return ret;
2283 }
2284
2285 /* read a chain from the ogg file. This code will
2286  * read all BOS pages and will create and return a GstOggChain 
2287  * structure with the results. 
2288  * 
2289  * This function will also read N pages from each stream in the
2290  * chain and submit them to the decoders. When the decoder has
2291  * decoded the first buffer, we know the timestamp of the first
2292  * page in the chain.
2293  */
2294 static GstFlowReturn
2295 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
2296 {
2297   GstFlowReturn ret;
2298   GstOggChain *chain = NULL;
2299   gint64 offset = ogg->offset;
2300   ogg_page op;
2301   gboolean done;
2302   gint i;
2303
2304   GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
2305
2306   /* first read the BOS pages, do typefind on them, create
2307    * the decoders, send data to the decoders. */
2308   while (TRUE) {
2309     GstOggPad *pad;
2310     glong serial;
2311
2312     ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
2313     if (ret != GST_FLOW_OK) {
2314       GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
2315       break;
2316     }
2317     if (!ogg_page_bos (&op)) {
2318       GST_WARNING_OBJECT (ogg, "page is not BOS page");
2319       /* if we did not find a chain yet, assume this is a bogus stream and
2320        * ignore it */
2321       if (!chain)
2322         ret = GST_FLOW_UNEXPECTED;
2323       break;
2324     }
2325
2326     if (chain == NULL) {
2327       chain = gst_ogg_chain_new (ogg);
2328       chain->offset = offset;
2329     }
2330
2331     serial = ogg_page_serialno (&op);
2332     if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
2333       GST_WARNING_OBJECT (ogg, "found serial %08lx BOS page twice, ignoring",
2334           serial);
2335       continue;
2336     }
2337
2338     pad = gst_ogg_chain_new_stream (chain, serial);
2339     gst_ogg_pad_submit_page (pad, &op);
2340   }
2341
2342   if (ret != GST_FLOW_OK || chain == NULL) {
2343     if (ret == GST_FLOW_OK) {
2344       GST_WARNING_OBJECT (ogg, "no chain was found");
2345       ret = GST_FLOW_ERROR;
2346     } else if (ret != GST_FLOW_UNEXPECTED) {
2347       GST_WARNING_OBJECT (ogg, "failed to read chain");
2348     } else {
2349       GST_DEBUG_OBJECT (ogg, "done reading chains");
2350     }
2351     if (chain) {
2352       gst_ogg_chain_free (chain);
2353     }
2354     if (res_chain)
2355       *res_chain = NULL;
2356     return ret;
2357   }
2358
2359   chain->have_bos = TRUE;
2360   GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
2361
2362   /* now read pages until we receive a buffer from each of the
2363    * stream decoders, this will tell us the timestamp of the
2364    * first packet in the chain then */
2365
2366   /* save the offset to the first non bos page in the chain: if searching for
2367    * pad->first_time we read past the end of the chain, we'll seek back to this
2368    * position
2369    */
2370   offset = ogg->offset;
2371
2372   done = FALSE;
2373   while (!done) {
2374     glong serial;
2375     gboolean known_serial = FALSE;
2376     GstFlowReturn ret;
2377
2378     serial = ogg_page_serialno (&op);
2379     done = TRUE;
2380     for (i = 0; i < chain->streams->len; i++) {
2381       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2382
2383       GST_LOG_OBJECT (ogg, "serial %08x time %" GST_TIME_FORMAT,
2384           pad->map.serialno, GST_TIME_ARGS (pad->start_time));
2385
2386       if (pad->map.serialno == serial) {
2387         known_serial = TRUE;
2388
2389         /* submit the page now, this will fill in the start_time when the
2390          * internal decoder finds it */
2391         gst_ogg_pad_submit_page (pad, &op);
2392
2393         if (!pad->map.is_skeleton && pad->start_time == -1
2394             && ogg_page_eos (&op)) {
2395           /* got EOS on a pad before we could find its start_time.
2396            * We have no chance of finding a start_time for every pad so
2397            * stop searching for the other start_time(s).
2398            */
2399           done = TRUE;
2400           break;
2401         }
2402       }
2403       /* the timestamp will be filled in when we submit the pages */
2404       if (!pad->map.is_skeleton)
2405         done &= (pad->start_time != GST_CLOCK_TIME_NONE);
2406
2407       GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
2408     }
2409
2410     /* we read a page not belonging to the current chain: seek back to the
2411      * beginning of the chain
2412      */
2413     if (!known_serial) {
2414       GST_LOG_OBJECT (ogg, "unknown serial %08lx", serial);
2415       gst_ogg_demux_seek (ogg, offset);
2416       break;
2417     }
2418
2419     if (!done) {
2420       ret = gst_ogg_demux_get_next_page (ogg, &op, -1, NULL);
2421       if (ret != GST_FLOW_OK)
2422         break;
2423     }
2424   }
2425   GST_LOG_OBJECT (ogg, "done reading chain");
2426   /* now we can fill in the missing info using queries */
2427   for (i = 0; i < chain->streams->len; i++) {
2428     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2429
2430     if (pad->map.is_skeleton)
2431       continue;
2432
2433     pad->mode = GST_OGG_PAD_MODE_STREAMING;
2434   }
2435
2436   if (res_chain)
2437     *res_chain = chain;
2438
2439   return GST_FLOW_OK;
2440 }
2441
2442 /* read the last pages from the ogg stream to get the final
2443  * page end_offsets.
2444  */
2445 static GstFlowReturn
2446 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
2447 {
2448   gint64 begin = chain->end_offset;
2449   gint64 end = begin;
2450   gint64 last_granule = -1;
2451   GstOggPad *last_pad = NULL;
2452   GstFlowReturn ret;
2453   gboolean done = FALSE;
2454   ogg_page og;
2455   gint i;
2456
2457   while (!done) {
2458     begin -= CHUNKSIZE;
2459     if (begin < 0)
2460       begin = 0;
2461
2462     gst_ogg_demux_seek (ogg, begin);
2463
2464     /* now continue reading until we run out of data, if we find a page
2465      * start, we save it. It might not be the final page as there could be
2466      * another page after this one. */
2467     while (ogg->offset < end) {
2468       ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
2469
2470       if (ret == GST_FLOW_LIMIT)
2471         break;
2472       if (ret != GST_FLOW_OK)
2473         return ret;
2474
2475       for (i = 0; i < chain->streams->len; i++) {
2476         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2477
2478         if (pad->map.is_skeleton)
2479           continue;
2480
2481         if (pad->map.serialno == ogg_page_serialno (&og)) {
2482           gint64 granulepos = ogg_page_granulepos (&og);
2483
2484           if (last_granule == -1 || last_granule < granulepos) {
2485             last_granule = granulepos;
2486             last_pad = pad;
2487           }
2488           if (last_granule != -1) {
2489             done = TRUE;
2490           }
2491           break;
2492         }
2493       }
2494     }
2495   }
2496
2497   if (last_pad) {
2498     chain->segment_stop =
2499         gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
2500         last_granule);
2501   } else {
2502     chain->segment_stop = GST_CLOCK_TIME_NONE;
2503   }
2504
2505   GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
2506
2507   return GST_FLOW_OK;
2508 }
2509
2510 /* find a pad with a given serial number
2511  */
2512 static GstOggPad *
2513 gst_ogg_demux_find_pad (GstOggDemux * ogg, int serialno)
2514 {
2515   GstOggPad *pad;
2516   gint i;
2517
2518   /* first look in building chain if any */
2519   if (ogg->building_chain) {
2520     pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
2521     if (pad)
2522       return pad;
2523   }
2524
2525   /* then look in current chain if any */
2526   if (ogg->current_chain) {
2527     pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
2528     if (pad)
2529       return pad;
2530   }
2531
2532   for (i = 0; i < ogg->chains->len; i++) {
2533     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2534
2535     pad = gst_ogg_chain_get_stream (chain, serialno);
2536     if (pad)
2537       return pad;
2538   }
2539   return NULL;
2540 }
2541
2542 /* find a chain with a given serial number
2543  */
2544 static GstOggChain *
2545 gst_ogg_demux_find_chain (GstOggDemux * ogg, int serialno)
2546 {
2547   GstOggPad *pad;
2548
2549   pad = gst_ogg_demux_find_pad (ogg, serialno);
2550   if (pad) {
2551     return pad->chain;
2552   }
2553   return NULL;
2554 }
2555
2556 /* returns TRUE if all streams have valid start time */
2557 static gboolean
2558 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
2559 {
2560   gint i;
2561   gboolean res = TRUE;
2562
2563   chain->total_time = GST_CLOCK_TIME_NONE;
2564   chain->segment_start = G_MAXUINT64;
2565
2566   GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
2567
2568   for (i = 0; i < chain->streams->len; i++) {
2569     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2570
2571     if (pad->map.is_skeleton)
2572       continue;
2573
2574     /*  can do this if the pad start time is not defined */
2575     if (pad->start_time == GST_CLOCK_TIME_NONE)
2576       res = FALSE;
2577     else
2578       chain->segment_start = MIN (chain->segment_start, pad->start_time);
2579   }
2580
2581   if (chain->segment_stop != GST_CLOCK_TIME_NONE
2582       && chain->segment_start != G_MAXUINT64)
2583     chain->total_time = chain->segment_stop - chain->segment_start;
2584
2585   GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
2586
2587   GST_DEBUG_OBJECT (ogg, "return %d", res);
2588
2589   return res;
2590 }
2591
2592 static void
2593 gst_ogg_demux_collect_info (GstOggDemux * ogg)
2594 {
2595   gint i;
2596
2597   /* collect all info */
2598   ogg->total_time = 0;
2599
2600   for (i = 0; i < ogg->chains->len; i++) {
2601     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
2602
2603     chain->begin_time = ogg->total_time;
2604
2605     gst_ogg_demux_collect_chain_info (ogg, chain);
2606
2607     ogg->total_time += chain->total_time;
2608   }
2609   gst_segment_set_duration (&ogg->segment, GST_FORMAT_TIME, ogg->total_time);
2610 }
2611
2612 /* find all the chains in the ogg file, this reads the first and
2613  * last page of the ogg stream, if they match then the ogg file has
2614  * just one chain, else we do a binary search for all chains.
2615  */
2616 static GstFlowReturn
2617 gst_ogg_demux_find_chains (GstOggDemux * ogg)
2618 {
2619   ogg_page og;
2620   GstPad *peer;
2621   GstFormat format;
2622   gboolean res;
2623   gulong serialno;
2624   GstOggChain *chain;
2625   GstFlowReturn ret;
2626
2627   /* get peer to figure out length */
2628   if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
2629     goto no_peer;
2630
2631   /* find length to read last page, we store this for later use. */
2632   format = GST_FORMAT_BYTES;
2633   res = gst_pad_query_duration (peer, &format, &ogg->length);
2634   gst_object_unref (peer);
2635   if (!res || ogg->length <= 0)
2636     goto no_length;
2637
2638   GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
2639
2640   /* read chain from offset 0, this is the first chain of the
2641    * ogg file. */
2642   gst_ogg_demux_seek (ogg, 0);
2643   ret = gst_ogg_demux_read_chain (ogg, &chain);
2644   if (ret != GST_FLOW_OK)
2645     goto no_first_chain;
2646
2647   /* read page from end offset, we use this page to check if its serial
2648    * number is contained in the first chain. If this is the case then
2649    * this ogg is not a chained ogg and we can skip the scanning. */
2650   gst_ogg_demux_seek (ogg, ogg->length);
2651   ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
2652   if (ret != GST_FLOW_OK)
2653     goto no_last_page;
2654
2655   serialno = ogg_page_serialno (&og);
2656
2657   if (!gst_ogg_chain_has_stream (chain, serialno)) {
2658     /* the last page is not in the first stream, this means we should
2659      * find all the chains in this chained ogg. */
2660     ret =
2661         gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
2662         0);
2663   } else {
2664     /* we still call this function here but with an empty range so that
2665      * we can reuse the setup code in this routine. */
2666     ret =
2667         gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length,
2668         chain, 0);
2669   }
2670   if (ret != GST_FLOW_OK)
2671     goto done;
2672
2673   /* all fine, collect and print */
2674   gst_ogg_demux_collect_info (ogg);
2675
2676   /* dump our chains and streams */
2677   gst_ogg_print (ogg);
2678
2679 done:
2680   return ret;
2681
2682   /*** error cases ***/
2683 no_peer:
2684   {
2685     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
2686     return GST_FLOW_NOT_LINKED;
2687   }
2688 no_length:
2689   {
2690     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
2691     return GST_FLOW_NOT_SUPPORTED;
2692   }
2693 no_first_chain:
2694   {
2695     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
2696     return GST_FLOW_ERROR;
2697   }
2698 no_last_page:
2699   {
2700     GST_DEBUG_OBJECT (ogg, "can't get last page");
2701     if (chain)
2702       gst_ogg_chain_free (chain);
2703     return ret;
2704   }
2705 }
2706
2707 static GstFlowReturn
2708 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
2709 {
2710   GstOggPad *pad;
2711   gint64 granule;
2712   guint serialno;
2713   GstFlowReturn result = GST_FLOW_OK;
2714
2715   serialno = ogg_page_serialno (page);
2716   granule = ogg_page_granulepos (page);
2717
2718   GST_LOG_OBJECT (ogg,
2719       "processing ogg page (serial %08x, pageno %ld, granulepos %"
2720       G_GINT64_FORMAT ", bos %d)",
2721       serialno, ogg_page_pageno (page), granule, ogg_page_bos (page));
2722
2723   if (ogg_page_bos (page)) {
2724     GstOggChain *chain;
2725
2726     /* first page */
2727     /* see if we know about the chain already */
2728     chain = gst_ogg_demux_find_chain (ogg, serialno);
2729     if (chain) {
2730       GstEvent *event;
2731       gint64 start = 0;
2732
2733       if (chain->segment_start != GST_CLOCK_TIME_NONE)
2734         start = chain->segment_start;
2735
2736       /* create the newsegment event we are going to send out */
2737       event = gst_event_new_new_segment (FALSE, ogg->segment.rate,
2738           GST_FORMAT_TIME, start, chain->segment_stop, chain->begin_time);
2739       gst_event_set_seqnum (event, ogg->seqnum);
2740
2741       GST_DEBUG_OBJECT (ogg,
2742           "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
2743           ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
2744           GST_TIME_ARGS (chain->segment_stop),
2745           GST_TIME_ARGS (chain->begin_time));
2746
2747       /* activate it as it means we have a non-header, this will also deactivate
2748        * the currently running chain. */
2749       gst_ogg_demux_activate_chain (ogg, chain, event);
2750       pad = gst_ogg_demux_find_pad (ogg, serialno);
2751     } else {
2752       GstClockTime chain_time;
2753       GstOggChain *current_chain;
2754       gint64 current_time;
2755
2756       /* this can only happen in non-seekabe mode */
2757       if (ogg->seekable)
2758         goto unknown_chain;
2759
2760       current_chain = ogg->current_chain;
2761       current_time = ogg->segment.last_stop;
2762
2763       /* time of new chain is current time */
2764       chain_time = current_time;
2765
2766       if (ogg->building_chain == NULL) {
2767         GstOggChain *newchain;
2768
2769         newchain = gst_ogg_chain_new (ogg);
2770         newchain->offset = 0;
2771         /* set new chain begin time aligned with end time of old chain */
2772         newchain->begin_time = chain_time;
2773         GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
2774             GST_TIME_ARGS (chain_time));
2775
2776         /* and this is the one we are building now */
2777         ogg->building_chain = newchain;
2778       }
2779       pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
2780     }
2781   } else {
2782     pad = gst_ogg_demux_find_pad (ogg, serialno);
2783   }
2784   if (pad) {
2785     result = gst_ogg_pad_submit_page (pad, page);
2786   } else {
2787     /* no pad. This means an ogg page without bos has been seen for this
2788      * serialno. we just ignore it but post a warning... */
2789     GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
2790         (NULL), ("unknown ogg pad for serial %08x detected", serialno));
2791     return GST_FLOW_OK;
2792   }
2793   return result;
2794
2795   /* ERRORS */
2796 unknown_chain:
2797   {
2798     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2799         (NULL), ("unknown ogg chain for serial %08x detected", serialno));
2800     return GST_FLOW_ERROR;
2801   }
2802 }
2803
2804 /* streaming mode, receive a buffer, parse it, create pads for
2805  * the serialno, submit pages and packets to the oggpads
2806  */
2807 static GstFlowReturn
2808 gst_ogg_demux_chain (GstPad * pad, GstBuffer * buffer)
2809 {
2810   GstOggDemux *ogg;
2811   gint ret = 0;
2812   GstFlowReturn result = GST_FLOW_OK;
2813
2814   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
2815
2816   GST_DEBUG_OBJECT (ogg, "chain");
2817   result = gst_ogg_demux_submit_buffer (ogg, buffer);
2818
2819   while (result == GST_FLOW_OK) {
2820     ogg_page page;
2821
2822     ret = ogg_sync_pageout (&ogg->sync, &page);
2823     if (ret == 0)
2824       /* need more data */
2825       break;
2826     if (ret == -1) {
2827       /* discontinuity in the pages */
2828       GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
2829     } else {
2830       result = gst_ogg_demux_handle_page (ogg, &page);
2831     }
2832   }
2833   if (ret == 0 || result == GST_FLOW_OK) {
2834     gst_ogg_demux_sync_streams (ogg);
2835   }
2836   return result;
2837 }
2838
2839 static void
2840 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
2841 {
2842   GstOggChain *chain = ogg->current_chain;
2843
2844   if (chain) {
2845     gint i;
2846
2847     for (i = 0; i < chain->streams->len; i++) {
2848       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2849
2850       gst_event_ref (event);
2851       GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
2852       gst_pad_push_event (GST_PAD (pad), event);
2853     }
2854   }
2855   gst_event_unref (event);
2856 }
2857
2858 static GstFlowReturn
2859 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
2860     GstFlowReturn ret)
2861 {
2862   GstOggChain *chain;
2863
2864   /* store the value */
2865   pad->last_ret = ret;
2866
2867   /* any other error that is not-linked can be returned right
2868    * away */
2869   if (ret != GST_FLOW_NOT_LINKED)
2870     goto done;
2871
2872   /* only return NOT_LINKED if all other pads returned NOT_LINKED */
2873   chain = ogg->current_chain;
2874   if (chain) {
2875     gint i;
2876
2877     for (i = 0; i < chain->streams->len; i++) {
2878       GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
2879
2880       ret = opad->last_ret;
2881       /* some other return value (must be SUCCESS but we can return
2882        * other values as well) */
2883       if (ret != GST_FLOW_NOT_LINKED)
2884         goto done;
2885     }
2886     /* if we get here, all other pads were unlinked and we return
2887      * NOT_LINKED then */
2888   }
2889 done:
2890   return ret;
2891 }
2892
2893 static GstFlowReturn
2894 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
2895 {
2896   GstFlowReturn ret;
2897   GstBuffer *buffer;
2898
2899   if (ogg->offset == ogg->length) {
2900     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
2901         " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
2902     ret = GST_FLOW_UNEXPECTED;
2903     goto done;
2904   }
2905
2906   GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
2907   ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
2908   if (ret != GST_FLOW_OK) {
2909     GST_LOG_OBJECT (ogg, "Failed pull_range");
2910     goto done;
2911   }
2912
2913   ogg->offset += GST_BUFFER_SIZE (buffer);
2914
2915   if (G_UNLIKELY (ogg->newsegment)) {
2916     gst_ogg_demux_send_event (ogg, ogg->newsegment);
2917     ogg->newsegment = NULL;
2918   }
2919
2920   ret = gst_ogg_demux_chain (ogg->sinkpad, buffer);
2921   if (ret != GST_FLOW_OK) {
2922     GST_LOG_OBJECT (ogg, "Failed demux_chain");
2923     goto done;
2924   }
2925
2926   /* check for the end of the segment */
2927   if (ogg->segment.stop != -1 && ogg->segment.last_stop != -1) {
2928     if (ogg->segment.last_stop > ogg->segment.stop) {
2929       ret = GST_FLOW_UNEXPECTED;
2930       goto done;
2931     }
2932   }
2933 done:
2934   return ret;
2935 }
2936
2937 /* reverse mode.
2938  *
2939  * We read the pages backwards and send the packets forwards. The first packet
2940  * in the page will be pushed with the DISCONT flag set.
2941  *
2942  * Special care has to be taken for continued pages, which we can only decode
2943  * when we have the previous page(s).
2944  */
2945 static GstFlowReturn
2946 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
2947 {
2948   GstFlowReturn ret;
2949   ogg_page page;
2950   gint64 offset;
2951
2952   if (ogg->offset == 0) {
2953     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
2954         " == 0", ogg->offset);
2955     ret = GST_FLOW_UNEXPECTED;
2956     goto done;
2957   }
2958
2959   GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
2960   ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
2961   if (ret != GST_FLOW_OK)
2962     goto done;
2963
2964   ogg->offset = offset;
2965
2966   if (G_UNLIKELY (ogg->newsegment)) {
2967     gst_ogg_demux_send_event (ogg, ogg->newsegment);
2968     ogg->newsegment = NULL;
2969   }
2970
2971   ret = gst_ogg_demux_handle_page (ogg, &page);
2972   if (ret != GST_FLOW_OK)
2973     goto done;
2974
2975   /* check for the end of the segment */
2976   if (ogg->segment.start != -1 && ogg->segment.last_stop != -1) {
2977     if (ogg->segment.last_stop <= ogg->segment.start) {
2978       ret = GST_FLOW_UNEXPECTED;
2979       goto done;
2980     }
2981   }
2982 done:
2983   return ret;
2984 }
2985
2986 static void
2987 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
2988 {
2989   GstClockTime cur;
2990   GstOggChain *chain;
2991   guint i;
2992
2993   chain = ogg->current_chain;
2994   cur = ogg->segment.last_stop;
2995   if (chain == NULL || cur == -1)
2996     return;
2997
2998   for (i = 0; i < chain->streams->len; i++) {
2999     GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
3000
3001     /* Theoretically, we should be doing this for all streams, but we're only
3002      * doing it for known-to-be-sparse streams at the moment in order not to
3003      * break things for wrongly-muxed streams (like we used to produce once) */
3004     if (stream->is_sparse && stream->last_stop != GST_CLOCK_TIME_NONE) {
3005
3006       /* Does this stream lag? Random threshold of 2 seconds */
3007       if (GST_CLOCK_DIFF (stream->last_stop, cur) > (2 * GST_SECOND)) {
3008         GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
3009             "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
3010             GST_TIME_ARGS (stream->last_stop), GST_TIME_ARGS (cur));
3011         stream->last_stop = cur;
3012         /* advance stream time (FIXME: is this right, esp. time_pos?) */
3013         gst_pad_push_event (GST_PAD_CAST (stream),
3014             gst_event_new_new_segment (TRUE, ogg->segment.rate,
3015                 GST_FORMAT_TIME, stream->last_stop, -1, stream->last_stop));
3016       }
3017     }
3018   }
3019 }
3020
3021 /* random access code
3022  *
3023  * - first find all the chains and streams by scanning the file.
3024  * - then get and chain buffers, just like the streaming case.
3025  * - when seeking, we can use the chain info to perform the seek.
3026  */
3027 static void
3028 gst_ogg_demux_loop (GstOggPad * pad)
3029 {
3030   GstOggDemux *ogg;
3031   GstFlowReturn ret;
3032   GstEvent *event;
3033
3034   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
3035
3036   if (ogg->need_chains) {
3037     gboolean res;
3038
3039     /* this is the only place where we write chains and thus need to lock. */
3040     GST_CHAIN_LOCK (ogg);
3041     ret = gst_ogg_demux_find_chains (ogg);
3042     GST_CHAIN_UNLOCK (ogg);
3043     if (ret != GST_FLOW_OK)
3044       goto chain_read_failed;
3045
3046     ogg->need_chains = FALSE;
3047
3048     GST_OBJECT_LOCK (ogg);
3049     ogg->running = TRUE;
3050     event = ogg->event;
3051     ogg->event = NULL;
3052     GST_OBJECT_UNLOCK (ogg);
3053
3054     /* and seek to configured positions without FLUSH */
3055     res = gst_ogg_demux_perform_seek (ogg, event);
3056     if (event)
3057       gst_event_unref (event);
3058
3059     if (!res)
3060       goto seek_failed;
3061   }
3062
3063   if (ogg->segment.rate >= 0.0)
3064     ret = gst_ogg_demux_loop_forward (ogg);
3065   else
3066     ret = gst_ogg_demux_loop_reverse (ogg);
3067
3068   if (ret != GST_FLOW_OK)
3069     goto pause;
3070
3071   gst_ogg_demux_sync_streams (ogg);
3072   return;
3073
3074   /* ERRORS */
3075 chain_read_failed:
3076   {
3077     /* error was posted */
3078     goto pause;
3079   }
3080 seek_failed:
3081   {
3082     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
3083         ("failed to start demuxing ogg"));
3084     ret = GST_FLOW_ERROR;
3085     goto pause;
3086   }
3087 pause:
3088   {
3089     const gchar *reason = gst_flow_get_name (ret);
3090     GstEvent *event = NULL;
3091
3092     GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
3093     ogg->segment_running = FALSE;
3094     gst_pad_pause_task (ogg->sinkpad);
3095
3096     if (GST_FLOW_IS_FATAL (ret) || ret == GST_FLOW_NOT_LINKED) {
3097       if (ret == GST_FLOW_UNEXPECTED) {
3098         /* perform EOS logic */
3099         if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3100           gint64 stop;
3101           GstMessage *message;
3102
3103           /* for segment playback we need to post when (in stream time)
3104            * we stopped, this is either stop (when set) or the duration. */
3105           if ((stop = ogg->segment.stop) == -1)
3106             stop = ogg->segment.duration;
3107
3108           GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
3109           message =
3110               gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
3111               stop);
3112           gst_message_set_seqnum (message, ogg->seqnum);
3113
3114           gst_element_post_message (GST_ELEMENT (ogg), message);
3115         } else {
3116           /* normal playback, send EOS to all linked pads */
3117           GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
3118           event = gst_event_new_eos ();
3119         }
3120       } else {
3121         GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
3122             (_("Internal data stream error.")),
3123             ("stream stopped, reason %s", reason));
3124         event = gst_event_new_eos ();
3125       }
3126       if (event) {
3127         gst_event_set_seqnum (event, ogg->seqnum);
3128         gst_ogg_demux_send_event (ogg, event);
3129       }
3130     }
3131     return;
3132   }
3133 }
3134
3135 static void
3136 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
3137 {
3138   gint i;
3139
3140   gst_ogg_demux_deactivate_current_chain (ogg);
3141
3142   GST_CHAIN_LOCK (ogg);
3143   for (i = 0; i < ogg->chains->len; i++) {
3144     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3145
3146     gst_ogg_chain_free (chain);
3147   }
3148   ogg->chains = g_array_set_size (ogg->chains, 0);
3149   GST_CHAIN_UNLOCK (ogg);
3150 }
3151
3152 /* this function is called when the pad is activated and should start
3153  * processing data.
3154  *
3155  * We check if we can do random access to decide if we work push or
3156  * pull based.
3157  */
3158 static gboolean
3159 gst_ogg_demux_sink_activate (GstPad * sinkpad)
3160 {
3161   if (gst_pad_check_pull_range (sinkpad)) {
3162     GST_DEBUG_OBJECT (sinkpad, "activating pull");
3163     return gst_pad_activate_pull (sinkpad, TRUE);
3164   } else {
3165     GST_DEBUG_OBJECT (sinkpad, "activating push");
3166     return gst_pad_activate_push (sinkpad, TRUE);
3167   }
3168 }
3169
3170 /* this function gets called when we activate ourselves in push mode.
3171  * We cannot seek (ourselves) in the stream */
3172 static gboolean
3173 gst_ogg_demux_sink_activate_push (GstPad * sinkpad, gboolean active)
3174 {
3175   GstOggDemux *ogg;
3176
3177   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
3178
3179   ogg->seekable = FALSE;
3180
3181   return TRUE;
3182 }
3183
3184 /* this function gets called when we activate ourselves in pull mode.
3185  * We can perform  random access to the resource and we start a task
3186  * to start reading */
3187 static gboolean
3188 gst_ogg_demux_sink_activate_pull (GstPad * sinkpad, gboolean active)
3189 {
3190   GstOggDemux *ogg;
3191
3192   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (sinkpad));
3193
3194   if (active) {
3195     ogg->need_chains = TRUE;
3196     ogg->seekable = TRUE;
3197
3198     return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3199         sinkpad);
3200   } else {
3201     return gst_pad_stop_task (sinkpad);
3202   }
3203 }
3204
3205 static GstStateChangeReturn
3206 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
3207 {
3208   GstOggDemux *ogg;
3209   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
3210
3211   ogg = GST_OGG_DEMUX (element);
3212
3213   switch (transition) {
3214     case GST_STATE_CHANGE_NULL_TO_READY:
3215       ogg->basetime = 0;
3216       ogg->have_fishead = FALSE;
3217       ogg_sync_init (&ogg->sync);
3218       break;
3219     case GST_STATE_CHANGE_READY_TO_PAUSED:
3220       ogg_sync_reset (&ogg->sync);
3221       ogg->running = FALSE;
3222       ogg->segment_running = FALSE;
3223       gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
3224       break;
3225     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
3226       break;
3227     default:
3228       break;
3229   }
3230
3231   result = parent_class->change_state (element, transition);
3232
3233   switch (transition) {
3234     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
3235       break;
3236     case GST_STATE_CHANGE_PAUSED_TO_READY:
3237       gst_ogg_demux_clear_chains (ogg);
3238       GST_OBJECT_LOCK (ogg);
3239       ogg->running = FALSE;
3240       ogg->segment_running = FALSE;
3241       ogg->have_fishead = FALSE;
3242       GST_OBJECT_UNLOCK (ogg);
3243       break;
3244     case GST_STATE_CHANGE_READY_TO_NULL:
3245       ogg_sync_clear (&ogg->sync);
3246       break;
3247     default:
3248       break;
3249   }
3250   return result;
3251 }
3252
3253 gboolean
3254 gst_ogg_demux_plugin_init (GstPlugin * plugin)
3255 {
3256   GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
3257   GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
3258       "ogg demuxer setup stage when parsing pipeline");
3259
3260 #if ENABLE_NLS
3261   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
3262       LOCALEDIR);
3263   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
3264   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
3265 #endif
3266
3267   return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
3268       GST_TYPE_OGG_DEMUX);
3269 }
3270
3271 /* prints all info about the element */
3272 #undef GST_CAT_DEFAULT
3273 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
3274
3275 #ifdef GST_DISABLE_GST_DEBUG
3276
3277 static void
3278 gst_ogg_print (GstOggDemux * ogg)
3279 {
3280   /* NOP */
3281 }
3282
3283 #else /* !GST_DISABLE_GST_DEBUG */
3284
3285 static void
3286 gst_ogg_print (GstOggDemux * ogg)
3287 {
3288   guint j, i;
3289
3290   GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
3291   GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
3292       GST_TIME_ARGS (ogg->total_time));
3293
3294   for (i = 0; i < ogg->chains->len; i++) {
3295     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3296
3297     GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
3298     GST_INFO_OBJECT (ogg, "  offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT,
3299         chain->offset, chain->end_offset);
3300     GST_INFO_OBJECT (ogg, "  begin time: %" GST_TIME_FORMAT,
3301         GST_TIME_ARGS (chain->begin_time));
3302     GST_INFO_OBJECT (ogg, "  total time: %" GST_TIME_FORMAT,
3303         GST_TIME_ARGS (chain->total_time));
3304     GST_INFO_OBJECT (ogg, "  segment start: %" GST_TIME_FORMAT,
3305         GST_TIME_ARGS (chain->segment_start));
3306     GST_INFO_OBJECT (ogg, "  segment stop:  %" GST_TIME_FORMAT,
3307         GST_TIME_ARGS (chain->segment_stop));
3308
3309     for (j = 0; j < chain->streams->len; j++) {
3310       GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
3311
3312       GST_INFO_OBJECT (ogg, "  stream %08x:", stream->map.serialno);
3313       GST_INFO_OBJECT (ogg, "   start time:       %" GST_TIME_FORMAT,
3314           GST_TIME_ARGS (stream->start_time));
3315     }
3316   }
3317 }
3318 #endif /* GST_DISABLE_GST_DEBUG */