/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */ /* * gst-editing-services * * Copyright (C) 2013 Thibault Saunier <tsaunier@gnome.org> * gst-editing-services is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * gst-editing-services is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>."; */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <gst/audio/audio.h> #include "ges-types.h" #include "ges-internal.h" #include "ges-smart-adder.h" G_DEFINE_TYPE (GESSmartAdder, ges_smart_adder, GST_TYPE_BIN); #define GET_LOCK(obj) (&((GESSmartAdder*)(obj))->lock) #define LOCK(obj) (g_mutex_lock (GET_LOCK(obj))) #define UNLOCK(obj) (g_mutex_unlock (GET_LOCK(obj))) static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw") ); static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u", GST_PAD_SINK, GST_PAD_REQUEST, GST_STATIC_CAPS ("audio/x-raw") ); #if G_BYTE_ORDER == G_LITTLE_ENDIAN #define DEFAULT_CAPS "audio/x-raw,format=(string)S32LE;" #else #define DEFAULT_CAPS "audio/x-raw,format=(string)S32BE;" #endif typedef struct _PadInfos { GESSmartAdder *self; GstPad *adder_pad; GstElement *bin; } PadInfos; static void destroy_pad (PadInfos * infos) { if (G_LIKELY (infos->bin)) { gst_element_set_state (infos->bin, GST_STATE_NULL); gst_element_unlink (infos->bin, infos->self->adder); gst_bin_remove (GST_BIN (infos->self), infos->bin); } if (infos->adder_pad) { gst_element_release_request_pad (infos->self->adder, infos->adder_pad); gst_object_unref (infos->adder_pad); } g_free (infos); } /**************************************************** * GstElement vmetods * ****************************************************/ static GstPad * _request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * name, const GstCaps * caps) { GstPad *audioresample_srcpad, *audioconvert_sinkpad, *tmpghost; GstPad *ghost; GstElement *audioconvert, *audioresample; PadInfos *infos = g_new0 (PadInfos, 1); GESSmartAdder *self = GES_SMART_ADDER (element); infos->adder_pad = gst_element_request_pad (self->adder, gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (self->adder), "sink_%u"), NULL, caps); if (infos->adder_pad == NULL) { GST_WARNING_OBJECT (element, "Could not get any pad from GstAdder"); g_free (infos); return NULL; } infos->self = self; infos->bin = gst_bin_new (NULL); audioconvert = gst_element_factory_make ("audioconvert", NULL); audioresample = gst_element_factory_make ("audioresample", NULL); gst_bin_add_many (GST_BIN (infos->bin), audioconvert, audioresample, NULL); gst_element_link_many (audioconvert, audioresample, NULL); audioconvert_sinkpad = gst_element_get_static_pad (audioconvert, "sink"); tmpghost = GST_PAD (gst_ghost_pad_new (NULL, audioconvert_sinkpad)); gst_object_unref (audioconvert_sinkpad); gst_pad_set_active (tmpghost, TRUE); gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost); gst_bin_add (GST_BIN (self), infos->bin); ghost = gst_ghost_pad_new (NULL, tmpghost); gst_pad_set_active (ghost, TRUE); if (!gst_element_add_pad (GST_ELEMENT (self), ghost)) goto could_not_add; audioresample_srcpad = gst_element_get_static_pad (audioresample, "src"); tmpghost = GST_PAD (gst_ghost_pad_new (NULL, audioresample_srcpad)); gst_object_unref (audioresample_srcpad); gst_pad_set_active (tmpghost, TRUE); gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost); gst_pad_link (tmpghost, infos->adder_pad); LOCK (self); g_hash_table_insert (self->pads_infos, ghost, infos); UNLOCK (self); GST_DEBUG_OBJECT (self, "Returning new pad %" GST_PTR_FORMAT, ghost); return ghost; could_not_add: { GST_ERROR_OBJECT (self, "could not add pad"); destroy_pad (infos); return NULL; } } static void _release_pad (GstElement * element, GstPad * pad) { GST_DEBUG_OBJECT (element, "Releasing pad %" GST_PTR_FORMAT, pad); LOCK (element); g_hash_table_remove (GES_SMART_ADDER (element)->pads_infos, pad); UNLOCK (element); } /**************************************************** * GObject vmethods * ****************************************************/ static void ges_smart_adder_dispose (GObject * object) { GESSmartAdder *self = GES_SMART_ADDER (object); if (self->pads_infos) { g_hash_table_unref (self->pads_infos); self->pads_infos = NULL; } G_OBJECT_CLASS (ges_smart_adder_parent_class)->dispose (object); } static void ges_smart_adder_finalize (GObject * object) { GESSmartAdder *self = GES_SMART_ADDER (object); g_mutex_clear (&self->lock); G_OBJECT_CLASS (ges_smart_adder_parent_class)->finalize (object); } static void ges_smart_adder_class_init (GESSmartAdderClass * klass) { /* GstBinClass *parent_class = GST_BIN_CLASS (klass); */ GObjectClass *object_class = G_OBJECT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); /* FIXME Make sure the AdderClass doesn get destroy before ourself */ gst_element_class_add_static_pad_template (element_class, &src_template); gst_element_class_add_static_pad_template (element_class, &sink_template); gst_element_class_set_static_metadata (element_class, "GES Smart adder", "Generic/Audio", "Use adder making use of GES informations", "Thibault Saunier <thibault.saunier@collabora.com>"); element_class->request_new_pad = GST_DEBUG_FUNCPTR (_request_new_pad); element_class->release_pad = GST_DEBUG_FUNCPTR (_release_pad); object_class->dispose = ges_smart_adder_dispose; object_class->finalize = ges_smart_adder_finalize; } static void ges_smart_adder_init (GESSmartAdder * self) { GstPad *pad; g_mutex_init (&self->lock); self->adder = gst_element_factory_make ("audiomixer", "smart-adder-adder"); gst_bin_add (GST_BIN (self), self->adder); self->capsfilter = gst_element_factory_make ("capsfilter", "smart-adder-capsfilter"); gst_bin_add (GST_BIN (self), self->capsfilter); gst_element_link (self->adder, self->capsfilter); pad = gst_element_get_static_pad (self->capsfilter, "src"); self->srcpad = gst_ghost_pad_new ("src", pad); gst_pad_set_active (self->srcpad, TRUE); gst_object_unref (pad); gst_element_add_pad (GST_ELEMENT (self), self->srcpad); self->pads_infos = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) destroy_pad); } static void restriction_caps_cb (GESTrack * track, GParamSpec * arg G_GNUC_UNUSED, GESSmartAdder * self) { GstCaps *caps; g_object_get (track, "restriction-caps", &caps, NULL); if (!caps) caps = gst_caps_from_string (DEFAULT_CAPS); GST_DEBUG_OBJECT (self, "Setting adder caps to %" GST_PTR_FORMAT, caps); g_object_set (self->capsfilter, "caps", caps, NULL); gst_caps_unref (caps); } GstElement * ges_smart_adder_new (GESTrack * track) { GESSmartAdder *self; self = g_object_new (GES_TYPE_SMART_ADDER, NULL); self->track = track; if (track) { restriction_caps_cb (track, NULL, self); g_signal_connect (track, "notify::restriction-caps", G_CALLBACK (restriction_caps_cb), self); } /* FIXME Make adder smart and let it properly negotiate caps! */ return GST_ELEMENT (self); }