Merge remote-tracking branch 'origin/master' into 0.11
[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
43 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
44  * with newer GLib versions (>= 2.31.0) */
45 #define GLIB_DISABLE_DEPRECATION_WARNINGS
46
47 #include <string.h>
48 #include <gst/gst-i18n-plugin.h>
49 #include <gst/tag/tag.h>
50
51 #include "gstoggdemux.h"
52
53 #include "gst/glib-compat-private.h"
54
55 #define CHUNKSIZE (8500)        /* this is out of vorbisfile */
56
57 /* we hope we get a granpos within this many bytes off the end */
58 #define DURATION_CHUNK_OFFSET (64*1024)
59
60 /* stop duration checks within this much of EOS */
61 #define EOS_AVOIDANCE_THRESHOLD 8192
62
63 #define GST_FLOW_LIMIT GST_FLOW_CUSTOM_ERROR
64 #define GST_FLOW_SKIP_PUSH GST_FLOW_CUSTOM_SUCCESS_1
65
66 #define GST_CHAIN_LOCK(ogg)     g_mutex_lock((ogg)->chain_lock)
67 #define GST_CHAIN_UNLOCK(ogg)   g_mutex_unlock((ogg)->chain_lock)
68
69 #define GST_PUSH_LOCK(ogg)                  \
70   do {                                      \
71     GST_TRACE_OBJECT(ogg, "Push lock");     \
72     g_mutex_lock((ogg)->push_lock);         \
73   } while(0)
74
75 #define GST_PUSH_UNLOCK(ogg)                \
76   do {                                      \
77     GST_TRACE_OBJECT(ogg, "Push unlock");   \
78     g_mutex_unlock((ogg)->push_lock);       \
79   } while(0)
80
81 GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
82 GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
83 #define GST_CAT_DEFAULT gst_ogg_demux_debug
84
85
86 static ogg_packet *
87 _ogg_packet_copy (const ogg_packet * packet)
88 {
89   ogg_packet *ret = g_slice_new (ogg_packet);
90
91   *ret = *packet;
92   ret->packet = g_memdup (packet->packet, packet->bytes);
93
94   return ret;
95 }
96
97 static void
98 _ogg_packet_free (ogg_packet * packet)
99 {
100   g_free (packet->packet);
101   g_slice_free (ogg_packet, packet);
102 }
103
104 static ogg_page *
105 gst_ogg_page_copy (ogg_page * page)
106 {
107   ogg_page *p = g_slice_new (ogg_page);
108
109   /* make a copy of the page */
110   p->header = g_memdup (page->header, page->header_len);
111   p->header_len = page->header_len;
112   p->body = g_memdup (page->body, page->body_len);
113   p->body_len = page->body_len;
114
115   return p;
116 }
117
118 static void
119 gst_ogg_page_free (ogg_page * page)
120 {
121   g_free (page->header);
122   g_free (page->body);
123   g_slice_free (ogg_page, page);
124 }
125
126 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
127     GstOggChain * chain);
128 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
129     GstOggChain * chain, GstEvent * event);
130 static void gst_ogg_pad_mark_discont (GstOggPad * pad);
131 static void gst_ogg_chain_mark_discont (GstOggChain * chain);
132
133 static gboolean gst_ogg_demux_perform_seek (GstOggDemux * ogg,
134     GstEvent * event);
135 static gboolean gst_ogg_demux_receive_event (GstElement * element,
136     GstEvent * event);
137
138 static void gst_ogg_pad_dispose (GObject * object);
139 static void gst_ogg_pad_finalize (GObject * object);
140
141 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstObject * parent,
142     GstQuery * query);
143 static gboolean gst_ogg_pad_event (GstPad * pad, GstObject * parent,
144     GstEvent * event);
145 static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
146     guint32 serialno);
147
148 static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
149     GstOggPad * pad, GstFlowReturn ret);
150 static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
151
152 GstCaps *gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg,
153     GstCaps * caps, GList * headers);
154 static gboolean gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event);
155 static gboolean gst_ogg_demux_perform_seek_push (GstOggDemux * ogg,
156     GstEvent * event);
157 static gboolean gst_ogg_demux_check_duration_push (GstOggDemux * ogg,
158     GstSeekFlags flags, GstEvent * event);
159
160 GType gst_ogg_pad_get_type (void);
161 G_DEFINE_TYPE (GstOggPad, gst_ogg_pad, GST_TYPE_PAD);
162
163 static void
164 gst_ogg_pad_class_init (GstOggPadClass * klass)
165 {
166   GObjectClass *gobject_class;
167
168   gobject_class = (GObjectClass *) klass;
169
170   gobject_class->dispose = gst_ogg_pad_dispose;
171   gobject_class->finalize = gst_ogg_pad_finalize;
172 }
173
174 static void
175 gst_ogg_pad_init (GstOggPad * pad)
176 {
177   gst_pad_set_event_function (GST_PAD (pad),
178       GST_DEBUG_FUNCPTR (gst_ogg_pad_event));
179   gst_pad_set_query_function (GST_PAD (pad),
180       GST_DEBUG_FUNCPTR (gst_ogg_pad_src_query));
181   gst_pad_use_fixed_caps (GST_PAD (pad));
182
183   pad->mode = GST_OGG_PAD_MODE_INIT;
184
185   pad->current_granule = -1;
186   pad->keyframe_granule = -1;
187
188   pad->start_time = GST_CLOCK_TIME_NONE;
189
190   pad->position = GST_CLOCK_TIME_NONE;
191
192   pad->have_type = FALSE;
193   pad->continued = NULL;
194   pad->map.headers = NULL;
195   pad->map.queued = NULL;
196
197   pad->map.granulerate_n = 0;
198   pad->map.granulerate_d = 0;
199   pad->map.granuleshift = -1;
200 }
201
202 static void
203 gst_ogg_pad_dispose (GObject * object)
204 {
205   GstOggPad *pad = GST_OGG_PAD (object);
206
207   pad->chain = NULL;
208   pad->ogg = NULL;
209
210   g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
211   g_list_free (pad->map.headers);
212   pad->map.headers = NULL;
213   g_list_foreach (pad->map.queued, (GFunc) _ogg_packet_free, NULL);
214   g_list_free (pad->map.queued);
215   pad->map.queued = NULL;
216
217   g_free (pad->map.index);
218   pad->map.index = NULL;
219
220   /* clear continued pages */
221   g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
222   g_list_free (pad->continued);
223   pad->continued = NULL;
224
225   if (pad->map.caps) {
226     gst_caps_unref (pad->map.caps);
227     pad->map.caps = NULL;
228   }
229
230   if (pad->map.taglist) {
231     gst_tag_list_free (pad->map.taglist);
232     pad->map.taglist = NULL;
233   }
234
235   ogg_stream_reset (&pad->map.stream);
236
237   G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
238 }
239
240 static void
241 gst_ogg_pad_finalize (GObject * object)
242 {
243   GstOggPad *pad = GST_OGG_PAD (object);
244
245   ogg_stream_clear (&pad->map.stream);
246
247   G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
248 }
249
250 static gboolean
251 gst_ogg_pad_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
252 {
253   gboolean res = TRUE;
254   GstOggDemux *ogg;
255
256   ogg = GST_OGG_DEMUX (parent);
257
258   switch (GST_QUERY_TYPE (query)) {
259     case GST_QUERY_DURATION:
260     {
261       GstFormat format;
262       gint64 total_time = -1;
263
264       gst_query_parse_duration (query, &format, NULL);
265       /* can only get position in time */
266       if (format != GST_FORMAT_TIME)
267         goto wrong_format;
268
269       if (ogg->total_time != -1) {
270         /* we can return the total length */
271         total_time = ogg->total_time;
272       } else {
273         gint bitrate = ogg->bitrate;
274
275         /* try with length and bitrate */
276         if (bitrate > 0) {
277           GstQuery *uquery;
278
279           /* ask upstream for total length in bytes */
280           uquery = gst_query_new_duration (GST_FORMAT_BYTES);
281           if (gst_pad_peer_query (ogg->sinkpad, uquery)) {
282             gint64 length;
283
284             gst_query_parse_duration (uquery, NULL, &length);
285
286             /* estimate using the bitrate */
287             total_time =
288                 gst_util_uint64_scale (length, 8 * GST_SECOND, bitrate);
289
290             GST_LOG_OBJECT (ogg,
291                 "length: %" G_GINT64_FORMAT ", bitrate %d, total_time %"
292                 GST_TIME_FORMAT, length, bitrate, GST_TIME_ARGS (total_time));
293           }
294           gst_query_unref (uquery);
295         }
296       }
297
298       gst_query_set_duration (query, GST_FORMAT_TIME, total_time);
299       break;
300     }
301     case GST_QUERY_SEEKING:
302     {
303       GstFormat format;
304
305       gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
306       if (format == GST_FORMAT_TIME) {
307         gboolean seekable = FALSE;
308         gint64 stop = -1;
309
310         if (ogg->pullmode) {
311           seekable = TRUE;
312           stop = ogg->total_time;
313         } else if (ogg->push_disable_seeking) {
314           seekable = FALSE;
315         } else if (ogg->current_chain->streams->len) {
316           gint i;
317
318           seekable = FALSE;
319           for (i = 0; i < ogg->current_chain->streams->len; i++) {
320             GstOggPad *pad =
321                 g_array_index (ogg->current_chain->streams, GstOggPad *, i);
322
323             seekable = TRUE;
324             if (pad->map.index != NULL && pad->map.n_index != 0) {
325               GstOggIndex *idx;
326               GstClockTime idx_time;
327
328               idx = &pad->map.index[pad->map.n_index - 1];
329               idx_time =
330                   gst_util_uint64_scale (idx->timestamp, GST_SECOND,
331                   pad->map.kp_denom);
332               if (stop == -1)
333                 stop = idx_time;
334               else
335                 stop = MAX (idx_time, stop);
336             } else {
337               stop = -1;        /* we've no clue, sadly, without seeking */
338             }
339           }
340         }
341
342         gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, stop);
343       } else {
344         res = FALSE;
345       }
346       break;
347     }
348
349     default:
350       res = gst_pad_query_default (pad, parent, query);
351       break;
352   }
353 done:
354
355   return res;
356
357   /* ERRORS */
358 wrong_format:
359   {
360     GST_DEBUG_OBJECT (ogg, "only query duration on TIME is supported");
361     res = FALSE;
362     goto done;
363   }
364 }
365
366 static gboolean
367 gst_ogg_demux_receive_event (GstElement * element, GstEvent * event)
368 {
369   gboolean res;
370   GstOggDemux *ogg;
371
372   ogg = GST_OGG_DEMUX (element);
373
374   switch (GST_EVENT_TYPE (event)) {
375     case GST_EVENT_SEEK:
376       /* now do the seek */
377       res = gst_ogg_demux_perform_seek (ogg, event);
378       gst_event_unref (event);
379       break;
380     default:
381       GST_DEBUG_OBJECT (ogg, "We only handle seek events here");
382       goto error;
383   }
384
385   return res;
386
387   /* ERRORS */
388 error:
389   {
390     GST_DEBUG_OBJECT (ogg, "error handling event");
391     gst_event_unref (event);
392     return FALSE;
393   }
394 }
395
396 static gboolean
397 gst_ogg_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
398 {
399   gboolean res;
400   GstOggDemux *ogg;
401
402   ogg = GST_OGG_DEMUX (parent);
403
404   switch (GST_EVENT_TYPE (event)) {
405     case GST_EVENT_SEEK:
406       /* now do the seek */
407       res = gst_ogg_demux_perform_seek (ogg, event);
408       gst_event_unref (event);
409       break;
410     default:
411       res = gst_pad_event_default (pad, parent, event);
412       break;
413   }
414
415   return res;
416 }
417
418 static void
419 gst_ogg_pad_reset (GstOggPad * pad)
420 {
421   ogg_stream_reset (&pad->map.stream);
422
423   GST_DEBUG_OBJECT (pad, "doing reset");
424
425   /* clear continued pages */
426   g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
427   g_list_free (pad->continued);
428   pad->continued = NULL;
429
430   pad->last_ret = GST_FLOW_OK;
431   pad->position = GST_CLOCK_TIME_NONE;
432   pad->current_granule = -1;
433   pad->keyframe_granule = -1;
434   pad->is_eos = FALSE;
435 }
436
437 /* queue data, basically takes the packet, puts it in a buffer and store the
438  * buffer in the queued list.  */
439 static GstFlowReturn
440 gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet)
441 {
442 #ifndef GST_DISABLE_GST_DEBUG
443   GstOggDemux *ogg = pad->ogg;
444 #endif
445
446   GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x",
447       pad, pad->map.serialno);
448
449   pad->map.queued = g_list_append (pad->map.queued, _ogg_packet_copy (packet));
450
451   /* we are ok now */
452   return GST_FLOW_OK;
453 }
454
455 static GstFlowReturn
456 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet,
457     gboolean push_headers)
458 {
459   GstBuffer *buf = NULL;
460   GstFlowReturn ret, cret;
461   GstOggDemux *ogg = pad->ogg;
462   gint64 current_time;
463   GstOggChain *chain;
464   gint64 duration;
465   gint offset;
466   gint trim;
467   GstClockTime out_timestamp, out_duration;
468   guint64 out_offset, out_offset_end;
469   gboolean delta_unit = FALSE;
470
471   cret = GST_FLOW_OK;
472
473   GST_DEBUG_OBJECT (pad, "Chaining %d %d %" GST_TIME_FORMAT " %d %p",
474       ogg->pullmode, ogg->push_state, GST_TIME_ARGS (ogg->push_time_length),
475       ogg->push_disable_seeking, ogg->building_chain);
476   GST_PUSH_LOCK (ogg);
477   if (!ogg->pullmode && ogg->push_state == PUSH_PLAYING
478       && ogg->push_time_length == GST_CLOCK_TIME_NONE
479       && !ogg->push_disable_seeking) {
480     if (!ogg->building_chain) {
481       /* we got all headers, now try to get duration */
482       if (!gst_ogg_demux_check_duration_push (ogg, GST_SEEK_FLAG_FLUSH, NULL)) {
483         GST_PUSH_UNLOCK (ogg);
484         return GST_FLOW_OK;
485       }
486     }
487     GST_PUSH_UNLOCK (ogg);
488     return GST_FLOW_OK;
489   }
490   GST_PUSH_UNLOCK (ogg);
491
492   GST_DEBUG_OBJECT (ogg,
493       "%p streaming to peer serial %08x", pad, pad->map.serialno);
494
495   if (pad->map.is_ogm) {
496     const guint8 *data;
497     long bytes;
498
499     data = packet->packet;
500     bytes = packet->bytes;
501
502     if (bytes < 1)
503       goto empty_packet;
504
505     if ((data[0] & 1) || (data[0] & 3 && pad->map.is_ogm_text)) {
506       /* We don't push header packets for OGM */
507       goto done;
508     }
509
510     offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
511     delta_unit = (((data[0] & 0x08) >> 3) == 0);
512
513     trim = 0;
514
515     /* Strip trailing \0 for subtitles */
516     if (pad->map.is_ogm_text) {
517       while (bytes && data[bytes - 1] == 0) {
518         trim++;
519         bytes--;
520       }
521     }
522   } else if (pad->map.is_vp8) {
523     if ((packet->bytes >= 7 && memcmp (packet->packet, "OVP80\2 ", 7) == 0) ||
524         packet->b_o_s ||
525         (packet->bytes >= 5 && memcmp (packet->packet, "OVP80", 5) == 0)) {
526       /* We don't push header packets for VP8 */
527       goto done;
528     }
529     offset = 0;
530     trim = 0;
531   } else {
532     offset = 0;
533     trim = 0;
534   }
535
536   /* get timing info for the packet */
537   if (gst_ogg_stream_packet_is_header (&pad->map, packet)) {
538     duration = 0;
539     GST_DEBUG_OBJECT (ogg, "packet is header");
540   } else {
541     duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
542     GST_DEBUG_OBJECT (ogg, "packet duration %" G_GUINT64_FORMAT, duration);
543   }
544
545   if (packet->b_o_s) {
546     out_timestamp = GST_CLOCK_TIME_NONE;
547     out_duration = GST_CLOCK_TIME_NONE;
548     out_offset = 0;
549     out_offset_end = -1;
550   } else {
551     if (packet->granulepos != -1) {
552       pad->current_granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
553           packet->granulepos);
554       pad->keyframe_granule =
555           gst_ogg_stream_granulepos_to_key_granule (&pad->map,
556           packet->granulepos);
557       GST_DEBUG_OBJECT (ogg, "new granule %" G_GUINT64_FORMAT,
558           pad->current_granule);
559     } else if (ogg->segment.rate > 0.0 && pad->current_granule != -1) {
560       pad->current_granule += duration;
561       GST_DEBUG_OBJECT (ogg, "interpolating granule %" G_GUINT64_FORMAT,
562           pad->current_granule);
563     }
564     if (ogg->segment.rate < 0.0 && packet->granulepos == -1) {
565       /* negative rates, only set timestamp on the packets with a granulepos */
566       out_timestamp = -1;
567       out_duration = -1;
568       out_offset = -1;
569       out_offset_end = -1;
570     } else {
571       /* we only push buffers after we have a valid granule. This is done so that
572        * we nicely skip packets without a timestamp after a seek. This is ok
573        * because we base or seek on the packet after the page with the smaller
574        * timestamp. */
575       if (pad->current_granule == -1)
576         goto no_timestamp;
577
578       if (pad->map.is_ogm) {
579         out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
580             pad->current_granule);
581         out_duration = gst_util_uint64_scale (duration,
582             GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
583       } else if (pad->map.is_sparse) {
584         out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
585             pad->current_granule);
586         if (duration == GST_CLOCK_TIME_NONE) {
587           out_duration = GST_CLOCK_TIME_NONE;
588         } else {
589           out_duration = gst_util_uint64_scale (duration,
590               GST_SECOND * pad->map.granulerate_d, pad->map.granulerate_n);
591         }
592       } else {
593         out_timestamp = gst_ogg_stream_granule_to_time (&pad->map,
594             pad->current_granule - duration);
595         out_duration =
596             gst_ogg_stream_granule_to_time (&pad->map,
597             pad->current_granule) - out_timestamp;
598       }
599       out_offset_end =
600           gst_ogg_stream_granule_to_granulepos (&pad->map,
601           pad->current_granule, pad->keyframe_granule);
602       out_offset =
603           gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule);
604     }
605   }
606
607   if (pad->map.is_ogm_text) {
608     /* check for invalid buffer sizes */
609     if (G_UNLIKELY (offset + trim >= packet->bytes))
610       goto empty_packet;
611   }
612
613   if (!pad->added)
614     goto not_added;
615
616   buf = gst_buffer_new_and_alloc (packet->bytes - offset - trim);
617
618   /* set delta flag for OGM content */
619   if (delta_unit)
620     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
621
622   /* copy packet in buffer */
623   gst_buffer_fill (buf, 0, packet->packet + offset,
624       packet->bytes - offset - trim);
625
626   GST_BUFFER_TIMESTAMP (buf) = out_timestamp;
627   GST_BUFFER_DURATION (buf) = out_duration;
628   GST_BUFFER_OFFSET (buf) = out_offset;
629   GST_BUFFER_OFFSET_END (buf) = out_offset_end;
630
631   /* Mark discont on the buffer */
632   if (pad->discont) {
633     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
634     pad->discont = FALSE;
635   }
636
637   pad->position = ogg->segment.position;
638
639   /* don't push the header packets when we are asked to skip them */
640   if (!packet->b_o_s || push_headers) {
641     ret = gst_pad_push (GST_PAD_CAST (pad), buf);
642     buf = NULL;
643
644     /* combine flows */
645     cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
646   }
647
648   /* we're done with skeleton stuff */
649   if (pad->map.is_skeleton)
650     goto done;
651
652   /* check if valid granulepos, then we can calculate the current
653    * position. We know the granule for each packet but we only want to update
654    * the position when we have a valid granulepos on the packet because else
655    * our time jumps around for the different streams. */
656   if (packet->granulepos < 0)
657     goto done;
658
659   /* convert to time */
660   current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
661       packet->granulepos);
662
663   /* convert to stream time */
664   if ((chain = pad->chain)) {
665     gint64 chain_start = 0;
666
667     if (chain->segment_start != GST_CLOCK_TIME_NONE)
668       chain_start = chain->segment_start;
669
670     current_time = current_time - chain_start + chain->begin_time;
671   }
672
673   /* and store as the current position */
674   ogg->segment.position = current_time;
675
676   GST_DEBUG_OBJECT (ogg, "ogg current time %" GST_TIME_FORMAT,
677       GST_TIME_ARGS (current_time));
678
679   /* check stream eos */
680   if ((ogg->segment.rate > 0.0 && ogg->segment.stop != GST_CLOCK_TIME_NONE &&
681           current_time > ogg->segment.stop) ||
682       (ogg->segment.rate < 0.0 && ogg->segment.start != GST_CLOCK_TIME_NONE &&
683           current_time < ogg->segment.start)) {
684     GST_DEBUG_OBJECT (ogg, "marking pad %p EOS", pad);
685     pad->is_eos = TRUE;
686   }
687
688 done:
689   if (buf)
690     gst_buffer_unref (buf);
691   /* return combined flow result */
692   return cret;
693
694   /* special cases */
695 empty_packet:
696   {
697     GST_DEBUG_OBJECT (ogg, "Skipping empty packet");
698     goto done;
699   }
700
701 no_timestamp:
702   {
703     GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet");
704     goto done;
705   }
706 not_added:
707   {
708     GST_DEBUG_OBJECT (ogg, "pad not added yet");
709     goto done;
710   }
711 }
712
713 static guint64
714 gst_ogg_demux_collect_start_time (GstOggDemux * ogg, GstOggChain * chain)
715 {
716   gint i;
717   guint64 start_time = G_MAXUINT64;
718
719   for (i = 0; i < chain->streams->len; i++) {
720     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
721
722     if (pad->map.is_skeleton)
723       continue;
724
725     /*  can do this if the pad start time is not defined */
726     GST_DEBUG_OBJECT (ogg, "Pad %08x (%s) start time is %" GST_TIME_FORMAT,
727         pad->map.serialno, gst_ogg_stream_get_media_type (&pad->map),
728         GST_TIME_ARGS (pad->start_time));
729     if (pad->start_time == GST_CLOCK_TIME_NONE) {
730       if (!pad->map.is_sparse) {
731         start_time = G_MAXUINT64;
732         break;
733       }
734     } else {
735       start_time = MIN (start_time, pad->start_time);
736     }
737   }
738   return start_time;
739 }
740
741 static GstClockTime
742 gst_ogg_demux_collect_sync_time (GstOggDemux * ogg, GstOggChain * chain)
743 {
744   gint i;
745   GstClockTime sync_time = GST_CLOCK_TIME_NONE;
746
747   if (!chain) {
748     GST_WARNING_OBJECT (ogg, "No chain!");
749     return GST_CLOCK_TIME_NONE;
750   }
751
752   for (i = 0; i < chain->streams->len; i++) {
753     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
754
755     if (pad->map.is_sparse)
756       continue;
757
758     if (pad->push_sync_time == GST_CLOCK_TIME_NONE) {
759       sync_time = GST_CLOCK_TIME_NONE;
760       break;
761     } else {
762       if (sync_time == GST_CLOCK_TIME_NONE)
763         sync_time = pad->push_sync_time;
764       else
765         sync_time = MAX (sync_time, pad->push_sync_time);
766     }
767   }
768   return sync_time;
769 }
770
771 /* submit a packet to the oggpad, this function will run the
772  * typefind code for the pad if this is the first packet for this
773  * stream 
774  */
775 static GstFlowReturn
776 gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
777 {
778   gint64 granule;
779   GstFlowReturn ret = GST_FLOW_OK;
780
781   GstOggDemux *ogg = pad->ogg;
782
783   GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x",
784       pad, pad->map.serialno);
785
786   if (!pad->have_type) {
787     pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
788     if (!pad->have_type) {
789       pad->map.caps = gst_caps_new_empty_simple ("application/x-unknown");
790     }
791     if (pad->map.is_skeleton) {
792       GST_DEBUG_OBJECT (ogg, "we have a fishead");
793       /* copy values over to global ogg level */
794       ogg->basetime = pad->map.basetime;
795       ogg->prestime = pad->map.prestime;
796
797       /* use total time to update the total ogg time */
798       if (ogg->total_time == -1) {
799         ogg->total_time = pad->map.total_time;
800       } else if (pad->map.total_time > 0) {
801         ogg->total_time = MAX (ogg->total_time, pad->map.total_time);
802       }
803     }
804     if (pad->map.caps) {
805       gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
806     } else {
807       GST_WARNING_OBJECT (ogg, "stream parser didn't create src pad caps");
808     }
809   }
810
811   if (pad->map.is_skeleton) {
812     guint32 serialno;
813     GstOggPad *skel_pad;
814     GstOggSkeleton type;
815
816     /* try to parse the serialno first */
817     if (gst_ogg_map_parse_fisbone (&pad->map, packet->packet, packet->bytes,
818             &serialno, &type)) {
819
820       GST_DEBUG_OBJECT (pad->ogg,
821           "got skeleton packet for stream 0x%08x", serialno);
822
823       skel_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
824       if (skel_pad) {
825         switch (type) {
826           case GST_OGG_SKELETON_FISBONE:
827             /* parse the remainder of the fisbone in the pad with the serialno,
828              * note that we ignore the start_time as this is usually wrong for
829              * live streams */
830             gst_ogg_map_add_fisbone (&skel_pad->map, &pad->map, packet->packet,
831                 packet->bytes, NULL);
832             break;
833           case GST_OGG_SKELETON_INDEX:
834             gst_ogg_map_add_index (&skel_pad->map, &pad->map, packet->packet,
835                 packet->bytes);
836
837             /* use total time to update the total ogg time */
838             if (ogg->total_time == -1) {
839               ogg->total_time = skel_pad->map.total_time;
840             } else if (skel_pad->map.total_time > 0) {
841               ogg->total_time = MAX (ogg->total_time, skel_pad->map.total_time);
842             }
843             break;
844           default:
845             break;
846         }
847
848       } else {
849         GST_WARNING_OBJECT (pad->ogg,
850             "found skeleton fisbone for an unknown stream 0x%08x", serialno);
851       }
852     }
853   }
854
855   granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
856       packet->granulepos);
857   if (granule != -1) {
858     GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
859     pad->current_granule = granule;
860   }
861
862   /* restart header packet count when seeing a b_o_s page;
863    * particularly useful following a seek or even following chain finding */
864   if (packet->b_o_s) {
865     GST_DEBUG_OBJECT (ogg, "b_o_s packet, resetting header packet count");
866     pad->map.n_header_packets_seen = 0;
867     if (!pad->map.have_headers) {
868       GST_DEBUG_OBJECT (ogg, "clearing header packets");
869       g_list_foreach (pad->map.headers, (GFunc) _ogg_packet_free, NULL);
870       g_list_free (pad->map.headers);
871       pad->map.headers = NULL;
872     }
873   }
874
875   /* Overload the value of b_o_s in ogg_packet with a flag whether or
876    * not this is a header packet.  Maybe some day this could be cleaned
877    * up.  */
878   packet->b_o_s = gst_ogg_stream_packet_is_header (&pad->map, packet);
879   if (!packet->b_o_s) {
880     GST_DEBUG ("found non-header packet");
881     pad->map.have_headers = TRUE;
882     if (pad->start_time == GST_CLOCK_TIME_NONE) {
883       gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
884       GST_DEBUG ("duration %" G_GINT64_FORMAT, duration);
885       if (duration != -1) {
886         pad->map.accumulated_granule += duration;
887         GST_DEBUG ("accumulated granule %" G_GINT64_FORMAT,
888             pad->map.accumulated_granule);
889       }
890
891       if (packet->granulepos != -1) {
892         ogg_int64_t start_granule;
893         gint64 granule;
894
895         granule = gst_ogg_stream_granulepos_to_granule (&pad->map,
896             packet->granulepos);
897
898         if (granule > pad->map.accumulated_granule)
899           start_granule = granule - pad->map.accumulated_granule;
900         else
901           start_granule = 0;
902
903         pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
904             start_granule);
905         GST_DEBUG_OBJECT (ogg,
906             "start time %" GST_TIME_FORMAT " (%" GST_TIME_FORMAT ") for %s "
907             "from granpos %" G_GINT64_FORMAT " (granule %" G_GINT64_FORMAT ", "
908             "accumulated granule %" G_GINT64_FORMAT,
909             GST_TIME_ARGS (pad->start_time), GST_TIME_ARGS (pad->start_time),
910             gst_ogg_stream_get_media_type (&pad->map),
911             (gint64) packet->granulepos, granule, pad->map.accumulated_granule);
912       } else {
913         packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map,
914             pad->map.accumulated_granule, pad->keyframe_granule);
915       }
916     }
917   } else {
918     /* look for tags in header packet (before inc header count) */
919     gst_ogg_stream_extract_tags (&pad->map, packet);
920     pad->map.n_header_packets_seen++;
921     if (!pad->map.have_headers) {
922       pad->map.headers =
923           g_list_append (pad->map.headers, _ogg_packet_copy (packet));
924       GST_DEBUG ("keeping header packet %d", pad->map.n_header_packets_seen);
925     }
926   }
927
928   /* we know the start_time of the pad data, see if we
929    * can activate the complete chain if this is a dynamic
930    * chain. We need all the headers too for this. */
931   if (pad->start_time != GST_CLOCK_TIME_NONE && pad->map.have_headers) {
932     GstOggChain *chain = pad->chain;
933
934     /* check if complete chain has start time */
935     if (chain == ogg->building_chain) {
936       GstEvent *event = NULL;
937
938       if (ogg->resync) {
939         guint64 start_time;
940
941         GST_DEBUG_OBJECT (ogg, "need to resync");
942
943         /* when we need to resync after a seek, we wait until we have received
944          * timestamps on all streams */
945         start_time = gst_ogg_demux_collect_start_time (ogg, chain);
946
947         if (start_time != G_MAXUINT64) {
948           gint64 segment_time;
949           GstSegment segment;
950
951           GST_DEBUG_OBJECT (ogg, "start_time:  %" GST_TIME_FORMAT,
952               GST_TIME_ARGS (start_time));
953
954           if (chain->segment_start < start_time)
955             segment_time =
956                 (start_time - chain->segment_start) + chain->begin_time;
957           else
958             segment_time = chain->begin_time;
959
960           /* create the newsegment event we are going to send out */
961           gst_segment_init (&segment, GST_FORMAT_TIME);
962
963           GST_PUSH_LOCK (ogg);
964           if (!ogg->pullmode && ogg->push_state == PUSH_LINEAR2) {
965             /* if we are fast forwarding to the actual seek target,
966                ensure previous frames are clipped */
967             GST_DEBUG_OBJECT (ogg,
968                 "Resynced, starting segment at %" GST_TIME_FORMAT
969                 ", start_time %" GST_TIME_FORMAT,
970                 GST_TIME_ARGS (ogg->push_seek_time_original_target),
971                 GST_TIME_ARGS (start_time));
972             segment.rate = ogg->push_seek_rate;
973             segment.start = ogg->push_seek_time_original_target;
974             segment.stop = -1;
975             segment.time = ogg->push_seek_time_original_target;
976             event = gst_event_new_segment (&segment);
977             ogg->push_state = PUSH_PLAYING;
978           } else {
979             segment.rate = ogg->segment.rate;
980             segment.applied_rate = ogg->segment.applied_rate;
981             segment.start = start_time;
982             segment.stop = chain->segment_stop;
983             segment.time = segment_time;
984             event = gst_event_new_segment (&segment);
985           }
986           GST_PUSH_UNLOCK (ogg);
987
988           ogg->resync = FALSE;
989         }
990       } else {
991         /* see if we have enough info to activate the chain, we have enough info
992          * when all streams have a valid start time. */
993         if (gst_ogg_demux_collect_chain_info (ogg, chain)) {
994           GstSegment segment;
995
996           GST_DEBUG_OBJECT (ogg, "segment_start: %" GST_TIME_FORMAT,
997               GST_TIME_ARGS (chain->segment_start));
998           GST_DEBUG_OBJECT (ogg, "segment_stop:  %" GST_TIME_FORMAT,
999               GST_TIME_ARGS (chain->segment_stop));
1000           GST_DEBUG_OBJECT (ogg, "segment_time:  %" GST_TIME_FORMAT,
1001               GST_TIME_ARGS (chain->begin_time));
1002
1003           /* create the newsegment event we are going to send out */
1004           gst_segment_init (&segment, GST_FORMAT_TIME);
1005           segment.rate = ogg->segment.rate;
1006           segment.applied_rate = ogg->segment.applied_rate;
1007           segment.start = chain->segment_start;
1008           segment.stop = chain->segment_stop;
1009           segment.time = chain->begin_time;
1010           event = gst_event_new_segment (&segment);
1011         }
1012       }
1013
1014       if (event) {
1015         gst_event_set_seqnum (event, ogg->seqnum);
1016
1017         gst_ogg_demux_activate_chain (ogg, chain, event);
1018
1019         ogg->building_chain = NULL;
1020       }
1021     }
1022   }
1023
1024   /* if we are building a chain, store buffer for when we activate
1025    * it. This path is taken if we operate in streaming mode. */
1026   if (ogg->building_chain) {
1027     /* bos packets where stored in the header list so we can discard
1028      * them here*/
1029     if (!packet->b_o_s)
1030       ret = gst_ogg_demux_queue_data (pad, packet);
1031   }
1032   /* else we are completely streaming to the peer */
1033   else {
1034     ret = gst_ogg_demux_chain_peer (pad, packet, !ogg->pullmode);
1035   }
1036   return ret;
1037 }
1038
1039 /* flush at most @npackets from the stream layer. All packets if 
1040  * @npackets is 0;
1041  */
1042 static GstFlowReturn
1043 gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets)
1044 {
1045   GstFlowReturn result = GST_FLOW_OK;
1046   gboolean done = FALSE;
1047   GstOggDemux *ogg;
1048
1049   ogg = pad->ogg;
1050
1051   while (!done) {
1052     int ret;
1053     ogg_packet packet;
1054
1055     ret = ogg_stream_packetout (&pad->map.stream, &packet);
1056     switch (ret) {
1057       case 0:
1058         GST_LOG_OBJECT (ogg, "packetout done");
1059         done = TRUE;
1060         break;
1061       case -1:
1062         GST_LOG_OBJECT (ogg, "packetout discont");
1063         if (!pad->map.is_sparse) {
1064           gst_ogg_chain_mark_discont (pad->chain);
1065         } else {
1066           gst_ogg_pad_mark_discont (pad);
1067         }
1068         break;
1069       case 1:
1070         GST_LOG_OBJECT (ogg, "packetout gave packet of size %ld", packet.bytes);
1071         if (packet.bytes > ogg->max_packet_size)
1072           ogg->max_packet_size = packet.bytes;
1073         result = gst_ogg_pad_submit_packet (pad, &packet);
1074         /* not linked is not a problem, it's possible that we are still
1075          * collecting headers and that we don't have exposed the pads yet */
1076         if (result == GST_FLOW_NOT_LINKED)
1077           break;
1078         else if (result <= GST_FLOW_EOS)
1079           goto could_not_submit;
1080         break;
1081       default:
1082         GST_WARNING_OBJECT (ogg,
1083             "invalid return value %d for ogg_stream_packetout, resetting stream",
1084             ret);
1085         gst_ogg_pad_reset (pad);
1086         break;
1087     }
1088     if (npackets > 0) {
1089       npackets--;
1090       done = (npackets == 0);
1091     }
1092   }
1093   return result;
1094
1095   /* ERRORS */
1096 could_not_submit:
1097   {
1098     GST_WARNING_OBJECT (ogg,
1099         "could not submit packet for stream %08x, "
1100         "error: %d", pad->map.serialno, result);
1101     gst_ogg_pad_reset (pad);
1102     return result;
1103   }
1104 }
1105
1106 static void
1107 gst_ogg_demux_setup_bisection_bounds (GstOggDemux * ogg)
1108 {
1109   if (ogg->push_last_seek_time >= ogg->push_seek_time_target) {
1110     GST_DEBUG_OBJECT (ogg, "We overshot by %" GST_TIME_FORMAT,
1111         GST_TIME_ARGS (ogg->push_last_seek_time - ogg->push_seek_time_target));
1112     ogg->push_offset1 = ogg->push_last_seek_offset;
1113     ogg->push_time1 = ogg->push_last_seek_time;
1114     ogg->seek_undershot = FALSE;
1115   } else {
1116     GST_DEBUG_OBJECT (ogg, "We undershot by %" GST_TIME_FORMAT,
1117         GST_TIME_ARGS (ogg->push_seek_time_target - ogg->push_last_seek_time));
1118     ogg->push_offset0 = ogg->push_last_seek_offset;
1119     ogg->push_time0 = ogg->push_last_seek_time;
1120     ogg->seek_undershot = TRUE;
1121   }
1122 }
1123
1124 static gint64
1125 gst_ogg_demux_estimate_bisection_target (GstOggDemux * ogg, float seek_quality)
1126 {
1127   gint64 best;
1128   gint64 segment_bitrate;
1129   gint64 skew;
1130
1131   /* we might not know the length of the stream in time,
1132      so push_time1 might not be set */
1133   GST_DEBUG_OBJECT (ogg,
1134       "push time 1: %" GST_TIME_FORMAT ", dbytes %" G_GINT64_FORMAT,
1135       GST_TIME_ARGS (ogg->push_time1), ogg->push_offset1 - ogg->push_offset0);
1136   if (ogg->push_time1 == GST_CLOCK_TIME_NONE) {
1137     GST_DEBUG_OBJECT (ogg,
1138         "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1139         ", time %" GST_TIME_FORMAT " (open ended)", ogg->push_offset0,
1140         ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0));
1141     if (ogg->push_last_seek_time == ogg->push_start_time) {
1142       /* if we're at start and don't know the end time, we can't estimate
1143          bitrate, so get the nominal declared bitrate as a failsafe, or some
1144          random constant which will be discarded after we made a (probably
1145          dire) first guess */
1146       segment_bitrate = (ogg->bitrate > 0 ? ogg->bitrate : 1000);
1147     } else {
1148       segment_bitrate =
1149           gst_util_uint64_scale (ogg->push_last_seek_offset - 0,
1150           8 * GST_SECOND, ogg->push_last_seek_time - ogg->push_start_time);
1151     }
1152     best =
1153         ogg->push_offset0 +
1154         gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1155         segment_bitrate, 8 * GST_SECOND);
1156     ogg->seek_secant = TRUE;
1157   } else {
1158     GST_DEBUG_OBJECT (ogg,
1159         "New segment to consider: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT
1160         ", time %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, ogg->push_offset0,
1161         ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
1162         GST_TIME_ARGS (ogg->push_time1));
1163     if (ogg->push_time0 == ogg->push_time1) {
1164       best = ogg->push_offset0;
1165     } else {
1166       segment_bitrate =
1167           gst_util_uint64_scale (ogg->push_offset1 - ogg->push_offset0,
1168           8 * GST_SECOND, ogg->push_time1 - ogg->push_time0);
1169       GST_DEBUG_OBJECT (ogg,
1170           "Local bitrate on the %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1171           " segment: %" G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_time0),
1172           GST_TIME_ARGS (ogg->push_time1), segment_bitrate);
1173
1174       best =
1175           ogg->push_offset0 +
1176           gst_util_uint64_scale (ogg->push_seek_time_target - ogg->push_time0,
1177           segment_bitrate, 8 * GST_SECOND);
1178       if (seek_quality < 0.5f && ogg->seek_secant) {
1179         gint64 new_best, best2 = (ogg->push_offset0 + ogg->push_offset1) / 2;
1180         /* if dire result, give as much as 25% weight to a dumb bisection guess */
1181         float secant_weight = 1.0f - ((0.5 - seek_quality) / 0.5f) * 0.25;
1182         new_best = (best * secant_weight + best2 * (1.0f - secant_weight));
1183         GST_DEBUG_OBJECT (ogg,
1184             "Secant says %" G_GINT64_FORMAT ", straight is %" G_GINT64_FORMAT
1185             ", new best %" G_GINT64_FORMAT " with secant_weight %f", best,
1186             best2, new_best, secant_weight);
1187         best = new_best;
1188         ogg->seek_secant = FALSE;
1189       } else {
1190         ogg->seek_secant = TRUE;
1191       }
1192     }
1193   }
1194
1195   GST_DEBUG_OBJECT (ogg, "Raw best guess: %" G_GINT64_FORMAT, best);
1196
1197   /* offset the guess down as we need to capture the start of the
1198      page we are targetting - but only do so if we did not undershoot
1199      last time, as we're likely to still do this time */
1200   if (!ogg->seek_undershot) {
1201     /* very small packets are packed on pages, so offset by at least
1202        a value which is likely to get us at least one page where the
1203        packet starts */
1204     skew =
1205         ogg->max_packet_size >
1206         ogg->max_page_size ? ogg->max_packet_size : ogg->max_page_size;
1207     GST_DEBUG_OBJECT (ogg, "Offsetting by %" G_GINT64_FORMAT, skew);
1208     best -= skew;
1209   }
1210
1211   /* do not seek too close to the bounds, as we stop seeking
1212      when we get to within max_packet_size before the target */
1213   if (best > ogg->push_offset1 - ogg->max_packet_size) {
1214     best = ogg->push_offset1 - ogg->max_packet_size;
1215     GST_DEBUG_OBJECT (ogg,
1216         "Too close to high bound, pushing back to %" G_GINT64_FORMAT, best);
1217   } else if (best < ogg->push_offset0 + ogg->max_packet_size) {
1218     best = ogg->push_offset0 + ogg->max_packet_size;
1219     GST_DEBUG_OBJECT (ogg,
1220         "Too close to low bound, pushing forth to %" G_GINT64_FORMAT, best);
1221   }
1222
1223   /* keep within bounds */
1224   if (best > ogg->push_offset1)
1225     best = ogg->push_offset1;
1226   if (best < ogg->push_offset0)
1227     best = ogg->push_offset0;
1228
1229   GST_DEBUG_OBJECT (ogg, "Choosing target %" G_GINT64_FORMAT, best);
1230   return best;
1231 }
1232
1233 static void
1234 gst_ogg_demux_record_keyframe_time (GstOggDemux * ogg, GstOggPad * pad,
1235     ogg_int64_t granpos)
1236 {
1237   gint64 kf_granule;
1238   GstClockTime kf_time;
1239
1240   kf_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, granpos);
1241   kf_time = gst_ogg_stream_granule_to_time (&pad->map, kf_granule);
1242
1243   pad->push_kf_time = kf_time;
1244 }
1245
1246 /* returns the earliest keyframe time for all non sparse pads in the chain,
1247  * if known, and GST_CLOCK_TIME_NONE if not */
1248 static GstClockTime
1249 gst_ogg_demux_get_earliest_keyframe_time (GstOggDemux * ogg)
1250 {
1251   GstClockTime t = GST_CLOCK_TIME_NONE;
1252   GstOggChain *chain = ogg->building_chain;
1253   int i;
1254
1255   if (!chain) {
1256     GST_WARNING_OBJECT (ogg, "No chain!");
1257     return GST_CLOCK_TIME_NONE;
1258   }
1259   for (i = 0; i < chain->streams->len; i++) {
1260     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1261
1262     if (pad->map.is_sparse)
1263       continue;
1264     if (pad->push_kf_time == GST_CLOCK_TIME_NONE)
1265       return GST_CLOCK_TIME_NONE;
1266     if (t == GST_CLOCK_TIME_NONE || pad->push_kf_time < t)
1267       t = pad->push_kf_time;
1268   }
1269
1270   return t;
1271 }
1272
1273 /* MUST be called with the push lock locked, and will unlock it
1274    regardless of return value. */
1275 static GstFlowReturn
1276 gst_ogg_demux_seek_back_after_push_duration_check_unlock (GstOggDemux * ogg)
1277 {
1278   GstEvent *event;
1279
1280   /* Get the delayed event, if any */
1281   event = ogg->push_mode_seek_delayed_event;
1282   ogg->push_mode_seek_delayed_event = NULL;
1283
1284   ogg->push_state = PUSH_PLAYING;
1285
1286   GST_PUSH_UNLOCK (ogg);
1287
1288   if (event) {
1289     /* If there is one, perform it */
1290     gst_ogg_demux_perform_seek_push (ogg, event);
1291   } else {
1292     /* If there wasn't, seek back at start to start normal playback */
1293     GST_INFO_OBJECT (ogg, "Seeking back to 0 after duration check");
1294     event = gst_event_new_seek (1.0, GST_FORMAT_BYTES,
1295         GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
1296         GST_SEEK_TYPE_SET, 1, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
1297     if (!gst_pad_push_event (ogg->sinkpad, event)) {
1298       GST_WARNING_OBJECT (ogg, "Failed seeking back to start");
1299       return GST_FLOW_ERROR;
1300     }
1301   }
1302
1303   return GST_FLOW_OK;
1304 }
1305
1306 static float
1307 gst_ogg_demux_estimate_seek_quality (GstOggDemux * ogg)
1308 {
1309   gint64 diff;                  /* how far from the goal we ended up */
1310   gint64 dist;                  /* how far we moved this iteration */
1311   float seek_quality;
1312
1313   if (ogg->push_prev_seek_time == GST_CLOCK_TIME_NONE) {
1314     /* for the first seek, we pretend we got a good seek,
1315        as we don't have a previous seek yet */
1316     return 1.0f;
1317   }
1318
1319   /* We take a guess at how good the last seek was at guessing
1320      the byte target by comparing the amplitude of the last
1321      seek to the error */
1322   diff = ogg->push_seek_time_target - ogg->push_last_seek_time;
1323   if (diff < 0)
1324     diff = -diff;
1325   dist = ogg->push_last_seek_time - ogg->push_prev_seek_time;
1326   if (dist < 0)
1327     dist = -dist;
1328
1329   seek_quality = (dist == 0) ? 0.0f : 1.0f / (1.0f + diff / (float) dist);
1330
1331   GST_DEBUG_OBJECT (ogg,
1332       "We moved %" GST_TIME_FORMAT ", we're off by %" GST_TIME_FORMAT
1333       ", seek quality %f", GST_TIME_ARGS (dist), GST_TIME_ARGS (diff),
1334       seek_quality);
1335   return seek_quality;
1336 }
1337
1338 static void
1339 gst_ogg_demux_update_bisection_stats (GstOggDemux * ogg)
1340 {
1341   int n;
1342
1343   GST_INFO_OBJECT (ogg, "Bisection needed %d + %d steps",
1344       ogg->push_bisection_steps[0], ogg->push_bisection_steps[1]);
1345
1346   for (n = 0; n < 2; ++n) {
1347     ogg->stats_bisection_steps[n] += ogg->push_bisection_steps[n];
1348     if (ogg->stats_bisection_max_steps[n] < ogg->push_bisection_steps[n])
1349       ogg->stats_bisection_max_steps[n] = ogg->push_bisection_steps[n];
1350   }
1351   ogg->stats_nbisections++;
1352
1353   GST_INFO_OBJECT (ogg,
1354       "So far, %.2f + %.2f bisections needed per seek (max %d + %d)",
1355       ogg->stats_bisection_steps[0] / (float) ogg->stats_nbisections,
1356       ogg->stats_bisection_steps[1] / (float) ogg->stats_nbisections,
1357       ogg->stats_bisection_max_steps[0], ogg->stats_bisection_max_steps[1]);
1358 }
1359
1360 static gboolean
1361 gst_ogg_pad_handle_push_mode_state (GstOggPad * pad, ogg_page * page)
1362 {
1363   GstOggDemux *ogg = pad->ogg;
1364   ogg_int64_t granpos = ogg_page_granulepos (page);
1365
1366   GST_PUSH_LOCK (ogg);
1367   if (granpos >= 0) {
1368     if (ogg->push_start_time == GST_CLOCK_TIME_NONE) {
1369       ogg->push_start_time =
1370           gst_ogg_stream_get_start_time_for_granulepos (&pad->map, granpos);
1371       GST_DEBUG_OBJECT (ogg, "Stream start time: %" GST_TIME_FORMAT,
1372           GST_TIME_ARGS (ogg->push_start_time));
1373     }
1374     ogg->push_time_offset =
1375         gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1376     if (ogg->push_time_offset > 0) {
1377       GST_DEBUG_OBJECT (ogg, "Bitrate since start: %" G_GUINT64_FORMAT,
1378           gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
1379               ogg->push_time_offset));
1380     }
1381
1382     if (ogg->push_state == PUSH_DURATION) {
1383       GstClockTime t =
1384           gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1385
1386       if (ogg->total_time == GST_CLOCK_TIME_NONE || t > ogg->total_time) {
1387         GST_DEBUG_OBJECT (ogg, "New total time: %" GST_TIME_FORMAT,
1388             GST_TIME_ARGS (t));
1389         ogg->total_time = t;
1390         ogg->push_time_length = t;
1391       }
1392
1393       /* If we were determining the duration of the stream, we're now done,
1394          and can get back to sending the original event we delayed.
1395          We stop a bit before the end of the stream, as if we get a EOS
1396          event and there is a queue2 upstream (such as when using playbin2),
1397          it will pause the task *after* we come back from the EOS handler,
1398          so we cannot prevent the pausing by issuing a seek. */
1399       if (ogg->push_byte_offset + EOS_AVOIDANCE_THRESHOLD >=
1400           ogg->push_byte_length) {
1401         GstMessage *message;
1402         GstFlowReturn res;
1403
1404         /* tell the pipeline we've just found out the duration */
1405         ogg->push_time_length = ogg->total_time;
1406         GST_INFO_OBJECT (ogg, "New duration found: %" GST_TIME_FORMAT,
1407             GST_TIME_ARGS (ogg->total_time));
1408         message =
1409             gst_message_new_duration (GST_OBJECT (ogg), GST_FORMAT_TIME,
1410             ogg->total_time);
1411         gst_element_post_message (GST_ELEMENT (ogg), message);
1412
1413         GST_DEBUG_OBJECT (ogg,
1414             "We're close enough to the end, and we're scared "
1415             "to get too close, seeking back to start");
1416
1417         res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
1418         if (res != GST_FLOW_OK)
1419           return res;
1420         return GST_FLOW_SKIP_PUSH;
1421       } else {
1422         GST_PUSH_UNLOCK (ogg);
1423       }
1424       return GST_FLOW_SKIP_PUSH;
1425     }
1426   }
1427
1428   /* if we're seeking, look at time, and decide what to do */
1429   if (ogg->push_state != PUSH_PLAYING && ogg->push_state != PUSH_LINEAR2) {
1430     GstClockTime t;
1431     gint64 best = -1;
1432     GstEvent *sevent;
1433     int res;
1434     gboolean close_enough;
1435     float seek_quality;
1436
1437     /* ignore -1 granpos when seeking, we want to sync on a real granpos */
1438     if (granpos < 0) {
1439       GST_PUSH_UNLOCK (ogg);
1440       if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1441         goto choked;
1442       return GST_FLOW_SKIP_PUSH;
1443     }
1444
1445     t = gst_ogg_stream_get_end_time_for_granulepos (&pad->map, granpos);
1446
1447     if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1448       GstClockTime sync_time;
1449
1450       if (pad->push_sync_time == GST_CLOCK_TIME_NONE)
1451         pad->push_sync_time = t;
1452       GST_DEBUG_OBJECT (ogg, "Got timestamp %" GST_TIME_FORMAT " for %s",
1453           GST_TIME_ARGS (t), gst_ogg_stream_get_media_type (&pad->map));
1454       sync_time = gst_ogg_demux_collect_sync_time (ogg, ogg->building_chain);
1455       if (sync_time == GST_CLOCK_TIME_NONE) {
1456         GST_PUSH_UNLOCK (ogg);
1457         GST_DEBUG_OBJECT (ogg,
1458             "Not enough timing info collected for sync, waiting for more");
1459         if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1460           goto choked;
1461         return GST_FLOW_SKIP_PUSH;
1462       }
1463       ogg->push_last_seek_time = sync_time;
1464
1465       GST_DEBUG_OBJECT (ogg,
1466           "Bisection just seeked at %" G_GINT64_FORMAT ", time %"
1467           GST_TIME_FORMAT ", target was %" GST_TIME_FORMAT,
1468           ogg->push_last_seek_offset,
1469           GST_TIME_ARGS (ogg->push_last_seek_time),
1470           GST_TIME_ARGS (ogg->push_seek_time_target));
1471
1472       if (ogg->push_time1 != GST_CLOCK_TIME_NONE) {
1473         seek_quality = gst_ogg_demux_estimate_seek_quality (ogg);
1474         GST_DEBUG_OBJECT (ogg,
1475             "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1476             G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1477             " (%" GST_TIME_FORMAT "), seek quality %f", ogg->push_offset0,
1478             ogg->push_offset1, ogg->push_offset1 - ogg->push_offset0,
1479             GST_TIME_ARGS (ogg->push_time0), GST_TIME_ARGS (ogg->push_time1),
1480             GST_TIME_ARGS (ogg->push_time1 - ogg->push_time0), seek_quality);
1481       } else {
1482         /* in a open ended seek, we can't do bisection, so we pretend
1483            we like our result so far */
1484         seek_quality = 1.0f;
1485         GST_DEBUG_OBJECT (ogg,
1486             "Interval was %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%"
1487             G_GINT64_FORMAT "), time %" GST_TIME_FORMAT " - unknown",
1488             ogg->push_offset0, ogg->push_offset1,
1489             ogg->push_offset1 - ogg->push_offset0,
1490             GST_TIME_ARGS (ogg->push_time0));
1491       }
1492       ogg->push_prev_seek_time = ogg->push_last_seek_time;
1493
1494       gst_ogg_demux_setup_bisection_bounds (ogg);
1495
1496       best = gst_ogg_demux_estimate_bisection_target (ogg, seek_quality);
1497
1498       if (ogg->push_seek_time_target == 0) {
1499         GST_DEBUG_OBJECT (ogg, "Seeking to 0, deemed close enough");
1500         close_enough = (ogg->push_last_seek_time == 0);
1501       } else {
1502         /* TODO: make this dependent on framerate ? */
1503         GstClockTime time_threshold = GST_SECOND / 2;
1504         guint64 byte_threshold =
1505             (ogg->max_packet_size >
1506             64 * 1024 ? ogg->max_packet_size : 64 * 1024);
1507
1508         /* We want to be within half a second before the target,
1509            or before the target and half less or equal to the max
1510            packet size left to search in */
1511         if (time_threshold > ogg->push_seek_time_target)
1512           time_threshold = ogg->push_seek_time_target;
1513         close_enough = ogg->push_last_seek_time < ogg->push_seek_time_target
1514             && (ogg->push_last_seek_time >=
1515             ogg->push_seek_time_target - time_threshold
1516             || ogg->push_offset1 <= ogg->push_offset0 + byte_threshold);
1517         GST_DEBUG_OBJECT (ogg,
1518             "testing if we're close enough: %" GST_TIME_FORMAT " <= %"
1519             GST_TIME_FORMAT " < %" GST_TIME_FORMAT ", or %" G_GUINT64_FORMAT
1520             " <= %" G_GUINT64_FORMAT " ? %s",
1521             GST_TIME_ARGS (ogg->push_seek_time_target - time_threshold),
1522             GST_TIME_ARGS (ogg->push_last_seek_time),
1523             GST_TIME_ARGS (ogg->push_seek_time_target),
1524             ogg->push_offset1 - ogg->push_offset0, byte_threshold,
1525             close_enough ? "Yes" : "No");
1526       }
1527
1528       if (close_enough || best == ogg->push_last_seek_offset) {
1529         if (ogg->push_state == PUSH_BISECT1) {
1530           /* we now know the time segment we'll have to search for
1531              the second bisection */
1532           ogg->push_time0 = ogg->push_start_time;
1533           ogg->push_offset0 = 0;
1534
1535           GST_DEBUG_OBJECT (ogg,
1536               "Seek to %" GST_TIME_FORMAT
1537               " (%lx) done, now gathering pages for all non-sparse streams",
1538               GST_TIME_ARGS (ogg->push_seek_time_target), (long) granpos);
1539           ogg->push_state = PUSH_LINEAR1;
1540         } else {
1541           /* If we're asked for an accurate seek, we'll go forward till
1542              we get to the original seek target time, else we'll just drop
1543              here at the keyframe */
1544           if (ogg->push_seek_flags & GST_SEEK_FLAG_ACCURATE) {
1545             GST_INFO_OBJECT (ogg,
1546                 "Seek to keyframe at %" GST_TIME_FORMAT " done (we're at %"
1547                 GST_TIME_FORMAT "), skipping to original target (%"
1548                 GST_TIME_FORMAT ")",
1549                 GST_TIME_ARGS (ogg->push_seek_time_target),
1550                 GST_TIME_ARGS (sync_time),
1551                 GST_TIME_ARGS (ogg->push_seek_time_original_target));
1552             ogg->push_state = PUSH_LINEAR2;
1553           } else {
1554             GST_INFO_OBJECT (ogg, "Seek to keyframe done, playing");
1555
1556             /* we're synced to the seek target, so flush stream and stuff
1557                any queued pages into the stream so we start decoding there */
1558             ogg->push_state = PUSH_PLAYING;
1559           }
1560           gst_ogg_demux_update_bisection_stats (ogg);
1561         }
1562       }
1563     } else if (ogg->push_state == PUSH_LINEAR1) {
1564       if (pad->push_kf_time == GST_CLOCK_TIME_NONE) {
1565         GstClockTime earliest_keyframe_time;
1566
1567         gst_ogg_demux_record_keyframe_time (ogg, pad, granpos);
1568         GST_DEBUG_OBJECT (ogg,
1569             "Previous keyframe for %s stream at %" GST_TIME_FORMAT,
1570             gst_ogg_stream_get_media_type (&pad->map),
1571             GST_TIME_ARGS (pad->push_kf_time));
1572         earliest_keyframe_time = gst_ogg_demux_get_earliest_keyframe_time (ogg);
1573         if (earliest_keyframe_time != GST_CLOCK_TIME_NONE) {
1574           if (earliest_keyframe_time > ogg->push_last_seek_time) {
1575             GST_INFO_OBJECT (ogg,
1576                 "All non sparse streams now have a previous keyframe time, "
1577                 "and we already decoded it, switching to playing");
1578             ogg->push_state = PUSH_PLAYING;
1579             gst_ogg_demux_update_bisection_stats (ogg);
1580           } else {
1581             GST_INFO_OBJECT (ogg,
1582                 "All non sparse streams now have a previous keyframe time, "
1583                 "bisecting again to %" GST_TIME_FORMAT,
1584                 GST_TIME_ARGS (earliest_keyframe_time));
1585
1586             ogg->push_seek_time_target = earliest_keyframe_time;
1587             ogg->push_offset0 = 0;
1588             ogg->push_time0 = ogg->push_start_time;
1589             ogg->push_offset1 = ogg->push_last_seek_offset;
1590             ogg->push_time1 = ogg->push_last_seek_time;
1591             ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
1592             ogg->seek_secant = FALSE;
1593             ogg->seek_undershot = FALSE;
1594
1595             ogg->push_state = PUSH_BISECT2;
1596             best = gst_ogg_demux_estimate_bisection_target (ogg, 1.0f);
1597           }
1598         }
1599       }
1600     }
1601
1602     if (ogg->push_state == PUSH_BISECT1 || ogg->push_state == PUSH_BISECT2) {
1603       gint i;
1604
1605       ogg_sync_reset (&ogg->sync);
1606       for (i = 0; i < ogg->building_chain->streams->len; i++) {
1607         GstOggPad *pad =
1608             g_array_index (ogg->building_chain->streams, GstOggPad *, i);
1609
1610         pad->push_sync_time = GST_CLOCK_TIME_NONE;
1611         ogg_stream_reset (&pad->map.stream);
1612       }
1613
1614       GST_DEBUG_OBJECT (ogg,
1615           "seeking to %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, best,
1616           (gint64) - 1);
1617       /* do seek */
1618       g_assert (best != -1);
1619       ogg->push_bisection_steps[ogg->push_state == PUSH_BISECT2 ? 1 : 0]++;
1620       sevent =
1621           gst_event_new_seek (ogg->push_seek_rate, GST_FORMAT_BYTES,
1622           ogg->push_seek_flags, GST_SEEK_TYPE_SET, best,
1623           GST_SEEK_TYPE_NONE, -1);
1624
1625       GST_PUSH_UNLOCK (ogg);
1626       res = gst_pad_push_event (ogg->sinkpad, sevent);
1627       if (!res) {
1628         /* We failed to send the seek event, notify the pipeline */
1629         GST_ELEMENT_ERROR (ogg, RESOURCE, SEEK, (NULL), ("Failed to seek"));
1630         return GST_FLOW_ERROR;
1631       }
1632       return GST_FLOW_SKIP_PUSH;
1633     }
1634
1635     if (ogg->push_state != PUSH_PLAYING) {
1636       GST_PUSH_UNLOCK (ogg);
1637       return GST_FLOW_SKIP_PUSH;
1638     }
1639   }
1640   GST_PUSH_UNLOCK (ogg);
1641
1642   return GST_FLOW_OK;
1643
1644 choked:
1645   {
1646     GST_WARNING_OBJECT (ogg,
1647         "ogg stream choked on page (serial %08x), "
1648         "resetting stream", pad->map.serialno);
1649     gst_ogg_pad_reset (pad);
1650     /* we continue to recover */
1651     return GST_FLOW_SKIP_PUSH;
1652   }
1653 }
1654
1655 /* submit a page to an oggpad, this function will then submit all
1656  * the packets in the page.
1657  */
1658 static GstFlowReturn
1659 gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page)
1660 {
1661   GstFlowReturn result = GST_FLOW_OK;
1662   GstOggDemux *ogg;
1663   gboolean continued = FALSE;
1664
1665   ogg = pad->ogg;
1666
1667   /* for negative rates we read pages backwards and must therefore be careful
1668    * with continued pages */
1669   if (ogg->segment.rate < 0.0) {
1670     gint npackets;
1671
1672     continued = ogg_page_continued (page);
1673
1674     /* number of completed packets in the page */
1675     npackets = ogg_page_packets (page);
1676     if (!continued) {
1677       /* page is not continued so it contains at least one packet start. It's
1678        * possible that no packet ends on this page (npackets == 0). In that
1679        * case, the next (continued) page(s) we kept contain the remainder of the
1680        * packets. We mark npackets=1 to make us start decoding the pages in the
1681        * remainder of the algorithm. */
1682       if (npackets == 0)
1683         npackets = 1;
1684     }
1685     GST_LOG_OBJECT (ogg, "continued: %d, %d packets", continued, npackets);
1686
1687     if (npackets == 0) {
1688       GST_LOG_OBJECT (ogg, "no decodable packets, we need a previous page");
1689       goto done;
1690     }
1691   }
1692
1693   /* keep track of time in push mode */
1694   if (!ogg->pullmode) {
1695     result = gst_ogg_pad_handle_push_mode_state (pad, page);
1696     if (result == GST_FLOW_SKIP_PUSH)
1697       return GST_FLOW_OK;
1698     if (result != GST_FLOW_OK)
1699       return result;
1700   }
1701
1702   if (page->header_len + page->body_len > ogg->max_page_size)
1703     ogg->max_page_size = page->header_len + page->body_len;
1704
1705   if (ogg_stream_pagein (&pad->map.stream, page) != 0)
1706     goto choked;
1707
1708   /* flush all packets in the stream layer, this might not give a packet if
1709    * the page had no packets finishing on the page (npackets == 0). */
1710   result = gst_ogg_pad_stream_out (pad, 0);
1711
1712   if (pad->continued) {
1713     ogg_packet packet;
1714
1715     /* now send the continued pages to the stream layer */
1716     while (pad->continued) {
1717       ogg_page *p = (ogg_page *) pad->continued->data;
1718
1719       GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
1720       if (ogg_stream_pagein (&pad->map.stream, p) != 0)
1721         goto choked;
1722
1723       pad->continued = g_list_delete_link (pad->continued, pad->continued);
1724
1725       /* free the page */
1726       gst_ogg_page_free (p);
1727     }
1728
1729     GST_LOG_OBJECT (ogg, "flushing last continued packet");
1730     /* flush 1 continued packet in the stream layer */
1731     result = gst_ogg_pad_stream_out (pad, 1);
1732
1733     /* flush all remaining packets, we pushed them in the previous round.
1734      * We don't use _reset() because we still want to get the discont when
1735      * we submit a next page. */
1736     while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
1737   }
1738
1739 done:
1740   /* keep continued pages (only in reverse mode) */
1741   if (continued) {
1742     ogg_page *p = gst_ogg_page_copy (page);
1743
1744     GST_LOG_OBJECT (ogg, "keeping continued page %p", p);
1745     pad->continued = g_list_prepend (pad->continued, p);
1746   }
1747
1748   return result;
1749
1750 choked:
1751   {
1752     GST_WARNING_OBJECT (ogg,
1753         "ogg stream choked on page (serial %08x), "
1754         "resetting stream", pad->map.serialno);
1755     gst_ogg_pad_reset (pad);
1756     /* we continue to recover */
1757     return GST_FLOW_OK;
1758   }
1759 }
1760
1761
1762 static GstOggChain *
1763 gst_ogg_chain_new (GstOggDemux * ogg)
1764 {
1765   GstOggChain *chain = g_slice_new0 (GstOggChain);
1766
1767   GST_DEBUG_OBJECT (ogg, "creating new chain %p", chain);
1768   chain->ogg = ogg;
1769   chain->offset = -1;
1770   chain->bytes = -1;
1771   chain->have_bos = FALSE;
1772   chain->streams = g_array_new (FALSE, TRUE, sizeof (GstOggPad *));
1773   chain->begin_time = GST_CLOCK_TIME_NONE;
1774   chain->segment_start = GST_CLOCK_TIME_NONE;
1775   chain->segment_stop = GST_CLOCK_TIME_NONE;
1776   chain->total_time = GST_CLOCK_TIME_NONE;
1777
1778   return chain;
1779 }
1780
1781 static void
1782 gst_ogg_chain_free (GstOggChain * chain)
1783 {
1784   gint i;
1785
1786   for (i = 0; i < chain->streams->len; i++) {
1787     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1788
1789     gst_object_unref (pad);
1790   }
1791   g_array_free (chain->streams, TRUE);
1792   g_slice_free (GstOggChain, chain);
1793 }
1794
1795 static void
1796 gst_ogg_pad_mark_discont (GstOggPad * pad)
1797 {
1798   pad->discont = TRUE;
1799   pad->map.last_size = 0;
1800 }
1801
1802 static void
1803 gst_ogg_chain_mark_discont (GstOggChain * chain)
1804 {
1805   gint i;
1806
1807   for (i = 0; i < chain->streams->len; i++) {
1808     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1809
1810     gst_ogg_pad_mark_discont (pad);
1811   }
1812 }
1813
1814 static void
1815 gst_ogg_chain_reset (GstOggChain * chain)
1816 {
1817   gint i;
1818
1819   for (i = 0; i < chain->streams->len; i++) {
1820     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1821
1822     gst_ogg_pad_reset (pad);
1823   }
1824 }
1825
1826 static GstOggPad *
1827 gst_ogg_chain_new_stream (GstOggChain * chain, guint32 serialno)
1828 {
1829   GstOggPad *ret;
1830   GstTagList *list;
1831   gchar *name;
1832
1833   GST_DEBUG_OBJECT (chain->ogg,
1834       "creating new stream %08x in chain %p", serialno, chain);
1835
1836   name = g_strdup_printf ("src_%08x", serialno);
1837   ret = g_object_new (GST_TYPE_OGG_PAD, "name", name, NULL);
1838   g_free (name);
1839   /* we own this one */
1840   gst_object_ref_sink (ret);
1841
1842   GST_PAD_DIRECTION (ret) = GST_PAD_SRC;
1843   gst_ogg_pad_mark_discont (ret);
1844
1845   ret->chain = chain;
1846   ret->ogg = chain->ogg;
1847
1848   ret->map.serialno = serialno;
1849   if (ogg_stream_init (&ret->map.stream, serialno) != 0)
1850     goto init_failed;
1851
1852   /* FIXME: either do something with it or remove it */
1853   list = gst_tag_list_new_empty ();
1854   gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_SERIAL, serialno,
1855       NULL);
1856   gst_tag_list_free (list);
1857
1858   GST_DEBUG_OBJECT (chain->ogg,
1859       "created new ogg src %p for stream with serial %08x", ret, serialno);
1860
1861   g_array_append_val (chain->streams, ret);
1862   gst_pad_set_active (GST_PAD_CAST (ret), TRUE);
1863
1864   return ret;
1865
1866   /* ERRORS */
1867 init_failed:
1868   {
1869     GST_ERROR ("Could not initialize ogg_stream struct for serial %08x",
1870         serialno);
1871     gst_object_unref (ret);
1872     return NULL;
1873   }
1874 }
1875
1876 static GstOggPad *
1877 gst_ogg_chain_get_stream (GstOggChain * chain, guint32 serialno)
1878 {
1879   gint i;
1880
1881   for (i = 0; i < chain->streams->len; i++) {
1882     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
1883
1884     if (pad->map.serialno == serialno)
1885       return pad;
1886   }
1887   return NULL;
1888 }
1889
1890 static gboolean
1891 gst_ogg_chain_has_stream (GstOggChain * chain, guint32 serialno)
1892 {
1893   return gst_ogg_chain_get_stream (chain, serialno) != NULL;
1894 }
1895
1896 /* signals and args */
1897 enum
1898 {
1899   /* FILL ME */
1900   LAST_SIGNAL
1901 };
1902
1903 enum
1904 {
1905   ARG_0
1906       /* FILL ME */
1907 };
1908
1909 static GstStaticPadTemplate ogg_demux_src_template_factory =
1910 GST_STATIC_PAD_TEMPLATE ("src_%08x",
1911     GST_PAD_SRC,
1912     GST_PAD_SOMETIMES,
1913     GST_STATIC_CAPS_ANY);
1914
1915 static GstStaticPadTemplate ogg_demux_sink_template_factory =
1916     GST_STATIC_PAD_TEMPLATE ("sink",
1917     GST_PAD_SINK,
1918     GST_PAD_ALWAYS,
1919     GST_STATIC_CAPS ("application/ogg; application/x-annodex")
1920     );
1921
1922 static void gst_ogg_demux_finalize (GObject * object);
1923
1924 static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
1925     GstOggChain ** chain);
1926 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
1927     GstOggChain * chain);
1928
1929 static gboolean gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent,
1930     GstEvent * event);
1931 static void gst_ogg_demux_loop (GstOggPad * pad);
1932 static GstFlowReturn gst_ogg_demux_chain (GstPad * pad, GstObject * parent,
1933     GstBuffer * buffer);
1934 static gboolean gst_ogg_demux_sink_activate (GstPad * sinkpad,
1935     GstObject * parent);
1936 static gboolean gst_ogg_demux_sink_activate_mode (GstPad * sinkpad,
1937     GstObject * parent, GstPadMode mode, gboolean active);
1938 static GstStateChangeReturn gst_ogg_demux_change_state (GstElement * element,
1939     GstStateChange transition);
1940
1941 static void gst_ogg_print (GstOggDemux * demux);
1942
1943 #define gst_ogg_demux_parent_class parent_class
1944 G_DEFINE_TYPE (GstOggDemux, gst_ogg_demux, GST_TYPE_ELEMENT);
1945
1946 static void
1947 gst_ogg_demux_class_init (GstOggDemuxClass * klass)
1948 {
1949   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
1950   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1951
1952   gst_element_class_set_details_simple (gstelement_class,
1953       "Ogg demuxer", "Codec/Demuxer",
1954       "demux ogg streams (info about ogg: http://xiph.org)",
1955       "Wim Taymans <wim@fluendo.com>");
1956
1957   gst_element_class_add_pad_template (gstelement_class,
1958       gst_static_pad_template_get (&ogg_demux_sink_template_factory));
1959   gst_element_class_add_pad_template (gstelement_class,
1960       gst_static_pad_template_get (&ogg_demux_src_template_factory));
1961
1962   gstelement_class->change_state = gst_ogg_demux_change_state;
1963   gstelement_class->send_event = gst_ogg_demux_receive_event;
1964
1965   gobject_class->finalize = gst_ogg_demux_finalize;
1966 }
1967
1968 static void
1969 gst_ogg_demux_init (GstOggDemux * ogg)
1970 {
1971   /* create the sink pad */
1972   ogg->sinkpad =
1973       gst_pad_new_from_static_template (&ogg_demux_sink_template_factory,
1974       "sink");
1975
1976   gst_pad_set_event_function (ogg->sinkpad, gst_ogg_demux_sink_event);
1977   gst_pad_set_chain_function (ogg->sinkpad, gst_ogg_demux_chain);
1978   gst_pad_set_activate_function (ogg->sinkpad, gst_ogg_demux_sink_activate);
1979   gst_pad_set_activatemode_function (ogg->sinkpad,
1980       gst_ogg_demux_sink_activate_mode);
1981   gst_element_add_pad (GST_ELEMENT (ogg), ogg->sinkpad);
1982
1983   ogg->chain_lock = g_mutex_new ();
1984   ogg->push_lock = g_mutex_new ();
1985   ogg->chains = g_array_new (FALSE, TRUE, sizeof (GstOggChain *));
1986
1987   ogg->stats_nbisections = 0;
1988   ogg->stats_bisection_steps[0] = 0;
1989   ogg->stats_bisection_steps[1] = 0;
1990   ogg->stats_bisection_max_steps[0] = 0;
1991   ogg->stats_bisection_max_steps[1] = 0;
1992
1993   ogg->newsegment = NULL;
1994 }
1995
1996 static void
1997 gst_ogg_demux_finalize (GObject * object)
1998 {
1999   GstOggDemux *ogg;
2000
2001   ogg = GST_OGG_DEMUX (object);
2002
2003   g_array_free (ogg->chains, TRUE);
2004   g_mutex_free (ogg->chain_lock);
2005   g_mutex_free (ogg->push_lock);
2006   ogg_sync_clear (&ogg->sync);
2007
2008   if (ogg->newsegment)
2009     gst_event_unref (ogg->newsegment);
2010
2011   G_OBJECT_CLASS (parent_class)->finalize (object);
2012 }
2013
2014 static void
2015 gst_ogg_demux_reset_streams (GstOggDemux * ogg)
2016 {
2017   GstOggChain *chain;
2018   guint i;
2019
2020   chain = ogg->current_chain;
2021   if (chain == NULL)
2022     return;
2023
2024   for (i = 0; i < chain->streams->len; i++) {
2025     GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2026
2027     stream->start_time = -1;
2028     stream->map.accumulated_granule = 0;
2029   }
2030   ogg->building_chain = chain;
2031   GST_DEBUG_OBJECT (ogg, "Resetting current chain");
2032   ogg->current_chain = NULL;
2033   ogg->resync = TRUE;
2034 }
2035
2036 static gboolean
2037 gst_ogg_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
2038 {
2039   gboolean res;
2040   GstOggDemux *ogg;
2041
2042   ogg = GST_OGG_DEMUX (parent);
2043
2044   switch (GST_EVENT_TYPE (event)) {
2045     case GST_EVENT_FLUSH_START:
2046       res = gst_ogg_demux_send_event (ogg, event);
2047       break;
2048     case GST_EVENT_FLUSH_STOP:
2049       GST_DEBUG_OBJECT (ogg, "got a flush stop event");
2050       ogg_sync_reset (&ogg->sync);
2051       res = gst_ogg_demux_send_event (ogg, event);
2052       if (ogg->pullmode || ogg->push_state != PUSH_DURATION) {
2053         /* it's starting to feel reaaaally dirty :(
2054            if we're on a spliced seek to get duration, don't reset streams,
2055            we'll need them for the delayed seek */
2056         gst_ogg_demux_reset_streams (ogg);
2057       }
2058       break;
2059     case GST_EVENT_SEGMENT:
2060       GST_DEBUG_OBJECT (ogg, "got a new segment event");
2061       {
2062         GstSegment segment;
2063
2064         gst_event_copy_segment (event, &segment);
2065
2066         if (segment.format == GST_FORMAT_BYTES) {
2067           GST_PUSH_LOCK (ogg);
2068           ogg->push_byte_offset = segment.start;
2069           ogg->push_last_seek_offset = segment.start;
2070           GST_PUSH_UNLOCK (ogg);
2071         } else {
2072           GST_WARNING_OBJECT (ogg, "unexpected segment format: %s",
2073               gst_format_get_name (segment.format));
2074         }
2075       }
2076       gst_event_unref (event);
2077       res = TRUE;
2078       break;
2079     case GST_EVENT_EOS:
2080     {
2081       GST_DEBUG_OBJECT (ogg, "got an EOS event");
2082 #if 0
2083       /* This would be what is needed (recover from EOS by going on to
2084          the next step (issue the delayed seek)), but it does not work
2085          if there is a queue2 upstream - see more details comment in
2086          gst_ogg_pad_submit_page.
2087          If I could find a way to bypass queue2 behavior, this should
2088          be enabled. */
2089       GST_PUSH_LOCK (ogg);
2090       if (ogg->push_state == PUSH_DURATION) {
2091         GST_DEBUG_OBJECT (ogg, "Got EOS while determining length");
2092         res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
2093         if (res != GST_FLOW_OK) {
2094           GST_DEBUG_OBJECT (ogg, "Error seeking back after duration check: %d",
2095               res);
2096         }
2097         break;
2098       }
2099       GST_PUSH_UNLOCK (ogg);
2100 #endif
2101       res = gst_ogg_demux_send_event (ogg, event);
2102       if (ogg->current_chain == NULL) {
2103         GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
2104             ("can't get first chain"));
2105       }
2106       break;
2107     }
2108     default:
2109       res = gst_ogg_demux_send_event (ogg, event);
2110       break;
2111   }
2112
2113   return res;
2114 }
2115
2116 /* submit the given buffer to the ogg sync */
2117 static GstFlowReturn
2118 gst_ogg_demux_submit_buffer (GstOggDemux * ogg, GstBuffer * buffer)
2119 {
2120   gsize size;
2121   gchar *oggbuffer;
2122   GstFlowReturn ret = GST_FLOW_OK;
2123
2124   size = gst_buffer_get_size (buffer);
2125   GST_DEBUG_OBJECT (ogg, "submitting %" G_GSIZE_FORMAT " bytes", size);
2126   if (G_UNLIKELY (size == 0))
2127     goto done;
2128
2129   oggbuffer = ogg_sync_buffer (&ogg->sync, size);
2130   if (G_UNLIKELY (oggbuffer == NULL))
2131     goto no_buffer;
2132
2133   gst_buffer_extract (buffer, 0, oggbuffer, size);
2134
2135   if (G_UNLIKELY (ogg_sync_wrote (&ogg->sync, size) < 0))
2136     goto write_failed;
2137
2138   if (!ogg->pullmode) {
2139     GST_PUSH_LOCK (ogg);
2140     ogg->push_byte_offset += size;
2141     GST_PUSH_UNLOCK (ogg);
2142   }
2143
2144 done:
2145   gst_buffer_unref (buffer);
2146
2147   return ret;
2148
2149   /* ERRORS */
2150 no_buffer:
2151   {
2152     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
2153         (NULL), ("failed to get ogg sync buffer"));
2154     ret = GST_FLOW_ERROR;
2155     goto done;
2156   }
2157 write_failed:
2158   {
2159     GST_ELEMENT_ERROR (ogg, STREAM, DECODE, (NULL),
2160         ("failed to write %" G_GSIZE_FORMAT " bytes to the sync buffer", size));
2161     ret = GST_FLOW_ERROR;
2162     goto done;
2163   }
2164 }
2165
2166 /* in random access mode this code updates the current read position
2167  * and resets the ogg sync buffer so that the next read will happen
2168  * from this new location.
2169  */
2170 static void
2171 gst_ogg_demux_seek (GstOggDemux * ogg, gint64 offset)
2172 {
2173   GST_LOG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, offset);
2174
2175   ogg->offset = offset;
2176   ogg->read_offset = offset;
2177   ogg_sync_reset (&ogg->sync);
2178 }
2179
2180 /* read more data from the current offset and submit to
2181  * the ogg sync layer.
2182  */
2183 static GstFlowReturn
2184 gst_ogg_demux_get_data (GstOggDemux * ogg, gint64 end_offset)
2185 {
2186   GstFlowReturn ret;
2187   GstBuffer *buffer;
2188
2189   GST_LOG_OBJECT (ogg,
2190       "get data %" G_GINT64_FORMAT " %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
2191       ogg->read_offset, ogg->length, end_offset);
2192
2193   if (end_offset > 0 && ogg->read_offset >= end_offset)
2194     goto boundary_reached;
2195
2196   if (ogg->read_offset == ogg->length)
2197     goto eos;
2198
2199   ret = gst_pad_pull_range (ogg->sinkpad, ogg->read_offset, CHUNKSIZE, &buffer);
2200   if (ret != GST_FLOW_OK)
2201     goto error;
2202
2203   ogg->read_offset += gst_buffer_get_size (buffer);
2204
2205   ret = gst_ogg_demux_submit_buffer (ogg, buffer);
2206
2207   return ret;
2208
2209   /* ERROR */
2210 boundary_reached:
2211   {
2212     GST_LOG_OBJECT (ogg, "reached boundary");
2213     return GST_FLOW_LIMIT;
2214   }
2215 eos:
2216   {
2217     GST_LOG_OBJECT (ogg, "reached EOS");
2218     return GST_FLOW_EOS;
2219   }
2220 error:
2221   {
2222     GST_WARNING_OBJECT (ogg, "got %d (%s) from pull range", ret,
2223         gst_flow_get_name (ret));
2224     return ret;
2225   }
2226 }
2227
2228 /* Read the next page from the current offset.
2229  * boundary: number of bytes ahead we allow looking for;
2230  * -1 if no boundary
2231  *
2232  * @offset will contain the offset the next page starts at when this function
2233  * returns GST_FLOW_OK.
2234  *
2235  * GST_FLOW_EOS is returned on EOS.
2236  *
2237  * GST_FLOW_LIMIT is returned when we did not find a page before the
2238  * boundary. If @boundary is -1, this is never returned.
2239  *
2240  * Any other error returned while retrieving data from the peer is returned as
2241  * is.
2242  */
2243 static GstFlowReturn
2244 gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og,
2245     gint64 boundary, gint64 * offset)
2246 {
2247   gint64 end_offset = -1;
2248   GstFlowReturn ret;
2249
2250   GST_LOG_OBJECT (ogg,
2251       "get next page, current offset %" G_GINT64_FORMAT ", bytes boundary %"
2252       G_GINT64_FORMAT, ogg->offset, boundary);
2253
2254   if (boundary >= 0)
2255     end_offset = ogg->offset + boundary;
2256
2257   while (TRUE) {
2258     glong more;
2259
2260     if (end_offset > 0 && ogg->offset >= end_offset)
2261       goto boundary_reached;
2262
2263     more = ogg_sync_pageseek (&ogg->sync, og);
2264
2265     GST_LOG_OBJECT (ogg, "pageseek gave %ld", more);
2266
2267     if (more < 0) {
2268       /* skipped n bytes */
2269       ogg->offset -= more;
2270       GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT,
2271           more, ogg->offset);
2272     } else if (more == 0) {
2273       /* we need more data */
2274       if (boundary == 0)
2275         goto boundary_reached;
2276
2277       GST_LOG_OBJECT (ogg, "need more data");
2278       ret = gst_ogg_demux_get_data (ogg, end_offset);
2279       if (ret != GST_FLOW_OK)
2280         break;
2281     } else {
2282       gint64 res_offset = ogg->offset;
2283
2284       /* got a page.  Return the offset at the page beginning,
2285          advance the internal offset past the page end */
2286       if (offset)
2287         *offset = res_offset;
2288       ret = GST_FLOW_OK;
2289
2290       ogg->offset += more;
2291
2292       GST_LOG_OBJECT (ogg,
2293           "got page at %" G_GINT64_FORMAT ", serial %08x, end at %"
2294           G_GINT64_FORMAT ", granule %" G_GINT64_FORMAT, res_offset,
2295           ogg_page_serialno (og), ogg->offset,
2296           (gint64) ogg_page_granulepos (og));
2297       break;
2298     }
2299   }
2300   GST_LOG_OBJECT (ogg, "returning %d", ret);
2301
2302   return ret;
2303
2304   /* ERRORS */
2305 boundary_reached:
2306   {
2307     GST_LOG_OBJECT (ogg,
2308         "offset %" G_GINT64_FORMAT " >= end_offset %" G_GINT64_FORMAT,
2309         ogg->offset, end_offset);
2310     return GST_FLOW_LIMIT;
2311   }
2312 }
2313
2314 /* from the current offset, find the previous page, seeking backwards
2315  * until we find the page. 
2316  */
2317 static GstFlowReturn
2318 gst_ogg_demux_get_prev_page (GstOggDemux * ogg, ogg_page * og, gint64 * offset)
2319 {
2320   GstFlowReturn ret;
2321   gint64 begin = ogg->offset;
2322   gint64 end = begin;
2323   gint64 cur_offset = -1;
2324
2325   GST_LOG_OBJECT (ogg, "getting page before %" G_GINT64_FORMAT, begin);
2326
2327   while (cur_offset == -1) {
2328     begin -= CHUNKSIZE;
2329     if (begin < 0)
2330       begin = 0;
2331
2332     /* seek CHUNKSIZE back */
2333     gst_ogg_demux_seek (ogg, begin);
2334
2335     /* now continue reading until we run out of data, if we find a page
2336      * start, we save it. It might not be the final page as there could be
2337      * another page after this one. */
2338     while (ogg->offset < end) {
2339       gint64 new_offset;
2340
2341       ret =
2342           gst_ogg_demux_get_next_page (ogg, og, end - ogg->offset, &new_offset);
2343       /* we hit the upper limit, offset contains the last page start */
2344       if (ret == GST_FLOW_LIMIT) {
2345         GST_LOG_OBJECT (ogg, "hit limit");
2346         break;
2347       }
2348       /* something went wrong */
2349       if (ret == GST_FLOW_EOS) {
2350         new_offset = 0;
2351         GST_LOG_OBJECT (ogg, "got unexpected");
2352         /* We hit EOS. */
2353         goto beach;
2354       } else if (ret != GST_FLOW_OK) {
2355         GST_LOG_OBJECT (ogg, "got error %d", ret);
2356         return ret;
2357       }
2358
2359       GST_LOG_OBJECT (ogg, "found page at %" G_GINT64_FORMAT, new_offset);
2360
2361       /* offset is next page start */
2362       cur_offset = new_offset;
2363     }
2364   }
2365
2366   GST_LOG_OBJECT (ogg, "found previous page at %" G_GINT64_FORMAT, cur_offset);
2367
2368   /* we have the offset.  Actually snork and hold the page now */
2369   gst_ogg_demux_seek (ogg, cur_offset);
2370   ret = gst_ogg_demux_get_next_page (ogg, og, -1, NULL);
2371   if (ret != GST_FLOW_OK) {
2372     GST_WARNING_OBJECT (ogg, "can't get last page at %" G_GINT64_FORMAT,
2373         cur_offset);
2374     /* this shouldn't be possible */
2375     return ret;
2376   }
2377
2378   if (offset)
2379     *offset = cur_offset;
2380
2381 beach:
2382   return ret;
2383 }
2384
2385 static gboolean
2386 gst_ogg_demux_deactivate_current_chain (GstOggDemux * ogg)
2387 {
2388   gint i;
2389   GstOggChain *chain = ogg->current_chain;
2390
2391   if (chain == NULL)
2392     return TRUE;
2393
2394   GST_DEBUG_OBJECT (ogg, "deactivating chain %p", chain);
2395
2396   /* send EOS on all the pads */
2397   for (i = 0; i < chain->streams->len; i++) {
2398     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2399     GstEvent *event;
2400
2401     if (!pad->added)
2402       continue;
2403
2404     event = gst_event_new_eos ();
2405     gst_event_set_seqnum (event, ogg->seqnum);
2406     gst_pad_push_event (GST_PAD_CAST (pad), event);
2407
2408     GST_DEBUG_OBJECT (ogg, "removing pad %" GST_PTR_FORMAT, pad);
2409
2410     /* deactivate first */
2411     gst_pad_set_active (GST_PAD_CAST (pad), FALSE);
2412
2413     gst_element_remove_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2414
2415     pad->added = FALSE;
2416   }
2417
2418   /* if we cannot seek back to the chain, we can destroy the chain 
2419    * completely */
2420   if (!ogg->pullmode) {
2421     gst_ogg_chain_free (chain);
2422   }
2423
2424   return TRUE;
2425 }
2426
2427 GstCaps *
2428 gst_ogg_demux_set_header_on_caps (GstOggDemux * ogg, GstCaps * caps,
2429     GList * headers)
2430 {
2431   GstStructure *structure;
2432   GValue array = { 0 };
2433
2434   GST_LOG_OBJECT (ogg, "caps: %" GST_PTR_FORMAT, caps);
2435
2436   if (G_UNLIKELY (!caps))
2437     return NULL;
2438   if (G_UNLIKELY (!headers))
2439     return NULL;
2440
2441   caps = gst_caps_make_writable (caps);
2442   structure = gst_caps_get_structure (caps, 0);
2443
2444   g_value_init (&array, GST_TYPE_ARRAY);
2445
2446   while (headers) {
2447     GValue value = { 0 };
2448     GstBuffer *buffer;
2449     ogg_packet *op = headers->data;
2450     g_assert (op);
2451     buffer = gst_buffer_new_and_alloc (op->bytes);
2452     if (op->bytes)
2453       gst_buffer_fill (buffer, 0, op->packet, op->bytes);
2454     GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS);
2455     g_value_init (&value, GST_TYPE_BUFFER);
2456     gst_value_take_buffer (&value, buffer);
2457     gst_value_array_append_value (&array, &value);
2458     g_value_unset (&value);
2459     headers = headers->next;
2460   }
2461
2462   gst_structure_set_value (structure, "streamheader", &array);
2463   g_value_unset (&array);
2464   GST_LOG_OBJECT (ogg, "here are the newly set caps: %" GST_PTR_FORMAT, caps);
2465
2466   return caps;
2467 }
2468
2469 static void
2470 gst_ogg_demux_push_queued_buffers (GstOggDemux * ogg, GstOggPad * pad)
2471 {
2472   GList *walk;
2473
2474   /* push queued packets */
2475   for (walk = pad->map.queued; walk; walk = g_list_next (walk)) {
2476     ogg_packet *p = walk->data;
2477
2478     gst_ogg_demux_chain_peer (pad, p, TRUE);
2479     _ogg_packet_free (p);
2480   }
2481   /* and free the queued buffers */
2482   g_list_free (pad->map.queued);
2483   pad->map.queued = NULL;
2484 }
2485
2486 static gboolean
2487 gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain,
2488     GstEvent * event)
2489 {
2490   gint i;
2491   gint bitrate, idx_bitrate;
2492
2493   g_return_val_if_fail (chain != NULL, FALSE);
2494
2495   if (chain == ogg->current_chain) {
2496     if (event)
2497       gst_event_unref (event);
2498
2499     for (i = 0; i < chain->streams->len; i++) {
2500       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2501       gst_ogg_demux_push_queued_buffers (ogg, pad);
2502     }
2503     return TRUE;
2504   }
2505
2506
2507   GST_DEBUG_OBJECT (ogg, "activating chain %p", chain);
2508
2509   bitrate = idx_bitrate = 0;
2510
2511   /* first add the pads */
2512   for (i = 0; i < chain->streams->len; i++) {
2513     GstOggPad *pad;
2514
2515     pad = g_array_index (chain->streams, GstOggPad *, i);
2516
2517     if (pad->map.idx_bitrate)
2518       idx_bitrate = MAX (idx_bitrate, pad->map.idx_bitrate);
2519
2520     bitrate += pad->map.bitrate;
2521
2522     /* mark discont */
2523     gst_ogg_pad_mark_discont (pad);
2524     pad->last_ret = GST_FLOW_OK;
2525
2526     if (pad->map.is_skeleton || pad->added
2527         || !gst_pad_has_current_caps (GST_PAD_CAST (pad)))
2528       continue;
2529
2530     GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
2531
2532     /* activate first */
2533     gst_pad_set_active (GST_PAD_CAST (pad), TRUE);
2534
2535     gst_element_add_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad));
2536     pad->added = TRUE;
2537   }
2538   /* prefer the index bitrate over the ones encoded in the streams */
2539   ogg->bitrate = (idx_bitrate ? idx_bitrate : bitrate);
2540
2541   /* after adding the new pads, remove the old pads */
2542   gst_ogg_demux_deactivate_current_chain (ogg);
2543
2544   GST_DEBUG_OBJECT (ogg, "Setting current chain to %p", chain);
2545   ogg->current_chain = chain;
2546
2547   /* we are finished now */
2548   gst_element_no_more_pads (GST_ELEMENT (ogg));
2549
2550   /* FIXME, must be sent from the streaming thread */
2551   if (event) {
2552     GstTagList *tags;
2553
2554     gst_ogg_demux_send_event (ogg, event);
2555
2556     tags = gst_tag_list_new (GST_TAG_CONTAINER_FORMAT, "Ogg", NULL);
2557     gst_ogg_demux_send_event (ogg, gst_event_new_tag (tags));
2558   }
2559
2560   GST_DEBUG_OBJECT (ogg, "starting chain");
2561
2562   /* then send out any headers and queued packets */
2563   for (i = 0; i < chain->streams->len; i++) {
2564     GList *walk;
2565     GstOggPad *pad;
2566
2567     pad = g_array_index (chain->streams, GstOggPad *, i);
2568
2569     /* FIXME also streaming thread */
2570     if (pad->map.taglist) {
2571       GST_DEBUG_OBJECT (ogg, "pushing tags");
2572       gst_pad_push_event (GST_PAD_CAST (pad),
2573           gst_event_new_tag (pad->map.taglist));
2574       pad->map.taglist = NULL;
2575     }
2576
2577     /* Set headers on caps */
2578     pad->map.caps =
2579         gst_ogg_demux_set_header_on_caps (ogg, pad->map.caps, pad->map.headers);
2580     gst_pad_set_caps (GST_PAD_CAST (pad), pad->map.caps);
2581
2582     GST_DEBUG_OBJECT (ogg, "pushing headers");
2583     /* push headers */
2584     for (walk = pad->map.headers; walk; walk = g_list_next (walk)) {
2585       ogg_packet *p = walk->data;
2586
2587       gst_ogg_demux_chain_peer (pad, p, TRUE);
2588     }
2589
2590     GST_DEBUG_OBJECT (ogg, "pushing queued buffers");
2591     gst_ogg_demux_push_queued_buffers (ogg, pad);
2592   }
2593   return TRUE;
2594 }
2595
2596 static gboolean
2597 do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2598     gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2599     gint64 * offset)
2600 {
2601   gint64 best;
2602   GstFlowReturn ret;
2603   gint64 result = 0;
2604
2605   best = begin;
2606
2607   GST_DEBUG_OBJECT (ogg,
2608       "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT,
2609       begin, end);
2610   GST_DEBUG_OBJECT (ogg,
2611       "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT,
2612       GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime));
2613   GST_DEBUG_OBJECT (ogg, "target %" GST_TIME_FORMAT, GST_TIME_ARGS (target));
2614
2615   /* perform the seek */
2616   while (begin < end) {
2617     gint64 bisect;
2618
2619     if ((end - begin < CHUNKSIZE) || (endtime == begintime)) {
2620       bisect = begin;
2621     } else {
2622       /* take a (pretty decent) guess, avoiding overflow */
2623       gint64 rate = (end - begin) * GST_MSECOND / (endtime - begintime);
2624
2625       bisect = (target - begintime) / GST_MSECOND * rate + begin - CHUNKSIZE;
2626
2627       if (bisect <= begin)
2628         bisect = begin;
2629       GST_DEBUG_OBJECT (ogg, "Initial guess: %" G_GINT64_FORMAT, bisect);
2630     }
2631     gst_ogg_demux_seek (ogg, bisect);
2632
2633     while (begin < end) {
2634       ogg_page og;
2635
2636       GST_DEBUG_OBJECT (ogg,
2637           "after seek, bisect %" G_GINT64_FORMAT ", begin %" G_GINT64_FORMAT
2638           ", end %" G_GINT64_FORMAT, bisect, begin, end);
2639
2640       ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2641       GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2642           result);
2643
2644       if (ret == GST_FLOW_LIMIT) {
2645         /* we hit the upper limit, go back a bit */
2646         if (bisect <= begin + 1) {
2647           end = begin;          /* found it */
2648         } else {
2649           if (bisect == 0)
2650             goto seek_error;
2651
2652           bisect -= CHUNKSIZE;
2653           if (bisect <= begin)
2654             bisect = begin + 1;
2655
2656           gst_ogg_demux_seek (ogg, bisect);
2657         }
2658       } else if (ret == GST_FLOW_OK) {
2659         /* found offset of next ogg page */
2660         gint64 granulepos;
2661         GstClockTime granuletime;
2662         GstOggPad *pad;
2663
2664         /* get the granulepos */
2665         GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
2666             result);
2667         granulepos = ogg_page_granulepos (&og);
2668         if (granulepos == -1) {
2669           GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2670           continue;
2671         }
2672
2673         /* get the stream */
2674         pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2675         if (pad == NULL || pad->map.is_skeleton)
2676           continue;
2677
2678         /* convert granulepos to time */
2679         granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2680             granulepos);
2681         if (granuletime < pad->start_time)
2682           continue;
2683
2684         GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
2685             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2686
2687         granuletime -= pad->start_time;
2688         granuletime += chain->begin_time;
2689
2690         GST_DEBUG_OBJECT (ogg,
2691             "found page with granule %" G_GINT64_FORMAT " and time %"
2692             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
2693
2694         if (granuletime < target) {
2695           best = result;        /* raw offset of packet with granulepos */
2696           begin = ogg->offset;  /* raw offset of next page */
2697           begintime = granuletime;
2698
2699           bisect = begin;       /* *not* begin + 1 */
2700         } else {
2701           if (bisect <= begin + 1) {
2702             end = begin;        /* found it */
2703           } else {
2704             if (end == ogg->offset) {   /* we're pretty close - we'd be stuck in */
2705               end = result;
2706               bisect -= CHUNKSIZE;      /* an endless loop otherwise. */
2707               if (bisect <= begin)
2708                 bisect = begin + 1;
2709               gst_ogg_demux_seek (ogg, bisect);
2710             } else {
2711               end = result;
2712               endtime = granuletime;
2713               break;
2714             }
2715           }
2716         }
2717       } else
2718         goto seek_error;
2719     }
2720   }
2721   GST_DEBUG_OBJECT (ogg, "seeking to %" G_GINT64_FORMAT, best);
2722   gst_ogg_demux_seek (ogg, best);
2723   *offset = best;
2724
2725   return TRUE;
2726
2727   /* ERRORS */
2728 seek_error:
2729   {
2730     GST_DEBUG_OBJECT (ogg, "got a seek error");
2731     return FALSE;
2732   }
2733 }
2734
2735 static gboolean
2736 do_index_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin,
2737     gint64 end, gint64 begintime, gint64 endtime, gint64 target,
2738     gint64 * p_offset, gint64 * p_timestamp)
2739 {
2740   guint i;
2741   guint64 timestamp, offset;
2742   guint64 r_timestamp, r_offset;
2743   gboolean result = FALSE;
2744
2745   target -= begintime;
2746
2747   r_offset = -1;
2748   r_timestamp = -1;
2749
2750   for (i = 0; i < chain->streams->len; i++) {
2751     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
2752
2753     timestamp = target;
2754     if (gst_ogg_map_search_index (&pad->map, TRUE, &timestamp, &offset)) {
2755       GST_INFO ("found %" G_GUINT64_FORMAT " at offset %" G_GUINT64_FORMAT,
2756           timestamp, offset);
2757
2758       if (r_offset == -1 || offset < r_offset) {
2759         r_offset = offset;
2760         r_timestamp = timestamp;
2761       }
2762       result |= TRUE;
2763     }
2764   }
2765
2766   if (p_timestamp)
2767     *p_timestamp = r_timestamp;
2768   if (p_offset)
2769     *p_offset = r_offset;
2770
2771   return result;
2772 }
2773
2774 /*
2775  * do seek to time @position, return FALSE or chain and TRUE
2776  */
2777 static gboolean
2778 gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
2779     gboolean accurate, gboolean keyframe, GstOggChain ** rchain)
2780 {
2781   guint64 position;
2782   GstOggChain *chain = NULL;
2783   gint64 begin, end;
2784   gint64 begintime, endtime;
2785   gint64 target, keytarget;
2786   gint64 best;
2787   gint64 total;
2788   gint64 result = 0;
2789   GstFlowReturn ret;
2790   gint i, pending, len;
2791   gboolean first_parsed_page = TRUE;
2792
2793   position = segment->position;
2794
2795   /* first find the chain to search in */
2796   total = ogg->total_time;
2797   if (ogg->chains->len == 0)
2798     goto no_chains;
2799
2800   for (i = ogg->chains->len - 1; i >= 0; i--) {
2801     chain = g_array_index (ogg->chains, GstOggChain *, i);
2802     total -= chain->total_time;
2803     if (position >= total)
2804       break;
2805   }
2806
2807   /* first step, locate page containing the required data */
2808   begin = chain->offset;
2809   end = chain->end_offset;
2810   begintime = chain->begin_time;
2811   endtime = begintime + chain->total_time;
2812   target = position - total + begintime;
2813
2814   if (!do_binary_search (ogg, chain, begin, end, begintime, endtime, target,
2815           &best))
2816     goto seek_error;
2817
2818   /* second step: find pages for all streams, we use the keyframe_granule to keep
2819    * track of which ones we saw. If we have seen a page for each stream we can
2820    * calculate the positions of each keyframe. */
2821   GST_DEBUG_OBJECT (ogg, "find keyframes");
2822   len = pending = chain->streams->len;
2823
2824   /* figure out where the keyframes are */
2825   keytarget = target;
2826
2827   while (TRUE) {
2828     ogg_page og;
2829     gint64 granulepos;
2830     GstOggPad *pad;
2831     GstClockTime keyframe_time, granule_time;
2832
2833     ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, &result);
2834     GST_LOG_OBJECT (ogg, "looking for next page returned %" G_GINT64_FORMAT,
2835         result);
2836     if (ret == GST_FLOW_LIMIT) {
2837       GST_LOG_OBJECT (ogg, "reached limit");
2838       break;
2839     } else if (ret != GST_FLOW_OK)
2840       goto seek_error;
2841
2842     /* get the stream */
2843     pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
2844     if (pad == NULL)
2845       continue;
2846
2847     if (pad->map.is_skeleton)
2848       goto next;
2849
2850     granulepos = ogg_page_granulepos (&og);
2851     if (granulepos == -1 || granulepos == 0) {
2852       GST_LOG_OBJECT (ogg, "granulepos of next page is -1");
2853       continue;
2854     }
2855
2856     /* we only do this the first time we pass here */
2857     if (first_parsed_page) {
2858       /* Now that we have a time reference from the page, we can check
2859        * whether all streams still have pages from here on.
2860        *
2861        * This would be more elegant before the loop, but getting the page from
2862        * there without breaking anything would be more costly */
2863       granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2864           granulepos);
2865       for (i = 0; i < len; i++) {
2866         GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
2867
2868         if (stream == pad)
2869           /* we already know we have at least one page (the current one)
2870            * for this stream */
2871           continue;
2872
2873         if (granule_time > stream->map.total_time)
2874           /* we won't encounter any more pages of this stream, so we don't
2875            * try finding a key frame for it */
2876           pending--;
2877       }
2878       first_parsed_page = FALSE;
2879     }
2880
2881
2882     /* in reverse we want to go past the page with the lower timestamp */
2883     if (segment->rate < 0.0) {
2884       /* get time for this pad */
2885       granule_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
2886           granulepos);
2887
2888       GST_LOG_OBJECT (ogg,
2889           "looking at page with ts %" GST_TIME_FORMAT ", target %"
2890           GST_TIME_FORMAT, GST_TIME_ARGS (granule_time),
2891           GST_TIME_ARGS (target));
2892       if (granule_time < target)
2893         continue;
2894     }
2895
2896     /* we've seen this pad before */
2897     if (pad->keyframe_granule != -1)
2898       continue;
2899
2900     /* convert granule of this pad to the granule of the keyframe */
2901     pad->keyframe_granule =
2902         gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos);
2903     GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT,
2904         pad->keyframe_granule);
2905
2906     /* get time of the keyframe */
2907     keyframe_time =
2908         gst_ogg_stream_granule_to_time (&pad->map, pad->keyframe_granule);
2909     GST_LOG_OBJECT (ogg,
2910         "stream %08x granule time %" GST_TIME_FORMAT,
2911         pad->map.serialno, GST_TIME_ARGS (keyframe_time));
2912
2913     /* collect smallest value */
2914     if (keyframe_time != -1) {
2915       keyframe_time += begintime;
2916       if (keyframe_time < keytarget)
2917         keytarget = keyframe_time;
2918     }
2919
2920   next:
2921     pending--;
2922     if (pending == 0)
2923       break;
2924   }
2925
2926   /* for negative rates we will get to the keyframe backwards */
2927   if (segment->rate < 0.0)
2928     goto done;
2929
2930   if (keytarget != target) {
2931     GST_LOG_OBJECT (ogg, "final seek to target %" GST_TIME_FORMAT,
2932         GST_TIME_ARGS (keytarget));
2933
2934     /* last step, seek to the location of the keyframe */
2935     if (!do_binary_search (ogg, chain, begin, end, begintime, endtime,
2936             keytarget, &best))
2937       goto seek_error;
2938   } else {
2939     /* seek back to previous position */
2940     GST_LOG_OBJECT (ogg, "keyframe on target");
2941     gst_ogg_demux_seek (ogg, best);
2942   }
2943
2944 done:
2945   if (keyframe) {
2946     if (segment->rate > 0.0)
2947       segment->time = keytarget;
2948     segment->position = keytarget - begintime;
2949   }
2950
2951   *rchain = chain;
2952
2953   return TRUE;
2954
2955 no_chains:
2956   {
2957     GST_DEBUG_OBJECT (ogg, "no chains");
2958     return FALSE;
2959   }
2960 seek_error:
2961   {
2962     GST_DEBUG_OBJECT (ogg, "got a seek error");
2963     return FALSE;
2964   }
2965 }
2966
2967 /* does not take ownership of the event */
2968 static gboolean
2969 gst_ogg_demux_perform_seek_pull (GstOggDemux * ogg, GstEvent * event)
2970 {
2971   GstOggChain *chain = NULL;
2972   gboolean res;
2973   gboolean flush, accurate, keyframe;
2974   GstFormat format;
2975   gdouble rate;
2976   GstSeekFlags flags;
2977   GstSeekType cur_type, stop_type;
2978   gint64 cur, stop;
2979   gboolean update;
2980   guint32 seqnum;
2981   GstEvent *tevent;
2982
2983   if (event) {
2984     GST_DEBUG_OBJECT (ogg, "seek with event");
2985
2986     gst_event_parse_seek (event, &rate, &format, &flags,
2987         &cur_type, &cur, &stop_type, &stop);
2988
2989     /* we can only seek on time */
2990     if (format != GST_FORMAT_TIME) {
2991       GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
2992       goto error;
2993     }
2994     seqnum = gst_event_get_seqnum (event);
2995   } else {
2996     GST_DEBUG_OBJECT (ogg, "seek without event");
2997
2998     flags = 0;
2999     rate = 1.0;
3000     seqnum = gst_util_seqnum_next ();
3001   }
3002
3003   GST_DEBUG_OBJECT (ogg, "seek, rate %g", rate);
3004
3005   flush = flags & GST_SEEK_FLAG_FLUSH;
3006   accurate = flags & GST_SEEK_FLAG_ACCURATE;
3007   keyframe = flags & GST_SEEK_FLAG_KEY_UNIT;
3008
3009   /* first step is to unlock the streaming thread if it is
3010    * blocked in a chain call, we do this by starting the flush. because
3011    * we cannot yet hold any streaming lock, we have to protect the chains
3012    * with their own lock. */
3013   if (flush) {
3014     gint i;
3015
3016     tevent = gst_event_new_flush_start ();
3017     gst_event_set_seqnum (tevent, seqnum);
3018
3019     gst_event_ref (tevent);
3020     gst_pad_push_event (ogg->sinkpad, tevent);
3021
3022     GST_CHAIN_LOCK (ogg);
3023     for (i = 0; i < ogg->chains->len; i++) {
3024       GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3025       gint j;
3026
3027       for (j = 0; j < chain->streams->len; j++) {
3028         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, j);
3029
3030         gst_event_ref (tevent);
3031         gst_pad_push_event (GST_PAD (pad), tevent);
3032       }
3033     }
3034     GST_CHAIN_UNLOCK (ogg);
3035
3036     gst_event_unref (tevent);
3037   } else {
3038     gst_pad_pause_task (ogg->sinkpad);
3039   }
3040
3041   /* now grab the stream lock so that streaming cannot continue, for
3042    * non flushing seeks when the element is in PAUSED this could block
3043    * forever. */
3044   GST_PAD_STREAM_LOCK (ogg->sinkpad);
3045
3046   if (event) {
3047     gst_segment_do_seek (&ogg->segment, rate, format, flags,
3048         cur_type, cur, stop_type, stop, &update);
3049   }
3050
3051   GST_DEBUG_OBJECT (ogg, "segment positions set to %" GST_TIME_FORMAT "-%"
3052       GST_TIME_FORMAT, GST_TIME_ARGS (ogg->segment.start),
3053       GST_TIME_ARGS (ogg->segment.stop));
3054
3055   /* we need to stop flushing on the srcpad as we're going to use it
3056    * next. We can do this as we have the STREAM lock now. */
3057   if (flush) {
3058     tevent = gst_event_new_flush_stop (TRUE);
3059     gst_event_set_seqnum (tevent, seqnum);
3060     gst_pad_push_event (ogg->sinkpad, tevent);
3061   }
3062
3063   {
3064     gint i;
3065
3066     /* reset all ogg streams now, need to do this from within the lock to
3067      * make sure the streaming thread is not messing with the stream */
3068     for (i = 0; i < ogg->chains->len; i++) {
3069       GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3070
3071       gst_ogg_chain_reset (chain);
3072     }
3073   }
3074
3075   /* for reverse we will already seek accurately */
3076   res = gst_ogg_demux_do_seek (ogg, &ogg->segment, accurate, keyframe, &chain);
3077
3078   /* seek failed, make sure we continue the current chain */
3079   if (!res) {
3080     GST_DEBUG_OBJECT (ogg, "seek failed");
3081     chain = ogg->current_chain;
3082   } else {
3083     GST_DEBUG_OBJECT (ogg, "seek success");
3084   }
3085
3086   if (!chain)
3087     goto no_chain;
3088
3089   /* now we have a new position, prepare for streaming again */
3090   {
3091     GstEvent *event;
3092     gint64 stop;
3093     gint64 start;
3094     gint64 position, begin_time;
3095     GstSegment segment;
3096
3097     /* we have to send the flush to the old chain, not the new one */
3098     if (flush) {
3099       tevent = gst_event_new_flush_stop (TRUE);
3100       gst_event_set_seqnum (tevent, seqnum);
3101       gst_ogg_demux_send_event (ogg, tevent);
3102     }
3103
3104     /* we need this to see how far inside the chain we need to start */
3105     if (chain->begin_time != GST_CLOCK_TIME_NONE)
3106       begin_time = chain->begin_time;
3107     else
3108       begin_time = 0;
3109
3110     /* segment.start gives the start over all chains, we calculate the amount
3111      * of time into this chain we need to start */
3112     start = ogg->segment.start - begin_time;
3113     if (chain->segment_start != GST_CLOCK_TIME_NONE)
3114       start += chain->segment_start;
3115
3116     if ((stop = ogg->segment.stop) == -1)
3117       stop = ogg->segment.duration;
3118
3119     /* segment.stop gives the stop time over all chains, calculate the amount of
3120      * time we need to stop in this chain */
3121     if (stop != -1) {
3122       if (stop > begin_time)
3123         stop -= begin_time;
3124       else
3125         stop = 0;
3126       stop += chain->segment_start;
3127       /* we must stop when this chain ends and switch to the next chain to play
3128        * the remainder of the segment. */
3129       stop = MIN (stop, chain->segment_stop);
3130     }
3131
3132     position = ogg->segment.position;
3133     if (chain->segment_start != GST_CLOCK_TIME_NONE)
3134       position += chain->segment_start;
3135
3136     gst_segment_copy_into (&ogg->segment, &segment);
3137
3138     /* create the segment event we are going to send out */
3139     if (ogg->segment.rate >= 0.0) {
3140       segment.start = position;
3141       segment.stop = stop;
3142     } else {
3143       segment.start = start;
3144       segment.stop = position;
3145     }
3146     event = gst_event_new_segment (&segment);
3147     gst_event_set_seqnum (event, seqnum);
3148
3149     if (chain != ogg->current_chain) {
3150       /* switch to different chain, send segment on new chain */
3151       gst_ogg_demux_activate_chain (ogg, chain, event);
3152     } else {
3153       /* mark discont and send segment on current chain */
3154       gst_ogg_chain_mark_discont (chain);
3155       /* This event should be sent from the streaming thread (sink pad task) */
3156       if (ogg->newsegment)
3157         gst_event_unref (ogg->newsegment);
3158       ogg->newsegment = event;
3159     }
3160
3161     /* notify start of new segment */
3162     if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
3163       GstMessage *message;
3164
3165       message = gst_message_new_segment_start (GST_OBJECT (ogg),
3166           GST_FORMAT_TIME, ogg->segment.position);
3167       gst_message_set_seqnum (message, seqnum);
3168
3169       gst_element_post_message (GST_ELEMENT (ogg), message);
3170     }
3171
3172     ogg->seqnum = seqnum;
3173     /* restart our task since it might have been stopped when we did the 
3174      * flush. */
3175     gst_pad_start_task (ogg->sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
3176         ogg->sinkpad);
3177   }
3178
3179   /* streaming can continue now */
3180   GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3181
3182   return res;
3183
3184   /* ERRORS */
3185 error:
3186   {
3187     GST_DEBUG_OBJECT (ogg, "seek failed");
3188     return FALSE;
3189   }
3190 no_chain:
3191   {
3192     GST_DEBUG_OBJECT (ogg, "no chain to seek in");
3193     GST_PAD_STREAM_UNLOCK (ogg->sinkpad);
3194     return FALSE;
3195   }
3196 }
3197
3198 static gboolean
3199 gst_ogg_demux_get_duration_push (GstOggDemux * ogg, int flags)
3200 {
3201   /* In push mode, we get to the end of the stream to get the duration */
3202   gint64 position;
3203   GstEvent *sevent;
3204   gboolean res;
3205
3206   /* A full Ogg page can be almost 64 KB. There's no guarantee that there'll be a
3207      granpos there, but it's fairly likely */
3208   position =
3209       ogg->push_byte_length - DURATION_CHUNK_OFFSET - EOS_AVOIDANCE_THRESHOLD;
3210   if (position < 0)
3211     position = 0;
3212
3213   GST_DEBUG_OBJECT (ogg,
3214       "Getting duration, seeking near the end, to %" G_GINT64_FORMAT, position);
3215   ogg->push_state = PUSH_DURATION;
3216   /* do not read the last byte */
3217   sevent = gst_event_new_seek (1.0, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET,
3218       position, GST_SEEK_TYPE_SET, ogg->push_byte_length - 1);
3219   res = gst_pad_push_event (ogg->sinkpad, sevent);
3220   if (res) {
3221     GST_DEBUG_OBJECT (ogg, "Seek succesful");
3222     return TRUE;
3223   } else {
3224     GST_INFO_OBJECT (ogg, "Seek failed, duration will stay unknown");
3225     ogg->push_state = PUSH_PLAYING;
3226     ogg->push_disable_seeking = TRUE;
3227     return FALSE;
3228   }
3229 }
3230
3231 static gboolean
3232 gst_ogg_demux_check_duration_push (GstOggDemux * ogg, GstSeekFlags flags,
3233     GstEvent * event)
3234 {
3235   if (ogg->push_byte_length < 0) {
3236     GstPad *peer;
3237
3238     GST_DEBUG_OBJECT (ogg, "Trying to find byte/time length");
3239     if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
3240       gint64 length;
3241       int res;
3242
3243       res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &length);
3244       if (res && length > 0) {
3245         ogg->push_byte_length = length;
3246         GST_DEBUG_OBJECT (ogg,
3247             "File byte length %" G_GINT64_FORMAT, ogg->push_byte_length);
3248       } else {
3249         GST_DEBUG_OBJECT (ogg, "File byte length unknown, assuming live");
3250         ogg->push_disable_seeking = TRUE;
3251         return TRUE;
3252       }
3253       res = gst_pad_query_duration (peer, GST_FORMAT_TIME, &length);
3254       gst_object_unref (peer);
3255       if (res && length >= 0) {
3256         ogg->push_time_length = length;
3257         GST_DEBUG_OBJECT (ogg, "File time length %" GST_TIME_FORMAT,
3258             GST_TIME_ARGS (ogg->push_time_length));
3259       } else if (!ogg->push_disable_seeking) {
3260         gboolean res;
3261
3262         res = gst_ogg_demux_get_duration_push (ogg, flags);
3263         if (res) {
3264           GST_DEBUG_OBJECT (ogg,
3265               "File time length unknown, trying to determine");
3266           ogg->push_mode_seek_delayed_event = NULL;
3267           if (event) {
3268             GST_DEBUG_OBJECT (ogg,
3269                 "Let me intercept this innocent looking seek request");
3270             ogg->push_mode_seek_delayed_event = gst_event_copy (event);
3271           }
3272           return FALSE;
3273         }
3274       }
3275     }
3276   }
3277   return TRUE;
3278 }
3279
3280 static gboolean
3281 gst_ogg_demux_perform_seek_push (GstOggDemux * ogg, GstEvent * event)
3282 {
3283   gint bitrate;
3284   gboolean res = TRUE;
3285   GstFormat format;
3286   gdouble rate;
3287   GstSeekFlags flags;
3288   GstSeekType start_type, stop_type;
3289   gint64 start, stop;
3290   GstEvent *sevent;
3291   GstOggChain *chain;
3292   gint64 best, best_time;
3293   gint i;
3294
3295   GST_DEBUG_OBJECT (ogg, "Push mode seek request received");
3296
3297   gst_event_parse_seek (event, &rate, &format, &flags,
3298       &start_type, &start, &stop_type, &stop);
3299
3300   if (format != GST_FORMAT_TIME) {
3301     GST_DEBUG_OBJECT (ogg, "can only seek on TIME");
3302     goto error;
3303   }
3304
3305   if (start_type != GST_SEEK_TYPE_SET) {
3306     GST_DEBUG_OBJECT (ogg, "can only seek to a SET target");
3307     goto error;
3308   }
3309
3310   if (!(flags & GST_SEEK_FLAG_FLUSH)) {
3311     GST_DEBUG_OBJECT (ogg, "can only do flushing seeks");
3312     goto error;
3313   }
3314
3315   GST_DEBUG_OBJECT (ogg, "Push mode seek request: %" GST_TIME_FORMAT,
3316       GST_TIME_ARGS (start));
3317
3318   chain = ogg->current_chain;
3319   if (!chain) {
3320     GST_WARNING_OBJECT (ogg, "No chain to seek on");
3321     goto error;
3322   }
3323
3324   /* start accessing push_* members */
3325   GST_PUSH_LOCK (ogg);
3326
3327   /* not if we disabled seeking (chained streams) */
3328   if (ogg->push_disable_seeking) {
3329     GST_DEBUG_OBJECT (ogg, "Seeking disabled");
3330     goto error_locked;
3331   }
3332
3333   /* not when we're trying to work out duration */
3334   if (ogg->push_state == PUSH_DURATION) {
3335     GST_DEBUG_OBJECT (ogg, "Busy working out duration, try again later");
3336     goto error_locked;
3337   }
3338
3339   /* actually, not if we're doing any seeking already */
3340   if (ogg->push_state != PUSH_PLAYING) {
3341     GST_DEBUG_OBJECT (ogg, "Already doing some seeking, try again later");
3342     goto error_locked;
3343   }
3344
3345   /* on the first seek, get length if we can */
3346   if (!gst_ogg_demux_check_duration_push (ogg, flags, event)) {
3347     GST_PUSH_UNLOCK (ogg);
3348     return FALSE;
3349   }
3350
3351   if (do_index_search (ogg, chain, 0, -1, 0, -1, start, &best, &best_time)) {
3352     /* the index gave some result */
3353     GST_DEBUG_OBJECT (ogg,
3354         "found offset %" G_GINT64_FORMAT " with time %" G_GUINT64_FORMAT,
3355         best, best_time);
3356   } else {
3357     if (ogg->push_time_length > 0) {
3358       /* if we know the time length, we know the full segment bitrate */
3359       GST_DEBUG_OBJECT (ogg, "Using real file bitrate");
3360       bitrate =
3361           gst_util_uint64_scale (ogg->push_byte_length, 8 * GST_SECOND,
3362           ogg->push_time_length);
3363     } else if (ogg->push_time_offset > 0) {
3364       /* get a first approximation using known bitrate to the current position */
3365       GST_DEBUG_OBJECT (ogg, "Using file bitrate so far");
3366       bitrate =
3367           gst_util_uint64_scale (ogg->push_byte_offset, 8 * GST_SECOND,
3368           ogg->push_time_offset);
3369     } else if (ogg->bitrate > 0) {
3370       /* nominal bitrate is better than nothing, even if it lies often */
3371       GST_DEBUG_OBJECT (ogg, "Using nominal bitrate");
3372       bitrate = ogg->bitrate;
3373     } else {
3374       /* meh */
3375       GST_DEBUG_OBJECT (ogg,
3376           "At stream start, and no nominal bitrate, using some random magic "
3377           "number to seed");
3378       /* the bisection, once started, should give us a better approximation */
3379       bitrate = 1000;
3380     }
3381     best = gst_util_uint64_scale (start, bitrate, 8 * GST_SECOND);
3382   }
3383
3384   /* offset by typical page length, and ensure our best guess is within
3385      reasonable bounds */
3386   best -= CHUNKSIZE;
3387   if (best < 0)
3388     best = 0;
3389   if (ogg->push_byte_length > 0 && best >= ogg->push_byte_length)
3390     best = ogg->push_byte_length - 1;
3391
3392   /* set up bisection search */
3393   ogg->push_offset0 = 0;
3394   ogg->push_offset1 = ogg->push_byte_length - 1;
3395   ogg->push_time0 = ogg->push_start_time;
3396   ogg->push_time1 = ogg->push_time_length;
3397   ogg->push_seek_time_target = start;
3398   ogg->push_prev_seek_time = GST_CLOCK_TIME_NONE;
3399   ogg->push_seek_time_original_target = start;
3400   ogg->push_state = PUSH_BISECT1;
3401   ogg->seek_secant = FALSE;
3402   ogg->seek_undershot = FALSE;
3403
3404   /* reset pad push mode seeking state */
3405   for (i = 0; i < chain->streams->len; i++) {
3406     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3407     pad->push_kf_time = GST_CLOCK_TIME_NONE;
3408     pad->push_sync_time = GST_CLOCK_TIME_NONE;
3409   }
3410
3411   GST_DEBUG_OBJECT (ogg,
3412       "Setting up bisection search for %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT
3413       " (time %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", ogg->push_offset0,
3414       ogg->push_offset1, GST_TIME_ARGS (ogg->push_time0),
3415       GST_TIME_ARGS (ogg->push_time1));
3416   GST_DEBUG_OBJECT (ogg,
3417       "Target time is %" GST_TIME_FORMAT ", best first guess is %"
3418       G_GINT64_FORMAT, GST_TIME_ARGS (ogg->push_seek_time_target), best);
3419
3420   ogg->push_seek_rate = rate;
3421   ogg->push_seek_flags = flags;
3422   ogg->push_mode_seek_delayed_event = NULL;
3423   ogg->push_bisection_steps[0] = 1;
3424   ogg->push_bisection_steps[1] = 0;
3425   sevent = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags,
3426       start_type, best, GST_SEEK_TYPE_NONE, -1);
3427
3428   GST_PUSH_UNLOCK (ogg);
3429   res = gst_pad_push_event (ogg->sinkpad, sevent);
3430
3431   return res;
3432
3433   /* ERRORS */
3434 error:
3435   {
3436     GST_DEBUG_OBJECT (ogg, "seek failed");
3437     return FALSE;
3438   }
3439
3440 error_locked:
3441   GST_PUSH_UNLOCK (ogg);
3442   goto error;
3443 }
3444
3445 static gboolean
3446 gst_ogg_demux_perform_seek (GstOggDemux * ogg, GstEvent * event)
3447 {
3448   gboolean res;
3449
3450   if (ogg->pullmode) {
3451     res = gst_ogg_demux_perform_seek_pull (ogg, event);
3452   } else {
3453     res = gst_ogg_demux_perform_seek_push (ogg, event);
3454   }
3455   return res;
3456 }
3457
3458
3459 /* finds each bitstream link one at a time using a bisection search
3460  * (has to begin by knowing the offset of the lb's initial page).
3461  * Recurses for each link so it can alloc the link storage after
3462  * finding them all, then unroll and fill the cache at the same time
3463  */
3464 static GstFlowReturn
3465 gst_ogg_demux_bisect_forward_serialno (GstOggDemux * ogg,
3466     gint64 begin, gint64 searched, gint64 end, GstOggChain * chain, glong m)
3467 {
3468   gint64 endsearched = end;
3469   gint64 next = end;
3470   ogg_page og;
3471   GstFlowReturn ret;
3472   gint64 offset;
3473   GstOggChain *nextchain;
3474
3475   GST_LOG_OBJECT (ogg,
3476       "bisect begin: %" G_GINT64_FORMAT ", searched: %" G_GINT64_FORMAT
3477       ", end %" G_GINT64_FORMAT ", chain: %p", begin, searched, end, chain);
3478
3479   /* the below guards against garbage seperating the last and
3480    * first pages of two links. */
3481   while (searched < endsearched) {
3482     gint64 bisect;
3483
3484     if (endsearched - searched < CHUNKSIZE) {
3485       bisect = searched;
3486     } else {
3487       bisect = (searched + endsearched) / 2;
3488     }
3489
3490     gst_ogg_demux_seek (ogg, bisect);
3491     ret = gst_ogg_demux_get_next_page (ogg, &og, -1, &offset);
3492
3493     if (ret == GST_FLOW_EOS) {
3494       endsearched = bisect;
3495     } else if (ret == GST_FLOW_OK) {
3496       guint32 serial = ogg_page_serialno (&og);
3497
3498       if (!gst_ogg_chain_has_stream (chain, serial)) {
3499         endsearched = bisect;
3500         next = offset;
3501       } else {
3502         searched = offset + og.header_len + og.body_len;
3503       }
3504     } else
3505       return ret;
3506   }
3507
3508   GST_LOG_OBJECT (ogg, "current chain ends at %" G_GINT64_FORMAT, searched);
3509
3510   chain->end_offset = searched;
3511   ret = gst_ogg_demux_read_end_chain (ogg, chain);
3512   if (ret != GST_FLOW_OK)
3513     return ret;
3514
3515   GST_LOG_OBJECT (ogg, "found begin at %" G_GINT64_FORMAT, next);
3516
3517   gst_ogg_demux_seek (ogg, next);
3518   ret = gst_ogg_demux_read_chain (ogg, &nextchain);
3519   if (ret == GST_FLOW_EOS) {
3520     nextchain = NULL;
3521     ret = GST_FLOW_OK;
3522     GST_LOG_OBJECT (ogg, "no next chain");
3523   } else if (ret != GST_FLOW_OK)
3524     goto done;
3525
3526   if (searched < end && nextchain != NULL) {
3527     ret = gst_ogg_demux_bisect_forward_serialno (ogg, next, ogg->offset,
3528         end, nextchain, m + 1);
3529     if (ret != GST_FLOW_OK)
3530       goto done;
3531   }
3532   GST_LOG_OBJECT (ogg, "adding chain %p", chain);
3533
3534   g_array_insert_val (ogg->chains, 0, chain);
3535
3536 done:
3537   return ret;
3538 }
3539
3540 /* read a chain from the ogg file. This code will
3541  * read all BOS pages and will create and return a GstOggChain 
3542  * structure with the results. 
3543  * 
3544  * This function will also read N pages from each stream in the
3545  * chain and submit them to the decoders. When the decoder has
3546  * decoded the first buffer, we know the timestamp of the first
3547  * page in the chain.
3548  */
3549 static GstFlowReturn
3550 gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
3551 {
3552   GstFlowReturn ret;
3553   GstOggChain *chain = NULL;
3554   gint64 offset = ogg->offset;
3555   ogg_page og;
3556   gboolean done;
3557   gint i;
3558
3559   GST_LOG_OBJECT (ogg, "reading chain at %" G_GINT64_FORMAT, offset);
3560
3561   /* first read the BOS pages, do typefind on them, create
3562    * the decoders, send data to the decoders. */
3563   while (TRUE) {
3564     GstOggPad *pad;
3565     guint32 serial;
3566
3567     ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3568     if (ret != GST_FLOW_OK) {
3569       if (ret == GST_FLOW_EOS) {
3570         GST_DEBUG_OBJECT (ogg, "Reached EOS, done reading end chain");
3571       } else {
3572         GST_WARNING_OBJECT (ogg, "problem reading BOS page: ret=%d", ret);
3573       }
3574       break;
3575     }
3576     if (!ogg_page_bos (&og)) {
3577       GST_INFO_OBJECT (ogg, "page is not BOS page, all streams identified");
3578       /* if we did not find a chain yet, assume this is a bogus stream and
3579        * ignore it */
3580       if (!chain) {
3581         GST_WARNING_OBJECT (ogg, "No chain found, no Ogg data in stream ?");
3582         ret = GST_FLOW_EOS;
3583       }
3584       break;
3585     }
3586
3587     if (chain == NULL) {
3588       chain = gst_ogg_chain_new (ogg);
3589       chain->offset = offset;
3590     }
3591
3592     serial = ogg_page_serialno (&og);
3593     if (gst_ogg_chain_get_stream (chain, serial) != NULL) {
3594       GST_WARNING_OBJECT (ogg,
3595           "found serial %08x BOS page twice, ignoring", serial);
3596       continue;
3597     }
3598
3599     pad = gst_ogg_chain_new_stream (chain, serial);
3600     gst_ogg_pad_submit_page (pad, &og);
3601   }
3602
3603   if (ret != GST_FLOW_OK || chain == NULL) {
3604     if (ret == GST_FLOW_OK) {
3605       GST_WARNING_OBJECT (ogg, "no chain was found");
3606       ret = GST_FLOW_ERROR;
3607     } else if (ret != GST_FLOW_EOS) {
3608       GST_WARNING_OBJECT (ogg, "failed to read chain");
3609     } else {
3610       GST_DEBUG_OBJECT (ogg, "done reading chains");
3611     }
3612     if (chain) {
3613       gst_ogg_chain_free (chain);
3614     }
3615     if (res_chain)
3616       *res_chain = NULL;
3617     return ret;
3618   }
3619
3620   chain->have_bos = TRUE;
3621   GST_LOG_OBJECT (ogg, "read bos pages, init decoder now");
3622
3623   /* now read pages until we receive a buffer from each of the
3624    * stream decoders, this will tell us the timestamp of the
3625    * first packet in the chain then */
3626
3627   /* save the offset to the first non bos page in the chain: if searching for
3628    * pad->first_time we read past the end of the chain, we'll seek back to this
3629    * position
3630    */
3631   offset = ogg->offset;
3632
3633   done = FALSE;
3634   while (!done) {
3635     guint32 serial;
3636     gboolean known_serial = FALSE;
3637     GstFlowReturn ret;
3638
3639     serial = ogg_page_serialno (&og);
3640     done = TRUE;
3641     for (i = 0; i < chain->streams->len; i++) {
3642       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3643
3644       GST_LOG_OBJECT (ogg,
3645           "serial %08x time %" GST_TIME_FORMAT,
3646           pad->map.serialno, GST_TIME_ARGS (pad->start_time));
3647
3648       if (pad->map.serialno == serial) {
3649         known_serial = TRUE;
3650
3651         /* submit the page now, this will fill in the start_time when the
3652          * internal decoder finds it */
3653         gst_ogg_pad_submit_page (pad, &og);
3654
3655         if (!pad->map.is_skeleton && pad->start_time == -1
3656             && ogg_page_eos (&og)) {
3657           /* got EOS on a pad before we could find its start_time.
3658            * We have no chance of finding a start_time for every pad so
3659            * stop searching for the other start_time(s).
3660            */
3661           done = TRUE;
3662           break;
3663         }
3664       }
3665       /* the timestamp will be filled in when we submit the pages */
3666       if (!pad->map.is_sparse)
3667         done &= (pad->start_time != GST_CLOCK_TIME_NONE);
3668
3669       GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
3670     }
3671
3672     /* we read a page not belonging to the current chain: seek back to the
3673      * beginning of the chain
3674      */
3675     if (!known_serial) {
3676       GST_LOG_OBJECT (ogg, "unknown serial %08x", serial);
3677       gst_ogg_demux_seek (ogg, offset);
3678       break;
3679     }
3680
3681     if (!done) {
3682       ret = gst_ogg_demux_get_next_page (ogg, &og, -1, NULL);
3683       if (ret != GST_FLOW_OK)
3684         break;
3685     }
3686   }
3687   GST_LOG_OBJECT (ogg, "done reading chain");
3688   /* now we can fill in the missing info using queries */
3689   for (i = 0; i < chain->streams->len; i++) {
3690     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3691
3692     if (pad->map.is_skeleton)
3693       continue;
3694
3695     pad->mode = GST_OGG_PAD_MODE_STREAMING;
3696   }
3697
3698   if (res_chain)
3699     *res_chain = chain;
3700
3701   return GST_FLOW_OK;
3702 }
3703
3704 /* read the last pages from the ogg stream to get the final
3705  * page end_offsets.
3706  */
3707 static GstFlowReturn
3708 gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
3709 {
3710   gint64 begin = chain->end_offset;
3711   gint64 end = begin;
3712   gint64 last_granule = -1;
3713   GstOggPad *last_pad = NULL;
3714   GstFlowReturn ret;
3715   gboolean done = FALSE;
3716   ogg_page og;
3717   gint i;
3718
3719   while (!done) {
3720     begin -= CHUNKSIZE;
3721     if (begin < 0)
3722       begin = 0;
3723
3724     gst_ogg_demux_seek (ogg, begin);
3725
3726     /* now continue reading until we run out of data, if we find a page
3727      * start, we save it. It might not be the final page as there could be
3728      * another page after this one. */
3729     while (ogg->offset < end) {
3730       ret = gst_ogg_demux_get_next_page (ogg, &og, end - ogg->offset, NULL);
3731
3732       if (ret == GST_FLOW_LIMIT)
3733         break;
3734       if (ret != GST_FLOW_OK)
3735         return ret;
3736
3737       for (i = 0; i < chain->streams->len; i++) {
3738         GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
3739
3740         if (pad->map.is_skeleton)
3741           continue;
3742
3743         if (pad->map.serialno == ogg_page_serialno (&og)) {
3744           gint64 granulepos = ogg_page_granulepos (&og);
3745
3746           if (granulepos != -1) {
3747             last_granule = granulepos;
3748             last_pad = pad;
3749             done = TRUE;
3750           }
3751           break;
3752         }
3753       }
3754     }
3755   }
3756
3757   if (last_pad) {
3758     chain->segment_stop =
3759         gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
3760         last_granule);
3761   } else {
3762     chain->segment_stop = GST_CLOCK_TIME_NONE;
3763   }
3764
3765   GST_INFO ("segment stop %" G_GUINT64_FORMAT, chain->segment_stop);
3766
3767   return GST_FLOW_OK;
3768 }
3769
3770 /* find a pad with a given serial number
3771  */
3772 static GstOggPad *
3773 gst_ogg_demux_find_pad (GstOggDemux * ogg, guint32 serialno)
3774 {
3775   GstOggPad *pad;
3776   gint i;
3777
3778   /* first look in building chain if any */
3779   if (ogg->building_chain) {
3780     pad = gst_ogg_chain_get_stream (ogg->building_chain, serialno);
3781     if (pad)
3782       return pad;
3783   }
3784
3785   /* then look in current chain if any */
3786   if (ogg->current_chain) {
3787     pad = gst_ogg_chain_get_stream (ogg->current_chain, serialno);
3788     if (pad)
3789       return pad;
3790   }
3791
3792   for (i = 0; i < ogg->chains->len; i++) {
3793     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3794
3795     pad = gst_ogg_chain_get_stream (chain, serialno);
3796     if (pad)
3797       return pad;
3798   }
3799   return NULL;
3800 }
3801
3802 /* find a chain with a given serial number
3803  */
3804 static GstOggChain *
3805 gst_ogg_demux_find_chain (GstOggDemux * ogg, guint32 serialno)
3806 {
3807   GstOggPad *pad;
3808
3809   pad = gst_ogg_demux_find_pad (ogg, serialno);
3810   if (pad) {
3811     return pad->chain;
3812   }
3813   return NULL;
3814 }
3815
3816 /* returns TRUE if all streams have valid start time */
3817 static gboolean
3818 gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
3819 {
3820   gboolean res = TRUE;
3821
3822   chain->total_time = GST_CLOCK_TIME_NONE;
3823   GST_DEBUG_OBJECT (ogg, "trying to collect chain info");
3824
3825   /* see if we have a start time on all streams */
3826   chain->segment_start = gst_ogg_demux_collect_start_time (ogg, chain);
3827
3828   if (chain->segment_start == G_MAXUINT64) {
3829     /* not yet, stream some more data */
3830     res = FALSE;
3831   } else if (chain->segment_stop != GST_CLOCK_TIME_NONE) {
3832     /* we can calculate a total time */
3833     chain->total_time = chain->segment_stop - chain->segment_start;
3834   }
3835
3836   GST_DEBUG ("total time %" G_GUINT64_FORMAT, chain->total_time);
3837
3838   GST_DEBUG_OBJECT (ogg, "return %d", res);
3839
3840   return res;
3841 }
3842
3843 static void
3844 gst_ogg_demux_collect_info (GstOggDemux * ogg)
3845 {
3846   gint i;
3847
3848   /* collect all info */
3849   ogg->total_time = 0;
3850
3851   for (i = 0; i < ogg->chains->len; i++) {
3852     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
3853
3854     chain->begin_time = ogg->total_time;
3855
3856     gst_ogg_demux_collect_chain_info (ogg, chain);
3857
3858     ogg->total_time += chain->total_time;
3859   }
3860   ogg->segment.duration = ogg->total_time;
3861 }
3862
3863 /* find all the chains in the ogg file, this reads the first and
3864  * last page of the ogg stream, if they match then the ogg file has
3865  * just one chain, else we do a binary search for all chains.
3866  */
3867 static GstFlowReturn
3868 gst_ogg_demux_find_chains (GstOggDemux * ogg)
3869 {
3870   ogg_page og;
3871   GstPad *peer;
3872   gboolean res;
3873   guint32 serialno;
3874   GstOggChain *chain;
3875   GstFlowReturn ret;
3876
3877   /* get peer to figure out length */
3878   if ((peer = gst_pad_get_peer (ogg->sinkpad)) == NULL)
3879     goto no_peer;
3880
3881   /* find length to read last page, we store this for later use. */
3882   res = gst_pad_query_duration (peer, GST_FORMAT_BYTES, &ogg->length);
3883   gst_object_unref (peer);
3884   if (!res || ogg->length <= 0)
3885     goto no_length;
3886
3887   GST_DEBUG_OBJECT (ogg, "file length %" G_GINT64_FORMAT, ogg->length);
3888
3889   /* read chain from offset 0, this is the first chain of the
3890    * ogg file. */
3891   gst_ogg_demux_seek (ogg, 0);
3892   ret = gst_ogg_demux_read_chain (ogg, &chain);
3893   if (ret != GST_FLOW_OK)
3894     goto no_first_chain;
3895
3896   /* read page from end offset, we use this page to check if its serial
3897    * number is contained in the first chain. If this is the case then
3898    * this ogg is not a chained ogg and we can skip the scanning. */
3899   gst_ogg_demux_seek (ogg, ogg->length);
3900   ret = gst_ogg_demux_get_prev_page (ogg, &og, NULL);
3901   if (ret != GST_FLOW_OK)
3902     goto no_last_page;
3903
3904   serialno = ogg_page_serialno (&og);
3905
3906   if (!gst_ogg_chain_has_stream (chain, serialno)) {
3907     /* the last page is not in the first stream, this means we should
3908      * find all the chains in this chained ogg. */
3909     ret =
3910         gst_ogg_demux_bisect_forward_serialno (ogg, 0, 0, ogg->length, chain,
3911         0);
3912   } else {
3913     /* we still call this function here but with an empty range so that
3914      * we can reuse the setup code in this routine. */
3915     ret =
3916         gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length,
3917         ogg->length, chain, 0);
3918   }
3919   if (ret != GST_FLOW_OK)
3920     goto done;
3921
3922   /* all fine, collect and print */
3923   gst_ogg_demux_collect_info (ogg);
3924
3925   /* dump our chains and streams */
3926   gst_ogg_print (ogg);
3927
3928 done:
3929   return ret;
3930
3931   /*** error cases ***/
3932 no_peer:
3933   {
3934     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("we don't have a peer"));
3935     return GST_FLOW_NOT_LINKED;
3936   }
3937 no_length:
3938   {
3939     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get file length"));
3940     return GST_FLOW_NOT_SUPPORTED;
3941   }
3942 no_first_chain:
3943   {
3944     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL), ("can't get first chain"));
3945     return GST_FLOW_ERROR;
3946   }
3947 no_last_page:
3948   {
3949     GST_DEBUG_OBJECT (ogg, "can't get last page");
3950     if (chain)
3951       gst_ogg_chain_free (chain);
3952     return ret;
3953   }
3954 }
3955
3956 static GstFlowReturn
3957 gst_ogg_demux_handle_page (GstOggDemux * ogg, ogg_page * page)
3958 {
3959   GstOggPad *pad;
3960   gint64 granule;
3961   guint32 serialno;
3962   GstFlowReturn result = GST_FLOW_OK;
3963
3964   serialno = ogg_page_serialno (page);
3965   granule = ogg_page_granulepos (page);
3966
3967   GST_LOG_OBJECT (ogg,
3968       "processing ogg page (serial %08x, "
3969       "pageno %ld, granulepos %" G_GINT64_FORMAT ", bos %d)", serialno,
3970       ogg_page_pageno (page), granule, ogg_page_bos (page));
3971
3972   if (ogg_page_bos (page)) {
3973     GstOggChain *chain;
3974
3975     /* first page */
3976     /* see if we know about the chain already */
3977     chain = gst_ogg_demux_find_chain (ogg, serialno);
3978     if (chain) {
3979       GstEvent *event;
3980       gint64 start = 0;
3981       GstSegment segment;
3982
3983       if (chain->segment_start != GST_CLOCK_TIME_NONE)
3984         start = chain->segment_start;
3985
3986       /* create the newsegment event we are going to send out */
3987       gst_segment_copy_into (&ogg->segment, &segment);
3988       segment.start = start;
3989       segment.stop = chain->segment_stop;
3990       segment.time = chain->begin_time;
3991       event = gst_event_new_segment (&segment);
3992       gst_event_set_seqnum (event, ogg->seqnum);
3993
3994       GST_DEBUG_OBJECT (ogg,
3995           "segment: start %" GST_TIME_FORMAT ", stop %" GST_TIME_FORMAT
3996           ", time %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
3997           GST_TIME_ARGS (chain->segment_stop),
3998           GST_TIME_ARGS (chain->begin_time));
3999
4000       /* activate it as it means we have a non-header, this will also deactivate
4001        * the currently running chain. */
4002       gst_ogg_demux_activate_chain (ogg, chain, event);
4003       pad = gst_ogg_demux_find_pad (ogg, serialno);
4004     } else {
4005       GstClockTime chain_time;
4006       gint64 current_time;
4007
4008       /* this can only happen in push mode */
4009       if (ogg->pullmode)
4010         goto unknown_chain;
4011
4012       current_time = ogg->segment.position;
4013
4014       /* time of new chain is current time */
4015       chain_time = current_time;
4016
4017       if (ogg->building_chain == NULL) {
4018         GstOggChain *newchain;
4019
4020         newchain = gst_ogg_chain_new (ogg);
4021         newchain->offset = 0;
4022         /* set new chain begin time aligned with end time of old chain */
4023         newchain->begin_time = chain_time;
4024         GST_DEBUG_OBJECT (ogg, "new chain, begin time %" GST_TIME_FORMAT,
4025             GST_TIME_ARGS (chain_time));
4026
4027         /* and this is the one we are building now */
4028         ogg->building_chain = newchain;
4029       }
4030       pad = gst_ogg_chain_new_stream (ogg->building_chain, serialno);
4031     }
4032   } else {
4033     pad = gst_ogg_demux_find_pad (ogg, serialno);
4034   }
4035   if (pad) {
4036     result = gst_ogg_pad_submit_page (pad, page);
4037   } else {
4038     GST_PUSH_LOCK (ogg);
4039     if (!ogg->pullmode && !ogg->push_disable_seeking) {
4040       /* no pad while probing for duration, we must have a chained stream,
4041          and we don't support them, so back off */
4042       GST_INFO_OBJECT (ogg, "We seem to have a chained stream, we won't seek");
4043       if (ogg->push_state == PUSH_DURATION) {
4044         GstFlowReturn res;
4045
4046         res = gst_ogg_demux_seek_back_after_push_duration_check_unlock (ogg);
4047         if (res != GST_FLOW_OK)
4048           return res;
4049       }
4050
4051       /* only once we seeked back */
4052       GST_PUSH_LOCK (ogg);
4053       ogg->push_disable_seeking = TRUE;
4054     } else {
4055       GST_PUSH_UNLOCK (ogg);
4056       /* no pad. This means an ogg page without bos has been seen for this
4057        * serialno. we just ignore it but post a warning... */
4058       GST_ELEMENT_WARNING (ogg, STREAM, DECODE,
4059           (NULL), ("unknown ogg pad for serial %08x detected", serialno));
4060       return GST_FLOW_OK;
4061     }
4062     GST_PUSH_UNLOCK (ogg);
4063   }
4064   return result;
4065
4066   /* ERRORS */
4067 unknown_chain:
4068   {
4069     GST_ELEMENT_ERROR (ogg, STREAM, DECODE,
4070         (NULL), ("unknown ogg chain for serial %08x detected", serialno));
4071     return GST_FLOW_ERROR;
4072   }
4073 }
4074
4075 /* streaming mode, receive a buffer, parse it, create pads for
4076  * the serialno, submit pages and packets to the oggpads
4077  */
4078 static GstFlowReturn
4079 gst_ogg_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
4080 {
4081   GstOggDemux *ogg;
4082   gint ret = 0;
4083   GstFlowReturn result = GST_FLOW_OK;
4084
4085   ogg = GST_OGG_DEMUX (parent);
4086
4087   GST_DEBUG_OBJECT (ogg, "enter");
4088   result = gst_ogg_demux_submit_buffer (ogg, buffer);
4089   if (result < 0) {
4090     GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_submit_buffer returned %d", result);
4091   }
4092
4093   while (result == GST_FLOW_OK) {
4094     ogg_page page;
4095
4096     ret = ogg_sync_pageout (&ogg->sync, &page);
4097     if (ret == 0)
4098       /* need more data */
4099       break;
4100     if (ret == -1) {
4101       /* discontinuity in the pages */
4102       GST_DEBUG_OBJECT (ogg, "discont in page found, continuing");
4103     } else {
4104       result = gst_ogg_demux_handle_page (ogg, &page);
4105       if (result < 0) {
4106         GST_DEBUG_OBJECT (ogg, "gst_ogg_demux_handle_page returned %d", result);
4107       }
4108     }
4109   }
4110   if (ret == 0 || result == GST_FLOW_OK) {
4111     gst_ogg_demux_sync_streams (ogg);
4112   }
4113   GST_DEBUG_OBJECT (ogg, "leave with %d", result);
4114   return result;
4115 }
4116
4117 static gboolean
4118 gst_ogg_demux_send_event (GstOggDemux * ogg, GstEvent * event)
4119 {
4120   GstOggChain *chain = ogg->current_chain;
4121   gboolean res = TRUE;
4122
4123   if (!chain)
4124     chain = ogg->building_chain;
4125
4126   if (chain) {
4127     gint i;
4128
4129     for (i = 0; i < chain->streams->len; i++) {
4130       GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
4131
4132       gst_event_ref (event);
4133       GST_DEBUG_OBJECT (pad, "Pushing event %" GST_PTR_FORMAT, event);
4134       res &= gst_pad_push_event (GST_PAD (pad), event);
4135     }
4136   } else {
4137     GST_WARNING_OBJECT (ogg, "No chain to forward event to");
4138   }
4139   gst_event_unref (event);
4140
4141   return res;
4142 }
4143
4144 static GstFlowReturn
4145 gst_ogg_demux_combine_flows (GstOggDemux * ogg, GstOggPad * pad,
4146     GstFlowReturn ret)
4147 {
4148   GstOggChain *chain;
4149
4150   /* store the value */
4151   pad->last_ret = ret;
4152
4153   /* any other error that is not-linked can be returned right
4154    * away */
4155   if (ret != GST_FLOW_NOT_LINKED)
4156     goto done;
4157
4158   /* only return NOT_LINKED if all other pads returned NOT_LINKED */
4159   chain = ogg->current_chain;
4160   if (chain) {
4161     gint i;
4162
4163     for (i = 0; i < chain->streams->len; i++) {
4164       GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4165
4166       ret = opad->last_ret;
4167       /* some other return value (must be SUCCESS but we can return
4168        * other values as well) */
4169       if (ret != GST_FLOW_NOT_LINKED)
4170         goto done;
4171     }
4172     /* if we get here, all other pads were unlinked and we return
4173      * NOT_LINKED then */
4174   }
4175 done:
4176   return ret;
4177 }
4178
4179 /* returns TRUE if all streams in current chain reached EOS, FALSE otherwise */
4180 static gboolean
4181 gst_ogg_demux_check_eos (GstOggDemux * ogg)
4182 {
4183   GstOggChain *chain;
4184   gboolean eos = TRUE;
4185
4186   chain = ogg->current_chain;
4187   if (G_LIKELY (chain)) {
4188     gint i;
4189
4190     for (i = 0; i < chain->streams->len; i++) {
4191       GstOggPad *opad = g_array_index (chain->streams, GstOggPad *, i);
4192
4193       eos = eos && opad->is_eos;
4194     }
4195   } else {
4196     eos = FALSE;
4197   }
4198
4199   return eos;
4200 }
4201
4202 static GstFlowReturn
4203 gst_ogg_demux_loop_forward (GstOggDemux * ogg)
4204 {
4205   GstFlowReturn ret;
4206   GstBuffer *buffer;
4207
4208   if (ogg->offset == ogg->length) {
4209     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4210         " == %" G_GINT64_FORMAT, ogg->offset, ogg->length);
4211     ret = GST_FLOW_EOS;
4212     goto done;
4213   }
4214
4215   GST_LOG_OBJECT (ogg, "pull data %" G_GINT64_FORMAT, ogg->offset);
4216   ret = gst_pad_pull_range (ogg->sinkpad, ogg->offset, CHUNKSIZE, &buffer);
4217   if (ret != GST_FLOW_OK) {
4218     GST_LOG_OBJECT (ogg, "Failed pull_range");
4219     goto done;
4220   }
4221
4222   ogg->offset += gst_buffer_get_size (buffer);
4223
4224   if (G_UNLIKELY (ogg->newsegment)) {
4225     gst_ogg_demux_send_event (ogg, ogg->newsegment);
4226     ogg->newsegment = NULL;
4227   }
4228
4229   ret = gst_ogg_demux_chain (ogg->sinkpad, GST_OBJECT_CAST (ogg), buffer);
4230   if (ret != GST_FLOW_OK) {
4231     GST_LOG_OBJECT (ogg, "Failed demux_chain");
4232     goto done;
4233   }
4234
4235   /* check for the end of the segment */
4236   if (gst_ogg_demux_check_eos (ogg)) {
4237     GST_LOG_OBJECT (ogg, "got EOS");
4238     ret = GST_FLOW_EOS;
4239     goto done;
4240   }
4241 done:
4242   return ret;
4243 }
4244
4245 /* reverse mode.
4246  *
4247  * We read the pages backwards and send the packets forwards. The first packet
4248  * in the page will be pushed with the DISCONT flag set.
4249  *
4250  * Special care has to be taken for continued pages, which we can only decode
4251  * when we have the previous page(s).
4252  */
4253 static GstFlowReturn
4254 gst_ogg_demux_loop_reverse (GstOggDemux * ogg)
4255 {
4256   GstFlowReturn ret;
4257   ogg_page page;
4258   gint64 offset;
4259
4260   if (ogg->offset == 0) {
4261     GST_LOG_OBJECT (ogg, "no more data to pull %" G_GINT64_FORMAT
4262         " == 0", ogg->offset);
4263     ret = GST_FLOW_EOS;
4264     goto done;
4265   }
4266
4267   GST_LOG_OBJECT (ogg, "read page from %" G_GINT64_FORMAT, ogg->offset);
4268   ret = gst_ogg_demux_get_prev_page (ogg, &page, &offset);
4269   if (ret != GST_FLOW_OK)
4270     goto done;
4271
4272   ogg->offset = offset;
4273
4274   if (G_UNLIKELY (ogg->newsegment)) {
4275     gst_ogg_demux_send_event (ogg, ogg->newsegment);
4276     ogg->newsegment = NULL;
4277   }
4278
4279   ret = gst_ogg_demux_handle_page (ogg, &page);
4280   if (ret != GST_FLOW_OK)
4281     goto done;
4282
4283   /* check for the end of the segment */
4284   if (gst_ogg_demux_check_eos (ogg)) {
4285     GST_LOG_OBJECT (ogg, "got EOS");
4286     ret = GST_FLOW_EOS;
4287     goto done;
4288   }
4289 done:
4290   return ret;
4291 }
4292
4293 static void
4294 gst_ogg_demux_sync_streams (GstOggDemux * ogg)
4295 {
4296   GstClockTime cur;
4297   GstOggChain *chain;
4298   guint i;
4299
4300   chain = ogg->current_chain;
4301   cur = ogg->segment.position;
4302   if (chain == NULL || cur == -1)
4303     return;
4304
4305   for (i = 0; i < chain->streams->len; i++) {
4306     GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, i);
4307
4308     /* Theoretically, we should be doing this for all streams, but we're only
4309      * doing it for known-to-be-sparse streams at the moment in order not to
4310      * break things for wrongly-muxed streams (like we used to produce once) */
4311     if (stream->map.is_sparse && stream->position != GST_CLOCK_TIME_NONE) {
4312
4313       /* Does this stream lag? Random threshold of 2 seconds */
4314       if (GST_CLOCK_DIFF (stream->position, cur) > (2 * GST_SECOND)) {
4315         GST_DEBUG_OBJECT (stream, "synchronizing stream with others by "
4316             "advancing time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
4317             GST_TIME_ARGS (stream->position), GST_TIME_ARGS (cur));
4318
4319         stream->position = cur;
4320
4321 #if 0
4322         ogg->segment.base += cur - stream->position;
4323         /* advance stream time (FIXME: is this right, esp. time_pos?) */
4324         gst_pad_push_event (GST_PAD_CAST (stream),
4325             gst_event_new_new_segment (TRUE, ogg->segment.rate,
4326                 ogg->segment.applied_rate,
4327                 GST_FORMAT_TIME, stream->position, -1, stream->position));
4328 #endif
4329       }
4330     }
4331   }
4332 }
4333
4334 /* random access code
4335  *
4336  * - first find all the chains and streams by scanning the file.
4337  * - then get and chain buffers, just like the streaming case.
4338  * - when seeking, we can use the chain info to perform the seek.
4339  */
4340 static void
4341 gst_ogg_demux_loop (GstOggPad * pad)
4342 {
4343   GstOggDemux *ogg;
4344   GstFlowReturn ret;
4345   GstEvent *event;
4346
4347   ogg = GST_OGG_DEMUX (GST_OBJECT_PARENT (pad));
4348
4349   if (ogg->need_chains) {
4350     gboolean res;
4351
4352     /* this is the only place where we write chains and thus need to lock. */
4353     GST_CHAIN_LOCK (ogg);
4354     ret = gst_ogg_demux_find_chains (ogg);
4355     GST_CHAIN_UNLOCK (ogg);
4356     if (ret != GST_FLOW_OK)
4357       goto chain_read_failed;
4358
4359     ogg->need_chains = FALSE;
4360
4361     GST_OBJECT_LOCK (ogg);
4362     ogg->running = TRUE;
4363     event = ogg->event;
4364     ogg->event = NULL;
4365     GST_OBJECT_UNLOCK (ogg);
4366
4367     /* and seek to configured positions without FLUSH */
4368     res = gst_ogg_demux_perform_seek_pull (ogg, event);
4369     if (event)
4370       gst_event_unref (event);
4371
4372     if (!res)
4373       goto seek_failed;
4374   }
4375
4376   if (ogg->segment.rate >= 0.0)
4377     ret = gst_ogg_demux_loop_forward (ogg);
4378   else
4379     ret = gst_ogg_demux_loop_reverse (ogg);
4380
4381   if (ret != GST_FLOW_OK)
4382     goto pause;
4383
4384   gst_ogg_demux_sync_streams (ogg);
4385   return;
4386
4387   /* ERRORS */
4388 chain_read_failed:
4389   {
4390     /* error was posted */
4391     goto pause;
4392   }
4393 seek_failed:
4394   {
4395     GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4396         ("failed to start demuxing ogg"));
4397     ret = GST_FLOW_ERROR;
4398     goto pause;
4399   }
4400 pause:
4401   {
4402     const gchar *reason = gst_flow_get_name (ret);
4403     GstEvent *event = NULL;
4404
4405     GST_LOG_OBJECT (ogg, "pausing task, reason %s", reason);
4406     gst_pad_pause_task (ogg->sinkpad);
4407
4408     if (ret == GST_FLOW_EOS) {
4409       /* perform EOS logic */
4410       if (ogg->segment.flags & GST_SEEK_FLAG_SEGMENT) {
4411         gint64 stop;
4412         GstMessage *message;
4413
4414         /* for segment playback we need to post when (in stream time)
4415          * we stopped, this is either stop (when set) or the duration. */
4416         if ((stop = ogg->segment.stop) == -1)
4417           stop = ogg->segment.duration;
4418
4419         GST_LOG_OBJECT (ogg, "Sending segment done, at end of segment");
4420         message =
4421             gst_message_new_segment_done (GST_OBJECT (ogg), GST_FORMAT_TIME,
4422             stop);
4423         gst_message_set_seqnum (message, ogg->seqnum);
4424
4425         gst_element_post_message (GST_ELEMENT (ogg), message);
4426       } else {
4427         /* normal playback, send EOS to all linked pads */
4428         GST_LOG_OBJECT (ogg, "Sending EOS, at end of stream");
4429         event = gst_event_new_eos ();
4430       }
4431     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
4432       GST_ELEMENT_ERROR (ogg, STREAM, FAILED,
4433           (_("Internal data stream error.")),
4434           ("stream stopped, reason %s", reason));
4435       event = gst_event_new_eos ();
4436     }
4437
4438     /* For wrong-state we still want to pause the task and stop
4439      * but no error message or other things are necessary.
4440      * wrong-state is no real error and will be caused by flushing,
4441      * e.g. because of a flushing seek.
4442      */
4443     if (event) {
4444       /* guard against corrupt/truncated files, where one can hit EOS
4445          before prerolling is done and a chain created. If we have no
4446          chain to send the event to, error out. */
4447       if (ogg->current_chain || ogg->building_chain) {
4448         gst_event_set_seqnum (event, ogg->seqnum);
4449         gst_ogg_demux_send_event (ogg, event);
4450       } else {
4451         gst_event_unref (event);
4452         GST_ELEMENT_ERROR (ogg, STREAM, DEMUX, (NULL),
4453             ("EOS before finding a chain"));
4454       }
4455     }
4456     return;
4457   }
4458 }
4459
4460 static void
4461 gst_ogg_demux_clear_chains (GstOggDemux * ogg)
4462 {
4463   gint i;
4464
4465   gst_ogg_demux_deactivate_current_chain (ogg);
4466
4467   GST_CHAIN_LOCK (ogg);
4468   for (i = 0; i < ogg->chains->len; i++) {
4469     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4470
4471     gst_ogg_chain_free (chain);
4472   }
4473   ogg->chains = g_array_set_size (ogg->chains, 0);
4474   GST_CHAIN_UNLOCK (ogg);
4475 }
4476
4477 /* this function is called when the pad is activated and should start
4478  * processing data.
4479  *
4480  * We check if we can do random access to decide if we work push or
4481  * pull based.
4482  */
4483 static gboolean
4484 gst_ogg_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
4485 {
4486   GstQuery *query;
4487   gboolean pull_mode;
4488
4489   query = gst_query_new_scheduling ();
4490
4491   if (!gst_pad_peer_query (sinkpad, query)) {
4492     gst_query_unref (query);
4493     goto activate_push;
4494   }
4495
4496   pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL);
4497   gst_query_unref (query);
4498
4499   if (!pull_mode)
4500     goto activate_push;
4501
4502   GST_DEBUG_OBJECT (sinkpad, "activating pull");
4503   return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
4504
4505 activate_push:
4506   {
4507     GST_DEBUG_OBJECT (sinkpad, "activating push");
4508     return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
4509   }
4510 }
4511
4512 static gboolean
4513 gst_ogg_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
4514     GstPadMode mode, gboolean active)
4515 {
4516   gboolean res;
4517   GstOggDemux *ogg;
4518
4519   ogg = GST_OGG_DEMUX (parent);
4520
4521   switch (mode) {
4522     case GST_PAD_MODE_PUSH:
4523       ogg->pullmode = FALSE;
4524       ogg->resync = FALSE;
4525       res = TRUE;
4526       break;
4527     case GST_PAD_MODE_PULL:
4528       if (active) {
4529         ogg->need_chains = TRUE;
4530         ogg->pullmode = TRUE;
4531
4532         res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ogg_demux_loop,
4533             sinkpad);
4534       } else {
4535         res = gst_pad_stop_task (sinkpad);
4536       }
4537       break;
4538     default:
4539       res = FALSE;
4540       break;
4541   }
4542   return res;
4543 }
4544
4545 static GstStateChangeReturn
4546 gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
4547 {
4548   GstOggDemux *ogg;
4549   GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
4550
4551   ogg = GST_OGG_DEMUX (element);
4552
4553   switch (transition) {
4554     case GST_STATE_CHANGE_NULL_TO_READY:
4555       ogg->basetime = 0;
4556       ogg_sync_init (&ogg->sync);
4557       break;
4558     case GST_STATE_CHANGE_READY_TO_PAUSED:
4559       ogg_sync_reset (&ogg->sync);
4560       ogg->running = FALSE;
4561       ogg->bitrate = 0;
4562       ogg->total_time = -1;
4563       GST_PUSH_LOCK (ogg);
4564       ogg->push_byte_offset = 0;
4565       ogg->push_byte_length = -1;
4566       ogg->push_time_length = GST_CLOCK_TIME_NONE;
4567       ogg->push_time_offset = GST_CLOCK_TIME_NONE;
4568       ogg->push_state = PUSH_PLAYING;
4569
4570       ogg->push_disable_seeking = FALSE;
4571       if (!ogg->pullmode) {
4572         GstPad *peer;
4573         if ((peer = gst_pad_get_peer (ogg->sinkpad)) != NULL) {
4574           gint64 length = -1;
4575           if (!gst_pad_query_duration (peer, GST_FORMAT_BYTES, &length)
4576               || length <= 0) {
4577             GST_DEBUG_OBJECT (ogg,
4578                 "Unable to determine stream size, assuming live, seeking disabled");
4579             ogg->push_disable_seeking = TRUE;
4580           }
4581           gst_object_unref (peer);
4582         }
4583       }
4584
4585       GST_PUSH_UNLOCK (ogg);
4586       gst_segment_init (&ogg->segment, GST_FORMAT_TIME);
4587       break;
4588     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
4589       break;
4590     default:
4591       break;
4592   }
4593
4594   result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4595
4596   switch (transition) {
4597     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
4598       break;
4599     case GST_STATE_CHANGE_PAUSED_TO_READY:
4600       gst_ogg_demux_clear_chains (ogg);
4601       GST_OBJECT_LOCK (ogg);
4602       ogg->running = FALSE;
4603       GST_OBJECT_UNLOCK (ogg);
4604       break;
4605     case GST_STATE_CHANGE_READY_TO_NULL:
4606       ogg_sync_clear (&ogg->sync);
4607       break;
4608     default:
4609       break;
4610   }
4611   return result;
4612 }
4613
4614 gboolean
4615 gst_ogg_demux_plugin_init (GstPlugin * plugin)
4616 {
4617   GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_debug, "oggdemux", 0, "ogg demuxer");
4618   GST_DEBUG_CATEGORY_INIT (gst_ogg_demux_setup_debug, "oggdemux_setup", 0,
4619       "ogg demuxer setup stage when parsing pipeline");
4620
4621 #ifdef ENABLE_NLS
4622   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
4623       LOCALEDIR);
4624   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4625   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4626 #endif
4627
4628   return gst_element_register (plugin, "oggdemux", GST_RANK_PRIMARY,
4629       GST_TYPE_OGG_DEMUX);
4630 }
4631
4632 /* prints all info about the element */
4633 #undef GST_CAT_DEFAULT
4634 #define GST_CAT_DEFAULT gst_ogg_demux_setup_debug
4635
4636 #ifdef GST_DISABLE_GST_DEBUG
4637
4638 static void
4639 gst_ogg_print (GstOggDemux * ogg)
4640 {
4641   /* NOP */
4642 }
4643
4644 #else /* !GST_DISABLE_GST_DEBUG */
4645
4646 static void
4647 gst_ogg_print (GstOggDemux * ogg)
4648 {
4649   guint j, i;
4650
4651   GST_INFO_OBJECT (ogg, "%u chains", ogg->chains->len);
4652   GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT,
4653       GST_TIME_ARGS (ogg->total_time));
4654
4655   for (i = 0; i < ogg->chains->len; i++) {
4656     GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i);
4657
4658     GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len);
4659     GST_INFO_OBJECT (ogg,
4660         "  offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset,
4661         chain->end_offset);
4662     GST_INFO_OBJECT (ogg, "  begin time: %" GST_TIME_FORMAT,
4663         GST_TIME_ARGS (chain->begin_time));
4664     GST_INFO_OBJECT (ogg, "  total time: %" GST_TIME_FORMAT,
4665         GST_TIME_ARGS (chain->total_time));
4666     GST_INFO_OBJECT (ogg, "  segment start: %" GST_TIME_FORMAT,
4667         GST_TIME_ARGS (chain->segment_start));
4668     GST_INFO_OBJECT (ogg, "  segment stop:  %" GST_TIME_FORMAT,
4669         GST_TIME_ARGS (chain->segment_stop));
4670
4671     for (j = 0; j < chain->streams->len; j++) {
4672       GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
4673
4674       GST_INFO_OBJECT (ogg, "  stream %08x: %s", stream->map.serialno,
4675           gst_ogg_stream_get_media_type (&stream->map));
4676       GST_INFO_OBJECT (ogg, "   start time:       %" GST_TIME_FORMAT,
4677           GST_TIME_ARGS (stream->start_time));
4678     }
4679   }
4680 }
4681 #endif /* GST_DISABLE_GST_DEBUG */