diff --git a/configure.ac b/configure.ac index 35ef31f8af..0a5aea9b36 100644 --- a/configure.ac +++ b/configure.ac @@ -475,7 +475,6 @@ GST_PLUGINS_NONPORTED=" cdxaparse \ mve nuvdemux \ patchdetect \ sdi tta \ - videomeasure \ linsys \ apexsink \ nas sdl timidity \ @@ -548,7 +547,6 @@ AG_GST_CHECK_PLUGIN(stereo) AG_GST_CHECK_PLUGIN(timecode) AG_GST_CHECK_PLUGIN(tta) AG_GST_CHECK_PLUGIN(videofilters) -AG_GST_CHECK_PLUGIN(videomeasure) AG_GST_CHECK_PLUGIN(videoparsers) AG_GST_CHECK_PLUGIN(videosignal) AG_GST_CHECK_PLUGIN(vmnc) @@ -3748,7 +3746,6 @@ gst/stereo/Makefile gst/tta/Makefile gst/timecode/Makefile gst/videofilters/Makefile -gst/videomeasure/Makefile gst/videoparsers/Makefile gst/videosignal/Makefile gst/vmnc/Makefile diff --git a/gst/meson.build b/gst/meson.build index c44335190f..a1eba92a1c 100644 --- a/gst/meson.build +++ b/gst/meson.build @@ -81,8 +81,6 @@ subdir('subenc') #subdir('vbidec') subdir('videofilters') subdir('videoframe_audiolevel') -# did not work -#subdir('videomeasure') subdir('videoparsers') subdir('videosignal') subdir('vmnc') diff --git a/gst/videomeasure/Makefile.am b/gst/videomeasure/Makefile.am deleted file mode 100644 index 139dee9546..0000000000 --- a/gst/videomeasure/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -plugin_LTLIBRARIES = libgstvideomeasure.la - -noinst_HEADERS = gstvideomeasure_ssim.h gstvideomeasure_collector.h - -libgstvideomeasure_la_SOURCES = \ - gstvideomeasure.c \ - gstvideomeasure.h \ - gstvideomeasure_ssim.c \ - gstvideomeasure_collector.c - -libgstvideomeasure_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) \ - $(GST_PLUGINS_BASE_CFLAGS) \ - $(GST_BASE_CFLAGS) \ - $(GST_CFLAGS) -libgstvideomeasure_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ - -lgstvideo-@GST_API_VERSION@ $(GST_BASE_LIBS) $(GST_LIBS) $(LIBM) -libgstvideomeasure_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstvideomeasure_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) diff --git a/gst/videomeasure/gstvideomeasure.c b/gst/videomeasure/gstvideomeasure.c deleted file mode 100644 index aa97a3f089..0000000000 --- a/gst/videomeasure/gstvideomeasure.c +++ /dev/null @@ -1,71 +0,0 @@ -/* GStreamer - * Copyright (C) <2009> Руслан Ижбулатов - * - * This library 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 2.1 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "gstvideomeasure.h" -#include "gstvideomeasure_ssim.h" -#include "gstvideomeasure_collector.h" - -GstEvent * -gst_event_new_measured (guint64 framenumber, GstClockTime timestamp, - const gchar * metric, const GValue * mean, const GValue * lowest, - const GValue * highest) -{ - GstStructure *str = gst_structure_new (GST_EVENT_VIDEO_MEASURE, - "event", G_TYPE_STRING, "frame-measured", - "offset", G_TYPE_UINT64, framenumber, - "timestamp", GST_TYPE_CLOCK_TIME, timestamp, - "metric", G_TYPE_STRING, metric, - NULL); - gst_structure_set_value (str, "mean", mean); - gst_structure_set_value (str, "lowest", lowest); - gst_structure_set_value (str, "highest", highest); - return gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, str); -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - gboolean res; - -#ifdef ENABLE_NLS - GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, - LOCALEDIR); - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); -#endif - - res = gst_element_register (plugin, "ssim", GST_RANK_NONE, GST_TYPE_SSIM); - - res &= gst_element_register (plugin, "measurecollector", GST_RANK_NONE, - GST_TYPE_MEASURE_COLLECTOR); - - return res; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - videomeasure, - "Various video measurers", - plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/videomeasure/gstvideomeasure.h b/gst/videomeasure/gstvideomeasure.h deleted file mode 100644 index e385a8d1d5..0000000000 --- a/gst/videomeasure/gstvideomeasure.h +++ /dev/null @@ -1,31 +0,0 @@ -/* GStreamer - * Copyright (C) <2009> Руслан Ижбулатов - * - * This library 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 2.1 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#ifndef __GST_VIDEO_MEASURE_H__ -#define __GST_VIDEO_MEASURE_H__ - -#include - -#define GST_EVENT_VIDEO_MEASURE "application/x-videomeasure" - -GstEvent *gst_event_new_measured (guint64 framenumber, GstClockTime timestamp, - const gchar *metric, const GValue *mean, const GValue *lowest, - const GValue *highest); - -#endif /* __GST_VIDEO_MEASURE_H__ */ diff --git a/gst/videomeasure/gstvideomeasure_collector.c b/gst/videomeasure/gstvideomeasure_collector.c deleted file mode 100644 index 3778bbf20d..0000000000 --- a/gst/videomeasure/gstvideomeasure_collector.c +++ /dev/null @@ -1,410 +0,0 @@ -/* GStreamer - * Copyright (C) <2009> Руслан Ижбулатов - * - * This library 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 2.1 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -/** - * SECTION:element-measurecollector - * - * This plugin collects measurements from measuring elemtns and calculates - * total measure for the whole sequence and also outputs measurements to a file - * "GstMeasureCollector". - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "gstvideomeasure_collector.h" - -#include -#include -#include - -#include - -/* GstMeasureCollector signals and args */ - -enum -{ - PROP_0, - PROP_FLAGS, - PROP_FILENAME -}; - -GST_DEBUG_CATEGORY_STATIC (measure_collector_debug); -#define GST_CAT_DEFAULT measure_collector_debug - -static GstStaticPadTemplate gst_measure_collector_src_template = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate gst_measure_collector_sink_template = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -//static GstBaseTransformClass *parent_class = NULL; - -static void gst_measure_collector_finalize (GObject * object); -static gboolean gst_measure_collector_event (GstBaseTransform * base, - GstEvent * event); -static void gst_measure_collector_save_csv (GstMeasureCollector * mc); - -static void gst_measure_collector_post_message (GstMeasureCollector * mc); - -GST_BOILERPLATE (GstMeasureCollector, gst_measure_collector, GstBaseTransform, - GST_TYPE_BASE_TRANSFORM); - -static void -gst_measure_collector_collect (GstMeasureCollector * mc, GstEvent * gstevent) -{ - const GstStructure *str; - const gchar *event, *metric; - guint64 framenumber = G_MAXUINT64; - const GValue *framenumber_v; - - str = gst_event_get_structure (gstevent); - - event = gst_structure_get_string (str, "event"); - metric = gst_structure_get_string (str, "metric"); - - if (strcmp (event, "frame-measured") == 0 && metric != NULL) { - GstStructure *cpy; - cpy = gst_structure_copy (str); - - framenumber_v = gst_structure_get_value (str, "offset"); - if (framenumber_v) { - if (G_VALUE_TYPE (framenumber_v) == G_TYPE_UINT64) - framenumber = g_value_get_uint64 (framenumber_v); - else if (G_VALUE_TYPE (framenumber_v) == G_TYPE_INT64) - framenumber = g_value_get_int64 (framenumber_v); - } - - if (framenumber == G_MAXUINT64) - framenumber = mc->nextoffset++; - - if (mc->measurements->len <= framenumber) - g_ptr_array_set_size (mc->measurements, framenumber + 1); - g_ptr_array_index (mc->measurements, framenumber) = cpy; - - mc->nextoffset = framenumber + 1; - - if (!mc->metric) - mc->metric = g_strdup (metric); - } -} - -static void -gst_measure_collector_post_message (GstMeasureCollector * mc) -{ - GstMessage *m; - guint64 i; - - g_return_if_fail (mc->metric); - - if (strcmp (mc->metric, "SSIM") == 0) { - gfloat dresult = 0; - guint64 mlen; - g_free (mc->result); - mc->result = g_new0 (GValue, 1); - g_value_init (mc->result, G_TYPE_FLOAT); - mlen = mc->measurements->len; - for (i = 0; i < mc->measurements->len; i++) { - const GValue *v; - GstStructure *str = - (GstStructure *) g_ptr_array_index (mc->measurements, i); - if (str) { - v = gst_structure_get_value (str, "mean"); - dresult += g_value_get_float (v); - } else { - GST_WARNING_OBJECT (mc, - "No measurement info for frame %" G_GUINT64_FORMAT, i); - mlen--; - } - } - g_value_set_float (mc->result, dresult / mlen); - } - - m = gst_message_new_element (GST_OBJECT_CAST (mc), - gst_structure_new ("GstMeasureCollector", - "measure-result", G_TYPE_VALUE, mc->result, NULL)); - - gst_element_post_message (GST_ELEMENT_CAST (mc), m); -} - -static void -gst_measure_collector_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstMeasureCollector *measurecollector; - - measurecollector = GST_MEASURE_COLLECTOR (object); - - switch (prop_id) { - case PROP_FLAGS: - measurecollector->flags = g_value_get_uint64 (value); - break; - case PROP_FILENAME: - measurecollector->filename = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_measure_collector_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstMeasureCollector *measurecollector; - - measurecollector = GST_MEASURE_COLLECTOR (object); - - switch (prop_id) { - case PROP_FLAGS: - g_value_set_uint64 (value, measurecollector->flags); - break; - case PROP_FILENAME: - g_value_set_string (value, measurecollector->filename); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gboolean -gst_measure_collector_event (GstBaseTransform * base, GstEvent * event) -{ - GstMeasureCollector *mc = GST_MEASURE_COLLECTOR (base); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_CUSTOM_DOWNSTREAM: - if (gst_event_has_name (event, GST_EVENT_VIDEO_MEASURE)) - gst_measure_collector_collect (mc, event); - break; - case GST_EVENT_EOS: - gst_measure_collector_post_message (mc); - gst_measure_collector_save_csv (mc); - break; - default: - break; - } - - return parent_class->event (base, event); -} - -static void -gst_measure_collector_save_csv (GstMeasureCollector * mc) -{ - gchar *name_local; - FILE *file; - guint64 i, j; - GstStructure *str; - GValue tmp = { 0 }; - g_value_init (&tmp, G_TYPE_STRING); - - if (!(mc->flags & GST_MEASURE_COLLECTOR_WRITE_CSV)) - return; - - if (mc->measurements->len <= 0) - goto empty; - - /* open the file */ - if (mc->filename == NULL || mc->filename[0] == '\0') - goto no_filename; - - name_local = g_filename_from_utf8 ((const gchar *) mc->filename, - -1, NULL, NULL, NULL); - - /* open the file */ - if (name_local == NULL || name_local[0] == '\0') - goto not_good_filename; - - - /* FIXME, can we use g_fopen here? some people say that the FILE object is - * local to the .so that performed the fopen call, which would not be us when - * we use g_fopen. */ - file = fopen (name_local, "wb"); - - g_free (name_local); - - if (file == NULL) - goto open_failed; - - str = (GstStructure *) g_ptr_array_index (mc->measurements, 0); - - for (j = 0; j < gst_structure_n_fields (str); j++) { - const gchar *fieldname; - fieldname = gst_structure_nth_field_name (str, j); - if (G_LIKELY (j > 0)) - fprintf (file, ";"); - fprintf (file, "%s", fieldname); - } - - for (i = 0; i < mc->measurements->len; i++) { - fprintf (file, "\n"); - str = (GstStructure *) g_ptr_array_index (mc->measurements, i); - if (str != NULL) { - for (j = 0; j < gst_structure_n_fields (str); j++) { - const gchar *fieldname; - fieldname = gst_structure_nth_field_name (str, j); - if (G_LIKELY (j > 0)) - fprintf (file, ";"); - if (G_LIKELY (g_value_transform (gst_structure_get_value (str, - fieldname), &tmp))) - fprintf (file, "%s", g_value_get_string (&tmp)); - else - fprintf (file, ""); - } - } - } - - fclose (file); - - /* ERRORS */ -empty: - { - return; - } -no_filename: - { - GST_ELEMENT_ERROR (mc, RESOURCE, NOT_FOUND, - (_("No file name specified for writing.")), (NULL)); - return; - } -not_good_filename: - { - GST_ELEMENT_ERROR (mc, RESOURCE, NOT_FOUND, - (_("Given file name \"%s\" can't be converted to local file name \ -encoding."), mc->filename), (NULL)); - return; - } -open_failed: - { - GST_ELEMENT_ERROR (mc, RESOURCE, OPEN_WRITE, - (_("Could not open file \"%s\" for writing."), mc->filename), - GST_ERROR_SYSTEM); - return; - } -} - -static void -gst_measure_collector_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gst_element_class_set_static_metadata (element_class, - "Video measure collector", "Filter/Effect/Video", - "Collect measurements from a measuring element", - "Руслан Ижбулатов "); - - gst_element_class_add_static_pad_template (element_class, - &gst_measure_collector_sink_template); - gst_element_class_add_static_pad_template (element_class, - &gst_measure_collector_src_template); -} - -static void -gst_measure_collector_class_init (GstMeasureCollectorClass * klass) -{ - GObjectClass *gobject_class; - GstBaseTransformClass *trans_class; - - gobject_class = G_OBJECT_CLASS (klass); - trans_class = GST_BASE_TRANSFORM_CLASS (klass); - - GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "measurecollect", 0, - "measurement collector"); - - gobject_class->set_property = gst_measure_collector_set_property; - gobject_class->get_property = gst_measure_collector_get_property; - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_measure_collector_finalize); - - g_object_class_install_property (gobject_class, PROP_FLAGS, - g_param_spec_uint64 ("flags", "Flags", - "Flags that control the operation of the element", - 0, G_MAXUINT64, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_FILENAME, - g_param_spec_string ("filename", "Output file name", - "A name of a file into which element will write the measurement" - " information", "", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - - trans_class->event = GST_DEBUG_FUNCPTR (gst_measure_collector_event); - - trans_class->passthrough_on_same_caps = TRUE; - -} - -static void -gst_measure_collector_init (GstMeasureCollector * instance, - GstMeasureCollectorClass * g_class) -{ - GstMeasureCollector *measurecollector; - - measurecollector = GST_MEASURE_COLLECTOR (instance); - - GST_DEBUG_OBJECT (measurecollector, "gst_measure_collector_init"); - - gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (measurecollector), - FALSE); - - measurecollector->measurements = g_ptr_array_new (); - measurecollector->metric = NULL; - measurecollector->inited = TRUE; - measurecollector->filename = NULL; - measurecollector->flags = 0; - measurecollector->nextoffset = 0; - measurecollector->result = NULL; -} - -static void -gst_measure_collector_finalize (GObject * object) -{ - gint i; - GstMeasureCollector *mc = GST_MEASURE_COLLECTOR (object); - - for (i = 0; i < mc->measurements->len; i++) { - if (g_ptr_array_index (mc->measurements, i) != NULL) - gst_structure_free ((GstStructure *) g_ptr_array_index (mc->measurements, - i)); - } - - g_ptr_array_free (mc->measurements, TRUE); - mc->measurements = NULL; - - g_free (mc->result); - mc->result = NULL; - - g_free (mc->metric); - mc->metric = NULL; - - g_free (mc->filename); - mc->filename = NULL; - - G_OBJECT_CLASS (parent_class)->finalize (object); -} diff --git a/gst/videomeasure/gstvideomeasure_collector.h b/gst/videomeasure/gstvideomeasure_collector.h deleted file mode 100644 index 49a4e9318d..0000000000 --- a/gst/videomeasure/gstvideomeasure_collector.h +++ /dev/null @@ -1,80 +0,0 @@ -/* GStreamer - * Copyright (C) <2009> Руслан Ижбулатов - * - * This library 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 2.1 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#ifndef __GST_MEASURE_COLLECTOR_H__ -#define __GST_MEASURE_COLLECTOR_H__ - -#include "gstvideomeasure.h" -#include - -G_BEGIN_DECLS - -typedef struct _GstMeasureCollector GstMeasureCollector; -typedef struct _GstMeasureCollectorClass GstMeasureCollectorClass; - -#define GST_TYPE_MEASURE_COLLECTOR (gst_measure_collector_get_type()) -#define GST_MEASURE_COLLECTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MEASURE_COLLECTOR, \ - GstMeasureCollector)) -#define GST_IS_MEASURE_COLLECTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_MEASURE_COLLECTOR)) -#define GST_MEASURE_COLLECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\ - GST_TYPE_MEASURE_COLLECTOR, GstMeasureCollectorClass)) -#define GST_IS_MEASURE_COLLECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\ - GST_TYPE_MEASURE_COLLECTOR)) -#define GST_MEASURE_COLLECTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),\ - GST_TYPE_MEASURE_COLLECTOR, GstMeasureCollectorClass)) - -typedef enum { - GST_MEASURE_COLLECTOR_0 = 0, - GST_MEASURE_COLLECTOR_WRITE_CSV = 0x1, - GST_MEASURE_COLLECTOR_EMIT_MESSAGE = 0x1 << 1, - GST_MEASURE_COLLECTOR_ALL = - GST_MEASURE_COLLECTOR_WRITE_CSV | - GST_MEASURE_COLLECTOR_EMIT_MESSAGE -} GstMeasureCollectorFlags; - -struct _GstMeasureCollector { - GstBaseTransform element; - - guint64 flags; - - gchar *filename; - - /* Array of pointers to GstStructure */ - GPtrArray *measurements; - - GValue *result; - - guint64 nextoffset; - - gchar *metric; - - gboolean inited; -}; - -struct _GstMeasureCollectorClass { - GstBaseTransformClass parent_class; -}; - -GType gst_measure_collector_get_type (void); - -G_END_DECLS - -#endif /* __GST_MEASURE_COLLECTOR_H__ */ diff --git a/gst/videomeasure/gstvideomeasure_ssim.c b/gst/videomeasure/gstvideomeasure_ssim.c deleted file mode 100644 index d0a2c9d66f..0000000000 --- a/gst/videomeasure/gstvideomeasure_ssim.c +++ /dev/null @@ -1,1680 +0,0 @@ -/* GStreamer - * Copyright (C) <2009> Руслан Ижбулатов - * - * This library 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 2.1 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -/** - * SECTION:element-ssim - * - * The ssim calculates SSIM (Structural SIMilarity) index for two or more - * streams, for each frame. - * First stream is the original, other streams are modified (compressed) ones. - * ssim will calculate SSIM index of each frame of each modified stream, using - * original stream as a reference. - * - * The ssim accepts only YUV planar top-first data and calculates only Y-SSIM. - * All streams must have the same width, height and colorspace. - * Output streams are greyscale video streams, where bright pixels indicate - * high SSIM values, dark pixels - low SSIM values. - * The ssim also calculates mean SSIM index for each frame and emits is as a - * message. - * ssim is intended to be used with videomeasure_collector element to catch the - * events (such as mean SSIM index values) and save them into a file. - * - * - * Example launch line - * |[ - * gst-launch ssim name=ssim ssim.src0 ! videoconvert ! glimagesink filesrc - * location=orig.avi ! decodebin ! ssim.original filesrc location=compr.avi ! - * decodebin ! ssim.modified0 - * ]| This pipeline produces a video stream that consists of SSIM frames. - * - */ -/* Element-Checklist-Version: 5 */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstvideomeasure.h" -#include "gstvideomeasure_ssim.h" -#include -#include -#include -#include - -#define GST_CAT_DEFAULT gst_ssim_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); - -/* elementfactory information */ - -#define SINK_CAPS \ - "video/x-raw-yuv, " \ - "format = (fourcc) { I420, YV12, Y41B, Y42B } " - - -#define SRC_CAPS \ - "video/x-raw-gray, " \ - "width = (int) [ 1, MAX ], " \ - "height = (int) [ 1, MAX ], " \ - "framerate = (fraction) [ 0/1, MAX ], " \ - "bpp = (int) 8, " \ - "depth = (int) 8 " - -static GstStaticPadTemplate gst_ssim_src_template = -GST_STATIC_PAD_TEMPLATE ("src_%u", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS (SRC_CAPS) - ); - -static GstStaticPadTemplate gst_ssim_sink_original_template = -GST_STATIC_PAD_TEMPLATE ("original", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS (SINK_CAPS) - ); - -static GstStaticPadTemplate gst_ssim_sink_modified_template = -GST_STATIC_PAD_TEMPLATE ("modified_%u", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS (SINK_CAPS) - ); - -static void gst_ssim_class_init (GstSSimClass * klass); -static void gst_ssim_init (GstSSim * ssim); -static void gst_ssim_finalize (GObject * object); - -static gboolean gst_ssim_setcaps (GstPad * pad, GstCaps * caps); -static gboolean gst_ssim_query (GstPad * pad, GstQuery * query); -static gboolean gst_ssim_src_event (GstPad * pad, GstEvent * event); -static gboolean gst_ssim_sink_event (GstPad * pad, GstEvent * event); - -static GstPad *gst_ssim_request_new_pad (GstElement * element, - GstPadTemplate * temp, const gchar * unused); -static void gst_ssim_release_pad (GstElement * element, GstPad * pad); - -static GstStateChangeReturn gst_ssim_change_state (GstElement * element, - GstStateChange transition); - -static GstFlowReturn gst_ssim_collected (GstCollectPads * pads, - gpointer user_data); - -static GstElementClass *parent_class = NULL; - -GType -gst_ssim_get_type (void) -{ - static GType ssim_type = 0; - - if (G_UNLIKELY (ssim_type == 0)) { - static const GTypeInfo ssim_info = { - sizeof (GstSSimClass), NULL, NULL, - (GClassInitFunc) gst_ssim_class_init, NULL, NULL, - sizeof (GstSSim), 0, - (GInstanceInitFunc) gst_ssim_init, - }; - - ssim_type = g_type_register_static (GST_TYPE_ELEMENT, "GstSSim", - &ssim_info, 0); - GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "ssim", 0, "SSIM calculator"); - } - return ssim_type; -} - -static void -gst_ssim_post_message (GstSSim * ssim, GstBuffer * buffer, gfloat mssim, - gfloat lowest, gfloat highest) -{ - GstMessage *m; - guint64 offset; - - offset = GST_BUFFER_OFFSET (buffer); - - m = gst_message_new_element (GST_OBJECT_CAST (ssim), - gst_structure_new ("SSIM", - "offset", G_TYPE_UINT64, offset, - "timestamp", GST_TYPE_CLOCK_TIME, GST_BUFFER_TIMESTAMP (buffer), - "mean", G_TYPE_FLOAT, mssim, - "lowest", G_TYPE_FLOAT, lowest, - "highest", G_TYPE_FLOAT, highest, NULL)); - - GST_DEBUG_OBJECT (GST_OBJECT (ssim), "Frame %" G_GINT64_FORMAT - " @ %" GST_TIME_FORMAT " mean SSIM is %f, l-h is %f-%f", offset, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), mssim, lowest, highest); - - gst_element_post_message (GST_ELEMENT_CAST (ssim), m); -} - -static GstCaps * -gst_ssim_src_getcaps (GstPad * pad) -{ - GstCaps *result; - gchar *capstr; - - result = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); - capstr = gst_caps_to_string (result); - GST_DEBUG ("getsrccaps - return static caps: %s", capstr); - g_free (capstr); - return result; -} - -static GstCaps * -gst_ssim_sink_getcaps (GstPad * pad) -{ - GstCaps *result = NULL; - GstSSim *ssim; - gchar *capstr; - - ssim = GST_SSIM (GST_PAD_PARENT (pad)); - - GST_OBJECT_LOCK (ssim); - - result = gst_pad_get_fixed_caps_func (pad); - capstr = gst_caps_to_string (result); - GST_DEBUG ("getsinkcaps - return caps: %s", capstr); - g_free (capstr); - - GST_OBJECT_UNLOCK (ssim); - - return result; -} - -static void -calculate_mu (GstSSim * ssim, gfloat * outmu, guint8 * buf) -{ - gint oy, ox, iy, ix; - - for (oy = 0; oy < ssim->height; oy++) { - for (ox = 0; ox < ssim->width; ox++) { - gfloat mu = 0; - gfloat elsumm; - gint weight_y_base, weight_x_base; - gint weight_offset; - gint pixel_offset; - gint winstart_y; - gint wghstart_y; - gint winend_y; - gint winstart_x; - gint wghstart_x; - gint winend_x; - gfloat weight; - gint source_offset; - - source_offset = oy * ssim->width + ox; - - winstart_x = ssim->windows[source_offset].x_window_start; - wghstart_x = ssim->windows[source_offset].x_weight_start; - winend_x = ssim->windows[source_offset].x_window_end; - winstart_y = ssim->windows[source_offset].y_window_start; - wghstart_y = ssim->windows[source_offset].y_weight_start; - winend_y = ssim->windows[source_offset].y_window_end; - elsumm = ssim->windows[source_offset].element_summ; - - switch (ssim->windowtype) { - case 0: - for (iy = winstart_y; iy <= winend_y; iy++) { - pixel_offset = iy * ssim->width; - for (ix = winstart_x; ix <= winend_x; ix++) - mu += buf[pixel_offset + ix]; - } - mu = mu / elsumm; - break; - case 1: - - weight_y_base = wghstart_y - winstart_y; - weight_x_base = wghstart_x - winstart_x; - - for (iy = winstart_y; iy <= winend_y; iy++) { - pixel_offset = iy * ssim->width; - weight_offset = (weight_y_base + iy) * ssim->windowsize + - weight_x_base; - for (ix = winstart_x; ix <= winend_x; ix++) { - weight = ssim->weights[weight_offset + ix]; - mu += weight * buf[pixel_offset + ix]; - } - } - mu = mu / elsumm; - break; - } - outmu[oy * ssim->width + ox] = mu; - } - } - -} - -static void -calcssim_without_mu (GstSSim * ssim, guint8 * org, gfloat * orgmu, guint8 * mod, - guint8 * out, gfloat * mean, gfloat * lowest, gfloat * highest) -{ - gint oy, ox, iy, ix; - gfloat cumulative_ssim = 0; - *lowest = G_MAXFLOAT; - *highest = -G_MAXFLOAT; - - for (oy = 0; oy < ssim->height; oy++) { - for (ox = 0; ox < ssim->width; ox++) { - gfloat mu_o = 128, mu_m = 128; - gdouble sigma_o = 0, sigma_m = 0, sigma_om = 0; - gfloat tmp1 = 0, tmp2 = 0; - gfloat elsumm = 0; - gint weight_y_base, weight_x_base; - gint weight_offset; - gint pixel_offset; - gint winstart_y; - gint wghstart_y; - gint winend_y; - gint winstart_x; - gint wghstart_x; - gint winend_x; - gfloat weight; - gint source_offset; - - source_offset = oy * ssim->width + ox; - - winstart_x = ssim->windows[source_offset].x_window_start; - wghstart_x = ssim->windows[source_offset].x_weight_start; - winend_x = ssim->windows[source_offset].x_window_end; - winstart_y = ssim->windows[source_offset].y_window_start; - wghstart_y = ssim->windows[source_offset].y_weight_start; - winend_y = ssim->windows[source_offset].y_window_end; - elsumm = ssim->windows[source_offset].element_summ; - - weight_y_base = wghstart_y - winstart_y; - weight_x_base = wghstart_x - winstart_x; - switch (ssim->windowtype) { - case 0: - for (iy = winstart_y; iy <= winend_y; iy++) { - guint8 *org_with_offset, *mod_with_offset; - pixel_offset = iy * ssim->width; - org_with_offset = &org[pixel_offset]; - mod_with_offset = &mod[pixel_offset]; - for (ix = winstart_x; ix <= winend_x; ix++) { - tmp1 = org_with_offset[ix] - mu_o; - sigma_o += tmp1 * tmp1; - tmp2 = mod_with_offset[ix] - mu_m; - sigma_m += tmp2 * tmp2; - sigma_om += tmp1 * tmp2; - } - } - break; - case 1: - - weight_y_base = wghstart_y - winstart_y; - weight_x_base = wghstart_x - winstart_x; - - for (iy = winstart_y; iy <= winend_y; iy++) { - guint8 *org_with_offset, *mod_with_offset; - gfloat *weights_with_offset; - gfloat wt1, wt2; - pixel_offset = iy * ssim->width; - weight_offset = (weight_y_base + iy) * ssim->windowsize + - weight_x_base; - org_with_offset = &org[pixel_offset]; - mod_with_offset = &mod[pixel_offset]; - weights_with_offset = &ssim->weights[weight_offset]; - for (ix = winstart_x; ix <= winend_x; ix++) { - weight = weights_with_offset[ix]; - tmp1 = org_with_offset[ix] - mu_o; - tmp2 = mod_with_offset[ix] - mu_m; - wt1 = weight * tmp1; - wt2 = weight * tmp2; - sigma_o += wt1 * tmp1; - sigma_m += wt2 * tmp2; - sigma_om += wt1 * tmp2; - } - } - break; - } - sigma_o = sqrt (sigma_o / elsumm); - sigma_m = sqrt (sigma_m / elsumm); - sigma_om = sigma_om / elsumm; - tmp1 = (2 * mu_o * mu_m + ssim->const1) * (2 * sigma_om + ssim->const2) / - ((mu_o * mu_o + mu_m * mu_m + ssim->const1) * - (sigma_o * sigma_o + sigma_m * sigma_m + ssim->const2)); - - /* SSIM can go negative, that's why it is - 127 + index * 128 instead of index * 255 */ - out[oy * ssim->width + ox] = 127 + tmp1 * 128; - *lowest = MIN (*lowest, tmp1); - *highest = MAX (*highest, tmp1); - cumulative_ssim += tmp1; - } - } - *mean = cumulative_ssim / (ssim->width * ssim->height); -} - -static void -calcssim_canonical (GstSSim * ssim, guint8 * org, gfloat * orgmu, guint8 * mod, - guint8 * out, gfloat * mean, gfloat * lowest, gfloat * highest) -{ - gint oy, ox, iy, ix; - gfloat cumulative_ssim = 0; - *lowest = G_MAXFLOAT; - *highest = -G_MAXFLOAT; - - for (oy = 0; oy < ssim->height; oy++) { - for (ox = 0; ox < ssim->width; ox++) { - gfloat mu_o = 0, mu_m = 0; - gdouble sigma_o = 0, sigma_m = 0, sigma_om = 0; - gfloat tmp1, tmp2; - gfloat elsumm = 0; - gint weight_y_base, weight_x_base; - gint weight_offset; - gint pixel_offset; - gint winstart_y; - gint wghstart_y; - gint winend_y; - gint winstart_x; - gint wghstart_x; - gint winend_x; - gfloat weight; - gint source_offset; - - source_offset = oy * ssim->width + ox; - - winstart_x = ssim->windows[source_offset].x_window_start; - wghstart_x = ssim->windows[source_offset].x_weight_start; - winend_x = ssim->windows[source_offset].x_window_end; - winstart_y = ssim->windows[source_offset].y_window_start; - wghstart_y = ssim->windows[source_offset].y_weight_start; - winend_y = ssim->windows[source_offset].y_window_end; - elsumm = ssim->windows[source_offset].element_summ; - - switch (ssim->windowtype) { - case 0: - for (iy = winstart_y; iy <= winend_y; iy++) { - pixel_offset = iy * ssim->width; - for (ix = winstart_x; ix <= winend_x; ix++) { - mu_m += mod[pixel_offset + ix]; - } - } - mu_m = mu_m / elsumm; - mu_o = orgmu[oy * ssim->width + ox]; - for (iy = winstart_y; iy <= winend_y; iy++) { - pixel_offset = iy * ssim->width; - for (ix = winstart_x; ix <= winend_x; ix++) { - tmp1 = org[pixel_offset + ix] - mu_o; - tmp2 = mod[pixel_offset + ix] - mu_m; - sigma_o += tmp1 * tmp1; - sigma_m += tmp2 * tmp2; - sigma_om += tmp1 * tmp2; - } - } - break; - case 1: - - weight_y_base = wghstart_y - winstart_y; - weight_x_base = wghstart_x - winstart_x; - - for (iy = winstart_y; iy <= winend_y; iy++) { - pixel_offset = iy * ssim->width; - weight_offset = (weight_y_base + iy) * ssim->windowsize + - weight_x_base; - for (ix = winstart_x; ix <= winend_x; ix++) { - weight = ssim->weights[weight_offset + ix]; - mu_o += weight * org[pixel_offset + ix]; - mu_m += weight * mod[pixel_offset + ix]; - } - } - mu_m = mu_m / elsumm; - mu_o = orgmu[oy * ssim->width + ox]; - for (iy = winstart_y; iy <= winend_y; iy++) { - gfloat *weights_with_offset; - guint8 *org_with_offset, *mod_with_offset; - gfloat wt1, wt2; - pixel_offset = iy * ssim->width; - weight_offset = (weight_y_base + iy) * ssim->windowsize + - weight_x_base; - weights_with_offset = &ssim->weights[weight_offset]; - org_with_offset = &org[pixel_offset]; - mod_with_offset = &mod[pixel_offset]; - for (ix = winstart_x; ix <= winend_x; ix++) { - weight = weights_with_offset[ix]; - tmp1 = org_with_offset[ix] - mu_o; - tmp2 = mod_with_offset[ix] - mu_m; - wt1 = weight * tmp1; - wt2 = weight * tmp2; - sigma_o += wt1 * tmp1; - sigma_m += wt2 * tmp2; - sigma_om += wt1 * tmp2; - } - } - break; - } - sigma_o = sqrt (sigma_o / elsumm); - sigma_m = sqrt (sigma_m / elsumm); - sigma_om = sigma_om / elsumm; - tmp1 = (2 * mu_o * mu_m + ssim->const1) * (2 * sigma_om + ssim->const2) / - ((mu_o * mu_o + mu_m * mu_m + ssim->const1) * - (sigma_o * sigma_o + sigma_m * sigma_m + ssim->const2)); - - /* SSIM can go negative, that's why it is - 127 + index * 128 instead of index * 255 */ - out[oy * ssim->width + ox] = 127 + tmp1 * 128; - *lowest = MIN (*lowest, tmp1); - *highest = MAX (*highest, tmp1); - cumulative_ssim += tmp1; - } - } - *mean = cumulative_ssim / (ssim->width * ssim->height); -} - - -/* the first caps we receive on any of the sinkpads will define the caps for all - * the other sinkpads because we can only measure streams with the same caps. - */ -static gboolean -gst_ssim_setcaps (GstPad * pad, GstCaps * caps) -{ - GstSSim *ssim; - GList *pads; - const char *media_type; - GstStructure *capsstr; - gint width, height, fps_n, fps_d; - guint32 fourcc; - - ssim = GST_SSIM (GST_PAD_PARENT (pad)); - - GST_DEBUG_OBJECT (ssim, "setting caps on pad %p,%s to %" GST_PTR_FORMAT, pad, - GST_PAD_NAME (pad), caps); - - capsstr = gst_caps_get_structure (caps, 0); - gst_structure_get_int (capsstr, "width", &width); - gst_structure_get_int (capsstr, "height", &height); - gst_structure_get_fraction (capsstr, "framerate", &fps_n, &fps_d); - gst_structure_get_fourcc (capsstr, "format", &fourcc); - - GST_OBJECT_LOCK (ssim); - - /* Sink caps are stored only once. At the moment it doesn't feel - * right to measure streams with variable caps. - */ - if (G_UNLIKELY (!ssim->sinkcaps)) { - GstStructure *newstr; - GValue list = { 0, } - , fourcc = { - 0,}; - - g_value_init (&list, GST_TYPE_LIST); - g_value_init (&fourcc, GST_TYPE_FOURCC); - - gst_value_set_fourcc (&fourcc, GST_MAKE_FOURCC ('I', '4', '2', '0')); - gst_value_list_append_value (&list, &fourcc); - gst_value_set_fourcc (&fourcc, GST_MAKE_FOURCC ('Y', 'V', '1', '2')); - gst_value_list_append_value (&list, &fourcc); - gst_value_set_fourcc (&fourcc, GST_MAKE_FOURCC ('Y', '4', '1', 'B')); - gst_value_list_append_value (&list, &fourcc); - gst_value_set_fourcc (&fourcc, GST_MAKE_FOURCC ('Y', '4', '2', 'B')); - gst_value_list_append_value (&list, &fourcc); - - newstr = gst_structure_new ("video/x-raw-yuv", NULL); - gst_structure_set (newstr, "width", G_TYPE_INT, width, NULL); - gst_structure_set (newstr, "height", G_TYPE_INT, height, NULL); - gst_structure_set_value (newstr, "format", &list); - - ssim->sinkcaps = gst_caps_new_full (newstr, NULL); - - g_value_unset (&list); - g_value_unset (&fourcc); - } - - if (G_UNLIKELY (!ssim->srccaps)) { - GstStructure *newstr; - - newstr = gst_structure_new ("video/x-raw-gray", NULL); - gst_structure_set (newstr, "width", G_TYPE_INT, width, NULL); - gst_structure_set (newstr, "height", G_TYPE_INT, height, NULL); - gst_structure_set (newstr, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, - NULL); - /* Calculates SSIM only for Y channel, hence the output is monochrome. - * TODO: an option (a mask?) to calculate SSIM for more than one channel, - * will probably output RGB, one metric per channel...that would - * look kinda funny :) - */ - gst_structure_set (newstr, "bpp", G_TYPE_INT, 8, "depth", G_TYPE_INT, 8, - NULL); - - ssim->srccaps = gst_caps_new_full (newstr, NULL); - } - - pads = GST_ELEMENT (ssim)->pads; - while (pads) { - GstPadDirection direction; - GstPad *otherpad = GST_PAD (pads->data); - direction = gst_pad_get_direction (otherpad); - - GST_DEBUG_OBJECT (ssim, "checking caps on pad %p", otherpad); - if (direction == GST_PAD_SINK) { - gchar *capstr; - capstr = gst_caps_to_string (GST_PAD_CAPS (otherpad)); - GST_DEBUG_OBJECT (ssim, "old caps on pad %p,%s were %s", otherpad, - GST_PAD_NAME (otherpad), capstr); - g_free (capstr); - gst_caps_replace (&GST_PAD_CAPS (otherpad), ssim->sinkcaps); - capstr = gst_caps_to_string (ssim->sinkcaps); - GST_DEBUG_OBJECT (ssim, "new caps on pad %p,%s are %s", otherpad, - GST_PAD_NAME (otherpad), capstr); - g_free (capstr); - } else if (direction == GST_PAD_SRC) { - gst_caps_replace (&GST_PAD_CAPS (otherpad), ssim->srccaps); - } - pads = g_list_next (pads); - } - - /* parse caps now */ - media_type = gst_structure_get_name (capsstr); - GST_DEBUG_OBJECT (ssim, "media type is %s", media_type); - if (strcmp (media_type, "video/x-raw-yuv") == 0) { - ssim->width = width; - ssim->height = height; - ssim->frame_rate = fps_n; - ssim->frame_rate_base = fps_d; - - GST_INFO_OBJECT (ssim, "parse_caps sets ssim to yuv format " - "%d, %dx%d, %d/%d fps", fourcc, ssim->width, ssim->height, - ssim->frame_rate, ssim->frame_rate_base); - - /* Only planar formats are supported. - * TODO: implement support for interleaved formats - * Only YUV formats are supported. There's no sense in calculating the - * index for R, G or B channels separately. - */ - switch (fourcc) { - case GST_MAKE_FOURCC ('I', '4', '2', '0'): - case GST_MAKE_FOURCC ('Y', 'V', '1', '2'): - case GST_MAKE_FOURCC ('Y', '4', '1', 'B'): - case GST_MAKE_FOURCC ('Y', '4', '2', 'B'): - break; - default: - goto not_supported; - } - - } else { - goto not_supported; - } - - GST_OBJECT_UNLOCK (ssim); - return TRUE; - /* ERRORS */ -not_supported: - { - GST_OBJECT_UNLOCK (ssim); - GST_DEBUG_OBJECT (ssim, "unsupported format set as caps"); - return FALSE; - } -} - -static gboolean -gst_ssim_query_latency (GstSSim * ssim, GstQuery * query) -{ - GstClockTime min, max; - gboolean live; - gboolean res; - GstIterator *it; - gboolean done; - - res = TRUE; - done = FALSE; - - live = FALSE; - min = 0; - max = GST_CLOCK_TIME_NONE; - - /* Take maximum of all latency values */ - it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (ssim)); - while (!done) { - GstIteratorResult ires; - - gpointer item; - - ires = gst_iterator_next (it, &item); - switch (ires) { - case GST_ITERATOR_DONE: - done = TRUE; - break; - case GST_ITERATOR_OK: - { - GstPad *pad = GST_PAD_CAST (item); - GstQuery *peerquery; - GstClockTime min_cur, max_cur; - gboolean live_cur; - - peerquery = gst_query_new_latency (); - - /* Ask peer for latency */ - res &= gst_pad_peer_query (pad, peerquery); - - /* take max from all valid return values */ - if (res) { - gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur); - - if (live_cur) { - if (min_cur > min) - min = min_cur; - - if (max == GST_CLOCK_TIME_NONE) - max = max_cur; - else if (max_cur < max) - max = max_cur; - - live = TRUE; - } - } - - gst_query_unref (peerquery); - gst_object_unref (pad); - break; - } - case GST_ITERATOR_RESYNC: - live = FALSE; - min = 0; - max = GST_CLOCK_TIME_NONE; - res = TRUE; - gst_iterator_resync (it); - break; - default: - res = FALSE; - done = TRUE; - break; - } - } - gst_iterator_free (it); - - if (res) { - /* store the results */ - GST_DEBUG_OBJECT (ssim, "Calculated total latency: live %s, min %" - GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, - (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max)); - gst_query_set_latency (query, live, min, max); - } - - return res; -} - -static gboolean -gst_ssim_query_duration (GstSSim * ssim, GstQuery * query) -{ - gint64 max, min; - gboolean res; - GstFormat format; - GstIterator *it; - gboolean done; - - /* parse format */ - gst_query_parse_duration (query, &format, NULL); - - max = -1; - min = G_MAXINT64; - res = TRUE; - done = FALSE; - - it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (ssim)); - while (!done) { - GstIteratorResult ires; - - gpointer item; - - ires = gst_iterator_next (it, &item); - switch (ires) { - case GST_ITERATOR_DONE: - done = TRUE; - break; - case GST_ITERATOR_OK: - { - GstPad *pad = GST_PAD_CAST (item); - - gint64 duration; - - /* ask sink peer for duration */ - res &= gst_pad_query_peer_duration (pad, &format, &duration); - /* take min&max from all valid return values */ - if (res) { - /* valid unknown length, stop searching */ - if (duration == -1) { - max = duration; - done = TRUE; - } - /* else see if bigger than current max */ - else { - if (duration > max) - max = duration; - if (duration < min) - min = duration; - } - } - gst_object_unref (pad); - break; - } - case GST_ITERATOR_RESYNC: - max = -1; - min = G_MAXINT64; - res = TRUE; - gst_iterator_resync (it); - break; - default: - res = FALSE; - done = TRUE; - break; - } - } - gst_iterator_free (it); - - if (res) { - /* and store the max */ - GST_DEBUG_OBJECT (ssim, "Total duration in format %s: %" - GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (min)); - gst_query_set_duration (query, format, min); - } - - return res; -} - - -static gboolean -gst_ssim_query (GstPad * pad, GstQuery * query) -{ - GstSSim *ssim = GST_SSIM (gst_pad_get_parent (pad)); - gboolean res = FALSE; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - GstFormat format; - - gst_query_parse_position (query, &format, NULL); - - switch (format) { - case GST_FORMAT_TIME: - /* FIXME, bring to stream time, might be tricky */ - gst_query_set_position (query, format, ssim->timestamp); - res = TRUE; - break; - case GST_FORMAT_DEFAULT: - gst_query_set_position (query, format, ssim->offset); - res = TRUE; - break; - default: - break; - } - break; - } - case GST_QUERY_DURATION: - res = gst_ssim_query_duration (ssim, query); - break; - case GST_QUERY_LATENCY: - res = gst_ssim_query_latency (ssim, query); - break; - default: - /* FIXME, needs a custom query handler because we have multiple - * sinkpads - */ - res = gst_pad_query_default (pad, query); - break; - } - - gst_object_unref (ssim); - return res; -} - -static gboolean -forward_event_func (GstPad * pad, GValue * ret, GstEvent * event) -{ - gst_event_ref (event); - GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event)); - if (!gst_pad_push_event (pad, event)) { - g_value_set_boolean (ret, FALSE); - GST_LOG_OBJECT (pad, "Sending event %p (%s) failed.", - event, GST_EVENT_TYPE_NAME (event)); - } else { - GST_LOG_OBJECT (pad, "Sent event %p (%s).", - event, GST_EVENT_TYPE_NAME (event)); - } - gst_object_unref (pad); - return TRUE; -} - -/* forwards the event to all sinkpads, takes ownership of the - * event - * - * Returns: TRUE if the event could be forwarded on all - * sinkpads. - */ -static gboolean -forward_event (GstSSim * ssim, GstEvent * event) -{ - GstIterator *it; - GValue vret = { 0 }; - - GST_LOG_OBJECT (ssim, "Forwarding event %p (%s)", event, - GST_EVENT_TYPE_NAME (event)); - - g_value_init (&vret, G_TYPE_BOOLEAN); - g_value_set_boolean (&vret, TRUE); - it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (ssim)); - gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret, - event); - gst_iterator_free (it); - gst_event_unref (event); - - return g_value_get_boolean (&vret); -} - -static gboolean -gst_ssim_src_event (GstPad * pad, GstEvent * event) -{ - GstSSim *ssim; - gboolean result; - - ssim = GST_SSIM (gst_pad_get_parent (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_QOS: - /* QoS might be tricky */ - result = FALSE; - break; - case GST_EVENT_SEEK: - { - GstSeekFlags flags; - GstSeekType curtype; - gint64 cur; - - /* parse the seek parameters */ - gst_event_parse_seek (event, &ssim->segment_rate, NULL, &flags, &curtype, - &cur, NULL, NULL); - - /* check if we are flushing */ - if (flags & GST_SEEK_FLAG_FLUSH) { - /* make sure we accept nothing anymore and return WRONG_STATE */ - gst_collect_pads_set_flushing (ssim->collect, TRUE); - - /* flushing seek, start flush downstream, the flush will be done - * when all pads received a FLUSH_STOP. */ - gst_pad_push_event (pad, gst_event_new_flush_start ()); - } - /* now wait for the collected to be finished and mark a new - * segment */ - GST_OBJECT_LOCK (ssim->collect); - if (curtype == GST_SEEK_TYPE_SET) - ssim->segment_position = cur; - else - ssim->segment_position = 0; - { - GstSSimOutputContext *c; - gint i = 0; - for (i = 0; i < ssim->src->len; i++) { - c = (GstSSimOutputContext *) g_ptr_array_index (ssim->src, i); - c->segment_pending = TRUE; - } - } - GST_OBJECT_UNLOCK (ssim->collect); - - result = forward_event (ssim, event); - break; - } - case GST_EVENT_NAVIGATION: - /* navigation is rather pointless. */ - result = FALSE; - break; - default: - /* just forward the rest for now */ - result = forward_event (ssim, event); - break; - } - gst_object_unref (ssim); - - return result; -} - -static gboolean -gst_ssim_sink_event (GstPad * pad, GstEvent * event) -{ - GstSSim *ssim; - gboolean ret; - - ssim = GST_SSIM (gst_pad_get_parent (pad)); - - GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event), - GST_DEBUG_PAD_NAME (pad)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT: - { - gboolean update; - gdouble rate; - gdouble applied_rate; - GstFormat format; - gint64 start; - gint64 stop; - gint64 position; - gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, - &format, &start, &stop, &position); - GST_DEBUG ("NEWSEGMENTEVENT: update(%d), rate(%f), app_rate(%f), " - "format(%d), start(%" GST_TIME_FORMAT ") stop(%" GST_TIME_FORMAT ") " - "position(%" GST_TIME_FORMAT ")", update, rate, applied_rate, format, - GST_TIME_ARGS (start), GST_TIME_ARGS (stop), - GST_TIME_ARGS (position)); - break; - } - case GST_EVENT_FLUSH_STOP: - /* mark a pending new segment. This event is synchronized - * with the streaming thread so we can safely update the - * variable without races. It's somewhat weird because we - * assume the collectpads forwarded the FLUSH_STOP past us - * and downstream (using our source pad, the bastard!). - */ - { - GstSSimOutputContext *c; - gint i = 0; - for (i = 0; i < ssim->src->len; i++) { - c = (GstSSimOutputContext *) g_ptr_array_index (ssim->src, i); - c->segment_pending = TRUE; - } - } - break; - default: - break; - } - - /* now GstCollectPads can take care of the rest, e.g. EOS */ - GST_DEBUG ("Dispatching %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event), - GST_DEBUG_PAD_NAME (pad)); - ret = ssim->collect_event (pad, event); - GST_DEBUG ("Event %s on pad %s:%s is dispatched", GST_EVENT_TYPE_NAME (event), - GST_DEBUG_PAD_NAME (pad)); - gst_object_unref (ssim); - return ret; -} - -static void -gst_ssim_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstSSim *ssim; - - ssim = GST_SSIM (object); - - switch (prop_id) { - case PROP_SSIM_TYPE: - ssim->ssimtype = g_value_get_int (value); - break; - case PROP_WINDOW_TYPE: - ssim->windowtype = g_value_get_int (value); - g_free (ssim->windows); - ssim->windows = NULL; - break; - case PROP_WINDOW_SIZE: - ssim->windowsize = g_value_get_int (value); - g_free (ssim->windows); - ssim->windows = NULL; - break; - case PROP_GAUSS_SIGMA: - ssim->sigma = g_value_get_float (value); - g_free (ssim->windows); - ssim->windows = NULL; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_ssim_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstSSim *ssim; - - ssim = GST_SSIM (object); - - switch (prop_id) { - case PROP_SSIM_TYPE: - g_value_set_int (value, ssim->ssimtype); - break; - case PROP_WINDOW_TYPE: - g_value_set_int (value, ssim->windowtype); - break; - case PROP_WINDOW_SIZE: - g_value_set_int (value, ssim->windowsize); - break; - case PROP_GAUSS_SIGMA: - g_value_set_float (value, ssim->sigma); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -static void -gst_ssim_class_init (GstSSimClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstElementClass *gstelement_class = (GstElementClass *) klass; - - gobject_class->set_property = gst_ssim_set_property; - gobject_class->get_property = gst_ssim_get_property; - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_ssim_finalize); - - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SSIM_TYPE, - g_param_spec_int ("ssim-type", "SSIM type", - "Type of the SSIM metric. 0 - canonical. 1 - with fixed mu " - "(almost the same results, but roughly 20% faster)", - 0, 1, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WINDOW_TYPE, - g_param_spec_int ("window-type", "Window type", - "Type of the weighting in the window. " - "0 - no weighting. 1 - Gaussian weighting (controlled by \"sigma\")", - 0, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WINDOW_SIZE, - g_param_spec_int ("window-size", "Window size", - "Size of a window.", 1, 22, 11, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_GAUSS_SIGMA, - g_param_spec_float ("gauss-sigma", "Deviation (for Gauss function)", - "Used to calculate Gussian weights " - "(only when using Gaussian window).", - G_MINFLOAT, 10, 1.5, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gst_element_class_add_static_pad_template (gstelement_class, - &gst_ssim_src_template); - gst_element_class_add_static_pad_template (gstelement_class, - &gst_ssim_sink_original_template); - gst_element_class_add_static_pad_template (gstelement_class, - &gst_ssim_sink_modified_template); - gst_element_class_set_static_metadata (gstelement_class, "SSim", - "Filter/Analyzer/Video", "Calculate Y-SSIM for n+2 YUV video streams", - "Руслан Ижбулатов "); - - parent_class = g_type_class_peek_parent (klass); - - gstelement_class->request_new_pad = - GST_DEBUG_FUNCPTR (gst_ssim_request_new_pad); - gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_ssim_release_pad); - - gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_ssim_change_state); -} - -static GstPad * -gst_ssim_request_new_pad (GstElement * element, GstPadTemplate * templ, - const gchar * padname) -{ - gchar *name; - GstSSim *ssim; - GstPad *newpad; - GstPad *newsrc; - gint padcount; - GstPadTemplate *template; - guint num = -1; - - if (templ->direction != GST_PAD_SINK) - goto not_sink; - - ssim = GST_SSIM (element); - - padcount = ssim->padcount; - - GST_DEBUG_OBJECT (ssim, "number of pads = %d", padcount); - - if (padname) - GST_DEBUG_OBJECT (ssim, "reqested pad %s", padname); - else - goto unnamed_pad; - - if (strcmp (padname, "original") == 0) { - newpad = gst_pad_new_from_template (templ, "original"); - GST_DEBUG_OBJECT (ssim, "request new sink pad original"); - ssim->orig = newpad; - } else if (strncmp (padname, "modified_", 9) == 0) { - const gchar *numstr = &padname[9]; - num = strtoul (numstr, NULL, 10); - if (errno == EINVAL || errno == ERANGE) - goto bad_name; - newpad = gst_pad_new_from_template (templ, padname); - GST_DEBUG_OBJECT (ssim, "request new sink pad %s", padname); - } else - goto bad_name; - - gst_pad_set_getcaps_function (newpad, - GST_DEBUG_FUNCPTR (gst_ssim_sink_getcaps)); - gst_pad_set_setcaps_function (newpad, GST_DEBUG_FUNCPTR (gst_ssim_setcaps)); - gst_collect_pads_add_pad (ssim->collect, newpad, sizeof (GstCollectData), - NULL, TRUE); - - /* FIXME: hacked way to override/extend the event function of - * GstCollectPads; because it sets its own event function giving the - * element no access to events - */ - GST_DEBUG_OBJECT (ssim, "Current collect_event is %p, changing to %p", - ssim->collect_event, GST_PAD_EVENTFUNC (newpad)); - ssim->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad); - gst_pad_set_event_function (newpad, GST_DEBUG_FUNCPTR (gst_ssim_sink_event)); - - GST_DEBUG_OBJECT (ssim, "Adding a pad..."); - /* takes ownership of the pad */ - if (!gst_element_add_pad (GST_ELEMENT (ssim), newpad)) - goto could_not_add_sink; - else - /* increment pad counter */ - padcount = g_atomic_int_add (&ssim->padcount, 1); - - if (num != -1) { - GstSSimOutputContext *c; - - template = gst_static_pad_template_get (&gst_ssim_src_template); - name = g_strdup_printf ("src_%u", num); - newsrc = gst_pad_new_from_template (template, name); - GST_DEBUG_OBJECT (ssim, "creating src pad %s", name); - g_free (name); - gst_object_unref (template); - - gst_pad_set_getcaps_function (newsrc, - GST_DEBUG_FUNCPTR (gst_ssim_src_getcaps)); - gst_pad_set_query_function (newsrc, GST_DEBUG_FUNCPTR (gst_ssim_query)); - gst_pad_set_event_function (newsrc, GST_DEBUG_FUNCPTR (gst_ssim_src_event)); - - if (!gst_element_add_pad (GST_ELEMENT (ssim), newsrc)) - goto could_not_add_src; - - c = g_new (GstSSimOutputContext, 1); - c->pad = newsrc; - g_object_set_data (G_OBJECT (newpad), "ssim-match-output-context", c); - g_ptr_array_add (ssim->src, (gpointer) c); - } - - return newpad; - - /* errors */ -bad_name: - { - g_warning ("gstssim: request new pad with bad name %s (must be " - "'modified')\n", padname); - return NULL; - } -unnamed_pad: - { - g_warning ("gstssim: request new pad without a name (must be " - "'modified')\n"); - return NULL; - } -not_sink: - { - g_warning ("gstssim: request new pad that is not a SINK pad\n"); - return NULL; - } -could_not_add_src: - { - GST_DEBUG_OBJECT (ssim, "could not add src pad"); - gst_object_unref (newsrc); - } -could_not_add_sink: - { - GST_DEBUG_OBJECT (ssim, "could not add sink pad"); - gst_collect_pads_remove_pad (ssim->collect, newpad); - gst_object_unref (newpad); - return NULL; - } -} - -static void -gst_ssim_release_pad (GstElement * element, GstPad * pad) -{ - GstSSim *ssim; - - ssim = GST_SSIM (element); - - GST_DEBUG_OBJECT (ssim, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - gst_collect_pads_remove_pad (ssim->collect, pad); - gst_element_remove_pad (element, pad); -} - - -static void -gst_ssim_init (GstSSim * ssim) -{ - ssim->windowsize = 11; - ssim->windowtype = 1; - ssim->windows = NULL; - ssim->sigma = 1.5; - ssim->ssimtype = 0; - ssim->src = g_ptr_array_new (); - ssim->padcount = 0; - ssim->collect_event = NULL; - ssim->sinkcaps = NULL; - - /* keep track of the sinkpads requested */ - ssim->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (ssim->collect, - GST_DEBUG_FUNCPTR (gst_ssim_collected), ssim); -} - -static void -gst_ssim_finalize (GObject * object) -{ - GstSSim *ssim = GST_SSIM (object); - - gst_object_unref (ssim->collect); - ssim->collect = NULL; - - g_free (ssim->windows); - ssim->windows = NULL; - - g_free (ssim->weights); - ssim->weights = NULL; - - if (ssim->sinkcaps) - gst_caps_unref (ssim->sinkcaps); - if (ssim->srccaps) - gst_caps_unref (ssim->srccaps); - - g_ptr_array_free (ssim->src, TRUE); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -typedef gfloat (*GstSSimWeightFunc) (GstSSim * ssim, gint y, gint x); - -static gfloat -gst_ssim_weight_func_none (GstSSim * ssim, gint y, gint x) -{ - return 1; -} - -static gfloat -gst_ssim_weight_func_gauss (GstSSim * ssim, gint y, gint x) -{ - gfloat coord = sqrt (x * x + y * y); - return exp (-1 * (coord * coord) / (2 * ssim->sigma * ssim->sigma)) / - (ssim->sigma * sqrt (2 * G_PI)); -} - -static gboolean -gst_ssim_regenerate_windows (GstSSim * ssim) -{ - gint windowiseven; - gint y, x, y2, x2; - GstSSimWeightFunc func; - gfloat normal_summ = 0; - gint normal_count = 0; - - g_free (ssim->weights); - - ssim->weights = g_new (gfloat, ssim->windowsize * ssim->windowsize); - - windowiseven = ((gint) ssim->windowsize / 2) * 2 == ssim->windowsize ? 1 : 0; - - g_free (ssim->windows); - - ssim->windows = g_new (GstSSimWindowCache, ssim->height * ssim->width); - - switch (ssim->windowtype) { - case 0: - func = gst_ssim_weight_func_none; - break; - case 1: - func = gst_ssim_weight_func_gauss; - break; - default: - GST_WARNING_OBJECT (ssim, "unknown window type - %d. Defaulting to %d", - ssim->windowtype, 1); - ssim->windowtype = 1; - func = gst_ssim_weight_func_gauss; - } - - for (y = 0; y < ssim->windowsize; y++) { - gint yoffset = y * ssim->windowsize; - for (x = 0; x < ssim->windowsize; x++) { - ssim->weights[yoffset + x] = func (ssim, x - ssim->windowsize / 2 + - windowiseven, y - ssim->windowsize / 2 + windowiseven); - normal_summ += ssim->weights[yoffset + x]; - normal_count++; - } - } - - for (y = 0; y < ssim->height; y++) { - for (x = 0; x < ssim->width; x++) { - GstSSimWindowCache win; - gint element_count = 0; - - win.x_window_start = x - ssim->windowsize / 2 + windowiseven; - win.x_weight_start = 0; - if (win.x_window_start < 0) { - win.x_weight_start = -win.x_window_start; - win.x_window_start = 0; - } - - win.x_window_end = x + ssim->windowsize / 2; - if (win.x_window_end >= ssim->width) - win.x_window_end = ssim->width - 1; - - win.y_window_start = y - ssim->windowsize / 2 + windowiseven; - win.y_weight_start = 0; - if (win.y_window_start < 0) { - win.y_weight_start = -win.y_window_start; - win.y_window_start = 0; - } - - win.y_window_end = y + ssim->windowsize / 2; - if (win.y_window_end >= ssim->height) - win.y_window_end = ssim->height - 1; - - win.element_summ = 0; - element_count = (win.y_window_end - win.y_window_start + 1) * - (win.x_window_end - win.x_window_start + 1); - if (element_count == normal_count) - win.element_summ = normal_summ; - else { - for (y2 = win.y_weight_start; y2 < ssim->windowsize; y2++) { - for (x2 = win.x_weight_start; x2 < ssim->windowsize; x2++) { - win.element_summ += ssim->weights[y2 * ssim->windowsize + x2]; - } - } - } - ssim->windows[(y * ssim->width + x)] = win; - } - } - - /* FIXME: while 0.01 and 0.03 are pretty much static, the 255 implies that - * we're working with 8-bit-per-color-component format, which may not be true - */ - ssim->const1 = 0.01 * 255 * 0.01 * 255; - ssim->const2 = 0.03 * 255 * 0.03 * 255; - return TRUE; -} - -static GstFlowReturn -gst_ssim_collected (GstCollectPads * pads, gpointer user_data) -{ - GstSSim *ssim; - GSList *collected; - GstFlowReturn ret = GST_FLOW_OK; - GstBuffer *orgbuf = NULL; - gfloat *orgmu = NULL; - GstBuffer *outbuf = NULL; - gpointer outdata = NULL; - guint outsize = 0; - gfloat mssim = 0, lowest = 1, highest = -1; - gboolean ready = TRUE; - gint padnumber = 0; - - ssim = GST_SSIM (user_data); - - if (G_UNLIKELY (ssim->windows == NULL)) { - GST_DEBUG_OBJECT (ssim, "Regenerating windows"); - gst_ssim_regenerate_windows (ssim); - } - - switch (ssim->ssimtype) { - case 0: - ssim->func = (GstSSimFunction) calcssim_canonical; - break; - case 1: - ssim->func = (GstSSimFunction) calcssim_without_mu; - break; - default: - return GST_FLOW_ERROR; - } - - for (collected = pads->data; collected; collected = g_slist_next (collected)) { - GstCollectData *collect_data; - GstBuffer *inbuf; - - collect_data = (GstCollectData *) collected->data; - - inbuf = gst_collect_pads_peek (pads, collect_data); - - if (inbuf == NULL) { - GST_LOG_OBJECT (ssim, "channel %p: no bytes available", collect_data); - ready = FALSE; - } else - gst_buffer_unref (inbuf); - } - - /* if _collected() was called, all pads should have data, but if - * one of them doesn't, it means that it is EOS and we can't go any further - * - * FIXME, shouldn't we do something about pads that DO have data? - * Flush them or something? - */ - if (G_UNLIKELY (!ready)) - goto eos; - - /* Mu is just a blur, we can calculate it once */ - if (ssim->ssimtype == 0) { - orgmu = g_new (gfloat, ssim->width * ssim->height); - - for (collected = pads->data; collected; - collected = g_slist_next (collected)) { - GstCollectData *collect_data; - - collect_data = (GstCollectData *) collected->data; - - if (collect_data->pad == ssim->orig) { - orgbuf = gst_collect_pads_pop (pads, collect_data); - - GST_DEBUG_OBJECT (ssim, "Original stream - flags(0x%x), timestamp(%" - GST_TIME_FORMAT "), duration(%" GST_TIME_FORMAT ")", - GST_BUFFER_FLAGS (orgbuf), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (orgbuf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (orgbuf))); - calculate_mu (ssim, orgmu, GST_BUFFER_DATA (orgbuf)); - - break; - } - } - } - - GST_LOG_OBJECT (ssim, "starting to cycle through streams"); - - for (collected = pads->data; collected; collected = g_slist_next (collected)) { - GstCollectData *collect_data; - GstBuffer *inbuf; - guint8 *indata; - - collect_data = (GstCollectData *) collected->data; - - if (collect_data->pad != ssim->orig) { - inbuf = gst_collect_pads_pop (pads, collect_data); - - indata = GST_BUFFER_DATA (inbuf); - - GST_DEBUG_OBJECT (ssim, "Modified stream - flags(0x%x), timestamp(%" - GST_TIME_FORMAT "), duration(%" GST_TIME_FORMAT ")", - GST_BUFFER_FLAGS (inbuf), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf))); - - if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { - GstSSimOutputContext *c; - GstEvent *measured; - guint64 offset; - GValue vmean = { 0 } - , vlowest = { - 0} - , vhighest = { - 0}; - - c = (GstSSimOutputContext *) - g_object_get_data (G_OBJECT (collect_data->pad), - "ssim-match-output-context"); - - GST_DEBUG_OBJECT (ssim, "Output context is %" GST_PTR_FORMAT - ", pad will be %" GST_PTR_FORMAT, c, c->pad); - - outsize = GST_ROUND_UP_4 (ssim->width) * ssim->height; - GST_LOG_OBJECT (ssim, "channel %p: making output buffer of %d bytes", - collect_data, outsize); - - /* first buffer, alloc outsize. - * FIXME: we can easily subbuffer and _make_writable. - * FIXME: only create empty buffer for first non-gap buffer, so that we - * only use ssim function when really calculating - */ - outbuf = gst_buffer_new_and_alloc (GST_ROUND_UP_4 (ssim->width) * - ssim->height); - outdata = GST_BUFFER_DATA (outbuf); - gst_buffer_set_caps (outbuf, gst_pad_get_fixed_caps_func (c->pad)); - - /* Videos should match, so the output video has the same characteristics - * as the input video - */ - /* set timestamps on the output buffer */ - gst_buffer_copy_metadata (outbuf, inbuf, (GstBufferCopyFlags) - GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS); - - g_value_init (&vmean, G_TYPE_FLOAT); - g_value_init (&vlowest, G_TYPE_FLOAT); - g_value_init (&vhighest, G_TYPE_FLOAT); - - GST_LOG_OBJECT (ssim, "channel %p: calculating SSIM", collect_data); - - ssim->func (ssim, GST_BUFFER_DATA (orgbuf), orgmu, indata, outdata, - &mssim, &lowest, &highest); - - GST_DEBUG_OBJECT (GST_OBJECT (ssim), "MSSIM is %f, l-h is %f - %f", - mssim, lowest, highest); - - gst_ssim_post_message (ssim, outbuf, mssim, lowest, highest); - - g_value_set_float (&vmean, mssim); - g_value_set_float (&vlowest, lowest); - g_value_set_float (&vhighest, highest); - offset = GST_BUFFER_OFFSET (inbuf); - - /* our timestamping is very simple, just an ever incrementing - * counter, the new segment time will take care of their respective - * stream time. - */ - if (c->segment_pending) { - GstEvent *event; - - /* FIXME, use rate/applied_rate as set on all sinkpads. - * - currently we just set rate as received from last seek-event - * We could potentially figure out the duration as well using - * the current segment positions and the stated stop positions. - * Also we just start from stream time 0 which is rather - * weird. For non-synchronized mixing, the time should be - * the min of the stream times of all received segments, - * rationale being that the duration is at least going to - * be as long as the earliest stream we start mixing. This - * would also be correct for synchronized mixing but then - * the later streams would be delayed until the stream times` - * match. - */ - event = gst_event_new_new_segment_full (FALSE, ssim->segment_rate, - 1.0, GST_FORMAT_TIME, ssim->timestamp, -1, - ssim->segment_position); - - gst_pad_push_event (c->pad, event); - c->segment_pending = FALSE; - } - - measured = gst_event_new_measured (offset, - GST_BUFFER_TIMESTAMP (inbuf), "SSIM", &vmean, &vlowest, &vhighest); - gst_pad_push_event (c->pad, measured); - - /* send it out */ - GST_DEBUG_OBJECT (ssim, "pushing outbuf, timestamp %" GST_TIME_FORMAT - ", size %d", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), - GST_BUFFER_SIZE (outbuf)); - ret &= gst_pad_push (c->pad, outbuf); - - } else { - GST_LOG_OBJECT (ssim, "channel %p: skipping", collect_data); - } - gst_buffer_unref (inbuf); - padnumber++; - } - } - gst_buffer_unref (orgbuf); - - if (ssim->ssimtype == 0) - g_free (orgmu); - - ssim->segment_position = 0; - - return ret; - - /* ERRORS */ -eos: - { - gint i; - GST_DEBUG_OBJECT (ssim, "no data available, must be EOS"); - for (i = 0; i < ssim->src->len; i++) { - GstSSimOutputContext *c = - (GstSSimOutputContext *) g_ptr_array_index (ssim->src, i); - gst_pad_push_event (c->pad, gst_event_new_eos ()); - } - - return GST_FLOW_UNEXPECTED; - } -} - -static GstStateChangeReturn -gst_ssim_change_state (GstElement * element, GstStateChange transition) -{ - GstSSim *ssim; - GstStateChangeReturn ret; - - ssim = GST_SSIM (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - ssim->timestamp = 0; - ssim->offset = 0; - { - GstSSimOutputContext *c; - gint i = 0; - for (i = 0; i < ssim->src->len; i++) { - c = (GstSSimOutputContext *) g_ptr_array_index (ssim->src, i); - c->segment_pending = TRUE; - } - } - ssim->segment_position = 0; - ssim->segment_rate = 1.0; - gst_segment_init (&ssim->segment, GST_FORMAT_UNDEFINED); - gst_collect_pads_start (ssim->collect); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - /* need to unblock the collectpads before calling the - * parent change_state so that streaming can finish - */ - gst_collect_pads_stop (ssim->collect); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - default: - break; - } - - return ret; -} diff --git a/gst/videomeasure/gstvideomeasure_ssim.h b/gst/videomeasure/gstvideomeasure_ssim.h deleted file mode 100644 index 57e0907fbf..0000000000 --- a/gst/videomeasure/gstvideomeasure_ssim.h +++ /dev/null @@ -1,140 +0,0 @@ -/* GStreamer - * Copyright (C) <2009> Руслан Ижбулатов - * - * This library 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 2.1 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#ifndef __GST_SSIM_H__ -#define __GST_SSIM_H__ - -#include -#include -#include - -G_BEGIN_DECLS - -enum -{ - PROP_0, - PROP_SSIM_TYPE, - PROP_WINDOW_TYPE, - PROP_WINDOW_SIZE, - PROP_GAUSS_SIGMA, -}; - - -#define GST_TYPE_SSIM (gst_ssim_get_type()) -#define GST_SSIM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ - GST_TYPE_SSIM,GstSSim)) -#define GST_IS_SSIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ - GST_TYPE_SSIM)) -#define GST_SSIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) , \ - GST_TYPE_SSIM,GstSSimClass)) -#define GST_IS_SSIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) , \ - GST_TYPE_SSIM)) -#define GST_SSIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) , \ - GST_TYPE_SSIM,GstSSimClass)) - -typedef struct _GstSSim GstSSim; -typedef struct _GstSSimClass GstSSimClass; - -typedef struct _GstSSimWindowCache { - gint x_window_start; - gint x_weight_start; - gint x_window_end; - gint y_window_start; - gint y_weight_start; - gint y_window_end; - gfloat element_summ; -} GstSSimWindowCache; - -typedef void (*GstSSimFunction) (GstSSim *ssim, guint8 *org, gfloat *orgmu, - guint8 *mod, guint8 *out, gfloat *mean, gfloat *lowest, gfloat *highest); - -typedef struct _GstSSimOutputContext GstSSimOutputContext; - -/* TODO: check if all fields are used */ -struct _GstSSimOutputContext { - GstPad *pad; - gboolean segment_pending; -}; - -/** - * GstSSim: - * - * The ssim object structure. - */ -struct _GstSSim { - GstElement element; - - /* Array of GstSSimOutputContext */ - GPtrArray *src; - - gint padcount; - - GstCollectPads *collect; - GstPad *orig; - - gint frame_rate; - gint frame_rate_base; - gint width; - gint height; - GstCaps *sinkcaps; - GstCaps *srccaps; - - /* SSIM type (0 - canonical; 1 - without mu) */ - gint ssimtype; - - /* Size of a window, windows are square */ - gint windowsize; - - /* Type of a weight-generator. 0 - no weighting. 1 - Gaussian weighting */ - gint windowtype; - - /* Array of width*height GstSSimWindowCaches */ - GstSSimWindowCache *windows; - - /* Array of windowsize*windowsize gfloats */ - gfloat *weights; - - /* For Gaussian function */ - gfloat sigma; - - GstSSimFunction func; - - gfloat const1; - gfloat const2; - - /* counters to keep track of timestamps */ - gint64 timestamp; - gint64 offset; - - /* sink event handling */ - GstPadEventFunction collect_event; - GstSegment segment; - guint64 segment_position; - gdouble segment_rate; -}; - -struct _GstSSimClass { - GstElementClass parent_class; -}; - -GType gst_ssim_get_type (void); - -G_END_DECLS - -#endif /* __GST_SSIM_H__ */ diff --git a/gst/videomeasure/meson.build b/gst/videomeasure/meson.build deleted file mode 100644 index 4b0eaf6d66..0000000000 --- a/gst/videomeasure/meson.build +++ /dev/null @@ -1,14 +0,0 @@ -measure_sources = [ - 'gstvideomeasure.c', - 'gstvideomeasure_ssim.c', - 'gstvideomeasure_collector.c', -] - -gstvideomeasure = library('gstvideomeasure', - measure_sources, - c_args : gst_plugins_bad_args, - include_directories : [configinc, libsinc], - dependencies : [gstbase_dep, gstvideo_dep, libm], - install : true, - install_dir : plugins_install_dir, -)