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