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