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