Cache node, port and device params

So that we don't need to keep asking when it didn't change.

Fixes #455
This commit is contained in:
Wim Taymans 2020-12-09 17:07:34 +01:00
parent ec77979890
commit ccfe439786
5 changed files with 366 additions and 46 deletions

View file

@ -26,6 +26,7 @@
#include <spa/debug/types.h>
#include <spa/monitor/utils.h>
#include <spa/pod/filter.h>
#include "pipewire/impl.h"
#include "pipewire/private.h"
@ -34,6 +35,11 @@
struct impl {
struct pw_impl_device this;
struct spa_list param_list;
struct spa_list pending_list;
unsigned int cache_params:1;
};
#define pw_device_resource(r,m,v,...) pw_resource_call(r,struct pw_device_events,m,v,__VA_ARGS__)
@ -41,10 +47,13 @@ struct impl {
#define pw_device_resource_param(r,...) pw_device_resource(r,param,0,__VA_ARGS__)
struct result_device_params_data {
struct impl *impl;
void *data;
int (*callback) (void *data, int seq,
uint32_t id, uint32_t index, uint32_t next,
struct spa_pod *param);
int seq;
unsigned int cache:1;
};
struct resource_data {
@ -61,6 +70,7 @@ struct resource_data {
int seq;
int orig_seq;
int end;
struct spa_param_info *pi;
struct result_device_params_data data;
struct spa_hook listener;
};
@ -139,6 +149,9 @@ struct pw_impl_device *pw_context_create_device(struct pw_context *context,
res = -errno;
goto error_cleanup;
}
spa_list_init(&impl->param_list);
spa_list_init(&impl->pending_list);
impl->cache_params = true;
this = &impl->this;
this->name = strdup("device");
@ -179,6 +192,7 @@ error_cleanup:
SPA_EXPORT
void pw_impl_device_destroy(struct pw_impl_device *device)
{
struct impl *impl = SPA_CONTAINER_OF(device, struct impl, this);
struct object_data *od;
pw_log_debug(NAME" %p: destroy", device);
@ -200,6 +214,9 @@ void pw_impl_device_destroy(struct pw_impl_device *device)
pw_log_debug(NAME" %p: free", device);
pw_impl_device_emit_free(device);
pw_param_clear(&impl->param_list, SPA_ID_INVALID);
pw_param_clear(&impl->pending_list, SPA_ID_INVALID);
spa_hook_list_clean(&device->listener_list);
pw_properties_free(device->properties);
@ -210,7 +227,15 @@ void pw_impl_device_destroy(struct pw_impl_device *device)
static void remove_busy_resource(struct resource_data *d)
{
struct pw_impl_device *device = d->device;
struct impl *impl = SPA_CONTAINER_OF(device, struct impl, this);
if (d->end != -1) {
if (d->pi && d->data.cache) {
pw_param_update(&impl->param_list, &impl->pending_list);
d->pi->user = 1;
d->pi = NULL;
}
spa_hook_remove(&d->listener);
d->end = -1;
pw_impl_client_set_busy(d->resource->client, false);
@ -242,11 +267,18 @@ static const struct pw_resource_events resource_events = {
static void result_device_params(void *data, int seq, int res, uint32_t type, const void *result)
{
struct result_device_params_data *d = data;
struct impl *impl = d->impl;
pw_log_debug(NAME" %p: type %d", impl, type);
switch (type) {
case SPA_RESULT_TYPE_DEVICE_PARAMS:
{
const struct spa_result_device_params *r = result;
d->callback(d->data, seq, r->id, r->index, r->next, r->param);
if (d->cache) {
pw_log_debug(NAME" %p: add param %d", impl, r->id);
pw_param_add(&impl->pending_list, r->id, r->param);
}
break;
}
default:
@ -265,26 +297,73 @@ int pw_impl_device_for_each_param(struct pw_impl_device *device,
void *data)
{
int res;
struct result_device_params_data user_data = { data, callback };
struct impl *impl = SPA_CONTAINER_OF(device, struct impl, this);
struct result_device_params_data user_data = { impl, data, callback, seq, false };
struct spa_hook listener;
struct spa_param_info *pi;
static const struct spa_device_events device_events = {
SPA_VERSION_DEVICE_EVENTS,
.result = result_device_params,
};
pi = pw_param_info_find(device->info.params, device->info.n_params, param_id);
if (pi == NULL)
return -ENOENT;
if (max == 0)
max = UINT32_MAX;
pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u", device, param_id,
pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u cached:%d", device, param_id,
spa_debug_type_find_name(spa_type_param, param_id),
index, max);
index, max, pi->user);
spa_zero(listener);
spa_device_add_listener(device->device, &listener,
&device_events, &user_data);
res = spa_device_enum_params(device->device, seq,
param_id, index, max, filter);
spa_hook_remove(&listener);
if (pi->user == 1) {
struct pw_param *p;
uint8_t buffer[1024];
struct spa_pod_builder b = { 0 };
struct spa_result_device_params result;
uint32_t count = 0;
bool found = false;
result.id = param_id;
result.next = 0;
spa_list_for_each(p, &impl->param_list, link) {
result.index = result.next++;
if (p->id != param_id)
continue;
found = true;
if (result.index < index)
continue;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
if (spa_pod_filter(&b, &result.param, p->param, filter) != 0)
continue;
pw_log_debug(NAME " %p: %d param %u", device, seq, result.index);
result_device_params(&user_data, seq, 0, SPA_RESULT_TYPE_DEVICE_PARAMS, &result);
if (++count == max)
break;
}
res = found ? 0 : -ENOENT;
} else {
user_data.cache = impl->cache_params && filter == NULL;
spa_zero(listener);
spa_device_add_listener(device->device, &listener,
&device_events, &user_data);
res = spa_device_enum_params(device->device, seq,
param_id, index, max, filter);
spa_hook_remove(&listener);
if (!SPA_RESULT_IS_ASYNC(res) && user_data.cache) {
pw_param_update(&impl->param_list, &impl->pending_list);
pi->user = 1;
}
}
return res;
}
@ -304,11 +383,10 @@ static void result_device_params_async(void *data, int seq, int res, uint32_t ty
pw_log_debug(NAME" %p: async result %d %d (%d/%d)", d->device,
res, seq, d->seq, d->end);
if (seq == d->end)
remove_busy_resource(d);
if (seq == d->seq)
result_device_params(&d->data, d->orig_seq, res, type, result);
if (seq == d->end)
remove_busy_resource(d);
}
static int device_enum_params(void *object, int seq, uint32_t id, uint32_t start, uint32_t num,
@ -317,6 +395,7 @@ static int device_enum_params(void *object, int seq, uint32_t id, uint32_t start
struct resource_data *data = object;
struct pw_resource *resource = data->resource;
struct pw_impl_device *device = data->device;
struct impl *impl = SPA_CONTAINER_OF(device, struct impl, this);
struct pw_impl_client *client = resource->client;
int res;
static const struct spa_device_events device_events = {
@ -333,11 +412,15 @@ static int device_enum_params(void *object, int seq, uint32_t id, uint32_t start
spa_debug_type_find_name(spa_type_param, id));
} else if (SPA_RESULT_IS_ASYNC(res)) {
pw_impl_client_set_busy(client, true);
data->data.impl = impl;
data->data.data = data;
data->data.callback = reply_param;
data->data.cache = impl->cache_params && filter == NULL;
if (data->end == -1)
spa_device_add_listener(device->device, &data->listener,
&device_events, data);
data->pi = pw_param_info_find(device->info.params,
device->info.n_params, id);
data->orig_seq = seq;
data->seq = res;
data->end = spa_device_sync(device->device, res);
@ -634,8 +717,12 @@ static void emit_params(struct pw_impl_device *device, uint32_t *changed_ids, ui
static void device_info(void *data, const struct spa_device_info *info)
{
struct pw_impl_device *device = data;
struct impl *impl = SPA_CONTAINER_OF(device, struct impl, this);
uint32_t changed_ids[MAX_PARAMS], n_changed_ids = 0;
pw_log_debug(NAME" %p: flags:%08"PRIx64" change_mask:%08"PRIx64,
device, info->flags, info->change_mask);
if (info->change_mask & SPA_DEVICE_CHANGE_MASK_PROPS) {
update_properties(device, info->props);
}
@ -646,16 +733,22 @@ static void device_info(void *data, const struct spa_device_info *info)
device->info.n_params = SPA_MIN(info->n_params, SPA_N_ELEMENTS(device->params));
for (i = 0; i < device->info.n_params; i++) {
uint32_t id = info->params[i].id;
pw_log_debug(NAME" %p: param %d id:%d (%s) %08x:%08x", device, i,
info->params[i].id,
spa_debug_type_find_name(spa_type_param, info->params[i].id),
id, spa_debug_type_find_name(spa_type_param, id),
device->info.params[i].flags, info->params[i].flags);
if (device->info.params[i].flags != info->params[i].flags &&
info->params[i].flags & SPA_PARAM_INFO_READ)
changed_ids[n_changed_ids++] = info->params[i].id;
if (device->info.params[i].flags == info->params[i].flags)
continue;
pw_log_debug(NAME" %p: update param %d", device, id);
pw_param_clear(&impl->pending_list, id);
device->info.params[i] = info->params[i];
device->info.params[i].user = 0;
if (info->params[i].flags & SPA_PARAM_INFO_READ)
changed_ids[n_changed_ids++] = id;
}
}
emit_info_changed(device);

View file

@ -31,6 +31,7 @@
#include <spa/support/system.h>
#include <spa/pod/parser.h>
#include <spa/pod/filter.h>
#include <spa/node/utils.h>
#include <spa/debug/types.h>
@ -52,7 +53,11 @@ struct impl {
int last_error;
struct spa_list param_list;
struct spa_list pending_list;
unsigned int pause_on_idle:1;
unsigned int cache_params:1;
};
#define pw_node_resource(r,m,v,...) pw_resource_call(r,struct pw_node_events,m,v,__VA_ARGS__)
@ -814,6 +819,11 @@ static void check_properties(struct pw_impl_node *node)
else
impl->pause_on_idle = true;
if ((str = pw_properties_get(node->properties, PW_KEY_NODE_CACHE_PARAMS)))
impl->cache_params = pw_properties_parse_bool(str);
else
impl->cache_params = true;
if ((str = pw_properties_get(node->properties, PW_KEY_NODE_DRIVER)))
driver = pw_properties_parse_bool(str);
else
@ -1071,6 +1081,9 @@ struct pw_impl_node *pw_context_create_node(struct pw_context *context,
goto error_exit;
}
spa_list_init(&impl->param_list);
spa_list_init(&impl->pending_list);
this = &impl->this;
this->context = context;
this->name = strdup("node");
@ -1229,6 +1242,7 @@ int pw_impl_node_update_properties(struct pw_impl_node *node, const struct spa_d
static void node_info(void *data, const struct spa_node_info *info)
{
struct pw_impl_node *node = data;
struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this);
uint32_t changed_ids[MAX_PARAMS], n_changed_ids = 0;
bool flags_changed = false;
@ -1256,16 +1270,22 @@ static void node_info(void *data, const struct spa_node_info *info)
node->info.n_params = SPA_MIN(info->n_params, SPA_N_ELEMENTS(node->params));
for (i = 0; i < node->info.n_params; i++) {
uint32_t id = info->params[i].id;
pw_log_debug(NAME" %p: param %d id:%d (%s) %08x:%08x", node, i,
info->params[i].id,
spa_debug_type_find_name(spa_type_param, info->params[i].id),
id, spa_debug_type_find_name(spa_type_param, id),
node->info.params[i].flags, info->params[i].flags);
if (node->info.params[i].flags != info->params[i].flags &&
info->params[i].flags & SPA_PARAM_INFO_READ)
changed_ids[n_changed_ids++] = info->params[i].id;
if (node->info.params[i].flags == info->params[i].flags)
continue;
pw_log_debug(NAME" %p: update param %d", node, id);
pw_param_clear(&impl->pending_list, id);
node->info.params[i] = info->params[i];
node->info.params[i].user = 0;
if (info->params[i].flags & SPA_PARAM_INFO_READ)
changed_ids[n_changed_ids++] = id;
}
}
emit_info_changed(node, flags_changed);
@ -1686,6 +1706,9 @@ void pw_impl_node_destroy(struct pw_impl_node *node)
pw_work_queue_destroy(impl->work);
pw_param_clear(&impl->param_list, SPA_ID_INVALID);
pw_param_clear(&impl->pending_list, SPA_ID_INVALID);
pw_map_clear(&node->input_port_map);
pw_map_clear(&node->output_port_map);
@ -1719,22 +1742,28 @@ int pw_impl_node_for_each_port(struct pw_impl_node *node,
}
struct result_node_params_data {
struct impl *impl;
void *data;
int (*callback) (void *data, int seq,
uint32_t id, uint32_t index, uint32_t next,
struct spa_pod *param);
int seq;
unsigned int cache:1;
};
static void result_node_params(void *data, int seq, int res, uint32_t type, const void *result)
{
struct result_node_params_data *d = data;
struct impl *impl = d->impl;
switch (type) {
case SPA_RESULT_TYPE_NODE_PARAMS:
{
const struct spa_result_node_params *r = result;
if (d->seq == seq)
if (d->seq == seq) {
d->callback(d->data, seq, r->id, r->index, r->next, r->param);
if (d->cache)
pw_param_add(&impl->pending_list, r->id, r->param);
}
break;
}
default:
@ -1753,27 +1782,73 @@ int pw_impl_node_for_each_param(struct pw_impl_node *node,
void *data)
{
int res;
struct result_node_params_data user_data = { data, callback, seq };
struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this);
struct result_node_params_data user_data = { impl, data, callback, seq, false };
struct spa_hook listener;
struct spa_param_info *pi;
static const struct spa_node_events node_events = {
SPA_VERSION_NODE_EVENTS,
.result = result_node_params,
};
pi = pw_param_info_find(node->info.params, node->info.n_params, param_id);
if (pi == NULL)
return -ENOENT;
if (max == 0)
max = UINT32_MAX;
pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u", node, param_id,
pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u cached:%d", node, param_id,
spa_debug_type_find_name(spa_type_param, param_id),
index, max);
index, max, pi->user);
spa_zero(listener);
spa_node_add_listener(node->node, &listener, &node_events, &user_data);
res = spa_node_enum_params(node->node, seq,
param_id, index, max,
filter);
spa_hook_remove(&listener);
if (pi->user == 1) {
struct pw_param *p;
uint8_t buffer[1024];
struct spa_pod_builder b = { 0 };
struct spa_result_node_params result;
uint32_t count = 0;
bool found = false;
result.id = param_id;
result.next = 0;
spa_list_for_each(p, &impl->param_list, link) {
result.index = result.next++;
if (p->id != param_id)
continue;
found = true;
if (result.index < index)
continue;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
if (spa_pod_filter(&b, &result.param, p->param, filter) != 0)
continue;
pw_log_debug(NAME " %p: %d param %u", node, seq, result.index);
result_node_params(&user_data, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
if (++count == max)
break;
}
res = found ? 0 : -ENOENT;
} else {
user_data.cache = impl->cache_params && filter == NULL;
spa_zero(listener);
spa_node_add_listener(node->node, &listener, &node_events, &user_data);
res = spa_node_enum_params(node->node, seq,
param_id, index, max,
filter);
spa_hook_remove(&listener);
if (user_data.cache) {
pw_param_update(&impl->param_list, &impl->pending_list);
pi->user = 1;
}
}
return res;
}

View file

@ -31,6 +31,7 @@
#include <spa/node/utils.h>
#include <spa/utils/names.h>
#include <spa/debug/types.h>
#include <spa/pod/filter.h>
#include "pipewire/impl.h"
#include "pipewire/private.h"
@ -41,6 +42,11 @@
struct impl {
struct pw_impl_port this;
struct spa_node mix_node; /**< mix node implementation */
struct spa_list param_list;
struct spa_list pending_list;
unsigned int cache_params:1;
};
#define pw_port_resource(r,m,v,...) pw_resource_call(r,struct pw_port_events,m,v,__VA_ARGS__)
@ -338,8 +344,12 @@ static void emit_params(struct pw_impl_port *port, uint32_t *changed_ids, uint32
static void update_info(struct pw_impl_port *port, const struct spa_port_info *info)
{
struct impl *impl = SPA_CONTAINER_OF(port, struct impl, this);
uint32_t changed_ids[MAX_PARAMS], n_changed_ids = 0;
pw_log_debug(NAME" %p: flags:%08"PRIx64" change_mask:%08"PRIx64,
port, info->flags, info->change_mask);
if (info->change_mask & SPA_PORT_CHANGE_MASK_FLAGS) {
port->spa_flags = info->flags;
}
@ -357,11 +367,22 @@ static void update_info(struct pw_impl_port *port, const struct spa_port_info *i
port->info.n_params = SPA_MIN(info->n_params, SPA_N_ELEMENTS(port->params));
for (i = 0; i < port->info.n_params; i++) {
if (port->info.params[i].flags != info->params[i].flags &&
info->params[i].flags & SPA_PARAM_INFO_READ)
changed_ids[n_changed_ids++] = info->params[i].id;
uint32_t id = info->params[i].id;
pw_log_debug(NAME" %p: param %d id:%d (%s) %08x:%08x", port, i,
id, spa_debug_type_find_name(spa_type_param, id),
port->info.params[i].flags, info->params[i].flags);
if (port->info.params[i].flags == info->params[i].flags)
continue;
pw_log_debug(NAME" %p: update param %d", port, id);
pw_param_clear(&impl->pending_list, id);
port->info.params[i] = info->params[i];
port->info.params[i].user = 0;
if (info->params[i].flags & SPA_PARAM_INFO_READ)
changed_ids[n_changed_ids++] = id;
}
}
@ -387,6 +408,10 @@ struct pw_impl_port *pw_context_create_port(
if (impl == NULL)
return NULL;
spa_list_init(&impl->param_list);
spa_list_init(&impl->pending_list);
impl->cache_params = true;
this = &impl->this;
pw_log_debug(NAME" %p: new %s %d", this,
pw_direction_as_string(direction), port_id);
@ -994,6 +1019,7 @@ static void pw_impl_port_remove(struct pw_impl_port *port)
void pw_impl_port_destroy(struct pw_impl_port *port)
{
struct impl *impl = SPA_CONTAINER_OF(port, struct impl, this);
struct pw_control *control;
pw_log_debug(NAME" %p: destroy", port);
@ -1024,6 +1050,9 @@ void pw_impl_port_destroy(struct pw_impl_port *port)
pw_buffers_clear(&port->mix_buffers);
free((void*)port->error);
pw_param_clear(&impl->param_list, SPA_ID_INVALID);
pw_param_clear(&impl->pending_list, SPA_ID_INVALID);
pw_map_clear(&port->mix_port_map);
pw_properties_free(port->properties);
@ -1032,22 +1061,28 @@ void pw_impl_port_destroy(struct pw_impl_port *port)
}
struct result_port_params_data {
struct impl *impl;
void *data;
int (*callback) (void *data, int seq,
uint32_t id, uint32_t index, uint32_t next,
struct spa_pod *param);
int seq;
unsigned int cache:1;
};
static void result_port_params(void *data, int seq, int res, uint32_t type, const void *result)
{
struct result_port_params_data *d = data;
struct impl *impl = d->impl;
switch (type) {
case SPA_RESULT_TYPE_NODE_PARAMS:
{
const struct spa_result_node_params *r = result;
if (d->seq == seq)
if (d->seq == seq) {
d->callback(d->data, seq, r->id, r->index, r->next, r->param);
if (d->cache)
pw_param_add(&impl->pending_list, r->id, r->param);
}
break;
}
default:
@ -1066,28 +1101,75 @@ int pw_impl_port_for_each_param(struct pw_impl_port *port,
void *data)
{
int res;
struct impl *impl = SPA_CONTAINER_OF(port, struct impl, this);
struct pw_impl_node *node = port->node;
struct result_port_params_data user_data = { data, callback, seq };
struct result_port_params_data user_data = { impl, data, callback, seq, false };
struct spa_hook listener;
struct spa_param_info *pi;
static const struct spa_node_events node_events = {
SPA_VERSION_NODE_EVENTS,
.result = result_port_params,
};
pi = pw_param_info_find(port->info.params, port->info.n_params, param_id);
if (pi == NULL)
return -ENOENT;
if (max == 0)
max = UINT32_MAX;
pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u", port, param_id,
pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u cached:%d", port, param_id,
spa_debug_type_find_name(spa_type_param, param_id),
index, max);
index, max, pi->user);
spa_zero(listener);
spa_node_add_listener(node->node, &listener, &node_events, &user_data);
res = spa_node_port_enum_params(node->node, seq,
port->direction, port->port_id,
param_id, index, max,
filter);
spa_hook_remove(&listener);
if (pi->user == 1) {
struct pw_param *p;
uint8_t buffer[1024];
struct spa_pod_builder b = { 0 };
struct spa_result_node_params result;
uint32_t count = 0;
bool found = false;
result.id = param_id;
result.next = 0;
spa_list_for_each(p, &impl->param_list, link) {
result.index = result.next++;
if (p->id != param_id)
continue;
found = true;
if (result.index < index)
continue;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
if (spa_pod_filter(&b, &result.param, p->param, filter) != 0)
continue;
pw_log_debug(NAME " %p: %d param %u", port, seq, result.index);
result_port_params(&user_data, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
if (++count == max)
break;
}
res = found ? 0 : -ENOENT;
} else {
user_data.cache = filter == NULL;
spa_zero(listener);
spa_node_add_listener(node->node, &listener, &node_events, &user_data);
res = spa_node_port_enum_params(node->node, seq,
port->direction, port->port_id,
param_id, index, max,
filter);
spa_hook_remove(&listener);
if (user_data.cache) {
pw_param_update(&impl->param_list, &impl->pending_list);
pi->user = 1;
}
}
pw_log_debug(NAME" %p: res %d: (%s)", port, res, spa_strerror(res));
return res;

View file

@ -142,6 +142,7 @@ extern "C" {
#define PW_KEY_NODE_DONT_RECONNECT "node.dont-reconnect" /**< don't reconnect this node */
#define PW_KEY_NODE_ALWAYS_PROCESS "node.always-process" /**< process even when unlinked */
#define PW_KEY_NODE_PAUSE_ON_IDLE "node.pause-on-idle" /**< pause the node when idle */
#define PW_KEY_NODE_CACHE_PARAMS "node.cache-params" /**< cache the node params */
#define PW_KEY_NODE_DRIVER "node.driver" /**< node can drive the graph */
#define PW_KEY_NODE_STREAM "node.stream" /**< node is a stream, the server side should
* add a converter */
@ -155,6 +156,7 @@ extern "C" {
#define PW_KEY_PORT_TERMINAL "port.terminal" /**< if this port consumes the data */
#define PW_KEY_PORT_CONTROL "port.control" /**< if this port is a control port */
#define PW_KEY_PORT_MONITOR "port.monitor" /**< if this port is a monitor port */
#define PW_KEY_PORT_CACHE_PARAMS "port.cache-params" /**< cache the node port params */
/** link properties */
#define PW_KEY_LINK_ID "link.id" /**< a link id */
@ -202,6 +204,7 @@ extern "C" {
* roles (see PW_KEY_MEDIA_ROLE) this device
* is particularly well suited for, due to
* latency, quality or form factor. */
#define PW_KEY_DEVICE_CACHE_PARAMS "device.cache-params" /**< cache the device spa params */
/** module properties */
#define PW_KEY_MODULE_ID "module.id" /**< the module id */

View file

@ -84,6 +84,73 @@ static inline bool ratelimit_test(struct ratelimit *r, uint64_t now)
#define MAX_PARAMS 32
struct pw_param {
uint32_t id;
struct spa_list link;
struct spa_pod *param;
};
static inline struct pw_param *pw_param_add(struct spa_list *params,
uint32_t id, const struct spa_pod *param)
{
struct pw_param *p;
if (param == NULL || !spa_pod_is_object(param)) {
errno = EINVAL;
return NULL;
}
if (id == SPA_ID_INVALID)
id = SPA_POD_OBJECT_ID(param);
if ((p = malloc(sizeof(*p) + SPA_POD_SIZE(param))) == NULL)
return NULL;
p->id = id;
p->param = SPA_MEMBER(p, sizeof(*p), struct spa_pod);
memcpy(p->param, param, SPA_POD_SIZE(param));
spa_list_append(params, &p->link);
return p;
}
static inline uint32_t pw_param_clear(struct spa_list *param_list, uint32_t id)
{
struct pw_param *p, *t;
uint32_t count = 0;
spa_list_for_each_safe(p, t, param_list, link) {
if (id == SPA_ID_INVALID || p->id == id) {
spa_list_remove(&p->link);
free(p);
count++;
}
}
return count;
}
static inline void pw_param_update(struct spa_list *param_list, struct spa_list *pending_list)
{
struct pw_param *p;
spa_list_for_each(p, pending_list, link)
pw_param_clear(param_list, p->id);
spa_list_consume(p, pending_list, link) {
spa_list_remove(&p->link);
spa_list_append(param_list, &p->link);
}
}
static inline struct spa_param_info *pw_param_info_find(struct spa_param_info info[],
uint32_t n_info, uint32_t id)
{
uint32_t i;
for (i = 0; i < n_info; i++) {
if (info[i].id == id)
return &info[i];
}
return NULL;
}
#define pw_protocol_emit_destroy(p) spa_hook_list_call(&p->listener_list, struct pw_protocol_events, destroy, 0)
struct pw_protocol {