1 /* concat muxer plugin for GStreamer
2 * Copyright (C) 2004 Renato Filhps <renato.filho@indt.org.br>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-concatmux
22 * @short_description: Concat that takes one or several digital streams
23 * and muxes them to a single stream.
26 * <title>Sample pipelines</title>
28 * Here is a simple pipeline to concat 2 files into a another file:
30 * gst-launch concatmux name=m ! filesink location=output.txt filesrc location=file_a.txt ! m. filesrc location=file_b.txt ! m.
41 #include <gst/base/gstcollectpads.h>
45 GST_DEBUG_CATEGORY_STATIC (gst_concat_mux_debug);
46 #define GST_CAT_DEFAULT gst_concat_mux_debug
48 #define GST_TYPE_CONCAT_MUX (gst_concat_mux_get_type())
49 #define GST_CONCAT_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CONCAT_MUX, GstConcatMux))
50 #define GST_CONCAT_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CONCAT_MUX, GstConcatMux))
51 #define GST_IS_CONCAT_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CONCAT_MUX))
52 #define GST_IS_CONCAT_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CONCAT_MUX))
54 typedef struct _GstConcatMux GstConcatMux;
55 typedef struct _GstConcatMuxClass GstConcatMuxClass;
60 * The opaque #GstConcatMux structure.
77 /* offset in stream */
87 struct _GstConcatMuxClass
89 GstElementClass parent_class;
92 /* elementfactory information */
93 static const GstElementDetails gst_concat_mux_details =
94 GST_ELEMENT_DETAILS ("Concat muxer",
97 "Renato Filho <renato.filho@indt.org>");
99 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
105 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
111 static void gst_concat_mux_base_init (gpointer g_class);
112 static void gst_concat_mux_class_init (GstConcatMuxClass * klass);
113 static void gst_concat_mux_init (GstConcatMux * concat_mux);
115 static void gst_concat_mux_finalize (GObject * object);
117 static gboolean gst_concat_mux_handle_src_event (GstPad * pad,
119 static gboolean gst_concat_mux_handle_sink_event (GstPad * pad,
122 static GstPad *gst_concat_mux_request_new_pad (GstElement * element,
123 GstPadTemplate * templ, const gchar * name);
124 static GstStateChangeReturn gst_concat_mux_change_state (GstElement *
125 element, GstStateChange transition);
127 static GstFlowReturn gst_concat_mux_chain (GstPad * pad, GstBuffer * buf);
128 static void gst_concat_mux_clear (GstConcatMux *mux);
131 static GstElementClass *parent_class = NULL;
134 gst_concat_mux_get_type (void)
136 static GType concat_mux_type = 0;
138 if (!concat_mux_type) {
139 static const GTypeInfo concat_mux_info = {
140 sizeof (GstConcatMuxClass),
141 gst_concat_mux_base_init,
143 (GClassInitFunc) gst_concat_mux_class_init,
146 sizeof (GstConcatMux),
148 (GInstanceInitFunc) gst_concat_mux_init,
152 g_type_register_static (GST_TYPE_ELEMENT, "GstConcatMux",
153 &concat_mux_info, 0);
155 return concat_mux_type;
159 gst_concat_mux_base_init (gpointer g_class)
161 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
163 gst_element_class_add_pad_template (element_class,
164 gst_static_pad_template_get (&src_factory));
165 gst_element_class_add_pad_template (element_class,
166 gst_static_pad_template_get (&sink_factory));
168 gst_element_class_set_details (element_class, &gst_concat_mux_details);
172 gst_concat_mux_class_init (GstConcatMuxClass * klass)
174 GObjectClass *gobject_class;
175 GstElementClass *gstelement_class;
177 gobject_class = (GObjectClass *) klass;
178 gstelement_class = (GstElementClass *) klass;
180 parent_class = g_type_class_peek_parent (klass);
182 gobject_class->finalize = gst_concat_mux_finalize;
184 gstelement_class->request_new_pad = gst_concat_mux_request_new_pad;
185 gstelement_class->change_state = gst_concat_mux_change_state;
189 gst_concat_mux_init (GstConcatMux * concat_mux)
191 GstElementClass *klass = GST_ELEMENT_GET_CLASS (concat_mux);
194 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
196 gst_pad_set_event_function (concat_mux->srcpad,
197 gst_concat_mux_handle_src_event);
198 gst_element_add_pad (GST_ELEMENT (concat_mux), concat_mux->srcpad);
202 gst_concat_mux_finalize (GObject * object)
204 GstConcatMux *concat_mux;
206 concat_mux = GST_CONCAT_MUX (object);
207 gst_concat_mux_clear (GST_CONCAT_MUX (object));
209 G_OBJECT_CLASS (parent_class)->finalize (object);
213 gst_concat_mux_free_pad (gpointer data,
218 mux = gst_pad_get_element_private (GST_PAD (data));
219 g_mutex_unlock (mux);
221 gst_object_unref (GST_OBJECT (data));
225 gst_concat_mux_clear (GstConcatMux *mux)
230 mux->negotiated = FALSE;
232 if (mux->sinks != NULL) {
233 g_slist_foreach (mux->sinks, gst_concat_mux_free_pad, mux);
234 g_slist_free (mux->sinks);
240 static GstPadLinkReturn
241 gst_concat_mux_sinkconnect (GstPad * pad, GstPad * peer)
243 gchar *pad_name = NULL;
244 GstConcatMux *concat_mux;
246 concat_mux = GST_CONCAT_MUX (gst_pad_get_parent (pad));
248 if (concat_mux->sink_caps != NULL) {
249 GstCaps *peer_caps = gst_pad_get_caps (peer);
252 intersect = gst_caps_intersect (concat_mux->sink_caps, peer_caps);
253 if (intersect == NULL) {
254 gst_caps_unref (peer_caps);
255 return GST_PAD_LINK_NOFORMAT;
257 gst_caps_unref (peer_caps);
258 gst_caps_unref (intersect);
260 concat_mux->sink_caps = gst_pad_get_caps (pad);
263 pad_name = gst_pad_get_name (pad);
265 GST_DEBUG_OBJECT (concat_mux, "sinkconnect triggered on %s", pad_name);
269 gst_object_unref (concat_mux);
271 return GST_PAD_LINK_OK;
275 gst_concat_mux_request_new_pad (GstElement * element,
276 GstPadTemplate * templ, const gchar * req_name)
278 GstConcatMux *concat_mux;
280 GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
283 g_return_val_if_fail (templ != NULL, NULL);
285 if (templ->direction != GST_PAD_SINK) {
286 g_warning ("concat_mux: request pad that is not a SINK pad\n");
290 g_return_val_if_fail (GST_IS_CONCAT_MUX (element), NULL);
292 concat_mux = GST_CONCAT_MUX (element);
294 if (templ == gst_element_class_get_pad_template (klass, "sink_%d")) {
297 /* create new pad with the name */
298 name = g_strdup_printf ("sink_%02d", concat_mux->numpads);
299 g_debug ("NEw pad %s", name);
300 newpad = gst_pad_new_from_template (templ, name);
302 concat_mux->sinks = g_slist_append (concat_mux->sinks, newpad);
303 g_debug ("New sink %p / %d", newpad, g_slist_length (concat_mux->sinks));
304 concat_mux->numpads++;
306 g_warning ("concat_mux: this is not our template!\n");
310 mutex = g_mutex_new ();
312 if (concat_mux->sinkpad == NULL) {
313 concat_mux->sinkpad = newpad;
316 g_mutex_lock (mutex);
319 gst_pad_set_element_private (newpad, mutex);
320 /* setup some pad functions */
321 gst_pad_set_link_function (newpad, gst_concat_mux_sinkconnect);
322 gst_pad_set_event_function (newpad, gst_concat_mux_handle_sink_event);
323 gst_pad_set_chain_function (newpad, gst_concat_mux_chain);
325 /* add the pad to the element */
326 gst_element_add_pad (element, newpad);
333 gst_concat_mux_handle_src_event (GstPad * pad, GstEvent * event)
335 GstConcatMux *concat_mux;
338 concat_mux = GST_CONCAT_MUX (gst_pad_get_parent (pad));
340 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
344 /* disable seeking for now */
350 gst_object_unref (concat_mux);
352 return gst_pad_event_default (pad, event);
357 gst_concat_mux_handle_sink_event (GstPad * pad, GstEvent * event)
362 mux = GST_CONCAT_MUX (gst_pad_get_parent (pad));
364 type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
370 g_debug ("sink EOS %p / %d", pad, g_slist_length (mux->sinks));
372 mux->sinks = g_slist_remove (mux->sinks, pad);
373 g_debug ("sink len %d", g_slist_length (mux->sinks));
374 if (g_slist_length (mux->sinks) != 0) {
376 mux->sinkpad = mux->sinks->data;
377 mutex = (GMutex *) gst_pad_get_element_private (mux->sinkpad);
378 g_mutex_unlock (mutex);
379 g_debug ("sink pad %p", mux->sinkpad);
383 g_debug ("sink list is empty");
389 gst_object_unref (mux);
391 return gst_pad_event_default (pad, event);
395 gst_concat_mux_chain (GstPad * pad, GstBuffer * buf)
397 GstConcatMux *mux = (GstConcatMux *) GST_PAD_PARENT (pad);
398 GstBuffer *databuf = NULL;
399 GstFlowReturn ret = GST_FLOW_OK;
403 mutex = (GMutex*) gst_pad_get_element_private (pad);
405 g_mutex_lock (mutex);
407 g_debug ("DONE pad %p", pad);
411 databuf = gst_buffer_make_metadata_writable (buf);
413 if (!mux->negotiated) {
416 newcaps = gst_pad_get_caps (mux->sinkpad);
418 g_debug ("CAPS: %s",gst_caps_to_string (newcaps));
420 if (!gst_pad_set_caps (mux->srcpad, newcaps))
423 mux->negotiated = TRUE;
426 g_debug ("Running [%s]\n"
427 "\tTOFFSET [%"G_GUINT64_FORMAT"]\n"
428 "\tB_TSTAMP [%"G_GUINT64_FORMAT"]\n"
429 "\tB_DURATION [%"G_GUINT64_FORMAT"]\n"
430 "\tOFFSET [%"G_GUINT64_FORMAT"]\n"
431 "\tB_OFFSET [%"G_GUINT64_FORMAT"]",
432 gst_element_get_name (mux),
434 GST_BUFFER_TIMESTAMP (databuf),
435 GST_BUFFER_DURATION (databuf),
437 GST_BUFFER_OFFSET (databuf));
441 g_debug ("RESYNC [%s]", gst_element_get_name (mux));
442 mux->timeoffset += GST_BUFFER_TIMESTAMP (databuf);
443 GST_BUFFER_TIMESTAMP (databuf) = mux->timeoffset;
444 mux->timeoffset += GST_BUFFER_DURATION (databuf);
446 mux->offset += GST_BUFFER_OFFSET (databuf);
447 GST_BUFFER_OFFSET (databuf) = mux->offset;
448 mux->offset += GST_BUFFER_SIZE (databuf);
452 GST_BUFFER_TIMESTAMP (databuf) = mux->timeoffset;
453 mux->timeoffset += GST_BUFFER_DURATION (databuf);
455 GST_BUFFER_OFFSET (databuf) = mux->offset;
456 mux->offset += GST_BUFFER_SIZE (databuf);
459 gst_buffer_set_caps (databuf, GST_PAD_CAPS (pad));
460 ret = gst_pad_push (mux->srcpad, databuf);
462 //gst_buffer_unref (buf);
464 g_mutex_unlock (mutex);
469 GST_WARNING_OBJECT (mux, "failed to set caps");
470 GST_ELEMENT_ERROR (mux, CORE, NEGOTIATION, (NULL), (NULL));
471 return GST_FLOW_NOT_NEGOTIATED;
477 GST_WARNING_OBJECT (mux, "no caps on the incoming buffer %p", best->buffer);
478 GST_ELEMENT_ERROR (mux, CORE, NEGOTIATION, (NULL), (NULL));
479 ret = GST_FLOW_NOT_NEGOTIATED;
485 static GstStateChangeReturn
486 gst_concat_mux_change_state (GstElement * element, GstStateChange transition)
488 GstConcatMux *concat_mux;
489 GstStateChangeReturn ret;
491 concat_mux = GST_CONCAT_MUX (element);
493 switch (transition) {
494 case GST_STATE_CHANGE_READY_TO_PAUSED:
495 concat_mux->done = FALSE;
496 concat_mux->resync = TRUE;
497 GST_DEBUG_OBJECT (concat_mux, "starting collect pads");
499 case GST_STATE_CHANGE_PAUSED_TO_READY:
500 GST_DEBUG_OBJECT (concat_mux, "stopping collect pads");
501 gst_concat_mux_clear (concat_mux);
507 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
508 if (ret == GST_STATE_CHANGE_FAILURE)
511 switch (transition) {
520 gst_concat_mux_plugin_init (GstPlugin * plugin)
523 setlocale (LC_ALL, "");
524 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
525 #endif /* ENABLE_NLS */
527 GST_DEBUG_CATEGORY_INIT (gst_concat_mux_debug, "concatmux", 0,
530 return gst_element_register (plugin, "concatmux", GST_RANK_NONE,
531 GST_TYPE_CONCAT_MUX);
534 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
538 plugin_init, VERSION, "LGPL", "ConcatMux", "")