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