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,width=320,height=240 ! videoconvert ! 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 video_sink_factory =
104 GST_STATIC_PAD_TEMPLATE ("video_%u",
107 GST_STATIC_CAPS ("video/x-theora; "
108 "application/x-ogm-video; video/x-dirac; "
109 "video/x-smoke; video/x-vp8; ")
112 static GstStaticPadTemplate audio_sink_factory =
113 GST_STATIC_PAD_TEMPLATE ("audio_%u",
117 ("audio/x-vorbis; audio/x-flac; audio/x-speex; audio/x-celt; "
118 "application/x-ogm-audio; audio/x-opus")
121 static GstStaticPadTemplate subtitle_sink_factory =
122 GST_STATIC_PAD_TEMPLATE ("subtitle_%u",
125 GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) TRUE; "
126 "subtitle/x-kate; application/x-kate")
129 static void gst_ogg_mux_finalize (GObject * object);
132 gst_ogg_mux_collected (GstCollectPads2 * pads, GstOggMux * ogg_mux);
133 static gboolean gst_ogg_mux_handle_src_event (GstPad * pad, GstObject * parent,
135 static GstPad *gst_ogg_mux_request_new_pad (GstElement * element,
136 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
137 static void gst_ogg_mux_release_pad (GstElement * element, GstPad * pad);
139 static void gst_ogg_mux_set_property (GObject * object,
140 guint prop_id, const GValue * value, GParamSpec * pspec);
141 static void gst_ogg_mux_get_property (GObject * object,
142 guint prop_id, GValue * value, GParamSpec * pspec);
143 static GstStateChangeReturn gst_ogg_mux_change_state (GstElement * element,
144 GstStateChange transition);
146 /*static guint gst_ogg_mux_signals[LAST_SIGNAL] = { 0 }; */
147 #define gst_ogg_mux_parent_class parent_class
148 G_DEFINE_TYPE_WITH_CODE (GstOggMux, gst_ogg_mux, GST_TYPE_ELEMENT,
149 G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
152 gst_ogg_mux_class_init (GstOggMuxClass * klass)
154 GObjectClass *gobject_class;
155 GstElementClass *gstelement_class;
157 gobject_class = (GObjectClass *) klass;
158 gstelement_class = (GstElementClass *) klass;
160 gobject_class->finalize = gst_ogg_mux_finalize;
161 gobject_class->get_property = gst_ogg_mux_get_property;
162 gobject_class->set_property = gst_ogg_mux_set_property;
164 gst_element_class_add_pad_template (gstelement_class,
165 gst_static_pad_template_get (&src_factory));
166 gst_element_class_add_pad_template (gstelement_class,
167 gst_static_pad_template_get (&video_sink_factory));
168 gst_element_class_add_pad_template (gstelement_class,
169 gst_static_pad_template_get (&audio_sink_factory));
170 gst_element_class_add_pad_template (gstelement_class,
171 gst_static_pad_template_get (&subtitle_sink_factory));
173 gst_element_class_set_details_simple (gstelement_class,
174 "Ogg muxer", "Codec/Muxer",
175 "mux ogg streams (info about ogg: http://xiph.org)",
176 "Wim Taymans <wim@fluendo.com>");
178 gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad;
179 gstelement_class->release_pad = gst_ogg_mux_release_pad;
181 g_object_class_install_property (gobject_class, ARG_MAX_DELAY,
182 g_param_spec_uint64 ("max-delay", "Max delay",
183 "Maximum delay in multiplexing streams", 0, G_MAXUINT64,
185 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
186 g_object_class_install_property (gobject_class, ARG_MAX_PAGE_DELAY,
187 g_param_spec_uint64 ("max-page-delay", "Max page delay",
188 "Maximum delay for sending out a page", 0, G_MAXUINT64,
189 DEFAULT_MAX_PAGE_DELAY,
190 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
191 g_object_class_install_property (gobject_class, ARG_MAX_TOLERANCE,
192 g_param_spec_uint64 ("max-tolerance", "Max time tolerance",
193 "Maximum timestamp difference for maintaining perfect granules",
194 0, G_MAXUINT64, DEFAULT_MAX_TOLERANCE,
195 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
196 g_object_class_install_property (gobject_class, ARG_SKELETON,
197 g_param_spec_boolean ("skeleton", "Skeleton",
198 "Whether to include a Skeleton track",
200 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
202 gstelement_class->change_state = gst_ogg_mux_change_state;
207 static const GstEventMask *
208 gst_ogg_mux_get_sink_event_masks (GstPad * pad)
210 static const GstEventMask gst_ogg_mux_sink_event_masks[] = {
212 {GST_EVENT_DISCONTINUOUS, 0},
216 return gst_ogg_mux_sink_event_masks;
221 gst_ogg_mux_clear (GstOggMux * ogg_mux)
223 ogg_mux->pulling = NULL;
224 ogg_mux->need_headers = TRUE;
225 ogg_mux->delta_pad = NULL;
227 ogg_mux->next_ts = 0;
228 ogg_mux->last_ts = GST_CLOCK_TIME_NONE;
232 gst_ogg_mux_init (GstOggMux * ogg_mux)
234 GstElementClass *klass = GST_ELEMENT_GET_CLASS (ogg_mux);
237 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
239 gst_pad_set_event_function (ogg_mux->srcpad, gst_ogg_mux_handle_src_event);
240 gst_element_add_pad (GST_ELEMENT (ogg_mux), ogg_mux->srcpad);
242 GST_OBJECT_FLAG_SET (GST_ELEMENT (ogg_mux), GST_OGG_FLAG_BOS);
244 /* seed random number generator for creation of serial numbers */
247 ogg_mux->collect = gst_collect_pads2_new ();
248 gst_collect_pads2_set_function (ogg_mux->collect,
249 (GstCollectPads2Function) GST_DEBUG_FUNCPTR (gst_ogg_mux_collected),
252 ogg_mux->max_delay = DEFAULT_MAX_DELAY;
253 ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY;
254 ogg_mux->max_tolerance = DEFAULT_MAX_TOLERANCE;
256 gst_ogg_mux_clear (ogg_mux);
260 gst_ogg_mux_finalize (GObject * object)
264 ogg_mux = GST_OGG_MUX (object);
266 if (ogg_mux->collect) {
267 gst_object_unref (ogg_mux->collect);
268 ogg_mux->collect = NULL;
271 G_OBJECT_CLASS (parent_class)->finalize (object);
275 gst_ogg_mux_ogg_pad_destroy_notify (GstCollectData2 * data)
277 GstOggPadData *oggpad = (GstOggPadData *) data;
280 ogg_stream_clear (&oggpad->map.stream);
281 gst_caps_replace (&oggpad->map.caps, NULL);
283 if (oggpad->pagebuffers) {
284 while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
285 gst_buffer_unref (buf);
287 g_queue_free (oggpad->pagebuffers);
288 oggpad->pagebuffers = NULL;
292 static GstPadLinkReturn
293 gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer)
297 ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
299 GST_DEBUG_OBJECT (ogg_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad));
301 gst_object_unref (ogg_mux);
303 return GST_PAD_LINK_OK;
307 gst_ogg_mux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
309 GstOggMux *ogg_mux = GST_OGG_MUX (parent);
310 GstOggPadData *ogg_pad = (GstOggPadData *) gst_pad_get_element_private (pad);
311 gboolean ret = FALSE;
313 GST_DEBUG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event));
315 switch (GST_EVENT_TYPE (event)) {
316 case GST_EVENT_SEGMENT:
318 const GstSegment *segment;
320 gst_event_parse_segment (event, &segment);
322 /* We don't support non time NEWSEGMENT events */
323 if (segment->format != GST_FORMAT_TIME) {
324 gst_event_unref (event);
329 gst_segment_copy_into (segment, &ogg_pad->segment);
332 case GST_EVENT_FLUSH_STOP:{
333 gst_segment_init (&ogg_pad->segment, GST_FORMAT_TIME);
339 gst_event_parse_tag (event, &tags);
340 tags = gst_tag_list_merge (ogg_pad->tags, tags, GST_TAG_MERGE_APPEND);
342 gst_tag_list_free (ogg_pad->tags);
343 ogg_pad->tags = tags;
345 GST_DEBUG_OBJECT (ogg_mux, "Got tags %" GST_PTR_FORMAT, ogg_pad->tags);
352 /* now GstCollectPads can take care of the rest, e.g. EOS */
354 ret = ogg_pad->collect_event (pad, parent, event);
360 gst_ogg_mux_is_serialno_present (GstOggMux * ogg_mux, guint32 serialno)
364 walk = ogg_mux->collect->data;
366 GstOggPadData *pad = (GstOggPadData *) walk->data;
367 if (pad->map.serialno == serialno)
376 gst_ogg_mux_generate_serialno (GstOggMux * ogg_mux)
381 serialno = g_random_int_range (0, G_MAXINT32);
382 } while (gst_ogg_mux_is_serialno_present (ogg_mux, serialno));
388 gst_ogg_mux_request_new_pad (GstElement * element,
389 GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps)
393 GstElementClass *klass;
395 g_return_val_if_fail (templ != NULL, NULL);
397 if (templ->direction != GST_PAD_SINK)
398 goto wrong_direction;
400 g_return_val_if_fail (GST_IS_OGG_MUX (element), NULL);
401 ogg_mux = GST_OGG_MUX (element);
403 klass = GST_ELEMENT_GET_CLASS (element);
405 if (templ != gst_element_class_get_pad_template (klass, "video_%u") &&
406 templ != gst_element_class_get_pad_template (klass, "audio_%u") &&
407 templ != gst_element_class_get_pad_template (klass, "subtitle_%u")) {
415 if (req_name == NULL || strlen (req_name) < 6) {
416 /* no name given when requesting the pad, use random serial number */
417 serial = gst_ogg_mux_generate_serialno (ogg_mux);
419 /* parse serial number from requested padname */
420 unsigned long long_serial;
422 long_serial = strtoul (&req_name[5], &endptr, 10);
423 if ((endptr && *endptr) || (long_serial & ~0xffffffff)) {
424 GST_WARNING_OBJECT (ogg_mux, "Invalid serial number specification: %s",
428 serial = (guint32) long_serial;
430 /* create new pad with the name */
431 GST_DEBUG_OBJECT (ogg_mux, "Creating new pad for serial %d", serial);
433 if (templ == gst_element_class_get_pad_template (klass, "video_%u")) {
434 name = g_strdup_printf ("video_%u", serial);
435 } else if (templ == gst_element_class_get_pad_template (klass, "audio_%u")) {
436 name = g_strdup_printf ("audio_%u", serial);
437 } else if (templ == gst_element_class_get_pad_template (klass,
439 name = g_strdup_printf ("subtitle_%u", serial);
441 newpad = gst_pad_new_from_template (templ, name);
444 /* construct our own wrapper data structure for the pad to
445 * keep track of its status */
447 GstOggPadData *oggpad;
449 oggpad = (GstOggPadData *)
450 gst_collect_pads2_add_pad_full (ogg_mux->collect, newpad,
451 sizeof (GstOggPadData), gst_ogg_mux_ogg_pad_destroy_notify, TRUE);
452 ogg_mux->active_pads++;
454 oggpad->map.serialno = serial;
455 ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
456 oggpad->packetno = 0;
459 /* we assume there will be some control data first for this pad */
460 oggpad->state = GST_OGG_PAD_STATE_CONTROL;
461 oggpad->new_page = TRUE;
462 oggpad->first_delta = FALSE;
463 oggpad->prev_delta = FALSE;
464 oggpad->data_pushed = FALSE;
465 oggpad->pagebuffers = g_queue_new ();
466 oggpad->map.headers = NULL;
467 oggpad->map.queued = NULL;
468 oggpad->next_granule = 0;
469 oggpad->keyframe_granule = -1;
471 gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
473 oggpad->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
474 gst_pad_set_event_function (newpad,
475 GST_DEBUG_FUNCPTR (gst_ogg_mux_sink_event));
479 /* setup some pad functions */
480 gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect);
482 /* dd the pad to the element */
483 gst_element_add_pad (element, newpad);
490 g_warning ("ogg_mux: request pad that is not a SINK pad\n");
495 g_warning ("ogg_mux: this is not our template!\n");
501 gst_ogg_mux_release_pad (GstElement * element, GstPad * pad)
505 ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
507 gst_collect_pads2_remove_pad (ogg_mux->collect, pad);
508 gst_element_remove_pad (element, pad);
510 gst_object_unref (ogg_mux);
515 gst_ogg_mux_handle_src_event (GstPad * pad, GstObject * parent,
520 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
524 /* disable seeking for now */
530 return gst_pad_event_default (pad, parent, event);
534 gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
538 /* allocate space for header and body */
539 buffer = gst_buffer_new_and_alloc (page->header_len + page->body_len);
540 gst_buffer_fill (buffer, 0, page->header, page->header_len);
541 gst_buffer_fill (buffer, page->header_len, page->body, page->body_len);
543 /* Here we set granulepos as our OFFSET_END to give easy direct access to
544 * this value later. Before we push it, we reset this to OFFSET + SIZE
545 * (see gst_ogg_mux_push_buffer). */
546 GST_BUFFER_OFFSET_END (buffer) = ogg_page_granulepos (page);
548 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
550 GST_LOG_OBJECT (mux, GST_GP_FORMAT
551 " created buffer %p from ogg page",
552 GST_GP_CAST (ogg_page_granulepos (page)), buffer);
558 gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer,
559 GstOggPadData * oggpad)
561 /* fix up OFFSET and OFFSET_END again */
562 GST_BUFFER_OFFSET (buffer) = mux->offset;
563 mux->offset += gst_buffer_get_size (buffer);
564 GST_BUFFER_OFFSET_END (buffer) = mux->offset;
566 /* Ensure we have monotonically increasing timestamps in the output. */
567 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
568 gint64 run_time = GST_BUFFER_TIMESTAMP (buffer);
569 if (mux->last_ts != GST_CLOCK_TIME_NONE && run_time < mux->last_ts)
570 GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts;
572 mux->last_ts = run_time;
575 return gst_pad_push (mux->srcpad, buffer);
578 /* if all queues have at least one page, dequeue the page with the lowest
581 gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret)
584 GstOggPadData *opad = NULL; /* "oldest" pad */
585 GstClockTime oldest = GST_CLOCK_TIME_NONE;
586 GstBuffer *buf = NULL;
587 gboolean ret = FALSE;
589 *flowret = GST_FLOW_OK;
591 walk = mux->collect->data;
593 GstOggPadData *pad = (GstOggPadData *) walk->data;
595 /* We need each queue to either be at EOS, or have one or more pages
596 * available with a set granulepos (i.e. not -1), otherwise we don't have
597 * enough data yet to determine which stream needs to go next for correct
599 if (pad->pagebuffers->length == 0) {
601 GST_LOG_OBJECT (pad->collect.pad,
602 "pad is EOS, skipping for dequeue decision");
604 GST_LOG_OBJECT (pad->collect.pad,
605 "no pages in this queue, can't dequeue");
609 /* We then need to check for a non-negative granulepos */
611 gboolean valid = FALSE;
613 for (i = 0; i < pad->pagebuffers->length; i++) {
614 buf = g_queue_peek_nth (pad->pagebuffers, i);
615 /* Here we check the OFFSET_END, which is actually temporarily the
616 * granulepos value for this buffer */
617 if (GST_BUFFER_OFFSET_END (buf) != -1) {
623 GST_LOG_OBJECT (pad->collect.pad,
624 "No page timestamps in queue, can't dequeue");
629 walk = g_slist_next (walk);
632 walk = mux->collect->data;
634 GstOggPadData *pad = (GstOggPadData *) walk->data;
636 /* any page with a granulepos of -1 can be pushed immediately.
637 * TODO: it CAN be, but it seems silly to do so? */
638 buf = g_queue_peek_head (pad->pagebuffers);
639 while (buf && GST_BUFFER_OFFSET_END (buf) == -1) {
640 GST_LOG_OBJECT (pad->collect.pad, "[gp -1] pushing page");
641 g_queue_pop_head (pad->pagebuffers);
642 *flowret = gst_ogg_mux_push_buffer (mux, buf, pad);
643 buf = g_queue_peek_head (pad->pagebuffers);
648 /* if no oldest buffer yet, take this one */
649 if (oldest == GST_CLOCK_TIME_NONE) {
650 GST_LOG_OBJECT (mux, "no oldest yet, taking buffer %p from pad %"
651 GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
652 buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
653 oldest = GST_BUFFER_OFFSET (buf);
656 /* if we have an oldest, compare with this one */
657 if (GST_BUFFER_OFFSET (buf) < oldest) {
658 GST_LOG_OBJECT (mux, "older buffer %p, taking from pad %"
659 GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
660 buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
661 oldest = GST_BUFFER_OFFSET (buf);
666 walk = g_slist_next (walk);
669 if (oldest != GST_CLOCK_TIME_NONE) {
671 buf = g_queue_pop_head (opad->pagebuffers);
672 GST_LOG_OBJECT (opad->collect.pad,
673 GST_GP_FORMAT " pushing oldest page buffer %p (granulepos time %"
674 GST_TIME_FORMAT ")", GST_BUFFER_OFFSET_END (buf), buf,
675 GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
676 *flowret = gst_ogg_mux_push_buffer (mux, buf, opad);
683 /* put the given ogg page on a per-pad queue, timestamping it correctly.
684 * after that, dequeue and push as many pages as possible.
685 * Caller should make sure:
686 * pad->timestamp was set with the timestamp of the first packet put
688 * pad->timestamp_end was set with the timestamp + duration of the last packet
690 * pad->gp_time was set with the time matching the gp of the last
691 * packet put on the page
693 * will also reset timestamp and timestamp_end, so caller func can restart
697 gst_ogg_mux_pad_queue_page (GstOggMux * mux, GstOggPadData * pad,
698 ogg_page * page, gboolean delta)
701 GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);
703 /* take the timestamp of the first packet on this page */
704 GST_BUFFER_TIMESTAMP (buffer) = pad->timestamp;
705 GST_BUFFER_DURATION (buffer) = pad->timestamp_end - pad->timestamp;
706 /* take the gp time of the last completed packet on this page */
707 GST_BUFFER_OFFSET (buffer) = pad->gp_time;
709 /* the next page will start where the current page's end time leaves off */
710 pad->timestamp = pad->timestamp_end;
712 g_queue_push_tail (pad->pagebuffers, buffer);
713 GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
714 " queued buffer page %p (gp time %"
715 GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT
716 "), %d page buffers queued", GST_GP_CAST (ogg_page_granulepos (page)),
717 buffer, GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)),
718 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
719 g_queue_get_length (pad->pagebuffers));
721 while (gst_ogg_mux_dequeue_page (mux, &ret)) {
722 if (ret != GST_FLOW_OK)
730 * Given two pads, compare the buffers queued on it.
732 * 0 if they have an equal priority
733 * -1 if the first is better
734 * 1 if the second is better
735 * Priority decided by: a) validity, b) older timestamp, c) smaller number
739 gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPadData * first,
740 GstOggPadData * second)
742 guint64 firsttime, secondtime;
744 /* if the first pad doesn't contain anything or is even NULL, return
745 * the second pad as best candidate and vice versa */
751 /* no timestamp on first buffer, it must go first */
752 firsttime = GST_BUFFER_TIMESTAMP (first->buffer);
753 if (firsttime == GST_CLOCK_TIME_NONE)
756 /* no timestamp on second buffer, it must go first */
757 secondtime = GST_BUFFER_TIMESTAMP (second->buffer);
758 if (secondtime == GST_CLOCK_TIME_NONE)
761 /* first buffer has higher timestamp, second one should go first */
762 if (secondtime < firsttime)
764 /* second buffer has higher timestamp, first one should go first */
765 else if (secondtime > firsttime)
768 /* buffers with equal timestamps, prefer the pad that has the
769 * least number of pages muxed */
770 if (second->pageno < first->pageno)
772 else if (second->pageno > first->pageno)
776 /* same priority if all of the above failed */
781 gst_ogg_mux_decorate_buffer (GstOggMux * ogg_mux, GstOggPadData * pad,
785 gint64 duration, granule, limit;
786 GstClockTime next_time;
787 GstClockTimeDiff diff;
791 /* ensure messing with metadata is ok */
792 buf = gst_buffer_make_writable (buf);
794 /* convert time to running time, so we need no longer bother about that */
795 time = GST_BUFFER_TIMESTAMP (buf);
796 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
797 time = gst_segment_to_running_time (&pad->segment, GST_FORMAT_TIME, time);
798 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
799 gst_buffer_unref (buf);
802 GST_BUFFER_TIMESTAMP (buf) = time;
806 /* now come up with granulepos stuff corresponding to time */
807 if (!pad->have_type ||
808 pad->map.granulerate_n <= 0 || pad->map.granulerate_d <= 0)
811 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
813 duration = gst_ogg_stream_get_packet_duration (&pad->map, &packet);
814 gst_buffer_unmap (buf, packet.packet, size);
816 /* give up if no duration can be determined, relying on upstream */
817 if (G_UNLIKELY (duration < 0)) {
818 /* well, if some day we really could handle sparse input ... */
819 if (pad->map.is_sparse) {
824 GST_WARNING_OBJECT (pad->collect.pad,
825 "failed to determine packet duration");
829 GST_LOG_OBJECT (pad->collect.pad, "buffer ts %" GST_TIME_FORMAT
830 ", duration %" GST_TIME_FORMAT ", granule duration %" G_GINT64_FORMAT,
831 GST_TIME_ARGS (time), GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
834 /* determine granule corresponding to time,
835 * using the inverse of oggdemux' granule -> time */
837 /* see if interpolated granule matches good enough */
838 granule = pad->next_granule;
839 next_time = gst_ogg_stream_granule_to_time (&pad->map, pad->next_granule);
840 diff = GST_CLOCK_DIFF (next_time, time);
842 /* we tolerate deviation up to configured or within granule granularity */
843 limit = gst_ogg_stream_granule_to_time (&pad->map, 1) / 2;
844 limit = MAX (limit, ogg_mux->max_tolerance);
846 GST_LOG_OBJECT (pad->collect.pad, "expected granule %" G_GINT64_FORMAT " == "
847 "time %" GST_TIME_FORMAT " --> ts diff %" GST_TIME_FORMAT
848 " < tolerance %" GST_TIME_FORMAT " (?)",
849 granule, GST_TIME_ARGS (next_time), GST_TIME_ARGS (ABS (diff)),
850 GST_TIME_ARGS (limit));
853 /* if not good enough, determine granule based on time */
854 if (diff > limit || diff < -limit) {
855 granule = gst_util_uint64_scale_round (time, pad->map.granulerate_n,
856 GST_SECOND * pad->map.granulerate_d);
857 GST_DEBUG_OBJECT (pad->collect.pad,
858 "resyncing to determined granule %" G_GINT64_FORMAT, granule);
861 if (pad->map.is_ogm || pad->map.is_sparse) {
862 pad->next_granule = granule;
865 pad->next_granule = granule;
868 /* track previous keyframe */
869 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
870 pad->keyframe_granule = granule;
872 /* determine corresponding time and granulepos */
873 GST_BUFFER_OFFSET (buf) = gst_ogg_stream_granule_to_time (&pad->map, granule);
874 GST_BUFFER_OFFSET_END (buf) =
875 gst_ogg_stream_granule_to_granulepos (&pad->map, granule,
876 pad->keyframe_granule);
883 GST_DEBUG_OBJECT (pad->collect.pad, "could not determine granulepos, "
884 "falling back to upstream provided metadata");
890 /* make sure at least one buffer is queued on all pads, two if possible
892 * if pad->buffer == NULL, pad->next_buffer != NULL, then
893 * we do not know if the buffer is the last or not
894 * if pad->buffer != NULL, pad->next_buffer != NULL, then
895 * pad->buffer is not the last buffer for the pad
896 * if pad->buffer != NULL, pad->next_buffer == NULL, then
897 * pad->buffer if the last buffer for the pad
899 * returns a pointer to an oggpad that holds the best buffer, or
900 * NULL when no pad was usable. "best" means the buffer marked
901 * with the lowest timestamp. If best->buffer == NULL then either
902 * we're at EOS (popped = FALSE), or a buffer got dropped, so retry. */
903 static GstOggPadData *
904 gst_ogg_mux_queue_pads (GstOggMux * ogg_mux, gboolean * popped)
906 GstOggPadData *bestpad = NULL;
911 /* try to make sure we have a buffer from each usable pad first */
912 walk = ogg_mux->collect->data;
915 GstCollectData2 *data;
917 data = (GstCollectData2 *) walk->data;
918 pad = (GstOggPadData *) data;
920 walk = g_slist_next (walk);
922 GST_LOG_OBJECT (data->pad, "looking at pad for buffer");
924 /* try to get a new buffer for this pad if needed and possible */
925 if (pad->buffer == NULL) {
928 buf = gst_collect_pads2_pop (ogg_mux->collect, data);
929 GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
931 /* On EOS we get a NULL buffer */
935 if (ogg_mux->delta_pad == NULL &&
936 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
937 ogg_mux->delta_pad = pad;
939 /* if we need headers */
940 if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
941 /* and we have one */
946 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
949 /* if we're not yet in data mode, ensure we're setup on the first packet */
950 if (!pad->have_type) {
953 /* Use headers in caps, if any; this will allow us to be resilient
954 * to starting streams on the fly, and some streams (like VP8
955 * at least) do not send headers packets, as other muxers don't
956 * expect/need them. */
957 caps = gst_pad_get_current_caps (GST_PAD_CAST (data->pad));
959 gst_ogg_stream_setup_map_from_caps_headers (&pad->map, caps);
961 if (!pad->have_type) {
962 /* fallback on the packet */
963 pad->have_type = gst_ogg_stream_setup_map (&pad->map, &packet);
965 if (!pad->have_type) {
966 GST_ERROR_OBJECT (pad, "mapper didn't recognise input stream "
967 "(pad caps: %" GST_PTR_FORMAT ")", caps);
969 GST_DEBUG_OBJECT (pad, "caps detected: %" GST_PTR_FORMAT,
973 gst_caps_unref (caps);
977 is_header = gst_ogg_stream_packet_is_header (&pad->map, &packet);
978 else /* fallback (FIXME 0.11: remove IN_CAPS hack) */
979 is_header = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
981 gst_buffer_unmap (buf, packet.packet, size);
984 GST_DEBUG_OBJECT (ogg_mux,
985 "got header buffer in control state, ignoring");
987 pad->map.n_header_packets_seen++;
988 gst_buffer_unref (buf);
991 GST_DEBUG_OBJECT (ogg_mux,
992 "got data buffer in control state, switching to data mode");
993 /* this is a data buffer so switch to data state */
994 pad->state = GST_OGG_PAD_STATE_DATA;
996 /* check if this type of stream allows generating granulepos
997 * metadata here, if not, upstream will have to provide */
998 if (gst_ogg_stream_granule_to_granulepos (&pad->map, 1, 1) < 0) {
999 GST_WARNING_OBJECT (data->pad, "can not generate metadata; "
1000 "relying on upstream");
1001 /* disable metadata code path, otherwise not used anyway */
1002 pad->map.granulerate_n = 0;
1007 /* so now we should have a real data packet;
1008 * see that it is properly decorated */
1009 if (G_LIKELY (buf)) {
1010 buf = gst_ogg_mux_decorate_buffer (ogg_mux, pad, buf);
1011 if (G_UNLIKELY (!buf))
1012 GST_DEBUG_OBJECT (data->pad, "buffer clipped");
1019 /* we should have a buffer now, see if it is the best pad to
1022 if (gst_ogg_mux_compare_pads (ogg_mux, bestpad, pad) > 0) {
1023 GST_LOG_OBJECT (data->pad,
1024 "new best pad, with buffer %" GST_PTR_FORMAT, pad->buffer);
1035 gst_ogg_mux_get_headers (GstOggPadData * pad)
1038 GstStructure *structure;
1042 thepad = pad->collect.pad;
1044 GST_LOG_OBJECT (thepad, "getting headers");
1046 caps = gst_pad_get_current_caps (thepad);
1048 const GValue *streamheader;
1050 structure = gst_caps_get_structure (caps, 0);
1051 streamheader = gst_structure_get_value (structure, "streamheader");
1052 if (streamheader != NULL) {
1053 GST_LOG_OBJECT (thepad, "got header");
1054 if (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
1055 GArray *bufarr = g_value_peek_pointer (streamheader);
1058 GST_LOG_OBJECT (thepad, "got fixed list");
1060 for (i = 0; i < bufarr->len; i++) {
1061 GValue *bufval = &g_array_index (bufarr, GValue, i);
1063 GST_LOG_OBJECT (thepad, "item %d", i);
1064 if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
1065 GstBuffer *buf = g_value_peek_pointer (bufval);
1067 GST_LOG_OBJECT (thepad, "adding item %d to header list", i);
1069 gst_buffer_ref (buf);
1070 res = g_list_append (res, buf);
1074 GST_LOG_OBJECT (thepad, "streamheader is not fixed list");
1077 } else if (gst_structure_has_name (structure, "video/x-dirac")) {
1078 res = g_list_append (res, pad->buffer);
1081 GST_LOG_OBJECT (thepad, "caps don't have streamheader");
1083 gst_caps_unref (caps);
1085 GST_LOG_OBJECT (thepad, "got empty caps as negotiated format");
1091 gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
1093 GstStructure *structure;
1094 GValue array = { 0 };
1095 GList *walk = buffers;
1097 caps = gst_caps_make_writable (caps);
1099 structure = gst_caps_get_structure (caps, 0);
1101 /* put buffers in a fixed list */
1102 g_value_init (&array, GST_TYPE_ARRAY);
1105 GstBuffer *buf = GST_BUFFER (walk->data);
1107 GValue value = { 0 };
1112 GST_LOG ("Setting IN_CAPS on buffer of length %" G_GSIZE_FORMAT,
1113 gst_buffer_get_size (buf));
1114 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
1116 g_value_init (&value, GST_TYPE_BUFFER);
1117 copy = gst_buffer_copy (buf);
1118 gst_value_set_buffer (&value, copy);
1119 gst_buffer_unref (copy);
1120 gst_value_array_append_value (&array, &value);
1121 g_value_unset (&value);
1123 gst_structure_set_value (structure, "streamheader", &array);
1124 g_value_unset (&array);
1130 gst_ogg_mux_create_header_packet_with_flags (ogg_packet * packet,
1131 gboolean bos, gboolean eos)
1133 packet->granulepos = 0;
1134 /* mark BOS and packet number */
1135 packet->b_o_s = bos;
1137 packet->e_o_s = eos;
1141 gst_ogg_mux_create_header_packet (ogg_packet * packet, GstOggPadData * pad)
1143 gst_ogg_mux_create_header_packet_with_flags (packet, pad->packetno == 0, 0);
1144 packet->packetno = pad->packetno++;
1148 gst_ogg_mux_submit_skeleton_header_packet (GstOggMux * mux,
1149 ogg_stream_state * os, GstBuffer * buf, gboolean bos, gboolean eos)
1154 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1155 packet.bytes = size;
1156 gst_ogg_mux_create_header_packet_with_flags (&packet, bos, eos);
1157 ogg_stream_packetin (os, &packet);
1158 gst_buffer_unref (buf);
1162 gst_ogg_mux_make_fishead (GstOggMux * mux, ogg_stream_state * os)
1167 GST_DEBUG_OBJECT (mux, "Creating fishead");
1169 gst_byte_writer_init_with_size (&bw, 64, TRUE);
1170 gst_byte_writer_put_string_utf8 (&bw, "fishead");
1171 gst_byte_writer_put_int16_le (&bw, 3); /* version major */
1172 gst_byte_writer_put_int16_le (&bw, 0); /* version minor */
1173 gst_byte_writer_put_int64_le (&bw, 0); /* presentation time numerator */
1174 gst_byte_writer_put_int64_le (&bw, 1000); /* ...and denominator */
1175 gst_byte_writer_put_int64_le (&bw, 0); /* base time numerator */
1176 gst_byte_writer_put_int64_le (&bw, 1000); /* ...and denominator */
1177 gst_byte_writer_fill (&bw, ' ', 20); /* UTC time */
1178 g_assert (gst_byte_writer_get_pos (&bw) == 64);
1179 fishead = gst_byte_writer_reset_and_get_buffer (&bw);
1180 gst_ogg_mux_submit_skeleton_header_packet (mux, os, fishead, 1, 0);
1184 gst_ogg_mux_byte_writer_put_string_utf8 (GstByteWriter * bw, const char *s)
1186 gst_byte_writer_put_data (bw, (const guint8 *) s, strlen (s));
1190 gst_ogg_mux_add_fisbone_message_header (GstOggMux * mux, GstByteWriter * bw,
1191 const char *tag, const char *value)
1193 /* It is valid to pass NULL as the value to omit the tag */
1196 GST_DEBUG_OBJECT (mux, "Adding fisbone message header %s: %s", tag, value);
1197 gst_ogg_mux_byte_writer_put_string_utf8 (bw, tag);
1198 gst_ogg_mux_byte_writer_put_string_utf8 (bw, ": ");
1199 gst_ogg_mux_byte_writer_put_string_utf8 (bw, value);
1200 gst_ogg_mux_byte_writer_put_string_utf8 (bw, "\r\n");
1204 gst_ogg_mux_add_fisbone_message_header_from_tags (GstOggMux * mux,
1205 GstByteWriter * bw, const char *header, const char *tag,
1206 const GstTagList * tags)
1209 guint size = gst_tag_list_get_tag_size (tags, tag), n;
1210 GST_DEBUG_OBJECT (mux, "Found %u tags for name %s", size, tag);
1213 s = g_string_new ("");
1214 for (n = 0; n < size; ++n) {
1217 g_string_append (s, ", ");
1218 gst_tag_list_get_string_index (tags, tag, n, &tmp);
1219 g_string_append (s, tmp);
1222 gst_ogg_mux_add_fisbone_message_header (mux, bw, header, s->str);
1223 g_string_free (s, TRUE);
1226 /* This is a basic placeholder to generate roles for the tracks.
1227 For tracks with more than one video, both video tracks will get
1228 tagged with a "video/main" role, but we have no way of knowing
1229 which one is the main one, if any. We could just pick one. For
1230 audio, it's more complicated as we don't know which is music,
1231 which is dubbing, etc. For kate, we could take a pretty good
1232 guess based on the category, as role essentially is category.
1233 For now, leave this as is. */
1235 gst_ogg_mux_get_default_role (GstOggPadData * pad)
1237 const char *type = gst_ogg_stream_get_media_type (&pad->map);
1239 if (!strncmp (type, "video/", strlen ("video/")))
1240 return "video/main";
1241 if (!strncmp (type, "audio/", strlen ("audio/")))
1242 return "audio/main";
1243 if (!strcmp (type + strlen (type) - strlen ("kate"), "kate"))
1244 return "text/caption";
1250 gst_ogg_mux_make_fisbone (GstOggMux * mux, ogg_stream_state * os,
1251 GstOggPadData * pad)
1255 GST_DEBUG_OBJECT (mux,
1256 "Creating %s fisbone for serial %08x",
1257 gst_ogg_stream_get_media_type (&pad->map), pad->map.serialno);
1259 gst_byte_writer_init (&bw);
1260 gst_byte_writer_put_string_utf8 (&bw, "fisbone");
1261 gst_byte_writer_put_int32_le (&bw, 44); /* offset to message headers */
1262 gst_byte_writer_put_uint32_le (&bw, pad->map.serialno);
1263 gst_byte_writer_put_uint32_le (&bw, pad->map.n_header_packets);
1264 gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_n);
1265 gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_d);
1266 gst_byte_writer_put_uint64_le (&bw, 0); /* base granule */
1267 gst_byte_writer_put_uint32_le (&bw, pad->map.preroll);
1268 gst_byte_writer_put_uint8 (&bw, pad->map.granuleshift);
1269 gst_byte_writer_fill (&bw, 0, 3); /* padding */
1270 /* message header fields - MIME type for now */
1271 gst_ogg_mux_add_fisbone_message_header (mux, &bw, "Content-Type",
1272 gst_ogg_stream_get_media_type (&pad->map));
1273 gst_ogg_mux_add_fisbone_message_header (mux, &bw, "Role",
1274 gst_ogg_mux_get_default_role (pad));
1275 gst_ogg_mux_add_fisbone_message_header_from_tags (mux, &bw, "Language",
1276 GST_TAG_LANGUAGE_CODE, pad->tags);
1277 gst_ogg_mux_add_fisbone_message_header_from_tags (mux, &bw, "Title",
1278 GST_TAG_TITLE, pad->tags);
1280 gst_ogg_mux_submit_skeleton_header_packet (mux, os,
1281 gst_byte_writer_reset_and_get_buffer (&bw), 0, 0);
1285 gst_ogg_mux_make_fistail (GstOggMux * mux, ogg_stream_state * os)
1287 GST_DEBUG_OBJECT (mux, "Creating fistail");
1289 gst_ogg_mux_submit_skeleton_header_packet (mux, os,
1290 gst_buffer_new_and_alloc (0), 0, 1);
1294 * For each pad we need to write out one (small) header in one
1295 * page that allows decoders to identify the type of the stream.
1296 * After that we need to write out all extra info for the decoders.
1297 * In the case of a codec that also needs data as configuration, we can
1298 * find that info in the streamcaps.
1299 * After writing the headers we must start a new page for the data.
1301 static GstFlowReturn
1302 gst_ogg_mux_send_headers (GstOggMux * mux)
1305 GList *hbufs, *hwalk;
1309 ogg_stream_state skeleton_stream;
1314 GST_LOG_OBJECT (mux, "collecting headers");
1316 walk = mux->collect->data;
1321 pad = (GstOggPadData *) walk->data;
1322 thepad = pad->collect.pad;
1324 walk = g_slist_next (walk);
1326 GST_LOG_OBJECT (mux, "looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
1328 /* if the pad has no buffer, we don't care */
1329 if (pad->buffer == NULL)
1332 /* now figure out the headers */
1333 pad->map.headers = gst_ogg_mux_get_headers (pad);
1336 GST_LOG_OBJECT (mux, "creating BOS pages");
1337 walk = mux->collect->data;
1344 GstStructure *structure;
1348 pad = (GstOggPadData *) walk->data;
1349 thepad = pad->collect.pad;
1350 caps = gst_pad_get_current_caps (thepad);
1351 structure = gst_caps_get_structure (caps, 0);
1357 GST_LOG_OBJECT (thepad, "looping over headers");
1359 if (pad->map.headers) {
1360 buf = GST_BUFFER (pad->map.headers->data);
1361 pad->map.headers = g_list_remove (pad->map.headers, buf);
1362 } else if (pad->buffer) {
1364 gst_buffer_ref (buf);
1366 /* fixme -- should be caught in the previous list traversal. */
1367 GST_OBJECT_LOCK (thepad);
1368 g_critical ("No headers or buffers on pad %s:%s",
1369 GST_DEBUG_PAD_NAME (thepad));
1370 GST_OBJECT_UNLOCK (thepad);
1374 /* create a packet from the buffer */
1375 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1376 packet.bytes = size;
1378 gst_ogg_mux_create_header_packet (&packet, pad);
1380 /* swap the packet in */
1381 ogg_stream_packetin (&pad->map.stream, &packet);
1383 gst_buffer_unmap (buf, packet.packet, size);
1384 gst_buffer_unref (buf);
1386 GST_LOG_OBJECT (thepad, "flushing out BOS page");
1387 if (!ogg_stream_flush (&pad->map.stream, &page))
1388 g_critical ("Could not flush BOS page");
1390 hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1392 GST_LOG_OBJECT (mux, "swapped out page with mime type %s",
1393 gst_structure_get_name (structure));
1395 /* quick hack: put video pages at the front.
1396 * Ideally, we would have a settable enum for which Ogg
1397 * profile we work with, and order based on that.
1398 * (FIXME: if there is more than one video stream, shouldn't we only put
1399 * one's BOS into the first page, followed by an audio stream's BOS, and
1400 * only then followed by the remaining video and audio streams?) */
1401 if (pad->map.is_video) {
1402 GST_DEBUG_OBJECT (thepad, "putting %s page at the front",
1403 gst_structure_get_name (structure));
1404 hbufs = g_list_prepend (hbufs, hbuf);
1406 hbufs = g_list_append (hbufs, hbuf);
1409 gst_caps_unref (caps);
1412 /* The Skeleton BOS goes first - even before the video that went first before */
1413 if (mux->use_skeleton) {
1414 ogg_stream_init (&skeleton_stream, gst_ogg_mux_generate_serialno (mux));
1415 gst_ogg_mux_make_fishead (mux, &skeleton_stream);
1416 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1417 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1418 hbufs = g_list_append (hbufs, hbuf);
1422 GST_LOG_OBJECT (mux, "creating next headers");
1423 walk = mux->collect->data;
1428 pad = (GstOggPadData *) walk->data;
1429 thepad = pad->collect.pad;
1433 if (mux->use_skeleton)
1434 gst_ogg_mux_make_fisbone (mux, &skeleton_stream, pad);
1436 GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
1437 GST_DEBUG_PAD_NAME (thepad));
1439 hwalk = pad->map.headers;
1441 GstBuffer *buf = GST_BUFFER (hwalk->data);
1446 hwalk = hwalk->next;
1448 /* create a packet from the buffer */
1449 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1450 packet.bytes = size;
1452 gst_ogg_mux_create_header_packet (&packet, pad);
1454 /* swap the packet in */
1455 ogg_stream_packetin (&pad->map.stream, &packet);
1456 gst_buffer_unmap (buf, packet.packet, size);
1457 gst_buffer_unref (buf);
1459 /* if last header, flush page */
1460 if (hwalk == NULL) {
1461 GST_LOG_OBJECT (mux,
1462 "flushing page as packet %" G_GUINT64_FORMAT " is first or "
1463 "last packet", (guint64) packet.packetno);
1464 while (ogg_stream_flush (&pad->map.stream, &page)) {
1465 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1467 GST_LOG_OBJECT (mux, "swapped out page");
1468 hbufs = g_list_append (hbufs, hbuf);
1471 GST_LOG_OBJECT (mux, "try to swap out page");
1472 /* just try to swap out a page then */
1473 while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1474 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1476 GST_LOG_OBJECT (mux, "swapped out page");
1477 hbufs = g_list_append (hbufs, hbuf);
1481 g_list_free (pad->map.headers);
1482 pad->map.headers = NULL;
1485 if (mux->use_skeleton) {
1486 /* flush accumulated fisbones, the fistail must be on a separate page */
1487 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1488 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1489 hbufs = g_list_append (hbufs, hbuf);
1491 gst_ogg_mux_make_fistail (mux, &skeleton_stream);
1492 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1493 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1494 hbufs = g_list_append (hbufs, hbuf);
1496 ogg_stream_clear (&skeleton_stream);
1499 /* hbufs holds all buffers for the headers now */
1501 /* create caps with the buffers */
1502 caps = gst_pad_query_caps (mux->srcpad, NULL);
1504 caps = gst_ogg_mux_set_header_on_caps (caps, hbufs);
1505 gst_pad_set_caps (mux->srcpad, caps);
1506 gst_caps_unref (caps);
1508 /* and send the buffers */
1509 while (hbufs != NULL) {
1510 GstBuffer *buf = GST_BUFFER (hbufs->data);
1512 hbufs = g_list_delete_link (hbufs, hbufs);
1514 if ((ret = gst_ogg_mux_push_buffer (mux, buf, NULL)) != GST_FLOW_OK)
1517 /* free any remaining nodes/buffers in case we couldn't push them */
1518 g_list_foreach (hbufs, (GFunc) gst_mini_object_unref, NULL);
1519 g_list_free (hbufs);
1524 /* this function is called to process data on the best pending pad.
1528 * 1) store the selected pad and keep on pulling until we fill a
1529 * complete ogg page or the ogg page is filled above the max-delay
1530 * threshold. This is needed because the ogg spec says that
1531 * you should fill a complete page with data from the same logical
1532 * stream. When the page is filled, go back to 1).
1533 * 2) before filling a page, read ahead one more buffer to see if this
1534 * packet is the last of the stream. We need to do this because the ogg
1535 * spec mandates that the last packet should have the EOS flag set before
1536 * sending it to ogg. if pad->buffer is NULL we need to wait to find out
1537 * whether there are any more buffers.
1538 * 3) pages get queued on a per-pad queue. Every time a page is queued, a
1539 * dequeue is called, which will dequeue the oldest page on any pad, provided
1540 * that ALL pads have at least one marked page in the queue (or remaining
1543 static GstFlowReturn
1544 gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
1546 GstFlowReturn ret = GST_FLOW_OK;
1547 gboolean delta_unit;
1548 gint64 granulepos = 0;
1549 GstClockTime timestamp, gp_time;
1550 GstBuffer *next_buf;
1552 GST_LOG_OBJECT (ogg_mux, "best pad %" GST_PTR_FORMAT
1553 ", currently pulling from %" GST_PTR_FORMAT, best->collect.pad,
1554 ogg_mux->pulling ? ogg_mux->pulling->collect.pad : NULL);
1556 if (ogg_mux->pulling) {
1557 next_buf = gst_collect_pads2_peek (ogg_mux->collect,
1558 &ogg_mux->pulling->collect);
1560 ogg_mux->pulling->eos = FALSE;
1561 gst_buffer_unref (next_buf);
1563 GST_DEBUG_OBJECT (ogg_mux->pulling->collect.pad, "setting eos to true");
1564 ogg_mux->pulling->eos = TRUE;
1568 /* We could end up pushing from the best pad instead, so check that
1570 if (best && best != ogg_mux->pulling) {
1571 next_buf = gst_collect_pads2_peek (ogg_mux->collect, &best->collect);
1574 gst_buffer_unref (next_buf);
1576 GST_DEBUG_OBJECT (best->collect.pad, "setting eos to true");
1581 /* if we were already pulling from one pad, but the new "best" buffer is
1582 * from another pad, we need to check if we have reason to flush a page
1583 * for the pad we were pulling from before */
1584 if (ogg_mux->pulling && best &&
1585 ogg_mux->pulling != best && ogg_mux->pulling->buffer) {
1586 GstOggPadData *pad = ogg_mux->pulling;
1587 GstClockTime last_ts = GST_BUFFER_END_TIME (pad->buffer);
1589 /* if the next packet in the current page is going to make the page
1590 * too long, we need to flush */
1591 if (last_ts > ogg_mux->next_ts + ogg_mux->max_delay) {
1594 GST_LOG_OBJECT (pad->collect.pad,
1595 GST_GP_FORMAT " stored packet %" G_GINT64_FORMAT
1596 " will make page too long, flushing",
1597 GST_BUFFER_OFFSET_END (pad->buffer),
1598 (gint64) pad->map.stream.packetno);
1600 while (ogg_stream_flush (&pad->map.stream, &page)) {
1601 /* end time of this page is the timestamp of the next buffer */
1602 ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
1603 /* Place page into the per-pad queue */
1604 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1606 /* increment the page number counter */
1608 /* mark other pages as delta */
1609 pad->first_delta = TRUE;
1611 pad->new_page = TRUE;
1612 ogg_mux->pulling = NULL;
1616 /* if we don't know which pad to pull on, use the best one */
1617 if (ogg_mux->pulling == NULL) {
1618 ogg_mux->pulling = best;
1619 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from best pad");
1621 /* remember timestamp and gp time of first buffer for this new pad */
1622 if (ogg_mux->pulling != NULL) {
1623 ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer);
1624 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "updated times, next ts %"
1625 GST_TIME_FORMAT, GST_TIME_ARGS (ogg_mux->next_ts));
1627 /* no pad to pull on, send EOS */
1628 gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1629 return GST_FLOW_WRONG_STATE;
1633 if (ogg_mux->need_headers) {
1634 ret = gst_ogg_mux_send_headers (ogg_mux);
1635 ogg_mux->need_headers = FALSE;
1638 /* we are pulling from a pad, continue to do so until a page
1639 * has been filled and queued */
1640 if (ogg_mux->pulling != NULL) {
1643 GstBuffer *buf, *tmpbuf;
1644 GstOggPadData *pad = ogg_mux->pulling;
1646 gboolean force_flush;
1649 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from pad");
1651 /* now see if we have a buffer */
1654 GST_DEBUG_OBJECT (ogg_mux, "pad was EOS");
1655 ogg_mux->pulling = NULL;
1659 delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
1660 duration = GST_BUFFER_DURATION (buf);
1662 /* if the current "next timestamp" on the pad is unset, then this is the
1663 * first packet on the new page. Update our pad's page timestamp */
1664 if (ogg_mux->pulling->timestamp == GST_CLOCK_TIME_NONE) {
1665 ogg_mux->pulling->timestamp = GST_BUFFER_TIMESTAMP (buf);
1666 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad,
1667 "updated pad timestamp to %" GST_TIME_FORMAT,
1668 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
1670 /* create a packet from the buffer */
1671 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1672 packet.bytes = size;
1673 packet.granulepos = GST_BUFFER_OFFSET_END (buf);
1674 if (packet.granulepos == -1)
1675 packet.granulepos = 0;
1676 /* mark BOS and packet number */
1677 packet.b_o_s = (pad->packetno == 0);
1678 packet.packetno = pad->packetno++;
1679 GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
1680 " packet %" G_GINT64_FORMAT " (%ld bytes) created from buffer",
1681 GST_GP_CAST (packet.granulepos), (gint64) packet.packetno,
1684 packet.e_o_s = ogg_mux->pulling->eos ? 1 : 0;
1687 /* we flush when we see a new keyframe */
1688 force_flush = (pad->prev_delta && !delta_unit)
1689 || pad->map.always_flush_page;
1690 if (duration != -1) {
1691 pad->duration += duration;
1692 /* if page duration exceeds max, flush page */
1693 if (pad->duration > ogg_mux->max_page_delay) {
1699 if (GST_BUFFER_IS_DISCONT (buf)) {
1700 if (pad->data_pushed) {
1701 GST_LOG_OBJECT (pad->collect.pad, "got discont");
1703 /* No public API for this; hack things in */
1704 pad->map.stream.pageno++;
1707 GST_LOG_OBJECT (pad->collect.pad, "discont at stream start");
1711 /* flush the currently built page if necessary */
1713 GST_LOG_OBJECT (pad->collect.pad,
1714 GST_GP_FORMAT " forced flush of page before this packet",
1715 GST_BUFFER_OFFSET_END (pad->buffer));
1716 while (ogg_stream_flush (&pad->map.stream, &page)) {
1717 /* end time of this page is the timestamp of the next buffer */
1718 ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
1719 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1722 /* increment the page number counter */
1724 /* mark other pages as delta */
1725 pad->first_delta = TRUE;
1727 pad->new_page = TRUE;
1730 /* if this is the first packet of a new page figure out the delta flag */
1731 if (pad->new_page) {
1733 /* mark the page as delta */
1734 pad->first_delta = TRUE;
1736 /* got a keyframe */
1737 if (ogg_mux->delta_pad == pad) {
1738 /* if we get it on the pad with deltaunits,
1739 * we mark the page as non delta */
1740 pad->first_delta = FALSE;
1741 } else if (ogg_mux->delta_pad != NULL) {
1742 /* if there are pads with delta frames, we
1743 * must mark this one as delta */
1744 pad->first_delta = TRUE;
1746 pad->first_delta = FALSE;
1749 pad->new_page = FALSE;
1752 /* save key unit to track delta->key unit transitions */
1753 pad->prev_delta = delta_unit;
1755 /* swap the packet in */
1756 if (packet.e_o_s == 1)
1757 GST_DEBUG_OBJECT (pad->collect.pad, "swapping in EOS packet");
1758 if (packet.b_o_s == 1)
1759 GST_DEBUG_OBJECT (pad->collect.pad, "swapping in BOS packet");
1761 ogg_stream_packetin (&pad->map.stream, &packet);
1762 gst_buffer_unmap (buf, packet.packet, size);
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 (GstCollectPads2 * 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 (GstCollectPads2 * 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_EOS;
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 (GstCollectPads2 * 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 (GstCollectPads2 * 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_pads2_start (ogg_mux->collect);
2055 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2057 case GST_STATE_CHANGE_PAUSED_TO_READY:
2058 gst_collect_pads2_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,