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/gstbytewriter.h>
44 #include <gst/tag/tag.h>
46 #include "gstoggmux.h"
48 /* memcpy - if someone knows a way to get rid of it, please speak up
49 * note: the ogg docs even say you need this... */
52 #include <stdlib.h> /* rand, srand, atoi */
54 GST_DEBUG_CATEGORY_STATIC (gst_ogg_mux_debug);
55 #define GST_CAT_DEFAULT gst_ogg_mux_debug
57 /* This isn't generally what you'd want with an end-time macro, because
58 technically the end time of a buffer with invalid duration is invalid. But
59 for sorting ogg pages this is what we want. */
60 #define GST_BUFFER_END_TIME(buf) \
61 (GST_BUFFER_DURATION_IS_VALID (buf) \
62 ? GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf) \
63 : GST_BUFFER_TIMESTAMP (buf))
65 #define GST_GP_FORMAT "[gp %8" G_GINT64_FORMAT "]"
66 #define GST_GP_CAST(_gp) ((gint64) _gp)
70 GST_OGG_FLAG_BOS = GST_ELEMENT_FLAG_LAST,
75 /* OggMux signals and args */
82 /* set to 0.5 seconds by default */
83 #define DEFAULT_MAX_DELAY G_GINT64_CONSTANT(500000000)
84 #define DEFAULT_MAX_PAGE_DELAY G_GINT64_CONSTANT(500000000)
85 #define DEFAULT_MAX_TOLERANCE G_GINT64_CONSTANT(40000000)
86 #define DEFAULT_SKELETON FALSE
97 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
100 GST_STATIC_CAPS ("application/ogg")
103 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
106 GST_STATIC_CAPS ("video/x-theora; "
107 "audio/x-vorbis; audio/x-flac; audio/x-speex; audio/x-celt; "
108 "application/x-ogm-video; application/x-ogm-audio; video/x-dirac; "
109 "video/x-smoke; video/x-vp8; text/x-cmml, encoded = (boolean) TRUE; "
110 "subtitle/x-kate; application/x-kate; audio/x-opus")
113 static void gst_ogg_mux_base_init (gpointer g_class);
114 static void gst_ogg_mux_class_init (GstOggMuxClass * klass);
115 static void gst_ogg_mux_init (GstOggMux * ogg_mux);
116 static void gst_ogg_mux_finalize (GObject * object);
119 gst_ogg_mux_collected (GstCollectPads2 * pads, GstOggMux * ogg_mux);
120 static gboolean gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event);
121 static GstPad *gst_ogg_mux_request_new_pad (GstElement * element,
122 GstPadTemplate * templ, const gchar * name);
123 static void gst_ogg_mux_release_pad (GstElement * element, GstPad * pad);
125 static void gst_ogg_mux_set_property (GObject * object,
126 guint prop_id, const GValue * value, GParamSpec * pspec);
127 static void gst_ogg_mux_get_property (GObject * object,
128 guint prop_id, GValue * value, GParamSpec * pspec);
129 static GstStateChangeReturn gst_ogg_mux_change_state (GstElement * element,
130 GstStateChange transition);
132 static GstElementClass *parent_class = NULL;
134 /*static guint gst_ogg_mux_signals[LAST_SIGNAL] = { 0 }; */
137 gst_ogg_mux_get_type (void)
139 static GType ogg_mux_type = 0;
141 if (G_UNLIKELY (ogg_mux_type == 0)) {
142 static const GTypeInfo ogg_mux_info = {
143 sizeof (GstOggMuxClass),
144 gst_ogg_mux_base_init,
146 (GClassInitFunc) gst_ogg_mux_class_init,
151 (GInstanceInitFunc) gst_ogg_mux_init,
153 static const GInterfaceInfo preset_info = {
160 g_type_register_static (GST_TYPE_ELEMENT, "GstOggMux", &ogg_mux_info,
163 g_type_add_interface_static (ogg_mux_type, GST_TYPE_PRESET, &preset_info);
169 gst_ogg_mux_base_init (gpointer g_class)
171 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
173 gst_element_class_add_static_pad_template (element_class, &src_factory);
174 gst_element_class_add_static_pad_template (element_class, &sink_factory);
176 gst_element_class_set_details_simple (element_class,
177 "Ogg muxer", "Codec/Muxer",
178 "mux ogg streams (info about ogg: http://xiph.org)",
179 "Wim Taymans <wim@fluendo.com>");
183 gst_ogg_mux_class_init (GstOggMuxClass * klass)
185 GObjectClass *gobject_class;
186 GstElementClass *gstelement_class;
188 gobject_class = (GObjectClass *) klass;
189 gstelement_class = (GstElementClass *) klass;
191 parent_class = g_type_class_peek_parent (klass);
193 gobject_class->finalize = gst_ogg_mux_finalize;
194 gobject_class->get_property = gst_ogg_mux_get_property;
195 gobject_class->set_property = gst_ogg_mux_set_property;
197 gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad;
198 gstelement_class->release_pad = gst_ogg_mux_release_pad;
200 g_object_class_install_property (gobject_class, ARG_MAX_DELAY,
201 g_param_spec_uint64 ("max-delay", "Max delay",
202 "Maximum delay in multiplexing streams", 0, G_MAXUINT64,
204 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
205 g_object_class_install_property (gobject_class, ARG_MAX_PAGE_DELAY,
206 g_param_spec_uint64 ("max-page-delay", "Max page delay",
207 "Maximum delay for sending out a page", 0, G_MAXUINT64,
208 DEFAULT_MAX_PAGE_DELAY,
209 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
210 g_object_class_install_property (gobject_class, ARG_MAX_TOLERANCE,
211 g_param_spec_uint64 ("max-tolerance", "Max time tolerance",
212 "Maximum timestamp difference for maintaining perfect granules",
213 0, G_MAXUINT64, DEFAULT_MAX_TOLERANCE,
214 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
215 g_object_class_install_property (gobject_class, ARG_SKELETON,
216 g_param_spec_boolean ("skeleton", "Skeleton",
217 "Whether to include a Skeleton track",
219 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
221 gstelement_class->change_state = gst_ogg_mux_change_state;
226 static const GstEventMask *
227 gst_ogg_mux_get_sink_event_masks (GstPad * pad)
229 static const GstEventMask gst_ogg_mux_sink_event_masks[] = {
231 {GST_EVENT_DISCONTINUOUS, 0},
235 return gst_ogg_mux_sink_event_masks;
240 gst_ogg_mux_clear (GstOggMux * ogg_mux)
242 ogg_mux->pulling = NULL;
243 ogg_mux->need_headers = TRUE;
244 ogg_mux->delta_pad = NULL;
246 ogg_mux->next_ts = 0;
247 ogg_mux->last_ts = GST_CLOCK_TIME_NONE;
251 gst_ogg_mux_init (GstOggMux * ogg_mux)
253 GstElementClass *klass = GST_ELEMENT_GET_CLASS (ogg_mux);
256 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
258 gst_pad_set_event_function (ogg_mux->srcpad, gst_ogg_mux_handle_src_event);
259 gst_element_add_pad (GST_ELEMENT (ogg_mux), ogg_mux->srcpad);
261 GST_OBJECT_FLAG_SET (GST_ELEMENT (ogg_mux), GST_OGG_FLAG_BOS);
263 /* seed random number generator for creation of serial numbers */
266 ogg_mux->collect = gst_collect_pads2_new ();
267 gst_collect_pads2_set_function (ogg_mux->collect,
268 (GstCollectPads2Function) GST_DEBUG_FUNCPTR (gst_ogg_mux_collected),
271 ogg_mux->max_delay = DEFAULT_MAX_DELAY;
272 ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY;
273 ogg_mux->max_tolerance = DEFAULT_MAX_TOLERANCE;
275 gst_ogg_mux_clear (ogg_mux);
279 gst_ogg_mux_finalize (GObject * object)
283 ogg_mux = GST_OGG_MUX (object);
285 if (ogg_mux->collect) {
286 gst_object_unref (ogg_mux->collect);
287 ogg_mux->collect = NULL;
290 G_OBJECT_CLASS (parent_class)->finalize (object);
294 gst_ogg_mux_ogg_pad_destroy_notify (GstCollectData2 * data)
296 GstOggPadData *oggpad = (GstOggPadData *) data;
299 ogg_stream_clear (&oggpad->map.stream);
300 gst_caps_replace (&oggpad->map.caps, NULL);
302 if (oggpad->pagebuffers) {
303 while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
304 gst_buffer_unref (buf);
306 g_queue_free (oggpad->pagebuffers);
307 oggpad->pagebuffers = NULL;
311 static GstPadLinkReturn
312 gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer)
316 ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
318 GST_DEBUG_OBJECT (ogg_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad));
320 gst_object_unref (ogg_mux);
322 return GST_PAD_LINK_OK;
326 gst_ogg_mux_sink_event (GstPad * pad, GstEvent * event)
328 GstOggMux *ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
329 GstOggPadData *ogg_pad = (GstOggPadData *) gst_pad_get_element_private (pad);
330 gboolean ret = FALSE;
332 GST_DEBUG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event));
334 switch (GST_EVENT_TYPE (event)) {
335 case GST_EVENT_NEWSEGMENT:{
338 gdouble applied_rate;
340 gint64 start, stop, position;
342 gst_event_parse_new_segment_full (event, &update, &rate,
343 &applied_rate, &format, &start, &stop, &position);
345 /* We don't support non time NEWSEGMENT events */
346 if (format != GST_FORMAT_TIME) {
347 gst_event_unref (event);
352 gst_segment_set_newsegment_full (&ogg_pad->segment, update, rate,
353 applied_rate, format, start, stop, position);
357 case GST_EVENT_FLUSH_STOP:{
358 gst_segment_init (&ogg_pad->segment, GST_FORMAT_TIME);
364 gst_event_parse_tag (event, &tags);
365 tags = gst_tag_list_merge (ogg_pad->tags, tags, GST_TAG_MERGE_APPEND);
367 gst_tag_list_free (ogg_pad->tags);
368 ogg_pad->tags = tags;
370 GST_DEBUG_OBJECT (ogg_mux, "Got tags %" GST_PTR_FORMAT, ogg_pad->tags);
377 /* now GstCollectPads can take care of the rest, e.g. EOS */
379 ret = ogg_pad->collect_event (pad, event);
381 gst_object_unref (ogg_mux);
386 gst_ogg_mux_is_serialno_present (GstOggMux * ogg_mux, guint32 serialno)
390 walk = ogg_mux->collect->data;
392 GstOggPadData *pad = (GstOggPadData *) walk->data;
393 if (pad->map.serialno == serialno)
402 gst_ogg_mux_generate_serialno (GstOggMux * ogg_mux)
407 serialno = g_random_int_range (0, G_MAXINT32);
408 } while (gst_ogg_mux_is_serialno_present (ogg_mux, serialno));
414 gst_ogg_mux_request_new_pad (GstElement * element,
415 GstPadTemplate * templ, const gchar * req_name)
419 GstElementClass *klass;
421 g_return_val_if_fail (templ != NULL, NULL);
423 if (templ->direction != GST_PAD_SINK)
424 goto wrong_direction;
426 g_return_val_if_fail (GST_IS_OGG_MUX (element), NULL);
427 ogg_mux = GST_OGG_MUX (element);
429 klass = GST_ELEMENT_GET_CLASS (element);
431 if (templ != gst_element_class_get_pad_template (klass, "sink_%d"))
438 if (req_name == NULL || strlen (req_name) < 6) {
439 /* no name given when requesting the pad, use random serial number */
440 serial = gst_ogg_mux_generate_serialno (ogg_mux);
442 /* parse serial number from requested padname */
443 unsigned long long_serial;
445 long_serial = strtoul (&req_name[5], &endptr, 10);
446 if ((endptr && *endptr) || (long_serial & ~0xffffffff)) {
447 GST_WARNING_OBJECT (ogg_mux, "Invalid serial number specification: %s",
451 serial = (guint32) long_serial;
453 /* create new pad with the name */
454 GST_DEBUG_OBJECT (ogg_mux, "Creating new pad for serial %d", serial);
455 name = g_strdup_printf ("sink_%d", serial);
456 newpad = gst_pad_new_from_template (templ, name);
459 /* construct our own wrapper data structure for the pad to
460 * keep track of its status */
462 GstOggPadData *oggpad;
464 oggpad = (GstOggPadData *)
465 gst_collect_pads2_add_pad_full (ogg_mux->collect, newpad,
466 sizeof (GstOggPadData), gst_ogg_mux_ogg_pad_destroy_notify, FALSE);
467 ogg_mux->active_pads++;
469 oggpad->map.serialno = serial;
470 oggpad->packetno = 0;
473 /* we assume there will be some control data first for this pad */
474 oggpad->state = GST_OGG_PAD_STATE_CONTROL;
475 oggpad->new_page = TRUE;
476 oggpad->first_delta = FALSE;
477 oggpad->prev_delta = FALSE;
478 oggpad->data_pushed = FALSE;
479 oggpad->map.headers = NULL;
480 oggpad->map.queued = NULL;
481 oggpad->next_granule = 0;
482 oggpad->keyframe_granule = -1;
484 if (GST_STATE (ogg_mux) > GST_STATE_READY) {
485 /* This will be initialized in init_collectpads when going from ready
487 ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
488 oggpad->pagebuffers = g_queue_new ();
491 gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
493 oggpad->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
494 gst_pad_set_event_function (newpad,
495 GST_DEBUG_FUNCPTR (gst_ogg_mux_sink_event));
499 /* setup some pad functions */
500 gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect);
502 /* dd the pad to the element */
503 gst_element_add_pad (element, newpad);
510 g_warning ("ogg_mux: request pad that is not a SINK pad\n");
515 g_warning ("ogg_mux: this is not our template!\n");
521 gst_ogg_mux_release_pad (GstElement * element, GstPad * pad)
525 ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
527 gst_collect_pads2_remove_pad (ogg_mux->collect, pad);
528 gst_element_remove_pad (element, pad);
530 gst_object_unref (ogg_mux);
535 gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event)
539 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
543 /* disable seeking for now */
549 return gst_pad_event_default (pad, event);
553 gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
557 /* allocate space for header and body */
558 buffer = gst_buffer_new_and_alloc (page->header_len + page->body_len);
559 memcpy (GST_BUFFER_DATA (buffer), page->header, page->header_len);
560 memcpy (GST_BUFFER_DATA (buffer) + page->header_len,
561 page->body, page->body_len);
563 /* Here we set granulepos as our OFFSET_END to give easy direct access to
564 * this value later. Before we push it, we reset this to OFFSET + SIZE
565 * (see gst_ogg_mux_push_buffer). */
566 GST_BUFFER_OFFSET_END (buffer) = ogg_page_granulepos (page);
568 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
570 GST_LOG_OBJECT (mux, GST_GP_FORMAT
571 " created buffer %p from ogg page",
572 GST_GP_CAST (ogg_page_granulepos (page)), buffer);
578 gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer,
579 GstOggPadData * oggpad)
583 /* fix up OFFSET and OFFSET_END again */
584 GST_BUFFER_OFFSET (buffer) = mux->offset;
585 mux->offset += GST_BUFFER_SIZE (buffer);
586 GST_BUFFER_OFFSET_END (buffer) = mux->offset;
588 /* Ensure we have monotonically increasing timestamps in the output. */
589 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
590 gint64 run_time = GST_BUFFER_TIMESTAMP (buffer);
591 if (mux->last_ts != GST_CLOCK_TIME_NONE && run_time < mux->last_ts)
592 GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts;
594 mux->last_ts = run_time;
597 caps = gst_pad_get_negotiated_caps (mux->srcpad);
598 gst_buffer_set_caps (buffer, caps);
600 gst_caps_unref (caps);
602 return gst_pad_push (mux->srcpad, buffer);
605 /* if all queues have at least one page, dequeue the page with the lowest
608 gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret)
611 GstOggPadData *opad = NULL; /* "oldest" pad */
612 GstClockTime oldest = GST_CLOCK_TIME_NONE;
613 GstBuffer *buf = NULL;
614 gboolean ret = FALSE;
616 *flowret = GST_FLOW_OK;
618 walk = mux->collect->data;
620 GstOggPadData *pad = (GstOggPadData *) walk->data;
622 /* We need each queue to either be at EOS, or have one or more pages
623 * available with a set granulepos (i.e. not -1), otherwise we don't have
624 * enough data yet to determine which stream needs to go next for correct
626 if (pad->pagebuffers->length == 0) {
628 GST_LOG_OBJECT (pad->collect.pad,
629 "pad is EOS, skipping for dequeue decision");
631 GST_LOG_OBJECT (pad->collect.pad,
632 "no pages in this queue, can't dequeue");
636 /* We then need to check for a non-negative granulepos */
638 gboolean valid = FALSE;
640 for (i = 0; i < pad->pagebuffers->length; i++) {
641 buf = g_queue_peek_nth (pad->pagebuffers, i);
642 /* Here we check the OFFSET_END, which is actually temporarily the
643 * granulepos value for this buffer */
644 if (GST_BUFFER_OFFSET_END (buf) != -1) {
650 GST_LOG_OBJECT (pad->collect.pad,
651 "No page timestamps in queue, can't dequeue");
656 walk = g_slist_next (walk);
659 walk = mux->collect->data;
661 GstOggPadData *pad = (GstOggPadData *) walk->data;
663 /* any page with a granulepos of -1 can be pushed immediately.
664 * TODO: it CAN be, but it seems silly to do so? */
665 buf = g_queue_peek_head (pad->pagebuffers);
666 while (buf && GST_BUFFER_OFFSET_END (buf) == -1) {
667 GST_LOG_OBJECT (pad->collect.pad, "[gp -1] pushing page");
668 g_queue_pop_head (pad->pagebuffers);
669 *flowret = gst_ogg_mux_push_buffer (mux, buf, pad);
670 buf = g_queue_peek_head (pad->pagebuffers);
675 /* if no oldest buffer yet, take this one */
676 if (oldest == GST_CLOCK_TIME_NONE) {
677 GST_LOG_OBJECT (mux, "no oldest yet, taking buffer %p from pad %"
678 GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
679 buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
680 oldest = GST_BUFFER_OFFSET (buf);
683 /* if we have an oldest, compare with this one */
684 if (GST_BUFFER_OFFSET (buf) < oldest) {
685 GST_LOG_OBJECT (mux, "older buffer %p, taking from pad %"
686 GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
687 buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
688 oldest = GST_BUFFER_OFFSET (buf);
693 walk = g_slist_next (walk);
696 if (oldest != GST_CLOCK_TIME_NONE) {
698 buf = g_queue_pop_head (opad->pagebuffers);
699 GST_LOG_OBJECT (opad->collect.pad,
700 GST_GP_FORMAT " pushing oldest page buffer %p (granulepos time %"
701 GST_TIME_FORMAT ")", GST_BUFFER_OFFSET_END (buf), buf,
702 GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
703 *flowret = gst_ogg_mux_push_buffer (mux, buf, opad);
710 /* put the given ogg page on a per-pad queue, timestamping it correctly.
711 * after that, dequeue and push as many pages as possible.
712 * Caller should make sure:
713 * pad->timestamp was set with the timestamp of the first packet put
715 * pad->timestamp_end was set with the timestamp + duration of the last packet
717 * pad->gp_time was set with the time matching the gp of the last
718 * packet put on the page
720 * will also reset timestamp and timestamp_end, so caller func can restart
724 gst_ogg_mux_pad_queue_page (GstOggMux * mux, GstOggPadData * pad,
725 ogg_page * page, gboolean delta)
728 GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);
730 /* take the timestamp of the first packet on this page */
731 GST_BUFFER_TIMESTAMP (buffer) = pad->timestamp;
732 GST_BUFFER_DURATION (buffer) = pad->timestamp_end - pad->timestamp;
733 /* take the gp time of the last completed packet on this page */
734 GST_BUFFER_OFFSET (buffer) = pad->gp_time;
736 /* the next page will start where the current page's end time leaves off */
737 pad->timestamp = pad->timestamp_end;
739 g_queue_push_tail (pad->pagebuffers, buffer);
740 GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
741 " queued buffer page %p (gp time %"
742 GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT
743 "), %d page buffers queued", GST_GP_CAST (ogg_page_granulepos (page)),
744 buffer, GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)),
745 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
746 g_queue_get_length (pad->pagebuffers));
748 while (gst_ogg_mux_dequeue_page (mux, &ret)) {
749 if (ret != GST_FLOW_OK)
757 * Given two pads, compare the buffers queued on it.
759 * 0 if they have an equal priority
760 * -1 if the first is better
761 * 1 if the second is better
762 * Priority decided by: a) validity, b) older timestamp, c) smaller number
766 gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPadData * first,
767 GstOggPadData * second)
769 guint64 firsttime, secondtime;
771 /* if the first pad doesn't contain anything or is even NULL, return
772 * the second pad as best candidate and vice versa */
778 /* no timestamp on first buffer, it must go first */
779 firsttime = GST_BUFFER_TIMESTAMP (first->buffer);
780 if (firsttime == GST_CLOCK_TIME_NONE)
783 /* no timestamp on second buffer, it must go first */
784 secondtime = GST_BUFFER_TIMESTAMP (second->buffer);
785 if (secondtime == GST_CLOCK_TIME_NONE)
788 /* first buffer has higher timestamp, second one should go first */
789 if (secondtime < firsttime)
791 /* second buffer has higher timestamp, first one should go first */
792 else if (secondtime > firsttime)
795 /* buffers with equal timestamps, prefer the pad that has the
796 * least number of pages muxed */
797 if (second->pageno < first->pageno)
799 else if (second->pageno > first->pageno)
803 /* same priority if all of the above failed */
808 gst_ogg_mux_decorate_buffer (GstOggMux * ogg_mux, GstOggPadData * pad,
812 gint64 duration, granule, limit;
813 GstClockTime next_time;
814 GstClockTimeDiff diff;
817 /* ensure messing with metadata is ok */
818 buf = gst_buffer_make_metadata_writable (buf);
820 /* convert time to running time, so we need no longer bother about that */
821 time = GST_BUFFER_TIMESTAMP (buf);
822 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
823 time = gst_segment_to_running_time (&pad->segment, GST_FORMAT_TIME, time);
824 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
825 gst_buffer_unref (buf);
828 GST_BUFFER_TIMESTAMP (buf) = time;
832 /* now come up with granulepos stuff corresponding to time */
833 if (!pad->have_type ||
834 pad->map.granulerate_n <= 0 || pad->map.granulerate_d <= 0)
837 packet.packet = GST_BUFFER_DATA (buf);
838 packet.bytes = GST_BUFFER_SIZE (buf);
839 duration = gst_ogg_stream_get_packet_duration (&pad->map, &packet);
841 /* give up if no duration can be determined, relying on upstream */
842 if (G_UNLIKELY (duration < 0)) {
843 /* well, if some day we really could handle sparse input ... */
844 if (pad->map.is_sparse) {
849 GST_WARNING_OBJECT (pad->collect.pad,
850 "failed to determine packet duration");
854 GST_LOG_OBJECT (pad->collect.pad, "buffer ts %" GST_TIME_FORMAT
855 ", duration %" GST_TIME_FORMAT ", granule duration %" G_GINT64_FORMAT,
856 GST_TIME_ARGS (time), GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
859 /* determine granule corresponding to time,
860 * using the inverse of oggdemux' granule -> time */
862 /* see if interpolated granule matches good enough */
863 granule = pad->next_granule;
864 next_time = gst_ogg_stream_granule_to_time (&pad->map, pad->next_granule);
865 diff = GST_CLOCK_DIFF (next_time, time);
867 /* we tolerate deviation up to configured or within granule granularity */
868 limit = gst_ogg_stream_granule_to_time (&pad->map, 1) / 2;
869 limit = MAX (limit, ogg_mux->max_tolerance);
871 GST_LOG_OBJECT (pad->collect.pad, "expected granule %" G_GINT64_FORMAT " == "
872 "time %" GST_TIME_FORMAT " --> ts diff %" GST_TIME_FORMAT
873 " < tolerance %" GST_TIME_FORMAT " (?)",
874 granule, GST_TIME_ARGS (next_time), GST_TIME_ARGS (ABS (diff)),
875 GST_TIME_ARGS (limit));
878 /* if not good enough, determine granule based on time */
879 if (diff > limit || diff < -limit) {
880 granule = gst_util_uint64_scale_round (time, pad->map.granulerate_n,
881 GST_SECOND * pad->map.granulerate_d);
882 GST_DEBUG_OBJECT (pad->collect.pad,
883 "resyncing to determined granule %" G_GINT64_FORMAT, granule);
886 if (pad->map.is_ogm || pad->map.is_sparse) {
887 pad->next_granule = granule;
890 pad->next_granule = granule;
893 /* track previous keyframe */
894 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
895 pad->keyframe_granule = granule;
897 /* determine corresponding time and granulepos */
898 GST_BUFFER_OFFSET (buf) = gst_ogg_stream_granule_to_time (&pad->map, granule);
899 GST_BUFFER_OFFSET_END (buf) =
900 gst_ogg_stream_granule_to_granulepos (&pad->map, granule,
901 pad->keyframe_granule);
908 GST_DEBUG_OBJECT (pad->collect.pad, "could not determine granulepos, "
909 "falling back to upstream provided metadata");
915 /* make sure at least one buffer is queued on all pads, two if possible
917 * if pad->buffer == NULL, pad->next_buffer != NULL, then
918 * we do not know if the buffer is the last or not
919 * if pad->buffer != NULL, pad->next_buffer != NULL, then
920 * pad->buffer is not the last buffer for the pad
921 * if pad->buffer != NULL, pad->next_buffer == NULL, then
922 * pad->buffer if the last buffer for the pad
924 * returns a pointer to an oggpad that holds the best buffer, or
925 * NULL when no pad was usable. "best" means the buffer marked
926 * with the lowest timestamp. If best->buffer == NULL then either
927 * we're at EOS (popped = FALSE), or a buffer got dropped, so retry. */
928 static GstOggPadData *
929 gst_ogg_mux_queue_pads (GstOggMux * ogg_mux, gboolean * popped)
931 GstOggPadData *bestpad = NULL;
936 /* try to make sure we have a buffer from each usable pad first */
937 walk = ogg_mux->collect->data;
940 GstCollectData2 *data;
942 data = (GstCollectData2 *) walk->data;
943 pad = (GstOggPadData *) data;
945 walk = g_slist_next (walk);
947 GST_LOG_OBJECT (data->pad, "looking at pad for buffer");
949 /* try to get a new buffer for this pad if needed and possible */
950 if (pad->buffer == NULL) {
953 buf = gst_collect_pads2_pop (ogg_mux->collect, data);
954 GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
956 /* On EOS we get a NULL buffer */
960 if (ogg_mux->delta_pad == NULL &&
961 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
962 ogg_mux->delta_pad = pad;
964 /* if we need headers */
965 if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
966 /* and we have one */
970 packet.packet = GST_BUFFER_DATA (buf);
971 packet.bytes = GST_BUFFER_SIZE (buf);
973 /* if we're not yet in data mode, ensure we're setup on the first packet */
974 if (!pad->have_type) {
975 /* Use headers in caps, if any; this will allow us to be resilient
976 * to starting streams on the fly, and some streams (like VP8
977 * at least) do not send headers packets, as other muxers don't
978 * expect/need them. */
980 gst_ogg_stream_setup_map_from_caps_headers (&pad->map,
981 GST_BUFFER_CAPS (buf));
983 if (!pad->have_type) {
984 /* fallback on the packet */
985 pad->have_type = gst_ogg_stream_setup_map (&pad->map, &packet);
987 if (!pad->have_type) {
988 GST_ERROR_OBJECT (pad, "mapper didn't recognise input stream "
989 "(pad caps: %" GST_PTR_FORMAT ")", GST_PAD_CAPS (pad));
991 GST_DEBUG_OBJECT (pad, "caps detected: %" GST_PTR_FORMAT,
994 if (pad->map.is_sparse) {
995 GST_DEBUG_OBJECT (pad, "Pad is sparse, marking as such");
996 gst_collect_pads2_set_waiting (ogg_mux->collect,
997 (GstCollectData2 *) pad, FALSE);
1003 is_header = gst_ogg_stream_packet_is_header (&pad->map, &packet);
1004 else /* fallback (FIXME 0.11: remove IN_CAPS hack) */
1005 is_header = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
1008 GST_DEBUG_OBJECT (ogg_mux,
1009 "got header buffer in control state, ignoring");
1011 pad->map.n_header_packets_seen++;
1012 gst_buffer_unref (buf);
1015 GST_DEBUG_OBJECT (ogg_mux,
1016 "got data buffer in control state, switching to data mode");
1017 /* this is a data buffer so switch to data state */
1018 pad->state = GST_OGG_PAD_STATE_DATA;
1020 /* check if this type of stream allows generating granulepos
1021 * metadata here, if not, upstream will have to provide */
1022 if (gst_ogg_stream_granule_to_granulepos (&pad->map, 1, 1) < 0) {
1023 GST_WARNING_OBJECT (data->pad, "can not generate metadata; "
1024 "relying on upstream");
1025 /* disable metadata code path, otherwise not used anyway */
1026 pad->map.granulerate_n = 0;
1031 /* so now we should have a real data packet;
1032 * see that it is properly decorated */
1033 if (G_LIKELY (buf)) {
1034 buf = gst_ogg_mux_decorate_buffer (ogg_mux, pad, buf);
1035 if (G_UNLIKELY (!buf))
1036 GST_DEBUG_OBJECT (data->pad, "buffer clipped");
1043 /* we should have a buffer now, see if it is the best pad to
1046 if (gst_ogg_mux_compare_pads (ogg_mux, bestpad, pad) > 0) {
1047 GST_LOG_OBJECT (data->pad,
1048 "new best pad, with buffer %" GST_PTR_FORMAT, pad->buffer);
1059 gst_ogg_mux_get_headers (GstOggPadData * pad)
1062 GstStructure *structure;
1066 thepad = pad->collect.pad;
1068 GST_LOG_OBJECT (thepad, "getting headers");
1070 caps = gst_pad_get_negotiated_caps (thepad);
1072 const GValue *streamheader;
1074 structure = gst_caps_get_structure (caps, 0);
1075 streamheader = gst_structure_get_value (structure, "streamheader");
1076 if (streamheader != NULL) {
1077 GST_LOG_OBJECT (thepad, "got header");
1078 if (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
1079 GArray *bufarr = g_value_peek_pointer (streamheader);
1082 GST_LOG_OBJECT (thepad, "got fixed list");
1084 for (i = 0; i < bufarr->len; i++) {
1085 GValue *bufval = &g_array_index (bufarr, GValue, i);
1087 GST_LOG_OBJECT (thepad, "item %d", i);
1088 if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
1089 GstBuffer *buf = g_value_peek_pointer (bufval);
1091 GST_LOG_OBJECT (thepad, "adding item %d to header list", i);
1093 gst_buffer_ref (buf);
1094 res = g_list_append (res, buf);
1098 GST_LOG_OBJECT (thepad, "streamheader is not fixed list");
1101 } else if (gst_structure_has_name (structure, "video/x-dirac")) {
1102 res = g_list_append (res, pad->buffer);
1105 GST_LOG_OBJECT (thepad, "caps don't have streamheader");
1107 gst_caps_unref (caps);
1109 GST_LOG_OBJECT (thepad, "got empty caps as negotiated format");
1115 gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
1117 GstStructure *structure;
1118 GValue array = { 0 };
1119 GList *walk = buffers;
1121 caps = gst_caps_make_writable (caps);
1123 structure = gst_caps_get_structure (caps, 0);
1125 /* put buffers in a fixed list */
1126 g_value_init (&array, GST_TYPE_ARRAY);
1129 GstBuffer *buf = GST_BUFFER (walk->data);
1131 GValue value = { 0 };
1136 GST_LOG ("Setting IN_CAPS on buffer of length %d", GST_BUFFER_SIZE (buf));
1137 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
1139 g_value_init (&value, GST_TYPE_BUFFER);
1140 copy = gst_buffer_copy (buf);
1141 gst_value_set_buffer (&value, copy);
1142 gst_buffer_unref (copy);
1143 gst_value_array_append_value (&array, &value);
1144 g_value_unset (&value);
1146 gst_structure_set_value (structure, "streamheader", &array);
1147 g_value_unset (&array);
1153 gst_ogg_mux_create_header_packet_with_flags (ogg_packet * packet,
1154 GstBuffer * buf, gboolean bos, gboolean eos)
1156 packet->packet = GST_BUFFER_DATA (buf);
1157 packet->bytes = GST_BUFFER_SIZE (buf);
1158 packet->granulepos = 0;
1159 /* mark BOS and packet number */
1160 packet->b_o_s = bos;
1162 packet->e_o_s = eos;
1166 gst_ogg_mux_create_header_packet (ogg_packet * packet, GstBuffer * buf,
1167 GstOggPadData * pad)
1169 gst_ogg_mux_create_header_packet_with_flags (packet, buf, pad->packetno == 0,
1171 packet->packetno = pad->packetno++;
1175 gst_ogg_mux_submit_skeleton_header_packet (GstOggMux * mux,
1176 ogg_stream_state * os, GstBuffer * buf, gboolean bos, gboolean eos)
1179 gst_ogg_mux_create_header_packet_with_flags (&packet, buf, bos, eos);
1180 ogg_stream_packetin (os, &packet);
1181 gst_buffer_unref (buf);
1185 gst_ogg_mux_make_fishead (GstOggMux * mux, ogg_stream_state * os)
1190 GST_DEBUG_OBJECT (mux, "Creating fishead");
1192 fishead = gst_buffer_new_and_alloc (64);
1193 gst_byte_writer_init_with_buffer (&bw, fishead, FALSE);
1194 gst_byte_writer_put_string_utf8 (&bw, "fishead");
1195 gst_byte_writer_put_int16_le (&bw, 3); /* version major */
1196 gst_byte_writer_put_int16_le (&bw, 0); /* version minor */
1197 gst_byte_writer_put_int64_le (&bw, 0); /* presentation time numerator */
1198 gst_byte_writer_put_int64_le (&bw, 1000); /* ...and denominator */
1199 gst_byte_writer_put_int64_le (&bw, 0); /* base time numerator */
1200 gst_byte_writer_put_int64_le (&bw, 1000); /* ...and denominator */
1201 gst_byte_writer_fill (&bw, ' ', 20); /* UTC time */
1202 g_assert (gst_byte_writer_get_pos (&bw) == GST_BUFFER_SIZE (fishead));
1203 gst_ogg_mux_submit_skeleton_header_packet (mux, os, fishead, 1, 0);
1207 gst_ogg_mux_byte_writer_put_string_utf8 (GstByteWriter * bw, const char *s)
1209 gst_byte_writer_put_data (bw, (const guint8 *) s, strlen (s));
1213 gst_ogg_mux_add_fisbone_message_header (GstOggMux * mux, GstByteWriter * bw,
1214 const char *tag, const char *value)
1216 /* It is valid to pass NULL as the value to omit the tag */
1219 GST_DEBUG_OBJECT (mux, "Adding fisbone message header %s: %s", tag, value);
1220 gst_ogg_mux_byte_writer_put_string_utf8 (bw, tag);
1221 gst_ogg_mux_byte_writer_put_string_utf8 (bw, ": ");
1222 gst_ogg_mux_byte_writer_put_string_utf8 (bw, value);
1223 gst_ogg_mux_byte_writer_put_string_utf8 (bw, "\r\n");
1227 gst_ogg_mux_add_fisbone_message_header_from_tags (GstOggMux * mux,
1228 GstByteWriter * bw, const char *header, const char *tag,
1229 const GstTagList * tags)
1232 guint size = gst_tag_list_get_tag_size (tags, tag), n;
1233 GST_DEBUG_OBJECT (mux, "Found %u tags for name %s", size, tag);
1236 s = g_string_new ("");
1237 for (n = 0; n < size; ++n) {
1240 g_string_append (s, ", ");
1241 gst_tag_list_get_string_index (tags, tag, n, &tmp);
1242 g_string_append (s, tmp);
1245 gst_ogg_mux_add_fisbone_message_header (mux, bw, header, s->str);
1246 g_string_free (s, TRUE);
1249 /* This is a basic placeholder to generate roles for the tracks.
1250 For tracks with more than one video, both video tracks will get
1251 tagged with a "video/main" role, but we have no way of knowing
1252 which one is the main one, if any. We could just pick one. For
1253 audio, it's more complicated as we don't know which is music,
1254 which is dubbing, etc. For kate, we could take a pretty good
1255 guess based on the category, as role essentially is category.
1256 For now, leave this as is. */
1258 gst_ogg_mux_get_default_role (GstOggPadData * pad)
1260 const char *type = gst_ogg_stream_get_media_type (&pad->map);
1262 if (!strncmp (type, "video/", strlen ("video/")))
1263 return "video/main";
1264 if (!strncmp (type, "audio/", strlen ("audio/")))
1265 return "audio/main";
1266 if (!strcmp (type + strlen (type) - strlen ("kate"), "kate"))
1267 return "text/caption";
1273 gst_ogg_mux_make_fisbone (GstOggMux * mux, ogg_stream_state * os,
1274 GstOggPadData * pad)
1278 GST_DEBUG_OBJECT (mux,
1279 "Creating %s fisbone for serial %08x",
1280 gst_ogg_stream_get_media_type (&pad->map), pad->map.serialno);
1282 gst_byte_writer_init (&bw);
1283 gst_byte_writer_put_string_utf8 (&bw, "fisbone");
1284 gst_byte_writer_put_int32_le (&bw, 44); /* offset to message headers */
1285 gst_byte_writer_put_uint32_le (&bw, pad->map.serialno);
1286 gst_byte_writer_put_uint32_le (&bw, pad->map.n_header_packets);
1287 gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_n);
1288 gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_d);
1289 gst_byte_writer_put_uint64_le (&bw, 0); /* base granule */
1290 gst_byte_writer_put_uint32_le (&bw, pad->map.preroll);
1291 gst_byte_writer_put_uint8 (&bw, pad->map.granuleshift);
1292 gst_byte_writer_fill (&bw, 0, 3); /* padding */
1293 /* message header fields - MIME type for now */
1294 gst_ogg_mux_add_fisbone_message_header (mux, &bw, "Content-Type",
1295 gst_ogg_stream_get_media_type (&pad->map));
1296 gst_ogg_mux_add_fisbone_message_header (mux, &bw, "Role",
1297 gst_ogg_mux_get_default_role (pad));
1298 gst_ogg_mux_add_fisbone_message_header_from_tags (mux, &bw, "Language",
1299 GST_TAG_LANGUAGE_CODE, pad->tags);
1300 gst_ogg_mux_add_fisbone_message_header_from_tags (mux, &bw, "Title",
1301 GST_TAG_TITLE, pad->tags);
1303 gst_ogg_mux_submit_skeleton_header_packet (mux, os,
1304 gst_byte_writer_reset_and_get_buffer (&bw), 0, 0);
1308 gst_ogg_mux_make_fistail (GstOggMux * mux, ogg_stream_state * os)
1310 GST_DEBUG_OBJECT (mux, "Creating fistail");
1312 gst_ogg_mux_submit_skeleton_header_packet (mux, os,
1313 gst_buffer_new_and_alloc (0), 0, 1);
1317 * For each pad we need to write out one (small) header in one
1318 * page that allows decoders to identify the type of the stream.
1319 * After that we need to write out all extra info for the decoders.
1320 * In the case of a codec that also needs data as configuration, we can
1321 * find that info in the streamcaps.
1322 * After writing the headers we must start a new page for the data.
1324 static GstFlowReturn
1325 gst_ogg_mux_send_headers (GstOggMux * mux)
1328 GList *hbufs, *hwalk;
1332 ogg_stream_state skeleton_stream;
1337 GST_LOG_OBJECT (mux, "collecting headers");
1339 walk = mux->collect->data;
1344 pad = (GstOggPadData *) walk->data;
1345 thepad = pad->collect.pad;
1347 walk = g_slist_next (walk);
1349 GST_LOG_OBJECT (mux, "looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
1351 /* if the pad has no buffer and is not sparse, we don't care */
1352 if (pad->buffer == NULL && !pad->map.is_sparse)
1355 /* now figure out the headers */
1356 pad->map.headers = gst_ogg_mux_get_headers (pad);
1359 GST_LOG_OBJECT (mux, "creating BOS pages");
1360 walk = mux->collect->data;
1367 GstStructure *structure;
1370 pad = (GstOggPadData *) walk->data;
1371 thepad = pad->collect.pad;
1372 caps = gst_pad_get_negotiated_caps (thepad);
1373 structure = gst_caps_get_structure (caps, 0);
1379 GST_LOG_OBJECT (thepad, "looping over headers");
1381 if (pad->map.headers) {
1382 buf = GST_BUFFER (pad->map.headers->data);
1383 pad->map.headers = g_list_remove (pad->map.headers, buf);
1384 } else if (pad->buffer) {
1386 gst_buffer_ref (buf);
1388 /* fixme -- should be caught in the previous list traversal. */
1389 GST_OBJECT_LOCK (thepad);
1390 g_critical ("No headers or buffers on pad %s:%s",
1391 GST_DEBUG_PAD_NAME (thepad));
1392 GST_OBJECT_UNLOCK (thepad);
1396 /* create a packet from the buffer */
1397 gst_ogg_mux_create_header_packet (&packet, buf, pad);
1399 /* swap the packet in */
1400 ogg_stream_packetin (&pad->map.stream, &packet);
1401 gst_buffer_unref (buf);
1403 GST_LOG_OBJECT (thepad, "flushing out BOS page");
1404 if (!ogg_stream_flush (&pad->map.stream, &page))
1405 g_critical ("Could not flush BOS page");
1407 hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1409 GST_LOG_OBJECT (mux, "swapped out page with mime type %s",
1410 gst_structure_get_name (structure));
1412 /* quick hack: put video pages at the front.
1413 * Ideally, we would have a settable enum for which Ogg
1414 * profile we work with, and order based on that.
1415 * (FIXME: if there is more than one video stream, shouldn't we only put
1416 * one's BOS into the first page, followed by an audio stream's BOS, and
1417 * only then followed by the remaining video and audio streams?) */
1418 if (pad->map.is_video) {
1419 GST_DEBUG_OBJECT (thepad, "putting %s page at the front",
1420 gst_structure_get_name (structure));
1421 hbufs = g_list_prepend (hbufs, hbuf);
1423 hbufs = g_list_append (hbufs, hbuf);
1426 gst_caps_unref (caps);
1429 /* The Skeleton BOS goes first - even before the video that went first before */
1430 if (mux->use_skeleton) {
1431 ogg_stream_init (&skeleton_stream, gst_ogg_mux_generate_serialno (mux));
1432 gst_ogg_mux_make_fishead (mux, &skeleton_stream);
1433 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1434 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1435 hbufs = g_list_append (hbufs, hbuf);
1439 GST_LOG_OBJECT (mux, "creating next headers");
1440 walk = mux->collect->data;
1445 pad = (GstOggPadData *) walk->data;
1446 thepad = pad->collect.pad;
1450 if (mux->use_skeleton)
1451 gst_ogg_mux_make_fisbone (mux, &skeleton_stream, pad);
1453 GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
1454 GST_DEBUG_PAD_NAME (thepad));
1456 hwalk = pad->map.headers;
1458 GstBuffer *buf = GST_BUFFER (hwalk->data);
1461 hwalk = hwalk->next;
1463 /* create a packet from the buffer */
1464 gst_ogg_mux_create_header_packet (&packet, buf, pad);
1466 /* swap the packet in */
1467 ogg_stream_packetin (&pad->map.stream, &packet);
1468 gst_buffer_unref (buf);
1470 /* if last header, flush page */
1471 if (hwalk == NULL) {
1472 GST_LOG_OBJECT (mux,
1473 "flushing page as packet %" G_GUINT64_FORMAT " is first or "
1474 "last packet", (guint64) packet.packetno);
1475 while (ogg_stream_flush (&pad->map.stream, &page)) {
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);
1482 GST_LOG_OBJECT (mux, "try to swap out page");
1483 /* just try to swap out a page then */
1484 while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1485 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1487 GST_LOG_OBJECT (mux, "swapped out page");
1488 hbufs = g_list_append (hbufs, hbuf);
1492 g_list_free (pad->map.headers);
1493 pad->map.headers = NULL;
1496 if (mux->use_skeleton) {
1497 /* flush accumulated fisbones, the fistail must be on a separate page */
1498 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1499 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1500 hbufs = g_list_append (hbufs, hbuf);
1502 gst_ogg_mux_make_fistail (mux, &skeleton_stream);
1503 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1504 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1505 hbufs = g_list_append (hbufs, hbuf);
1507 ogg_stream_clear (&skeleton_stream);
1510 /* hbufs holds all buffers for the headers now */
1512 /* create caps with the buffers */
1513 caps = gst_pad_get_caps (mux->srcpad);
1515 caps = gst_ogg_mux_set_header_on_caps (caps, hbufs);
1516 gst_pad_set_caps (mux->srcpad, caps);
1517 gst_caps_unref (caps);
1519 /* and send the buffers */
1520 while (hbufs != NULL) {
1521 GstBuffer *buf = GST_BUFFER (hbufs->data);
1523 hbufs = g_list_delete_link (hbufs, hbufs);
1525 if ((ret = gst_ogg_mux_push_buffer (mux, buf, NULL)) != GST_FLOW_OK)
1528 /* free any remaining nodes/buffers in case we couldn't push them */
1529 g_list_foreach (hbufs, (GFunc) gst_mini_object_unref, NULL);
1530 g_list_free (hbufs);
1535 /* this function is called to process data on the best pending pad.
1539 * 1) store the selected pad and keep on pulling until we fill a
1540 * complete ogg page or the ogg page is filled above the max-delay
1541 * threshold. This is needed because the ogg spec says that
1542 * you should fill a complete page with data from the same logical
1543 * stream. When the page is filled, go back to 1).
1544 * 2) before filling a page, read ahead one more buffer to see if this
1545 * packet is the last of the stream. We need to do this because the ogg
1546 * spec mandates that the last packet should have the EOS flag set before
1547 * sending it to ogg. if pad->buffer is NULL we need to wait to find out
1548 * whether there are any more buffers.
1549 * 3) pages get queued on a per-pad queue. Every time a page is queued, a
1550 * dequeue is called, which will dequeue the oldest page on any pad, provided
1551 * that ALL pads have at least one marked page in the queue (or remaining
1554 static GstFlowReturn
1555 gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
1557 GstFlowReturn ret = GST_FLOW_OK;
1558 gboolean delta_unit;
1559 gint64 granulepos = 0;
1560 GstClockTime timestamp, gp_time;
1561 GstBuffer *next_buf;
1563 GST_LOG_OBJECT (ogg_mux, "best pad %" GST_PTR_FORMAT
1564 ", currently pulling from %" GST_PTR_FORMAT, best->collect.pad,
1565 ogg_mux->pulling ? ogg_mux->pulling->collect.pad : NULL);
1567 if (ogg_mux->pulling) {
1568 next_buf = gst_collect_pads2_peek (ogg_mux->collect,
1569 &ogg_mux->pulling->collect);
1571 ogg_mux->pulling->eos = FALSE;
1572 gst_buffer_unref (next_buf);
1574 GST_DEBUG_OBJECT (ogg_mux->pulling->collect.pad, "setting eos to true");
1575 ogg_mux->pulling->eos = TRUE;
1579 /* We could end up pushing from the best pad instead, so check that
1581 if (best && best != ogg_mux->pulling) {
1582 next_buf = gst_collect_pads2_peek (ogg_mux->collect, &best->collect);
1585 gst_buffer_unref (next_buf);
1587 GST_DEBUG_OBJECT (best->collect.pad, "setting eos to true");
1592 /* if we were already pulling from one pad, but the new "best" buffer is
1593 * from another pad, we need to check if we have reason to flush a page
1594 * for the pad we were pulling from before */
1595 if (ogg_mux->pulling && best &&
1596 ogg_mux->pulling != best && ogg_mux->pulling->buffer) {
1597 GstOggPadData *pad = ogg_mux->pulling;
1598 GstClockTime last_ts = GST_BUFFER_END_TIME (pad->buffer);
1600 /* if the next packet in the current page is going to make the page
1601 * too long, we need to flush */
1602 if (last_ts > ogg_mux->next_ts + ogg_mux->max_delay) {
1605 GST_LOG_OBJECT (pad->collect.pad,
1606 GST_GP_FORMAT " stored packet %" G_GINT64_FORMAT
1607 " will make page too long, flushing",
1608 GST_BUFFER_OFFSET_END (pad->buffer),
1609 (gint64) pad->map.stream.packetno);
1611 while (ogg_stream_flush (&pad->map.stream, &page)) {
1612 /* end time of this page is the timestamp of the next buffer */
1613 ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
1614 /* Place page into the per-pad queue */
1615 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1617 /* increment the page number counter */
1619 /* mark other pages as delta */
1620 pad->first_delta = TRUE;
1622 pad->new_page = TRUE;
1623 ogg_mux->pulling = NULL;
1627 /* if we don't know which pad to pull on, use the best one */
1628 if (ogg_mux->pulling == NULL) {
1629 ogg_mux->pulling = best;
1630 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from best pad");
1632 /* remember timestamp and gp time of first buffer for this new pad */
1633 if (ogg_mux->pulling != NULL) {
1634 ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer);
1635 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "updated times, next ts %"
1636 GST_TIME_FORMAT, GST_TIME_ARGS (ogg_mux->next_ts));
1638 /* no pad to pull on, send EOS */
1639 gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1640 return GST_FLOW_WRONG_STATE;
1644 if (ogg_mux->need_headers) {
1645 ret = gst_ogg_mux_send_headers (ogg_mux);
1646 ogg_mux->need_headers = FALSE;
1649 /* we are pulling from a pad, continue to do so until a page
1650 * has been filled and queued */
1651 if (ogg_mux->pulling != NULL) {
1654 GstBuffer *buf, *tmpbuf;
1655 GstOggPadData *pad = ogg_mux->pulling;
1657 gboolean force_flush;
1659 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from pad");
1661 /* now see if we have a buffer */
1664 GST_DEBUG_OBJECT (ogg_mux, "pad was EOS");
1665 ogg_mux->pulling = NULL;
1669 delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
1670 duration = GST_BUFFER_DURATION (buf);
1672 /* if the current "next timestamp" on the pad is unset, then this is the
1673 * first packet on the new page. Update our pad's page timestamp */
1674 if (ogg_mux->pulling->timestamp == GST_CLOCK_TIME_NONE) {
1675 ogg_mux->pulling->timestamp = GST_BUFFER_TIMESTAMP (buf);
1676 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad,
1677 "updated pad timestamp to %" GST_TIME_FORMAT,
1678 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
1680 /* create a packet from the buffer */
1681 packet.packet = GST_BUFFER_DATA (buf);
1682 packet.bytes = GST_BUFFER_SIZE (buf);
1683 packet.granulepos = GST_BUFFER_OFFSET_END (buf);
1684 if (packet.granulepos == -1)
1685 packet.granulepos = 0;
1686 /* mark BOS and packet number */
1687 packet.b_o_s = (pad->packetno == 0);
1688 packet.packetno = pad->packetno++;
1689 GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
1690 " packet %" G_GINT64_FORMAT " (%ld bytes) created from buffer",
1691 GST_GP_CAST (packet.granulepos), (gint64) packet.packetno,
1694 packet.e_o_s = ogg_mux->pulling->eos ? 1 : 0;
1697 /* we flush when we see a new keyframe */
1698 force_flush = (pad->prev_delta && !delta_unit)
1699 || pad->map.always_flush_page;
1700 if (duration != -1) {
1701 pad->duration += duration;
1702 /* if page duration exceeds max, flush page */
1703 if (pad->duration > ogg_mux->max_page_delay) {
1709 if (GST_BUFFER_IS_DISCONT (buf)) {
1710 if (pad->data_pushed) {
1711 GST_LOG_OBJECT (pad->collect.pad, "got discont");
1713 /* No public API for this; hack things in */
1714 pad->map.stream.pageno++;
1717 GST_LOG_OBJECT (pad->collect.pad, "discont at stream start");
1721 /* flush the currently built page if necessary */
1723 GST_LOG_OBJECT (pad->collect.pad,
1724 GST_GP_FORMAT " forced flush of page before this packet",
1725 GST_BUFFER_OFFSET_END (pad->buffer));
1726 while (ogg_stream_flush (&pad->map.stream, &page)) {
1727 /* end time of this page is the timestamp of the next buffer */
1728 ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
1729 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1732 /* increment the page number counter */
1734 /* mark other pages as delta */
1735 pad->first_delta = TRUE;
1737 pad->new_page = TRUE;
1740 /* if this is the first packet of a new page figure out the delta flag */
1741 if (pad->new_page) {
1743 /* mark the page as delta */
1744 pad->first_delta = TRUE;
1746 /* got a keyframe */
1747 if (ogg_mux->delta_pad == pad) {
1748 /* if we get it on the pad with deltaunits,
1749 * we mark the page as non delta */
1750 pad->first_delta = FALSE;
1751 } else if (ogg_mux->delta_pad != NULL) {
1752 /* if there are pads with delta frames, we
1753 * must mark this one as delta */
1754 pad->first_delta = TRUE;
1756 pad->first_delta = FALSE;
1759 pad->new_page = FALSE;
1762 /* save key unit to track delta->key unit transitions */
1763 pad->prev_delta = delta_unit;
1765 /* swap the packet in */
1766 if (packet.e_o_s == 1)
1767 GST_DEBUG_OBJECT (pad->collect.pad, "swapping in EOS packet");
1768 if (packet.b_o_s == 1)
1769 GST_DEBUG_OBJECT (pad->collect.pad, "swapping in BOS packet");
1771 ogg_stream_packetin (&pad->map.stream, &packet);
1772 pad->data_pushed = TRUE;
1774 gp_time = GST_BUFFER_OFFSET (pad->buffer);
1775 granulepos = GST_BUFFER_OFFSET_END (pad->buffer);
1776 timestamp = GST_BUFFER_TIMESTAMP (pad->buffer);
1778 GST_LOG_OBJECT (pad->collect.pad,
1779 GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", gp time %"
1780 GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT " packetin'd",
1781 granulepos, (gint64) packet.packetno, GST_TIME_ARGS (gp_time),
1782 GST_TIME_ARGS (timestamp));
1783 /* don't need the old buffer anymore */
1784 gst_buffer_unref (pad->buffer);
1785 /* store new readahead buffer */
1786 pad->buffer = tmpbuf;
1788 /* let ogg write out the pages now. The packet we got could end
1789 * up in more than one page so we need to write them all */
1790 if (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1791 /* we have a new page, so we need to timestamp it correctly.
1792 * if this fresh packet ends on this page, then the page's granulepos
1793 * comes from that packet, and we should set this buffer's timestamp */
1795 GST_LOG_OBJECT (pad->collect.pad,
1796 GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", time %"
1797 GST_TIME_FORMAT ") caused new page",
1798 granulepos, (gint64) packet.packetno, GST_TIME_ARGS (timestamp));
1799 GST_LOG_OBJECT (pad->collect.pad,
1800 GST_GP_FORMAT " new page %ld",
1801 GST_GP_CAST (ogg_page_granulepos (&page)), pad->map.stream.pageno);
1803 if (ogg_page_granulepos (&page) == granulepos) {
1804 /* the packet we streamed in finishes on the current page,
1805 * because the page's granulepos is the granulepos of the last
1806 * packet completed on that page,
1807 * so update the timestamp that we will give to the page */
1808 GST_LOG_OBJECT (pad->collect.pad,
1810 " packet finishes on current page, updating gp time to %"
1811 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (gp_time));
1812 pad->gp_time = gp_time;
1814 GST_LOG_OBJECT (pad->collect.pad,
1816 " packet spans beyond current page, keeping old gp time %"
1817 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (pad->gp_time));
1821 /* end time of this page is the timestamp of the next buffer */
1822 pad->timestamp_end = timestamp;
1823 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, pad->first_delta);
1825 /* mark next pages as delta */
1826 pad->first_delta = TRUE;
1828 /* use an inner loop here to flush the remaining pages and
1829 * mark them as delta frames as well */
1830 while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1831 if (ogg_page_granulepos (&page) == granulepos) {
1832 /* the page has taken up the new packet completely, which means
1833 * the packet ends the page and we can update the gp time
1834 * before pushing out */
1835 pad->gp_time = gp_time;
1838 /* we have a complete page now, we can push the page
1839 * and make sure to pull on a new pad the next time around */
1840 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1842 /* increment the page number counter */
1845 /* need a new page as well */
1846 pad->new_page = TRUE;
1848 /* we're done pulling on this pad, make sure to choose a new
1849 * pad for pulling in the next iteration */
1850 ogg_mux->pulling = NULL;
1853 /* Update the gp time, if necessary, since any future page will have at
1854 * least this gp time.
1856 if (pad->gp_time < gp_time) {
1857 pad->gp_time = gp_time;
1858 GST_LOG_OBJECT (pad->collect.pad,
1859 "Updated running gp time of pad %" GST_PTR_FORMAT
1860 " to %" GST_TIME_FORMAT, pad->collect.pad, GST_TIME_ARGS (gp_time));
1869 * Checks if all pads are EOS'd by peeking.
1871 * Returns TRUE if all pads are EOS.
1874 all_pads_eos (GstCollectPads2 * pads)
1880 GstOggPadData *oggpad = (GstOggPadData *) walk->data;
1882 GST_DEBUG_OBJECT (oggpad->collect.pad,
1883 "oggpad %p eos %d", oggpad, oggpad->eos);
1885 if (oggpad->eos == FALSE)
1888 walk = g_slist_next (walk);
1894 /* This function is called when there is data on all pads.
1896 * It finds a pad to pull on, this is done by looking at the buffers
1897 * to decide which one to use, and using the 'oldest' one first. It then calls
1898 * gst_ogg_mux_process_best_pad() to process as much data as possible.
1900 * If all the pads have received EOS, it flushes out all data by continually
1901 * getting the best pad and calling gst_ogg_mux_process_best_pad() until they
1902 * are all empty, and then sends EOS.
1904 static GstFlowReturn
1905 gst_ogg_mux_collected (GstCollectPads2 * pads, GstOggMux * ogg_mux)
1907 GstOggPadData *best;
1911 GST_LOG_OBJECT (ogg_mux, "collected");
1913 /* queue buffers on all pads; find a buffer with the lowest timestamp */
1914 best = gst_ogg_mux_queue_pads (ogg_mux, &popped);
1919 if (best == NULL || best->buffer == NULL) {
1920 /* This is not supposed to happen */
1921 return GST_FLOW_ERROR;
1924 ret = gst_ogg_mux_process_best_pad (ogg_mux, best);
1926 if (best->eos && all_pads_eos (pads)) {
1927 gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1928 return GST_FLOW_UNEXPECTED;
1935 gst_ogg_mux_get_property (GObject * object,
1936 guint prop_id, GValue * value, GParamSpec * pspec)
1940 ogg_mux = GST_OGG_MUX (object);
1944 g_value_set_uint64 (value, ogg_mux->max_delay);
1946 case ARG_MAX_PAGE_DELAY:
1947 g_value_set_uint64 (value, ogg_mux->max_page_delay);
1949 case ARG_MAX_TOLERANCE:
1950 g_value_set_uint64 (value, ogg_mux->max_tolerance);
1953 g_value_set_boolean (value, ogg_mux->use_skeleton);
1956 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1962 gst_ogg_mux_set_property (GObject * object,
1963 guint prop_id, const GValue * value, GParamSpec * pspec)
1967 ogg_mux = GST_OGG_MUX (object);
1971 ogg_mux->max_delay = g_value_get_uint64 (value);
1973 case ARG_MAX_PAGE_DELAY:
1974 ogg_mux->max_page_delay = g_value_get_uint64 (value);
1976 case ARG_MAX_TOLERANCE:
1977 ogg_mux->max_tolerance = g_value_get_uint64 (value);
1980 ogg_mux->use_skeleton = g_value_get_boolean (value);
1983 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1988 /* reset all variables in the ogg pads. */
1990 gst_ogg_mux_init_collectpads (GstCollectPads2 * collect)
1994 walk = collect->data;
1996 GstOggPadData *oggpad = (GstOggPadData *) walk->data;
1998 ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
1999 oggpad->packetno = 0;
2001 oggpad->eos = FALSE;
2002 /* we assume there will be some control data first for this pad */
2003 oggpad->state = GST_OGG_PAD_STATE_CONTROL;
2004 oggpad->new_page = TRUE;
2005 oggpad->first_delta = FALSE;
2006 oggpad->prev_delta = FALSE;
2007 oggpad->data_pushed = FALSE;
2008 oggpad->pagebuffers = g_queue_new ();
2010 gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
2012 walk = g_slist_next (walk);
2016 /* Clear all buffers from the collectpads object */
2018 gst_ogg_mux_clear_collectpads (GstCollectPads2 * collect)
2022 for (walk = collect->data; walk; walk = g_slist_next (walk)) {
2023 GstOggPadData *oggpad = (GstOggPadData *) walk->data;
2026 ogg_stream_clear (&oggpad->map.stream);
2028 while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
2029 gst_buffer_unref (buf);
2031 g_queue_free (oggpad->pagebuffers);
2032 oggpad->pagebuffers = NULL;
2034 if (oggpad->buffer) {
2035 gst_buffer_unref (oggpad->buffer);
2036 oggpad->buffer = NULL;
2040 gst_tag_list_free (oggpad->tags);
2041 oggpad->tags = NULL;
2044 gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
2048 static GstStateChangeReturn
2049 gst_ogg_mux_change_state (GstElement * element, GstStateChange transition)
2052 GstStateChangeReturn ret;
2054 ogg_mux = GST_OGG_MUX (element);
2056 switch (transition) {
2057 case GST_STATE_CHANGE_NULL_TO_READY:
2059 case GST_STATE_CHANGE_READY_TO_PAUSED:
2060 gst_ogg_mux_clear (ogg_mux);
2061 gst_ogg_mux_init_collectpads (ogg_mux->collect);
2062 gst_collect_pads2_start (ogg_mux->collect);
2064 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2066 case GST_STATE_CHANGE_PAUSED_TO_READY:
2067 gst_collect_pads2_stop (ogg_mux->collect);
2073 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2075 switch (transition) {
2076 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2078 case GST_STATE_CHANGE_PAUSED_TO_READY:
2079 gst_ogg_mux_clear_collectpads (ogg_mux->collect);
2081 case GST_STATE_CHANGE_READY_TO_NULL:
2091 gst_ogg_mux_plugin_init (GstPlugin * plugin)
2093 GST_DEBUG_CATEGORY_INIT (gst_ogg_mux_debug, "oggmux", 0, "ogg muxer");
2095 return gst_element_register (plugin, "oggmux", GST_RANK_PRIMARY,