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