device: improve callbacks

Pass info structures to device callbacks to make it more extensible.
Also notify about the supported params on the device because we
can.
This commit is contained in:
Wim Taymans 2019-02-13 11:13:46 +01:00
parent 5de7898808
commit ac5ec9bbe2
7 changed files with 135 additions and 64 deletions

View file

@ -37,6 +37,34 @@ struct spa_device;
#include <spa/pod/builder.h> #include <spa/pod/builder.h>
#include <spa/pod/event.h> #include <spa/pod/event.h>
struct spa_device_info {
#define SPA_VERSION_DEVICE_INFO 0
uint32_t version;
#define SPA_DEVICE_CHANGE_MASK_INFO (1<<0)
#define SPA_DEVICE_CHANGE_MASK_PARAMS (1<<1)
uint64_t change_mask;
const struct spa_dict *info;
uint32_t n_params;
uint32_t *params;
};
#define SPA_DEVICE_INFO_INIT() (struct spa_device_info){ SPA_VERSION_DEVICE_INFO, }
struct spa_device_object_info {
#define SPA_VERSION_DEVICE_OBJECT_INFO 0
uint32_t version;
uint32_t type;
const struct spa_handle_factory *factory;
#define SPA_DEVICE_OBJECT_CHANGE_MASK_INFO (1<<0)
uint64_t change_mask;
const struct spa_dict *info;
};
#define SPA_DEVICE_OBJECT_INFO_INIT() (struct spa_device_object_info){ SPA_VERSION_DEVICE_OBJECT_INFO, }
/** /**
* spa_device_callbacks: * spa_device_callbacks:
*/ */
@ -45,18 +73,16 @@ struct spa_device_callbacks {
#define SPA_VERSION_DEVICE_CALLBACKS 0 #define SPA_VERSION_DEVICE_CALLBACKS 0
uint32_t version; uint32_t version;
/**< notify extra information about the device */ /** notify extra information about the device */
void (*info) (void *data, const struct spa_dict *info); void (*info) (void *data, const struct spa_device_info *info);
/** a device event */ /** a device event */
void (*event) (void *data, struct spa_event *event); void (*event) (void *data, struct spa_event *event);
/**< add a new object managed by the device */ /** info changed for an object managed by the device, info is NULL when
void (*add) (void *data, uint32_t id, * the object is removed */
const struct spa_handle_factory *factory, uint32_t type, void (*object_info) (void *data, uint32_t id,
const struct spa_dict *info); const struct spa_device_object_info *info);
/**< remove an object */
void (*remove) (void *data, uint32_t id);
}; };
/** /**

View file

@ -107,27 +107,28 @@ static const char *get_subclass(snd_pcm_info_t *pcminfo)
static int emit_node(struct impl *this, snd_pcm_info_t *pcminfo, uint32_t id) static int emit_node(struct impl *this, snd_pcm_info_t *pcminfo, uint32_t id)
{ {
struct spa_dict_item items[6]; struct spa_dict_item items[6];
const struct spa_handle_factory *factory;
char device_name[128]; char device_name[128];
struct spa_device_object_info info;
info = SPA_DEVICE_OBJECT_INFO_INIT();
info.type = SPA_TYPE_INTERFACE_Node;
if (snd_pcm_info_get_stream(pcminfo) == SND_PCM_STREAM_PLAYBACK)
info.factory = &spa_alsa_sink_factory;
else
info.factory = &spa_alsa_source_factory;
info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_INFO;
snprintf(device_name, 128, "%s,%d", this->props.device, snd_pcm_info_get_device(pcminfo)); snprintf(device_name, 128, "%s,%d", this->props.device, snd_pcm_info_get_device(pcminfo));
items[0] = SPA_DICT_ITEM_INIT("alsa.device", device_name); items[0] = SPA_DICT_ITEM_INIT("alsa.device", device_name);
items[1] = SPA_DICT_ITEM_INIT("alsa.pcm.id", snd_pcm_info_get_id(pcminfo)); items[1] = SPA_DICT_ITEM_INIT("alsa.pcm.id", snd_pcm_info_get_id(pcminfo));
items[2] = SPA_DICT_ITEM_INIT("alsa.pcm.name", snd_pcm_info_get_name(pcminfo)); items[2] = SPA_DICT_ITEM_INIT("alsa.pcm.name", snd_pcm_info_get_name(pcminfo));
items[3] = SPA_DICT_ITEM_INIT("alsa.pcm.subname", snd_pcm_info_get_subdevice_name(pcminfo)); items[3] = SPA_DICT_ITEM_INIT("alsa.pcm.subname", snd_pcm_info_get_subdevice_name(pcminfo));
items[4] = SPA_DICT_ITEM_INIT("alsa.pcm.class", get_class(pcminfo)); items[4] = SPA_DICT_ITEM_INIT("alsa.pcm.class", get_class(pcminfo));
items[5] = SPA_DICT_ITEM_INIT("alsa.pcm.subclass", get_subclass(pcminfo)); items[5] = SPA_DICT_ITEM_INIT("alsa.pcm.subclass", get_subclass(pcminfo));
info.info = &SPA_DICT_INIT_ARRAY(items);
if (snd_pcm_info_get_stream(pcminfo) == SND_PCM_STREAM_PLAYBACK) this->callbacks->object_info(this->callbacks_data, id, &info);
factory = &spa_alsa_sink_factory;
else
factory = &spa_alsa_source_factory;
this->callbacks->add(this->callbacks_data, id,
factory,
SPA_TYPE_INTERFACE_Node,
&SPA_DICT_INIT_ARRAY(items));
return 0; return 0;
} }
@ -141,9 +142,9 @@ static int activate_profile(struct impl *this, snd_ctl_t *ctl_hndl, uint32_t id)
spa_log_debug(this->log, "profile %d", id); spa_log_debug(this->log, "profile %d", id);
this->profile = id; this->profile = id;
if (this->callbacks && this->callbacks->remove) { if (this->callbacks->object_info) {
for (i = 0; i < this->n_nodes; i++) { for (i = 0; i < this->n_nodes; i++) {
this->callbacks->remove(this->callbacks_data, i); this->callbacks->object_info(this->callbacks_data, i, NULL);
} }
} }
this->n_nodes = 0; this->n_nodes = 0;
@ -170,7 +171,7 @@ static int activate_profile(struct impl *this, snd_ctl_t *ctl_hndl, uint32_t id)
if (err != -ENOENT) if (err != -ENOENT)
spa_log_error(this->log, "error pcm info: %s", snd_strerror(err)); spa_log_error(this->log, "error pcm info: %s", snd_strerror(err));
} }
if (err >= 0 && this->callbacks->add) if (err >= 0 && this->callbacks->object_info)
emit_node(this, pcminfo, i++); emit_node(this, pcminfo, i++);
snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE); snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE);
@ -178,7 +179,7 @@ static int activate_profile(struct impl *this, snd_ctl_t *ctl_hndl, uint32_t id)
if (err != -ENOENT) if (err != -ENOENT)
spa_log_error(this->log, "error pcm info: %s", snd_strerror(err)); spa_log_error(this->log, "error pcm info: %s", snd_strerror(err));
} }
if (err >= 0 && this->callbacks->add) if (err >= 0 && this->callbacks->object_info)
emit_node(this, pcminfo, i++); emit_node(this, pcminfo, i++);
} }
this->n_nodes = i; this->n_nodes = i;
@ -212,6 +213,8 @@ static int emit_info(struct impl *this)
struct spa_dict_item items[10]; struct spa_dict_item items[10];
snd_ctl_t *ctl_hndl; snd_ctl_t *ctl_hndl;
snd_ctl_card_info_t *info; snd_ctl_card_info_t *info;
struct spa_device_info dinfo;
uint32_t params[] = { SPA_PARAM_EnumProfile, SPA_PARAM_Profile };
spa_log_info(this->log, "open card %s", this->props.device); spa_log_info(this->log, "open card %s", this->props.device);
if ((err = snd_ctl_open(&ctl_hndl, this->props.device, 0)) < 0) { if ((err = snd_ctl_open(&ctl_hndl, this->props.device, 0)) < 0) {
@ -226,6 +229,9 @@ static int emit_info(struct impl *this)
goto exit; goto exit;
} }
dinfo = SPA_DEVICE_INFO_INIT();
dinfo.change_mask = SPA_DEVICE_CHANGE_MASK_INFO | SPA_DEVICE_CHANGE_MASK_PARAMS;
items[0] = SPA_DICT_ITEM_INIT("device.api", "alsa"); items[0] = SPA_DICT_ITEM_INIT("device.api", "alsa");
items[1] = SPA_DICT_ITEM_INIT("device.path", (char *)this->props.device); items[1] = SPA_DICT_ITEM_INIT("device.path", (char *)this->props.device);
items[2] = SPA_DICT_ITEM_INIT("device.nick", snd_ctl_card_info_get_id(info)); items[2] = SPA_DICT_ITEM_INIT("device.nick", snd_ctl_card_info_get_id(info));
@ -236,9 +242,12 @@ static int emit_info(struct impl *this)
items[7] = SPA_DICT_ITEM_INIT("alsa.card.name", snd_ctl_card_info_get_name(info)); items[7] = SPA_DICT_ITEM_INIT("alsa.card.name", snd_ctl_card_info_get_name(info));
items[8] = SPA_DICT_ITEM_INIT("alsa.card.longname", snd_ctl_card_info_get_longname(info)); items[8] = SPA_DICT_ITEM_INIT("alsa.card.longname", snd_ctl_card_info_get_longname(info));
items[9] = SPA_DICT_ITEM_INIT("alsa.card.mixername", snd_ctl_card_info_get_mixername(info)); items[9] = SPA_DICT_ITEM_INIT("alsa.card.mixername", snd_ctl_card_info_get_mixername(info));
dinfo.info = &SPA_DICT_INIT(items, 10);
dinfo.n_params = SPA_N_ELEMENTS(params);
dinfo.params = params;
if (this->callbacks->info) if (this->callbacks->info)
this->callbacks->info(this->callbacks_data, &SPA_DICT_INIT(items, 10)); this->callbacks->info(this->callbacks_data, &dinfo);
activate_profile(this, ctl_hndl, 0); activate_profile(this, ctl_hndl, 0);

View file

@ -73,8 +73,8 @@ channelmix_f32_n_m(void *data, int n_dst, void *dst[n_dst],
for (i = 0; i < n_dst; i++) { for (i = 0; i < n_dst; i++) {
float sum = 0.0f; float sum = 0.0f;
for (j = 0; j < n_src; j++) for (j = 0; j < n_src; j++)
sum += s[j][n] * m[i * n_src + j] * v; sum += s[j][n] * m[i * n_src + j];
d[i][n] = sum; d[i][n] = sum * v;
} }
} }
} }

View file

@ -91,15 +91,19 @@ static int emit_nodes(struct impl *this)
spa_list_for_each(t, &device->transport_list, device_link) { spa_list_for_each(t, &device->transport_list, device_link) {
if (t->profile == profile) { if (t->profile == profile) {
struct spa_device_object_info info;
char transport[16]; char transport[16];
snprintf(transport, 16, "%p", t); snprintf(transport, 16, "%p", t);
items[0] = SPA_DICT_ITEM_INIT("bluez5.transport", transport); items[0] = SPA_DICT_ITEM_INIT("bluez5.transport", transport);
this->callbacks->add(this->callbacks_data, 0, info = SPA_DEVICE_OBJECT_INFO_INIT();
&spa_a2dp_sink_factory, info.type = SPA_TYPE_INTERFACE_Node;
SPA_TYPE_INTERFACE_Node, info.factory = &spa_a2dp_sink_factory;
&SPA_DICT_INIT(items, 1)); info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_INFO;
info.info = &SPA_DICT_INIT_ARRAY(items);
this->callbacks->object_info(this->callbacks_data, 0, &info);
break; break;
} }
} }
@ -125,10 +129,19 @@ static int impl_set_callbacks(struct spa_device *device,
this->callbacks_data = data; this->callbacks_data = data;
if (callbacks) { if (callbacks) {
if (callbacks->info) if (callbacks->info) {
callbacks->info(data, &SPA_DICT_INIT_ARRAY(info_items)); struct spa_device_info info;
if (this->callbacks->add) info = SPA_DEVICE_INFO_INIT();
info.change_mask = SPA_DEVICE_CHANGE_MASK_INFO | SPA_DEVICE_CHANGE_MASK_PARAMS;
info.info = &SPA_DICT_INIT_ARRAY(info_items);
info.n_params = 0;
info.params = NULL;
callbacks->info(data, &info);
}
if (this->callbacks->object_info)
emit_nodes(this); emit_nodes(this);
} }

View file

@ -73,27 +73,40 @@ static int emit_info(struct impl *this)
{ {
int res; int res;
struct spa_dict_item items[6]; struct spa_dict_item items[6];
struct spa_dict dict; struct spa_device_info info;
uint32_t params[] = { SPA_PARAM_EnumProfile, SPA_PARAM_Profile };
if ((res = spa_v4l2_open(&this->dev, this->props.device)) < 0) if ((res = spa_v4l2_open(&this->dev, this->props.device)) < 0)
return res; return res;
info = SPA_DEVICE_INFO_INIT();
info.change_mask = SPA_DEVICE_CHANGE_MASK_INFO | SPA_DEVICE_CHANGE_MASK_PARAMS;
items[0] = SPA_DICT_ITEM_INIT("device.api", "v4l2"); items[0] = SPA_DICT_ITEM_INIT("device.api", "v4l2");
items[1] = SPA_DICT_ITEM_INIT("device.path", (char *)this->props.device); items[1] = SPA_DICT_ITEM_INIT("device.path", (char *)this->props.device);
items[2] = SPA_DICT_ITEM_INIT("media.class", "Video/Device"); items[2] = SPA_DICT_ITEM_INIT("media.class", "Video/Device");
items[3] = SPA_DICT_ITEM_INIT("v4l2.driver", (char *)this->dev.cap.driver); items[3] = SPA_DICT_ITEM_INIT("v4l2.driver", (char *)this->dev.cap.driver);
items[4] = SPA_DICT_ITEM_INIT("v4l2.card", (char *)this->dev.cap.card); items[4] = SPA_DICT_ITEM_INIT("v4l2.card", (char *)this->dev.cap.card);
items[5] = SPA_DICT_ITEM_INIT("v4l2.bus", (char *)this->dev.cap.bus_info); items[5] = SPA_DICT_ITEM_INIT("v4l2.bus", (char *)this->dev.cap.bus_info);
dict = SPA_DICT_INIT(items, 6); info.info = &SPA_DICT_INIT(items, 6);
info.n_params = SPA_N_ELEMENTS(params);
info.params = params;
if (this->callbacks->info) if (this->callbacks->info)
this->callbacks->info(this->callbacks_data, &dict); this->callbacks->info(this->callbacks_data, &info);
if (this->callbacks->object_info) {
if (this->callbacks->add) {
if (spa_v4l2_is_capture(&this->dev)) { if (spa_v4l2_is_capture(&this->dev)) {
this->callbacks->add(this->callbacks_data, 0, struct spa_device_object_info oinfo;
&spa_v4l2_source_factory,
SPA_TYPE_INTERFACE_Node, &dict); oinfo = SPA_DEVICE_OBJECT_INFO_INIT();
oinfo.type = SPA_TYPE_INTERFACE_Node;
oinfo.factory = &spa_v4l2_source_factory;
oinfo.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_INFO;
oinfo.info = &SPA_DICT_INIT(items, 6);
this->callbacks->object_info(this->callbacks_data, 0, &oinfo);
} }
} }

View file

@ -153,9 +153,6 @@ on_rtsocket_condition(void *user_data, int fd, enum spa_io mask)
pw_log_trace("remote %p: process %p", data->remote, proxy); pw_log_trace("remote %p: process %p", data->remote, proxy);
spa_graph_node_process(&data->node->rt.root); spa_graph_node_process(&data->node->rt.root);
} }
} }

View file

@ -288,17 +288,16 @@ static const struct pw_node_events node_events = {
}; };
static void device_info(void *data, const struct spa_dict *info) static void device_info(void *data, const struct spa_device_info *info)
{ {
struct pw_device *device = data; struct pw_device *device = data;
pw_device_update_properties(device, info); if (info->change_mask & SPA_DEVICE_CHANGE_MASK_INFO)
pw_device_update_properties(device, info->info);
} }
static void device_add(void *data, uint32_t id, static void device_add(struct pw_device *device, uint32_t id,
const struct spa_handle_factory *factory, uint32_t type, const struct spa_device_object_info *info)
const struct spa_dict *info)
{ {
struct pw_device *device = data;
const struct spa_support *support; const struct spa_support *support;
uint32_t n_support; uint32_t n_support;
struct pw_node *node; struct pw_node *node;
@ -307,8 +306,8 @@ static void device_add(void *data, uint32_t id,
int res; int res;
void *iface; void *iface;
if (type != SPA_TYPE_INTERFACE_Node) { if (info->type != SPA_TYPE_INTERFACE_Node) {
pw_log_warn("device %p: unknown type %d", device, type); pw_log_warn("device %p: unknown type %d", device, info->type);
return; return;
} }
@ -316,14 +315,14 @@ static void device_add(void *data, uint32_t id,
support = pw_core_get_support(device->core, &n_support); support = pw_core_get_support(device->core, &n_support);
props = pw_properties_copy(device->properties); props = pw_properties_copy(device->properties);
if (info) if (info->info)
pw_properties_update(props, info); pw_properties_update(props, info->info);
node = pw_node_new(device->core, node = pw_node_new(device->core,
device->info.name, device->info.name,
props, props,
sizeof(struct node_data) + sizeof(struct node_data) +
spa_handle_factory_get_size(factory, info)); spa_handle_factory_get_size(info->factory, info->info));
nd = pw_node_get_user_data(node); nd = pw_node_get_user_data(node);
nd->id = id; nd->id = id;
@ -332,16 +331,16 @@ static void device_add(void *data, uint32_t id,
pw_node_add_listener(node, &nd->node_listener, &node_events, nd); pw_node_add_listener(node, &nd->node_listener, &node_events, nd);
spa_list_append(&device->node_list, &nd->link); spa_list_append(&device->node_list, &nd->link);
if ((res = spa_handle_factory_init(factory, if ((res = spa_handle_factory_init(info->factory,
nd->handle, nd->handle,
info, info->info,
support, support,
n_support)) < 0) { n_support)) < 0) {
pw_log_error("can't make factory instance: %d", res); pw_log_error("can't make factory instance: %d", res);
goto error;; goto error;;
} }
if ((res = spa_handle_get_interface(nd->handle, type, &iface)) < 0) { if ((res = spa_handle_get_interface(nd->handle, info->type, &iface)) < 0) {
pw_log_error("can't get NODE interface: %d", res); pw_log_error("can't get NODE interface: %d", res);
goto error;; goto error;;
} }
@ -367,23 +366,37 @@ static struct node_data *find_node(struct pw_device *device, uint32_t id)
return NULL; return NULL;
} }
static void device_remove(void *data, uint32_t id) static void device_object_info(void *data, uint32_t id,
const struct spa_device_object_info *info)
{ {
struct pw_device *device = data; struct pw_device *device = data;
struct node_data *nd; struct node_data *nd;
pw_log_debug("device %p: remove node %d", device, id); nd = find_node(device, id);
if ((nd = find_node(device, id)) == NULL)
return;
if (info == NULL) {
if (nd) {
pw_log_debug("device %p: remove node %d", device, id);
pw_node_destroy(nd->node); pw_node_destroy(nd->node);
} }
else {
pw_log_warn("device %p: unknown node %d", device, id);
}
}
else if (nd != NULL) {
if (info->change_mask & SPA_DEVICE_OBJECT_CHANGE_MASK_INFO)
pw_node_update_properties(nd->node, info->info);
}
else {
device_add(device, id, info);
}
}
static const struct spa_device_callbacks device_callbacks = { static const struct spa_device_callbacks device_callbacks = {
SPA_VERSION_DEVICE_CALLBACKS, SPA_VERSION_DEVICE_CALLBACKS,
.info = device_info, .info = device_info,
.add = device_add, .object_info = device_object_info,
.remove = device_remove,
}; };
SPA_EXPORT SPA_EXPORT