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; ")
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, GstEvent * event);
134 static GstPad *gst_ogg_mux_request_new_pad (GstElement * element,
135 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
136 static void gst_ogg_mux_release_pad (GstElement * element, GstPad * pad);
138 static void gst_ogg_mux_set_property (GObject * object,
139 guint prop_id, const GValue * value, GParamSpec * pspec);
140 static void gst_ogg_mux_get_property (GObject * object,
141 guint prop_id, GValue * value, GParamSpec * pspec);
142 static GstStateChangeReturn gst_ogg_mux_change_state (GstElement * element,
143 GstStateChange transition);
145 /*static guint gst_ogg_mux_signals[LAST_SIGNAL] = { 0 }; */
146 #define gst_ogg_mux_parent_class parent_class
147 G_DEFINE_TYPE_WITH_CODE (GstOggMux, gst_ogg_mux, GST_TYPE_ELEMENT,
148 G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
151 gst_ogg_mux_class_init (GstOggMuxClass * klass)
153 GObjectClass *gobject_class;
154 GstElementClass *gstelement_class;
156 gobject_class = (GObjectClass *) klass;
157 gstelement_class = (GstElementClass *) klass;
159 gobject_class->finalize = gst_ogg_mux_finalize;
160 gobject_class->get_property = gst_ogg_mux_get_property;
161 gobject_class->set_property = gst_ogg_mux_set_property;
163 gst_element_class_add_pad_template (gstelement_class,
164 gst_static_pad_template_get (&src_factory));
165 gst_element_class_add_pad_template (gstelement_class,
166 gst_static_pad_template_get (&video_sink_factory));
167 gst_element_class_add_pad_template (gstelement_class,
168 gst_static_pad_template_get (&audio_sink_factory));
169 gst_element_class_add_pad_template (gstelement_class,
170 gst_static_pad_template_get (&subtitle_sink_factory));
172 gst_element_class_set_details_simple (gstelement_class,
173 "Ogg muxer", "Codec/Muxer",
174 "mux ogg streams (info about ogg: http://xiph.org)",
175 "Wim Taymans <wim@fluendo.com>");
177 gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad;
178 gstelement_class->release_pad = gst_ogg_mux_release_pad;
180 g_object_class_install_property (gobject_class, ARG_MAX_DELAY,
181 g_param_spec_uint64 ("max-delay", "Max delay",
182 "Maximum delay in multiplexing streams", 0, G_MAXUINT64,
184 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
185 g_object_class_install_property (gobject_class, ARG_MAX_PAGE_DELAY,
186 g_param_spec_uint64 ("max-page-delay", "Max page delay",
187 "Maximum delay for sending out a page", 0, G_MAXUINT64,
188 DEFAULT_MAX_PAGE_DELAY,
189 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
190 g_object_class_install_property (gobject_class, ARG_MAX_TOLERANCE,
191 g_param_spec_uint64 ("max-tolerance", "Max time tolerance",
192 "Maximum timestamp difference for maintaining perfect granules",
193 0, G_MAXUINT64, DEFAULT_MAX_TOLERANCE,
194 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
195 g_object_class_install_property (gobject_class, ARG_SKELETON,
196 g_param_spec_boolean ("skeleton", "Skeleton",
197 "Whether to include a Skeleton track",
199 (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
201 gstelement_class->change_state = gst_ogg_mux_change_state;
206 static const GstEventMask *
207 gst_ogg_mux_get_sink_event_masks (GstPad * pad)
209 static const GstEventMask gst_ogg_mux_sink_event_masks[] = {
211 {GST_EVENT_DISCONTINUOUS, 0},
215 return gst_ogg_mux_sink_event_masks;
220 gst_ogg_mux_clear (GstOggMux * ogg_mux)
222 ogg_mux->pulling = NULL;
223 ogg_mux->need_headers = TRUE;
224 ogg_mux->delta_pad = NULL;
226 ogg_mux->next_ts = 0;
227 ogg_mux->last_ts = GST_CLOCK_TIME_NONE;
231 gst_ogg_mux_init (GstOggMux * ogg_mux)
233 GstElementClass *klass = GST_ELEMENT_GET_CLASS (ogg_mux);
236 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
238 gst_pad_set_event_function (ogg_mux->srcpad, gst_ogg_mux_handle_src_event);
239 gst_element_add_pad (GST_ELEMENT (ogg_mux), ogg_mux->srcpad);
241 GST_OBJECT_FLAG_SET (GST_ELEMENT (ogg_mux), GST_OGG_FLAG_BOS);
243 /* seed random number generator for creation of serial numbers */
246 ogg_mux->collect = gst_collect_pads2_new ();
247 gst_collect_pads2_set_function (ogg_mux->collect,
248 (GstCollectPads2Function) GST_DEBUG_FUNCPTR (gst_ogg_mux_collected),
251 ogg_mux->max_delay = DEFAULT_MAX_DELAY;
252 ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY;
253 ogg_mux->max_tolerance = DEFAULT_MAX_TOLERANCE;
255 gst_ogg_mux_clear (ogg_mux);
259 gst_ogg_mux_finalize (GObject * object)
263 ogg_mux = GST_OGG_MUX (object);
265 if (ogg_mux->collect) {
266 gst_object_unref (ogg_mux->collect);
267 ogg_mux->collect = NULL;
270 G_OBJECT_CLASS (parent_class)->finalize (object);
274 gst_ogg_mux_ogg_pad_destroy_notify (GstCollectData2 * data)
276 GstOggPadData *oggpad = (GstOggPadData *) data;
279 ogg_stream_clear (&oggpad->map.stream);
280 gst_caps_replace (&oggpad->map.caps, NULL);
282 if (oggpad->pagebuffers) {
283 while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
284 gst_buffer_unref (buf);
286 g_queue_free (oggpad->pagebuffers);
287 oggpad->pagebuffers = NULL;
291 static GstPadLinkReturn
292 gst_ogg_mux_sinkconnect (GstPad * pad, GstPad * peer)
296 ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
298 GST_DEBUG_OBJECT (ogg_mux, "sinkconnect triggered on %s", GST_PAD_NAME (pad));
300 gst_object_unref (ogg_mux);
302 return GST_PAD_LINK_OK;
306 gst_ogg_mux_sink_event (GstPad * pad, GstEvent * event)
308 GstOggMux *ogg_mux = GST_OGG_MUX (gst_pad_get_parent (pad));
309 GstOggPadData *ogg_pad = (GstOggPadData *) gst_pad_get_element_private (pad);
310 gboolean ret = FALSE;
312 GST_DEBUG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event));
314 switch (GST_EVENT_TYPE (event)) {
315 case GST_EVENT_SEGMENT:
317 const GstSegment *segment;
319 gst_event_parse_segment (event, &segment);
321 /* We don't support non time NEWSEGMENT events */
322 if (segment->format != GST_FORMAT_TIME) {
323 gst_event_unref (event);
328 gst_segment_copy_into (segment, &ogg_pad->segment);
331 case GST_EVENT_FLUSH_STOP:{
332 gst_segment_init (&ogg_pad->segment, GST_FORMAT_TIME);
338 gst_event_parse_tag (event, &tags);
339 tags = gst_tag_list_merge (ogg_pad->tags, tags, GST_TAG_MERGE_APPEND);
341 gst_tag_list_free (ogg_pad->tags);
342 ogg_pad->tags = tags;
344 GST_DEBUG_OBJECT (ogg_mux, "Got tags %" GST_PTR_FORMAT, ogg_pad->tags);
351 /* now GstCollectPads can take care of the rest, e.g. EOS */
353 ret = ogg_pad->collect_event (pad, event);
355 gst_object_unref (ogg_mux);
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, GstEvent * event)
519 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
523 /* disable seeking for now */
529 return gst_pad_event_default (pad, event);
533 gst_ogg_mux_buffer_from_page (GstOggMux * mux, ogg_page * page, gboolean delta)
537 /* allocate space for header and body */
538 buffer = gst_buffer_new_and_alloc (page->header_len + page->body_len);
539 gst_buffer_fill (buffer, 0, page->header, page->header_len);
540 gst_buffer_fill (buffer, page->header_len, page->body, page->body_len);
542 /* Here we set granulepos as our OFFSET_END to give easy direct access to
543 * this value later. Before we push it, we reset this to OFFSET + SIZE
544 * (see gst_ogg_mux_push_buffer). */
545 GST_BUFFER_OFFSET_END (buffer) = ogg_page_granulepos (page);
547 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
549 GST_LOG_OBJECT (mux, GST_GP_FORMAT
550 " created buffer %p from ogg page",
551 GST_GP_CAST (ogg_page_granulepos (page)), buffer);
557 gst_ogg_mux_push_buffer (GstOggMux * mux, GstBuffer * buffer,
558 GstOggPadData * oggpad)
560 /* fix up OFFSET and OFFSET_END again */
561 GST_BUFFER_OFFSET (buffer) = mux->offset;
562 mux->offset += gst_buffer_get_size (buffer);
563 GST_BUFFER_OFFSET_END (buffer) = mux->offset;
565 /* Ensure we have monotonically increasing timestamps in the output. */
566 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
567 gint64 run_time = GST_BUFFER_TIMESTAMP (buffer);
568 if (mux->last_ts != GST_CLOCK_TIME_NONE && run_time < mux->last_ts)
569 GST_BUFFER_TIMESTAMP (buffer) = mux->last_ts;
571 mux->last_ts = run_time;
574 return gst_pad_push (mux->srcpad, buffer);
577 /* if all queues have at least one page, dequeue the page with the lowest
580 gst_ogg_mux_dequeue_page (GstOggMux * mux, GstFlowReturn * flowret)
583 GstOggPadData *opad = NULL; /* "oldest" pad */
584 GstClockTime oldest = GST_CLOCK_TIME_NONE;
585 GstBuffer *buf = NULL;
586 gboolean ret = FALSE;
588 *flowret = GST_FLOW_OK;
590 walk = mux->collect->data;
592 GstOggPadData *pad = (GstOggPadData *) walk->data;
594 /* We need each queue to either be at EOS, or have one or more pages
595 * available with a set granulepos (i.e. not -1), otherwise we don't have
596 * enough data yet to determine which stream needs to go next for correct
598 if (pad->pagebuffers->length == 0) {
600 GST_LOG_OBJECT (pad->collect.pad,
601 "pad is EOS, skipping for dequeue decision");
603 GST_LOG_OBJECT (pad->collect.pad,
604 "no pages in this queue, can't dequeue");
608 /* We then need to check for a non-negative granulepos */
610 gboolean valid = FALSE;
612 for (i = 0; i < pad->pagebuffers->length; i++) {
613 buf = g_queue_peek_nth (pad->pagebuffers, i);
614 /* Here we check the OFFSET_END, which is actually temporarily the
615 * granulepos value for this buffer */
616 if (GST_BUFFER_OFFSET_END (buf) != -1) {
622 GST_LOG_OBJECT (pad->collect.pad,
623 "No page timestamps in queue, can't dequeue");
628 walk = g_slist_next (walk);
631 walk = mux->collect->data;
633 GstOggPadData *pad = (GstOggPadData *) walk->data;
635 /* any page with a granulepos of -1 can be pushed immediately.
636 * TODO: it CAN be, but it seems silly to do so? */
637 buf = g_queue_peek_head (pad->pagebuffers);
638 while (buf && GST_BUFFER_OFFSET_END (buf) == -1) {
639 GST_LOG_OBJECT (pad->collect.pad, "[gp -1] pushing page");
640 g_queue_pop_head (pad->pagebuffers);
641 *flowret = gst_ogg_mux_push_buffer (mux, buf, pad);
642 buf = g_queue_peek_head (pad->pagebuffers);
647 /* if no oldest buffer yet, take this one */
648 if (oldest == GST_CLOCK_TIME_NONE) {
649 GST_LOG_OBJECT (mux, "no oldest yet, taking buffer %p from pad %"
650 GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
651 buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
652 oldest = GST_BUFFER_OFFSET (buf);
655 /* if we have an oldest, compare with this one */
656 if (GST_BUFFER_OFFSET (buf) < oldest) {
657 GST_LOG_OBJECT (mux, "older buffer %p, taking from pad %"
658 GST_PTR_FORMAT " with gp time %" GST_TIME_FORMAT,
659 buf, pad->collect.pad, GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
660 oldest = GST_BUFFER_OFFSET (buf);
665 walk = g_slist_next (walk);
668 if (oldest != GST_CLOCK_TIME_NONE) {
670 buf = g_queue_pop_head (opad->pagebuffers);
671 GST_LOG_OBJECT (opad->collect.pad,
672 GST_GP_FORMAT " pushing oldest page buffer %p (granulepos time %"
673 GST_TIME_FORMAT ")", GST_BUFFER_OFFSET_END (buf), buf,
674 GST_TIME_ARGS (GST_BUFFER_OFFSET (buf)));
675 *flowret = gst_ogg_mux_push_buffer (mux, buf, opad);
682 /* put the given ogg page on a per-pad queue, timestamping it correctly.
683 * after that, dequeue and push as many pages as possible.
684 * Caller should make sure:
685 * pad->timestamp was set with the timestamp of the first packet put
687 * pad->timestamp_end was set with the timestamp + duration of the last packet
689 * pad->gp_time was set with the time matching the gp of the last
690 * packet put on the page
692 * will also reset timestamp and timestamp_end, so caller func can restart
696 gst_ogg_mux_pad_queue_page (GstOggMux * mux, GstOggPadData * pad,
697 ogg_page * page, gboolean delta)
700 GstBuffer *buffer = gst_ogg_mux_buffer_from_page (mux, page, delta);
702 /* take the timestamp of the first packet on this page */
703 GST_BUFFER_TIMESTAMP (buffer) = pad->timestamp;
704 GST_BUFFER_DURATION (buffer) = pad->timestamp_end - pad->timestamp;
705 /* take the gp time of the last completed packet on this page */
706 GST_BUFFER_OFFSET (buffer) = pad->gp_time;
708 /* the next page will start where the current page's end time leaves off */
709 pad->timestamp = pad->timestamp_end;
711 g_queue_push_tail (pad->pagebuffers, buffer);
712 GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
713 " queued buffer page %p (gp time %"
714 GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT
715 "), %d page buffers queued", GST_GP_CAST (ogg_page_granulepos (page)),
716 buffer, GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)),
717 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
718 g_queue_get_length (pad->pagebuffers));
720 while (gst_ogg_mux_dequeue_page (mux, &ret)) {
721 if (ret != GST_FLOW_OK)
729 * Given two pads, compare the buffers queued on it.
731 * 0 if they have an equal priority
732 * -1 if the first is better
733 * 1 if the second is better
734 * Priority decided by: a) validity, b) older timestamp, c) smaller number
738 gst_ogg_mux_compare_pads (GstOggMux * ogg_mux, GstOggPadData * first,
739 GstOggPadData * second)
741 guint64 firsttime, secondtime;
743 /* if the first pad doesn't contain anything or is even NULL, return
744 * the second pad as best candidate and vice versa */
750 /* no timestamp on first buffer, it must go first */
751 firsttime = GST_BUFFER_TIMESTAMP (first->buffer);
752 if (firsttime == GST_CLOCK_TIME_NONE)
755 /* no timestamp on second buffer, it must go first */
756 secondtime = GST_BUFFER_TIMESTAMP (second->buffer);
757 if (secondtime == GST_CLOCK_TIME_NONE)
760 /* first buffer has higher timestamp, second one should go first */
761 if (secondtime < firsttime)
763 /* second buffer has higher timestamp, first one should go first */
764 else if (secondtime > firsttime)
767 /* buffers with equal timestamps, prefer the pad that has the
768 * least number of pages muxed */
769 if (second->pageno < first->pageno)
771 else if (second->pageno > first->pageno)
775 /* same priority if all of the above failed */
780 gst_ogg_mux_decorate_buffer (GstOggMux * ogg_mux, GstOggPadData * pad,
784 gint64 duration, granule, limit;
785 GstClockTime next_time;
786 GstClockTimeDiff diff;
790 /* ensure messing with metadata is ok */
791 buf = gst_buffer_make_writable (buf);
793 /* convert time to running time, so we need no longer bother about that */
794 time = GST_BUFFER_TIMESTAMP (buf);
795 if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
796 time = gst_segment_to_running_time (&pad->segment, GST_FORMAT_TIME, time);
797 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
798 gst_buffer_unref (buf);
801 GST_BUFFER_TIMESTAMP (buf) = time;
805 /* now come up with granulepos stuff corresponding to time */
806 if (!pad->have_type ||
807 pad->map.granulerate_n <= 0 || pad->map.granulerate_d <= 0)
810 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
812 duration = gst_ogg_stream_get_packet_duration (&pad->map, &packet);
813 gst_buffer_unmap (buf, packet.packet, size);
815 /* give up if no duration can be determined, relying on upstream */
816 if (G_UNLIKELY (duration < 0)) {
817 /* well, if some day we really could handle sparse input ... */
818 if (pad->map.is_sparse) {
823 GST_WARNING_OBJECT (pad->collect.pad,
824 "failed to determine packet duration");
828 GST_LOG_OBJECT (pad->collect.pad, "buffer ts %" GST_TIME_FORMAT
829 ", duration %" GST_TIME_FORMAT ", granule duration %" G_GINT64_FORMAT,
830 GST_TIME_ARGS (time), GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
833 /* determine granule corresponding to time,
834 * using the inverse of oggdemux' granule -> time */
836 /* see if interpolated granule matches good enough */
837 granule = pad->next_granule;
838 next_time = gst_ogg_stream_granule_to_time (&pad->map, pad->next_granule);
839 diff = GST_CLOCK_DIFF (next_time, time);
841 /* we tolerate deviation up to configured or within granule granularity */
842 limit = gst_ogg_stream_granule_to_time (&pad->map, 1) / 2;
843 limit = MAX (limit, ogg_mux->max_tolerance);
845 GST_LOG_OBJECT (pad->collect.pad, "expected granule %" G_GINT64_FORMAT " == "
846 "time %" GST_TIME_FORMAT " --> ts diff %" GST_TIME_FORMAT
847 " < tolerance %" GST_TIME_FORMAT " (?)",
848 granule, GST_TIME_ARGS (next_time), GST_TIME_ARGS (ABS (diff)),
849 GST_TIME_ARGS (limit));
852 /* if not good enough, determine granule based on time */
853 if (diff > limit || diff < -limit) {
854 granule = gst_util_uint64_scale_round (time, pad->map.granulerate_n,
855 GST_SECOND * pad->map.granulerate_d);
856 GST_DEBUG_OBJECT (pad->collect.pad,
857 "resyncing to determined granule %" G_GINT64_FORMAT, granule);
860 if (pad->map.is_ogm || pad->map.is_sparse) {
861 pad->next_granule = granule;
864 pad->next_granule = granule;
867 /* track previous keyframe */
868 if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
869 pad->keyframe_granule = granule;
871 /* determine corresponding time and granulepos */
872 GST_BUFFER_OFFSET (buf) = gst_ogg_stream_granule_to_time (&pad->map, granule);
873 GST_BUFFER_OFFSET_END (buf) =
874 gst_ogg_stream_granule_to_granulepos (&pad->map, granule,
875 pad->keyframe_granule);
882 GST_DEBUG_OBJECT (pad->collect.pad, "could not determine granulepos, "
883 "falling back to upstream provided metadata");
889 /* make sure at least one buffer is queued on all pads, two if possible
891 * if pad->buffer == NULL, pad->next_buffer != NULL, then
892 * we do not know if the buffer is the last or not
893 * if pad->buffer != NULL, pad->next_buffer != NULL, then
894 * pad->buffer is not the last buffer for the pad
895 * if pad->buffer != NULL, pad->next_buffer == NULL, then
896 * pad->buffer if the last buffer for the pad
898 * returns a pointer to an oggpad that holds the best buffer, or
899 * NULL when no pad was usable. "best" means the buffer marked
900 * with the lowest timestamp. If best->buffer == NULL then either
901 * we're at EOS (popped = FALSE), or a buffer got dropped, so retry. */
902 static GstOggPadData *
903 gst_ogg_mux_queue_pads (GstOggMux * ogg_mux, gboolean * popped)
905 GstOggPadData *bestpad = NULL;
910 /* try to make sure we have a buffer from each usable pad first */
911 walk = ogg_mux->collect->data;
914 GstCollectData2 *data;
916 data = (GstCollectData2 *) walk->data;
917 pad = (GstOggPadData *) data;
919 walk = g_slist_next (walk);
921 GST_LOG_OBJECT (data->pad, "looking at pad for buffer");
923 /* try to get a new buffer for this pad if needed and possible */
924 if (pad->buffer == NULL) {
927 buf = gst_collect_pads2_pop (ogg_mux->collect, data);
928 GST_LOG_OBJECT (data->pad, "popped buffer %" GST_PTR_FORMAT, buf);
930 /* On EOS we get a NULL buffer */
934 if (ogg_mux->delta_pad == NULL &&
935 GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
936 ogg_mux->delta_pad = pad;
938 /* if we need headers */
939 if (pad->state == GST_OGG_PAD_STATE_CONTROL) {
940 /* and we have one */
945 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
948 /* if we're not yet in data mode, ensure we're setup on the first packet */
949 if (!pad->have_type) {
952 /* Use headers in caps, if any; this will allow us to be resilient
953 * to starting streams on the fly, and some streams (like VP8
954 * at least) do not send headers packets, as other muxers don't
955 * expect/need them. */
956 caps = gst_pad_get_current_caps (GST_PAD_CAST (data->pad));
958 gst_ogg_stream_setup_map_from_caps_headers (&pad->map, caps);
960 if (!pad->have_type) {
961 /* fallback on the packet */
962 pad->have_type = gst_ogg_stream_setup_map (&pad->map, &packet);
964 if (!pad->have_type) {
965 GST_ERROR_OBJECT (pad, "mapper didn't recognise input stream "
966 "(pad caps: %" GST_PTR_FORMAT ")", caps);
968 GST_DEBUG_OBJECT (pad, "caps detected: %" GST_PTR_FORMAT,
972 gst_caps_unref (caps);
976 is_header = gst_ogg_stream_packet_is_header (&pad->map, &packet);
977 else /* fallback (FIXME 0.11: remove IN_CAPS hack) */
978 is_header = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
980 gst_buffer_unmap (buf, packet.packet, size);
983 GST_DEBUG_OBJECT (ogg_mux,
984 "got header buffer in control state, ignoring");
986 pad->map.n_header_packets_seen++;
987 gst_buffer_unref (buf);
990 GST_DEBUG_OBJECT (ogg_mux,
991 "got data buffer in control state, switching to data mode");
992 /* this is a data buffer so switch to data state */
993 pad->state = GST_OGG_PAD_STATE_DATA;
995 /* check if this type of stream allows generating granulepos
996 * metadata here, if not, upstream will have to provide */
997 if (gst_ogg_stream_granule_to_granulepos (&pad->map, 1, 1) < 0) {
998 GST_WARNING_OBJECT (data->pad, "can not generate metadata; "
999 "relying on upstream");
1000 /* disable metadata code path, otherwise not used anyway */
1001 pad->map.granulerate_n = 0;
1006 /* so now we should have a real data packet;
1007 * see that it is properly decorated */
1008 if (G_LIKELY (buf)) {
1009 buf = gst_ogg_mux_decorate_buffer (ogg_mux, pad, buf);
1010 if (G_UNLIKELY (!buf))
1011 GST_DEBUG_OBJECT (data->pad, "buffer clipped");
1018 /* we should have a buffer now, see if it is the best pad to
1021 if (gst_ogg_mux_compare_pads (ogg_mux, bestpad, pad) > 0) {
1022 GST_LOG_OBJECT (data->pad,
1023 "new best pad, with buffer %" GST_PTR_FORMAT, pad->buffer);
1034 gst_ogg_mux_get_headers (GstOggPadData * pad)
1037 GstStructure *structure;
1041 thepad = pad->collect.pad;
1043 GST_LOG_OBJECT (thepad, "getting headers");
1045 caps = gst_pad_get_current_caps (thepad);
1047 const GValue *streamheader;
1049 structure = gst_caps_get_structure (caps, 0);
1050 streamheader = gst_structure_get_value (structure, "streamheader");
1051 if (streamheader != NULL) {
1052 GST_LOG_OBJECT (thepad, "got header");
1053 if (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY) {
1054 GArray *bufarr = g_value_peek_pointer (streamheader);
1057 GST_LOG_OBJECT (thepad, "got fixed list");
1059 for (i = 0; i < bufarr->len; i++) {
1060 GValue *bufval = &g_array_index (bufarr, GValue, i);
1062 GST_LOG_OBJECT (thepad, "item %d", i);
1063 if (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER) {
1064 GstBuffer *buf = g_value_peek_pointer (bufval);
1066 GST_LOG_OBJECT (thepad, "adding item %d to header list", i);
1068 gst_buffer_ref (buf);
1069 res = g_list_append (res, buf);
1073 GST_LOG_OBJECT (thepad, "streamheader is not fixed list");
1076 } else if (gst_structure_has_name (structure, "video/x-dirac")) {
1077 res = g_list_append (res, pad->buffer);
1080 GST_LOG_OBJECT (thepad, "caps don't have streamheader");
1082 gst_caps_unref (caps);
1084 GST_LOG_OBJECT (thepad, "got empty caps as negotiated format");
1090 gst_ogg_mux_set_header_on_caps (GstCaps * caps, GList * buffers)
1092 GstStructure *structure;
1093 GValue array = { 0 };
1094 GList *walk = buffers;
1096 caps = gst_caps_make_writable (caps);
1098 structure = gst_caps_get_structure (caps, 0);
1100 /* put buffers in a fixed list */
1101 g_value_init (&array, GST_TYPE_ARRAY);
1104 GstBuffer *buf = GST_BUFFER (walk->data);
1106 GValue value = { 0 };
1111 GST_LOG ("Setting IN_CAPS on buffer of length %" G_GSIZE_FORMAT,
1112 gst_buffer_get_size (buf));
1113 GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
1115 g_value_init (&value, GST_TYPE_BUFFER);
1116 copy = gst_buffer_copy (buf);
1117 gst_value_set_buffer (&value, copy);
1118 gst_buffer_unref (copy);
1119 gst_value_array_append_value (&array, &value);
1120 g_value_unset (&value);
1122 gst_structure_set_value (structure, "streamheader", &array);
1123 g_value_unset (&array);
1129 gst_ogg_mux_create_header_packet_with_flags (ogg_packet * packet,
1130 gboolean bos, gboolean eos)
1132 packet->granulepos = 0;
1133 /* mark BOS and packet number */
1134 packet->b_o_s = bos;
1136 packet->e_o_s = eos;
1140 gst_ogg_mux_create_header_packet (ogg_packet * packet, GstOggPadData * pad)
1142 gst_ogg_mux_create_header_packet_with_flags (packet, pad->packetno == 0, 0);
1143 packet->packetno = pad->packetno++;
1147 gst_ogg_mux_submit_skeleton_header_packet (GstOggMux * mux,
1148 ogg_stream_state * os, GstBuffer * buf, gboolean bos, gboolean eos)
1153 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1154 packet.bytes = size;
1155 gst_ogg_mux_create_header_packet_with_flags (&packet, bos, eos);
1156 ogg_stream_packetin (os, &packet);
1157 gst_buffer_unref (buf);
1161 gst_ogg_mux_make_fishead (GstOggMux * mux, ogg_stream_state * os)
1166 GST_DEBUG_OBJECT (mux, "Creating fishead");
1168 gst_byte_writer_init_with_size (&bw, 64, TRUE);
1169 gst_byte_writer_put_string_utf8 (&bw, "fishead");
1170 gst_byte_writer_put_int16_le (&bw, 3); /* version major */
1171 gst_byte_writer_put_int16_le (&bw, 0); /* version minor */
1172 gst_byte_writer_put_int64_le (&bw, 0); /* presentation time numerator */
1173 gst_byte_writer_put_int64_le (&bw, 1000); /* ...and denominator */
1174 gst_byte_writer_put_int64_le (&bw, 0); /* base time numerator */
1175 gst_byte_writer_put_int64_le (&bw, 1000); /* ...and denominator */
1176 gst_byte_writer_fill (&bw, ' ', 20); /* UTC time */
1177 g_assert (gst_byte_writer_get_pos (&bw) == 64);
1178 fishead = gst_byte_writer_reset_and_get_buffer (&bw);
1179 gst_ogg_mux_submit_skeleton_header_packet (mux, os, fishead, 1, 0);
1183 gst_ogg_mux_byte_writer_put_string_utf8 (GstByteWriter * bw, const char *s)
1185 gst_byte_writer_put_data (bw, (const guint8 *) s, strlen (s));
1189 gst_ogg_mux_add_fisbone_message_header (GstOggMux * mux, GstByteWriter * bw,
1190 const char *tag, const char *value)
1192 /* It is valid to pass NULL as the value to omit the tag */
1195 GST_DEBUG_OBJECT (mux, "Adding fisbone message header %s: %s", tag, value);
1196 gst_ogg_mux_byte_writer_put_string_utf8 (bw, tag);
1197 gst_ogg_mux_byte_writer_put_string_utf8 (bw, ": ");
1198 gst_ogg_mux_byte_writer_put_string_utf8 (bw, value);
1199 gst_ogg_mux_byte_writer_put_string_utf8 (bw, "\r\n");
1203 gst_ogg_mux_add_fisbone_message_header_from_tags (GstOggMux * mux,
1204 GstByteWriter * bw, const char *header, const char *tag,
1205 const GstTagList * tags)
1208 guint size = gst_tag_list_get_tag_size (tags, tag), n;
1209 GST_DEBUG_OBJECT (mux, "Found %u tags for name %s", size, tag);
1212 s = g_string_new ("");
1213 for (n = 0; n < size; ++n) {
1216 g_string_append (s, ", ");
1217 gst_tag_list_get_string_index (tags, tag, n, &tmp);
1218 g_string_append (s, tmp);
1221 gst_ogg_mux_add_fisbone_message_header (mux, bw, header, s->str);
1222 g_string_free (s, TRUE);
1225 /* This is a basic placeholder to generate roles for the tracks.
1226 For tracks with more than one video, both video tracks will get
1227 tagged with a "video/main" role, but we have no way of knowing
1228 which one is the main one, if any. We could just pick one. For
1229 audio, it's more complicated as we don't know which is music,
1230 which is dubbing, etc. For kate, we could take a pretty good
1231 guess based on the category, as role essentially is category.
1232 For now, leave this as is. */
1234 gst_ogg_mux_get_default_role (GstOggPadData * pad)
1236 const char *type = gst_ogg_stream_get_media_type (&pad->map);
1238 if (!strncmp (type, "video/", strlen ("video/")))
1239 return "video/main";
1240 if (!strncmp (type, "audio/", strlen ("audio/")))
1241 return "audio/main";
1242 if (!strcmp (type + strlen (type) - strlen ("kate"), "kate"))
1243 return "text/caption";
1249 gst_ogg_mux_make_fisbone (GstOggMux * mux, ogg_stream_state * os,
1250 GstOggPadData * pad)
1254 GST_DEBUG_OBJECT (mux,
1255 "Creating %s fisbone for serial %08x",
1256 gst_ogg_stream_get_media_type (&pad->map), pad->map.serialno);
1258 gst_byte_writer_init (&bw);
1259 gst_byte_writer_put_string_utf8 (&bw, "fisbone");
1260 gst_byte_writer_put_int32_le (&bw, 44); /* offset to message headers */
1261 gst_byte_writer_put_uint32_le (&bw, pad->map.serialno);
1262 gst_byte_writer_put_uint32_le (&bw, pad->map.n_header_packets);
1263 gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_n);
1264 gst_byte_writer_put_uint64_le (&bw, pad->map.granulerate_d);
1265 gst_byte_writer_put_uint64_le (&bw, 0); /* base granule */
1266 gst_byte_writer_put_uint32_le (&bw, pad->map.preroll);
1267 gst_byte_writer_put_uint8 (&bw, pad->map.granuleshift);
1268 gst_byte_writer_fill (&bw, 0, 3); /* padding */
1269 /* message header fields - MIME type for now */
1270 gst_ogg_mux_add_fisbone_message_header (mux, &bw, "Content-Type",
1271 gst_ogg_stream_get_media_type (&pad->map));
1272 gst_ogg_mux_add_fisbone_message_header (mux, &bw, "Role",
1273 gst_ogg_mux_get_default_role (pad));
1274 gst_ogg_mux_add_fisbone_message_header_from_tags (mux, &bw, "Language",
1275 GST_TAG_LANGUAGE_CODE, pad->tags);
1276 gst_ogg_mux_add_fisbone_message_header_from_tags (mux, &bw, "Title",
1277 GST_TAG_TITLE, pad->tags);
1279 gst_ogg_mux_submit_skeleton_header_packet (mux, os,
1280 gst_byte_writer_reset_and_get_buffer (&bw), 0, 0);
1284 gst_ogg_mux_make_fistail (GstOggMux * mux, ogg_stream_state * os)
1286 GST_DEBUG_OBJECT (mux, "Creating fistail");
1288 gst_ogg_mux_submit_skeleton_header_packet (mux, os,
1289 gst_buffer_new_and_alloc (0), 0, 1);
1293 * For each pad we need to write out one (small) header in one
1294 * page that allows decoders to identify the type of the stream.
1295 * After that we need to write out all extra info for the decoders.
1296 * In the case of a codec that also needs data as configuration, we can
1297 * find that info in the streamcaps.
1298 * After writing the headers we must start a new page for the data.
1300 static GstFlowReturn
1301 gst_ogg_mux_send_headers (GstOggMux * mux)
1304 GList *hbufs, *hwalk;
1308 ogg_stream_state skeleton_stream;
1313 GST_LOG_OBJECT (mux, "collecting headers");
1315 walk = mux->collect->data;
1320 pad = (GstOggPadData *) walk->data;
1321 thepad = pad->collect.pad;
1323 walk = g_slist_next (walk);
1325 GST_LOG_OBJECT (mux, "looking at pad %s:%s", GST_DEBUG_PAD_NAME (thepad));
1327 /* if the pad has no buffer, we don't care */
1328 if (pad->buffer == NULL)
1331 /* now figure out the headers */
1332 pad->map.headers = gst_ogg_mux_get_headers (pad);
1335 GST_LOG_OBJECT (mux, "creating BOS pages");
1336 walk = mux->collect->data;
1343 GstStructure *structure;
1347 pad = (GstOggPadData *) walk->data;
1348 thepad = pad->collect.pad;
1349 caps = gst_pad_get_current_caps (thepad);
1350 structure = gst_caps_get_structure (caps, 0);
1356 GST_LOG_OBJECT (thepad, "looping over headers");
1358 if (pad->map.headers) {
1359 buf = GST_BUFFER (pad->map.headers->data);
1360 pad->map.headers = g_list_remove (pad->map.headers, buf);
1361 } else if (pad->buffer) {
1363 gst_buffer_ref (buf);
1365 /* fixme -- should be caught in the previous list traversal. */
1366 GST_OBJECT_LOCK (thepad);
1367 g_critical ("No headers or buffers on pad %s:%s",
1368 GST_DEBUG_PAD_NAME (thepad));
1369 GST_OBJECT_UNLOCK (thepad);
1373 /* create a packet from the buffer */
1374 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1375 packet.bytes = size;
1377 gst_ogg_mux_create_header_packet (&packet, pad);
1379 /* swap the packet in */
1380 ogg_stream_packetin (&pad->map.stream, &packet);
1382 gst_buffer_unmap (buf, packet.packet, size);
1383 gst_buffer_unref (buf);
1385 GST_LOG_OBJECT (thepad, "flushing out BOS page");
1386 if (!ogg_stream_flush (&pad->map.stream, &page))
1387 g_critical ("Could not flush BOS page");
1389 hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1391 GST_LOG_OBJECT (mux, "swapped out page with mime type %s",
1392 gst_structure_get_name (structure));
1394 /* quick hack: put video pages at the front.
1395 * Ideally, we would have a settable enum for which Ogg
1396 * profile we work with, and order based on that.
1397 * (FIXME: if there is more than one video stream, shouldn't we only put
1398 * one's BOS into the first page, followed by an audio stream's BOS, and
1399 * only then followed by the remaining video and audio streams?) */
1400 if (pad->map.is_video) {
1401 GST_DEBUG_OBJECT (thepad, "putting %s page at the front",
1402 gst_structure_get_name (structure));
1403 hbufs = g_list_prepend (hbufs, hbuf);
1405 hbufs = g_list_append (hbufs, hbuf);
1408 gst_caps_unref (caps);
1411 /* The Skeleton BOS goes first - even before the video that went first before */
1412 if (mux->use_skeleton) {
1413 ogg_stream_init (&skeleton_stream, gst_ogg_mux_generate_serialno (mux));
1414 gst_ogg_mux_make_fishead (mux, &skeleton_stream);
1415 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1416 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1417 hbufs = g_list_append (hbufs, hbuf);
1421 GST_LOG_OBJECT (mux, "creating next headers");
1422 walk = mux->collect->data;
1427 pad = (GstOggPadData *) walk->data;
1428 thepad = pad->collect.pad;
1432 if (mux->use_skeleton)
1433 gst_ogg_mux_make_fisbone (mux, &skeleton_stream, pad);
1435 GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
1436 GST_DEBUG_PAD_NAME (thepad));
1438 hwalk = pad->map.headers;
1440 GstBuffer *buf = GST_BUFFER (hwalk->data);
1445 hwalk = hwalk->next;
1447 /* create a packet from the buffer */
1448 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1449 packet.bytes = size;
1451 gst_ogg_mux_create_header_packet (&packet, pad);
1453 /* swap the packet in */
1454 ogg_stream_packetin (&pad->map.stream, &packet);
1455 gst_buffer_unmap (buf, packet.packet, size);
1456 gst_buffer_unref (buf);
1458 /* if last header, flush page */
1459 if (hwalk == NULL) {
1460 GST_LOG_OBJECT (mux,
1461 "flushing page as packet %" G_GUINT64_FORMAT " is first or "
1462 "last packet", (guint64) packet.packetno);
1463 while (ogg_stream_flush (&pad->map.stream, &page)) {
1464 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1466 GST_LOG_OBJECT (mux, "swapped out page");
1467 hbufs = g_list_append (hbufs, hbuf);
1470 GST_LOG_OBJECT (mux, "try to swap out page");
1471 /* just try to swap out a page then */
1472 while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1473 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1475 GST_LOG_OBJECT (mux, "swapped out page");
1476 hbufs = g_list_append (hbufs, hbuf);
1480 g_list_free (pad->map.headers);
1481 pad->map.headers = NULL;
1484 if (mux->use_skeleton) {
1485 /* flush accumulated fisbones, the fistail must be on a separate page */
1486 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1487 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1488 hbufs = g_list_append (hbufs, hbuf);
1490 gst_ogg_mux_make_fistail (mux, &skeleton_stream);
1491 while (ogg_stream_flush (&skeleton_stream, &page) > 0) {
1492 GstBuffer *hbuf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
1493 hbufs = g_list_append (hbufs, hbuf);
1495 ogg_stream_clear (&skeleton_stream);
1498 /* hbufs holds all buffers for the headers now */
1500 /* create caps with the buffers */
1501 caps = gst_pad_query_caps (mux->srcpad, NULL);
1503 caps = gst_ogg_mux_set_header_on_caps (caps, hbufs);
1504 gst_pad_set_caps (mux->srcpad, caps);
1505 gst_caps_unref (caps);
1507 /* and send the buffers */
1508 while (hbufs != NULL) {
1509 GstBuffer *buf = GST_BUFFER (hbufs->data);
1511 hbufs = g_list_delete_link (hbufs, hbufs);
1513 if ((ret = gst_ogg_mux_push_buffer (mux, buf, NULL)) != GST_FLOW_OK)
1516 /* free any remaining nodes/buffers in case we couldn't push them */
1517 g_list_foreach (hbufs, (GFunc) gst_mini_object_unref, NULL);
1518 g_list_free (hbufs);
1523 /* this function is called to process data on the best pending pad.
1527 * 1) store the selected pad and keep on pulling until we fill a
1528 * complete ogg page or the ogg page is filled above the max-delay
1529 * threshold. This is needed because the ogg spec says that
1530 * you should fill a complete page with data from the same logical
1531 * stream. When the page is filled, go back to 1).
1532 * 2) before filling a page, read ahead one more buffer to see if this
1533 * packet is the last of the stream. We need to do this because the ogg
1534 * spec mandates that the last packet should have the EOS flag set before
1535 * sending it to ogg. if pad->buffer is NULL we need to wait to find out
1536 * whether there are any more buffers.
1537 * 3) pages get queued on a per-pad queue. Every time a page is queued, a
1538 * dequeue is called, which will dequeue the oldest page on any pad, provided
1539 * that ALL pads have at least one marked page in the queue (or remaining
1542 static GstFlowReturn
1543 gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best)
1545 GstFlowReturn ret = GST_FLOW_OK;
1546 gboolean delta_unit;
1547 gint64 granulepos = 0;
1548 GstClockTime timestamp, gp_time;
1549 GstBuffer *next_buf;
1551 GST_LOG_OBJECT (ogg_mux, "best pad %" GST_PTR_FORMAT
1552 ", currently pulling from %" GST_PTR_FORMAT, best->collect.pad,
1553 ogg_mux->pulling ? ogg_mux->pulling->collect.pad : NULL);
1555 if (ogg_mux->pulling) {
1556 next_buf = gst_collect_pads2_peek (ogg_mux->collect,
1557 &ogg_mux->pulling->collect);
1559 ogg_mux->pulling->eos = FALSE;
1560 gst_buffer_unref (next_buf);
1562 GST_DEBUG_OBJECT (ogg_mux->pulling->collect.pad, "setting eos to true");
1563 ogg_mux->pulling->eos = TRUE;
1567 /* We could end up pushing from the best pad instead, so check that
1569 if (best && best != ogg_mux->pulling) {
1570 next_buf = gst_collect_pads2_peek (ogg_mux->collect, &best->collect);
1573 gst_buffer_unref (next_buf);
1575 GST_DEBUG_OBJECT (best->collect.pad, "setting eos to true");
1580 /* if we were already pulling from one pad, but the new "best" buffer is
1581 * from another pad, we need to check if we have reason to flush a page
1582 * for the pad we were pulling from before */
1583 if (ogg_mux->pulling && best &&
1584 ogg_mux->pulling != best && ogg_mux->pulling->buffer) {
1585 GstOggPadData *pad = ogg_mux->pulling;
1586 GstClockTime last_ts = GST_BUFFER_END_TIME (pad->buffer);
1588 /* if the next packet in the current page is going to make the page
1589 * too long, we need to flush */
1590 if (last_ts > ogg_mux->next_ts + ogg_mux->max_delay) {
1593 GST_LOG_OBJECT (pad->collect.pad,
1594 GST_GP_FORMAT " stored packet %" G_GINT64_FORMAT
1595 " will make page too long, flushing",
1596 GST_BUFFER_OFFSET_END (pad->buffer),
1597 (gint64) pad->map.stream.packetno);
1599 while (ogg_stream_flush (&pad->map.stream, &page)) {
1600 /* end time of this page is the timestamp of the next buffer */
1601 ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
1602 /* Place page into the per-pad queue */
1603 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1605 /* increment the page number counter */
1607 /* mark other pages as delta */
1608 pad->first_delta = TRUE;
1610 pad->new_page = TRUE;
1611 ogg_mux->pulling = NULL;
1615 /* if we don't know which pad to pull on, use the best one */
1616 if (ogg_mux->pulling == NULL) {
1617 ogg_mux->pulling = best;
1618 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from best pad");
1620 /* remember timestamp and gp time of first buffer for this new pad */
1621 if (ogg_mux->pulling != NULL) {
1622 ogg_mux->next_ts = GST_BUFFER_TIMESTAMP (ogg_mux->pulling->buffer);
1623 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "updated times, next ts %"
1624 GST_TIME_FORMAT, GST_TIME_ARGS (ogg_mux->next_ts));
1626 /* no pad to pull on, send EOS */
1627 gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1628 return GST_FLOW_WRONG_STATE;
1632 if (ogg_mux->need_headers) {
1633 ret = gst_ogg_mux_send_headers (ogg_mux);
1634 ogg_mux->need_headers = FALSE;
1637 /* we are pulling from a pad, continue to do so until a page
1638 * has been filled and queued */
1639 if (ogg_mux->pulling != NULL) {
1642 GstBuffer *buf, *tmpbuf;
1643 GstOggPadData *pad = ogg_mux->pulling;
1645 gboolean force_flush;
1648 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad, "pulling from pad");
1650 /* now see if we have a buffer */
1653 GST_DEBUG_OBJECT (ogg_mux, "pad was EOS");
1654 ogg_mux->pulling = NULL;
1658 delta_unit = GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
1659 duration = GST_BUFFER_DURATION (buf);
1661 /* if the current "next timestamp" on the pad is unset, then this is the
1662 * first packet on the new page. Update our pad's page timestamp */
1663 if (ogg_mux->pulling->timestamp == GST_CLOCK_TIME_NONE) {
1664 ogg_mux->pulling->timestamp = GST_BUFFER_TIMESTAMP (buf);
1665 GST_LOG_OBJECT (ogg_mux->pulling->collect.pad,
1666 "updated pad timestamp to %" GST_TIME_FORMAT,
1667 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
1669 /* create a packet from the buffer */
1670 packet.packet = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1671 packet.bytes = size;
1672 packet.granulepos = GST_BUFFER_OFFSET_END (buf);
1673 if (packet.granulepos == -1)
1674 packet.granulepos = 0;
1675 /* mark BOS and packet number */
1676 packet.b_o_s = (pad->packetno == 0);
1677 packet.packetno = pad->packetno++;
1678 GST_LOG_OBJECT (pad->collect.pad, GST_GP_FORMAT
1679 " packet %" G_GINT64_FORMAT " (%ld bytes) created from buffer",
1680 GST_GP_CAST (packet.granulepos), (gint64) packet.packetno,
1683 packet.e_o_s = ogg_mux->pulling->eos ? 1 : 0;
1686 /* we flush when we see a new keyframe */
1687 force_flush = (pad->prev_delta && !delta_unit)
1688 || pad->map.always_flush_page;
1689 if (duration != -1) {
1690 pad->duration += duration;
1691 /* if page duration exceeds max, flush page */
1692 if (pad->duration > ogg_mux->max_page_delay) {
1698 if (GST_BUFFER_IS_DISCONT (buf)) {
1699 if (pad->data_pushed) {
1700 GST_LOG_OBJECT (pad->collect.pad, "got discont");
1702 /* No public API for this; hack things in */
1703 pad->map.stream.pageno++;
1706 GST_LOG_OBJECT (pad->collect.pad, "discont at stream start");
1710 /* flush the currently built page if necessary */
1712 GST_LOG_OBJECT (pad->collect.pad,
1713 GST_GP_FORMAT " forced flush of page before this packet",
1714 GST_BUFFER_OFFSET_END (pad->buffer));
1715 while (ogg_stream_flush (&pad->map.stream, &page)) {
1716 /* end time of this page is the timestamp of the next buffer */
1717 ogg_mux->pulling->timestamp_end = GST_BUFFER_TIMESTAMP (pad->buffer);
1718 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1721 /* increment the page number counter */
1723 /* mark other pages as delta */
1724 pad->first_delta = TRUE;
1726 pad->new_page = TRUE;
1729 /* if this is the first packet of a new page figure out the delta flag */
1730 if (pad->new_page) {
1732 /* mark the page as delta */
1733 pad->first_delta = TRUE;
1735 /* got a keyframe */
1736 if (ogg_mux->delta_pad == pad) {
1737 /* if we get it on the pad with deltaunits,
1738 * we mark the page as non delta */
1739 pad->first_delta = FALSE;
1740 } else if (ogg_mux->delta_pad != NULL) {
1741 /* if there are pads with delta frames, we
1742 * must mark this one as delta */
1743 pad->first_delta = TRUE;
1745 pad->first_delta = FALSE;
1748 pad->new_page = FALSE;
1751 /* save key unit to track delta->key unit transitions */
1752 pad->prev_delta = delta_unit;
1754 /* swap the packet in */
1755 if (packet.e_o_s == 1)
1756 GST_DEBUG_OBJECT (pad->collect.pad, "swapping in EOS packet");
1757 if (packet.b_o_s == 1)
1758 GST_DEBUG_OBJECT (pad->collect.pad, "swapping in BOS packet");
1760 ogg_stream_packetin (&pad->map.stream, &packet);
1761 gst_buffer_unmap (buf, packet.packet, size);
1762 pad->data_pushed = TRUE;
1764 gp_time = GST_BUFFER_OFFSET (pad->buffer);
1765 granulepos = GST_BUFFER_OFFSET_END (pad->buffer);
1766 timestamp = GST_BUFFER_TIMESTAMP (pad->buffer);
1768 GST_LOG_OBJECT (pad->collect.pad,
1769 GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", gp time %"
1770 GST_TIME_FORMAT ", timestamp %" GST_TIME_FORMAT " packetin'd",
1771 granulepos, (gint64) packet.packetno, GST_TIME_ARGS (gp_time),
1772 GST_TIME_ARGS (timestamp));
1773 /* don't need the old buffer anymore */
1774 gst_buffer_unref (pad->buffer);
1775 /* store new readahead buffer */
1776 pad->buffer = tmpbuf;
1778 /* let ogg write out the pages now. The packet we got could end
1779 * up in more than one page so we need to write them all */
1780 if (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1781 /* we have a new page, so we need to timestamp it correctly.
1782 * if this fresh packet ends on this page, then the page's granulepos
1783 * comes from that packet, and we should set this buffer's timestamp */
1785 GST_LOG_OBJECT (pad->collect.pad,
1786 GST_GP_FORMAT " packet %" G_GINT64_FORMAT ", time %"
1787 GST_TIME_FORMAT ") caused new page",
1788 granulepos, (gint64) packet.packetno, GST_TIME_ARGS (timestamp));
1789 GST_LOG_OBJECT (pad->collect.pad,
1790 GST_GP_FORMAT " new page %ld",
1791 GST_GP_CAST (ogg_page_granulepos (&page)), pad->map.stream.pageno);
1793 if (ogg_page_granulepos (&page) == granulepos) {
1794 /* the packet we streamed in finishes on the current page,
1795 * because the page's granulepos is the granulepos of the last
1796 * packet completed on that page,
1797 * so update the timestamp that we will give to the page */
1798 GST_LOG_OBJECT (pad->collect.pad,
1800 " packet finishes on current page, updating gp time to %"
1801 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (gp_time));
1802 pad->gp_time = gp_time;
1804 GST_LOG_OBJECT (pad->collect.pad,
1806 " packet spans beyond current page, keeping old gp time %"
1807 GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (pad->gp_time));
1811 /* end time of this page is the timestamp of the next buffer */
1812 pad->timestamp_end = timestamp;
1813 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page, pad->first_delta);
1815 /* mark next pages as delta */
1816 pad->first_delta = TRUE;
1818 /* use an inner loop here to flush the remaining pages and
1819 * mark them as delta frames as well */
1820 while (ogg_stream_pageout (&pad->map.stream, &page) > 0) {
1821 if (ogg_page_granulepos (&page) == granulepos) {
1822 /* the page has taken up the new packet completely, which means
1823 * the packet ends the page and we can update the gp time
1824 * before pushing out */
1825 pad->gp_time = gp_time;
1828 /* we have a complete page now, we can push the page
1829 * and make sure to pull on a new pad the next time around */
1830 ret = gst_ogg_mux_pad_queue_page (ogg_mux, pad, &page,
1832 /* increment the page number counter */
1835 /* need a new page as well */
1836 pad->new_page = TRUE;
1838 /* we're done pulling on this pad, make sure to choose a new
1839 * pad for pulling in the next iteration */
1840 ogg_mux->pulling = NULL;
1843 /* Update the gp time, if necessary, since any future page will have at
1844 * least this gp time.
1846 if (pad->gp_time < gp_time) {
1847 pad->gp_time = gp_time;
1848 GST_LOG_OBJECT (pad->collect.pad,
1849 "Updated running gp time of pad %" GST_PTR_FORMAT
1850 " to %" GST_TIME_FORMAT, pad->collect.pad, GST_TIME_ARGS (gp_time));
1859 * Checks if all pads are EOS'd by peeking.
1861 * Returns TRUE if all pads are EOS.
1864 all_pads_eos (GstCollectPads2 * pads)
1870 GstOggPadData *oggpad = (GstOggPadData *) walk->data;
1872 GST_DEBUG_OBJECT (oggpad->collect.pad,
1873 "oggpad %p eos %d", oggpad, oggpad->eos);
1875 if (oggpad->eos == FALSE)
1878 walk = g_slist_next (walk);
1884 /* This function is called when there is data on all pads.
1886 * It finds a pad to pull on, this is done by looking at the buffers
1887 * to decide which one to use, and using the 'oldest' one first. It then calls
1888 * gst_ogg_mux_process_best_pad() to process as much data as possible.
1890 * If all the pads have received EOS, it flushes out all data by continually
1891 * getting the best pad and calling gst_ogg_mux_process_best_pad() until they
1892 * are all empty, and then sends EOS.
1894 static GstFlowReturn
1895 gst_ogg_mux_collected (GstCollectPads2 * pads, GstOggMux * ogg_mux)
1897 GstOggPadData *best;
1901 GST_LOG_OBJECT (ogg_mux, "collected");
1903 /* queue buffers on all pads; find a buffer with the lowest timestamp */
1904 best = gst_ogg_mux_queue_pads (ogg_mux, &popped);
1909 if (best == NULL || best->buffer == NULL) {
1910 /* This is not supposed to happen */
1911 return GST_FLOW_ERROR;
1914 ret = gst_ogg_mux_process_best_pad (ogg_mux, best);
1916 if (best->eos && all_pads_eos (pads)) {
1917 gst_pad_push_event (ogg_mux->srcpad, gst_event_new_eos ());
1918 return GST_FLOW_EOS;
1925 gst_ogg_mux_get_property (GObject * object,
1926 guint prop_id, GValue * value, GParamSpec * pspec)
1930 ogg_mux = GST_OGG_MUX (object);
1934 g_value_set_uint64 (value, ogg_mux->max_delay);
1936 case ARG_MAX_PAGE_DELAY:
1937 g_value_set_uint64 (value, ogg_mux->max_page_delay);
1939 case ARG_MAX_TOLERANCE:
1940 g_value_set_uint64 (value, ogg_mux->max_tolerance);
1943 g_value_set_boolean (value, ogg_mux->use_skeleton);
1946 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1952 gst_ogg_mux_set_property (GObject * object,
1953 guint prop_id, const GValue * value, GParamSpec * pspec)
1957 ogg_mux = GST_OGG_MUX (object);
1961 ogg_mux->max_delay = g_value_get_uint64 (value);
1963 case ARG_MAX_PAGE_DELAY:
1964 ogg_mux->max_page_delay = g_value_get_uint64 (value);
1966 case ARG_MAX_TOLERANCE:
1967 ogg_mux->max_tolerance = g_value_get_uint64 (value);
1970 ogg_mux->use_skeleton = g_value_get_boolean (value);
1973 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1978 /* reset all variables in the ogg pads. */
1980 gst_ogg_mux_init_collectpads (GstCollectPads2 * collect)
1984 walk = collect->data;
1986 GstOggPadData *oggpad = (GstOggPadData *) walk->data;
1988 ogg_stream_init (&oggpad->map.stream, oggpad->map.serialno);
1989 oggpad->packetno = 0;
1991 oggpad->eos = FALSE;
1992 /* we assume there will be some control data first for this pad */
1993 oggpad->state = GST_OGG_PAD_STATE_CONTROL;
1994 oggpad->new_page = TRUE;
1995 oggpad->first_delta = FALSE;
1996 oggpad->prev_delta = FALSE;
1997 oggpad->data_pushed = FALSE;
1998 oggpad->pagebuffers = g_queue_new ();
2000 gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
2002 walk = g_slist_next (walk);
2006 /* Clear all buffers from the collectpads object */
2008 gst_ogg_mux_clear_collectpads (GstCollectPads2 * collect)
2012 for (walk = collect->data; walk; walk = g_slist_next (walk)) {
2013 GstOggPadData *oggpad = (GstOggPadData *) walk->data;
2016 ogg_stream_clear (&oggpad->map.stream);
2018 while ((buf = g_queue_pop_head (oggpad->pagebuffers)) != NULL) {
2019 gst_buffer_unref (buf);
2021 g_queue_free (oggpad->pagebuffers);
2022 oggpad->pagebuffers = NULL;
2024 if (oggpad->buffer) {
2025 gst_buffer_unref (oggpad->buffer);
2026 oggpad->buffer = NULL;
2030 gst_tag_list_free (oggpad->tags);
2031 oggpad->tags = NULL;
2034 gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
2038 static GstStateChangeReturn
2039 gst_ogg_mux_change_state (GstElement * element, GstStateChange transition)
2042 GstStateChangeReturn ret;
2044 ogg_mux = GST_OGG_MUX (element);
2046 switch (transition) {
2047 case GST_STATE_CHANGE_NULL_TO_READY:
2049 case GST_STATE_CHANGE_READY_TO_PAUSED:
2050 gst_ogg_mux_clear (ogg_mux);
2051 gst_ogg_mux_init_collectpads (ogg_mux->collect);
2052 gst_collect_pads2_start (ogg_mux->collect);
2054 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2056 case GST_STATE_CHANGE_PAUSED_TO_READY:
2057 gst_collect_pads2_stop (ogg_mux->collect);
2063 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2065 switch (transition) {
2066 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2068 case GST_STATE_CHANGE_PAUSED_TO_READY:
2069 gst_ogg_mux_clear_collectpads (ogg_mux->collect);
2071 case GST_STATE_CHANGE_READY_TO_NULL:
2081 gst_ogg_mux_plugin_init (GstPlugin * plugin)
2083 GST_DEBUG_CATEGORY_INIT (gst_ogg_mux_debug, "oggmux", 0, "ogg muxer");
2085 return gst_element_register (plugin, "oggmux", GST_RANK_PRIMARY,