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