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