Merging gst-examples
[platform/upstream/gstreamer.git] / subprojects / gst-devtools / validate / gst / validate / flow / formatting.c
1 /* GStreamer
2  *
3  * Copyright (C) 2018-2019 Igalia S.L.
4  * Copyright (C) 2018 Metrological Group B.V.
5  *  Author: Alicia Boya GarcĂ­a <aboya@igalia.com>
6  *
7  * formatting.c: Functions used by validateflow to get string
8  * representations of buffers.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "formatting.h"
31
32 #include <gst/gst.h>
33 #include <gst/video/video.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <glib/gprintf.h>
37
38 #include "../../gst/validate/gst-validate-utils.h"
39
40 typedef void (*Uint64Formatter) (gchar * dest, guint64 time);
41 G_LOCK_DEFINE (checksums_as_id_lock);
42 static GstStructure *checksums_as_id = NULL;
43
44 #define CONSTIFY(strv) ((const gchar * const *) strv)
45
46 static gboolean
47 use_field (const gchar * field, gchar ** logged, gchar ** ignored)
48 {
49   if (logged)
50     return g_strv_contains (CONSTIFY (logged), field);
51
52   if (ignored)
53     return !g_strv_contains (CONSTIFY (ignored), field);
54
55   return TRUE;
56 }
57
58
59 void
60 format_time (gchar * dest_str, guint64 time)
61 {
62   if (GST_CLOCK_TIME_IS_VALID (time)) {
63     g_sprintf (dest_str, "%" GST_TIME_FORMAT, GST_TIME_ARGS (time));
64   } else {
65     strcpy (dest_str, "none");
66   }
67 }
68
69 static void
70 format_number (gchar * dest_str, guint64 number)
71 {
72   g_sprintf (dest_str, "%" G_GUINT64_FORMAT, number);
73 }
74
75 gchar *
76 validate_flow_format_segment (const GstSegment * segment,
77     gchar ** logged_fields, gchar ** ignored_fields)
78 {
79   Uint64Formatter uint64_format;
80   gchar *segment_str;
81   gchar *parts[12];
82   GString *format;
83   gchar start_str[32], offset_str[32], stop_str[32], time_str[32], base_str[32],
84       position_str[32], duration_str[32];
85   int parts_index = 0;
86
87   uint64_format =
88       segment->format == GST_FORMAT_TIME ? format_time : format_number;
89   uint64_format (start_str, segment->start);
90   uint64_format (offset_str, segment->offset);
91   uint64_format (stop_str, segment->stop);
92   uint64_format (time_str, segment->time);
93   uint64_format (base_str, segment->base);
94   uint64_format (position_str, segment->position);
95   uint64_format (duration_str, segment->duration);
96
97   format = g_string_new (gst_format_get_name (segment->format));
98   format = g_string_ascii_up (format);
99
100   if (use_field ("format", logged_fields, ignored_fields))
101     parts[parts_index++] = g_strdup_printf ("format=%s", format->str);
102
103   if (use_field ("start", logged_fields, ignored_fields))
104     parts[parts_index++] = g_strdup_printf ("start=%s", start_str);
105
106   if (use_field ("offset", logged_fields, ignored_fields))
107     parts[parts_index++] = g_strdup_printf ("offset=%s", offset_str);
108
109   if (use_field ("stop", logged_fields, ignored_fields))
110     parts[parts_index++] = g_strdup_printf ("stop=%s", stop_str);
111
112   if (segment->rate != 1.0)
113     parts[parts_index++] = g_strdup_printf ("rate=%f", segment->rate);
114   if (segment->applied_rate != 1.0)
115     parts[parts_index++] =
116         g_strdup_printf ("applied_rate=%f", segment->applied_rate);
117
118   if (segment->flags && use_field ("flags", logged_fields, ignored_fields))
119     parts[parts_index++] = g_strdup_printf ("flags=0x%02x", segment->flags);
120
121   if (use_field ("time", logged_fields, ignored_fields))
122     parts[parts_index++] = g_strdup_printf ("time=%s", time_str);
123   if (use_field ("base", logged_fields, ignored_fields))
124     parts[parts_index++] = g_strdup_printf ("base=%s", base_str);
125   if (use_field ("position", logged_fields, ignored_fields))
126     parts[parts_index++] = g_strdup_printf ("position=%s", position_str);
127   if (GST_CLOCK_TIME_IS_VALID (segment->duration)
128       && use_field ("duration", logged_fields, ignored_fields))
129     parts[parts_index++] = g_strdup_printf ("duration=%s", duration_str);
130   parts[parts_index] = NULL;
131
132   segment_str = g_strjoinv (", ", parts);
133
134   while (parts_index > 0)
135     g_free (parts[--parts_index]);
136   g_string_free (format, TRUE);
137
138   return segment_str;
139 }
140
141 typedef struct
142 {
143   GList *fields;
144
145   gchar **wanted_fields;
146   gchar **ignored_fields;
147 } StructureValues;
148
149 static gboolean
150 structure_set_fields (GQuark field_id, GValue * value, StructureValues * data)
151 {
152   const gchar *field = g_quark_to_string (field_id);
153
154   if (data->ignored_fields
155       && g_strv_contains ((const gchar **) data->ignored_fields, field))
156     return TRUE;
157
158   if (data->wanted_fields
159       && !g_strv_contains ((const gchar **) data->wanted_fields, field))
160     return TRUE;
161
162   data->fields = g_list_prepend (data->fields, (gchar *) field);
163
164   return TRUE;
165 }
166
167 static GstStructure *
168 validate_flow_structure_cleanup (const GstStructure * structure,
169     gchar ** wanted_fields, gchar ** ignored_fields)
170 {
171   GstStructure *nstructure;
172   StructureValues d = {
173     .fields = NULL,
174     .wanted_fields = wanted_fields,
175     .ignored_fields = ignored_fields,
176   };
177
178   gst_structure_foreach (structure,
179       (GstStructureForeachFunc) structure_set_fields, &d);
180   d.fields = g_list_sort (d.fields, (GCompareFunc) g_ascii_strcasecmp);
181   nstructure = gst_structure_new_empty (gst_structure_get_name (structure));
182   for (GList * tmp = d.fields; tmp; tmp = tmp->next) {
183     gchar *field = tmp->data;
184
185     gst_structure_set_value (nstructure, field,
186         gst_structure_get_value (structure, field));
187   }
188
189   g_list_free (d.fields);
190
191   return nstructure;
192 }
193
194 gchar *
195 validate_flow_format_caps (const GstCaps * caps, gchar ** wanted_fields)
196 {
197   guint i;
198   GstCaps *new_caps = gst_caps_new_empty ();
199   gchar *caps_str;
200
201   /* A single GstCaps can contain several caps structures (although only one is
202    * used in most cases). We will print them separated with spaces. */
203   for (i = 0; i < gst_caps_get_size (caps); i++) {
204     GstStructure *structure =
205         validate_flow_structure_cleanup (gst_caps_get_structure (caps, i),
206         wanted_fields, NULL);
207
208     gst_caps_append_structure_full (new_caps, structure,
209         gst_caps_features_copy (gst_caps_get_features (caps, i)));
210   }
211
212   caps_str = gst_caps_to_string (new_caps);
213   gst_caps_unref (new_caps);
214
215   return caps_str;
216 }
217
218
219 static gchar *
220 buffer_get_flags_string (GstBuffer * buffer)
221 {
222   GFlagsClass *flags_class =
223       G_FLAGS_CLASS (g_type_class_ref (gst_buffer_flags_get_type ()));
224   GstBufferFlags flags = GST_BUFFER_FLAGS (buffer);
225   GString *string = NULL;
226
227   while (1) {
228     GFlagsValue *value = g_flags_get_first_value (flags_class, flags);
229     if (!value)
230       break;
231
232     if (string == NULL)
233       string = g_string_new (NULL);
234     else
235       g_string_append (string, " ");
236
237     g_string_append (string, value->value_nick);
238     flags &= ~value->value;
239   }
240
241   return (string != NULL) ? g_string_free (string, FALSE) : NULL;
242 }
243
244 /* Returns a newly-allocated string describing the metas on this buffer, or NULL */
245 static gchar *
246 buffer_get_meta_string (GstBuffer * buffer)
247 {
248   gpointer state = NULL;
249   GstMeta *meta;
250   GString *s = NULL;
251
252   while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
253     const gchar *desc = g_type_name (meta->info->type);
254
255     if (s == NULL)
256       s = g_string_new (NULL);
257     else
258       g_string_append (s, ", ");
259
260     if (meta->info->api == GST_VIDEO_REGION_OF_INTEREST_META_API_TYPE) {
261       GstVideoRegionOfInterestMeta *roi = (GstVideoRegionOfInterestMeta *) meta;
262       g_string_append_printf (s,
263           "GstVideoRegionOfInterestMeta[x=%" G_GUINT32_FORMAT ", y=%"
264           G_GUINT32_FORMAT ", width=%" G_GUINT32_FORMAT ", height=%"
265           G_GUINT32_FORMAT "]", roi->x, roi->y, roi->w, roi->h);
266     } else {
267       g_string_append (s, desc);
268     }
269   }
270
271   return (s != NULL) ? g_string_free (s, FALSE) : NULL;
272 }
273
274 gchar *
275 validate_flow_format_buffer (GstBuffer * buffer, gint checksum_type,
276     GstStructure * logged_fields_struct, GstStructure * ignored_fields_struct)
277 {
278   gchar *flags_str, *meta_str, *buffer_str;
279   gchar *buffer_parts[7];
280   int buffer_parts_index = 0;
281   GstMapInfo map;
282   gchar **logged_fields =
283       logged_fields_struct ? gst_validate_utils_get_strv (logged_fields_struct,
284       "buffer") : NULL;
285   gchar **ignored_fields =
286       ignored_fields_struct ?
287       gst_validate_utils_get_strv (ignored_fields_struct, "buffer") : NULL;
288
289   if (checksum_type != CHECKSUM_TYPE_NONE || (logged_fields
290           && g_strv_contains (CONSTIFY (logged_fields), "checksum"))) {
291     if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
292       GST_ERROR ("Buffer could not be mapped.");
293     } else if (checksum_type == CHECKSUM_TYPE_CONTENT_HEX) {
294       gint i;
295       GString *content = g_string_new ("content=");
296
297       for (i = 0; i < map.size; i++) {
298         if (i)
299           g_string_append_c (content, ' ');
300         g_string_append_printf (content, "0x%02x", map.data[i]);
301       }
302
303       buffer_parts[buffer_parts_index++] = g_string_free (content, FALSE);
304     } else {
305       gchar *sum =
306           g_compute_checksum_for_data (checksum_type ==
307           CHECKSUM_TYPE_AS_ID ? G_CHECKSUM_SHA1 : checksum_type, map.data,
308           map.size);
309       gst_buffer_unmap (buffer, &map);
310
311       if (checksum_type == CHECKSUM_TYPE_AS_ID) {
312         gint id;
313
314         G_LOCK (checksums_as_id_lock);
315         if (!checksums_as_id)
316           checksums_as_id = gst_structure_new_empty ("checksums-id");
317         if (!gst_structure_get_int (checksums_as_id, sum, &id)) {
318           id = gst_structure_n_fields (checksums_as_id);
319           gst_structure_set (checksums_as_id, sum, G_TYPE_INT, id, NULL);
320         }
321         G_UNLOCK (checksums_as_id_lock);
322
323         buffer_parts[buffer_parts_index++] =
324             g_strdup_printf ("content-id=%d", id);
325       } else {
326         buffer_parts[buffer_parts_index++] =
327             g_strdup_printf ("checksum=%s", sum);
328       }
329       g_free (sum);
330     }
331   }
332
333   if (GST_CLOCK_TIME_IS_VALID (buffer->dts)
334       && use_field ("dts", logged_fields, ignored_fields)) {
335     gchar time_str[32];
336     format_time (time_str, buffer->dts);
337     buffer_parts[buffer_parts_index++] = g_strdup_printf ("dts=%s", time_str);
338   }
339
340   if (GST_CLOCK_TIME_IS_VALID (buffer->pts)
341       && use_field ("pts", logged_fields, ignored_fields)) {
342     gchar time_str[32];
343     format_time (time_str, buffer->pts);
344     buffer_parts[buffer_parts_index++] = g_strdup_printf ("pts=%s", time_str);
345   }
346
347   if (GST_CLOCK_TIME_IS_VALID (buffer->duration)
348       && use_field ("dur", logged_fields, ignored_fields)) {
349     gchar time_str[32];
350     format_time (time_str, buffer->duration);
351     buffer_parts[buffer_parts_index++] = g_strdup_printf ("dur=%s", time_str);
352   }
353
354   flags_str = buffer_get_flags_string (buffer);
355   if (flags_str && use_field ("flags", logged_fields, ignored_fields)) {
356     buffer_parts[buffer_parts_index++] =
357         g_strdup_printf ("flags=%s", flags_str);
358   }
359
360   meta_str = buffer_get_meta_string (buffer);
361   if (meta_str && use_field ("meta", logged_fields, ignored_fields))
362     buffer_parts[buffer_parts_index++] = g_strdup_printf ("meta=%s", meta_str);
363
364   buffer_parts[buffer_parts_index] = NULL;
365   buffer_str =
366       buffer_parts_index > 0 ? g_strjoinv (", ",
367       buffer_parts) : g_strdup ("(empty)");
368
369   g_free (meta_str);
370   g_free (flags_str);
371   while (buffer_parts_index > 0)
372     g_free (buffer_parts[--buffer_parts_index]);
373
374   return buffer_str;
375 }
376
377 gchar *
378 validate_flow_format_event (GstEvent * event,
379     const gchar * const *caps_properties,
380     GstStructure * logged_fields_struct,
381     GstStructure * ignored_fields_struct,
382     const gchar * const *ignored_event_types,
383     const gchar * const *logged_event_types)
384 {
385   const gchar *event_type;
386   gchar *structure_string;
387   gchar *event_string;
388   gchar **ignored_fields;
389   gchar **logged_fields;
390
391   event_type = gst_event_type_get_name (GST_EVENT_TYPE (event));
392
393   if (logged_event_types && !g_strv_contains (logged_event_types, event_type))
394     return NULL;
395
396   if (ignored_event_types && g_strv_contains (ignored_event_types, event_type))
397     return NULL;
398
399   logged_fields =
400       logged_fields_struct ? gst_validate_utils_get_strv (logged_fields_struct,
401       event_type) : NULL;
402   ignored_fields =
403       ignored_fields_struct ?
404       gst_validate_utils_get_strv (ignored_fields_struct, event_type) : NULL;
405   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
406     const GstSegment *segment;
407     gst_event_parse_segment (event, &segment);
408     structure_string =
409         validate_flow_format_segment (segment, logged_fields, ignored_fields);
410   } else if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
411     GstCaps *caps;
412     gst_event_parse_caps (event, &caps);
413
414     structure_string =
415         validate_flow_format_caps (caps,
416         logged_fields ? logged_fields : (gchar **) caps_properties);
417     /* FIXME: Remove spurious `;` and regenerate all the expectation files */
418     event_string = g_strdup_printf ("%s: %s;", event_type, structure_string);
419     goto done;
420   } else if (!gst_event_get_structure (event)) {
421     structure_string = g_strdup ("(no structure)");
422   } else {
423     GstStructure *structure =
424         validate_flow_structure_cleanup (gst_event_get_structure (event),
425         logged_fields, ignored_fields);
426     structure_string = gst_structure_to_string (structure);
427     gst_structure_free (structure);
428   }
429
430   event_string = g_strdup_printf ("%s: %s", event_type, structure_string);
431 done:
432   g_strfreev (logged_fields);
433   g_strfreev (ignored_fields);
434   g_free (structure_string);
435   return event_string;
436 }