pipewire/src/modules/spa/spa-node.c
Wim Taymans f3bca48398 Unify props, params and formats
Make enum_params and set_param to configure properties, format
and other parameters. This allows us to remove some duplicate
code and make the properties and parameters much more extensible.
Use the object id to mark the id of the parameter.
Remove the spa_format and spa_props.
We can now make the client-node easier by merging the various
format methods into the params.
Make the stream API more powerful now that we can pass params
around.
2017-11-07 17:48:40 +01:00

322 lines
8 KiB
C

/* PipeWire
* Copyright (C) 2015 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <stdio.h>
#include <dlfcn.h>
#include <spa/node.h>
#include <spa/graph.h>
#include "spa-node.h"
#include "pipewire/node.h"
#include "pipewire/port.h"
#include "pipewire/log.h"
#include "pipewire/private.h"
struct impl {
struct pw_node *this;
struct pw_client *owner;
struct pw_global *parent;
enum pw_spa_node_flags flags;
bool async_init;
void *hnd;
struct spa_handle *handle;
struct spa_node *node; /**< handle to SPA node */
char *lib;
char *factory_name;
struct spa_hook node_listener;
void *user_data;
};
static void pw_spa_node_destroy(void *data)
{
struct impl *impl = data;
struct pw_node *node = impl->this;
pw_log_debug("spa-node %p: destroy", node);
if (impl->handle) {
spa_handle_clear(impl->handle);
free(impl->handle);
}
free(impl->lib);
free(impl->factory_name);
if (impl->hnd)
dlclose(impl->hnd);
}
static void complete_init(struct impl *impl)
{
struct pw_node *this = impl->this;
pw_node_register(this, impl->owner, impl->parent);
if (impl->flags & PW_SPA_NODE_FLAG_ACTIVATE)
pw_node_set_active(this, true);
}
static void on_node_done(void *data, uint32_t seq, int res)
{
struct impl *impl = data;
struct pw_node *this = impl->this;
if (impl->async_init) {
complete_init(impl);
impl->async_init = false;
}
pw_log_debug("spa-node %p: async complete event %d %d", this, seq, res);
}
static const struct pw_node_events node_events = {
PW_VERSION_NODE_EVENTS,
.destroy = pw_spa_node_destroy,
.async_complete = on_node_done,
};
struct pw_node *
pw_spa_node_new(struct pw_core *core,
struct pw_client *owner,
struct pw_global *parent,
const char *name,
enum pw_spa_node_flags flags,
struct spa_node *node,
struct spa_handle *handle,
struct pw_properties *properties,
size_t user_data_size)
{
struct pw_node *this;
struct impl *impl;
void *iface = NULL;
struct pw_type *t = pw_core_get_type(core);
int res;
this = pw_node_new(core, name, properties, sizeof(struct impl) + user_data_size);
if (this == NULL)
return NULL;
if (handle) {
if ((res = spa_handle_get_interface(handle, t->spa_clock, &iface)) < 0)
iface = NULL;
this->clock = iface;
}
impl = this->user_data;
impl->this = this;
impl->owner = owner;
impl->parent = parent;
impl->node = node;
impl->flags = flags;
impl->async_init = flags & PW_SPA_NODE_FLAG_ASYNC;
if (user_data_size > 0)
impl->user_data = SPA_MEMBER(impl, sizeof(struct impl), void);
pw_node_add_listener(this, &impl->node_listener, &node_events, impl);
pw_node_set_implementation(this, impl->node);
if (!impl->async_init)
complete_init(impl);
return this;
}
void *pw_spa_node_get_user_data(struct pw_node *node)
{
struct impl *impl = node->user_data;
return impl->user_data;
}
static int
setup_props(struct pw_core *core, struct spa_node *spa_node, struct pw_properties *pw_props)
{
int res;
struct spa_pod_object *props;
void *state = NULL;
const char *key;
struct pw_type *t = pw_core_get_type(core);
uint32_t index = 0;
uint8_t buf[2048];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
if ((res = spa_node_enum_params(spa_node, t->param.idProps, &index, NULL, &b)) != SPA_RESULT_OK) {
pw_log_debug("spa_node_get_props failed: %d", res);
return SPA_RESULT_ERROR;
}
props = SPA_POD_BUILDER_DEREF(&b, 0, struct spa_pod_object);
while ((key = pw_properties_iterate(pw_props, &state))) {
struct spa_pod_prop *prop;
uint32_t id;
if (!spa_type_is_a(key, SPA_TYPE_PROPS_BASE))
continue;
id = spa_type_map_get_id(t->map, key);
if (id == SPA_ID_INVALID)
continue;
if ((prop = spa_pod_object_find_prop(props, id))) {
const char *value = pw_properties_get(pw_props, key);
pw_log_info("configure prop %s", key);
switch(prop->body.value.type) {
case SPA_POD_TYPE_BOOL:
SPA_POD_VALUE(struct spa_pod_bool, &prop->body.value) =
pw_properties_parse_bool(value);
break;
case SPA_POD_TYPE_ID:
SPA_POD_VALUE(struct spa_pod_id, &prop->body.value) =
spa_type_map_get_id(t->map, value);
break;
case SPA_POD_TYPE_INT:
SPA_POD_VALUE(struct spa_pod_int, &prop->body.value) =
pw_properties_parse_int(value);
break;
case SPA_POD_TYPE_LONG:
SPA_POD_VALUE(struct spa_pod_long, &prop->body.value) =
pw_properties_parse_int64(value);
break;
case SPA_POD_TYPE_FLOAT:
SPA_POD_VALUE(struct spa_pod_float, &prop->body.value) =
pw_properties_parse_float(value);
break;
case SPA_POD_TYPE_DOUBLE:
SPA_POD_VALUE(struct spa_pod_double, &prop->body.value) =
pw_properties_parse_double(value);
break;
case SPA_POD_TYPE_STRING:
break;
default:
break;
}
}
}
if ((res = spa_node_set_param(spa_node, t->param.idProps, 0, props)) != SPA_RESULT_OK) {
pw_log_debug("spa_node_set_props failed: %d", res);
return SPA_RESULT_ERROR;
}
return SPA_RESULT_OK;
}
struct pw_node *pw_spa_node_load(struct pw_core *core,
struct pw_client *owner,
struct pw_global *parent,
const char *lib,
const char *factory_name,
const char *name,
enum pw_spa_node_flags flags,
struct pw_properties *properties,
size_t user_data_size)
{
struct pw_node *this;
struct impl *impl;
struct spa_node *spa_node;
int res;
struct spa_handle *handle;
void *hnd;
uint32_t index;
spa_handle_factory_enum_func_t enum_func;
const struct spa_handle_factory *factory;
void *iface;
char *filename;
const char *dir;
const struct spa_support *support;
uint32_t n_support;
struct pw_type *t = pw_core_get_type(core);
if ((dir = getenv("SPA_PLUGIN_DIR")) == NULL)
dir = PLUGINDIR;
asprintf(&filename, "%s/%s.so", dir, lib);
if ((hnd = dlopen(filename, RTLD_NOW)) == NULL) {
pw_log_error("can't load %s: %s", filename, dlerror());
goto open_failed;
}
if ((enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) {
pw_log_error("can't find enum function");
goto no_symbol;
}
for (index = 0;; index++) {
if ((res = enum_func(&factory, index)) < 0) {
if (res != SPA_RESULT_ENUM_END)
pw_log_error("can't enumerate factories: %d", res);
goto enum_failed;
}
if (strcmp(factory->name, factory_name) == 0)
break;
}
support = pw_core_get_support(core, &n_support);
handle = calloc(1, factory->size);
if ((res = spa_handle_factory_init(factory,
handle, NULL, support, n_support)) < 0) {
pw_log_error("can't make factory instance: %d", res);
goto init_failed;
}
if (SPA_RESULT_IS_ASYNC(res))
flags |= PW_SPA_NODE_FLAG_ASYNC;
if ((res = spa_handle_get_interface(handle, t->spa_node, &iface)) < 0) {
pw_log_error("can't get node interface %d", res);
goto interface_failed;
}
spa_node = iface;
if (properties != NULL) {
if (setup_props(core, spa_node, properties) != SPA_RESULT_OK) {
pw_log_debug("Unrecognized properties");
}
}
this = pw_spa_node_new(core, owner, parent, name, flags,
spa_node, handle, properties, user_data_size);
impl = this->user_data;
impl->hnd = hnd;
impl->handle = handle;
impl->lib = filename;
impl->factory_name = strdup(factory_name);
return this;
interface_failed:
spa_handle_clear(handle);
init_failed:
free(handle);
enum_failed:
no_symbol:
dlclose(hnd);
open_failed:
free(filename);
return NULL;
}