diff options
author | Fabian Orccon <cfoch.fabian@gmail.com> | 2017-04-05 16:03:08 -0500 |
---|---|---|
committer | Sebastian Dröge <sebastian@centricular.com> | 2017-04-11 11:22:01 +0300 |
commit | d4797e445577190f861cf932d24014973d6d842a (patch) | |
tree | 0d038529eb0dee55839fabf9a5fd2cb5a51005ca /gst/faceoverlay | |
parent | 270f97611c9ebeb1c3a9869894c2bff5de7dafdb (diff) |
faceoverlay: Revert deletion
https://bugzilla.gnome.org/show_bug.cgi?id=764011
Diffstat (limited to 'gst/faceoverlay')
-rw-r--r-- | gst/faceoverlay/Makefile.am | 13 | ||||
-rw-r--r-- | gst/faceoverlay/gstfaceoverlay.c | 452 | ||||
-rw-r--r-- | gst/faceoverlay/gstfaceoverlay.h | 94 | ||||
-rw-r--r-- | gst/faceoverlay/meson.build | 12 |
4 files changed, 571 insertions, 0 deletions
diff --git a/gst/faceoverlay/Makefile.am b/gst/faceoverlay/Makefile.am new file mode 100644 index 000000000..31a438216 --- /dev/null +++ b/gst/faceoverlay/Makefile.am @@ -0,0 +1,13 @@ +plugin_LTLIBRARIES = libgstfaceoverlay.la + +# sources used to compile this plug-in +libgstfaceoverlay_la_SOURCES = gstfaceoverlay.c + +# compiler and linker flags used to compile this plugin, set in configure.ac +libgstfaceoverlay_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +libgstfaceoverlay_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_API_VERSION@ $(GST_LIBS) +libgstfaceoverlay_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstfaceoverlay_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +# headers we need but don't want installed +noinst_HEADERS = gstfaceoverlay.h diff --git a/gst/faceoverlay/gstfaceoverlay.c b/gst/faceoverlay/gstfaceoverlay.c new file mode 100644 index 000000000..19006f378 --- /dev/null +++ b/gst/faceoverlay/gstfaceoverlay.c @@ -0,0 +1,452 @@ +/* GStreamer faceoverlay plugin + * Copyright (C) 2011 Laura Lucas Alday <lauralucas@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-faceoverlay + * + * Overlays a SVG image over a detected face in a video stream. + * x, y, w, and h properties are optional, and change the image position and + * size relative to the detected face position and size. + * + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch autovideosrc ! videoconvert ! faceoverlay location=/path/to/gnome-video-effects/pixmaps/bow.svg x=-5 y=-15 w=0.3 h=0.1 ! videoconvert ! autovideosink + * ]| + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <gst/gst.h> +#include <gst/video/video.h> +#include <string.h> + +#include "gstfaceoverlay.h" + +GST_DEBUG_CATEGORY_STATIC (gst_face_overlay_debug); +#define GST_CAT_DEFAULT gst_face_overlay_debug + +enum +{ + PROP_0, + PROP_LOCATION, + PROP_X, + PROP_Y, + PROP_W, + PROP_H +}; + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-rgb; video/x-raw-yuv") + ); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-rgb; video/x-raw-yuv") + ); + +GST_BOILERPLATE (GstFaceOverlay, gst_face_overlay, GstBin, GST_TYPE_BIN); + +static void gst_face_overlay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_face_overlay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_face_overlay_message_handler (GstBin * bin, + GstMessage * message); +static GstStateChangeReturn gst_face_overlay_change_state (GstElement * element, + GstStateChange transition); +static gboolean gst_face_overlay_create_children (GstFaceOverlay * filter); + +static gboolean +gst_face_overlay_create_children (GstFaceOverlay * filter) +{ + GstElement *csp, *face_detect, *overlay; + GstPad *pad; + + csp = gst_element_factory_make ("videoconvert", NULL); + face_detect = gst_element_factory_make ("facedetect", NULL); + overlay = gst_element_factory_make ("rsvgoverlay", NULL); + + /* FIXME: post missing-plugin messages on NULL->READY if needed */ + if (csp == NULL || face_detect == NULL || overlay == NULL) + goto missing_element; + + g_object_set (face_detect, "display", FALSE, NULL); + + gst_bin_add_many (GST_BIN (filter), face_detect, csp, overlay, NULL); + filter->svg_overlay = overlay; + + if (!gst_element_link_many (face_detect, csp, overlay, NULL)) + GST_ERROR_OBJECT (filter, "couldn't link elements"); + + pad = gst_element_get_static_pad (face_detect, "sink"); + if (!gst_ghost_pad_set_target (GST_GHOST_PAD (filter->sinkpad), pad)) + GST_ERROR_OBJECT (filter->sinkpad, "couldn't set sinkpad target"); + gst_object_unref (pad); + + pad = gst_element_get_static_pad (overlay, "src"); + if (!gst_ghost_pad_set_target (GST_GHOST_PAD (filter->srcpad), pad)) + GST_ERROR_OBJECT (filter->srcpad, "couldn't set srcpad target"); + gst_object_unref (pad); + + return TRUE; + +/* ERRORS */ +missing_element: + { + /* clean up */ + if (csp == NULL) + GST_ERROR_OBJECT (filter, "videoconvert element not found"); + else + gst_object_unref (csp); + + if (face_detect == NULL) + GST_ERROR_OBJECT (filter, "facedetect element not found (opencv plugin)"); + else + gst_object_unref (face_detect); + + if (overlay == NULL) + GST_ERROR_OBJECT (filter, "rsvgoverlay element not found (rsvg plugin)"); + else + gst_object_unref (overlay); + + return FALSE; + } +} + +static GstStateChangeReturn +gst_face_overlay_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + GstFaceOverlay *filter = GST_FACEOVERLAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (filter->svg_overlay == NULL) { + GST_ELEMENT_ERROR (filter, CORE, MISSING_PLUGIN, (NULL), + ("Some required plugins are missing, probably either the opencv " + "facedetect element or rsvgoverlay")); + return GST_STATE_CHANGE_FAILURE; + } + filter->update_svg = TRUE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + default: + break; + } + + return ret; +} + +static void +gst_face_overlay_handle_faces (GstFaceOverlay * filter, const GstStructure * s) +{ + guint x, y, width, height; + gint svg_x, svg_y, svg_width, svg_height; + const GstStructure *face; + const GValue *faces_list, *face_val; + gchar *new_location = NULL; + gint face_count; + +#if 0 + /* optionally draw the image once every two messages for better performance */ + filter->process_message = !filter->process_message; + if (!filter->process_message) + return; +#endif + + faces_list = gst_structure_get_value (s, "faces"); + face_count = gst_value_list_get_size (faces_list); + GST_LOG_OBJECT (filter, "face count: %d", face_count); + + if (face_count == 0) { + GST_DEBUG_OBJECT (filter, "no face, clearing overlay"); + g_object_set (filter->svg_overlay, "location", NULL, NULL); + GST_OBJECT_LOCK (filter); + filter->update_svg = TRUE; + GST_OBJECT_UNLOCK (filter); + return; + } + + /* The last face in the list seems to be the right one, objects mistakenly + * detected as faces for a couple of frames seem to be in the list + * beginning. TODO: needs confirmation. */ + face_val = gst_value_list_get_value (faces_list, face_count - 1); + face = gst_value_get_structure (face_val); + gst_structure_get_uint (face, "x", &x); + gst_structure_get_uint (face, "y", &y); + gst_structure_get_uint (face, "width", &width); + gst_structure_get_uint (face, "height", &height); + + /* Apply x and y offsets relative to face position and size. + * Set image width and height as a fraction of face width and height. + * Cast to int since face position and size will never be bigger than + * G_MAX_INT and we may have negative values as svg_x or svg_y */ + + GST_OBJECT_LOCK (filter); + + svg_x = (gint) x + (gint) (filter->x * width); + svg_y = (gint) y + (gint) (filter->y * height); + + svg_width = (gint) (filter->w * width); + svg_height = (gint) (filter->h * height); + + if (filter->update_svg) { + new_location = g_strdup (filter->location); + filter->update_svg = FALSE; + } + GST_OBJECT_UNLOCK (filter); + + if (new_location != NULL) { + GST_DEBUG_OBJECT (filter, "set rsvgoverlay location=%s", new_location); + g_object_set (filter->svg_overlay, "location", new_location, NULL); + g_free (new_location); + } + + GST_LOG_OBJECT (filter, "overlay dimensions: %d x %d @ %d,%d", + svg_width, svg_height, svg_x, svg_y); + + g_object_set (filter->svg_overlay, + "x", svg_x, "y", svg_y, "width", svg_width, "height", svg_height, NULL); +} + +static void +gst_face_overlay_message_handler (GstBin * bin, GstMessage * message) +{ + if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) { + const GstStructure *s = gst_message_get_structure (message); + + if (gst_structure_has_name (s, "facedetect")) { + gst_face_overlay_handle_faces (GST_FACEOVERLAY (bin), s); + } + } + + GST_BIN_CLASS (parent_class)->handle_message (bin, message); +} + +static void +gst_face_overlay_base_init (gpointer gclass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); + + gst_element_class_set_static_metadata (element_class, + "faceoverlay", + "Filter/Editor/Video", + "Overlays SVG graphics over a detected face in a video stream", + "Laura Lucas Alday <lauralucas@gmail.com>"); + + gst_element_class_add_static_pad_template (element_class, &src_factory); + gst_element_class_add_static_pad_template (element_class, &sink_factory); +} + +static void +gst_face_overlay_class_init (GstFaceOverlayClass * klass) +{ + GObjectClass *gobject_class; + GstBinClass *gstbin_class; + GstElementClass *gstelement_class; + + gobject_class = G_OBJECT_CLASS (klass); + gstbin_class = GST_BIN_CLASS (klass); + gstelement_class = GST_ELEMENT_CLASS (klass); + + gobject_class->set_property = gst_face_overlay_set_property; + gobject_class->get_property = gst_face_overlay_get_property; + + g_object_class_install_property (gobject_class, PROP_LOCATION, + g_param_spec_string ("location", "Location", + "Location of SVG file to use for face overlay", + "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_X, + g_param_spec_float ("x", "face x offset", + "Specify image x relative to detected face x.", -G_MAXFLOAT, + G_MAXFLOAT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_Y, + g_param_spec_float ("y", "face y offset", + "Specify image y relative to detected face y.", -G_MAXFLOAT, + G_MAXFLOAT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_W, + g_param_spec_float ("w", "face width percent", + "Specify image width relative to face width.", 0, G_MAXFLOAT, 1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_H, + g_param_spec_float ("h", "face height percent", + "Specify image height relative to face height.", 0, G_MAXFLOAT, 1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gstbin_class->handle_message = + GST_DEBUG_FUNCPTR (gst_face_overlay_message_handler); + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_face_overlay_change_state); +} + +static void +gst_face_overlay_init (GstFaceOverlay * filter, GstFaceOverlayClass * gclass) +{ + GstPadTemplate *tmpl; + + filter->x = 0; + filter->y = 0; + filter->w = 1; + filter->h = 1; + filter->svg_overlay = NULL; + filter->location = NULL; + filter->process_message = TRUE; + + tmpl = gst_static_pad_template_get (&sink_factory); + filter->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", tmpl); + gst_object_unref (tmpl); + gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad); + + tmpl = gst_static_pad_template_get (&src_factory); + filter->srcpad = gst_ghost_pad_new_no_target_from_template ("src", tmpl); + gst_object_unref (tmpl); + gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad); + + gst_face_overlay_create_children (filter); +} + +static void +gst_face_overlay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstFaceOverlay *filter = GST_FACEOVERLAY (object); + + switch (prop_id) { + case PROP_LOCATION: + GST_OBJECT_LOCK (filter); + g_free (filter->location); + filter->location = g_value_dup_string (value); + filter->update_svg = TRUE; + GST_OBJECT_UNLOCK (filter); + break; + case PROP_X: + GST_OBJECT_LOCK (filter); + filter->x = g_value_get_float (value); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_Y: + GST_OBJECT_LOCK (filter); + filter->y = g_value_get_float (value); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_W: + GST_OBJECT_LOCK (filter); + filter->w = g_value_get_float (value); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_H: + GST_OBJECT_LOCK (filter); + filter->h = g_value_get_float (value); + GST_OBJECT_UNLOCK (filter); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_face_overlay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstFaceOverlay *filter = GST_FACEOVERLAY (object); + + switch (prop_id) { + case PROP_LOCATION: + GST_OBJECT_LOCK (filter); + g_value_set_string (value, filter->location); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_X: + GST_OBJECT_LOCK (filter); + g_value_set_float (value, filter->x); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_Y: + GST_OBJECT_LOCK (filter); + g_value_set_float (value, filter->y); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_W: + GST_OBJECT_LOCK (filter); + g_value_set_float (value, filter->w); + GST_OBJECT_UNLOCK (filter); + break; + case PROP_H: + GST_OBJECT_LOCK (filter); + g_value_set_float (value, filter->h); + GST_OBJECT_UNLOCK (filter); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +faceoverlay_init (GstPlugin * faceoverlay) +{ + GST_DEBUG_CATEGORY_INIT (gst_face_overlay_debug, "faceoverlay", + 0, "SVG Face Overlay"); + + return gst_element_register (faceoverlay, "faceoverlay", GST_RANK_NONE, + GST_TYPE_FACEOVERLAY); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + faceoverlay, + "SVG Face Overlay", + faceoverlay_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/faceoverlay/gstfaceoverlay.h b/gst/faceoverlay/gstfaceoverlay.h new file mode 100644 index 000000000..810e28adc --- /dev/null +++ b/gst/faceoverlay/gstfaceoverlay.h @@ -0,0 +1,94 @@ +/* GStreamer faceoverlay plugin + * Copyright (C) 2011 Laura Lucas Alday <lauralucas@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_FACEOVERLAY_H__ +#define __GST_FACEOVERLAY_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define GST_TYPE_FACEOVERLAY \ + (gst_face_overlay_get_type()) +#define GST_FACEOVERLAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FACEOVERLAY,GstFaceOverlay)) +#define GST_FACEOVERLAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FACEOVERLAY,GstFaceOverlayClass)) +#define GST_IS_FACEOVERLAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FACEOVERLAY)) +#define GST_IS_FACEOVERLAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FACEOVERLAY)) + +typedef struct _GstFaceOverlay GstFaceOverlay; +typedef struct _GstFaceOverlayClass GstFaceOverlayClass; + +struct _GstFaceOverlay +{ + GstBin parent; + + GstPad *sinkpad, *srcpad; + + GstElement *face_detect; + GstElement *colorspace; + GstElement *svg_overlay; + + gboolean process_message; + + gboolean update_svg; + + gchar *location; + gfloat x; + gfloat y; + gfloat w; + gfloat h; +}; + +struct _GstFaceOverlayClass +{ + GstBinClass parent_class; +}; + +GType gst_face_overlay_get_type (void); + +G_END_DECLS + +#endif /* __GST_FACEOVERLAY_H__ */ diff --git a/gst/faceoverlay/meson.build b/gst/faceoverlay/meson.build new file mode 100644 index 000000000..65aa7295d --- /dev/null +++ b/gst/faceoverlay/meson.build @@ -0,0 +1,12 @@ +fover_sources = [ + 'gstfaceoverlay.c', +] + +gstfaceoverlay = library('gstfaceoverlay', + fover_sources, + c_args : gst_plugins_bad_args, + include_directories : [configinc], + dependencies : [gstbase_dep, gstvideo_dep], + install : true, + install_dir : plugins_install_dir, +) |