mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-23 06:59:58 -05:00
Add an argument to pass the result param to the caller instead of having the caller have to pick it up from the builder. Improve docs for node, clock and monitor Pass spa_pod everywhere instead of spa_pod_object. Pass result argument to spa_pod_filter to make things a little nicer.
517 lines
14 KiB
C
517 lines
14 KiB
C
/* Simple Plugin API
|
|
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <spa/param/props.h>
|
|
#include <spa/pod/iter.h>
|
|
#include <spa/pod/builder.h>
|
|
|
|
static int compare_value(enum spa_pod_type type, const void *r1, const void *r2)
|
|
{
|
|
switch (type) {
|
|
case SPA_POD_TYPE_INVALID:
|
|
return 0;
|
|
case SPA_POD_TYPE_BOOL:
|
|
case SPA_POD_TYPE_ID:
|
|
return *(int32_t *) r1 == *(uint32_t *) r2 ? 0 : 1;
|
|
case SPA_POD_TYPE_INT:
|
|
return *(int32_t *) r1 - *(int32_t *) r2;
|
|
case SPA_POD_TYPE_LONG:
|
|
return *(int64_t *) r1 - *(int64_t *) r2;
|
|
case SPA_POD_TYPE_FLOAT:
|
|
return *(float *) r1 - *(float *) r2;
|
|
case SPA_POD_TYPE_DOUBLE:
|
|
return *(double *) r1 - *(double *) r2;
|
|
case SPA_POD_TYPE_STRING:
|
|
return strcmp(r1, r2);
|
|
case SPA_POD_TYPE_RECTANGLE:
|
|
{
|
|
const struct spa_rectangle *rec1 = (struct spa_rectangle *) r1,
|
|
*rec2 = (struct spa_rectangle *) r2;
|
|
if (rec1->width == rec2->width && rec1->height == rec2->height)
|
|
return 0;
|
|
else if (rec1->width < rec2->width || rec1->height < rec2->height)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
case SPA_POD_TYPE_FRACTION:
|
|
{
|
|
const struct spa_fraction *f1 = (struct spa_fraction *) r1,
|
|
*f2 = (struct spa_fraction *) r2;
|
|
int64_t n1, n2;
|
|
n1 = ((int64_t) f1->num) * f2->denom;
|
|
n2 = ((int64_t) f2->num) * f1->denom;
|
|
if (n1 < n2)
|
|
return -1;
|
|
else if (n1 > n2)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void fix_default(struct spa_pod_prop *prop)
|
|
{
|
|
void *val = SPA_MEMBER(prop, sizeof(struct spa_pod_prop), void),
|
|
*alt = SPA_MEMBER(val, prop->body.value.size, void);
|
|
int i, nalt = SPA_POD_PROP_N_VALUES(prop) - 1;
|
|
|
|
switch (prop->body.flags & SPA_POD_PROP_RANGE_MASK) {
|
|
case SPA_POD_PROP_RANGE_NONE:
|
|
break;
|
|
case SPA_POD_PROP_RANGE_MIN_MAX:
|
|
case SPA_POD_PROP_RANGE_STEP:
|
|
if (compare_value(prop->body.value.type, val, alt) < 0)
|
|
memcpy(val, alt, prop->body.value.size);
|
|
alt = SPA_MEMBER(alt, prop->body.value.size, void);
|
|
if (compare_value(prop->body.value.type, val, alt) > 0)
|
|
memcpy(val, alt, prop->body.value.size);
|
|
break;
|
|
case SPA_POD_PROP_RANGE_ENUM:
|
|
{
|
|
void *best = NULL;
|
|
|
|
for (i = 0; i < nalt; i++) {
|
|
if (compare_value(prop->body.value.type, val, alt) == 0) {
|
|
best = alt;
|
|
break;
|
|
}
|
|
if (best == NULL)
|
|
best = alt;
|
|
alt = SPA_MEMBER(alt, prop->body.value.size, void);
|
|
}
|
|
if (best)
|
|
memcpy(val, best, prop->body.value.size);
|
|
|
|
if (nalt <= 1) {
|
|
prop->body.flags &= ~SPA_POD_PROP_FLAG_UNSET;
|
|
prop->body.flags &= ~SPA_POD_PROP_RANGE_MASK;
|
|
prop->body.flags |= SPA_POD_PROP_RANGE_NONE;
|
|
}
|
|
break;
|
|
}
|
|
case SPA_POD_PROP_RANGE_FLAGS:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline struct spa_pod_prop *find_prop(const struct spa_pod *pod, uint32_t size, uint32_t key)
|
|
{
|
|
const struct spa_pod *res;
|
|
SPA_POD_FOREACH(pod, size, res) {
|
|
if (res->type == SPA_POD_TYPE_PROP
|
|
&& ((struct spa_pod_prop *) res)->body.key == key)
|
|
return (struct spa_pod_prop *) res;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
filter_prop(struct spa_pod_builder *b,
|
|
const struct spa_pod_prop *p1,
|
|
const struct spa_pod_prop *p2)
|
|
{
|
|
struct spa_pod_prop *np;
|
|
int nalt1, nalt2;
|
|
void *alt1, *alt2, *a1, *a2;
|
|
uint32_t rt1, rt2;
|
|
int j, k;
|
|
|
|
/* incompatible property types */
|
|
if (p1->body.value.type != p2->body.value.type)
|
|
return -EINVAL;
|
|
|
|
rt1 = p1->body.flags & SPA_POD_PROP_RANGE_MASK;
|
|
rt2 = p2->body.flags & SPA_POD_PROP_RANGE_MASK;
|
|
|
|
alt1 = SPA_MEMBER(p1, sizeof(struct spa_pod_prop), void);
|
|
nalt1 = SPA_POD_PROP_N_VALUES(p1);
|
|
alt2 = SPA_MEMBER(p2, sizeof(struct spa_pod_prop), void);
|
|
nalt2 = SPA_POD_PROP_N_VALUES(p2);
|
|
|
|
if (p1->body.flags & SPA_POD_PROP_FLAG_UNSET) {
|
|
alt1 = SPA_MEMBER(alt1, p1->body.value.size, void);
|
|
nalt1--;
|
|
} else {
|
|
nalt1 = 1;
|
|
rt1 = SPA_POD_PROP_RANGE_NONE;
|
|
}
|
|
|
|
if (p2->body.flags & SPA_POD_PROP_FLAG_UNSET) {
|
|
alt2 = SPA_MEMBER(alt2, p2->body.value.size, void);
|
|
nalt2--;
|
|
} else {
|
|
nalt2 = 1;
|
|
rt2 = SPA_POD_PROP_RANGE_NONE;
|
|
}
|
|
|
|
/* start with copying the property */
|
|
np = spa_pod_builder_deref(b, spa_pod_builder_push_prop(b, p1->body.key, 0));
|
|
|
|
/* default value */
|
|
spa_pod_builder_raw(b, &p1->body.value, sizeof(p1->body.value) + p1->body.value.size);
|
|
|
|
if ((rt1 == SPA_POD_PROP_RANGE_NONE && rt2 == SPA_POD_PROP_RANGE_NONE) ||
|
|
(rt1 == SPA_POD_PROP_RANGE_NONE && rt2 == SPA_POD_PROP_RANGE_ENUM) ||
|
|
(rt1 == SPA_POD_PROP_RANGE_ENUM && rt2 == SPA_POD_PROP_RANGE_NONE) ||
|
|
(rt1 == SPA_POD_PROP_RANGE_ENUM && rt2 == SPA_POD_PROP_RANGE_ENUM)) {
|
|
int n_copied = 0;
|
|
/* copy all equal values but don't copy the default value again */
|
|
for (j = 0, a1 = alt1; j < nalt1; j++, a1 += p1->body.value.size) {
|
|
for (k = 0, a2 = alt2; k < nalt2; k++, a2 += p2->body.value.size) {
|
|
if (compare_value(p1->body.value.type, a1, a2) == 0) {
|
|
if (rt1 == SPA_POD_PROP_RANGE_ENUM || j > 0)
|
|
spa_pod_builder_raw(b, a1, p1->body.value.size);
|
|
n_copied++;
|
|
}
|
|
}
|
|
}
|
|
if (n_copied == 0)
|
|
return -EINVAL;
|
|
np->body.flags |= SPA_POD_PROP_RANGE_ENUM | SPA_POD_PROP_FLAG_UNSET;
|
|
}
|
|
|
|
if ((rt1 == SPA_POD_PROP_RANGE_NONE && rt2 == SPA_POD_PROP_RANGE_MIN_MAX) ||
|
|
(rt1 == SPA_POD_PROP_RANGE_ENUM && rt2 == SPA_POD_PROP_RANGE_MIN_MAX)) {
|
|
int n_copied = 0;
|
|
/* copy all values inside the range */
|
|
for (j = 0, a1 = alt1, a2 = alt2; j < nalt1; j++, a1 += p1->body.value.size) {
|
|
if (compare_value(p1->body.value.type, a1, a2) < 0)
|
|
continue;
|
|
if (compare_value(p1->body.value.type, a1, a2 + p2->body.value.size) > 0)
|
|
continue;
|
|
spa_pod_builder_raw(b, a1, p1->body.value.size);
|
|
n_copied++;
|
|
}
|
|
if (n_copied == 0)
|
|
return -EINVAL;
|
|
np->body.flags |= SPA_POD_PROP_RANGE_ENUM | SPA_POD_PROP_FLAG_UNSET;
|
|
}
|
|
|
|
if ((rt1 == SPA_POD_PROP_RANGE_NONE && rt2 == SPA_POD_PROP_RANGE_STEP) ||
|
|
(rt1 == SPA_POD_PROP_RANGE_ENUM && rt2 == SPA_POD_PROP_RANGE_STEP)) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if ((rt1 == SPA_POD_PROP_RANGE_MIN_MAX && rt2 == SPA_POD_PROP_RANGE_NONE) ||
|
|
(rt1 == SPA_POD_PROP_RANGE_MIN_MAX && rt2 == SPA_POD_PROP_RANGE_ENUM)) {
|
|
int n_copied = 0;
|
|
/* copy all values inside the range */
|
|
for (k = 0, a1 = alt1, a2 = alt2; k < nalt2; k++, a2 += p2->body.value.size) {
|
|
if (compare_value(p1->body.value.type, a2, a1) < 0)
|
|
continue;
|
|
if (compare_value(p1->body.value.type, a2, a1 + p1->body.value.size) > 0)
|
|
continue;
|
|
spa_pod_builder_raw(b, a2, p2->body.value.size);
|
|
n_copied++;
|
|
}
|
|
if (n_copied == 0)
|
|
return -EINVAL;
|
|
np->body.flags |= SPA_POD_PROP_RANGE_ENUM | SPA_POD_PROP_FLAG_UNSET;
|
|
}
|
|
|
|
if (rt1 == SPA_POD_PROP_RANGE_MIN_MAX && rt2 == SPA_POD_PROP_RANGE_MIN_MAX) {
|
|
if (compare_value(p1->body.value.type, alt1, alt2) < 0)
|
|
spa_pod_builder_raw(b, alt2, p2->body.value.size);
|
|
else
|
|
spa_pod_builder_raw(b, alt1, p1->body.value.size);
|
|
|
|
alt1 += p1->body.value.size;
|
|
alt2 += p2->body.value.size;
|
|
|
|
if (compare_value(p1->body.value.type, alt1, alt2) < 0)
|
|
spa_pod_builder_raw(b, alt1, p1->body.value.size);
|
|
else
|
|
spa_pod_builder_raw(b, alt2, p2->body.value.size);
|
|
|
|
np->body.flags |= SPA_POD_PROP_RANGE_MIN_MAX | SPA_POD_PROP_FLAG_UNSET;
|
|
}
|
|
|
|
if (rt1 == SPA_POD_PROP_RANGE_NONE && rt2 == SPA_POD_PROP_RANGE_FLAGS)
|
|
return -ENOTSUP;
|
|
|
|
if (rt1 == SPA_POD_PROP_RANGE_MIN_MAX && rt2 == SPA_POD_PROP_RANGE_STEP)
|
|
return -ENOTSUP;
|
|
|
|
if (rt1 == SPA_POD_PROP_RANGE_MIN_MAX && rt2 == SPA_POD_PROP_RANGE_FLAGS)
|
|
return -ENOTSUP;
|
|
|
|
if (rt1 == SPA_POD_PROP_RANGE_ENUM && rt2 == SPA_POD_PROP_RANGE_FLAGS)
|
|
return -ENOTSUP;
|
|
|
|
if (rt1 == SPA_POD_PROP_RANGE_STEP && rt2 == SPA_POD_PROP_RANGE_NONE)
|
|
return -ENOTSUP;
|
|
if (rt1 == SPA_POD_PROP_RANGE_STEP && rt2 == SPA_POD_PROP_RANGE_MIN_MAX)
|
|
return -ENOTSUP;
|
|
|
|
if (rt1 == SPA_POD_PROP_RANGE_STEP && rt2 == SPA_POD_PROP_RANGE_STEP)
|
|
return -ENOTSUP;
|
|
if (rt1 == SPA_POD_PROP_RANGE_STEP && rt2 == SPA_POD_PROP_RANGE_ENUM)
|
|
return -ENOTSUP;
|
|
if (rt1 == SPA_POD_PROP_RANGE_STEP && rt2 == SPA_POD_PROP_RANGE_FLAGS)
|
|
return -ENOTSUP;
|
|
|
|
if (rt1 == SPA_POD_PROP_RANGE_FLAGS && rt2 == SPA_POD_PROP_RANGE_NONE)
|
|
return -ENOTSUP;
|
|
if (rt1 == SPA_POD_PROP_RANGE_FLAGS && rt2 == SPA_POD_PROP_RANGE_MIN_MAX)
|
|
return -ENOTSUP;
|
|
if (rt1 == SPA_POD_PROP_RANGE_FLAGS && rt2 == SPA_POD_PROP_RANGE_STEP)
|
|
return -ENOTSUP;
|
|
if (rt1 == SPA_POD_PROP_RANGE_FLAGS && rt2 == SPA_POD_PROP_RANGE_ENUM)
|
|
return -ENOTSUP;
|
|
if (rt1 == SPA_POD_PROP_RANGE_FLAGS && rt2 == SPA_POD_PROP_RANGE_FLAGS)
|
|
return -ENOTSUP;
|
|
|
|
spa_pod_builder_pop(b);
|
|
fix_default(np);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pod_filter(struct spa_pod_builder *b,
|
|
const struct spa_pod *pod,
|
|
uint32_t pod_size,
|
|
const struct spa_pod *filter,
|
|
uint32_t filter_size)
|
|
{
|
|
const struct spa_pod *pp, *pf, *tmp;
|
|
int res = 0;
|
|
|
|
pf = filter;
|
|
|
|
SPA_POD_FOREACH_SAFE(pod, pod_size, pp, tmp) {
|
|
bool do_copy = false, do_filter = false, do_advance = false;
|
|
void *pc, *fc;
|
|
uint32_t pcs, fcs;
|
|
|
|
switch (SPA_POD_TYPE(pp)) {
|
|
case SPA_POD_TYPE_STRUCT:
|
|
if (pf != NULL) {
|
|
if (SPA_POD_TYPE(pf) != SPA_POD_TYPE_STRUCT)
|
|
return -EINVAL;
|
|
|
|
pc = SPA_POD_CONTENTS(struct spa_pod_struct, pp);
|
|
pcs = SPA_POD_CONTENTS_SIZE(struct spa_pod_struct, pp);
|
|
fc = SPA_POD_CONTENTS(struct spa_pod_struct, pf);
|
|
fcs = SPA_POD_CONTENTS_SIZE(struct spa_pod_struct, pf);
|
|
|
|
spa_pod_builder_push_struct(b);
|
|
do_filter = true;
|
|
do_advance = true;
|
|
}
|
|
else
|
|
do_copy = true;
|
|
break;
|
|
case SPA_POD_TYPE_OBJECT:
|
|
if (pf != NULL) {
|
|
struct spa_pod_object *p1 = (struct spa_pod_object *) pp;
|
|
|
|
if (SPA_POD_TYPE(pf) != SPA_POD_TYPE_OBJECT)
|
|
return -EINVAL;
|
|
|
|
pc = SPA_POD_CONTENTS(struct spa_pod_object, pp);
|
|
pcs = SPA_POD_CONTENTS_SIZE(struct spa_pod_object, pp);
|
|
fc = SPA_POD_CONTENTS(struct spa_pod_object, pf);
|
|
fcs = SPA_POD_CONTENTS_SIZE(struct spa_pod_object, pf);
|
|
|
|
spa_pod_builder_push_object(b, p1->body.id, p1->body.type);
|
|
do_filter = true;
|
|
do_advance = true;
|
|
}
|
|
else
|
|
do_copy = true;
|
|
break;
|
|
|
|
case SPA_POD_TYPE_PROP:
|
|
{
|
|
struct spa_pod_prop *p1, *p2;
|
|
|
|
p1 = (struct spa_pod_prop *) pp;
|
|
p2 = find_prop(filter, filter_size, p1->body.key);
|
|
|
|
if (p2 != NULL)
|
|
res = filter_prop(b, p1, p2);
|
|
else
|
|
do_copy = true;
|
|
break;
|
|
}
|
|
default:
|
|
if (pf != NULL) {
|
|
if (SPA_POD_SIZE(pp) != SPA_POD_SIZE(pf))
|
|
return -EINVAL;
|
|
if (memcmp(pp, pf, SPA_POD_SIZE(pp)) != 0)
|
|
return -EINVAL;
|
|
do_advance = true;
|
|
}
|
|
do_copy = true;
|
|
break;
|
|
}
|
|
if (do_copy)
|
|
spa_pod_builder_raw_padded(b, pp, SPA_POD_SIZE(pp));
|
|
else if (do_filter) {
|
|
res = pod_filter(b, pc, pcs, fc, fcs);
|
|
spa_pod_builder_pop(b);
|
|
}
|
|
if (do_advance) {
|
|
pf = spa_pod_next(pf);
|
|
if (!spa_pod_is_inside(filter, filter_size, pf))
|
|
pf = NULL;
|
|
}
|
|
|
|
if (res < 0)
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int
|
|
spa_pod_filter(struct spa_pod_builder *b,
|
|
struct spa_pod **result,
|
|
const struct spa_pod *pod,
|
|
const struct spa_pod *filter)
|
|
{
|
|
int res;
|
|
uint32_t offset;
|
|
|
|
spa_return_val_if_fail(pod != NULL, -EINVAL);
|
|
spa_return_val_if_fail(b != NULL, -EINVAL);
|
|
|
|
if (filter == NULL) {
|
|
*result = spa_pod_builder_deref(b,
|
|
spa_pod_builder_raw_padded(b, pod, SPA_POD_SIZE(pod)));
|
|
return 0;
|
|
}
|
|
|
|
offset = b->state.offset;
|
|
|
|
if ((res = pod_filter(b, pod, SPA_POD_SIZE(pod), filter, SPA_POD_SIZE(filter))) == 0)
|
|
*result = spa_pod_builder_deref(b, offset);
|
|
|
|
return res;
|
|
}
|
|
|
|
int pod_compare(const struct spa_pod *pod1,
|
|
uint32_t pod1_size,
|
|
const struct spa_pod *pod2,
|
|
uint32_t pod2_size)
|
|
{
|
|
const struct spa_pod *p1, *p2;
|
|
int res;
|
|
|
|
p2 = pod2;
|
|
|
|
SPA_POD_FOREACH(pod1, pod1_size, p1) {
|
|
bool do_recurse = false, do_advance = true;
|
|
void *a1, *a2;
|
|
void *p1c, *p2c;
|
|
uint32_t p1cs, p2cs;
|
|
|
|
if (p2 == NULL)
|
|
return -EINVAL;
|
|
|
|
switch (SPA_POD_TYPE(p1)) {
|
|
case SPA_POD_TYPE_STRUCT:
|
|
if (SPA_POD_TYPE(p2) != SPA_POD_TYPE_STRUCT)
|
|
return -EINVAL;
|
|
|
|
p1c = SPA_POD_CONTENTS(struct spa_pod_struct, p1);
|
|
p1cs = SPA_POD_CONTENTS_SIZE(struct spa_pod_struct, p1);
|
|
p2c = SPA_POD_CONTENTS(struct spa_pod_struct, p2);
|
|
p2cs = SPA_POD_CONTENTS_SIZE(struct spa_pod_struct, p2);
|
|
do_recurse = true;
|
|
do_advance = true;
|
|
break;
|
|
case SPA_POD_TYPE_OBJECT:
|
|
if (SPA_POD_TYPE(p2) != SPA_POD_TYPE_OBJECT)
|
|
return -EINVAL;
|
|
|
|
p1c = SPA_POD_CONTENTS(struct spa_pod_object, p1);
|
|
p1cs = SPA_POD_CONTENTS_SIZE(struct spa_pod_object, p1);
|
|
p2c = SPA_POD_CONTENTS(struct spa_pod_object, p2);
|
|
p2cs = SPA_POD_CONTENTS_SIZE(struct spa_pod_object, p2);
|
|
do_recurse = true;
|
|
do_advance = true;
|
|
break;
|
|
case SPA_POD_TYPE_PROP:
|
|
{
|
|
struct spa_pod_prop *pr1, *pr2;
|
|
|
|
pr1 = (struct spa_pod_prop *) p1;
|
|
pr2 = find_prop(pod2, pod2_size, pr1->body.key);
|
|
|
|
if (pr2 == NULL)
|
|
return -EINVAL;
|
|
|
|
/* incompatible property types */
|
|
if (pr1->body.value.type != pr2->body.value.type)
|
|
return -EINVAL;
|
|
|
|
if (pr1->body.flags & SPA_POD_PROP_FLAG_UNSET ||
|
|
pr2->body.flags & SPA_POD_PROP_FLAG_UNSET)
|
|
return -EINVAL;
|
|
|
|
a1 = SPA_MEMBER(pr1, sizeof(struct spa_pod_prop), void);
|
|
a2 = SPA_MEMBER(pr2, sizeof(struct spa_pod_prop), void);
|
|
|
|
res = compare_value(pr1->body.value.type, a1, a2);
|
|
break;
|
|
}
|
|
default:
|
|
if (SPA_POD_TYPE(p1) != SPA_POD_TYPE(p2))
|
|
return -EINVAL;
|
|
|
|
res = compare_value(SPA_POD_TYPE(p1), SPA_POD_BODY(p1), SPA_POD_BODY(p2));
|
|
do_advance = true;
|
|
break;
|
|
}
|
|
if (do_advance) {
|
|
p2 = spa_pod_next(p2);
|
|
if (!spa_pod_is_inside(pod2, pod2_size, p2))
|
|
p2 = NULL;
|
|
}
|
|
if (do_recurse) {
|
|
res = pod_compare(p1c, p1cs, p2c, p2cs);
|
|
}
|
|
if (res != 0)
|
|
return res;
|
|
}
|
|
if (p2 != NULL)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int spa_pod_compare(const struct spa_pod *pod1,
|
|
const struct spa_pod *pod2)
|
|
{
|
|
spa_return_val_if_fail(pod1 != NULL, -EINVAL);
|
|
spa_return_val_if_fail(pod2 != NULL, -EINVAL);
|
|
|
|
return pod_compare(pod1, SPA_POD_SIZE(pod1), pod2, SPA_POD_SIZE(pod2));
|
|
}
|