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