mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
Make it possible to assign an arbitary node as the port mixer. Also remove dynamically added ports. Improve negotiation and allocation on the mixer ports Add some more SSE optimisations Move float mixer from the audio dsp to the port Remove pw_node_get_free_port() and do things more explicitly. Handle mixer ports in client-node
329 lines
8.1 KiB
C
329 lines
8.1 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/node.h>
|
|
#include <spa/param/props.h>
|
|
#include <spa/pod/iter.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_free(void *data)
|
|
{
|
|
struct impl *impl = data;
|
|
struct pw_node *node = impl->this;
|
|
|
|
pw_log_debug("spa-node %p: free", node);
|
|
|
|
spa_hook_remove(&impl->node_listener);
|
|
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;
|
|
|
|
if (SPA_FLAG_CHECK(impl->flags, PW_SPA_NODE_FLAG_DISABLE))
|
|
pw_node_set_enabled(this, false);
|
|
|
|
if (SPA_FLAG_CHECK(impl->flags, PW_SPA_NODE_FLAG_ACTIVATE))
|
|
pw_node_set_active(this, true);
|
|
|
|
if (!SPA_FLAG_CHECK(impl->flags, PW_SPA_NODE_FLAG_NO_REGISTER))
|
|
pw_node_register(this, impl->owner, impl->parent, NULL);
|
|
else
|
|
pw_node_initialized(this);
|
|
}
|
|
|
|
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,
|
|
.free = pw_spa_node_free,
|
|
.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;
|
|
|
|
this = pw_node_new(core, name, properties, sizeof(struct impl) + user_data_size);
|
|
if (this == NULL)
|
|
return NULL;
|
|
|
|
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 *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, &props, &b)) <= 0) {
|
|
pw_log_debug("spa_node_get_props failed: %d", res);
|
|
return res;
|
|
}
|
|
|
|
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_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)) < 0) {
|
|
pw_log_debug("spa_node_set_props failed: %d", res);
|
|
return res;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
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;;) {
|
|
if ((res = enum_func(&factory, &index)) <= 0) {
|
|
if (res != 0)
|
|
pw_log_error("can't enumerate factories: %s", spa_strerror(res));
|
|
goto enum_failed;
|
|
}
|
|
if (strcmp(factory->name, factory_name) == 0)
|
|
break;
|
|
}
|
|
|
|
support = pw_core_get_support(core, &n_support);
|
|
|
|
handle = calloc(1, spa_handle_factory_get_size(factory, NULL));
|
|
if (handle == NULL)
|
|
goto no_mem;
|
|
|
|
if ((res = spa_handle_factory_init(factory,
|
|
handle,
|
|
properties ? &properties->dict : 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) < 0) {
|
|
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_mem:
|
|
no_symbol:
|
|
dlclose(hnd);
|
|
open_failed:
|
|
free(filename);
|
|
return NULL;
|
|
}
|