1 /* OGG muxer plugin for GStreamer
2 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
3 * Copyright (C) 2006 Thomas Vander Stichele <thomas at apestaart dot org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-oggmux
23 * @see_also: <link linkend="gst-plugins-base-plugins-oggdemux">oggdemux</link>
25 * This element merges streams (audio and video) into ogg files.
28 * <title>Example pipelines</title>
30 * gst-launch v4l2src num-buffers=500 ! video/x-raw-yuv,width=320,height=240 ! ffmpegcolorspace ! theoraenc ! oggmux ! filesink location=video.ogg
31 * ]| Encodes a video stream captured from a v4l2-compatible camera to Ogg/Theora
32 * (the encoding will stop automatically after 500 frames)
35 * Last reviewed on 2008-02-06 (0.10.17)
43 #include <gst/base/gstcollectpads.h>
44 #include <gst/base/gstbytewriter.h>
45 #include <gst/tag/tag.h>
47 #include "gstoggmux.h"
49 /* memcpy - if someone knows a way to get rid of it, please speak up
50 * note: the ogg docs even say you need this... */
53 #include <stdlib.h> /* rand, srand, atoi */
55 GST_DEBUG_CATEGORY_STATIC (gst_ogg_mux_debug);
56 #define GST_CAT_DEFAULT gst_ogg_mux_debug
58 /* This isn't generally what you'd want with an end-time macro, because
59 technically the end time of a buffer with invalid duration is invalid. But
60 for sorting ogg pages this is what we want. */
61 #define GST_BUFFER_END_TIME(buf) \
62 (GST_BUFFER_DURATION_IS_VALID (buf) \
63 ? GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf) \
64 : GST_BUFFER_TIMESTAMP (buf))
66 #define GST_GP_FORMAT "[gp %8" G_GINT64_FORMAT "]"
67 #define GST_GP_CAST(_gp) ((gint64) _gp)
71 GST_OGG_FLAG_BOS = GST_ELEMENT_FLAG_LAST,
76 /* OggMux signals and args */
83 /* set to 0.5 seconds by default */
84 #define DEFAULT_MAX_DELAY G_GINT64_CONSTANT(500000000)
85 #define DEFAULT_MAX_PAGE_DELAY G_GINT64_CONSTANT(500000000)
86 #define DEFAULT_MAX_TOLERANCE G_GINT64_CONSTANT(40000000)
87 #define DEFAULT_SKELETON FALSE
98 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
101 GST_STATIC_CAPS ("application/ogg")
104 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
107 GST_STATIC_CAPS ("video/x-theora; "
108 "audio/x-vorbis; audio/x-flac; audio/x-speex; audio/x-celt; "
109 "application/x-ogm-video; application/x-ogm-audio; video/x-dirac; "
110 "video/x-smoke; video/x-vp8; text/x-cmml, encoded = (boolean) TRUE; "
111 "subtitle/x-kate; application/x-kate")
114 static void gst_ogg_mux_base_init (gpointer g_class);
115 static void gst_ogg_mux_class_init (GstOggMuxClass * klass);
116 static void gst_ogg_mux_init (GstOggMux * ogg_mux);
117 static void gst_ogg_mux_finalize (GObject * object);
120 gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux);
121 static gboolean gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event);
122 static GstPad *gst_ogg_mux_request_new_pad (GstElement * element,
123 GstPadTemplate * templ, const gchar * name);
124 static void gst_ogg_mux_release_pad (GstElement * element, GstPad * pad);
126 static void gst_ogg_mux_set_property (GObject * object,
127 guint prop_id, const GValue * value, GParamSpec * pspec);
128 static void gst_ogg_mux_get_property (GObject * object,
129 guint prop_id, GValue * value, GParamSpec * pspec);
130 static GstStateChangeReturn gst_ogg_mux_change_state (GstElement * element,
131 GstStateChange transition);
133 static GstElementClass *parent_class = NULL;
135 /*static guint gst_ogg_mux_signals[LAST_SIGNAL] = { 0 }; */
138 gst_ogg_mux_get_type (void)
140 static GType ogg_mux_type = 0;
142 if (G_UNLIKELY (ogg_mux_type == 0)) {
143 static const GTypeInfo ogg_mux_info = {
144 sizeof (GstOggMuxClass),
145 gst_ogg_mux_base_init,
147 (GClassInitFunc) gst_ogg_mux_class_init,
152 (GInstanceInitFunc) gst_ogg_mux_init,
154 static const GInterfaceInfo preset_info = {
161 g_type_register_static (GST_TYPE_ELEMENT, "GstOggMux", &ogg_mux_info,
164 g_type_add_interface_static (ogg_mux_type, GST_TYPE_PRESET, &preset_info);
170 gst_ogg_mux_base_init (gpointer g_class)
172 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
174 gst_element_class_add_pad_template (element_class,
175 gst_static_pad_template_get (&src_factory));
176 gst_element_class_add_pad_template (element_class,
177 gst_static_pad_template_get (&sink_factory));
179 gst_element_class_set_details_simple (element_class,
180 "Ogg muxer", "Codec/Muxer",
181 "mux ogg streams (info about ogg: http://xiph.org)",
182 "Wim Taymans <wim@fluendo.com>");
186 gst_ogg_mux_class_init (GstOggMuxClass * klass)
188 GObjectClass *gobject_class;
189 GstElementClass *gstelement_class;
191 gobject_class = (GObjectClass *) klass;
192 gstelement_class = (GstElementClass *) klass;
194 parent_class = g_type_class_peek_parent (klass);
196 gobject_class->finalize = gst_ogg_mux_finalize;
197 gobject_class->get_property = gst_ogg_mux_get_property;
198 gobject_class->set_property = gst_ogg_mux_set_property;
200 gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad;
201 gstelement_class->release_pad = gst_ogg_mux_release_pad;
203 g_object_class_install_property (gobject_class, ARG_MAX_DELAY,
204 g_param_spec_uint64 ("max-delay", "Max delay",
205 "Maximum delay in multiplexing streams", 0, G_MAXUINT64,
207 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
208 g_object_class_install_property (gobject_class, ARG_MAX_PAGE_DELAY,
209 g_param_spec_uint64 ("max-page-delay", "Max page delay",
210 "Maximum delay for sending out a page", 0, G_MAXUINT64,
211 DEFAULT_MAX_PAGE_DELAY,
212 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
213 g_object_class_install_property (gobject_class, ARG_MAX_TOLERANCE,
214 g_param_spec_uint64 ("max-tolerance", "Max time tolerance",
215 "Maximum timestamp difference for maintaining perfect granules",
216 0, G_MAXUINT64, DEFAULT_MAX_TOLERANCE,
217 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
218 g_object_class_install_property (gobject_class, ARG_SKELETON,
219 g_param_spec_boolean ("skeleton", "Skeleton",
220 "Whether to include a Skeleton track",
222 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
224 gstelement_class->change_state = gst_ogg_mux_change_state;
229 static const GstEventMask *
230 gst_ogg_mux_get_sink_event_masks (GstPad * pad)
232 static const GstEventMask gst_ogg_mux_sink_event_masks[] = {
234 {GST_EVENT_DISCONTINUOUS, 0},
238 return gst_ogg_mux_sink_event_masks;
243 gst_ogg_mux_clear (GstOggMux * ogg_mux)
245 ogg_mux->pulling = NULL;
246 ogg_mux->need_headers = TRUE;
247 ogg_mux->delta_pad = NULL;
249 ogg_mux->next_ts = 0;
250 ogg_mux->last_ts = GST_CLOCK_TIME_NONE;
254 gst_ogg_mux_init (GstOggMux * ogg_mux)
256 GstElementClass *klass = GST_ELEMENT_GET_CLASS (ogg_mux);
259 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
261 gst_pad_set_event_function (ogg_mux->srcpad, gst_ogg_mux_handle_src_event);
262 gst_element_add_pad (GST_ELEMENT (ogg_mux), ogg_mux->srcpad);
264 GST_OBJECT_FLAG_SET (GST_ELEMENT (ogg_mux), GST_OGG_FLAG_BOS);
266 /* seed random number generator for creation of serial numbers */
269 ogg_mux->collect = gst_collect_pads_new ();
270 gst_collect_pads_set_function (ogg_mux->collect,
271 (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_ogg_mux_collected),
274 ogg_mux->max_delay = DEFAULT_MAX_DELAY;
275 ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY;
276 ogg_mux->max_tolerance = DEFAULT_MAX_TOLERANCE;
278 gst_ogg_mux_clear (ogg_mux);
282 gst_ogg_mux_finalize (GObject * object)
286 ogg_mux = GST_OGG_MUX (object);
288 if (ogg_mux->collect) {
289 gst_object_unref (ogg_mux->collect);
290 ogg_mux->collect = NULL;
293 G_OBJECT_CLASS (parent_class)->finalize (object);
297 gst_ogg_mux_ogg_pad_destroy_notify (GstCollectData * data)
299 GstOggPadData *oggpad = (GstOggPadData *) data;
302 ogg_stream_clear (&oggpad->map.stream);
303 gst_caps_replace (&oggpad->map.caps, NULL);
305 if (oggpad->pagebuffers) {
306 while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
307 gst_buffer_unref (buf);
309 g_queue_free (oggpad->pagebuffers);
310 oggpad->pagebuffers = NULL;
314 static GstPadLinkReturn
315 gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer)
319 ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
321 GST_DEBUG_OBJECT (ogg_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad));
323 gst_object_unref (ogg_mux);
325 return GST_PAD_LINK_OK;
329 gst_ogg_mux_sink_event (GstPad * pad, GstEvent * event)
331 GstOggMux *ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
332 GstOggPadData *ogg_pad = (GstOggPadData *) gst_pad_get_element_private (pad);
333 gboolean ret = FALSE;
335 GST_DEBUG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event));
337 switch (GST_EVENT_TYPE (event)) {
338 case GST_EVENT_NEWSEGMENT:{
341 gdouble applied_rate;
343 gint64 start, stop, position;
345 gst_event_parse_new_segment_full (event, &update, &rate,
346 &applied_rate, &format, &start, &stop, &position);
348 /* We don't support non time NEWSEGMENT events */
349 if (format != GST_FORMAT_TIME) {
350 gst_event_unref (event);
355 gst_segment_set_newsegment_full (&ogg_pad->segment, update, rate,
356 applied_rate, format, start, stop, position);
360 case GST_EVENT_FLUSH_STOP:{
361 gst_segment_init (&ogg_pad->segment, GST_FORMAT_TIME);
367 gst_event_parse_tag (event, &tags);
368 tags = gst_tag_list_merge (ogg_pad->tags, tags, GST_TAG_MERGE_APPEND);
370 gst_tag_list_free (ogg_pad->tags);
371 ogg_pad->tags = tags;
373 GST_DEBUG_OBJECT (ogg_mux, "Got tags %" GST_PTR_FORMAT, ogg_pad->tags);
380 /* now GstCollectPads can take care of the rest, e.g. EOS */
382 ret = ogg_pad->collect_event (pad, event);
384 gst_object_unref (ogg_mux);
389 gst_ogg_mux_is_serialno_present (GstOggMux * ogg_mux, guint32 serialno)
393 walk = ogg_mux->collect->data;
395 GstOggPadData *pad = (GstOggPadData *) walk->data;
396 if (pad->map.serialno == serialno)
405 gst_ogg_mux_generate_serialno (GstOggMux * ogg_mux)
410 serialno = g_random_int_range (0, G_MAXINT32);
411 } while (gst_ogg_mux_is_serialno_present (ogg_mux, serialno));
417 gst_ogg_mux_request_new_pad (GstElement * element,
418 GstPadTemplate * templ, const gchar * req_name)
422 GstElementClass *klass;
424 g_return_val_if_fail (templ != NULL, NULL);
426 if (templ->direction != GST_PAD_SINK)
427 goto wrong_direction;
429 g_return_val_if_fail (GST_IS_OGG_MUX (element), NULL);
430 ogg_mux = GST_OGG_MUX (element);
432 klass = GST_ELEMENT_GET_CLASS (element);
434 if (templ != gst_element_class_get_pad_template (klass, "sink_%d"))
441 if (req_name == NULL || strlen (req_name) < 6) {
442 /* no name given when requesting the pad, use random serial number */
443 serial = gst_ogg_mux_generate_serialno (ogg_mux);
445 /* parse serial number from requested padname */
446 unsigned long long_serial;
448 long_serial = strtoul (&req_name[5], &endptr, 10);
449 if ((endptr && *endptr) || (long_serial & ~0xffffffff)) {
450 GST_WARNING_OBJECT (ogg_mux, "Invalid serial number specification: %s",
454 serial = (guint32) long_serial;
456 /* create new pad with the name */
457 GST_DEBUG_OBJECT (ogg_mux, "Creating new pad for serial %d", serial);
458 name = g_strdup_printf ("sink_%d", serial);
459 newpad = gst_pad_new_from_template (templ, name);
462 /* construct our own wrapper data structure for the pad to
463 * keep track of its status */
465 GstOggPadData *oggpad;
467 oggpad = (GstOggPadData *)
468 gst_collect_pads_add_pad_full (ogg_mux->collect, newpad,
469 sizeof (GstOggPadData), gst_ogg_mux_ogg_pad_destroy_notify);
470 ogg_mux->active_pads++;
472 oggpad->map.serialno = serial;
473 ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
474 oggpad->packetno = 0;
477 /* we assume there will be some control data first for this pad */
478 oggpad->state = GST_OGG_PAD_STATE_CONTROL;
479 oggpad->new_page = TRUE;
480 oggpad->first_delta = FALSE;
481 oggpad->prev_delta = FALSE;
482 oggpad->data_pushed = FALSE;
483 oggpad->pagebuffers = g_queue_new ();
484 oggpad->map.headers = NULL;
485 oggpad->map.queued = NULL;
486 oggpad->next_granule = 0;
487 oggpad->keyframe_granule = -1;
489 gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
491 oggpad->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
492 gst_pad_set_event_function (newpad,
493 GST_DEBUG_FUNCPTR (gst_ogg_mux_sink_event));
497 /* setup some pad functions */
498 gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect);
500 /* dd the pad to the element */
501 gst_element_add_pad (element, newpad);
508 g_warning ("ogg_mux: request pad that is not a SINK pad\n");
513 g_warning ("ogg_mux: this is not our template!\n");
519 gst_ogg_mux_release_pad (GstElement * element, GstPad * pad)
523 ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
525 gst_collect_pads_remove_pad (ogg_mux->collect, pad);
526 gst_element_remove_pad (element, pad);
528 gst_object_unref (ogg_mux);
533 gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event)
537 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
541 /* disable seeking for now */
547 return gst_pad_event_default (pad, event);
551 gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
555 /* allocate space for header and body */
556 buffer = gst_buffer_new_and_alloc (page->header_len + page->body_len);
557 memcpy (GST_BUFFER_DATA (buffer), page->header, page->header_len);
558 memcpy (GST_BUFFER_DATA (buffer) + page->header_len,
559 page->body, page->body_len);
561 /* Here we set granulepos as our OFFSET_END to give easy direct access to
562 * this value later. Before we push it, we reset this to OFFSET + SIZE
563 * (see gst_ogg_mux_push_buffer). */
564 GST_BUFFER_OFFSET_END (buffer) = ogg_page_granulepos (page);
566 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
568 GST_LOG_OBJECT (mux, GST_GP_FORMAT
569 " created buffer %p from ogg page",
570 GST_GP_CAST (ogg_page_granulepos (page)), buffer);
576 gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer,
577 GstOggPadData * oggpad)
581 /* fix up OFFSET and OFFSET_END again */
582 GST_BUFFER_OFFSET (buffer) = mux->offset;
583 mux->offset += GST_BUFFER_SIZE (buffer);
584 GST_BUFFER_OFFSET_END (buffer) = mux->offset;
586 /* Ensure we have monotonically increasing timestamps in the output. */
587 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
588 gint64 run_time = GST_BUFFER_TIMESTAMP (buffer);
589 if (mux->last_ts != GST_CLOCK_TIME_NONE && run_time < mux->last_ts)
590 GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts;
592 mux->last_ts = run_time;
595 caps = gst_pad_get_negotiated_caps (mux->srcpad);
596 gst_buffer_set_caps (buffer, caps);
598 gst_caps_unref (caps);
600 return gst_pad_push (mux->srcpad, buffer);
603 /* if all queues have at least one page, dequeue the page with the lowest
606 gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret)
609 GstOggPadData *opad = NULL; /* "oldest" pad */
610 GstClockTime oldest = GST_CLOCK_TIME_NONE;
611 GstBuffer *buf = NULL;
612 gboolean ret = FALSE;
614 *flowret = GST_FLOW_OK;
616 walk = mux->collect->data;
618 GstOggPadData *pad = (GstOggPadData *) walk->data;
620 /* We need each queue to either be at EOS, or have one or more pages
621 * available with a set granulepos (i.e. not -1), otherwise we don't have
622 * enough data yet to determine which stream needs to go next for correct
624 if (pad->pagebuffers->length == 0) {
626 GST_LOG_OBJECT (pad->collect.pad,
627 "pad is EOS, skipping for dequeue decision");
629 GST_LOG_OBJECT (pad->collect.pad,
630 "no pages in this queue, can't dequeue");
634 /* We then need to check for a non-negative granulepos */
636 gboolean valid = FALSE;
638 for (i = 0; i < pad->pagebuffers->length; i++) {
639 buf = g_queue_peek_nth (pad->pagebuffers, i);
640 /* Here we check the OFFSET_END, which is actually temporarily the
641 * granulepos value for this buffer */
642 if (GST_BUFFER_OFFSET_END (buf) != -1) {
648 GST_LOG_OBJECT (pad->collect.pad,
649 "No page timestamps in queue, can't dequeue");
654 walk = g_slist_next (walk);
657 walk = mux->collect->data;
659 GstOggPadData *pad = (GstOggPadData *) walk->data;
661 /* any page with a granulepos of -1 can be pushed immediately.
662 * TODO: it CAN be, but it seems silly to do so? */
663 buf = g_queue_peek_head (pad->pagebuffers);
664 while (buf && GST_BUFFER_OFFSET_END (buf) == -1) {
665 GST_LOG_OBJECT (pad->collect.pad, "[gp -1] pushing page");
666 g_queue_pop_head (pad->pagebuffers);
667 *flowret = gst_ogg_mux_push_buffer (mux, buf, pad);
668 buf = g_queue_peek_head (pad->pagebuffers);
673 /* if no oldest buffer yet, take this one */
674 if (oldest == GST_CLOCK_TIME_NONE) {
675 GST_LOG_OBJECT (mux, "no oldest yet, taking buffer %p from pad %"
676 GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
677 buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
678 oldest = GST_BUFFER_OFFSET (buf);
681 /* if we have an oldest, compare with this one */
682 if (GST_BUFFER_OFFSET (buf) < oldest) {
683 GST_LOG_OBJECT (mux, "older buffer %p, taking from pad %"
684 GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
685 buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
686 oldest = GST_BUFFER_OFFSET (buf);
691 walk = g_slist_next (walk);
694 if (oldest != GST_CLOCK_TIME_NONE) {
696 buf = g_queue_pop_head (opad->pagebuffers);
697 GST_LOG_OBJECT (opad->collect.pad,
698 GST_GP_FORMAT " pushing oldest page buffer %p (granulepos time %"
699 GST_TIME_FORMAT ")", GST_BUFFER_OFFSET_END (buf), buf,
700 GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
701 *flowret = gst_ogg_mux_push_buffer (mux, buf, opad);
708 /* put the given ogg page on a per-pad queue, timestamping it correctly.
709 * after that, dequeue and push as many pages as possible.
710 * Caller should make sure:
711 * pad->timestamp was set with the timestamp of the first packet put
713 * pad->timestamp_end was set with the timestamp + duration of the last packet
715 * pad->gp_time was set with the time matching the gp of the last
716 * packet put on the page
718 * will also reset timestamp and timestamp_end, so caller func can restart
722 gst_ogg_mux_pad_queue_page (GstOggMux * mux, GstOggPadData * pad,
723 ogg_page * page, gboolean delta)
726 GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);
728 /* take the timestamp of the first packet on this page */
729 GST_BUFFER_TIMESTAMP (buffer) = pad->timestamp;
730 GST_BUFFER_DURATION (buffer) = pad->timestamp_end - pad->timestamp;
731 /* take the gp time of the last completed packet on this page */
732 GST_BUFFER_OFFSET (buffer) = pad->gp_time;
734 /* the next page will start where the current page's end time leaves off */
735 pad->timestamp = pad->timestamp_end;
737 g_queue_push_tail (pad->pagebuffers, buffer);
738 GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
739 " queued buffer page %p (gp time %"
740 GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT
741 "), %d page buffers queued", GST_GP_CAST (ogg_page_granulepos (page)),
742 buffer, GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)),
743 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
744 g_queue_get_length (pad->pagebuffers));
746 while (gst_ogg_mux_dequeue_page (mux, &ret)) {
747 if (ret != GST_FLOW_OK)
755 * Given two pads, compare the buffers queued on it.
757 * 0 if they have an equal priority
758 * -1 if the first is better
759 * 1 if the second is better
760 * Priority decided by: a) validity, b) older timestamp, c) smaller number
764 gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPadData * first,
765 GstOggPadData * second)
767 guint64 firsttime, secondtime;
769 /* if the first pad doesn't contain anything or is even NULL, return
770 * the second pad as best candidate and vice versa */
776 /* no timestamp on first buffer, it must go first */
777 firsttime = GST_BUFFER_TIMESTAMP (first->buffer);
778 if (firsttime == GST_CLOCK_TIME_NONE)
781 /* no timestamp on second buffer, it must go first */
782 secondtime = GST_BUFFER_TIMESTAMP (second->buffer);
783 if (secondtime == GST_CLOCK_TIME_NONE)
786 /* first buffer has higher timestamp, second one should go first */
787 if (secondtime < firsttime)
789 /* second buffer has higher timestamp, first one should go first */
790 else if (secondtime > firsttime)
793 /* buffers with equal timestamps, prefer the pad that has the
794 * least number of pages muxed */
795 if (second->pageno < first->pageno)
797 else if (second->pageno > first->pageno)
801 /* same priority if all of the above failed */
806 gst_ogg_mux_decorate_buffer (GstOggMux * ogg_mux, GstOggPadData * pad,
810 gint64 duration, granule, limit;
811 GstClockTime next_time;
812 GstClockTimeDiff diff;
815 /* ensure messing with metadata is ok */
816 buf = gst_buffer_make_metadata_writable (buf);
818 /* convert time to running time, so we need no longer bother about that */
819 time = GST_BUFFER_TIMESTAMP (buf);
820 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
821 time = gst_segment_to_running_time (&pad->segment, GST_FORMAT_TIME, time);
822 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
823 gst_buffer_unref (buf);
826 GST_BUFFER_TIMESTAMP (buf) = time;
830 /* now come up with granulepos stuff corresponding to time */
831 if (!pad->have_type ||
832 pad->map.granulerate_n <= 0 || pad->map.granulerate_d <= 0)
835 packet.packet = GST_BUFFER_DATA (buf);
836 packet.bytes = GST_BUFFER_SIZE (buf);
837 duration = gst_ogg_stream_get_packet_duration (&pad->map, &packet);
839 /* give up if no duration can be determined, relying on upstream */
840 if (G_UNLIKELY (duration < 0)) {
841 /* well, if some day we really could handle sparse input ... */
842 if (pad->map.is_sparse) {
847 GST_WARNING_OBJECT (pad->collect.pad,
848 "failed to determine packet duration");
852 GST_LOG_OBJECT (pad->collect.pad, "buffer ts %" GST_TIME_FORMAT
853 ", duration %" GST_TIME_FORMAT ", granule duration %" G_GINT64_FORMAT,
854 GST_TIME_ARGS (time), GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
857 /* determine granule corresponding to time,
858 * using the inverse of oggdemux' granule -> time */
860 /* see if interpolated granule matches good enough */
861 granule = pad->next_granule;
862 next_time = gst_ogg_stream_granule_to_time (&pad->map, pad->next_granule);
863 diff = GST_CLOCK_DIFF (next_time, time);
865 /* we tolerate deviation up to configured or within granule granularity */
866 limit = gst_ogg_stream_granule_to_time (&pad->map, 1) / 2;
867 limit = MAX (limit, ogg_mux->max_tolerance);
869 GST_LOG_OBJECT (pad->collect.pad, "expected granule %" G_GINT64_FORMAT " == "
870 "time %" GST_TIME_FORMAT " --> ts diff %" GST_TIME_FORMAT
871 " < tolerance %" GST_TIME_FORMAT " (?)",
872 granule, GST_TIME_ARGS (next_time), GST_TIME_ARGS (ABS (diff)),
873 GST_TIME_ARGS (limit));
876 /* if not good enough, determine granule based on time */
877 if (diff > limit || diff < -limit) {
878 granule = gst_util_uint64_scale_round (time, pad->map.granulerate_n,
879 GST_SECOND * pad->map.granulerate_d);
880 GST_DEBUG_OBJECT (pad->collect.pad,
881 "resyncing to determined granule %" G_GINT64_FORMAT, granule);
884 if (pad->map.is_ogm || pad->map.is_sparse) {
885 pad->next_granule = granule;
888 pad->next_granule = granule;
891 /* track previous keyframe */
892 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
893 pad->keyframe_granule = granule;
895 /* determine corresponding time and granulepos */
896 GST_BUFFER_OFFSET (buf) = gst_ogg_stream_granule_to_time (&pad->map, granule);
897 GST_BUFFER_OFFSET_END (buf) =
898 gst_ogg_stream_granule_to_granulepos (&pad->map, granule,
899 pad->keyframe_granule);
906 GST_DEBUG_OBJECT (pad->collect.pad, "could not determine granulepos, "
907 "falling back to upstream provided metadata");
913 /* make sure at least one buffer is queued on all pads, two if possible
915 * if pad->buffer == NULL, pad->next_buffer != NULL, then
916 * we do not know if the buffer is the last or not
917 * if pad->buffer != NULL, pad->next_buffer != NULL, then
918 * pad->buffer is not the last buffer for the pad
919 * if pad->buffer != NULL, pad->next_buffer == NULL, then
920 * pad->buffer if the last buffer for the pad
922 * returns a pointer to an oggpad that holds the best buffer, or
923 * NULL when no pad was usable. "best" means the buffer marked
924 * with the lowest timestamp. If best->buffer == NULL then either
925 * we're at EOS (popped = FALSE), or a buffer got dropped, so retry. */
926 static GstOggPadData *
927 gst_ogg_mux_queue_pads (GstOggMux * ogg_mux, gboolean * popped)
929 GstOggPadData *bestpad = NULL;
934 /* try to make sure we have a buffer from each usable pad first */
935 walk = ogg_mux->collect->data;
938 GstCollectData *data;
940 data = (GstCollectData *) walk->data;
941 pad = (GstOggPadData *) data;
943 walk = g_slist_next (walk);
945 GST_LOG_OBJECT (data->pad, "looking at pad for buffer");
947 /* try to get a new buffer for this pad if needed and possible */
948 if (pad->buffer == NULL) {
951 buf = gst_collect_pads_pop (ogg_mux->collect, data);
952 GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
954 /* On EOS we get a NULL buffer */
958 if (ogg_mux->delta_pad == NULL &&
959 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
960 ogg_mux->delta_pad = pad;
962 /* if we need headers */
963 if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
964 /* and we have one */
968 packet.packet = GST_BUFFER_DATA (buf);
969 packet.bytes = GST_BUFFER_SIZE (buf);
971 /* if we're not yet in data mode, ensure we're setup on the first packet */
972 if (!pad->have_type) {
973 /* Use headers in caps, if any; this will allow us to be resilient
974 * to starting streams on the fly, and some streams (like VP8
975 * at least) do not send headers packets, as other muxers don't
976 * expect/need them. */
978 gst_ogg_stream_setup_map_from_caps_headers (&pad->map,
979 GST_BUFFER_CAPS (buf));
981 if (!pad->have_type) {
982 /* fallback on the packet */
983 pad->have_type = gst_ogg_stream_setup_map (&pad->map, &packet);
985 if (!pad->have_type) {
986 GST_ERROR_OBJECT (pad, "mapper didn't recognise input stream "
987 "(pad caps: %" GST_PTR_FORMAT ")", GST_PAD_CAPS (pad));
989 GST_DEBUG_OBJECT (pad, "caps detected: %" GST_PTR_FORMAT,
995 is_header = gst_ogg_stream_packet_is_header (&pad->map, &packet);
996 else /* fallback (FIXME 0.11: remove IN_CAPS hack) */
997 is_header = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
1000 GST_DEBUG_OBJECT (ogg_mux,
1001 "got header buffer in control state, ignoring");
1003 pad->map.n_header_packets_seen++;
1004 gst_buffer_unref (buf);
1007 GST_DEBUG_OBJECT (ogg_mux,
1008 "got data buffer in control state, switching to data mode");
1009 /* this is a data buffer so switch to data state */
1010 pad->state = GST_OGG_PAD_STATE_DATA;
1012 /* check if this type of stream allows generating granulepos
1013 * metadata here, if not, upstream will have to provide */
1014 if (gst_ogg_stream_granule_to_granulepos (&pad->map, 1, 1) < 0) {
1015 GST_WARNING_OBJECT (data->pad, "can not generate metadata; "
1016 "relying on upstream");
1017 /* disable metadata code path, otherwise not used anyway */
1018 pad->map.granulerate_n = 0;
1023 /* so now we should have a real data packet;
1024 * see that it is properly decorated */
1025 if (G_LIKELY (buf)) {
1026 buf = gst_ogg_mux_decorate_buffer (ogg_mux, pad, buf);
1027 if (G_UNLIKELY (!buf))
1028 GST_DEBUG_OBJECT (data->pad, "buffer clipped");
1035 /* we should have a buffer now, see if it is the best pad to
1038 if (gst_ogg_mux_compare_pads (ogg_mux, bestpad, pad) > 0) {
1039 GST_LOG_OBJECT (data->pad,
1040 "new best pad, with buffer %" GST_PTR_FORMAT, pad->buffer);
1051 gst_ogg_mux_get_headers (GstOggPadData * pad)
1054 GstStructure *structure;
1058 thepad = pad->collect.pad;
1060 GST_LOG_OBJECT (thepad, "getting headers");
1062 caps = gst_pad_get_negotiated_caps (thepad);
1064 const GValue *streamheader;
1066 structure = gst_caps_get_structure (caps, 0);
1067 streamheader = gst_structure_get_value (structure, "streamheader");
1068 if (streamheader != NULL) {
1069 GST_LOG_OBJECT (thepad, "got header");
1070 if (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
1071 GArray *bufarr = g_value_peek_pointer (streamheader);
1074 GST_LOG_OBJECT (thepad, "got fixed list");
1076 for (i = 0; i < bufarr->len; i++) {
1077 GValue *bufval = &g_array_index (bufarr, GValue, i);
1079 GST_LOG_OBJECT (thepad, "item %d", i);
1080 if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
1081 GstBuffer *buf = g_value_peek_pointer (bufval);
1083 GST_LOG_OBJECT (thepad, "adding item %d to header list", i);
1085 gst_buffer_ref (buf);
1086 res = g_list_append (res, buf);
1090 GST_LOG_OBJECT (thepad, "streamheader is not fixed list");
1093 } else if (gst_structure_has_name (structure, "video/x-dirac")) {
1094 res = g_list_append (res, pad->buffer);
1097 GST_LOG_OBJECT (thepad, "caps don't have streamheader");
1099 gst_caps_unref (caps);
1101 GST_LOG_OBJECT (thepad, "got empty caps as negotiated format");
1107 gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
1109 GstStructure *structure;
1110 GValue array = { 0 };
1111 GList *walk = buffers;
1113 caps = gst_caps_make_writable (caps);
1115 structure = gst_caps_get_structure (caps, 0);
1117 /* put buffers in a fixed list */
1118 g_value_init (&array, GST_TYPE_ARRAY);
1121 GstBuffer *buf = GST_BUFFER (walk->data);
1123 GValue value = { 0 };
1128 GST_LOG ("Setting IN_CAPS on buffer of length %d", GST_BUFFER_SIZE (buf));
1129 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
1131 g_value_init (&value, GST_TYPE_BUFFER);
1132 copy = gst_buffer_copy (buf);
1133 gst_value_set_buffer (&value, copy);
1134 gst_buffer_unref (copy);
1135 gst_value_array_append_value (&array, &value);
1136 g_value_unset (&value);
1138 gst_structure_set_value (structure, "streamheader", &array);
1139 g_value_unset (&array);
1145 gst_ogg_mux_create_header_packet_with_flags (ogg_packet * packet,
1146 GstBuffer * buf, gboolean bos, gboolean eos)
1148 packet->packet = GST_BUFFER_DATA (buf);
1149 packet->bytes = GST_BUFFER_SIZE (buf);
1150 packet->granulepos = 0;
1151 /* mark BOS and packet number */
1152 packet->b_o_s = bos;
1154 packet->e_o_s = eos;
1158 gst_ogg_mux_create_header_packet (ogg_packet * packet, GstBuffer * buf,
1159 GstOggPadData * pad)
1161 gst_ogg_mux_create_header_packet_with_flags (packet, buf, pad->packetno == 0,
1163 packet->packetno = pad->packetno++;
1167 gst_ogg_mux_submit_skeleton_header_packet (GstOggMux * mux,
1168 ogg_stream_state * os, GstBuffer * buf, gboolean bos, gboolean eos)
1171 gst_ogg_mux_create_header_packet_with_flags (&packet, buf, bos, eos);
1172 ogg_stream_packetin (os, &packet);
1173 gst_buffer_unref (buf);
1177 gst_ogg_mux_make_fishead (GstOggMux * mux, ogg_stream_state * os)
1182 GST_DEBUG_OBJECT (mux, "Creating fishead");
1184 fishead = gst_buffer_new_and_alloc (64);
1185 gst_byte_writer_init_with_buffer (&bw, fishead, FALSE);
1186 gst_byte_writer_put_string_utf8 (&bw, "fishead");
1187 gst_byte_writer_put_int16_le (&bw, 3); /* version major */
1188 gst_byte_writer_put_int16_le (&bw, 0); /* version minor */
1189 gst_byte_writer_put_int64_le (&bw, 0); /* presentation time numerator */
1190 gst_byte_writer_put_int64_le (&bw, 1000); /* ...and denominator */
1191 gst_byte_writer_put_int64_le (&bw, 0); /* base time numerator */
1192 gst_byte_writer_put_int64_le (&bw, 1000); /* ...and denominator */
1193 gst_byte_writer_fill (&bw, ' ', 20); /* UTC time */
1194 g_assert (gst_byte_writer_get_pos (&bw) == GST_BUFFER_SIZE (fishead));
1195 gst_ogg_mux_submit_skeleton_header_packet (mux, os, fishead, 1, 0);
1199 gst_ogg_mux_byte_writer_put_string_utf8 (GstByteWriter * bw, const char *s)
1201 gst_byte_writer_put_data (bw, (const guint8 *) s, strlen (s));
1205 gst_ogg_mux_add_fisbone_message_header (GstOggMux * mux, GstByteWriter * bw,
1206 const char *tag, const char *value)
1208 /* It is valid to pass NULL as the value to omit the tag */
1211 GST_DEBUG_OBJECT (mux, "Adding fisbone message header %s: %s", tag, value);
1212 gst_ogg_mux_byte_writer_put_string_utf8 (bw, tag);
1213 gst_ogg_mux_byte_writer_put_string_utf8 (bw, ": ");
1214 gst_ogg_mux_byte_writer_put_string_utf8 (bw, value);
1215 gst_ogg_mux_byte_writer_put_string_utf8 (bw, "\r\n");
1219 gst_ogg_mux_add_fisbone_message_header_from_tags (GstOggMux * mux,
1220 GstByteWriter * bw, const char *header, const char *tag,
1221 const GstTagList * tags)
1224 guint size = gst_tag_list_get_tag_size (tags, tag), n;
1225 GST_DEBUG_OBJECT (mux, "Found %u tags for name %s", size, tag);
1228 s = g_string_new ("");
1229 for (n = 0; n < size; ++n) {
1232 g_string_append (s, ", ");
1233 gst_tag_list_get_string_index (tags, tag, n, &tmp);
1234 g_string_append (s, tmp);
1236 gst_ogg_mux_add_fisbone_message_header (mux, bw, header, s->str);
1237 g_string_free (s, FALSE);
1240 /* This is a basic placeholder to generate roles for the tracks.
1241 For tracks with more than one video, both video tracks will get
1242 tagged with a "video/main" role, but we have no way of knowing
1243 which one is the main one, if any. We could just pick one. For
1244 audio, it's more complicated as we don't know which is music,
1245 which is dubbing, etc. For kate, we could take a pretty good
1246 guess based on the category, as role essentially is category.
1247 For now, leave this as is. */
1249 gst_ogg_mux_get_default_role (GstOggPadData * pad)
1251 const char *type = gst_ogg_stream_get_media_type (&pad->map);
1253 if (!strncmp (type, "video/", strlen ("video/")))
1254 return "video/main";
1255 if (!strncmp (type, "audio/", strlen ("audio/")))
1256 return "audio/main";
1257 if (!strcmp (type + strlen (type) - strlen ("kate"), "kate"))
1258 return "text/caption";
1264 gst_ogg_mux_make_fisbone (GstOggMux * mux, ogg_stream_state * os,
1265 GstOggPadData * pad)
1269 GST_DEBUG_OBJECT (mux,
1270 "Creating %s fisbone for serial %08x",
1271 gst_ogg_stream_get_media_type (&pad->map), pad->map.serialno);
1273 gst_byte_writer_init (&bw);
1274 gst_byte_writer_put_string_utf8 (&bw, "fisbone");
1275 gst_byte_writer_put_int32_le (&bw, 44); /* offset to message headers */
1276 gst_byte_writer_put_uint32_le (&bw, pad->map.serialno);
1277 gst_byte_writer_put_uint32_le (&bw, pad->map.n_header_packets);
1278 gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_n);
1279 gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_d);
1280 gst_byte_writer_put_uint64_le (&bw, 0); /* base granule */
1281 gst_byte_writer_put_uint32_le (&bw, pad->map.preroll);
1282 gst_byte_writer_put_uint8 (&bw, pad->map.granuleshift);
1283 gst_byte_writer_fill (&bw, 0, 3); /* padding */
1284 /* message header fields - MIME type for now */
1285 gst_ogg_mux_add_fisbone_message_header (mux, &bw, "Content-Type",
1286 gst_ogg_stream_get_media_type (&pad->map));
1287 gst_ogg_mux_add_fisbone_message_header (mux, &bw, "Role",
1288 gst_ogg_mux_get_default_role (pad));
1289 gst_ogg_mux_add_fisbone_message_header_from_tags (mux, &bw, "Language",
1290 GST_TAG_LANGUAGE_CODE, pad->tags);
1291 gst_ogg_mux_add_fisbone_message_header_from_tags (mux, &bw, "Title",
1292 GST_TAG_TITLE, pad->tags);
1294 gst_ogg_mux_submit_skeleton_header_packet (mux, os,
1295 gst_byte_writer_reset_and_get_buffer (&bw), 0, 0);
1299 gst_ogg_mux_make_fistail (GstOggMux * mux, ogg_stream_state * os)
1301 GST_DEBUG_OBJECT (mux, "Creating fistail");
1303 gst_ogg_mux_submit_skeleton_header_packet (mux, os,
1304 gst_buffer_new_and_alloc (0), 0, 1);
1308 * For each pad we need to write out one (small) header in one
1309 * page that allows decoders to identify the type of the stream.
1310 * After that we need to write out all extra info for the decoders.
1311 * In the case of a codec that also needs data as configuration, we can
1312 * find that info in the streamcaps.
1313 * After writing the headers we must start a new page for the data.
1315 static GstFlowReturn
1316 gst_ogg_mux_send_headers (GstOggMux * mux)
1319 GList *hbufs, *hwalk;
1323 ogg_stream_state skeleton_stream;
1328 GST_LOG_OBJECT (mux, "collecting headers");
1330 walk = mux->collect->data;
1335 pad = (GstOggPadData *) walk->data;
1336 thepad = pad->collect.pad;
1338 walk = g_slist_next (walk);
1340 GST_LOG_OBJECT (mux, "looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
1342 /* if the pad has no buffer, we don't care */
1343 if (pad->buffer == NULL)
1346 /* now figure out the headers */
1347 pad->map.headers = gst_ogg_mux_get_headers (pad);
1350 GST_LOG_OBJECT (mux, "creating BOS pages");
1351 walk = mux->collect->data;
1358 GstStructure *structure;
1361 pad = (GstOggPadData *) walk->data;
1362 thepad = pad->collect.pad;
1363 caps = gst_pad_get_negotiated_caps (thepad);
1364 structure = gst_caps_get_structure (caps, 0);
1370 GST_LOG_OBJECT (thepad, "looping over headers");
1372 if (pad->map.headers) {
1373 buf = GST_BUFFER (pad->map.headers->data);
1374 pad->map.headers = g_list_remove (pad->map.headers, buf);
1375 } else if (pad->buffer) {
1377 gst_buffer_ref (buf);
1379 /* fixme -- should be caught in the previous list traversal. */
1380 GST_OBJECT_LOCK (thepad);
1381 g_critical ("No headers or buffers on pad %s:%s",
1382 GST_DEBUG_PAD_NAME (thepad));
1383 GST_OBJECT_UNLOCK (thepad);
1387 /* create a packet from the buffer */
1388 gst_ogg_mux_create_header_packet (&packet, buf, pad);
1390 /* swap the packet in */
1391 ogg_stream_packetin (&pad->map.stream, &packet);
1392 gst_buffer_unref (buf);
1394 GST_LOG_OBJECT (thepad, "flushing out BOS page");
1395 if (!ogg_stream_flush (&pad->map.stream, &page))
1396 g_critical ("Could not flush BOS page");
1398 hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1400 GST_LOG_OBJECT (mux, "swapped out page with mime type %s",
1401 gst_structure_get_name (structure));
1403 /* quick hack: put video pages at the front.
1404 * Ideally, we would have a settable enum for which Ogg
1405 * profile we work with, and order based on that.
1406 * (FIXME: if there is more than one video stream, shouldn't we only put
1407 * one's BOS into the first page, followed by an audio stream's BOS, and
1408 * only then followed by the remaining video and audio streams?) */
1409 if (pad->map.is_video) {
1410 GST_DEBUG_OBJECT (thepad, "putting %s page at the front",
1411 gst_structure_get_name (structure));
1412 hbufs = g_list_prepend (hbufs, hbuf);
1414 hbufs = g_list_append (hbufs, hbuf);
1417 gst_caps_unref (caps);
1420 /* The Skeleton BOS goes first - even before the video that went first before */
1421 if (mux->use_skeleton) {
1422 ogg_stream_init (&skeleton_stream, gst_ogg_mux_generate_serialno (mux));
1423 gst_ogg_mux_make_fishead (mux, &skeleton_stream);
1424 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1425 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1426 hbufs = g_list_append (hbufs, hbuf);
1430 GST_LOG_OBJECT (mux, "creating next headers");
1431 walk = mux->collect->data;
1436 pad = (GstOggPadData *) walk->data;
1437 thepad = pad->collect.pad;
1441 if (mux->use_skeleton)
1442 gst_ogg_mux_make_fisbone (mux, &skeleton_stream, pad);
1444 GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
1445 GST_DEBUG_PAD_NAME (thepad));
1447 hwalk = pad->map.headers;
1449 GstBuffer *buf = GST_BUFFER (hwalk->data);
1452 hwalk = hwalk->next;
1454 /* create a packet from the buffer */
1455 gst_ogg_mux_create_header_packet (&packet, buf, pad);
1457 /* swap the packet in */
1458 ogg_stream_packetin (&pad->map.stream, &packet);
1459 gst_buffer_unref (buf);
1461 /* if last header, flush page */
1462 if (hwalk == NULL) {
1463 GST_LOG_OBJECT (mux,
1464 "flushing page as packet %" G_GUINT64_FORMAT " is first or "
1465 "last packet", (guint64) packet.packetno);
1466 while (ogg_stream_flush (&pad->map.stream, &page)) {
1467 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1469 GST_LOG_OBJECT (mux, "swapped out page");
1470 hbufs = g_list_append (hbufs, hbuf);
1473 GST_LOG_OBJECT (mux, "try to swap out page");
1474 /* just try to swap out a page then */
1475 while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1476 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1478 GST_LOG_OBJECT (mux, "swapped out page");
1479 hbufs = g_list_append (hbufs, hbuf);
1483 g_list_free (pad->map.headers);
1484 pad->map.headers = NULL;
1487 if (mux->use_skeleton) {
1488 /* flush accumulated fisbones, the fistail must be on a separate page */
1489 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1490 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1491 hbufs = g_list_append (hbufs, hbuf);
1493 gst_ogg_mux_make_fistail (mux, &skeleton_stream);
1494 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1495 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1496 hbufs = g_list_append (hbufs, hbuf);
1498 ogg_stream_clear (&skeleton_stream);
1501 /* hbufs holds all buffers for the headers now */
1503 /* create caps with the buffers */
1504 caps = gst_pad_get_caps (mux->srcpad);
1506 caps = gst_ogg_mux_set_header_on_caps (caps, hbufs);
1507 gst_pad_set_caps (mux->srcpad, caps);
1508 gst_caps_unref (caps);
1510 /* and send the buffers */
1511 while (hbufs != NULL) {
1512 GstBuffer *buf = GST_BUFFER (hbufs->data);
1514 hbufs = g_list_delete_link (hbufs, hbufs);
1516 if ((ret = gst_ogg_mux_push_buffer (mux, buf, NULL)) != GST_FLOW_OK)
1519 /* free any remaining nodes/buffers in case we couldn't push them */
1520 g_list_foreach (hbufs, (GFunc) gst_mini_object_unref, NULL);
1521 g_list_free (hbufs);
1526 /* this function is called to process data on the best pending pad.
1530 * 1) store the selected pad and keep on pulling until we fill a
1531 * complete ogg page or the ogg page is filled above the max-delay
1532 * threshold. This is needed because the ogg spec says that
1533 * you should fill a complete page with data from the same logical
1534 * stream. When the page is filled, go back to 1).
1535 * 2) before filling a page, read ahead one more buffer to see if this
1536 * packet is the last of the stream. We need to do this because the ogg
1537 * spec mandates that the last packet should have the EOS flag set before
1538 * sending it to ogg. if pad->buffer is NULL we need to wait to find out
1539 * whether there are any more buffers.
1540 * 3) pages get queued on a per-pad queue. Every time a page is queued, a
1541 * dequeue is called, which will dequeue the oldest page on any pad, provided
1542 * that ALL pads have at least one marked page in the queue (or remaining
1545 static GstFlowReturn
1546 gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
1548 GstFlowReturn ret = GST_FLOW_OK;
1549 gboolean delta_unit;
1550 gint64 granulepos = 0;
1551 GstClockTime timestamp, gp_time;
1552 GstBuffer *next_buf;
1554 GST_LOG_OBJECT (ogg_mux, "best pad %" GST_PTR_FORMAT
1555 ", currently pulling from %" GST_PTR_FORMAT, best->collect.pad,
1556 ogg_mux->pulling ? ogg_mux->pulling->collect.pad : NULL);
1558 if (ogg_mux->pulling) {
1559 next_buf = gst_collect_pads_peek (ogg_mux->collect,
1560 &ogg_mux->pulling->collect);
1562 ogg_mux->pulling->eos = FALSE;
1563 gst_buffer_unref (next_buf);
1565 GST_DEBUG_OBJECT (ogg_mux->pulling->collect.pad, "setting eos to true");
1566 ogg_mux->pulling->eos = TRUE;
1570 /* We could end up pushing from the best pad instead, so check that
1572 if (best && best != ogg_mux->pulling) {
1573 next_buf = gst_collect_pads_peek (ogg_mux->collect, &best->collect);
1576 gst_buffer_unref (next_buf);
1578 GST_DEBUG_OBJECT (best->collect.pad, "setting eos to true");
1583 /* if we were already pulling from one pad, but the new "best" buffer is
1584 * from another pad, we need to check if we have reason to flush a page
1585 * for the pad we were pulling from before */
1586 if (ogg_mux->pulling && best &&
1587 ogg_mux->pulling != best && ogg_mux->pulling->buffer) {
1588 GstOggPadData *pad = ogg_mux->pulling;
1589 GstClockTime last_ts = GST_BUFFER_END_TIME (pad->buffer);
1591 /* if the next packet in the current page is going to make the page
1592 * too long, we need to flush */
1593 if (last_ts > ogg_mux->next_ts + ogg_mux->max_delay) {
1596 GST_LOG_OBJECT (pad->collect.pad,
1597 GST_GP_FORMAT " stored packet %" G_GINT64_FORMAT
1598 " will make page too long, flushing",
1599 GST_BUFFER_OFFSET_END (pad->buffer),
1600 (gint64) pad->map.stream.packetno);
1602 while (ogg_stream_flush (&pad->map.stream, &page)) {
1603 /* end time of this page is the timestamp of the next buffer */
1604 ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
1605 /* Place page into the per-pad queue */
1606 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1608 /* increment the page number counter */
1610 /* mark other pages as delta */
1611 pad->first_delta = TRUE;
1613 pad->new_page = TRUE;
1614 ogg_mux->pulling = NULL;
1618 /* if we don't know which pad to pull on, use the best one */
1619 if (ogg_mux->pulling == NULL) {
1620 ogg_mux->pulling = best;
1621 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from best pad");
1623 /* remember timestamp and gp time of first buffer for this new pad */
1624 if (ogg_mux->pulling != NULL) {
1625 ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer);
1626 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "updated times, next ts %"
1627 GST_TIME_FORMAT, GST_TIME_ARGS (ogg_mux->next_ts));
1629 /* no pad to pull on, send EOS */
1630 gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1631 return GST_FLOW_WRONG_STATE;
1635 if (ogg_mux->need_headers) {
1636 ret = gst_ogg_mux_send_headers (ogg_mux);
1637 ogg_mux->need_headers = FALSE;
1640 /* we are pulling from a pad, continue to do so until a page
1641 * has been filled and queued */
1642 if (ogg_mux->pulling != NULL) {
1645 GstBuffer *buf, *tmpbuf;
1646 GstOggPadData *pad = ogg_mux->pulling;
1648 gboolean force_flush;
1650 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from pad");
1652 /* now see if we have a buffer */
1655 GST_DEBUG_OBJECT (ogg_mux, "pad was EOS");
1656 ogg_mux->pulling = NULL;
1660 delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
1661 duration = GST_BUFFER_DURATION (buf);
1663 /* if the current "next timestamp" on the pad is unset, then this is the
1664 * first packet on the new page. Update our pad's page timestamp */
1665 if (ogg_mux->pulling->timestamp == GST_CLOCK_TIME_NONE) {
1666 ogg_mux->pulling->timestamp = GST_BUFFER_TIMESTAMP (buf);
1667 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad,
1668 "updated pad timestamp to %" GST_TIME_FORMAT,
1669 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
1671 /* create a packet from the buffer */
1672 packet.packet = GST_BUFFER_DATA (buf);
1673 packet.bytes = GST_BUFFER_SIZE (buf);
1674 packet.granulepos = GST_BUFFER_OFFSET_END (buf);
1675 if (packet.granulepos == -1)
1676 packet.granulepos = 0;
1677 /* mark BOS and packet number */
1678 packet.b_o_s = (pad->packetno == 0);
1679 packet.packetno = pad->packetno++;
1680 GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
1681 " packet %" G_GINT64_FORMAT " (%ld bytes) created from buffer",
1682 GST_GP_CAST (packet.granulepos), (gint64) packet.packetno,
1685 packet.e_o_s = ogg_mux->pulling->eos ? 1 : 0;
1688 /* we flush when we see a new keyframe */
1689 force_flush = (pad->prev_delta && !delta_unit)
1690 || pad->map.always_flush_page;
1691 if (duration != -1) {
1692 pad->duration += duration;
1693 /* if page duration exceeds max, flush page */
1694 if (pad->duration > ogg_mux->max_page_delay) {
1700 if (GST_BUFFER_IS_DISCONT (buf)) {
1701 if (pad->data_pushed) {
1702 GST_LOG_OBJECT (pad->collect.pad, "got discont");
1704 /* No public API for this; hack things in */
1705 pad->map.stream.pageno++;
1708 GST_LOG_OBJECT (pad->collect.pad, "discont at stream start");
1712 /* flush the currently built page if necessary */
1714 GST_LOG_OBJECT (pad->collect.pad,
1715 GST_GP_FORMAT " forced flush of page before this packet",
1716 GST_BUFFER_OFFSET_END (pad->buffer));
1717 while (ogg_stream_flush (&pad->map.stream, &page)) {
1718 /* end time of this page is the timestamp of the next buffer */
1719 ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
1720 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1723 /* increment the page number counter */
1725 /* mark other pages as delta */
1726 pad->first_delta = TRUE;
1728 pad->new_page = TRUE;
1731 /* if this is the first packet of a new page figure out the delta flag */
1732 if (pad->new_page) {
1734 /* mark the page as delta */
1735 pad->first_delta = TRUE;
1737 /* got a keyframe */
1738 if (ogg_mux->delta_pad == pad) {
1739 /* if we get it on the pad with deltaunits,
1740 * we mark the page as non delta */
1741 pad->first_delta = FALSE;
1742 } else if (ogg_mux->delta_pad != NULL) {
1743 /* if there are pads with delta frames, we
1744 * must mark this one as delta */
1745 pad->first_delta = TRUE;
1747 pad->first_delta = FALSE;
1750 pad->new_page = FALSE;
1753 /* save key unit to track delta->key unit transitions */
1754 pad->prev_delta = delta_unit;
1756 /* swap the packet in */
1757 if (packet.e_o_s == 1)
1758 GST_DEBUG_OBJECT (pad->collect.pad, "swapping in EOS packet");
1759 if (packet.b_o_s == 1)
1760 GST_DEBUG_OBJECT (pad->collect.pad, "swapping in BOS packet");
1762 ogg_stream_packetin (&pad->map.stream, &packet);
1763 pad->data_pushed = TRUE;
1765 gp_time = GST_BUFFER_OFFSET (pad->buffer);
1766 granulepos = GST_BUFFER_OFFSET_END (pad->buffer);
1767 timestamp = GST_BUFFER_TIMESTAMP (pad->buffer);
1769 GST_LOG_OBJECT (pad->collect.pad,
1770 GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", gp time %"
1771 GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT " packetin'd",
1772 granulepos, (gint64) packet.packetno, GST_TIME_ARGS (gp_time),
1773 GST_TIME_ARGS (timestamp));
1774 /* don't need the old buffer anymore */
1775 gst_buffer_unref (pad->buffer);
1776 /* store new readahead buffer */
1777 pad->buffer = tmpbuf;
1779 /* let ogg write out the pages now. The packet we got could end
1780 * up in more than one page so we need to write them all */
1781 if (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1782 /* we have a new page, so we need to timestamp it correctly.
1783 * if this fresh packet ends on this page, then the page's granulepos
1784 * comes from that packet, and we should set this buffer's timestamp */
1786 GST_LOG_OBJECT (pad->collect.pad,
1787 GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", time %"
1788 GST_TIME_FORMAT ") caused new page",
1789 granulepos, (gint64) packet.packetno, GST_TIME_ARGS (timestamp));
1790 GST_LOG_OBJECT (pad->collect.pad,
1791 GST_GP_FORMAT " new page %ld",
1792 GST_GP_CAST (ogg_page_granulepos (&page)), pad->map.stream.pageno);
1794 if (ogg_page_granulepos (&page) == granulepos) {
1795 /* the packet we streamed in finishes on the current page,
1796 * because the page's granulepos is the granulepos of the last
1797 * packet completed on that page,
1798 * so update the timestamp that we will give to the page */
1799 GST_LOG_OBJECT (pad->collect.pad,
1801 " packet finishes on current page, updating gp time to %"
1802 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (gp_time));
1803 pad->gp_time = gp_time;
1805 GST_LOG_OBJECT (pad->collect.pad,
1807 " packet spans beyond current page, keeping old gp time %"
1808 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (pad->gp_time));
1812 /* end time of this page is the timestamp of the next buffer */
1813 pad->timestamp_end = timestamp;
1814 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, pad->first_delta);
1816 /* mark next pages as delta */
1817 pad->first_delta = TRUE;
1819 /* use an inner loop here to flush the remaining pages and
1820 * mark them as delta frames as well */
1821 while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1822 if (ogg_page_granulepos (&page) == granulepos) {
1823 /* the page has taken up the new packet completely, which means
1824 * the packet ends the page and we can update the gp time
1825 * before pushing out */
1826 pad->gp_time = gp_time;
1829 /* we have a complete page now, we can push the page
1830 * and make sure to pull on a new pad the next time around */
1831 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1833 /* increment the page number counter */
1836 /* need a new page as well */
1837 pad->new_page = TRUE;
1839 /* we're done pulling on this pad, make sure to choose a new
1840 * pad for pulling in the next iteration */
1841 ogg_mux->pulling = NULL;
1844 /* Update the gp time, if necessary, since any future page will have at
1845 * least this gp time.
1847 if (pad->gp_time < gp_time) {
1848 pad->gp_time = gp_time;
1849 GST_LOG_OBJECT (pad->collect.pad,
1850 "Updated running gp time of pad %" GST_PTR_FORMAT
1851 " to %" GST_TIME_FORMAT, pad->collect.pad, GST_TIME_ARGS (gp_time));
1860 * Checks if all pads are EOS'd by peeking.
1862 * Returns TRUE if all pads are EOS.
1865 all_pads_eos (GstCollectPads * pads)
1871 GstOggPadData *oggpad = (GstOggPadData *) walk->data;
1873 GST_DEBUG_OBJECT (oggpad->collect.pad,
1874 "oggpad %p eos %d", oggpad, oggpad->eos);
1876 if (oggpad->eos == FALSE)
1879 walk = g_slist_next (walk);
1885 /* This function is called when there is data on all pads.
1887 * It finds a pad to pull on, this is done by looking at the buffers
1888 * to decide which one to use, and using the 'oldest' one first. It then calls
1889 * gst_ogg_mux_process_best_pad() to process as much data as possible.
1891 * If all the pads have received EOS, it flushes out all data by continually
1892 * getting the best pad and calling gst_ogg_mux_process_best_pad() until they
1893 * are all empty, and then sends EOS.
1895 static GstFlowReturn
1896 gst_ogg_mux_collected (GstCollectPads * pads, GstOggMux * ogg_mux)
1898 GstOggPadData *best;
1902 GST_LOG_OBJECT (ogg_mux, "collected");
1904 /* queue buffers on all pads; find a buffer with the lowest timestamp */
1905 best = gst_ogg_mux_queue_pads (ogg_mux, &popped);
1910 if (best == NULL || best->buffer == NULL) {
1911 /* This is not supposed to happen */
1912 return GST_FLOW_ERROR;
1915 ret = gst_ogg_mux_process_best_pad (ogg_mux, best);
1917 if (best->eos && all_pads_eos (pads)) {
1918 gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1919 return GST_FLOW_UNEXPECTED;
1926 gst_ogg_mux_get_property (GObject * object,
1927 guint prop_id, GValue * value, GParamSpec * pspec)
1931 ogg_mux = GST_OGG_MUX (object);
1935 g_value_set_uint64 (value, ogg_mux->max_delay);
1937 case ARG_MAX_PAGE_DELAY:
1938 g_value_set_uint64 (value, ogg_mux->max_page_delay);
1940 case ARG_MAX_TOLERANCE:
1941 g_value_set_uint64 (value, ogg_mux->max_tolerance);
1944 g_value_set_boolean (value, ogg_mux->use_skeleton);
1947 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1953 gst_ogg_mux_set_property (GObject * object,
1954 guint prop_id, const GValue * value, GParamSpec * pspec)
1958 ogg_mux = GST_OGG_MUX (object);
1962 ogg_mux->max_delay = g_value_get_uint64 (value);
1964 case ARG_MAX_PAGE_DELAY:
1965 ogg_mux->max_page_delay = g_value_get_uint64 (value);
1967 case ARG_MAX_TOLERANCE:
1968 ogg_mux->max_tolerance = g_value_get_uint64 (value);
1971 ogg_mux->use_skeleton = g_value_get_boolean (value);
1974 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1979 /* reset all variables in the ogg pads. */
1981 gst_ogg_mux_init_collectpads (GstCollectPads * collect)
1985 walk = collect->data;
1987 GstOggPadData *oggpad = (GstOggPadData *) walk->data;
1989 ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
1990 oggpad->packetno = 0;
1992 oggpad->eos = FALSE;
1993 /* we assume there will be some control data first for this pad */
1994 oggpad->state = GST_OGG_PAD_STATE_CONTROL;
1995 oggpad->new_page = TRUE;
1996 oggpad->first_delta = FALSE;
1997 oggpad->prev_delta = FALSE;
1998 oggpad->data_pushed = FALSE;
1999 oggpad->pagebuffers = g_queue_new ();
2001 gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
2003 walk = g_slist_next (walk);
2007 /* Clear all buffers from the collectpads object */
2009 gst_ogg_mux_clear_collectpads (GstCollectPads * collect)
2013 for (walk = collect->data; walk; walk = g_slist_next (walk)) {
2014 GstOggPadData *oggpad = (GstOggPadData *) walk->data;
2017 ogg_stream_clear (&oggpad->map.stream);
2019 while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
2020 gst_buffer_unref (buf);
2022 g_queue_free (oggpad->pagebuffers);
2023 oggpad->pagebuffers = NULL;
2025 if (oggpad->buffer) {
2026 gst_buffer_unref (oggpad->buffer);
2027 oggpad->buffer = NULL;
2031 gst_tag_list_free (oggpad->tags);
2032 oggpad->tags = NULL;
2035 gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
2039 static GstStateChangeReturn
2040 gst_ogg_mux_change_state (GstElement * element, GstStateChange transition)
2043 GstStateChangeReturn ret;
2045 ogg_mux = GST_OGG_MUX (element);
2047 switch (transition) {
2048 case GST_STATE_CHANGE_NULL_TO_READY:
2050 case GST_STATE_CHANGE_READY_TO_PAUSED:
2051 gst_ogg_mux_clear (ogg_mux);
2052 gst_ogg_mux_init_collectpads (ogg_mux->collect);
2053 gst_collect_pads_start (ogg_mux->collect);
2055 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2057 case GST_STATE_CHANGE_PAUSED_TO_READY:
2058 gst_collect_pads_stop (ogg_mux->collect);
2064 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2066 switch (transition) {
2067 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2069 case GST_STATE_CHANGE_PAUSED_TO_READY:
2070 gst_ogg_mux_clear_collectpads (ogg_mux->collect);
2072 case GST_STATE_CHANGE_READY_TO_NULL:
2082 gst_ogg_mux_plugin_init (GstPlugin * plugin)
2084 GST_DEBUG_CATEGORY_INIT (gst_ogg_mux_debug, "oggmux", 0, "ogg muxer");
2086 return gst_element_register (plugin, "oggmux", GST_RANK_PRIMARY,