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