New validate plugin: validateflow
[platform/upstream/gstreamer.git] / validate / plugins / 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 #include "formatting.h"
27
28 #include <gst/gst.h>
29 #include <string.h>
30 #include <stdio.h>
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 typedef void (*Uint64Formatter) (gchar * dest, guint64 time);
37
38 void
39 format_time (gchar * dest_str, guint64 time)
40 {
41   if (GST_CLOCK_TIME_IS_VALID (time)) {
42     sprintf (dest_str, "%" GST_TIME_FORMAT, GST_TIME_ARGS (time));
43   } else {
44     strcpy (dest_str, "none");
45   }
46 }
47
48 static void
49 format_number (gchar * dest_str, guint64 number)
50 {
51   sprintf (dest_str, "%" G_GUINT64_FORMAT, number);
52 }
53
54 gchar *
55 validate_flow_format_segment (const GstSegment * segment)
56 {
57   Uint64Formatter uint64_format;
58   gchar *segment_str;
59   gchar *parts[7];
60   GString *format;
61   gchar start_str[32], offset_str[32], stop_str[32], time_str[32], base_str[32],
62       position_str[32], duration_str[32];
63   int parts_index = 0;
64
65   uint64_format =
66       segment->format == GST_FORMAT_TIME ? format_time : format_number;
67   uint64_format (start_str, segment->start);
68   uint64_format (offset_str, segment->offset);
69   uint64_format (stop_str, segment->stop);
70   uint64_format (time_str, segment->time);
71   uint64_format (base_str, segment->base);
72   uint64_format (position_str, segment->position);
73   uint64_format (duration_str, segment->duration);
74
75   format = g_string_new (gst_format_get_name (segment->format));
76   format = g_string_ascii_up (format);
77   parts[parts_index++] =
78       g_strdup_printf ("format=%s, start=%s, offset=%s, stop=%s", format->str,
79       start_str, offset_str, stop_str);
80   if (segment->rate != 1.0)
81     parts[parts_index++] = g_strdup_printf ("rate=%f", segment->rate);
82   if (segment->applied_rate != 1.0)
83     parts[parts_index++] =
84         g_strdup_printf ("applied_rate=%f", segment->applied_rate);
85   if (segment->flags)
86     parts[parts_index++] = g_strdup_printf ("flags=0x%02x", segment->flags);
87   parts[parts_index++] =
88       g_strdup_printf ("time=%s, base=%s, position=%s", time_str, base_str,
89       position_str);
90   if (GST_CLOCK_TIME_IS_VALID (segment->duration))
91     parts[parts_index++] = g_strdup_printf ("duration=%s", duration_str);
92   parts[parts_index] = NULL;
93
94   segment_str = g_strjoinv (", ", parts);
95
96   while (parts_index > 0)
97     g_free (parts[--parts_index]);
98   g_string_free (format, TRUE);
99
100   return segment_str;
101 }
102
103 static gboolean
104 structure_only_given_keys (GQuark field_id, GValue * value,
105     gpointer _keys_to_print)
106 {
107   const gchar *const *keys_to_print = (const gchar * const *) _keys_to_print;
108   return (!keys_to_print
109       || g_strv_contains (keys_to_print, g_quark_to_string (field_id)));
110 }
111
112 static void
113 gpointer_free (gpointer pointer_location)
114 {
115   g_free (*(void **) pointer_location);
116 }
117
118 gchar *
119 validate_flow_format_caps (const GstCaps * caps,
120     const gchar * const *keys_to_print)
121 {
122   guint i;
123   GArray *structures_strv = g_array_new (TRUE, FALSE, sizeof (gchar *));
124   gchar *caps_str;
125
126   g_array_set_clear_func (structures_strv, gpointer_free);
127
128   /* A single GstCaps can contain several caps structures (although only one is
129    * used in most cases). We will print them separated with spaces. */
130   for (i = 0; i < gst_caps_get_size (caps); i++) {
131     GstStructure *structure =
132         gst_structure_copy (gst_caps_get_structure (caps, i));
133     gchar *structure_str;
134     gst_structure_filter_and_map_in_place (structure, structure_only_given_keys,
135         (gpointer) keys_to_print);
136     structure_str = gst_structure_to_string (structure);
137     g_array_append_val (structures_strv, structure_str);
138   }
139
140   caps_str = g_strjoinv (" ", (gchar **) structures_strv->data);
141   g_array_free (structures_strv, TRUE);
142   return caps_str;
143 }
144
145
146 static gchar *
147 buffer_get_flags_string (GstBuffer * buffer)
148 {
149   GFlagsClass *flags_class =
150       G_FLAGS_CLASS (g_type_class_ref (gst_buffer_flags_get_type ()));
151   GstBufferFlags flags = GST_BUFFER_FLAGS (buffer);
152   GString *string = NULL;
153
154   while (1) {
155     GFlagsValue *value = g_flags_get_first_value (flags_class, flags);
156     if (!value)
157       break;
158
159     if (string == NULL)
160       string = g_string_new (NULL);
161     else
162       g_string_append (string, " ");
163
164     g_string_append (string, value->value_nick);
165     flags &= ~value->value;
166   }
167
168   return (string != NULL) ? g_string_free (string, FALSE) : NULL;
169 }
170
171 /* Returns a newly-allocated string describing the metas on this buffer, or NULL */
172 static gchar *
173 buffer_get_meta_string (GstBuffer * buffer)
174 {
175   gpointer state = NULL;
176   GstMeta *meta;
177   GString *s = NULL;
178
179   while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
180     const gchar *desc = g_type_name (meta->info->type);
181
182     if (s == NULL)
183       s = g_string_new (NULL);
184     else
185       g_string_append (s, ", ");
186
187     g_string_append (s, desc);
188   }
189
190   return (s != NULL) ? g_string_free (s, FALSE) : NULL;
191 }
192
193 gchar *
194 validate_flow_format_buffer (GstBuffer * buffer)
195 {
196   gchar *flags_str, *meta_str, *buffer_str;
197   gchar *buffer_parts[6];
198   int buffer_parts_index = 0;
199
200   if (GST_CLOCK_TIME_IS_VALID (buffer->dts)) {
201     gchar time_str[32];
202     format_time (time_str, buffer->dts);
203     buffer_parts[buffer_parts_index++] = g_strdup_printf ("dts=%s", time_str);
204   }
205
206   if (GST_CLOCK_TIME_IS_VALID (buffer->pts)) {
207     gchar time_str[32];
208     format_time (time_str, buffer->pts);
209     buffer_parts[buffer_parts_index++] = g_strdup_printf ("pts=%s", time_str);
210   }
211
212   if (GST_CLOCK_TIME_IS_VALID (buffer->duration)) {
213     gchar time_str[32];
214     format_time (time_str, buffer->duration);
215     buffer_parts[buffer_parts_index++] = g_strdup_printf ("dur=%s", time_str);
216   }
217
218   flags_str = buffer_get_flags_string (buffer);
219   if (flags_str) {
220     buffer_parts[buffer_parts_index++] =
221         g_strdup_printf ("flags=%s", flags_str);
222   }
223
224   meta_str = buffer_get_meta_string (buffer);
225   if (meta_str)
226     buffer_parts[buffer_parts_index++] = g_strdup_printf ("meta=%s", meta_str);
227
228   buffer_parts[buffer_parts_index] = NULL;
229   buffer_str =
230       buffer_parts_index > 0 ? g_strjoinv (", ",
231       buffer_parts) : g_strdup ("(empty)");
232
233   g_free (meta_str);
234   g_free (flags_str);
235   while (buffer_parts_index > 0)
236     g_free (buffer_parts[--buffer_parts_index]);
237
238   return buffer_str;
239 }
240
241 gchar *
242 validate_flow_format_event (GstEvent * event, gboolean allow_stream_id,
243     const gchar * const *caps_properties)
244 {
245   const gchar *event_type;
246   gchar *structure_string;
247   gchar *event_string;
248
249   event_type = gst_event_type_get_name (GST_EVENT_TYPE (event));
250
251   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
252     const GstSegment *segment;
253     gst_event_parse_segment (event, &segment);
254     structure_string = validate_flow_format_segment (segment);
255   } else if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
256     GstCaps *caps;
257     gst_event_parse_caps (event, &caps);
258     structure_string = validate_flow_format_caps (caps, caps_properties);
259   } else if (!gst_event_get_structure (event)) {
260     structure_string = g_strdup ("(no structure)");
261   } else {
262     GstStructure *printable =
263         gst_structure_copy (gst_event_get_structure (event));
264
265     if (GST_EVENT_TYPE (event) == GST_EVENT_STREAM_START && !allow_stream_id)
266       gst_structure_remove_fields (printable, "stream-id", NULL);
267
268     structure_string = gst_structure_to_string (printable);
269     gst_structure_free (printable);
270   }
271
272   event_string = g_strdup_printf ("%s: %s", event_type, structure_string);
273   g_free (structure_string);
274   return event_string;
275 }