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>
7 * formatting.c: Functions used by validateflow to get string
8 * representations of buffers.
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.
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.
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.
30 #include "formatting.h"
33 #include <gst/video/video.h>
36 #include <glib/gprintf.h>
38 #include "../../gst/validate/gst-validate-utils.h"
40 typedef void (*Uint64Formatter) (gchar * dest, guint64 time);
41 G_LOCK_DEFINE (checksums_as_id_lock);
42 static GstStructure *checksums_as_id = NULL;
44 #define CONSTIFY(strv) ((const gchar * const *) strv)
47 use_field (const gchar * field, gchar ** logged, gchar ** ignored)
50 return g_strv_contains (CONSTIFY (logged), field);
53 return !g_strv_contains (CONSTIFY (ignored), field);
60 format_time (gchar * dest_str, guint64 time)
62 if (GST_CLOCK_TIME_IS_VALID (time)) {
63 g_sprintf (dest_str, "%" GST_TIME_FORMAT, GST_TIME_ARGS (time));
65 strcpy (dest_str, "none");
70 format_number (gchar * dest_str, guint64 number)
72 g_sprintf (dest_str, "%" G_GUINT64_FORMAT, number);
76 validate_flow_format_segment (const GstSegment * segment,
77 gchar ** logged_fields, gchar ** ignored_fields)
79 Uint64Formatter uint64_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];
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);
97 format = g_string_new (gst_format_get_name (segment->format));
98 format = g_string_ascii_up (format);
100 if (use_field ("format", logged_fields, ignored_fields))
101 parts[parts_index++] = g_strdup_printf ("format=%s", format->str);
103 if (use_field ("start", logged_fields, ignored_fields))
104 parts[parts_index++] = g_strdup_printf ("start=%s", start_str);
106 if (use_field ("offset", logged_fields, ignored_fields))
107 parts[parts_index++] = g_strdup_printf ("offset=%s", offset_str);
109 if (use_field ("stop", logged_fields, ignored_fields))
110 parts[parts_index++] = g_strdup_printf ("stop=%s", stop_str);
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);
118 if (segment->flags && use_field ("flags", logged_fields, ignored_fields))
119 parts[parts_index++] = g_strdup_printf ("flags=0x%02x", segment->flags);
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;
132 segment_str = g_strjoinv (", ", parts);
134 while (parts_index > 0)
135 g_free (parts[--parts_index]);
136 g_string_free (format, TRUE);
145 gchar **wanted_fields;
146 gchar **ignored_fields;
150 structure_set_fields (GQuark field_id, GValue * value, StructureValues * data)
152 const gchar *field = g_quark_to_string (field_id);
154 if (data->ignored_fields
155 && g_strv_contains ((const gchar **) data->ignored_fields, field))
158 if (data->wanted_fields
159 && !g_strv_contains ((const gchar **) data->wanted_fields, field))
162 data->fields = g_list_prepend (data->fields, (gchar *) field);
167 static GstStructure *
168 validate_flow_structure_cleanup (const GstStructure * structure,
169 gchar ** wanted_fields, gchar ** ignored_fields)
171 GstStructure *nstructure;
172 StructureValues d = {
174 .wanted_fields = wanted_fields,
175 .ignored_fields = ignored_fields,
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;
185 gst_structure_set_value (nstructure, field,
186 gst_structure_get_value (structure, field));
189 g_list_free (d.fields);
195 validate_flow_format_caps (const GstCaps * caps, gchar ** wanted_fields)
198 GstCaps *new_caps = gst_caps_new_empty ();
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);
208 gst_caps_append_structure_full (new_caps, structure,
209 gst_caps_features_copy (gst_caps_get_features (caps, i)));
212 caps_str = gst_caps_to_string (new_caps);
213 gst_caps_unref (new_caps);
220 buffer_get_flags_string (GstBuffer * buffer)
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;
228 GFlagsValue *value = g_flags_get_first_value (flags_class, flags);
233 string = g_string_new (NULL);
235 g_string_append (string, " ");
237 g_string_append (string, value->value_nick);
238 flags &= ~value->value;
241 return (string != NULL) ? g_string_free (string, FALSE) : NULL;
244 /* Returns a newly-allocated string describing the metas on this buffer, or NULL */
246 buffer_get_meta_string (GstBuffer * buffer)
248 gpointer state = NULL;
252 while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
253 const gchar *desc = g_type_name (meta->info->type);
256 s = g_string_new (NULL);
258 g_string_append (s, ", ");
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);
267 g_string_append (s, desc);
271 return (s != NULL) ? g_string_free (s, FALSE) : NULL;
275 validate_flow_format_buffer (GstBuffer * buffer, gint checksum_type,
276 GstStructure * logged_fields_struct, GstStructure * ignored_fields_struct)
278 gchar *flags_str, *meta_str, *buffer_str;
279 gchar *buffer_parts[7];
280 int buffer_parts_index = 0;
282 gchar **logged_fields =
283 logged_fields_struct ? gst_validate_utils_get_strv (logged_fields_struct,
285 gchar **ignored_fields =
286 ignored_fields_struct ?
287 gst_validate_utils_get_strv (ignored_fields_struct, "buffer") : NULL;
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) {
295 GString *content = g_string_new ("content=");
297 for (i = 0; i < map.size; i++) {
299 g_string_append_c (content, ' ');
300 g_string_append_printf (content, "0x%02x", map.data[i]);
303 buffer_parts[buffer_parts_index++] = g_string_free (content, FALSE);
306 g_compute_checksum_for_data (checksum_type ==
307 CHECKSUM_TYPE_AS_ID ? G_CHECKSUM_SHA1 : checksum_type, map.data,
309 gst_buffer_unmap (buffer, &map);
311 if (checksum_type == CHECKSUM_TYPE_AS_ID) {
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);
321 G_UNLOCK (checksums_as_id_lock);
323 buffer_parts[buffer_parts_index++] =
324 g_strdup_printf ("content-id=%d", id);
326 buffer_parts[buffer_parts_index++] =
327 g_strdup_printf ("checksum=%s", sum);
333 if (GST_CLOCK_TIME_IS_VALID (buffer->dts)
334 && use_field ("dts", logged_fields, ignored_fields)) {
336 format_time (time_str, buffer->dts);
337 buffer_parts[buffer_parts_index++] = g_strdup_printf ("dts=%s", time_str);
340 if (GST_CLOCK_TIME_IS_VALID (buffer->pts)
341 && use_field ("pts", logged_fields, ignored_fields)) {
343 format_time (time_str, buffer->pts);
344 buffer_parts[buffer_parts_index++] = g_strdup_printf ("pts=%s", time_str);
347 if (GST_CLOCK_TIME_IS_VALID (buffer->duration)
348 && use_field ("dur", logged_fields, ignored_fields)) {
350 format_time (time_str, buffer->duration);
351 buffer_parts[buffer_parts_index++] = g_strdup_printf ("dur=%s", time_str);
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);
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);
364 buffer_parts[buffer_parts_index] = NULL;
366 buffer_parts_index > 0 ? g_strjoinv (", ",
367 buffer_parts) : g_strdup ("(empty)");
371 while (buffer_parts_index > 0)
372 g_free (buffer_parts[--buffer_parts_index]);
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)
385 const gchar *event_type;
386 gchar *structure_string;
388 gchar **ignored_fields;
389 gchar **logged_fields;
391 event_type = gst_event_type_get_name (GST_EVENT_TYPE (event));
393 if (logged_event_types && !g_strv_contains (logged_event_types, event_type))
396 if (ignored_event_types && g_strv_contains (ignored_event_types, event_type))
400 logged_fields_struct ? gst_validate_utils_get_strv (logged_fields_struct,
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);
409 validate_flow_format_segment (segment, logged_fields, ignored_fields);
410 } else if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
412 gst_event_parse_caps (event, &caps);
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);
420 } else if (!gst_event_get_structure (event)) {
421 structure_string = g_strdup ("(no structure)");
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);
430 event_string = g_strdup_printf ("%s: %s", event_type, structure_string);
432 g_strfreev (logged_fields);
433 g_strfreev (ignored_fields);
434 g_free (structure_string);