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