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