gstreamer/validate/plugins/flow/formatting.c
Thibault Saunier 5b82274f17 validate:flow: Add a way to set the types of events to log/ignore
Added two properties to the plugin:
     * ignored-event-types: A list of event types to be ignored when logging events
     * logged-event-types: A list of event types to be logged when logging events

This commits also moves the "ignored-event-fields" property to using a proper
GstValueList for the list of event fields to be taken into account, instead
of the home grown separated by comas list of string, making the API more
uniform.

This also adds a simple helper method: `gst_validate_utils_get_strv`
2020-02-04 16:09:36 -03:00

309 lines
9.1 KiB
C

/* GStreamer
*
* Copyright (C) 2018-2019 Igalia S.L.
* Copyright (C) 2018 Metrological Group B.V.
* Author: Alicia Boya García <aboya@igalia.com>
*
* formatting.c: Functions used by validateflow to get string
* representations of buffers.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "formatting.h"
#include <gst/gst.h>
#include <string.h>
#include <stdio.h>
#include "../../gst/validate/gst-validate-utils.h"
typedef void (*Uint64Formatter) (gchar * dest, guint64 time);
void
format_time (gchar * dest_str, guint64 time)
{
if (GST_CLOCK_TIME_IS_VALID (time)) {
sprintf (dest_str, "%" GST_TIME_FORMAT, GST_TIME_ARGS (time));
} else {
strcpy (dest_str, "none");
}
}
static void
format_number (gchar * dest_str, guint64 number)
{
sprintf (dest_str, "%" G_GUINT64_FORMAT, number);
}
gchar *
validate_flow_format_segment (const GstSegment * segment)
{
Uint64Formatter uint64_format;
gchar *segment_str;
gchar *parts[7];
GString *format;
gchar start_str[32], offset_str[32], stop_str[32], time_str[32], base_str[32],
position_str[32], duration_str[32];
int parts_index = 0;
uint64_format =
segment->format == GST_FORMAT_TIME ? format_time : format_number;
uint64_format (start_str, segment->start);
uint64_format (offset_str, segment->offset);
uint64_format (stop_str, segment->stop);
uint64_format (time_str, segment->time);
uint64_format (base_str, segment->base);
uint64_format (position_str, segment->position);
uint64_format (duration_str, segment->duration);
format = g_string_new (gst_format_get_name (segment->format));
format = g_string_ascii_up (format);
parts[parts_index++] =
g_strdup_printf ("format=%s, start=%s, offset=%s, stop=%s", format->str,
start_str, offset_str, stop_str);
if (segment->rate != 1.0)
parts[parts_index++] = g_strdup_printf ("rate=%f", segment->rate);
if (segment->applied_rate != 1.0)
parts[parts_index++] =
g_strdup_printf ("applied_rate=%f", segment->applied_rate);
if (segment->flags)
parts[parts_index++] = g_strdup_printf ("flags=0x%02x", segment->flags);
parts[parts_index++] =
g_strdup_printf ("time=%s, base=%s, position=%s", time_str, base_str,
position_str);
if (GST_CLOCK_TIME_IS_VALID (segment->duration))
parts[parts_index++] = g_strdup_printf ("duration=%s", duration_str);
parts[parts_index] = NULL;
segment_str = g_strjoinv (", ", parts);
while (parts_index > 0)
g_free (parts[--parts_index]);
g_string_free (format, TRUE);
return segment_str;
}
static gboolean
structure_only_given_keys (GQuark field_id, GValue * value,
gpointer _keys_to_print)
{
const gchar *const *keys_to_print = (const gchar * const *) _keys_to_print;
return (!keys_to_print
|| g_strv_contains (keys_to_print, g_quark_to_string (field_id)));
}
static void
gpointer_free (gpointer pointer_location)
{
g_free (*(void **) pointer_location);
}
gchar *
validate_flow_format_caps (const GstCaps * caps,
const gchar * const *keys_to_print)
{
guint i;
GArray *structures_strv = g_array_new (TRUE, FALSE, sizeof (gchar *));
gchar *caps_str;
g_array_set_clear_func (structures_strv, gpointer_free);
/* A single GstCaps can contain several caps structures (although only one is
* used in most cases). We will print them separated with spaces. */
for (i = 0; i < gst_caps_get_size (caps); i++) {
GstStructure *structure =
gst_structure_copy (gst_caps_get_structure (caps, i));
gchar *structure_str;
gst_structure_filter_and_map_in_place (structure, structure_only_given_keys,
(gpointer) keys_to_print);
structure_str = gst_structure_to_string (structure);
g_array_append_val (structures_strv, structure_str);
gst_structure_free (structure);
}
caps_str = g_strjoinv (" ", (gchar **) structures_strv->data);
g_array_free (structures_strv, TRUE);
return caps_str;
}
static gchar *
buffer_get_flags_string (GstBuffer * buffer)
{
GFlagsClass *flags_class =
G_FLAGS_CLASS (g_type_class_ref (gst_buffer_flags_get_type ()));
GstBufferFlags flags = GST_BUFFER_FLAGS (buffer);
GString *string = NULL;
while (1) {
GFlagsValue *value = g_flags_get_first_value (flags_class, flags);
if (!value)
break;
if (string == NULL)
string = g_string_new (NULL);
else
g_string_append (string, " ");
g_string_append (string, value->value_nick);
flags &= ~value->value;
}
return (string != NULL) ? g_string_free (string, FALSE) : NULL;
}
/* Returns a newly-allocated string describing the metas on this buffer, or NULL */
static gchar *
buffer_get_meta_string (GstBuffer * buffer)
{
gpointer state = NULL;
GstMeta *meta;
GString *s = NULL;
while ((meta = gst_buffer_iterate_meta (buffer, &state))) {
const gchar *desc = g_type_name (meta->info->type);
if (s == NULL)
s = g_string_new (NULL);
else
g_string_append (s, ", ");
g_string_append (s, desc);
}
return (s != NULL) ? g_string_free (s, FALSE) : NULL;
}
gchar *
validate_flow_format_buffer (GstBuffer * buffer, gboolean add_checksum)
{
gchar *flags_str, *meta_str, *buffer_str;
gchar *buffer_parts[7];
int buffer_parts_index = 0;
gchar *sum;
GstMapInfo map;
if (add_checksum) {
if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) {
GST_ERROR ("Buffer could not be mapped.");
} else {
sum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, map.data, map.size);
gst_buffer_unmap (buffer, &map);
buffer_parts[buffer_parts_index++] = g_strdup_printf ("checksum=%s", sum);
g_free (sum);
}
}
if (GST_CLOCK_TIME_IS_VALID (buffer->dts)) {
gchar time_str[32];
format_time (time_str, buffer->dts);
buffer_parts[buffer_parts_index++] = g_strdup_printf ("dts=%s", time_str);
}
if (GST_CLOCK_TIME_IS_VALID (buffer->pts)) {
gchar time_str[32];
format_time (time_str, buffer->pts);
buffer_parts[buffer_parts_index++] = g_strdup_printf ("pts=%s", time_str);
}
if (GST_CLOCK_TIME_IS_VALID (buffer->duration)) {
gchar time_str[32];
format_time (time_str, buffer->duration);
buffer_parts[buffer_parts_index++] = g_strdup_printf ("dur=%s", time_str);
}
flags_str = buffer_get_flags_string (buffer);
if (flags_str) {
buffer_parts[buffer_parts_index++] =
g_strdup_printf ("flags=%s", flags_str);
}
meta_str = buffer_get_meta_string (buffer);
if (meta_str)
buffer_parts[buffer_parts_index++] = g_strdup_printf ("meta=%s", meta_str);
buffer_parts[buffer_parts_index] = NULL;
buffer_str =
buffer_parts_index > 0 ? g_strjoinv (", ",
buffer_parts) : g_strdup ("(empty)");
g_free (meta_str);
g_free (flags_str);
while (buffer_parts_index > 0)
g_free (buffer_parts[--buffer_parts_index]);
return buffer_str;
}
gchar *
validate_flow_format_event (GstEvent * event,
const gchar * const *caps_properties, GstStructure * ignored_event_fields,
const gchar * const *ignored_event_types,
const gchar * const *logged_event_types)
{
const gchar *event_type;
gchar *structure_string;
gchar *event_string;
gchar **ignored_fields;
event_type = gst_event_type_get_name (GST_EVENT_TYPE (event));
if (logged_event_types && !g_strv_contains (logged_event_types, event_type))
return NULL;
if (ignored_event_types && g_strv_contains (ignored_event_types, event_type))
return NULL;
if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
const GstSegment *segment;
gst_event_parse_segment (event, &segment);
structure_string = validate_flow_format_segment (segment);
} else if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
GstCaps *caps;
gst_event_parse_caps (event, &caps);
structure_string = validate_flow_format_caps (caps, caps_properties);
} else if (!gst_event_get_structure (event)) {
structure_string = g_strdup ("(no structure)");
} else {
GstStructure *printable =
gst_structure_copy (gst_event_get_structure (event));
ignored_fields =
gst_validate_utils_get_strv (ignored_event_fields, event_type);
if (ignored_fields) {
gint i = 0;
gchar *field;
for (field = ignored_fields[i]; field; field = ignored_fields[++i])
gst_structure_remove_field (printable, field);
g_strfreev (ignored_fields);
}
structure_string = gst_structure_to_string (printable);
gst_structure_free (printable);
}
event_string = g_strdup_printf ("%s: %s", event_type, structure_string);
g_free (structure_string);
return event_string;
}