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