2 * concat muxer plugin for GStreamer Copyright (C) 2004 Renato Filhps
3 * <renato.filho@indt.org.br> This library is free software; you can
4 * redistribute it and/or modify it under the terms of the GNU Library
5 * General Public License as published by the Free Software Foundation;
6 * either version 2 of the License, or (at your option) any later version.
7 * This library is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library
10 * General Public License for more details. You should have received a copy
11 * of the GNU Library General Public License along with this library; if
12 * not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite
13 * 330, Boston, MA 02111-1307, USA.
17 * SECTION:element-concatmux
18 * @short_description: Concat that takes one or several digital streams
19 * and muxes them to a single stream.
22 * <title>Sample pipelines</title>
24 * Here is a simple pipeline to concat 2 files into a another file:
26 * gst-launch concatmux name=m ! filesink location=output.txt filesrc location=file_a.txt ! m. filesrc location=file_b.txt ! m.
37 #include <gst/base/gstcollectpads.h>
41 GST_DEBUG_CATEGORY_STATIC(gst_concat_mux_debug);
42 #define GST_CAT_DEFAULT gst_concat_mux_debug
44 #define GST_TYPE_CONCAT_MUX (gst_concat_mux_get_type())
45 #define GST_CONCAT_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CONCAT_MUX, GstConcatMux))
46 #define GST_CONCAT_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CONCAT_MUX, GstConcatMux))
47 #define GST_IS_CONCAT_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CONCAT_MUX))
48 #define GST_IS_CONCAT_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CONCAT_MUX))
50 typedef struct _GstConcatMux GstConcatMux;
51 typedef struct _GstConcatMuxClass GstConcatMuxClass;
56 * The opaque #GstConcatMux structure.
58 struct _GstConcatMux {
90 struct _GstConcatMuxClass {
91 GstElementClass parent_class;
95 * elementfactory information
97 static const GstElementDetails gst_concat_mux_details =
98 GST_ELEMENT_DETAILS("Concat muxer",
100 "mux concat streams",
101 "Renato Filho <renato.filho@indt.org>");
103 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE("src",
106 GST_STATIC_CAPS_ANY);
108 static GstStaticPadTemplate sink_factory =
109 GST_STATIC_PAD_TEMPLATE("sink_%d",
112 GST_STATIC_CAPS_ANY);
114 static void gst_concat_mux_base_init(gpointer g_class);
115 static void gst_concat_mux_class_init(GstConcatMuxClass * klass);
116 static void gst_concat_mux_init(GstConcatMux * concat_mux);
118 static void gst_concat_mux_finalize(GObject * object);
120 static gboolean gst_concat_mux_handle_src_event(GstPad * pad,
122 static gboolean gst_concat_mux_handle_sink_event(GstPad * pad,
125 static GstPad *gst_concat_mux_request_new_pad(GstElement * element,
126 GstPadTemplate * templ,
128 static GstStateChangeReturn gst_concat_mux_change_state(GstElement *
133 static GstFlowReturn gst_concat_mux_chain(GstPad * pad, GstBuffer * buf);
134 static void gst_concat_mux_clear(GstConcatMux * mux);
137 static GstElementClass *parent_class = NULL;
140 gst_concat_mux_get_type(void)
142 static GType concat_mux_type = 0;
144 if (!concat_mux_type) {
145 static const GTypeInfo concat_mux_info = {
146 sizeof(GstConcatMuxClass),
147 gst_concat_mux_base_init,
149 (GClassInitFunc) gst_concat_mux_class_init,
152 sizeof(GstConcatMux),
154 (GInstanceInitFunc) gst_concat_mux_init,
158 g_type_register_static(GST_TYPE_ELEMENT, "GstConcatMux",
159 &concat_mux_info, 0);
161 return concat_mux_type;
165 gst_concat_mux_base_init(gpointer g_class)
167 GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
169 gst_element_class_add_pad_template(element_class,
170 gst_static_pad_template_get
172 gst_element_class_add_pad_template(element_class,
173 gst_static_pad_template_get
176 gst_element_class_set_details(element_class, &gst_concat_mux_details);
180 gst_concat_mux_class_init(GstConcatMuxClass * klass)
182 GObjectClass *gobject_class;
183 GstElementClass *gstelement_class;
185 gobject_class = (GObjectClass *) klass;
186 gstelement_class = (GstElementClass *) klass;
188 parent_class = g_type_class_peek_parent(klass);
190 gobject_class->finalize = gst_concat_mux_finalize;
192 gstelement_class->request_new_pad = gst_concat_mux_request_new_pad;
193 gstelement_class->change_state = gst_concat_mux_change_state;
197 gst_concat_mux_init(GstConcatMux * concat_mux)
199 GstElementClass *klass = GST_ELEMENT_GET_CLASS(concat_mux);
202 gst_pad_new_from_template(gst_element_class_get_pad_template(klass,
205 gst_pad_set_event_function(concat_mux->srcpad,
206 gst_concat_mux_handle_src_event);
207 gst_element_add_pad(GST_ELEMENT(concat_mux), concat_mux->srcpad);
211 gst_concat_mux_finalize(GObject * object)
213 GstConcatMux *concat_mux;
215 concat_mux = GST_CONCAT_MUX(object);
216 gst_concat_mux_clear(GST_CONCAT_MUX(object));
218 G_OBJECT_CLASS(parent_class)->finalize(object);
222 gst_concat_mux_free_pad(gpointer data, gpointer user_data)
226 mux = gst_pad_get_element_private(GST_PAD(data));
229 gst_object_unref(GST_OBJECT(data));
233 gst_concat_mux_clear(GstConcatMux * mux)
238 mux->negotiated = FALSE;
240 if (mux->sinks != NULL) {
241 g_slist_foreach(mux->sinks, gst_concat_mux_free_pad, mux);
242 g_slist_free(mux->sinks);
248 static GstPadLinkReturn
249 gst_concat_mux_sinkconnect(GstPad * pad, GstPad * peer)
251 gchar *pad_name = NULL;
252 GstConcatMux *concat_mux;
254 concat_mux = GST_CONCAT_MUX(gst_pad_get_parent(pad));
256 if (concat_mux->sink_caps != NULL) {
257 GstCaps *peer_caps = gst_pad_get_caps(peer);
260 intersect = gst_caps_intersect(concat_mux->sink_caps, peer_caps);
261 if (intersect == NULL) {
262 gst_caps_unref(peer_caps);
263 return GST_PAD_LINK_NOFORMAT;
265 gst_caps_unref(peer_caps);
266 gst_caps_unref(intersect);
268 concat_mux->sink_caps = gst_pad_get_caps(pad);
271 pad_name = gst_pad_get_name(pad);
273 GST_DEBUG_OBJECT(concat_mux, "sinkconnect triggered on %s", pad_name);
277 gst_object_unref(concat_mux);
279 return GST_PAD_LINK_OK;
283 gst_concat_mux_request_new_pad(GstElement * element,
284 GstPadTemplate * templ,
285 const gchar * req_name)
287 GstConcatMux *concat_mux;
289 GstElementClass *klass = GST_ELEMENT_GET_CLASS(element);
292 g_return_val_if_fail(templ != NULL, NULL);
294 if (templ->direction != GST_PAD_SINK) {
295 g_warning("concat_mux: request pad that is not a SINK pad\n");
299 g_return_val_if_fail(GST_IS_CONCAT_MUX(element), NULL);
301 concat_mux = GST_CONCAT_MUX(element);
303 if (templ == gst_element_class_get_pad_template(klass, "sink_%d")) {
307 * create new pad with the name
309 name = g_strdup_printf("sink_%02d", concat_mux->numpads);
310 g_debug("NEw pad %s", name);
311 newpad = gst_pad_new_from_template(templ, name);
313 concat_mux->sinks = g_slist_append(concat_mux->sinks, newpad);
314 g_debug("New sink %p / %d", newpad,
315 g_slist_length(concat_mux->sinks));
316 concat_mux->numpads++;
318 g_warning("concat_mux: this is not our template!\n");
322 mutex = g_mutex_new();
324 if (concat_mux->sinkpad == NULL) {
325 concat_mux->sinkpad = newpad;
330 gst_pad_set_element_private(newpad, mutex);
332 * setup some pad functions
334 gst_pad_set_link_function(newpad, gst_concat_mux_sinkconnect);
335 gst_pad_set_event_function(newpad, gst_concat_mux_handle_sink_event);
336 gst_pad_set_chain_function(newpad, gst_concat_mux_chain);
339 * add the pad to the element
341 gst_element_add_pad(element, newpad);
350 gst_concat_mux_handle_src_event(GstPad * pad, GstEvent * event)
352 GstConcatMux *concat_mux;
355 concat_mux = GST_CONCAT_MUX(gst_pad_get_parent(pad));
357 type = event ? GST_EVENT_TYPE(event) : GST_EVENT_UNKNOWN;
362 * disable seeking for now
369 gst_object_unref(concat_mux);
371 return gst_pad_event_default(pad, event);
378 gst_concat_mux_handle_sink_event(GstPad * pad, GstEvent * event)
383 mux = GST_CONCAT_MUX(gst_pad_get_parent(pad));
385 type = event ? GST_EVENT_TYPE(event) : GST_EVENT_UNKNOWN;
391 g_debug("sink EOS %p / %d", pad, g_slist_length(mux->sinks));
395 mux->sinks = g_slist_remove(mux->sinks, pad);
396 g_debug("sink len %d", g_slist_length(mux->sinks));
397 if (g_slist_length(mux->sinks) != 0) {
399 mux->sinkpad = mux->sinks->data;
401 (GMutex *) gst_pad_get_element_private(mux->sinkpad);
402 g_mutex_unlock(mutex);
403 g_debug("sink pad %p", mux->sinkpad);
407 g_debug("sink list is empty");
413 gst_object_unref(mux);
415 return gst_pad_event_default(pad, event);
419 gst_concat_mux_chain(GstPad * pad, GstBuffer * buf)
421 GstConcatMux *mux = (GstConcatMux *) GST_PAD_PARENT(pad);
422 GstBuffer *databuf = NULL;
423 GstFlowReturn ret = GST_FLOW_OK;
427 mutex = (GMutex *) gst_pad_get_element_private(pad);
431 g_debug("DONE pad %p", pad);
432 g_mutex_unlock(mutex);
436 databuf = gst_buffer_make_metadata_writable(buf);
438 if (!mux->negotiated) {
440 * GstCaps *newcaps; newcaps = gst_pad_get_caps (mux->sinkpad);
442 * g_debug ("CAPS: %s",gst_caps_to_string (newcaps));
444 * if (!gst_pad_set_caps (mux->srcpad, newcaps)) goto nego_error;
446 mux->negotiated = TRUE;
450 * g_debug ("Running [%s]\n" "\tTOFFSET [%"G_GUINT64_FORMAT"]\n"
451 * "\tB_TSTAMP [%"G_GUINT64_FORMAT"]\n" "\tB_DURATION
452 * [%"G_GUINT64_FORMAT"]\n" "\tOFFSET [%"G_GUINT64_FORMAT"]\n"
453 * "\tB_OFFSET [%"G_GUINT64_FORMAT"]", gst_element_get_name (mux),
454 * mux->timeoffset, GST_BUFFER_TIMESTAMP (databuf),
455 * GST_BUFFER_DURATION (databuf), mux->offset, GST_BUFFER_OFFSET
461 g_debug("RESYNC [%s]", gst_element_get_name(mux));
462 mux->timeoffset += GST_BUFFER_TIMESTAMP(databuf);
463 GST_BUFFER_TIMESTAMP(databuf) = mux->timeoffset;
464 mux->timeoffset += GST_BUFFER_DURATION(databuf);
466 mux->offset += GST_BUFFER_OFFSET(databuf);
467 GST_BUFFER_OFFSET(databuf) = mux->offset;
468 mux->offset += GST_BUFFER_SIZE(databuf);
472 GST_BUFFER_TIMESTAMP(databuf) = mux->timeoffset;
473 mux->timeoffset += GST_BUFFER_DURATION(databuf);
475 GST_BUFFER_OFFSET(databuf) = mux->offset;
476 mux->offset += GST_BUFFER_SIZE(databuf);
479 gst_buffer_set_caps(databuf, GST_PAD_CAPS(pad));
480 ret = gst_pad_push(mux->srcpad, databuf);
482 // gst_buffer_unref (buf);
484 g_mutex_unlock(mutex);
487 * nego_error: { GST_WARNING_OBJECT (mux, "failed to set caps");
488 * GST_ELEMENT_ERROR (mux, CORE, NEGOTIATION, (NULL), (NULL)); return
489 * GST_FLOW_NOT_NEGOTIATED; }
492 * no_caps: { GST_WARNING_OBJECT (mux, "no caps on the incoming buffer
493 * %p", best->buffer); GST_ELEMENT_ERROR (mux, CORE, NEGOTIATION,
494 * (NULL), (NULL)); ret = GST_FLOW_NOT_NEGOTIATED; goto beach; }
498 static GstStateChangeReturn
499 gst_concat_mux_change_state(GstElement * element,
500 GstStateChange transition)
502 GstConcatMux *concat_mux;
503 GstStateChangeReturn ret;
505 concat_mux = GST_CONCAT_MUX(element);
507 switch (transition) {
508 case GST_STATE_CHANGE_READY_TO_PAUSED:
509 concat_mux->done = FALSE;
510 concat_mux->resync = TRUE;
511 GST_DEBUG_OBJECT(concat_mux, "starting collect pads");
513 case GST_STATE_CHANGE_PAUSED_TO_READY:
514 GST_DEBUG_OBJECT(concat_mux, "stopping collect pads");
515 gst_concat_mux_clear(concat_mux);
522 GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
523 if (ret == GST_STATE_CHANGE_FAILURE)
526 switch (transition) {
535 gst_concat_mux_plugin_init(GstPlugin * plugin)
538 setlocale(LC_ALL, "");
539 bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
540 #endif /* ENABLE_NLS */
542 GST_DEBUG_CATEGORY_INIT(gst_concat_mux_debug, "concatmux", 0,
545 return gst_element_register(plugin, "concatmux", GST_RANK_NONE,
546 GST_TYPE_CONCAT_MUX);
549 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
553 gst_concat_mux_plugin_init, VERSION, GST_LICENSE,
554 GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)