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