diff options
author | Thibault Saunier <thibault.saunier@osg.samsung.com> | 2016-11-16 18:01:19 -0300 |
---|---|---|
committer | Thibault Saunier <thibault.saunier@osg.samsung.com> | 2016-11-17 13:25:40 -0300 |
commit | ca3020cf46d49d8e3fd54ec323b3a17cd389d780 (patch) | |
tree | 048350bf96733a6e5aef32bb9fe198752270e78a | |
parent | b1710d7c158d969a151caffad4c6671d62c679a6 (diff) | |
download | gstreamer-plugins-bad-ca3020cf46d49d8e3fd54ec323b3a17cd389d780.tar.gz |
videomeasure: Remove old unported videomeasure plugin as it is now replaced by IQA
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | gst/meson.build | 2 | ||||
-rw-r--r-- | gst/videomeasure/Makefile.am | 18 | ||||
-rw-r--r-- | gst/videomeasure/gstvideomeasure.c | 71 | ||||
-rw-r--r-- | gst/videomeasure/gstvideomeasure.h | 31 | ||||
-rw-r--r-- | gst/videomeasure/gstvideomeasure_collector.c | 410 | ||||
-rw-r--r-- | gst/videomeasure/gstvideomeasure_collector.h | 80 | ||||
-rw-r--r-- | gst/videomeasure/gstvideomeasure_ssim.c | 1680 | ||||
-rw-r--r-- | gst/videomeasure/gstvideomeasure_ssim.h | 140 | ||||
-rw-r--r-- | gst/videomeasure/meson.build | 14 |
10 files changed, 0 insertions, 2449 deletions
diff --git a/configure.ac b/configure.ac index 35ef31f8a..0a5aea9b3 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 c44335190..a1eba92a1 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 139dee954..000000000 --- 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 aa97a3f08..000000000 --- a/gst/videomeasure/gstvideomeasure.c +++ /dev/null @@ -1,71 +0,0 @@ -/* GStreamer - * Copyright (C) <2009> Руслан Ижбулатов <lrn1986 _at_ gmail _dot_ com> - * - * 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 <gst/gst-i18n-plugin.h> - -#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 e385a8d1d..000000000 --- a/gst/videomeasure/gstvideomeasure.h +++ /dev/null @@ -1,31 +0,0 @@ -/* GStreamer
- * Copyright (C) <2009> Руслан Ижбулатов <lrn1986 _at_ gmail _dot_ com>
- *
- * 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 <gst/video/gstvideofilter.h>
-
-#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 3778bbf20..000000000 --- a/gst/videomeasure/gstvideomeasure_collector.c +++ /dev/null @@ -1,410 +0,0 @@ -/* GStreamer - * Copyright (C) <2009> Руслан Ижбулатов <lrn1986 _at_ gmail _dot_ com> - * - * 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 - * <classname>"GstMeasureCollector"</classname>. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/gst-i18n-plugin.h> - -#include "gstvideomeasure_collector.h" - -#include <stdio.h> -#include <string.h> -#include <math.h> - -#include <gst/video/video.h> - -/* 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, "<untranslatable>"); - } - } - } - - 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", - "Руслан Ижбулатов <lrn _at_ gmail _dot_ com>"); - - 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 49a4e9318..000000000 --- a/gst/videomeasure/gstvideomeasure_collector.h +++ /dev/null @@ -1,80 +0,0 @@ -/* GStreamer
- * Copyright (C) <2009> Руслан Ижбулатов <lrn1986 _at_ gmail _dot_ com>
- *
- * 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 <gst/base/gstbasetransform.h>
-
-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 d0a2c9d66..000000000 --- a/gst/videomeasure/gstvideomeasure_ssim.c +++ /dev/null @@ -1,1680 +0,0 @@ -/* GStreamer - * Copyright (C) <2009> Руслан Ижбулатов <lrn1986 _at_ gmail _dot_ com> - * - * 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. - * - * <refsect2> - * <title>Example launch line</title> - * |[ - * 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. - * </refsect2> - */ -/* Element-Checklist-Version: 5 */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstvideomeasure.h" -#include "gstvideomeasure_ssim.h" -#include <gst/audio/audio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> - -#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", - "Руслан Ижбулатов <lrn1986 _at_ gmail _dot_ com>"); - - 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 57e0907fb..000000000 --- a/gst/videomeasure/gstvideomeasure_ssim.h +++ /dev/null @@ -1,140 +0,0 @@ -/* GStreamer
- * Copyright (C) <2009> Руслан Ижбулатов <lrn1986 _at_ gmail _dot_ com>
- *
- * 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 <gst/gst.h>
-#include <gst/base/gstcollectpads.h>
-#include <gst/video/video.h>
-
-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 4b0eaf6d6..000000000 --- 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, -) |