/* WirePlumber * * Copyright © 2020 Collabora Ltd. * @author Julian Bouzas * * SPDX-License-Identifier: MIT */ #include "spa-pod.h" #include "spa-type.h" #include "log.h" #include #include #include #include WP_DEFINE_LOCAL_LOG_TOPIC ("wp-spa-pod") #define WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE 64 #define WP_SPA_POD_ID_PROPERTY_NAME_MAX 16 /*! \defgroup wpspapod WpSpaPod */ /*! * \struct WpSpaPod */ /*! * \struct WpSpaPodBuilder */ /*! * \struct WpSpaPodParser */ enum { FLAG_NO_OWNERSHIP = (1 << 0), FLAG_CONSTANT = (1 << 1) }; typedef enum { WP_SPA_POD_REGULAR = 0, WP_SPA_POD_PROPERTY, WP_SPA_POD_CONTROL, } WpSpaPodType; struct _WpSpaPod { grefcount ref; guint32 flags; /* The pipewire spa pod API does not have a type for Property and Control, * so we create our own and separate them with their data from the regular * spa pod types */ WpSpaPodType type; /* Pod */ union { struct spa_pod pod_none; struct spa_pod_bool pod_bool; struct spa_pod_id pod_id; struct spa_pod_int pod_int; struct spa_pod_long pod_long; struct spa_pod_float pod_float; struct spa_pod_double pod_double; struct spa_pod_pointer pod_pointer; struct spa_pod_fd pod_fd; struct spa_pod_rectangle pod_rectangle; struct spa_pod_fraction pod_fraction; struct wp_property_data { WpSpaIdTable table; guint32 key; guint32 flags; gchar id_key_name[WP_SPA_POD_ID_PROPERTY_NAME_MAX]; } data_property; /* Only used for property pods */ struct wp_control_data { guint32 offset; enum spa_control_type type; } data_control; /* Only used for control pods */ } static_pod; /* Only used for statically allocated pods */ WpSpaPodBuilder *builder; /* Only used for dynamically allocated pods */ struct spa_pod *pod; }; G_DEFINE_BOXED_TYPE (WpSpaPod, wp_spa_pod, wp_spa_pod_ref, wp_spa_pod_unref) struct _WpSpaPodBuilder { struct spa_pod_builder builder; struct spa_pod_frame frame; WpSpaType type; size_t size; guint8 *buf; }; G_DEFINE_BOXED_TYPE (WpSpaPodBuilder, wp_spa_pod_builder, wp_spa_pod_builder_ref, wp_spa_pod_builder_unref) struct _WpSpaPodParser { struct spa_pod_parser parser; struct spa_pod_frame frame; WpSpaType type; WpSpaPod *pod; }; G_DEFINE_BOXED_TYPE (WpSpaPodParser, wp_spa_pod_parser, wp_spa_pod_parser_ref, wp_spa_pod_parser_unref) static int wp_spa_pod_builder_overflow (gpointer data, uint32_t size) { WpSpaPodBuilder *self = data; const uint32_t next_size = self->size + WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE; const uint32_t new_size = size > next_size ? size : next_size; self->buf = g_realloc (self->buf, new_size); self->builder.data = self->buf; self->builder.size = new_size; self->size = new_size; return 0; } static const struct spa_pod_builder_callbacks builder_callbacks = { SPA_VERSION_POD_BUILDER_CALLBACKS, .overflow = wp_spa_pod_builder_overflow }; static WpSpaPodBuilder * wp_spa_pod_builder_new (size_t size, WpSpaType type) { WpSpaPodBuilder *self = g_rc_box_new0 (WpSpaPodBuilder); self->size = size; self->buf = g_new0 (guint8, self->size); self->builder = SPA_POD_BUILDER_INIT (self->buf, self->size); self->type = type; spa_pod_builder_set_callbacks (&self->builder, &builder_callbacks, self); return self; } /*! * \brief Increases the reference count of a spa pod object * \ingroup wpspapod * \param self a spa pod object * \returns (transfer full): \a self with an additional reference count on it */ WpSpaPod * wp_spa_pod_ref (WpSpaPod *self) { g_ref_count_inc (&self->ref); return self; } static void wp_spa_pod_free (WpSpaPod *self) { g_clear_pointer (&self->builder, wp_spa_pod_builder_unref); self->pod = NULL; g_slice_free (WpSpaPod, self); } /*! * \brief Decreases the reference count on \a self and frees it when the ref * count reaches zero. * \ingroup wpspapod * \param self (transfer full): a spa pod object */ void wp_spa_pod_unref (WpSpaPod *self) { if (g_ref_count_dec (&self->ref)) wp_spa_pod_free (self); } static WpSpaPod * wp_spa_pod_new (const struct spa_pod *pod, WpSpaPodType type, guint32 flags) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->flags = flags; self->type = type; /* Copy the reference if no ownership, otherwise copy the pod */ if (self->flags & FLAG_NO_OWNERSHIP) { self->pod = (struct spa_pod *)pod; } else { self->builder = wp_spa_pod_builder_new ( SPA_ROUND_UP_N (sizeof (*pod) + pod->size, 8), pod->type); self->pod = self->builder->builder.data; spa_pod_builder_primitive (&self->builder->builder, pod); } /* Set the prop table if it is an object */ if (pod->type == SPA_TYPE_Object) { self->static_pod.data_property.table = wp_spa_type_get_values_table (((struct spa_pod_object *) pod)->body.type); } return self; } /*! * \brief Constructs a new WpSpaPod that wraps the given `spa_pod`. * * \ingroup wpspapod * \param pod a spa_pod * \returns a new WpSpaPod that references the data in \a pod. \a pod is not * copied, so it needs to stay alive. The returned WpSpaPod can be modified * by using the setter functions, in which case \a pod will be modified * underneath. */ WpSpaPod * wp_spa_pod_new_wrap (struct spa_pod *pod) { return wp_spa_pod_new (pod, WP_SPA_POD_REGULAR, FLAG_NO_OWNERSHIP); } /*! * \brief Constructs a new immutable WpSpaPod that wraps the given `spa_pod`. * * \ingroup wpspapod * \param pod a constant spa_pod * \returns a new WpSpaPod that references the data in \a pod. \a pod is not * copied, so it needs to stay alive. The returned WpSpaPod cannot be * modified, unless it's copied first. */ WpSpaPod * wp_spa_pod_new_wrap_const (const struct spa_pod *pod) { return wp_spa_pod_new (pod, WP_SPA_POD_REGULAR, FLAG_NO_OWNERSHIP | FLAG_CONSTANT); } static WpSpaPod * wp_spa_pod_new_property_wrap (WpSpaIdTable table, guint32 key, guint32 flags, struct spa_pod *pod) { WpSpaPod *self = wp_spa_pod_new (pod, WP_SPA_POD_PROPERTY, FLAG_NO_OWNERSHIP); self->static_pod.data_property.table = table; self->static_pod.data_property.key = key; self->static_pod.data_property.flags = flags; return self; } static WpSpaPod * wp_spa_pod_new_control_wrap (guint32 offset, enum spa_control_type type, struct spa_pod *pod) { WpSpaPod *self = wp_spa_pod_new (pod, WP_SPA_POD_CONTROL, FLAG_NO_OWNERSHIP); self->static_pod.data_control.offset = offset; self->static_pod.data_control.type = type; return self; } #if 0 /* there is no use for these _const variants, but let's keep them just in case */ static WpSpaPod * wp_spa_pod_new_property_wrap_const (WpSpaIdTable table, guint32 key, guint32 flags, const struct spa_pod *pod) { WpSpaPod *self = wp_spa_pod_new (pod, WP_SPA_POD_PROPERTY, FLAG_NO_OWNERSHIP | FLAG_CONSTANT); self->static_pod.data_property.table = table; self->static_pod.data_property.key = key; self->static_pod.data_property.flags = flags; return self; } static WpSpaPod * wp_spa_pod_new_control_wrap_const (guint32 offset, enum spa_control_type type, const struct spa_pod *pod) { WpSpaPod *self = wp_spa_pod_new (pod, WP_SPA_POD_CONTROL, FLAG_NO_OWNERSHIP | FLAG_CONSTANT); self->static_pod.data_control.offset = offset; self->static_pod.data_control.type = type; return self; } #endif static WpSpaPod * wp_spa_pod_new_wrap_copy (const struct spa_pod *pod) { return wp_spa_pod_new (pod, WP_SPA_POD_REGULAR, 0); } static WpSpaPod * wp_spa_pod_new_property_wrap_copy (WpSpaIdTable table, guint32 key, guint32 flags, const struct spa_pod *pod) { WpSpaPod *self = wp_spa_pod_new (pod, WP_SPA_POD_PROPERTY, 0); self->static_pod.data_property.table = table; self->static_pod.data_property.key = key; self->static_pod.data_property.flags = flags; return self; } static WpSpaPod * wp_spa_pod_new_control_wrap_copy (guint32 offset, enum spa_control_type type, const struct spa_pod *pod) { WpSpaPod *self = wp_spa_pod_new (pod, WP_SPA_POD_CONTROL, 0); self->static_pod.data_control.offset = offset; self->static_pod.data_control.type = type; return self; } /*! * \brief Converts a WpSpaPod pointer to a `struct spa_pod` one, for use with * native pipewire & spa functions. The returned pointer is owned by WpSpaPod * and may not be modified or freed. * * \ingroup wpspapod * \param self a spa pod object * \returns a const pointer to the underlying spa_pod structure */ const struct spa_pod * wp_spa_pod_get_spa_pod (const WpSpaPod *self) { return self->pod; } /*! * \brief Gets the SPA type of the spa pod. * * If the pod is an object or pointer, this will return the derived * object/pointer type directly. * If the pod is an object property or a control, this will return the type * of the contained value. * * \ingroup wpspapod * \param self a spa pod * \returns (transfer none): the type of the spa pod */ WpSpaType wp_spa_pod_get_spa_type (WpSpaPod *self) { g_return_val_if_fail (self != NULL, WP_SPA_TYPE_INVALID); if (wp_spa_pod_is_object (self) || wp_spa_pod_is_pointer (self)) return SPA_POD_OBJECT_TYPE (self->pod); else return SPA_POD_TYPE (self->pod); } /*! * \brief If the pod is a Choice, this gets the choice type * (Range, Step, Enum, ...) * * \ingroup wpspapod * \param self a choice pod * \returns the choice type of the choice pod */ WpSpaIdValue wp_spa_pod_get_choice_type (WpSpaPod *self) { g_return_val_if_fail (wp_spa_pod_is_choice (self), NULL); return wp_spa_id_value_from_number ( SPA_TYPE_INFO_Choice, SPA_POD_CHOICE_TYPE (self->pod)); } /*! * \brief Copies a spa pod object * * \ingroup wpspapod * \param other a spa pod object * \returns (transfer full): The newly copied spa pod */ WpSpaPod * wp_spa_pod_copy (WpSpaPod *other) { g_return_val_if_fail (other, NULL); switch (other->type) { case WP_SPA_POD_PROPERTY: return wp_spa_pod_new_property_wrap_copy ( other->static_pod.data_property.table, other->static_pod.data_property.key, other->static_pod.data_property.flags, other->pod); case WP_SPA_POD_CONTROL: return wp_spa_pod_new_control_wrap_copy ( other->static_pod.data_control.offset, other->static_pod.data_control.type, other->pod); case WP_SPA_POD_REGULAR: default: break; } return wp_spa_pod_new_wrap_copy (other->pod); } /*! * \brief Checks if the pod is the unique owner of its data or not * * \ingroup wpspapod * \param self a spa pod object * \returns TRUE if the pod owns the data, FALSE otherwise. */ gboolean wp_spa_pod_is_unique_owner (WpSpaPod *self) { return g_ref_count_compare (&self->ref, 1) && !(self->flags & FLAG_NO_OWNERSHIP); } /*! * \brief If \a self is not uniquely owned already, then it is unrefed and a * copy of it is returned instead. You should always consider \a self as unsafe * to use after this call and you should use the returned object instead. * * \ingroup wpspapod * \param self (transfer full): a spa pod object * \returns (transfer full): the uniquely owned spa pod object which may or may * not be the same as \a self. */ WpSpaPod * wp_spa_pod_ensure_unique_owner (WpSpaPod *self) { WpSpaPod *copy = NULL; if (wp_spa_pod_is_unique_owner (self)) return self; copy = wp_spa_pod_copy (self); wp_spa_pod_unref (self); return copy; } /*! * \brief Creates a spa pod of type None * \ingroup wpspapod * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_none (void) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_none = SPA_POD_INIT_None(); self->pod = &self->static_pod.pod_none; return self; } /*! * \brief Creates a spa pod of type boolean * * \ingroup wpspapod * \param value the boolean value * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_boolean (gboolean value) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_bool = SPA_POD_INIT_Bool (value ? true : false); self->pod = &self->static_pod.pod_bool.pod; return self; } /*! * \brief Creates a spa pod of type Id * * \ingroup wpspapod * \param value the Id value * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_id (guint32 value) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_id = SPA_POD_INIT_Id (value); self->pod = &self->static_pod.pod_id.pod; return self; } /*! * \brief Creates a spa pod of type int * * \ingroup wpspapod * \param value the int value * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_int (gint32 value) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_int = SPA_POD_INIT_Int (value); self->pod = &self->static_pod.pod_int.pod; return self; } /*! * \brief Creates a spa pod of type long * * \ingroup wpspapod * \param value the long value * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_long (gint64 value) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_long = SPA_POD_INIT_Long (value); self->pod = &self->static_pod.pod_long.pod; return self; } /*! * \brief Creates a spa pod of type float * * \ingroup wpspapod * \param value the float value * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_float (float value) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_float = SPA_POD_INIT_Float (value); self->pod = &self->static_pod.pod_float.pod; return self; } /*! * \brief Creates a spa pod of type double * * \ingroup wpspapod * \param value the double value * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_double (double value) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_double = SPA_POD_INIT_Double (value); self->pod = &self->static_pod.pod_double.pod; return self; } /*! * \brief Creates a spa pod of type string * * \ingroup wpspapod * \param value the string value * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_string (const char *value) { const uint32_t len = value ? strlen (value) : 0; const char *str = value ? value : ""; WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; struct spa_pod_string p = SPA_POD_INIT_String (len + 1); self->builder = wp_spa_pod_builder_new ( SPA_ROUND_UP_N (sizeof (p) + len + 1, 8), SPA_TYPE_String); self->pod = self->builder->builder.data; spa_pod_builder_raw (&self->builder->builder, &p, sizeof(p)); spa_pod_builder_write_string (&self->builder->builder, str, len); return self; } /*! * \brief Creates a spa pod of type bytes * * \ingroup wpspapod * \param value the bytes value * \param len the length of the bytes value * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_bytes (gconstpointer value, guint32 len) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; const struct spa_pod_bytes p = SPA_POD_INIT_Bytes (len); self->builder = wp_spa_pod_builder_new ( SPA_ROUND_UP_N (sizeof (p) + p.pod.size, 8), SPA_TYPE_Bytes); self->pod = self->builder->builder.data; spa_pod_builder_raw (&self->builder->builder, &p, sizeof(p)); spa_pod_builder_raw_padded (&self->builder->builder, value, len); return self; } /*! * \brief Creates a spa pod of type pointer * * \ingroup wpspapod * \param type_name the name of the type of the pointer * \param value the pointer value * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_pointer (const char *type_name, gconstpointer value) { WpSpaType type = wp_spa_type_from_name (type_name); g_return_val_if_fail (type != WP_SPA_TYPE_INVALID, NULL); WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_pointer = SPA_POD_INIT_Pointer (type, value); self->pod = &self->static_pod.pod_pointer.pod; return self; } /*! * \brief Creates a spa pod of type Fd * * \ingroup wpspapod * \param value the Fd value * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_fd (gint64 value) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_fd = SPA_POD_INIT_Fd (value); self->pod = &self->static_pod.pod_fd.pod; return self; } /*! * \brief Creates a spa pod of type rectangle * * \ingroup wpspapod * \param width the width value of the rectangle * \param height the height value of the rectangle * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_rectangle (guint32 width, guint32 height) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_rectangle = SPA_POD_INIT_Rectangle (SPA_RECTANGLE (width, height)); self->pod = &self->static_pod.pod_rectangle.pod; return self; } /*! * \brief Creates a spa pod of type fraction * * \ingroup wpspapod * \param num the numerator value of the fraction * \param denom the denominator value of the fraction * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_fraction (guint32 num, guint32 denom) { WpSpaPod *self = g_slice_new0 (WpSpaPod); g_ref_count_init (&self->ref); self->type = WP_SPA_POD_REGULAR; self->static_pod.pod_fraction = SPA_POD_INIT_Fraction (SPA_FRACTION (num, denom)); self->pod = &self->static_pod.pod_fraction.pod; return self; } /*! * \brief Creates a spa pod of type choice * * \ingroup wpspapod * \param choice_type the name of the choice type ("Range", "Step", ...), * \param ... a list of choice values, followed by NULL * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_choice (const char *choice_type, ...) { WpSpaPod *self; va_list args; va_start (args, choice_type); self = wp_spa_pod_new_choice_valist (choice_type, args); va_end (args); return self; } /*! * \brief This is the `va_list` version of wp_spa_pod_new_choice() * * \ingroup wpspapod * \param choice_type the name of the choice type ("Range", "Step", ...) * \param args the variable arguments passed to wp_spa_pod_new_choice() * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_choice_valist (const char *choice_type, va_list args) { g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_choice (choice_type); wp_spa_pod_builder_add_valist (b, args); return wp_spa_pod_builder_end (b); } /*! * \brief Creates a spa pod of type object * * \ingroup wpspapod * \param type_name the type name of the object type * \param id_name the id name of the object, * \param ... a list of object properties with their values, followed by NULL * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_object (const char *type_name, const char *id_name, ...) { WpSpaPod *self; va_list args; va_start (args, id_name); self = wp_spa_pod_new_object_valist (type_name, id_name, args); va_end (args); return self; } /*! * \brief This is the `va_list` version of wp_spa_pod_new_object() * * \ingroup wpspapod * \param type_name the type name of the object type * \param id_name the id name of the object * \param args the variable arguments passed to wp_spa_pod_new_object() * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_object_valist (const char *type_name, const char *id_name, va_list args) { g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_object (type_name, id_name); wp_spa_pod_builder_add_valist (b, args); return wp_spa_pod_builder_end (b); } /*! * \brief Creates a spa pod of type sequence * * \ingroup wpspapod * \param unit the unit of the sequence * \param ... a list of sequence controls with their values, followed by NULL * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_sequence (guint unit, ...) { WpSpaPod *self; va_list args; va_start(args, unit); self = wp_spa_pod_new_sequence_valist (unit, args); va_end(args); return self; } /*! * \brief This is the `va_list` version of wp_spa_pod_new_sequence() * * \ingroup wpspapod * \param unit the unit of the sequence * \param args the variable arguments passed to wp_spa_pod_new_sequence() * \returns (transfer full): The new spa pod */ WpSpaPod * wp_spa_pod_new_sequence_valist (guint unit, va_list args) { g_autoptr (WpSpaPodBuilder) b = wp_spa_pod_builder_new_sequence (unit); wp_spa_pod_builder_add_valist (b, args); return wp_spa_pod_builder_end (b); } /*! * \brief Checks wether the spa pod is of type none or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type none, FALSE otherwise */ gboolean wp_spa_pod_is_none (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_none (self->pod); } /*! * \brief Checks wether the spa pod is of type boolean or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type boolean, FALSE otherwise */ gboolean wp_spa_pod_is_boolean (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_bool (self->pod); } /*! * \brief Checks wether the spa pod is of type Id or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type Id, FALSE otherwise */ gboolean wp_spa_pod_is_id (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_id (self->pod); } /*! * \brief Checks wether the spa pod is of type int or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type int, FALSE otherwise */ gboolean wp_spa_pod_is_int (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_int (self->pod); } /*! * \brief Checks wether the spa pod is of type long or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type long, FALSE otherwise */ gboolean wp_spa_pod_is_long (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_long (self->pod); } /*! * \brief Checks wether the spa pod is of type float or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type float, FALSE otherwise */ gboolean wp_spa_pod_is_float (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_float (self->pod); } /*! * \brief Checks wether the spa pod is of type double or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type double, FALSE otherwise */ gboolean wp_spa_pod_is_double (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_double (self->pod); } /*! * \brief Checks wether the spa pod is of type string or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type string, FALSE otherwise */ gboolean wp_spa_pod_is_string (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_string (self->pod); } /*! * \brief Checks wether the spa pod is of type bytes or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type bytes, FALSE otherwise */ gboolean wp_spa_pod_is_bytes (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_bytes (self->pod); } /*! * \brief Checks wether the spa pod is of type pointer or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type pointer, FALSE otherwise */ gboolean wp_spa_pod_is_pointer (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_pointer (self->pod); } /*! * \brief Checks wether the spa pod is of type Fd or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type Fd, FALSE otherwise */ gboolean wp_spa_pod_is_fd (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_fd (self->pod); } /*! * \brief Checks wether the spa pod is of type rectangle or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type rectangle, FALSE otherwise */ gboolean wp_spa_pod_is_rectangle (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_rectangle (self->pod); } /*! * \brief Checks wether the spa pod is of type fraction or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type fraction, FALSE otherwise */ gboolean wp_spa_pod_is_fraction (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_fraction (self->pod); } /*! * \brief Checks wether the spa pod is of type array or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type array, FALSE otherwise */ gboolean wp_spa_pod_is_array (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_array (self->pod); } /*! * \brief Checks wether the spa pod is of type choice or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type choice, FALSE otherwise */ gboolean wp_spa_pod_is_choice (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_choice (self->pod); } /*! * \brief Checks wether the spa pod is of type object or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type object, FALSE otherwise */ gboolean wp_spa_pod_is_object (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_object (self->pod); } /*! * \brief Checks wether the spa pod is of type struct or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type struct, FALSE otherwise */ gboolean wp_spa_pod_is_struct (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_struct (self->pod); } /*! * \brief Checks wether the spa pod is of type sequence or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type sequence, FALSE otherwise */ gboolean wp_spa_pod_is_sequence (WpSpaPod *self) { return self->type == WP_SPA_POD_REGULAR && spa_pod_is_sequence (self->pod); } /*! * \brief Checks wether the spa pod is of type property or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type property, FALSE otherwise */ gboolean wp_spa_pod_is_property (WpSpaPod *self) { return self->type == WP_SPA_POD_PROPERTY; } /*! * \brief Checks wether the spa pod is of type control or not * * \ingroup wpspapod * \param self the spa pod object * \returns TRUE if it is of type control, FALSE otherwise */ gboolean wp_spa_pod_is_control (WpSpaPod *self) { return self->type == WP_SPA_POD_CONTROL; } /*! * \brief Gets the boolean value of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param value (out): the boolean value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_boolean (WpSpaPod *self, gboolean *value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (value, FALSE); bool v = FALSE; const int res = spa_pod_get_bool (self->pod, &v); *value = v ? TRUE : FALSE; return res >= 0; } /*! * \brief Gets the Id value of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param value (out): the Id value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_id (WpSpaPod *self, guint32 *value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (value, FALSE); uint32_t v = 0; const int res = spa_pod_get_id (self->pod, &v); *value = v; return res >= 0; } /*! * \brief Gets the int value of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param value (out): the int value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_int (WpSpaPod *self, gint32 *value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (value, FALSE); return spa_pod_get_int (self->pod, value) >= 0; } /*! * \brief Gets the long value of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param value (out): the long value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_long (WpSpaPod *self, gint64 *value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (value, FALSE); return spa_pod_get_long (self->pod, value) >= 0; } /*! * \brief Gets the float value of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param value (out): the float value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_float (WpSpaPod *self, float *value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (value, FALSE); return spa_pod_get_float (self->pod, value) >= 0; } /*! * \brief Gets the double value of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param value (out): the double value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_double (WpSpaPod *self, double *value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (value, FALSE); return spa_pod_get_double (self->pod, value) >= 0; } /*! * \brief Gets the string value of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param value (out): the string value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_string (WpSpaPod *self, const char **value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (value, FALSE); return spa_pod_get_string (self->pod, value) >= 0; } /*! * \brief Gets the bytes value and its len of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param value (out): the bytes value * \param len (out): the length of the bytes value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_bytes (WpSpaPod *self, gconstpointer *value, guint32 *len) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (value, FALSE); g_return_val_if_fail (len, FALSE); return spa_pod_get_bytes (self->pod, value, len) >= 0; } /*! * \brief Gets the pointer value and its type name of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param value (out): the pointer value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_pointer (WpSpaPod *self, gconstpointer *value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (value, FALSE); guint32 type = 0; return spa_pod_get_pointer (self->pod, &type, value) >= 0; } /*! * \brief Gets the Fd value of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param value (out): the Fd value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_fd (WpSpaPod *self, gint64 *value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (value, FALSE); return spa_pod_get_fd (self->pod, value) >= 0; } /*! * \brief Gets the rectangle's width and height value of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param width (out): the rectangle's width value * \param height (out): the rectangle's height value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_rectangle (WpSpaPod *self, guint32 *width, guint32 *height) { g_return_val_if_fail (self, FALSE); struct spa_rectangle rectangle = { 0, }; const gboolean res = spa_pod_get_rectangle (self->pod, &rectangle) >= 0; if (width) *width = rectangle.width; if (height) *height = rectangle.height; return res; } /*! * \brief Gets the fractions's numerator and denominator value of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param num (out): the fractions's numerator value * \param denom (out): the fractions's denominator value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_fraction (WpSpaPod *self, guint32 *num, guint32 *denom) { g_return_val_if_fail (self, FALSE); struct spa_fraction fraction = { 0, }; const gboolean res = spa_pod_get_fraction (self->pod, &fraction) >= 0; if (num) *num = fraction.num; if (denom) *denom = fraction.denom; return res; } /*! * \brief Sets a boolean value in the spa pod object. * * \ingroup wpspapod * \param self the spa pod object * \param value the boolean value * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_boolean (WpSpaPod *self, gboolean value) { g_return_val_if_fail (wp_spa_pod_is_boolean (self), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); ((struct spa_pod_bool *)self->pod)->value = value ? true : false; return TRUE; } /*! * \brief Sets an Id value in the spa pod object. * * \ingroup wpspapod * \param self the spa pod object * \param value the Id value * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_id (WpSpaPod *self, guint32 value) { g_return_val_if_fail (wp_spa_pod_is_id (self), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); ((struct spa_pod_id *)self->pod)->value = value; return TRUE; } /*! * \brief Sets an int value in the spa pod object. * * \ingroup wpspapod * \param self the spa pod object * \param value the int value * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_int (WpSpaPod *self, gint32 value) { g_return_val_if_fail (wp_spa_pod_is_int (self), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); ((struct spa_pod_int *)self->pod)->value = value; return TRUE; } /*! * \brief Sets a long value in the spa pod object. * * \ingroup wpspapod * \param self the spa pod object * \param value the long value * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_long (WpSpaPod *self, gint64 value) { g_return_val_if_fail (wp_spa_pod_is_long (self), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); ((struct spa_pod_long *)self->pod)->value = value; return TRUE; } /*! * \brief Sets a float value in the spa pod object. * * \ingroup wpspapod * \param self the spa pod object * \param value the float value * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_float (WpSpaPod *self, float value) { g_return_val_if_fail (wp_spa_pod_is_float (self), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); ((struct spa_pod_float *)self->pod)->value = value; return TRUE; } /*! * \brief Sets a double value in the spa pod object. * * \ingroup wpspapod * \param self the spa pod object * \param value the double value * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_double (WpSpaPod *self, double value) { g_return_val_if_fail (wp_spa_pod_is_double (self), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); ((struct spa_pod_double *)self->pod)->value = value; return TRUE; } /*! * \brief Sets a pointer value with its type name in the spa pod object. * * \ingroup wpspapod * \param self the spa pod object * \param type_name the name of the type of the pointer * \param value the pointer value * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_pointer (WpSpaPod *self, const char *type_name, gconstpointer value) { WpSpaType type = wp_spa_type_from_name (type_name); g_return_val_if_fail (wp_spa_pod_is_pointer (self), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); g_return_val_if_fail (type != WP_SPA_TYPE_INVALID, FALSE); ((struct spa_pod_pointer *)self->pod)->body.type = type; ((struct spa_pod_pointer *)self->pod)->body.value = value; return TRUE; } /*! * \brief Sets a Fd value in the spa pod object. * * \ingroup wpspapod * \param self the spa pod object * \param value the Fd value * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_fd (WpSpaPod *self, gint64 value) { g_return_val_if_fail (wp_spa_pod_is_fd (self), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); ((struct spa_pod_fd *)self->pod)->value = value; return TRUE; } /*! * \brief Sets the width and height values of a rectangle in the spa pod object. * * \ingroup wpspapod * \param self the spa pod object * \param width the width value of the rectangle * \param height the height value of the rectangle * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_rectangle (WpSpaPod *self, guint32 width, guint32 height) { g_return_val_if_fail (wp_spa_pod_is_rectangle (self), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); ((struct spa_pod_rectangle *)self->pod)->value.width = width; ((struct spa_pod_rectangle *)self->pod)->value.height = height; return TRUE; } /*! * \brief Sets the numerator and denominator values of a fraction in the * spa pod object. * * \ingroup wpspapod * \param self the spa pod object * \param num the numerator value of the farction * \param denom the denominator value of the fraction * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_fraction (WpSpaPod *self, guint32 num, guint32 denom) { g_return_val_if_fail (wp_spa_pod_is_fraction (self), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); ((struct spa_pod_fraction *)self->pod)->value.num = num; ((struct spa_pod_fraction *)self->pod)->value.denom = denom; return TRUE; } /*! * \brief Sets the value of a spa pod object in the current spa pod object. * The spa pod objects must be of the same value. * * \ingroup wpspapod * \param self the spa pod object * \param pod the pod with the value to be set * \returns TRUE if the value could be set, FALSE othewrise. */ gboolean wp_spa_pod_set_pod (WpSpaPod *self, WpSpaPod *pod) { g_return_val_if_fail (self->type == pod->type, FALSE); g_return_val_if_fail (SPA_POD_TYPE (self->pod) == SPA_POD_TYPE (pod->pod), FALSE); g_return_val_if_fail (!(self->flags & FLAG_CONSTANT), FALSE); switch (SPA_POD_TYPE (self->pod)) { case SPA_TYPE_None: break; case SPA_TYPE_Bool: ((struct spa_pod_bool *)self->pod)->value = ((struct spa_pod_bool *)pod->pod)->value; break; case SPA_TYPE_Id: ((struct spa_pod_id *)self->pod)->value = ((struct spa_pod_id *)pod->pod)->value; break; case SPA_TYPE_Int: ((struct spa_pod_int *)self->pod)->value = ((struct spa_pod_int *)pod->pod)->value; break; case SPA_TYPE_Long: ((struct spa_pod_long *)self->pod)->value = ((struct spa_pod_long *)pod->pod)->value; break; case SPA_TYPE_Float: ((struct spa_pod_float *)self->pod)->value = ((struct spa_pod_float *)pod->pod)->value; break; case SPA_TYPE_Double: ((struct spa_pod_double *)self->pod)->value = ((struct spa_pod_double *)pod->pod)->value; break; case SPA_TYPE_Pointer: ((struct spa_pod_pointer *)self->pod)->body.type = ((struct spa_pod_pointer *)pod->pod)->body.type; ((struct spa_pod_pointer *)self->pod)->body.value = ((struct spa_pod_pointer *)pod->pod)->body.value; break; case SPA_TYPE_Fd: ((struct spa_pod_fd *)self->pod)->value = ((struct spa_pod_fd *)pod->pod)->value; break; case SPA_TYPE_Rectangle: ((struct spa_pod_rectangle *)self->pod)->value.width = ((struct spa_pod_rectangle *)pod->pod)->value.width; ((struct spa_pod_rectangle *)self->pod)->value.height = ((struct spa_pod_rectangle *)pod->pod)->value.height; break; case SPA_TYPE_Fraction: ((struct spa_pod_fraction *)self->pod)->value.num = ((struct spa_pod_fraction *)pod->pod)->value.num; ((struct spa_pod_fraction *)self->pod)->value.denom = ((struct spa_pod_fraction *)pod->pod)->value.denom; break; default: g_return_val_if_fail (self->pod->size >= pod->pod->size, FALSE); memcpy (SPA_MEMBER (self->pod, sizeof (struct spa_pod), void), SPA_MEMBER (pod->pod, sizeof (struct spa_pod), void), SPA_MIN (self->pod->size, pod->pod->size)); self->pod->type = pod->pod->type; self->pod->size = pod->pod->size; break; } switch (self->type) { case WP_SPA_POD_PROPERTY: self->static_pod.data_property.table = pod->static_pod.data_property.table; self->static_pod.data_property.key = pod->static_pod.data_property.key; self->static_pod.data_property.flags = pod->static_pod.data_property.flags; break; case WP_SPA_POD_CONTROL: self->static_pod.data_control.offset = pod->static_pod.data_control.offset; self->static_pod.data_control.type = pod->static_pod.data_control.type; break; case WP_SPA_POD_REGULAR: default: break; } return TRUE; } /*! * \brief Checks whether two spa pod objects have the same value or not * * \ingroup wpspapod * \param self the spa pod object * \param pod the pod with the value to be compared with * \returns TRUE if both spa pod objects have the same values, FALSE othewrise. */ gboolean wp_spa_pod_equal (WpSpaPod *self, WpSpaPod *pod) { g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (pod != NULL, FALSE); if (self->type != pod->type) return FALSE; if (SPA_POD_TYPE (self->pod) != SPA_POD_TYPE (pod->pod)) return FALSE; switch (SPA_POD_TYPE (self->pod)) { case SPA_TYPE_None: break; case SPA_TYPE_Bool: if (((struct spa_pod_bool *)self->pod)->value != ((struct spa_pod_bool *)pod->pod)->value) return FALSE; break; case SPA_TYPE_Id: if (((struct spa_pod_id *)self->pod)->value != ((struct spa_pod_id *)pod->pod)->value) return FALSE; break; case SPA_TYPE_Int: if (((struct spa_pod_int *)self->pod)->value != ((struct spa_pod_int *)pod->pod)->value) return FALSE; break; case SPA_TYPE_Long: if (((struct spa_pod_long *)self->pod)->value != ((struct spa_pod_long *)pod->pod)->value) return FALSE; break; case SPA_TYPE_Float: if (((struct spa_pod_float *)self->pod)->value != ((struct spa_pod_float *)pod->pod)->value) return FALSE; break; case SPA_TYPE_Double: if (((struct spa_pod_double *)self->pod)->value != ((struct spa_pod_double *)pod->pod)->value) return FALSE; break; case SPA_TYPE_Pointer: if (((struct spa_pod_pointer *)self->pod)->body.type != ((struct spa_pod_pointer *)pod->pod)->body.type || ((struct spa_pod_pointer *)self->pod)->body.value != ((struct spa_pod_pointer *)pod->pod)->body.value) return FALSE; break; case SPA_TYPE_Fd: if (((struct spa_pod_fd *)self->pod)->value != ((struct spa_pod_fd *)pod->pod)->value) return FALSE; break; case SPA_TYPE_Rectangle: if (((struct spa_pod_rectangle *)self->pod)->value.width != ((struct spa_pod_rectangle *)pod->pod)->value.width || ((struct spa_pod_rectangle *)self->pod)->value.height != ((struct spa_pod_rectangle *)pod->pod)->value.height) return FALSE; break; case SPA_TYPE_Fraction: if (((struct spa_pod_fraction *)self->pod)->value.num != ((struct spa_pod_fraction *)pod->pod)->value.num || ((struct spa_pod_fraction *)self->pod)->value.denom != ((struct spa_pod_fraction *)pod->pod)->value.denom) return FALSE; break; default: if (self->pod->size != pod->pod->size || memcmp (SPA_MEMBER (self->pod, sizeof (struct spa_pod), void), SPA_MEMBER (pod->pod, sizeof (struct spa_pod), void), self->pod->size) != 0) return FALSE; break; } switch (self->type) { case WP_SPA_POD_PROPERTY: if (self->static_pod.data_property.table != pod->static_pod.data_property.table || self->static_pod.data_property.key != pod->static_pod.data_property.key || self->static_pod.data_property.flags != pod->static_pod.data_property.flags) return FALSE; break; case WP_SPA_POD_CONTROL: if (self->static_pod.data_control.offset != pod->static_pod.data_control.offset || self->static_pod.data_control.type != pod->static_pod.data_control.type) return FALSE; break; case WP_SPA_POD_REGULAR: default: break; } return TRUE; } /*! * \brief Gets the object properties values of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param id_name (out): the id name of the object, * \param ... (out): the list of the object properties values, followed by NULL * \returns TRUE if the object properties values were obtained, FALSE otherwise */ gboolean wp_spa_pod_get_object (WpSpaPod *self, const char **id_name, ...) { va_list args; gboolean res; va_start (args, id_name); res = wp_spa_pod_get_object_valist (self, id_name, args); va_end (args); return res; } /*! * \brief This is the `va_list` version of wp_spa_pod_get_object() * * \ingroup wpspapod * \param self the spa pod object * \param id_name (out): the id name of the object * \param args (out): the variable arguments passed to wp_spa_pod_get_object() * \returns TRUE if the object properties values were obtained, FALSE otherwise */ gboolean wp_spa_pod_get_object_valist (WpSpaPod *self, const char **id_name, va_list args) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (wp_spa_pod_is_object (self), FALSE); g_autoptr (WpSpaPodParser) p = wp_spa_pod_parser_new_object (self, id_name); const gboolean res = wp_spa_pod_parser_get_valist (p, args); wp_spa_pod_parser_end (p); return res; } /*! * \brief Gets the struct's values of a spa pod object * * \ingroup wpspapod * \param self the spa pod object * \param ... (out): the list of the struct values, followed by NULL * \returns TRUE if the struct values were obtained, FALSE otherwise */ gboolean wp_spa_pod_get_struct (WpSpaPod *self, ...) { va_list args; gboolean res; va_start (args, self); res = wp_spa_pod_get_struct_valist (self, args); va_end (args); return res; } /*! * \brief This is the `va_list` version of wp_spa_pod_get_struct() * * \ingroup wpspapod * \param self the spa pod object * \param args (out): the variable arguments passed to wp_spa_pod_get_struct() * \returns TRUE if the struct values were obtained, FALSE otherwise */ gboolean wp_spa_pod_get_struct_valist (WpSpaPod *self, va_list args) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (wp_spa_pod_is_struct (self), FALSE); g_autoptr (WpSpaPodParser) p = wp_spa_pod_parser_new_struct (self); const gboolean res = wp_spa_pod_parser_get_valist (p, args); wp_spa_pod_parser_end (p); return res; } /*! * \brief Gets the name, flags and spa pod value of a spa pod property * * \ingroup wpspapod * \param self the spa pod object * \param key (out) (optional): the name of the property * \param value (out) (optional): the spa pod value of the property * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_property (WpSpaPod *self, const char **key, WpSpaPod **value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (wp_spa_pod_is_property (self), FALSE); if (key) { WpSpaIdValue key_val = wp_spa_id_table_find_value ( self->static_pod.data_property.table, self->static_pod.data_property.key); if (key_val) { *key = wp_spa_id_value_short_name (key_val); } else { g_snprintf (self->static_pod.data_property.id_key_name, WP_SPA_POD_ID_PROPERTY_NAME_MAX, "id-%08x", self->static_pod.data_property.key); *key = self->static_pod.data_property.id_key_name; } } if (value) *value = wp_spa_pod_new_wrap (self->pod); return TRUE; } /*! * \brief Gets the offset, type name and spa pod value of a spa pod control * * \ingroup wpspapod * \param self the spa pod object * \param offset (out) (optional): the offset of the control * \param ctl_type (out) (optional): the control type (Properties, Midi, ...) * \param value (out) (optional): the spa pod value of the control * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_get_control (WpSpaPod *self, guint32 *offset, const char **ctl_type, WpSpaPod **value) { g_return_val_if_fail (self, FALSE); g_return_val_if_fail (wp_spa_pod_is_control (self), FALSE); if (offset) *offset = self->static_pod.data_control.offset; if (ctl_type) { WpSpaIdValue type_val = wp_spa_id_value_from_number ( SPA_TYPE_INFO_Control, self->static_pod.data_control.type); g_return_val_if_fail (type_val != NULL, FALSE); *ctl_type = wp_spa_id_value_short_name (type_val); } if (value) *value = wp_spa_pod_new_wrap (self->pod); return TRUE; } /*! * \brief Gets the child of a spa pod choice object * * \ingroup wpspapod * \param self a spa pod choice object * \returns (transfer full): the child of the spa pod choice object */ WpSpaPod * wp_spa_pod_get_choice_child (WpSpaPod *self) { g_return_val_if_fail (wp_spa_pod_is_choice (self), NULL); return wp_spa_pod_new_wrap (SPA_POD_CHOICE_CHILD (self->pod)); } /*! * \brief Gets the child of a spa pod array object * * \ingroup wpspapod * \param self a spa pod choice object * \returns (transfer full): the child of the spa pod array object */ WpSpaPod * wp_spa_pod_get_array_child (WpSpaPod *self) { g_return_val_if_fail (wp_spa_pod_is_array (self), NULL); return wp_spa_pod_new_wrap (SPA_POD_ARRAY_CHILD (self->pod)); } /*! * \brief Fixates choices in an object pod so that they only have one value * * \ingroup wpspapod * \param self a spa pod * \returns TRUE if the pod was an object and it went through the fixation * procedure, FALSE otherwise */ gboolean wp_spa_pod_fixate (WpSpaPod *self) { g_return_val_if_fail (self, FALSE); if (wp_spa_pod_is_object (self)) return spa_pod_object_fixate ((struct spa_pod_object *) self->pod) == 0; return FALSE; } /*! * \brief Returns the intersection between \a self and \a filter * * This is typically used to intersect pods that describe formats, in order to * find a common format that is accceptable by both sides. For that purpose, * this is not exactly an intersection with its mathematical meaning. * Object properties can be thought of as format constraints. When one side does * not specify a specific property, it is considered to accept any value for it, * so the value of this property from the other side is added in the result. * * Both input pods are left unmodified after this function call. * * If NULL is passed in the \a filter, this function just copies \a self and * returns the copy. * * \param self the first pod * \param filter (nullable): the second pod * \return (transfer full) (nullable): a new pod that contains the intersection * between \a self and \a filter, or NULL if the intersection was not possible * to make */ WpSpaPod * wp_spa_pod_filter (WpSpaPod *self, WpSpaPod *filter) { char buffer[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(&buffer, sizeof(buffer)); struct spa_pod *result = NULL; g_return_val_if_fail (self, NULL); if (spa_pod_filter(&b, &result, self->pod, filter ? filter->pod : NULL) >= 0) return wp_spa_pod_new_wrap_copy (result); return NULL; } /*! * \brief Increases the reference count of a spa pod builder * * \ingroup wpspapod * \param self a spa pod builder object * \returns (transfer full): \a self with an additional reference count on it */ WpSpaPodBuilder * wp_spa_pod_builder_ref (WpSpaPodBuilder *self) { return (WpSpaPodBuilder *) g_rc_box_acquire ((gpointer) self); } static void wp_spa_pod_builder_free (WpSpaPodBuilder *self) { g_clear_pointer (&self->buf, g_free); } /*! * \brief Decreases the reference count on \a self and frees it when the ref * count reaches zero. * * \ingroup wpspapod * \param self (transfer full): a spa pod builder object */ void wp_spa_pod_builder_unref (WpSpaPodBuilder *self) { g_rc_box_release_full (self, (GDestroyNotify) wp_spa_pod_builder_free); } /*! * \brief Creates a spa pod builder of type array * \ingroup wpspapod * \returns (transfer full): the new spa pod builder */ WpSpaPodBuilder * wp_spa_pod_builder_new_array (void) { WpSpaPodBuilder *self = wp_spa_pod_builder_new ( WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE, SPA_TYPE_Array); spa_pod_builder_push_array (&self->builder, &self->frame); return self; } /*! * \brief Creates a spa pod builder of type choice * * \ingroup wpspapod * \param choice_type the name of the choice type ("Range", "Step", ...) * \returns (transfer full): the new spa pod builder */ WpSpaPodBuilder * wp_spa_pod_builder_new_choice (const char *choice_type) { WpSpaPodBuilder *self = NULL; WpSpaIdValue type = wp_spa_id_value_from_short_name ( SPA_TYPE_INFO_Choice, choice_type); g_return_val_if_fail (type != NULL, NULL); /* Construct the builder */ self = wp_spa_pod_builder_new (WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE, SPA_TYPE_Choice); /* Push the array */ spa_pod_builder_push_choice (&self->builder, &self->frame, wp_spa_id_value_number (type), 0); return self; } /*! * \brief Creates a spa pod builder of type object * * \ingroup wpspapod * \param type_name the type name of the object type * \param id_name the Id name of the object * \returns (transfer full): the new spa pod builder */ WpSpaPodBuilder * wp_spa_pod_builder_new_object (const char *type_name, const char *id_name) { WpSpaPodBuilder *self = NULL; WpSpaType type; WpSpaIdTable table; WpSpaIdValue id; /* Find the type */ type = wp_spa_type_from_name (type_name); g_return_val_if_fail (wp_spa_type_is_object (type), NULL); /* Find the id */ table = wp_spa_type_get_object_id_values_table (type); g_return_val_if_fail (table != NULL, NULL); id = wp_spa_id_table_find_value_from_short_name (table, id_name); g_return_val_if_fail (id != NULL, NULL); /* Construct the builder */ self = wp_spa_pod_builder_new (WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE, type); /* Push the object */ spa_pod_builder_push_object (&self->builder, &self->frame, type, wp_spa_id_value_number (id)); return self; } /*! * \brief Creates a spa pod builder of type struct * * \ingroup wpspapod * \returns (transfer full): the new spa pod builder */ WpSpaPodBuilder * wp_spa_pod_builder_new_struct (void) { WpSpaPodBuilder *self = NULL; self = wp_spa_pod_builder_new (WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE, SPA_TYPE_Struct); spa_pod_builder_push_struct (&self->builder, &self->frame); return self; } /*! * \brief Creates a spa pod builder of type sequence * * \ingroup wpspapod * \returns (transfer full): the new spa pod builder */ WpSpaPodBuilder * wp_spa_pod_builder_new_sequence (guint unit) { WpSpaPodBuilder *self = NULL; self = wp_spa_pod_builder_new (WP_SPA_POD_BUILDER_REALLOC_STEP_SIZE, SPA_TYPE_Sequence); spa_pod_builder_push_sequence (&self->builder, &self->frame, unit); return self; } /*! * \brief Adds a none value into the builder * * \ingroup wpspapod * \param self the spa pod builder object */ void wp_spa_pod_builder_add_none (WpSpaPodBuilder *self) { spa_pod_builder_none (&self->builder); } /*! * \brief Adds a boolean value into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param value the boolean value */ void wp_spa_pod_builder_add_boolean (WpSpaPodBuilder *self, gboolean value) { spa_pod_builder_bool (&self->builder, value ? true : false); } /*! * \brief Adds a Id value into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param value the Id value */ void wp_spa_pod_builder_add_id (WpSpaPodBuilder *self, guint32 value) { spa_pod_builder_id (&self->builder, value); } /*! * \brief Adds a int value into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param value the int value */ void wp_spa_pod_builder_add_int (WpSpaPodBuilder *self, gint32 value) { spa_pod_builder_int (&self->builder, value); } /*! * \brief Adds a long value into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param value the long value */ void wp_spa_pod_builder_add_long (WpSpaPodBuilder *self, gint64 value) { spa_pod_builder_long (&self->builder, value); } /*! * \brief Adds a float value into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param value the float value */ void wp_spa_pod_builder_add_float (WpSpaPodBuilder *self, float value) { spa_pod_builder_float (&self->builder, value); } /*! * \brief Adds a double value into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param value the double value */ void wp_spa_pod_builder_add_double (WpSpaPodBuilder *self, double value) { spa_pod_builder_double (&self->builder, value); } /*! * \brief Adds a string value into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param value the string value */ void wp_spa_pod_builder_add_string (WpSpaPodBuilder *self, const char *value) { spa_pod_builder_string (&self->builder, value); } /*! * \brief Adds a bytes value with its length into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param value the bytes value * \param len the length of the bytes value */ void wp_spa_pod_builder_add_bytes (WpSpaPodBuilder *self, gconstpointer value, guint32 len) { spa_pod_builder_bytes (&self->builder, value, len); } /*! * \brief Adds a pointer value with its type name into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param type_name the type name that the pointer points to * \param value the pointer vaue */ void wp_spa_pod_builder_add_pointer (WpSpaPodBuilder *self, const char *type_name, gconstpointer value) { WpSpaType type = wp_spa_type_from_name (type_name); g_return_if_fail (wp_spa_type_parent (type) == SPA_TYPE_Pointer); spa_pod_builder_pointer (&self->builder, type, value); } /*! * \brief Adds a Fd value into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param value the Fd value */ void wp_spa_pod_builder_add_fd (WpSpaPodBuilder *self, gint64 value) { spa_pod_builder_fd (&self->builder, value); } /*! * \brief Adds the width and height values of a rectangle into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param width the width value of the rectangle * \param height the height value of the rectangle */ void wp_spa_pod_builder_add_rectangle (WpSpaPodBuilder *self, guint32 width, guint32 height) { spa_pod_builder_rectangle (&self->builder, width, height); } /*! * \brief Adds the numerator and denominator values of a fraction into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param num the numerator value of the fraction * \param denom the denominator value of the fraction */ void wp_spa_pod_builder_add_fraction (WpSpaPodBuilder *self, guint32 num, guint32 denom) { spa_pod_builder_fraction (&self->builder, num, denom); } /*! * \brief Adds a pod value into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param pod the pod value */ void wp_spa_pod_builder_add_pod (WpSpaPodBuilder *self, WpSpaPod *pod) { spa_pod_builder_primitive (&self->builder, pod->pod); } /*! * \brief Adds a property into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param key the name of the property */ void wp_spa_pod_builder_add_property (WpSpaPodBuilder *self, const char *key) { guint key_id; if (g_str_has_prefix (key, "id-")) { g_return_if_fail (sscanf (key, "id-%08x", &key_id) == 1); } else { WpSpaIdTable table = wp_spa_type_get_values_table (self->type); WpSpaIdValue id = wp_spa_id_table_find_value_from_short_name (table, key); g_return_if_fail (id != NULL); key_id = wp_spa_id_value_number (id); } spa_pod_builder_prop (&self->builder, key_id, 0); } /*! * \brief Adds a property into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param id the id of the property */ void wp_spa_pod_builder_add_property_id (WpSpaPodBuilder *self, guint32 id) { spa_pod_builder_prop (&self->builder, id, 0); } /*! * \brief Adds a control into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param offset the offset of the control * \param ctl_type the type name of the control */ void wp_spa_pod_builder_add_control (WpSpaPodBuilder *self, guint32 offset, const char *ctl_type) { WpSpaIdValue id = wp_spa_id_value_from_short_name ( SPA_TYPE_INFO_Control, ctl_type); g_return_if_fail (id != NULL); spa_pod_builder_control (&self->builder, offset, wp_spa_id_value_number (id)); } /*! * \brief Adds a list of values into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param ... a list of additional values, followed by NULL */ void wp_spa_pod_builder_add (WpSpaPodBuilder *self, ...) { va_list args; va_start (args, self); wp_spa_pod_builder_add_valist (self, args); va_end (args); } /*! * \brief Adds a list of values into the builder * * \ingroup wpspapod * \param self the spa pod builder object * \param args the variable arguments passed to wp_spa_pod_builder_add() */ void wp_spa_pod_builder_add_valist (WpSpaPodBuilder *self, va_list args) { WpSpaIdTable table = wp_spa_type_get_values_table (self->type); do { WpSpaIdValue key = NULL; const char *format; int n_values = 1; struct spa_pod_frame f; gboolean choice; if (wp_spa_type_is_object (self->type)) { guint key_id; const char *key_name = va_arg(args, const char *); if (!key_name) return; if (g_str_has_prefix (key_name, "id-")) { g_return_if_fail (sscanf (key_name, "id-%08x", &key_id) == 1); } else { key = wp_spa_id_table_find_value_from_short_name (table, key_name); g_return_if_fail (key != NULL); key_id = wp_spa_id_value_number (key); } spa_pod_builder_prop (&self->builder, key_id, 0); } else if (self->type == SPA_TYPE_Sequence) { guint32 offset = va_arg(args, uint32_t); if (offset == 0) return; const char *control_name = va_arg(args, const char *); if (!control_name) return; WpSpaIdValue type = wp_spa_id_value_from_short_name ( SPA_TYPE_INFO_Control, control_name); g_return_if_fail (type != NULL); spa_pod_builder_control (&self->builder, offset, wp_spa_id_value_number (type)); } if ((format = va_arg(args, const char *)) == NULL) break; choice = *format == '?'; if (choice) { uint32_t type = spa_choice_from_id (*++format); if (*format != '\0') format++; spa_pod_builder_push_choice (&self->builder, &f, type, 0); n_values = va_arg(args, int); } while (n_values-- > 0) { switch (*format) { case 'P': /* Pod */ case 'V': /* Choice */ case 'O': /* Object */ case 'T': { /* Struct */ WpSpaPod *arg = va_arg(args, WpSpaPod *); if (arg == NULL) spa_pod_builder_none (&self->builder); else spa_pod_builder_primitive (&self->builder, arg->pod); break; } case 'K': { /* Id as string - WirePlumber extension */ const char * id = va_arg(args, const char *); if (key) { WpSpaIdTable id_table = NULL; wp_spa_id_value_get_value_type (key, &id_table); WpSpaIdValue id_val = wp_spa_id_table_find_value_from_short_name (id_table, id); spa_pod_builder_id (&self->builder, wp_spa_id_value_number (id_val)); } break; } case 'b': spa_pod_builder_bool(&self->builder, va_arg(args, gboolean) ? true : false); break; default: SPA_POD_BUILDER_COLLECT(&self->builder, *format, args); break; } } if (choice) spa_pod_builder_pop (&self->builder, &f); } while (TRUE); } /*! * \brief Ends the builder process and returns the constructed spa pod object * * \ingroup wpspapod * \param self the spa pod builder object * \returns (transfer full): the constructed spa pod object */ WpSpaPod * wp_spa_pod_builder_end (WpSpaPodBuilder *self) { WpSpaPod *ret = NULL; /* Construct the pod */ ret = g_slice_new0 (WpSpaPod); g_ref_count_init (&ret->ref); ret->type = WP_SPA_POD_REGULAR; ret->pod = spa_pod_builder_pop (&self->builder, &self->frame); ret->builder = wp_spa_pod_builder_ref (self); /* Also copy the specific object type if it is an object */ if (spa_pod_is_object (ret->pod)) ret->static_pod.data_property.table = wp_spa_type_get_values_table (ret->builder->type); return ret; } /*! * \brief Increases the reference count of a spa pod parser * \ingroup wpspapod * \param self a spa pod sparser object * \returns (transfer full): \a self with an additional reference count on it */ WpSpaPodParser * wp_spa_pod_parser_ref (WpSpaPodParser *self) { return (WpSpaPodParser *) g_rc_box_acquire ((gpointer) self); } static void wp_spa_pod_parser_free (WpSpaPodParser *self) { self->pod = NULL; } /*! * \brief Decreases the reference count on \a self and frees it when the ref * count reaches zero. * * \ingroup wpspapod * \param self (transfer full): a spa pod parser object */ void wp_spa_pod_parser_unref (WpSpaPodParser *self) { g_rc_box_release_full (self, (GDestroyNotify) wp_spa_pod_parser_free); } static WpSpaPodParser * wp_spa_pod_parser_new (WpSpaPod *pod, guint32 type) { WpSpaPodParser *self = g_rc_box_new0 (WpSpaPodParser); self->type = type; self->pod = pod; spa_pod_parser_pod (&self->parser, self->pod->pod); return self; } /*! * \brief Creates an object spa pod parser. The \a pod object must be valid for * the entire life-cycle of the returned parser. * * \ingroup wpspapod * \param pod the object spa pod to parse * \param id_name the Id name of the object * \returns (transfer full): The new spa pod parser */ WpSpaPodParser * wp_spa_pod_parser_new_object (WpSpaPod *pod, const char **id_name) { WpSpaPodParser *self = NULL; WpSpaType type = wp_spa_pod_get_spa_type (pod); guint32 id = SPA_ID_INVALID; g_return_val_if_fail (wp_spa_pod_is_object (pod), NULL); self = wp_spa_pod_parser_new (pod, type); spa_pod_parser_push_object (&self->parser, &self->frame, type, &id); if (id_name) { WpSpaIdTable table = wp_spa_type_get_object_id_values_table (type); *id_name = wp_spa_id_value_short_name ( wp_spa_id_table_find_value (table, id)); } return self; } /*! * \brief Creates an struct spa pod parser. The \a pod object must be valid for * the entire life-cycle of the returned parser. * * \ingroup wpspapod * \param pod the struct spa pod to parse * \returns (transfer full): The new spa pod parser */ WpSpaPodParser * wp_spa_pod_parser_new_struct (WpSpaPod *pod) { WpSpaPodParser *self = NULL; g_return_val_if_fail (wp_spa_pod_is_struct (pod), NULL); self = wp_spa_pod_parser_new (pod, SPA_TYPE_Struct); spa_pod_parser_push_struct (&self->parser, &self->frame); return self; } /*! * \brief Gets the boolean value from a spa pod parser * * \ingroup wpspapod * \param self the spa pod parser object * \param value (out): the boolean value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_boolean (WpSpaPodParser *self, gboolean *value) { g_return_val_if_fail (value, FALSE); bool v = FALSE; gboolean res = spa_pod_parser_get_bool (&self->parser, &v) >= 0; *value = v ? TRUE : FALSE; return res; } /*! * \brief Gets the Id value from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param value (out): the Id value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_id (WpSpaPodParser *self, guint32 *value) { g_return_val_if_fail (value, FALSE); return spa_pod_parser_get_id (&self->parser, value) >= 0; } /*! * \brief Gets the int value from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param value (out): the int value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_int (WpSpaPodParser *self, gint32 *value) { g_return_val_if_fail (value, FALSE); return spa_pod_parser_get_int (&self->parser, value) >= 0; } /*! * \brief Gets the long value from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param value (out): the long value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_long (WpSpaPodParser *self, gint64 *value) { g_return_val_if_fail (value, FALSE); return spa_pod_parser_get_long (&self->parser, value) >= 0; } /*! * \brief Gets the float value from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param value (out): the float value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_float (WpSpaPodParser *self, float *value) { g_return_val_if_fail (value, FALSE); return spa_pod_parser_get_float (&self->parser, value) >= 0; } /*! * \brief Gets the double value from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param value (out): the double value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_double (WpSpaPodParser *self, double *value) { g_return_val_if_fail (value, FALSE); return spa_pod_parser_get_double (&self->parser, value) >= 0; } /*! * \brief Gets the string value from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param value (out): the string value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_string (WpSpaPodParser *self, const char **value) { g_return_val_if_fail (value, FALSE); return spa_pod_parser_get_string (&self->parser, value) >= 0; } /*! * \brief Gets the bytes value and its length from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param value (out): the bytes value * \param len (out): the length of the bytes value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_bytes (WpSpaPodParser *self, gconstpointer *value, guint32 *len) { return spa_pod_parser_get_bytes (&self->parser, value, len) >= 0; } /*! * \brief Gets the pointer value and its type name from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param value (out): the pointer value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_pointer (WpSpaPodParser *self, gconstpointer *value) { g_return_val_if_fail (value, FALSE); guint32 type = 0; return spa_pod_parser_get_pointer (&self->parser, &type, value) >= 0; } /*! * \brief Gets the Fd value from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param value (out): the Fd value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_fd (WpSpaPodParser *self, gint64 *value) { g_return_val_if_fail (value, FALSE); return spa_pod_parser_get_fd (&self->parser, value) >= 0; } /*! * \brief Gets the rectangle's width and height value from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param width (out): the rectangle's width value * \param height (out): the rectangle's height value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_rectangle (WpSpaPodParser *self, guint32 *width, guint32 *height) { struct spa_rectangle r = { 0, }; gboolean res = spa_pod_parser_get_rectangle (&self->parser, &r) >= 0; if (width) *width = r.width; if (height) *height = r.height; return res; } /*! * \brief Gets the fractions's numerator and denominator value from a spa pod * parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param num (out): the fractions's numerator value * \param denom (out): the fractions's denominator value * \returns TRUE if the value was obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_fraction (WpSpaPodParser *self, guint32 *num, guint32 *denom) { struct spa_fraction f = { 0, }; gboolean res = spa_pod_parser_get_fraction (&self->parser, &f) >= 0; if (num) *num = f.num; if (denom) *denom = f.denom; return res; } /*! * \brief Gets the spa pod value from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \returns (transfer full): The spa pod value or NULL if it could not be * obtained */ WpSpaPod * wp_spa_pod_parser_get_pod (WpSpaPodParser *self) { struct spa_pod *p = NULL; gboolean res = spa_pod_parser_get_pod (&self->parser, &p) >= 0; if (!res || !p) return NULL; return wp_spa_pod_new_wrap (p); } /*! * \brief Gets a list of values from a spa pod parser object * * \ingroup wpspapod * \param self the spa pod parser object * \param ... (out): a list of values to get, followed by NULL * \returns TRUE if the values were obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get (WpSpaPodParser *self, ...) { gboolean res; va_list args; va_start (args, self); res = wp_spa_pod_parser_get_valist (self, args); va_end (args); return res; } static inline gboolean wp_spa_pod_parser_can_collect (const struct spa_pod *pod, char format) { format = (format == 'K') ? 'I' : format; return spa_pod_parser_can_collect (pod, format); } /*! * \brief This is the `va_list` version of wp_spa_pod_parser_get() * * \ingroup wpspapod * \param self the spa pod parser object * \param args the variable arguments passed to wp_spa_pod_parser_get() * \returns TRUE if the values were obtained, FALSE otherwise */ gboolean wp_spa_pod_parser_get_valist (WpSpaPodParser *self, va_list args) { const struct spa_pod_prop *prop = NULL; WpSpaIdTable table = wp_spa_type_get_values_table (self->type); do { WpSpaIdValue key = NULL; bool optional; const struct spa_pod *pod = NULL; const char *format; if (wp_spa_type_is_object (self->type)) { guint key_id; const struct spa_pod_object *object; const char *key_name = va_arg(args, const char *); if (!key_name) break; if (g_str_has_prefix (key_name, "id-")) { if (sscanf (key_name, "id-%08x", &key_id) != 1) return FALSE; } else { key = wp_spa_id_table_find_value_from_short_name (table, key_name); g_return_val_if_fail (key != NULL, FALSE); key_id = wp_spa_id_value_number (key); } object = (const struct spa_pod_object *)spa_pod_parser_frame (&self->parser, &self->frame); prop = spa_pod_object_find_prop (object, prop, key_id); pod = prop ? &prop->value : NULL; } if ((format = va_arg(args, char *)) == NULL) break; if (self->type == SPA_TYPE_Struct) pod = spa_pod_parser_next (&self->parser); if ((optional = (*format == '?'))) format++; if (!pod || !wp_spa_pod_parser_can_collect (pod, *format)) { if (!optional) return FALSE; SPA_POD_PARSER_SKIP (*format, args); } else { if (pod->type == SPA_TYPE_Choice && *format != 'V' && SPA_POD_CHOICE_TYPE(pod) == SPA_CHOICE_None) pod = SPA_POD_CHOICE_CHILD(pod); switch (*format) { case 'P': /* Pod */ case 'V': /* Choice */ case 'O': /* Object */ case 'T': /* Struct */ *va_arg(args, WpSpaPod**) = wp_spa_pod_new_wrap_copy (pod); break; case 'K': { /* Id as string - WirePlumber extension */ const char ** idstr = va_arg(args, const char **); uint32_t id = SPA_POD_VALUE(struct spa_pod_id, pod); if (key) { WpSpaIdTable id_table = NULL; wp_spa_id_value_get_value_type (key, &id_table); WpSpaIdValue id_val = wp_spa_id_table_find_value (id_table, id); *idstr = wp_spa_id_value_short_name (id_val); } break; } case 'b': *va_arg(args, gboolean*) = SPA_POD_VALUE(struct spa_pod_bool, pod) ? TRUE : FALSE; break; default: SPA_POD_PARSER_COLLECT (pod, *format, args); break; } } } while (TRUE); return TRUE; } /*! * \brief Ends the parser process * * \ingroup wpspapod * \param self the spa pod parser object */ void wp_spa_pod_parser_end (WpSpaPodParser *self) { spa_pod_parser_pop (&self->parser, &self->frame); } struct _WpSpaPodIterator { WpSpaPod *pod; union { gpointer value; /* Array and Choice */ struct spa_pod *pod; /* Struct */ struct spa_pod_prop *prop; /* Object */ struct spa_pod_control *control; /* Sequence */ } curr; }; typedef struct _WpSpaPodIterator WpSpaPodIterator; static gboolean wp_spa_pod_iterator_next_choice (WpSpaPodIterator *self, GValue *item) { const struct spa_pod_choice *pod_choice = (const struct spa_pod_choice *) self->pod->pod; if (!self->curr.value) self->curr.value = SPA_MEMBER (&pod_choice->body, sizeof(struct spa_pod_choice_body), void); else self->curr.value = SPA_MEMBER (self->curr.value, pod_choice->body.child.size, void); if (self->curr.value >= SPA_MEMBER(&pod_choice->body, SPA_POD_BODY_SIZE (pod_choice), void)) return FALSE; if (item) { g_value_init (item, G_TYPE_POINTER); g_value_set_pointer (item, self->curr.value); } return TRUE; } static gboolean wp_spa_pod_iterator_next_array (WpSpaPodIterator *self, GValue *item) { const struct spa_pod_array *pod_arr = (const struct spa_pod_array *) self->pod->pod; if (!self->curr.value) self->curr.value = SPA_MEMBER (&pod_arr->body, sizeof(struct spa_pod_array_body), void); else self->curr.value = SPA_MEMBER (self->curr.value, pod_arr->body.child.size, void); if (self->curr.value >= SPA_MEMBER(&pod_arr->body, SPA_POD_BODY_SIZE (pod_arr), void)) return FALSE; if (item) { g_value_init (item, G_TYPE_POINTER); g_value_set_pointer (item, self->curr.value); } return TRUE; } static gboolean wp_spa_pod_iterator_next_object (WpSpaPodIterator *self, GValue *item) { const struct spa_pod_object *pod_obj = (const struct spa_pod_object *) self->pod->pod; if (!self->curr.prop) self->curr.prop = spa_pod_prop_first (&pod_obj->body); else self->curr.prop = spa_pod_prop_next (self->curr.prop); if (!spa_pod_prop_is_inside (&pod_obj->body, SPA_POD_BODY_SIZE (pod_obj), self->curr.prop)) return FALSE; if (item) { g_value_init (item, WP_TYPE_SPA_POD); g_value_take_boxed (item, wp_spa_pod_new_property_wrap ( self->pod->static_pod.data_property.table, self->curr.prop->key, self->curr.prop->flags, &self->curr.prop->value)); } return TRUE; } static gboolean wp_spa_pod_iterator_next_struct (WpSpaPodIterator *self, GValue *item) { if (!self->curr.pod) self->curr.pod = SPA_POD_BODY (self->pod->pod); else self->curr.pod = spa_pod_next (self->curr.pod); if (!spa_pod_is_inside (SPA_POD_BODY (self->pod->pod), SPA_POD_BODY_SIZE (self->pod->pod), self->curr.pod)) return FALSE; if (item) { g_value_init (item, WP_TYPE_SPA_POD); g_value_take_boxed (item, wp_spa_pod_new_wrap (self->curr.pod)); } return TRUE; } static gboolean wp_spa_pod_iterator_next_sequence (WpSpaPodIterator *self, GValue *item) { const struct spa_pod_sequence *pod_seq = (const struct spa_pod_sequence *) self->pod->pod; if (!self->curr.control) self->curr.control = spa_pod_control_first (&pod_seq->body); else self->curr.control = spa_pod_control_next (self->curr.control); if (!spa_pod_control_is_inside (&pod_seq->body, SPA_POD_BODY_SIZE (pod_seq), self->curr.control)) return FALSE; if (item) { g_value_init (item, WP_TYPE_SPA_POD); g_value_take_boxed (item, wp_spa_pod_new_control_wrap ( self->curr.control->offset, self->curr.control->type, &self->curr.control->value)); } return TRUE; } static void wp_spa_pod_iterator_reset (WpIterator *iterator) { WpSpaPodIterator *self = wp_iterator_get_user_data (iterator); self->curr.value = NULL; self->curr.pod = NULL; self->curr.prop = NULL; self->curr.control = NULL; } static gboolean wp_spa_pod_iterator_next (WpIterator *iterator, GValue *item) { WpSpaPodIterator *self = wp_iterator_get_user_data (iterator); switch (self->pod->pod->type) { case SPA_TYPE_Choice: return wp_spa_pod_iterator_next_choice (self, item); case SPA_TYPE_Array: return wp_spa_pod_iterator_next_array (self, item); case SPA_TYPE_Object: return wp_spa_pod_iterator_next_object (self, item); case SPA_TYPE_Struct: return wp_spa_pod_iterator_next_struct (self, item); case SPA_TYPE_Sequence: return wp_spa_pod_iterator_next_sequence (self, item); default: break; } return FALSE; } static gboolean wp_spa_pod_iterator_fold (WpIterator *iterator, WpIteratorFoldFunc func, GValue *ret, gpointer data) { WpSpaPodIterator *self = wp_iterator_get_user_data (iterator); wp_iterator_reset (iterator); switch (self->pod->pod->type) { case SPA_TYPE_Choice: { const struct spa_pod_choice *pod_choice = (const struct spa_pod_choice *) self->pod->pod; gpointer p = NULL; SPA_POD_CHOICE_FOREACH (pod_choice, p) { GValue v = G_VALUE_INIT; g_value_init (&v, G_TYPE_POINTER); g_value_set_pointer (&v, p); const gboolean res = func (&v, ret, data); g_value_unset (&v); if (!res) return FALSE; } break; } case SPA_TYPE_Array: { const struct spa_pod_array *pod_arr = (const struct spa_pod_array *) self->pod->pod; gpointer p = NULL; SPA_POD_ARRAY_FOREACH (pod_arr, p) { GValue v = G_VALUE_INIT; g_value_init (&v, G_TYPE_POINTER); g_value_set_pointer (&v, p); const gboolean res = func (&v, ret, data); g_value_unset (&v); if (!res) return FALSE; } break; } case SPA_TYPE_Object: { const struct spa_pod_object *pod_obj = (const struct spa_pod_object *) self->pod->pod; struct spa_pod_prop *p = NULL; SPA_POD_OBJECT_FOREACH (pod_obj, p) { GValue v = G_VALUE_INIT; g_value_init (&v, WP_TYPE_SPA_POD); g_value_take_boxed (&v, wp_spa_pod_new_property_wrap ( self->pod->static_pod.data_property.table, p->key, p->flags, &p->value)); const gboolean res = func (&v, ret, data); g_value_unset (&v); if (!res) return FALSE; } break; } case SPA_TYPE_Struct: { struct spa_pod *p = NULL; SPA_POD_STRUCT_FOREACH (self->pod->pod, p) { GValue v = G_VALUE_INIT; g_value_init (&v, WP_TYPE_SPA_POD); g_value_take_boxed (&v, wp_spa_pod_new_wrap (p)); const gboolean res = func (&v, ret, data); g_value_unset (&v); if (!res) return FALSE; } break; } case SPA_TYPE_Sequence: { const struct spa_pod_sequence *pod_seq = (const struct spa_pod_sequence *) self->pod->pod; struct spa_pod_control *p = NULL; SPA_POD_SEQUENCE_FOREACH (pod_seq, p) { GValue v = G_VALUE_INIT; g_value_init (&v, WP_TYPE_SPA_POD); g_value_take_boxed (&v, wp_spa_pod_new_control_wrap (p->offset, p->type, &p->value)); const gboolean res = func (&v, ret, data); g_value_unset (&v); if (!res) return FALSE; } break; } default: return FALSE; } return TRUE; } static void wp_spa_pod_iterator_finalize (WpIterator *iterator) { WpSpaPodIterator *self = wp_iterator_get_user_data (iterator); g_clear_pointer (&self->pod, wp_spa_pod_unref); } /*! * \brief Creates a new iterator for a spa pod object. * * \ingroup wpspapod * \param pod a spa pod object * \returns (transfer full): the new spa pod iterator */ WpIterator * wp_spa_pod_new_iterator (WpSpaPod *pod) { static const WpIteratorMethods methods = { .version = WP_ITERATOR_METHODS_VERSION, .reset = wp_spa_pod_iterator_reset, .next = wp_spa_pod_iterator_next, .fold = wp_spa_pod_iterator_fold, .foreach = NULL, .finalize = wp_spa_pod_iterator_finalize }; WpIterator *it = wp_iterator_new (&methods, sizeof (WpSpaPodIterator)); WpSpaPodIterator *self = wp_iterator_get_user_data (it); self->pod = wp_spa_pod_ref (pod); return it; }