Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / gst / videomeasure / gstvideomeasure_collector.c
1 /* GStreamer
2  * Copyright (C) <2009> Руслан Ижбулатов <lrn1986 _at_ gmail _dot_ com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  * 
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301  USA
18  */
19
20 /**
21  * SECTION:element-measurecollector
22  *
23  * This plugin collects measurements from measuring elemtns and calculates
24  * total measure for the whole sequence and also outputs measurements to a file
25  * <classname>&quot;GstMeasureCollector&quot;</classname>.
26  *
27  *
28  * Last reviewed on 2009-03-15 (0.10.?)
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include <gst/gst-i18n-plugin.h>
36
37 #include "gstvideomeasure_collector.h"
38
39 #include <stdio.h>
40 #include <string.h>
41 #include <math.h>
42
43 #include <gst/video/video.h>
44
45 /* GstMeasureCollector signals and args */
46
47 enum
48 {
49   PROP_0,
50   PROP_FLAGS,
51   PROP_FILENAME
52 };
53
54 GST_DEBUG_CATEGORY_STATIC (measure_collector_debug);
55 #define GST_CAT_DEFAULT measure_collector_debug
56
57 static GstStaticPadTemplate gst_measure_collector_src_template =
58 GST_STATIC_PAD_TEMPLATE ("src",
59     GST_PAD_SRC,
60     GST_PAD_ALWAYS,
61     GST_STATIC_CAPS_ANY);
62
63 static GstStaticPadTemplate gst_measure_collector_sink_template =
64 GST_STATIC_PAD_TEMPLATE ("sink",
65     GST_PAD_SINK,
66     GST_PAD_ALWAYS,
67     GST_STATIC_CAPS_ANY);
68
69 //static GstBaseTransformClass *parent_class = NULL;
70
71 static void gst_measure_collector_finalize (GObject * object);
72 static gboolean gst_measure_collector_event (GstBaseTransform * base,
73     GstEvent * event);
74 static void gst_measure_collector_save_csv (GstMeasureCollector * mc);
75
76 static void gst_measure_collector_post_message (GstMeasureCollector * mc);
77
78 GST_BOILERPLATE (GstMeasureCollector, gst_measure_collector, GstBaseTransform,
79     GST_TYPE_BASE_TRANSFORM);
80
81 static void
82 gst_measure_collector_collect (GstMeasureCollector * mc, GstEvent * gstevent)
83 {
84   const GstStructure *str;
85   const gchar *event, *metric;
86   guint64 framenumber = G_MAXUINT64;
87   const GValue *framenumber_v;
88
89   str = gst_event_get_structure (gstevent);
90
91   event = gst_structure_get_string (str, "event");
92   metric = gst_structure_get_string (str, "metric");
93
94   if (strcmp (event, "frame-measured") == 0 && metric != NULL) {
95     GstStructure *cpy;
96     cpy = gst_structure_copy (str);
97
98     framenumber_v = gst_structure_get_value (str, "offset");
99     if (framenumber_v) {
100       if (G_VALUE_TYPE (framenumber_v) == G_TYPE_UINT64)
101         framenumber = g_value_get_uint64 (framenumber_v);
102       else if (G_VALUE_TYPE (framenumber_v) == G_TYPE_INT64)
103         framenumber = g_value_get_int64 (framenumber_v);
104     }
105
106     if (framenumber == G_MAXUINT64)
107       framenumber = mc->nextoffset++;
108
109     if (mc->measurements->len <= framenumber)
110       g_ptr_array_set_size (mc->measurements, framenumber + 1);
111     g_ptr_array_index (mc->measurements, framenumber) = cpy;
112
113     mc->nextoffset = framenumber + 1;
114
115     if (!mc->metric)
116       mc->metric = g_strdup (metric);
117   }
118 }
119
120 static void
121 gst_measure_collector_post_message (GstMeasureCollector * mc)
122 {
123   GstMessage *m;
124   guint64 i;
125
126   g_return_if_fail (mc->metric);
127
128   if (strcmp (mc->metric, "SSIM") == 0) {
129     gfloat dresult = 0;
130     guint64 mlen;
131     g_free (mc->result);
132     mc->result = g_new0 (GValue, 1);
133     g_value_init (mc->result, G_TYPE_FLOAT);
134     mlen = mc->measurements->len;
135     for (i = 0; i < mc->measurements->len; i++) {
136       const GValue *v;
137       GstStructure *str =
138           (GstStructure *) g_ptr_array_index (mc->measurements, i);
139       if (str) {
140         v = gst_structure_get_value (str, "mean");
141         dresult += g_value_get_float (v);
142       } else {
143         GST_WARNING_OBJECT (mc,
144             "No measurement info for frame %" G_GUINT64_FORMAT, i);
145         mlen--;
146       }
147     }
148     g_value_set_float (mc->result, dresult / mlen);
149   }
150
151   m = gst_message_new_element (GST_OBJECT_CAST (mc),
152       gst_structure_new ("GstMeasureCollector",
153           "measure-result", G_TYPE_VALUE, mc->result, NULL));
154
155   gst_element_post_message (GST_ELEMENT_CAST (mc), m);
156 }
157
158 static void
159 gst_measure_collector_set_property (GObject * object, guint prop_id,
160     const GValue * value, GParamSpec * pspec)
161 {
162   GstMeasureCollector *measurecollector;
163
164   measurecollector = GST_MEASURE_COLLECTOR (object);
165
166   switch (prop_id) {
167     case PROP_FLAGS:
168       measurecollector->flags = g_value_get_uint64 (value);
169       break;
170     case PROP_FILENAME:
171       measurecollector->filename = g_value_dup_string (value);
172       break;
173     default:
174       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175       break;
176   }
177 }
178
179 static void
180 gst_measure_collector_get_property (GObject * object, guint prop_id,
181     GValue * value, GParamSpec * pspec)
182 {
183   GstMeasureCollector *measurecollector;
184
185   measurecollector = GST_MEASURE_COLLECTOR (object);
186
187   switch (prop_id) {
188     case PROP_FLAGS:
189       g_value_set_uint64 (value, measurecollector->flags);
190       break;
191     case PROP_FILENAME:
192       g_value_set_string (value, measurecollector->filename);
193       break;
194     default:
195       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
196       break;
197   }
198 }
199
200 static gboolean
201 gst_measure_collector_event (GstBaseTransform * base, GstEvent * event)
202 {
203   GstMeasureCollector *mc = GST_MEASURE_COLLECTOR (base);
204
205   switch (GST_EVENT_TYPE (event)) {
206     case GST_EVENT_CUSTOM_DOWNSTREAM:
207       if (gst_event_has_name (event, GST_EVENT_VIDEO_MEASURE))
208         gst_measure_collector_collect (mc, event);
209       break;
210     case GST_EVENT_EOS:
211       gst_measure_collector_post_message (mc);
212       gst_measure_collector_save_csv (mc);
213       break;
214     default:
215       break;
216   }
217
218   return parent_class->event (base, event);
219 }
220
221 static void
222 gst_measure_collector_save_csv (GstMeasureCollector * mc)
223 {
224   gchar *name_local;
225   FILE *file;
226   guint64 i, j;
227   GstStructure *str;
228   GValue tmp = { 0 };
229   g_value_init (&tmp, G_TYPE_STRING);
230
231   if (!(mc->flags & GST_MEASURE_COLLECTOR_WRITE_CSV))
232     return;
233
234   if (mc->measurements->len <= 0)
235     goto empty;
236
237   /* open the file */
238   if (mc->filename == NULL || mc->filename[0] == '\0')
239     goto no_filename;
240
241   name_local = g_filename_from_utf8 ((const gchar *) mc->filename,
242       -1, NULL, NULL, NULL);
243
244   /* open the file */
245   if (name_local == NULL || name_local[0] == '\0')
246     goto not_good_filename;
247
248
249   /* FIXME, can we use g_fopen here? some people say that the FILE object is
250    * local to the .so that performed the fopen call, which would not be us when
251    * we use g_fopen. */
252   file = fopen (name_local, "wb");
253
254   g_free (name_local);
255
256   if (file == NULL)
257     goto open_failed;
258
259   str = (GstStructure *) g_ptr_array_index (mc->measurements, 0);
260
261   for (j = 0; j < gst_structure_n_fields (str); j++) {
262     const gchar *fieldname;
263     fieldname = gst_structure_nth_field_name (str, j);
264     if (G_LIKELY (j > 0))
265       fprintf (file, ";");
266     fprintf (file, "%s", fieldname);
267   }
268
269   for (i = 0; i < mc->measurements->len; i++) {
270     fprintf (file, "\n");
271     str = (GstStructure *) g_ptr_array_index (mc->measurements, i);
272     if (str != NULL) {
273       for (j = 0; j < gst_structure_n_fields (str); j++) {
274         const gchar *fieldname;
275         fieldname = gst_structure_nth_field_name (str, j);
276         if (G_LIKELY (j > 0))
277           fprintf (file, ";");
278         if (G_LIKELY (g_value_transform (gst_structure_get_value (str,
279                         fieldname), &tmp)))
280           fprintf (file, "%s", g_value_get_string (&tmp));
281         else
282           fprintf (file, "<untranslatable>");
283       }
284     }
285   }
286
287   fclose (file);
288
289   /* ERRORS */
290 empty:
291   {
292     return;
293   }
294 no_filename:
295   {
296     GST_ELEMENT_ERROR (mc, RESOURCE, NOT_FOUND,
297         (_("No file name specified for writing.")), (NULL));
298     return;
299   }
300 not_good_filename:
301   {
302     GST_ELEMENT_ERROR (mc, RESOURCE, NOT_FOUND,
303         (_("Given file name \"%s\" can't be converted to local file name \
304 encoding."), mc->filename), (NULL));
305     return;
306   }
307 open_failed:
308   {
309     GST_ELEMENT_ERROR (mc, RESOURCE, OPEN_WRITE,
310         (_("Could not open file \"%s\" for writing."), mc->filename),
311         GST_ERROR_SYSTEM);
312     return;
313   }
314 }
315
316 static void
317 gst_measure_collector_base_init (gpointer g_class)
318 {
319   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
320
321   gst_element_class_set_details_simple (element_class,
322       "Video measure collector", "Filter/Effect/Video",
323       "Collect measurements from a measuring element",
324       "Руслан Ижбулатов <lrn _at_ gmail _dot_ com>");
325
326   gst_element_class_add_static_pad_template (element_class,
327       &gst_measure_collector_sink_template);
328   gst_element_class_add_static_pad_template (element_class,
329       &gst_measure_collector_src_template);
330 }
331
332 static void
333 gst_measure_collector_class_init (GstMeasureCollectorClass * klass)
334 {
335   GObjectClass *gobject_class;
336   GstBaseTransformClass *trans_class;
337
338   gobject_class = G_OBJECT_CLASS (klass);
339   trans_class = GST_BASE_TRANSFORM_CLASS (klass);
340
341   GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "measurecollect", 0,
342       "measurement collector");
343
344   gobject_class->set_property = gst_measure_collector_set_property;
345   gobject_class->get_property = gst_measure_collector_get_property;
346   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_measure_collector_finalize);
347
348   g_object_class_install_property (gobject_class, PROP_FLAGS,
349       g_param_spec_uint64 ("flags", "Flags",
350           "Flags that control the operation of the element",
351           0, G_MAXUINT64, 0,
352           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
353
354   g_object_class_install_property (gobject_class, PROP_FILENAME,
355       g_param_spec_string ("filename", "Output file name",
356           "A name of a file into which element will write the measurement"
357           " information", "",
358           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
359
360   trans_class->event = GST_DEBUG_FUNCPTR (gst_measure_collector_event);
361
362   trans_class->passthrough_on_same_caps = TRUE;
363
364 }
365
366 static void
367 gst_measure_collector_init (GstMeasureCollector * instance,
368     GstMeasureCollectorClass * g_class)
369 {
370   GstMeasureCollector *measurecollector;
371
372   measurecollector = GST_MEASURE_COLLECTOR (instance);
373
374   GST_DEBUG_OBJECT (measurecollector, "gst_measure_collector_init");
375
376   gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (measurecollector),
377       FALSE);
378
379   measurecollector->measurements = g_ptr_array_new ();
380   measurecollector->metric = NULL;
381   measurecollector->inited = TRUE;
382   measurecollector->filename = NULL;
383   measurecollector->flags = 0;
384   measurecollector->nextoffset = 0;
385   measurecollector->result = NULL;
386 }
387
388 static void
389 gst_measure_collector_finalize (GObject * object)
390 {
391   gint i;
392   GstMeasureCollector *mc = GST_MEASURE_COLLECTOR (object);
393
394   for (i = 0; i < mc->measurements->len; i++) {
395     if (g_ptr_array_index (mc->measurements, i) != NULL)
396       gst_structure_free ((GstStructure *) g_ptr_array_index (mc->measurements,
397               i));
398   }
399
400   g_ptr_array_free (mc->measurements, TRUE);
401   mc->measurements = NULL;
402
403   g_free (mc->result);
404   mc->result = NULL;
405
406   g_free (mc->metric);
407   mc->metric = NULL;
408
409   g_free (mc->filename);
410   mc->filename = NULL;
411
412   G_OBJECT_CLASS (parent_class)->finalize (object);
413 }