mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-07 13:30:09 -05:00
move things around
This commit is contained in:
parent
847cef83b6
commit
d1655196c3
130 changed files with 363 additions and 335 deletions
91
src/modules/meson.build
Normal file
91
src/modules/meson.build
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
subdir('spa')
|
||||
|
||||
pipewire_module_c_args = [
|
||||
'-DHAVE_CONFIG_H',
|
||||
'-D_GNU_SOURCE',
|
||||
]
|
||||
|
||||
pipewire_module_flatpak = shared_library('pipewire-module-flatpak', [ 'module-flatpak.c' ],
|
||||
c_args : pipewire_module_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
link_with : spalib,
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
dependencies : [dbus_dep, mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
pipewire_module_autolink = shared_library('pipewire-module-autolink', [ 'module-autolink.c' ],
|
||||
c_args : pipewire_module_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
link_with : spalib,
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
pipewire_module_mixer = shared_library('pipewire-module-mixer',
|
||||
[ 'module-mixer.c', 'spa/spa-node.c' ],
|
||||
c_args : pipewire_module_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
link_with : spalib,
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
pipewire_module_client_node = shared_library('pipewire-module-client-node',
|
||||
[ 'module-client-node.c',
|
||||
'module-client-node/client-node.c',
|
||||
'module-client-node/protocol-native.c',
|
||||
'module-protocol-native/connection.c',
|
||||
'spa/spa-node.c', ],
|
||||
c_args : pipewire_module_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
link_with : spalib,
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
#pipewire_module_protocol_dbus = shared_library('pipewire-module-protocol-dbus', [ 'module-protocol-dbus.c', gdbus_target ],
|
||||
# c_args : pipewire_module_c_args,
|
||||
# include_directories : [configinc, spa_inc],
|
||||
# link_with : spalib,
|
||||
# install : true,
|
||||
# install_dir : modules_install_dir,
|
||||
# dependencies : [glib_dep, gio_dep, mathlib, dl_lib, pipewire_dep],
|
||||
#)
|
||||
|
||||
pipewire_module_protocol_native = shared_library('pipewire-module-protocol-native',
|
||||
[ 'module-protocol-native.c',
|
||||
'module-protocol-native/protocol-native.c',
|
||||
'module-protocol-native/connection.c' ],
|
||||
c_args : pipewire_module_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
link_with : spalib,
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
if jack_dep.found()
|
||||
pipewire_module_jack = shared_library('pipewire-module-jack',
|
||||
[ 'module-jack.c',
|
||||
'module-jack/shm.c' ],
|
||||
c_args : pipewire_module_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
link_with : spalib,
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
dependencies : [jack_dep, mathlib, dl_lib, rt_lib, pipewire_dep],
|
||||
)
|
||||
endif
|
||||
|
||||
pipewire_module_suspend_on_idle = shared_library('pipewire-module-suspend-on-idle', [ 'module-suspend-on-idle.c' ],
|
||||
c_args : pipewire_module_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
link_with : spalib,
|
||||
install : true,
|
||||
install_dir : modules_install_dir,
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
329
src/modules/module-autolink.c
Normal file
329
src/modules/module-autolink.c
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "pipewire/interfaces.h"
|
||||
#include "pipewire/core.h"
|
||||
#include "pipewire/module.h"
|
||||
|
||||
struct impl {
|
||||
struct pw_core *core;
|
||||
struct pw_properties *properties;
|
||||
|
||||
struct pw_listener global_added;
|
||||
struct pw_listener global_removed;
|
||||
|
||||
struct spa_list node_list;
|
||||
};
|
||||
|
||||
struct node_info {
|
||||
struct impl *impl;
|
||||
struct pw_node *node;
|
||||
struct spa_list link;
|
||||
struct pw_listener state_changed;
|
||||
struct pw_listener port_added;
|
||||
struct pw_listener port_removed;
|
||||
struct pw_listener port_unlinked;
|
||||
struct pw_listener link_state_changed;
|
||||
struct pw_listener link_destroy;
|
||||
};
|
||||
|
||||
static struct node_info *find_node_info(struct impl *impl, struct pw_node *node)
|
||||
{
|
||||
struct node_info *info;
|
||||
|
||||
spa_list_for_each(info, &impl->node_list, link) {
|
||||
if (info->node == node)
|
||||
return info;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void node_info_free(struct node_info *info)
|
||||
{
|
||||
spa_list_remove(&info->link);
|
||||
pw_signal_remove(&info->state_changed);
|
||||
pw_signal_remove(&info->port_added);
|
||||
pw_signal_remove(&info->port_removed);
|
||||
pw_signal_remove(&info->port_unlinked);
|
||||
pw_signal_remove(&info->link_destroy);
|
||||
pw_signal_remove(&info->link_state_changed);
|
||||
free(info);
|
||||
}
|
||||
|
||||
static void try_link_port(struct pw_node *node, struct pw_port *port, struct node_info *info);
|
||||
|
||||
static void
|
||||
on_link_port_unlinked(struct pw_listener *listener, struct pw_link *link, struct pw_port *port)
|
||||
{
|
||||
struct node_info *info = SPA_CONTAINER_OF(listener, struct node_info, port_unlinked);
|
||||
struct impl *impl = info->impl;
|
||||
|
||||
pw_log_debug("module %p: link %p: port %p unlinked", impl, link, port);
|
||||
if (port->direction == PW_DIRECTION_OUTPUT && link->input)
|
||||
try_link_port(link->input->node, link->input, info);
|
||||
}
|
||||
|
||||
static void
|
||||
on_link_state_changed(struct pw_listener *listener,
|
||||
struct pw_link *link, enum pw_link_state old, enum pw_link_state state)
|
||||
{
|
||||
struct node_info *info = SPA_CONTAINER_OF(listener, struct node_info, link_state_changed);
|
||||
struct impl *impl = info->impl;
|
||||
|
||||
switch (state) {
|
||||
case PW_LINK_STATE_ERROR:
|
||||
{
|
||||
struct pw_resource *resource;
|
||||
|
||||
pw_log_debug("module %p: link %p: state error: %s", impl, link,
|
||||
link->error);
|
||||
|
||||
spa_list_for_each(resource, &link->resource_list, link) {
|
||||
pw_core_notify_error(resource->client->core_resource,
|
||||
resource->id, SPA_RESULT_ERROR, link->error);
|
||||
}
|
||||
if (info->node->owner) {
|
||||
pw_core_notify_error(info->node->owner->client->core_resource,
|
||||
info->node->owner->id,
|
||||
SPA_RESULT_ERROR, link->error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case PW_LINK_STATE_UNLINKED:
|
||||
pw_log_debug("module %p: link %p: unlinked", impl, link);
|
||||
break;
|
||||
|
||||
case PW_LINK_STATE_INIT:
|
||||
case PW_LINK_STATE_NEGOTIATING:
|
||||
case PW_LINK_STATE_ALLOCATING:
|
||||
case PW_LINK_STATE_PAUSED:
|
||||
case PW_LINK_STATE_RUNNING:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_link_destroy(struct pw_listener *listener, struct pw_link *link)
|
||||
{
|
||||
struct node_info *info = SPA_CONTAINER_OF(listener, struct node_info, link_destroy);
|
||||
struct impl *impl = info->impl;
|
||||
|
||||
pw_log_debug("module %p: link %p destroyed", impl, link);
|
||||
pw_signal_remove(&info->port_unlinked);
|
||||
pw_signal_remove(&info->link_state_changed);
|
||||
pw_signal_remove(&info->link_destroy);
|
||||
spa_list_init(&info->port_unlinked.link);
|
||||
spa_list_init(&info->link_state_changed.link);
|
||||
spa_list_init(&info->link_destroy.link);
|
||||
}
|
||||
|
||||
static void try_link_port(struct pw_node *node, struct pw_port *port, struct node_info *info)
|
||||
{
|
||||
struct impl *impl = info->impl;
|
||||
struct pw_properties *props;
|
||||
const char *str;
|
||||
uint32_t path_id;
|
||||
char *error = NULL;
|
||||
struct pw_link *link;
|
||||
struct pw_port *target;
|
||||
|
||||
props = node->properties;
|
||||
if (props == NULL) {
|
||||
pw_log_debug("module %p: node has no properties", impl);
|
||||
return;
|
||||
}
|
||||
|
||||
str = pw_properties_get(props, "pipewire.target.node");
|
||||
if (str != NULL)
|
||||
path_id = atoi(str);
|
||||
else {
|
||||
str = pw_properties_get(props, "pipewire.autoconnect");
|
||||
if (str == NULL || atoi(str) == 0) {
|
||||
pw_log_debug("module %p: node does not need autoconnect", impl);
|
||||
return;
|
||||
}
|
||||
path_id = SPA_ID_INVALID;
|
||||
}
|
||||
|
||||
pw_log_debug("module %p: try to find and link to node '%d'", impl, path_id);
|
||||
|
||||
target = pw_core_find_port(impl->core, port, path_id, NULL, 0, NULL, &error);
|
||||
if (target == NULL)
|
||||
goto error;
|
||||
|
||||
if (port->direction == PW_DIRECTION_OUTPUT)
|
||||
link = pw_link_new(impl->core, port, target, NULL, NULL, &error);
|
||||
else
|
||||
link = pw_link_new(impl->core, target, port, NULL, NULL, &error);
|
||||
|
||||
if (link == NULL)
|
||||
goto error;
|
||||
|
||||
pw_signal_add(&link->port_unlinked, &info->port_unlinked, on_link_port_unlinked);
|
||||
pw_signal_add(&link->state_changed, &info->link_state_changed, on_link_state_changed);
|
||||
pw_signal_add(&link->destroy_signal, &info->link_destroy, on_link_destroy);
|
||||
|
||||
pw_link_activate(link);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
pw_log_error("module %p: can't link node '%s'", impl, error);
|
||||
if (info->node->owner && info->node->owner->client->core_resource) {
|
||||
pw_core_notify_error(info->node->owner->client->core_resource,
|
||||
info->node->owner->id, SPA_RESULT_ERROR, error);
|
||||
}
|
||||
free(error);
|
||||
return;
|
||||
}
|
||||
|
||||
static void on_port_added(struct pw_listener *listener, struct pw_node *node, struct pw_port *port)
|
||||
{
|
||||
struct node_info *info = SPA_CONTAINER_OF(listener, struct node_info, port_added);
|
||||
|
||||
try_link_port(node, port, info);
|
||||
}
|
||||
|
||||
static void
|
||||
on_port_removed(struct pw_listener *listener, struct pw_node *node, struct pw_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static void on_node_created(struct pw_node *node, struct node_info *info)
|
||||
{
|
||||
struct pw_port *port;
|
||||
|
||||
spa_list_for_each(port, &node->input_ports, link)
|
||||
on_port_added(&info->port_added, node, port);
|
||||
|
||||
spa_list_for_each(port, &node->output_ports, link)
|
||||
on_port_added(&info->port_added, node, port);
|
||||
}
|
||||
|
||||
static void
|
||||
on_state_changed(struct pw_listener *listener,
|
||||
struct pw_node *node, enum pw_node_state old, enum pw_node_state state)
|
||||
{
|
||||
struct node_info *info = SPA_CONTAINER_OF(listener, struct node_info, state_changed);
|
||||
|
||||
if (old == PW_NODE_STATE_CREATING && state == PW_NODE_STATE_SUSPENDED)
|
||||
on_node_created(node, info);
|
||||
}
|
||||
|
||||
static void
|
||||
on_global_added(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(listener, struct impl, global_added);
|
||||
|
||||
if (global->type == impl->core->type.node) {
|
||||
struct pw_node *node = global->object;
|
||||
struct node_info *ninfo;
|
||||
|
||||
ninfo = calloc(1, sizeof(struct node_info));
|
||||
ninfo->impl = impl;
|
||||
ninfo->node = node;
|
||||
spa_list_insert(impl->node_list.prev, &ninfo->link);
|
||||
spa_list_init(&ninfo->port_unlinked.link);
|
||||
spa_list_init(&ninfo->link_state_changed.link);
|
||||
spa_list_init(&ninfo->link_destroy.link);
|
||||
|
||||
pw_signal_add(&node->port_added, &ninfo->port_added, on_port_added);
|
||||
pw_signal_add(&node->port_removed, &ninfo->port_removed, on_port_removed);
|
||||
pw_signal_add(&node->state_changed, &ninfo->state_changed, on_state_changed);
|
||||
|
||||
pw_log_debug("module %p: node %p added", impl, node);
|
||||
|
||||
if (node->info.state > PW_NODE_STATE_CREATING)
|
||||
on_node_created(node, ninfo);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_global_removed(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(listener, struct impl, global_removed);
|
||||
|
||||
if (global->type == impl->core->type.node) {
|
||||
struct pw_node *node = global->object;
|
||||
struct node_info *ninfo;
|
||||
|
||||
if ((ninfo = find_node_info(impl, node)))
|
||||
node_info_free(ninfo);
|
||||
|
||||
pw_log_debug("module %p: node %p removed", impl, node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* module_new:
|
||||
* @core: #struct pw_core
|
||||
* @properties: #struct pw_properties
|
||||
*
|
||||
* Make a new #struct impl object with given @properties
|
||||
*
|
||||
* Returns: a new #struct impl
|
||||
*/
|
||||
static struct impl *module_new(struct pw_core *core, struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
pw_log_debug("module %p: new", impl);
|
||||
|
||||
impl->core = core;
|
||||
impl->properties = properties;
|
||||
|
||||
spa_list_init(&impl->node_list);
|
||||
|
||||
pw_signal_add(&core->global_added, &impl->global_added, on_global_added);
|
||||
pw_signal_add(&core->global_removed, &impl->global_removed, on_global_removed);
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void module_destroy(struct impl *impl)
|
||||
{
|
||||
pw_log_debug("module %p: destroy", impl);
|
||||
|
||||
pw_global_destroy(impl->global);
|
||||
|
||||
pw_signal_remove(&impl->global_added);
|
||||
pw_signal_remove(&impl->global_removed);
|
||||
pw_signal_remove(&impl->port_added);
|
||||
pw_signal_remove(&impl->port_removed);
|
||||
pw_signal_remove(&impl->port_unlinked);
|
||||
pw_signal_remove(&impl->link_state_changed);
|
||||
free(impl);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
module->user_data = module_new(module->core, NULL);
|
||||
return true;
|
||||
}
|
||||
97
src/modules/module-client-node.c
Normal file
97
src/modules/module-client-node.c
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2017 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 <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "pipewire/interfaces.h"
|
||||
#include "pipewire/core.h"
|
||||
#include "pipewire/module.h"
|
||||
|
||||
#include "module-client-node/client-node.h"
|
||||
|
||||
struct pw_protocol *pw_protocol_native_ext_client_node_init(void);
|
||||
|
||||
struct impl {
|
||||
struct pw_node_factory this;
|
||||
struct pw_properties *properties;
|
||||
};
|
||||
|
||||
static struct pw_node *create_node(struct pw_node_factory *factory,
|
||||
struct pw_resource *resource,
|
||||
const char *name,
|
||||
struct pw_properties *properties)
|
||||
{
|
||||
struct pw_client_node *node;
|
||||
|
||||
node = pw_client_node_new(resource, name, properties);
|
||||
if (node == NULL)
|
||||
goto no_mem;
|
||||
|
||||
return node->node;
|
||||
|
||||
no_mem:
|
||||
pw_log_error("can't create node");
|
||||
pw_core_notify_error(resource->client->core_resource,
|
||||
resource->client->core_resource->id, SPA_RESULT_NO_MEMORY, "no memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct impl *module_new(struct pw_core *core, struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
pw_log_debug("module %p: new", impl);
|
||||
|
||||
impl->properties = properties;
|
||||
|
||||
impl->this.core = core;
|
||||
impl->this.name = "client-node";
|
||||
impl->this.type = spa_type_map_get_id(core->type.map, PIPEWIRE_TYPE__ClientNode);
|
||||
pw_signal_init(&impl->this.destroy_signal);
|
||||
impl->this.create_node = create_node;
|
||||
|
||||
pw_protocol_native_ext_client_node_init();
|
||||
|
||||
spa_list_insert(core->node_factory_list.prev, &impl->this.link);
|
||||
|
||||
pw_core_add_global(core, NULL, core->type.node_factory, 0, impl, NULL, &impl->this.global);
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void module_destroy(struct impl *impl)
|
||||
{
|
||||
pw_log_debug("module %p: destroy", impl);
|
||||
|
||||
free(impl);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
module_new(module->core, NULL);
|
||||
return true;
|
||||
}
|
||||
1242
src/modules/module-client-node/client-node.c
Normal file
1242
src/modules/module-client-node/client-node.c
Normal file
File diff suppressed because it is too large
Load diff
57
src/modules/module-client-node/client-node.h
Normal file
57
src/modules/module-client-node/client-node.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef __PIPEWIRE_CLIENT_NODE_H__
|
||||
#define __PIPEWIRE_CLIENT_NODE_H__
|
||||
|
||||
#include <pipewire/node.h>
|
||||
#include <extensions/client-node.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** \class pw_client_node
|
||||
*
|
||||
* PipeWire client node interface
|
||||
*/
|
||||
struct pw_client_node {
|
||||
struct pw_node *node;
|
||||
|
||||
struct pw_resource *resource;
|
||||
|
||||
PW_SIGNAL(destroy_signal, (struct pw_listener *listener, struct pw_client_node *node));
|
||||
};
|
||||
|
||||
struct pw_client_node *
|
||||
pw_client_node_new(struct pw_resource *resource,
|
||||
const char *name,
|
||||
struct pw_properties *properties);
|
||||
|
||||
void
|
||||
pw_client_node_destroy(struct pw_client_node *node);
|
||||
|
||||
int
|
||||
pw_client_node_get_fds(struct pw_client_node *node, int *readfd, int *writefd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PIPEWIRE_CLIENT_NODE_H__ */
|
||||
873
src/modules/module-client-node/protocol-native.c
Normal file
873
src/modules/module-client-node/protocol-native.c
Normal file
|
|
@ -0,0 +1,873 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2017 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 "spa/pod-iter.h"
|
||||
|
||||
#include "pipewire/pipewire.h"
|
||||
#include "pipewire/interfaces.h"
|
||||
#include "pipewire/protocol.h"
|
||||
#include "pipewire/client.h"
|
||||
#include "extensions/client-node.h"
|
||||
|
||||
#include "modules/module-protocol-native/connection.h"
|
||||
|
||||
/** \cond */
|
||||
|
||||
typedef bool(*demarshal_func_t) (void *object, void *data, size_t size);
|
||||
|
||||
/** \endcond */
|
||||
|
||||
static void
|
||||
client_node_marshal_done(void *object, int seq, int res)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct pw_connection *connection = proxy->remote->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_proxy(connection, proxy, PW_CLIENT_NODE_METHOD_DONE);
|
||||
|
||||
spa_pod_builder_struct(b, &f,
|
||||
SPA_POD_TYPE_INT, seq,
|
||||
SPA_POD_TYPE_INT, res);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
client_node_marshal_update(void *object,
|
||||
uint32_t change_mask,
|
||||
uint32_t max_input_ports,
|
||||
uint32_t max_output_ports, const struct spa_props *props)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct pw_connection *connection = proxy->remote->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_proxy(connection, proxy, PW_CLIENT_NODE_METHOD_UPDATE);
|
||||
|
||||
spa_pod_builder_struct(b, &f,
|
||||
SPA_POD_TYPE_INT, change_mask,
|
||||
SPA_POD_TYPE_INT, max_input_ports,
|
||||
SPA_POD_TYPE_INT, max_output_ports, SPA_POD_TYPE_POD, props);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void
|
||||
client_node_marshal_port_update(void *object,
|
||||
enum spa_direction direction,
|
||||
uint32_t port_id,
|
||||
uint32_t change_mask,
|
||||
uint32_t n_possible_formats,
|
||||
const struct spa_format **possible_formats,
|
||||
const struct spa_format *format,
|
||||
uint32_t n_params,
|
||||
const struct spa_param **params, const struct spa_port_info *info)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct pw_connection *connection = proxy->remote->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f[2];
|
||||
int i;
|
||||
|
||||
b = pw_connection_begin_write_proxy(connection, proxy, PW_CLIENT_NODE_METHOD_PORT_UPDATE);
|
||||
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_TYPE_STRUCT, &f[0],
|
||||
SPA_POD_TYPE_INT, direction,
|
||||
SPA_POD_TYPE_INT, port_id,
|
||||
SPA_POD_TYPE_INT, change_mask, SPA_POD_TYPE_INT, n_possible_formats, 0);
|
||||
|
||||
for (i = 0; i < n_possible_formats; i++)
|
||||
spa_pod_builder_add(b, SPA_POD_TYPE_POD, possible_formats[i], 0);
|
||||
|
||||
spa_pod_builder_add(b, SPA_POD_TYPE_POD, format, SPA_POD_TYPE_INT, n_params, 0);
|
||||
|
||||
for (i = 0; i < n_params; i++) {
|
||||
const struct spa_param *p = params[i];
|
||||
spa_pod_builder_add(b, SPA_POD_TYPE_POD, p, 0);
|
||||
}
|
||||
|
||||
if (info) {
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_TYPE_STRUCT, &f[1],
|
||||
SPA_POD_TYPE_INT, info->flags, SPA_POD_TYPE_INT, info->rate, 0);
|
||||
spa_pod_builder_add(b, -SPA_POD_TYPE_STRUCT, &f[1], 0);
|
||||
} else {
|
||||
spa_pod_builder_add(b, SPA_POD_TYPE_POD, NULL, 0);
|
||||
}
|
||||
spa_pod_builder_add(b, -SPA_POD_TYPE_STRUCT, &f[0], 0);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void client_node_marshal_event_method(void *object, struct spa_event *event)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct pw_connection *connection = proxy->remote->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_proxy(connection, proxy, PW_CLIENT_NODE_METHOD_EVENT);
|
||||
|
||||
spa_pod_builder_struct(b, &f, SPA_POD_TYPE_POD, event);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void client_node_marshal_destroy(void *object)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct pw_connection *connection = proxy->remote->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_proxy(connection, proxy, PW_CLIENT_NODE_METHOD_DESTROY);
|
||||
|
||||
spa_pod_builder_struct(b, &f, 0);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_set_props(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
uint32_t seq;
|
||||
const struct spa_props *props = NULL;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &seq,
|
||||
-SPA_POD_TYPE_OBJECT, &props, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_events *) proxy->implementation)->set_props(proxy, seq, props);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_event_event(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
const struct spa_event *event;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &proxy->remote->types) ||
|
||||
!spa_pod_iter_get(&it, SPA_POD_TYPE_OBJECT, &event, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_events *) proxy->implementation)->event(proxy, event);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_add_port(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
int32_t seq, direction, port_id;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &seq,
|
||||
SPA_POD_TYPE_INT, &direction, SPA_POD_TYPE_INT, &port_id, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_events *) proxy->implementation)->add_port(proxy, seq, direction,
|
||||
port_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_remove_port(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
int32_t seq, direction, port_id;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &seq,
|
||||
SPA_POD_TYPE_INT, &direction, SPA_POD_TYPE_INT, &port_id, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_events *) proxy->implementation)->remove_port(proxy, seq, direction,
|
||||
port_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_set_format(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
uint32_t seq, direction, port_id, flags;
|
||||
const struct spa_format *format = NULL;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &proxy->remote->types) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &seq,
|
||||
SPA_POD_TYPE_INT, &direction,
|
||||
SPA_POD_TYPE_INT, &port_id,
|
||||
SPA_POD_TYPE_INT, &flags,
|
||||
-SPA_POD_TYPE_OBJECT, &format, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_events *) proxy->implementation)->set_format(proxy, seq, direction,
|
||||
port_id, flags,
|
||||
format);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_set_param(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
uint32_t seq, direction, port_id;
|
||||
const struct spa_param *param = NULL;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &proxy->remote->types) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &seq,
|
||||
SPA_POD_TYPE_INT, &direction,
|
||||
SPA_POD_TYPE_INT, &port_id,
|
||||
-SPA_POD_TYPE_OBJECT, ¶m, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_events *) proxy->implementation)->set_param(proxy, seq, direction,
|
||||
port_id, param);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_add_mem(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
struct pw_connection *connection = proxy->remote->protocol_private;
|
||||
uint32_t direction, port_id, mem_id, type, memfd_idx, flags, offset, sz;
|
||||
int memfd;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &proxy->remote->types) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &direction,
|
||||
SPA_POD_TYPE_INT, &port_id,
|
||||
SPA_POD_TYPE_INT, &mem_id,
|
||||
SPA_POD_TYPE_ID, &type,
|
||||
SPA_POD_TYPE_INT, &memfd_idx,
|
||||
SPA_POD_TYPE_INT, &flags,
|
||||
SPA_POD_TYPE_INT, &offset, SPA_POD_TYPE_INT, &sz, 0))
|
||||
return false;
|
||||
|
||||
memfd = pw_connection_get_fd(connection, memfd_idx);
|
||||
|
||||
((struct pw_client_node_events *) proxy->implementation)->add_mem(proxy,
|
||||
direction,
|
||||
port_id,
|
||||
mem_id,
|
||||
type,
|
||||
memfd, flags, offset, sz);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_use_buffers(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
uint32_t seq, direction, port_id, n_buffers, data_id;
|
||||
struct pw_client_node_buffer *buffers;
|
||||
int i, j;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &proxy->remote->types) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &seq,
|
||||
SPA_POD_TYPE_INT, &direction,
|
||||
SPA_POD_TYPE_INT, &port_id, SPA_POD_TYPE_INT, &n_buffers, 0))
|
||||
return false;
|
||||
|
||||
buffers = alloca(sizeof(struct pw_client_node_buffer) * n_buffers);
|
||||
for (i = 0; i < n_buffers; i++) {
|
||||
struct spa_buffer *buf = buffers[i].buffer = alloca(sizeof(struct spa_buffer));
|
||||
|
||||
if (!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &buffers[i].mem_id,
|
||||
SPA_POD_TYPE_INT, &buffers[i].offset,
|
||||
SPA_POD_TYPE_INT, &buffers[i].size,
|
||||
SPA_POD_TYPE_INT, &buf->id,
|
||||
SPA_POD_TYPE_INT, &buf->n_metas, 0))
|
||||
return false;
|
||||
|
||||
buf->metas = alloca(sizeof(struct spa_meta) * buf->n_metas);
|
||||
for (j = 0; j < buf->n_metas; j++) {
|
||||
struct spa_meta *m = &buf->metas[j];
|
||||
|
||||
if (!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_ID, &m->type,
|
||||
SPA_POD_TYPE_INT, &m->size, 0))
|
||||
return false;
|
||||
}
|
||||
if (!spa_pod_iter_get(&it, SPA_POD_TYPE_INT, &buf->n_datas, 0))
|
||||
return false;
|
||||
|
||||
buf->datas = alloca(sizeof(struct spa_data) * buf->n_datas);
|
||||
for (j = 0; j < buf->n_datas; j++) {
|
||||
struct spa_data *d = &buf->datas[j];
|
||||
|
||||
if (!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_ID, &d->type,
|
||||
SPA_POD_TYPE_INT, &data_id,
|
||||
SPA_POD_TYPE_INT, &d->flags,
|
||||
SPA_POD_TYPE_INT, &d->mapoffset,
|
||||
SPA_POD_TYPE_INT, &d->maxsize, 0))
|
||||
return false;
|
||||
|
||||
d->data = SPA_UINT32_TO_PTR(data_id);
|
||||
}
|
||||
}
|
||||
((struct pw_client_node_events *) proxy->implementation)->use_buffers(proxy,
|
||||
seq,
|
||||
direction,
|
||||
port_id,
|
||||
n_buffers, buffers);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_node_command(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
const struct spa_command *command;
|
||||
uint32_t seq;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &proxy->remote->types) ||
|
||||
!spa_pod_iter_get(&it, SPA_POD_TYPE_INT, &seq, SPA_POD_TYPE_OBJECT, &command, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_events *) proxy->implementation)->node_command(proxy, seq, command);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_port_command(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
const struct spa_command *command;
|
||||
uint32_t direction, port_id;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &proxy->remote->types) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &direction,
|
||||
SPA_POD_TYPE_INT, &port_id,
|
||||
SPA_POD_TYPE_OBJECT, &command, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_events *) proxy->implementation)->port_command(proxy,
|
||||
direction,
|
||||
port_id,
|
||||
command);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_transport(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_proxy *proxy = object;
|
||||
struct spa_pod_iter it;
|
||||
struct pw_connection *connection = proxy->remote->protocol_private;
|
||||
uint32_t node_id, ridx, widx, memfd_idx, offset, sz;
|
||||
int readfd, writefd, memfd;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &node_id,
|
||||
SPA_POD_TYPE_INT, &ridx,
|
||||
SPA_POD_TYPE_INT, &widx,
|
||||
SPA_POD_TYPE_INT, &memfd_idx,
|
||||
SPA_POD_TYPE_INT, &offset,
|
||||
SPA_POD_TYPE_INT, &sz, 0))
|
||||
return false;
|
||||
|
||||
readfd = pw_connection_get_fd(connection, ridx);
|
||||
writefd = pw_connection_get_fd(connection, widx);
|
||||
memfd = pw_connection_get_fd(connection, memfd_idx);
|
||||
if (readfd == -1 || writefd == -1 || memfd_idx == -1)
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_events *) proxy->implementation)->transport(proxy, node_id,
|
||||
readfd, writefd,
|
||||
memfd, offset, sz);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
client_node_marshal_set_props(void *object, uint32_t seq, const struct spa_props *props)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_SET_PROPS);
|
||||
|
||||
spa_pod_builder_struct(b, &f,
|
||||
SPA_POD_TYPE_INT, seq,
|
||||
SPA_POD_TYPE_POD, props);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void client_node_marshal_event_event(void *object, const struct spa_event *event)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_EVENT);
|
||||
|
||||
spa_pod_builder_struct(b, &f, SPA_POD_TYPE_POD, event);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void
|
||||
client_node_marshal_add_port(void *object,
|
||||
uint32_t seq, enum spa_direction direction, uint32_t port_id)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_ADD_PORT);
|
||||
|
||||
spa_pod_builder_struct(b, &f,
|
||||
SPA_POD_TYPE_INT, seq,
|
||||
SPA_POD_TYPE_INT, direction, SPA_POD_TYPE_INT, port_id);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void
|
||||
client_node_marshal_remove_port(void *object,
|
||||
uint32_t seq, enum spa_direction direction, uint32_t port_id)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_REMOVE_PORT);
|
||||
|
||||
spa_pod_builder_struct(b, &f,
|
||||
SPA_POD_TYPE_INT, seq,
|
||||
SPA_POD_TYPE_INT, direction, SPA_POD_TYPE_INT, port_id);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void
|
||||
client_node_marshal_set_format(void *object,
|
||||
uint32_t seq,
|
||||
enum spa_direction direction,
|
||||
uint32_t port_id,
|
||||
uint32_t flags,
|
||||
const struct spa_format *format)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_SET_FORMAT);
|
||||
|
||||
spa_pod_builder_struct(b, &f,
|
||||
SPA_POD_TYPE_INT, seq,
|
||||
SPA_POD_TYPE_INT, direction,
|
||||
SPA_POD_TYPE_INT, port_id,
|
||||
SPA_POD_TYPE_INT, flags,
|
||||
SPA_POD_TYPE_POD, format);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void
|
||||
client_node_marshal_set_param(void *object,
|
||||
uint32_t seq,
|
||||
enum spa_direction direction,
|
||||
uint32_t port_id,
|
||||
const struct spa_param *param)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_SET_PARAM);
|
||||
|
||||
spa_pod_builder_struct(b, &f,
|
||||
SPA_POD_TYPE_INT, seq,
|
||||
SPA_POD_TYPE_INT, direction,
|
||||
SPA_POD_TYPE_INT, port_id,
|
||||
SPA_POD_TYPE_POD, param);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void
|
||||
client_node_marshal_add_mem(void *object,
|
||||
enum spa_direction direction,
|
||||
uint32_t port_id,
|
||||
uint32_t mem_id,
|
||||
uint32_t type,
|
||||
int memfd, uint32_t flags, uint32_t offset, uint32_t size)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_ADD_MEM);
|
||||
|
||||
spa_pod_builder_struct(b, &f,
|
||||
SPA_POD_TYPE_INT, direction,
|
||||
SPA_POD_TYPE_INT, port_id,
|
||||
SPA_POD_TYPE_INT, mem_id,
|
||||
SPA_POD_TYPE_ID, type,
|
||||
SPA_POD_TYPE_INT, pw_connection_add_fd(connection, memfd),
|
||||
SPA_POD_TYPE_INT, flags,
|
||||
SPA_POD_TYPE_INT, offset, SPA_POD_TYPE_INT, size);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void
|
||||
client_node_marshal_use_buffers(void *object,
|
||||
uint32_t seq,
|
||||
enum spa_direction direction,
|
||||
uint32_t port_id,
|
||||
uint32_t n_buffers, struct pw_client_node_buffer *buffers)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
uint32_t i, j;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_USE_BUFFERS);
|
||||
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_TYPE_STRUCT, &f,
|
||||
SPA_POD_TYPE_INT, seq,
|
||||
SPA_POD_TYPE_INT, direction,
|
||||
SPA_POD_TYPE_INT, port_id, SPA_POD_TYPE_INT, n_buffers, 0);
|
||||
|
||||
for (i = 0; i < n_buffers; i++) {
|
||||
struct spa_buffer *buf = buffers[i].buffer;
|
||||
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_TYPE_INT, buffers[i].mem_id,
|
||||
SPA_POD_TYPE_INT, buffers[i].offset,
|
||||
SPA_POD_TYPE_INT, buffers[i].size,
|
||||
SPA_POD_TYPE_INT, buf->id, SPA_POD_TYPE_INT, buf->n_metas, 0);
|
||||
|
||||
for (j = 0; j < buf->n_metas; j++) {
|
||||
struct spa_meta *m = &buf->metas[j];
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_TYPE_ID, m->type, SPA_POD_TYPE_INT, m->size, 0);
|
||||
}
|
||||
spa_pod_builder_add(b, SPA_POD_TYPE_INT, buf->n_datas, 0);
|
||||
for (j = 0; j < buf->n_datas; j++) {
|
||||
struct spa_data *d = &buf->datas[j];
|
||||
spa_pod_builder_add(b,
|
||||
SPA_POD_TYPE_ID, d->type,
|
||||
SPA_POD_TYPE_INT, SPA_PTR_TO_UINT32(d->data),
|
||||
SPA_POD_TYPE_INT, d->flags,
|
||||
SPA_POD_TYPE_INT, d->mapoffset,
|
||||
SPA_POD_TYPE_INT, d->maxsize, 0);
|
||||
}
|
||||
}
|
||||
spa_pod_builder_add(b, -SPA_POD_TYPE_STRUCT, &f, 0);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void
|
||||
client_node_marshal_node_command(void *object, uint32_t seq, const struct spa_command *command)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_NODE_COMMAND);
|
||||
|
||||
spa_pod_builder_struct(b, &f, SPA_POD_TYPE_INT, seq, SPA_POD_TYPE_POD, command);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void
|
||||
client_node_marshal_port_command(void *object,
|
||||
uint32_t direction,
|
||||
uint32_t port_id,
|
||||
const struct spa_command *command)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_PORT_COMMAND);
|
||||
|
||||
spa_pod_builder_struct(b, &f,
|
||||
SPA_POD_TYPE_INT, direction,
|
||||
SPA_POD_TYPE_INT, port_id,
|
||||
SPA_POD_TYPE_POD, command);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
static void client_node_marshal_transport(void *object, uint32_t node_id, int readfd, int writefd,
|
||||
int memfd, uint32_t offset, uint32_t size)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct pw_connection *connection = resource->client->protocol_private;
|
||||
struct spa_pod_builder *b;
|
||||
struct spa_pod_frame f;
|
||||
|
||||
b = pw_connection_begin_write_resource(connection, resource, PW_CLIENT_NODE_EVENT_TRANSPORT);
|
||||
|
||||
spa_pod_builder_struct(b, &f,
|
||||
SPA_POD_TYPE_INT, node_id,
|
||||
SPA_POD_TYPE_INT, pw_connection_add_fd(connection, readfd),
|
||||
SPA_POD_TYPE_INT, pw_connection_add_fd(connection, writefd),
|
||||
SPA_POD_TYPE_INT, pw_connection_add_fd(connection, memfd),
|
||||
SPA_POD_TYPE_INT, offset, SPA_POD_TYPE_INT, size);
|
||||
|
||||
pw_connection_end_write(connection, b);
|
||||
}
|
||||
|
||||
|
||||
static bool client_node_demarshal_done(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_iter it;
|
||||
uint32_t seq, res;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &seq,
|
||||
SPA_POD_TYPE_INT, &res, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_methods *) resource->implementation)->done(resource, seq, res);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_update(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_iter it;
|
||||
uint32_t change_mask, max_input_ports, max_output_ports;
|
||||
const struct spa_props *props;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &resource->client->types) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &change_mask,
|
||||
SPA_POD_TYPE_INT, &max_input_ports,
|
||||
SPA_POD_TYPE_INT, &max_output_ports, -SPA_POD_TYPE_OBJECT, &props, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_methods *) resource->implementation)->update(resource, change_mask,
|
||||
max_input_ports,
|
||||
max_output_ports,
|
||||
props);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_port_update(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_iter it;
|
||||
uint32_t i, direction, port_id, change_mask, n_possible_formats, n_params;
|
||||
const struct spa_param **params = NULL;
|
||||
const struct spa_format **possible_formats = NULL, *format = NULL;
|
||||
struct spa_port_info info, *infop = NULL;
|
||||
struct spa_pod *ipod;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &resource->client->types) ||
|
||||
!spa_pod_iter_get(&it,
|
||||
SPA_POD_TYPE_INT, &direction,
|
||||
SPA_POD_TYPE_INT, &port_id,
|
||||
SPA_POD_TYPE_INT, &change_mask,
|
||||
SPA_POD_TYPE_INT, &n_possible_formats, 0))
|
||||
return false;
|
||||
|
||||
possible_formats = alloca(n_possible_formats * sizeof(struct spa_format *));
|
||||
for (i = 0; i < n_possible_formats; i++)
|
||||
if (!spa_pod_iter_get(&it, SPA_POD_TYPE_OBJECT, &possible_formats[i], 0))
|
||||
return false;
|
||||
|
||||
if (!spa_pod_iter_get(&it, -SPA_POD_TYPE_OBJECT, &format, SPA_POD_TYPE_INT, &n_params, 0))
|
||||
return false;
|
||||
|
||||
params = alloca(n_params * sizeof(struct spa_param *));
|
||||
for (i = 0; i < n_params; i++)
|
||||
if (!spa_pod_iter_get(&it, SPA_POD_TYPE_OBJECT, ¶ms[i], 0))
|
||||
return false;
|
||||
|
||||
if (!spa_pod_iter_get(&it, -SPA_POD_TYPE_STRUCT, &ipod, 0))
|
||||
return false;
|
||||
|
||||
if (ipod) {
|
||||
struct spa_pod_iter it2;
|
||||
infop = &info;
|
||||
|
||||
if (!spa_pod_iter_pod(&it2, ipod) ||
|
||||
!spa_pod_iter_get(&it2,
|
||||
SPA_POD_TYPE_INT, &info.flags,
|
||||
SPA_POD_TYPE_INT, &info.rate, 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
((struct pw_client_node_methods *) resource->implementation)->port_update(resource,
|
||||
direction,
|
||||
port_id,
|
||||
change_mask,
|
||||
n_possible_formats,
|
||||
possible_formats,
|
||||
format,
|
||||
n_params,
|
||||
params, infop);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_event_method(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_iter it;
|
||||
struct spa_event *event;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size) ||
|
||||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &resource->client->types) ||
|
||||
!spa_pod_iter_get(&it, SPA_POD_TYPE_OBJECT, &event, 0))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_methods *) resource->implementation)->event(resource, event);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool client_node_demarshal_destroy(void *object, void *data, size_t size)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct spa_pod_iter it;
|
||||
|
||||
if (!spa_pod_iter_struct(&it, data, size))
|
||||
return false;
|
||||
|
||||
((struct pw_client_node_methods *) resource->implementation)->destroy(resource);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct pw_client_node_methods pw_protocol_native_client_client_node_methods = {
|
||||
&client_node_marshal_done,
|
||||
&client_node_marshal_update,
|
||||
&client_node_marshal_port_update,
|
||||
&client_node_marshal_event_method,
|
||||
&client_node_marshal_destroy
|
||||
};
|
||||
|
||||
static const demarshal_func_t pw_protocol_native_client_client_node_demarshal[] = {
|
||||
&client_node_demarshal_transport,
|
||||
&client_node_demarshal_set_props,
|
||||
&client_node_demarshal_event_event,
|
||||
&client_node_demarshal_add_port,
|
||||
&client_node_demarshal_remove_port,
|
||||
&client_node_demarshal_set_format,
|
||||
&client_node_demarshal_set_param,
|
||||
&client_node_demarshal_add_mem,
|
||||
&client_node_demarshal_use_buffers,
|
||||
&client_node_demarshal_node_command,
|
||||
&client_node_demarshal_port_command,
|
||||
};
|
||||
|
||||
static const struct pw_interface pw_protocol_native_client_client_node_interface = {
|
||||
PIPEWIRE_TYPE_NODE_BASE "Client",
|
||||
PW_VERSION_CLIENT_NODE,
|
||||
PW_CLIENT_NODE_METHOD_NUM, &pw_protocol_native_client_client_node_methods,
|
||||
PW_CLIENT_NODE_EVENT_NUM, pw_protocol_native_client_client_node_demarshal,
|
||||
};
|
||||
|
||||
static const demarshal_func_t pw_protocol_native_server_client_node_demarshal[] = {
|
||||
&client_node_demarshal_done,
|
||||
&client_node_demarshal_update,
|
||||
&client_node_demarshal_port_update,
|
||||
&client_node_demarshal_event_method,
|
||||
&client_node_demarshal_destroy,
|
||||
};
|
||||
|
||||
static const struct pw_client_node_events pw_protocol_native_server_client_node_events = {
|
||||
&client_node_marshal_transport,
|
||||
&client_node_marshal_set_props,
|
||||
&client_node_marshal_event_event,
|
||||
&client_node_marshal_add_port,
|
||||
&client_node_marshal_remove_port,
|
||||
&client_node_marshal_set_format,
|
||||
&client_node_marshal_set_param,
|
||||
&client_node_marshal_add_mem,
|
||||
&client_node_marshal_use_buffers,
|
||||
&client_node_marshal_node_command,
|
||||
&client_node_marshal_port_command,
|
||||
};
|
||||
|
||||
const struct pw_interface pw_protocol_native_server_client_node_interface = {
|
||||
PIPEWIRE_TYPE_NODE_BASE "Client",
|
||||
PW_VERSION_CLIENT_NODE,
|
||||
PW_CLIENT_NODE_METHOD_NUM, &pw_protocol_native_server_client_node_demarshal,
|
||||
PW_CLIENT_NODE_EVENT_NUM, &pw_protocol_native_server_client_node_events,
|
||||
};
|
||||
|
||||
struct pw_protocol *pw_protocol_native_ext_client_node_init(void)
|
||||
{
|
||||
static bool init = false;
|
||||
struct pw_protocol *protocol;
|
||||
|
||||
protocol = pw_protocol_get(PW_TYPE_PROTOCOL__Native);
|
||||
|
||||
if (init)
|
||||
return protocol;
|
||||
|
||||
pw_protocol_add_interfaces(protocol,
|
||||
&pw_protocol_native_client_client_node_interface,
|
||||
&pw_protocol_native_server_client_node_interface);
|
||||
|
||||
init = true;
|
||||
|
||||
return protocol;
|
||||
}
|
||||
712
src/modules/module-flatpak.c
Normal file
712
src/modules/module-flatpak.c
Normal file
|
|
@ -0,0 +1,712 @@
|
|||
/* PipeWire
|
||||
* 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 <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include "pipewire/interfaces.h"
|
||||
#include "pipewire/utils.h"
|
||||
|
||||
#include "pipewire/core.h"
|
||||
#include "pipewire/module.h"
|
||||
|
||||
struct impl {
|
||||
struct pw_core *core;
|
||||
struct pw_properties *properties;
|
||||
|
||||
DBusConnection *bus;
|
||||
|
||||
struct pw_listener global_added;
|
||||
struct pw_listener global_removed;
|
||||
|
||||
struct spa_list client_list;
|
||||
|
||||
struct spa_source *dispatch_event;
|
||||
};
|
||||
|
||||
struct client_info {
|
||||
struct impl *impl;
|
||||
struct spa_list link;
|
||||
struct pw_client *client;
|
||||
bool is_sandboxed;
|
||||
const struct pw_core_methods *old_methods;
|
||||
struct pw_core_methods core_methods;
|
||||
struct spa_list async_pending;
|
||||
struct pw_listener resource_impl;
|
||||
struct pw_listener resource_removed;
|
||||
};
|
||||
|
||||
struct async_pending {
|
||||
struct spa_list link;
|
||||
struct client_info *info;
|
||||
bool handled;
|
||||
char *handle;
|
||||
struct pw_resource *resource;
|
||||
char *factory_name;
|
||||
char *name;
|
||||
struct pw_properties *properties;
|
||||
uint32_t new_id;
|
||||
};
|
||||
|
||||
static struct client_info *find_client_info(struct impl *impl, struct pw_client *client)
|
||||
{
|
||||
struct client_info *info;
|
||||
|
||||
spa_list_for_each(info, &impl->client_list, link) {
|
||||
if (info->client == client)
|
||||
return info;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void close_request(struct async_pending *p)
|
||||
{
|
||||
DBusMessage *m = NULL;
|
||||
struct impl *impl = p->info->impl;
|
||||
|
||||
pw_log_debug("pending %p: handle %s", p, p->handle);
|
||||
|
||||
if (!(m = dbus_message_new_method_call("org.freedesktop.portal.Request",
|
||||
p->handle,
|
||||
"org.freedesktop.portal.Request", "Close"))) {
|
||||
pw_log_error("Failed to create message");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dbus_connection_send(impl->bus, m, NULL))
|
||||
pw_log_error("Failed to send message");
|
||||
|
||||
dbus_message_unref(m);
|
||||
}
|
||||
|
||||
static struct async_pending *find_pending(struct client_info *cinfo, const char *handle)
|
||||
{
|
||||
struct async_pending *p;
|
||||
|
||||
spa_list_for_each(p, &cinfo->async_pending, link) {
|
||||
if (strcmp(p->handle, handle) == 0)
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void free_pending(struct async_pending *p)
|
||||
{
|
||||
if (!p->handled)
|
||||
close_request(p);
|
||||
|
||||
pw_log_debug("pending %p: handle %s", p, p->handle);
|
||||
spa_list_remove(&p->link);
|
||||
free(p->handle);
|
||||
free(p->factory_name);
|
||||
free(p->name);
|
||||
if (p->properties)
|
||||
pw_properties_free(p->properties);
|
||||
free(p);
|
||||
}
|
||||
|
||||
static void client_info_free(struct client_info *cinfo)
|
||||
{
|
||||
struct async_pending *p, *tmp;
|
||||
|
||||
spa_list_for_each_safe(p, tmp, &cinfo->async_pending, link)
|
||||
free_pending(p);
|
||||
|
||||
spa_list_remove(&cinfo->link);
|
||||
free(cinfo);
|
||||
}
|
||||
|
||||
static bool client_is_sandboxed(struct pw_client *cl)
|
||||
{
|
||||
char data[2048], *ptr;
|
||||
size_t n, size;
|
||||
const char *state = NULL;
|
||||
const char *current;
|
||||
bool result;
|
||||
int fd;
|
||||
pid_t pid;
|
||||
|
||||
if (cl->ucred_valid) {
|
||||
pw_log_info("client has trusted pid %d", cl->ucred.pid);
|
||||
} else {
|
||||
pw_log_info("no trusted pid found, assuming not sandboxed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
pid = cl->ucred.pid;
|
||||
|
||||
sprintf(data, "/proc/%u/cgroup", pid);
|
||||
fd = open(data, O_RDONLY | O_CLOEXEC, 0);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
|
||||
spa_zero(data);
|
||||
size = sizeof(data);
|
||||
ptr = data;
|
||||
|
||||
while (size > 0) {
|
||||
int r;
|
||||
|
||||
if ((r = read(fd, data, size)) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
ptr += r;
|
||||
size -= r;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
result = false;
|
||||
while ((current = pw_split_walk(data, "\n", &n, &state)) != NULL) {
|
||||
if (strncmp(current, "1:name=systemd:", strlen("1:name=systemd:")) == 0) {
|
||||
const char *p = strstr(current, "flatpak-");
|
||||
if (p && p - current < n) {
|
||||
pw_log_info("found a flatpak cgroup, assuming sandboxed\n");
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool
|
||||
check_global_owner(struct pw_core *core, struct pw_client *client, struct pw_global *global)
|
||||
{
|
||||
if (global == NULL)
|
||||
return false;
|
||||
|
||||
if (global->owner == NULL)
|
||||
return true;
|
||||
|
||||
if (global->owner->client->ucred.uid == client->ucred.uid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
do_global_filter(struct pw_global *global, struct pw_client *client, void *data)
|
||||
{
|
||||
if (global->type == client->core->type.link) {
|
||||
struct pw_link *link = global->object;
|
||||
|
||||
/* we must be able to see both nodes */
|
||||
if (link->output
|
||||
&& !check_global_owner(client->core, client, link->output->node->global))
|
||||
return false;
|
||||
|
||||
if (link->input
|
||||
&& !check_global_owner(client->core, client, link->input->node->global))
|
||||
return false;
|
||||
} else if (!check_global_owner(client->core, client, global))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
portal_response(DBusConnection *connection, DBusMessage *msg, void *user_data)
|
||||
{
|
||||
struct client_info *cinfo = user_data;
|
||||
|
||||
if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
|
||||
uint32_t response = 2;
|
||||
DBusError error;
|
||||
struct async_pending *p;
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
dbus_connection_remove_filter(connection, portal_response, cinfo);
|
||||
|
||||
if (!dbus_message_get_args
|
||||
(msg, &error, DBUS_TYPE_UINT32, &response, DBUS_TYPE_INVALID)) {
|
||||
pw_log_error("failed to parse Response: %s", error.message);
|
||||
dbus_error_free(&error);
|
||||
}
|
||||
|
||||
p = find_pending(cinfo, dbus_message_get_path(msg));
|
||||
if (p == NULL)
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
|
||||
p->handled = true;
|
||||
|
||||
pw_log_debug("portal check result: %d", response);
|
||||
|
||||
if (response == 0) {
|
||||
cinfo->old_methods->create_node (p->resource,
|
||||
p->factory_name,
|
||||
p->name,
|
||||
&p->properties->dict,
|
||||
p->new_id);
|
||||
} else {
|
||||
pw_core_notify_error(cinfo->client->core_resource,
|
||||
p->resource->id, SPA_RESULT_NO_PERMISSION, "not allowed");
|
||||
|
||||
}
|
||||
free_pending(p);
|
||||
pw_client_set_busy(cinfo->client, false);
|
||||
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static void do_create_node(void *object,
|
||||
const char *factory_name,
|
||||
const char *name,
|
||||
const struct spa_dict *props,
|
||||
uint32_t new_id)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct client_info *cinfo = resource->access_private;
|
||||
struct impl *impl = cinfo->impl;
|
||||
struct pw_client *client = resource->client;
|
||||
DBusMessage *m = NULL, *r = NULL;
|
||||
DBusError error;
|
||||
pid_t pid;
|
||||
DBusMessageIter msg_iter;
|
||||
DBusMessageIter dict_iter;
|
||||
const char *handle;
|
||||
const char *device;
|
||||
struct async_pending *p;
|
||||
|
||||
if (!cinfo->is_sandboxed) {
|
||||
cinfo->old_methods->create_node (object, factory_name, name, props, new_id);
|
||||
return;
|
||||
}
|
||||
if (strcmp(factory_name, "client-node") != 0) {
|
||||
pw_log_error("can only allow client-node");
|
||||
goto not_allowed;
|
||||
}
|
||||
|
||||
pw_log_info("ask portal for client %p", cinfo->client);
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
if (!(m = dbus_message_new_method_call("org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.Device", "AccessDevice")))
|
||||
goto no_method_call;
|
||||
|
||||
device = "camera";
|
||||
|
||||
pid = cinfo->client->ucred.pid;
|
||||
if (!dbus_message_append_args(m, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID))
|
||||
goto message_failed;
|
||||
|
||||
dbus_message_iter_init_append(m, &msg_iter);
|
||||
dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "s", &dict_iter);
|
||||
dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &device);
|
||||
dbus_message_iter_close_container(&msg_iter, &dict_iter);
|
||||
|
||||
dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter);
|
||||
dbus_message_iter_close_container(&msg_iter, &dict_iter);
|
||||
|
||||
if (!(r = dbus_connection_send_with_reply_and_block(impl->bus, m, -1, &error)))
|
||||
goto send_failed;
|
||||
|
||||
dbus_message_unref(m);
|
||||
|
||||
if (!dbus_message_get_args(r, &error, DBUS_TYPE_OBJECT_PATH, &handle, DBUS_TYPE_INVALID))
|
||||
goto parse_failed;
|
||||
|
||||
dbus_message_unref(r);
|
||||
|
||||
dbus_bus_add_match(impl->bus,
|
||||
"type='signal',interface='org.freedesktop.portal.Request'", &error);
|
||||
dbus_connection_flush(impl->bus);
|
||||
if (dbus_error_is_set(&error))
|
||||
goto subscribe_failed;
|
||||
|
||||
dbus_connection_add_filter(impl->bus, portal_response, cinfo, NULL);
|
||||
|
||||
p = calloc(1, sizeof(struct async_pending));
|
||||
p->info = cinfo;
|
||||
p->handle = strdup(handle);
|
||||
p->handled = false;
|
||||
p->resource = resource;
|
||||
p->factory_name = strdup(factory_name);
|
||||
p->name = strdup(name);
|
||||
p->properties = props ? pw_properties_new_dict(props) : NULL;
|
||||
p->new_id = new_id;
|
||||
pw_client_set_busy(client, true);
|
||||
|
||||
pw_log_debug("pending %p: handle %s", p, handle);
|
||||
spa_list_insert(cinfo->async_pending.prev, &p->link);
|
||||
|
||||
return;
|
||||
|
||||
no_method_call:
|
||||
pw_log_error("Failed to create message");
|
||||
goto not_allowed;
|
||||
message_failed:
|
||||
dbus_message_unref(m);
|
||||
goto not_allowed;
|
||||
send_failed:
|
||||
pw_log_error("Failed to call portal: %s", error.message);
|
||||
dbus_error_free(&error);
|
||||
dbus_message_unref(m);
|
||||
goto not_allowed;
|
||||
parse_failed:
|
||||
pw_log_error("Failed to parse AccessDevice result: %s", error.message);
|
||||
dbus_error_free(&error);
|
||||
dbus_message_unref(r);
|
||||
goto not_allowed;
|
||||
subscribe_failed:
|
||||
pw_log_error("Failed to subscribe to Request signal: %s", error.message);
|
||||
dbus_error_free(&error);
|
||||
goto not_allowed;
|
||||
not_allowed:
|
||||
pw_core_notify_error(client->core_resource,
|
||||
resource->id, SPA_RESULT_NO_PERMISSION, "not allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
do_create_link(void *object,
|
||||
uint32_t output_node_id,
|
||||
uint32_t output_port_id,
|
||||
uint32_t input_node_id,
|
||||
uint32_t input_port_id,
|
||||
const struct spa_format *filter,
|
||||
const struct spa_dict *props,
|
||||
uint32_t new_id)
|
||||
{
|
||||
struct pw_resource *resource = object;
|
||||
struct client_info *cinfo = resource->access_private;
|
||||
struct pw_client *client = resource->client;
|
||||
|
||||
if (cinfo->is_sandboxed) {
|
||||
pw_core_notify_error(client->core_resource,
|
||||
resource->id, SPA_RESULT_NO_PERMISSION, "not allowed");
|
||||
return;
|
||||
}
|
||||
cinfo->old_methods->create_link (object,
|
||||
output_node_id,
|
||||
output_port_id,
|
||||
input_node_id,
|
||||
input_port_id,
|
||||
filter,
|
||||
props,
|
||||
new_id);
|
||||
}
|
||||
|
||||
static void on_resource_impl(struct pw_listener *listener,
|
||||
struct pw_client *client,
|
||||
struct pw_resource *resource)
|
||||
{
|
||||
struct client_info *cinfo = SPA_CONTAINER_OF(listener, struct client_info, resource_impl);
|
||||
struct impl *impl = cinfo->impl;
|
||||
|
||||
if (resource->type == impl->core->type.core) {
|
||||
cinfo->old_methods = resource->implementation;
|
||||
cinfo->core_methods = *cinfo->old_methods;
|
||||
resource->implementation = &cinfo->core_methods;
|
||||
resource->access_private = cinfo;
|
||||
cinfo->core_methods.create_node = do_create_node;
|
||||
cinfo->core_methods.create_link = do_create_link;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_global_added(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(listener, struct impl, global_added);
|
||||
|
||||
if (global->type == impl->core->type.client) {
|
||||
struct pw_client *client = global->object;
|
||||
struct client_info *cinfo;
|
||||
|
||||
cinfo = calloc(1, sizeof(struct client_info));
|
||||
cinfo->impl = impl;
|
||||
cinfo->client = client;
|
||||
cinfo->is_sandboxed = client_is_sandboxed(client);
|
||||
spa_list_init(&cinfo->async_pending);
|
||||
|
||||
pw_signal_add(&client->resource_impl, &cinfo->resource_impl, on_resource_impl);
|
||||
|
||||
spa_list_insert(impl->client_list.prev, &cinfo->link);
|
||||
|
||||
pw_log_debug("module %p: client %p added", impl, client);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_global_removed(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(listener, struct impl, global_removed);
|
||||
|
||||
if (global->type == impl->core->type.client) {
|
||||
struct pw_client *client = global->object;
|
||||
struct client_info *cinfo;
|
||||
|
||||
if ((cinfo = find_client_info(impl, client)))
|
||||
client_info_free(cinfo);
|
||||
|
||||
pw_log_debug("module %p: client %p removed", impl, client);
|
||||
}
|
||||
}
|
||||
|
||||
static void dispatch_cb(struct spa_loop_utils *utils, struct spa_source *source, void *userdata)
|
||||
{
|
||||
struct impl *impl = userdata;
|
||||
|
||||
if (dbus_connection_dispatch(impl->bus) == DBUS_DISPATCH_COMPLETE)
|
||||
pw_loop_enable_idle(impl->core->main_loop, source, false);
|
||||
}
|
||||
|
||||
static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata)
|
||||
{
|
||||
struct impl *impl = userdata;
|
||||
|
||||
pw_loop_enable_idle(impl->core->main_loop,
|
||||
impl->dispatch_event, status == DBUS_DISPATCH_COMPLETE ? false : true);
|
||||
}
|
||||
|
||||
static inline enum spa_io dbus_to_io(DBusWatch *watch)
|
||||
{
|
||||
enum spa_io mask;
|
||||
unsigned int flags;
|
||||
|
||||
/* no watch flags for disabled watches */
|
||||
if (!dbus_watch_get_enabled(watch))
|
||||
return 0;
|
||||
|
||||
flags = dbus_watch_get_flags(watch);
|
||||
mask = SPA_IO_HUP | SPA_IO_ERR;
|
||||
|
||||
if (flags & DBUS_WATCH_READABLE)
|
||||
mask |= SPA_IO_IN;
|
||||
if (flags & DBUS_WATCH_WRITABLE)
|
||||
mask |= SPA_IO_OUT;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static inline unsigned int io_to_dbus(enum spa_io mask)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
|
||||
if (mask & SPA_IO_IN)
|
||||
flags |= DBUS_WATCH_READABLE;
|
||||
if (mask & SPA_IO_OUT)
|
||||
flags |= DBUS_WATCH_WRITABLE;
|
||||
if (mask & SPA_IO_HUP)
|
||||
flags |= DBUS_WATCH_HANGUP;
|
||||
if (mask & SPA_IO_ERR)
|
||||
flags |= DBUS_WATCH_ERROR;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_io_event(struct spa_loop_utils *utils,
|
||||
struct spa_source *source, int fd, enum spa_io mask, void *userdata)
|
||||
{
|
||||
DBusWatch *watch = userdata;
|
||||
|
||||
if (!dbus_watch_get_enabled(watch)) {
|
||||
pw_log_warn("Asked to handle disabled watch: %p %i", (void *) watch, fd);
|
||||
return;
|
||||
}
|
||||
dbus_watch_handle(watch, io_to_dbus(mask));
|
||||
}
|
||||
|
||||
static dbus_bool_t add_watch(DBusWatch *watch, void *userdata)
|
||||
{
|
||||
struct impl *impl = userdata;
|
||||
struct spa_source *source;
|
||||
|
||||
pw_log_debug("add watch %p %d", watch, dbus_watch_get_unix_fd(watch));
|
||||
|
||||
/* we dup because dbus tends to add the same fd multiple times and our epoll
|
||||
* implementation does not like that */
|
||||
source = pw_loop_add_io(impl->core->main_loop,
|
||||
dup(dbus_watch_get_unix_fd(watch)),
|
||||
dbus_to_io(watch), true, handle_io_event, watch);
|
||||
|
||||
dbus_watch_set_data(watch, source, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void remove_watch(DBusWatch *watch, void *userdata)
|
||||
{
|
||||
struct impl *impl = userdata;
|
||||
struct spa_source *source;
|
||||
|
||||
if ((source = dbus_watch_get_data(watch)))
|
||||
pw_loop_destroy_source(impl->core->main_loop, source);
|
||||
}
|
||||
|
||||
static void toggle_watch(DBusWatch *watch, void *userdata)
|
||||
{
|
||||
struct impl *impl = userdata;
|
||||
struct spa_source *source;
|
||||
|
||||
source = dbus_watch_get_data(watch);
|
||||
|
||||
pw_loop_update_io(impl->core->main_loop, source, dbus_to_io(watch));
|
||||
}
|
||||
|
||||
static void
|
||||
handle_timer_event(struct spa_loop_utils *utils, struct spa_source *source, void *userdata)
|
||||
{
|
||||
DBusTimeout *timeout = userdata;
|
||||
uint64_t t;
|
||||
struct timespec ts;
|
||||
|
||||
if (dbus_timeout_get_enabled(timeout)) {
|
||||
t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC;
|
||||
ts.tv_sec = t / SPA_NSEC_PER_SEC;
|
||||
ts.tv_nsec = t % SPA_NSEC_PER_SEC;
|
||||
spa_loop_utils_update_timer(utils, source, &ts, NULL, false);
|
||||
|
||||
dbus_timeout_handle(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
static dbus_bool_t add_timeout(DBusTimeout *timeout, void *userdata)
|
||||
{
|
||||
struct impl *impl = userdata;
|
||||
struct spa_source *source;
|
||||
struct timespec ts;
|
||||
uint64_t t;
|
||||
|
||||
if (!dbus_timeout_get_enabled(timeout))
|
||||
return FALSE;
|
||||
|
||||
source = pw_loop_add_timer(impl->core->main_loop, handle_timer_event, timeout);
|
||||
|
||||
dbus_timeout_set_data(timeout, source, NULL);
|
||||
|
||||
t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC;
|
||||
ts.tv_sec = t / SPA_NSEC_PER_SEC;
|
||||
ts.tv_nsec = t % SPA_NSEC_PER_SEC;
|
||||
pw_loop_update_timer(impl->core->main_loop, source, &ts, NULL, false);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void remove_timeout(DBusTimeout *timeout, void *userdata)
|
||||
{
|
||||
struct impl *impl = userdata;
|
||||
struct spa_source *source;
|
||||
|
||||
if ((source = dbus_timeout_get_data(timeout)))
|
||||
pw_loop_destroy_source(impl->core->main_loop, source);
|
||||
}
|
||||
|
||||
static void toggle_timeout(DBusTimeout *timeout, void *userdata)
|
||||
{
|
||||
struct impl *impl = userdata;
|
||||
struct spa_source *source;
|
||||
struct timespec ts, *tsp;
|
||||
|
||||
source = dbus_timeout_get_data(timeout);
|
||||
|
||||
if (dbus_timeout_get_enabled(timeout)) {
|
||||
uint64_t t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC;
|
||||
ts.tv_sec = t / SPA_NSEC_PER_SEC;
|
||||
ts.tv_nsec = t % SPA_NSEC_PER_SEC;
|
||||
tsp = &ts;
|
||||
} else {
|
||||
tsp = NULL;
|
||||
}
|
||||
pw_loop_update_timer(impl->core->main_loop, source, tsp, NULL, false);
|
||||
}
|
||||
|
||||
static void wakeup_main(void *userdata)
|
||||
{
|
||||
struct impl *impl = userdata;
|
||||
|
||||
pw_loop_enable_idle(impl->core->main_loop, impl->dispatch_event, true);
|
||||
}
|
||||
|
||||
static struct impl *module_new(struct pw_core *core, struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl;
|
||||
DBusError error;
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
pw_log_debug("module %p: new", impl);
|
||||
|
||||
impl->core = core;
|
||||
impl->properties = properties;
|
||||
|
||||
impl->bus = dbus_bus_get_private(DBUS_BUS_SESSION, &error);
|
||||
if (impl->bus == NULL)
|
||||
goto error;
|
||||
|
||||
impl->dispatch_event = pw_loop_add_idle(core->main_loop, false, dispatch_cb, impl);
|
||||
|
||||
dbus_connection_set_exit_on_disconnect(impl->bus, false);
|
||||
dbus_connection_set_dispatch_status_function(impl->bus, dispatch_status, impl, NULL);
|
||||
dbus_connection_set_watch_functions(impl->bus, add_watch, remove_watch, toggle_watch, impl,
|
||||
NULL);
|
||||
dbus_connection_set_timeout_functions(impl->bus, add_timeout, remove_timeout,
|
||||
toggle_timeout, impl, NULL);
|
||||
dbus_connection_set_wakeup_main_function(impl->bus, wakeup_main, impl, NULL);
|
||||
|
||||
spa_list_init(&impl->client_list);
|
||||
|
||||
pw_signal_add(&core->global_added, &impl->global_added, on_global_added);
|
||||
pw_signal_add(&core->global_removed, &impl->global_removed, on_global_removed);
|
||||
core->global_filter = do_global_filter;
|
||||
core->global_filter_data = impl;
|
||||
|
||||
return impl;
|
||||
|
||||
error:
|
||||
pw_log_error("Failed to connect to system bus: %s", error.message);
|
||||
dbus_error_free(&error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void module_destroy(struct impl *impl)
|
||||
{
|
||||
pw_log_debug("module %p: destroy", impl);
|
||||
|
||||
dbus_connection_close(impl->bus);
|
||||
dbus_connection_unref(impl->bus);
|
||||
free(impl);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
module_new(module->core, NULL);
|
||||
return true;
|
||||
}
|
||||
754
src/modules/module-jack.c
Normal file
754
src/modules/module-jack.c
Normal file
|
|
@ -0,0 +1,754 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/mman.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/session.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "pipewire/pipewire.h"
|
||||
#include "pipewire/log.h"
|
||||
#include "pipewire/interfaces.h"
|
||||
|
||||
#include "pipewire/core.h"
|
||||
#include "pipewire/node.h"
|
||||
#include "pipewire/module.h"
|
||||
#include "pipewire/client.h"
|
||||
#include "pipewire/resource.h"
|
||||
#include "pipewire/link.h"
|
||||
#include "pipewire/node-factory.h"
|
||||
#include "pipewire/data-loop.h"
|
||||
#include "pipewire/main-loop.h"
|
||||
|
||||
#include "modules/module-jack/defs.h"
|
||||
#include "modules/module-jack/shm.h"
|
||||
#include "modules/module-jack/shared.h"
|
||||
#include "modules/module-jack/synchro.h"
|
||||
#include "modules/module-jack/server.h"
|
||||
|
||||
#ifndef UNIX_PATH_MAX
|
||||
#define UNIX_PATH_MAX 108
|
||||
#endif
|
||||
|
||||
#define LOCK_SUFFIX ".lock"
|
||||
#define LOCK_SUFFIXLEN 5
|
||||
|
||||
int segment_num = 0;
|
||||
|
||||
typedef bool(*demarshal_func_t) (void *object, void *data, size_t size);
|
||||
|
||||
struct socket {
|
||||
int fd;
|
||||
struct sockaddr_un addr;
|
||||
char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN];
|
||||
|
||||
struct pw_loop *loop;
|
||||
struct spa_source *source;
|
||||
char *core_name;
|
||||
struct spa_list link;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
struct pw_core *core;
|
||||
struct spa_list link;
|
||||
|
||||
struct pw_properties *properties;
|
||||
|
||||
struct spa_list socket_list;
|
||||
struct spa_list client_list;
|
||||
|
||||
struct spa_loop_control_hooks hooks;
|
||||
|
||||
struct jack_server server;
|
||||
};
|
||||
|
||||
struct client {
|
||||
struct impl *impl;
|
||||
struct spa_list link;
|
||||
struct pw_client *client;
|
||||
int fd;
|
||||
struct spa_source *source;
|
||||
struct pw_listener busy_changed;
|
||||
};
|
||||
|
||||
static int process_messages(struct client *client);
|
||||
|
||||
static void client_destroy(void *data)
|
||||
{
|
||||
struct pw_client *client = data;
|
||||
struct client *this = client->user_data;
|
||||
|
||||
pw_loop_destroy_source(this->impl->core->main_loop, this->source);
|
||||
spa_list_remove(&this->link);
|
||||
|
||||
close(this->fd);
|
||||
}
|
||||
|
||||
static int
|
||||
handle_register_port(struct client *client)
|
||||
{
|
||||
int result = 0;
|
||||
int ref_num;
|
||||
char name[JACK_PORT_NAME_SIZE + 1];
|
||||
char port_type[JACK_PORT_TYPE_SIZE + 1];
|
||||
unsigned int flags;
|
||||
unsigned int buffer_size;
|
||||
static jack_port_id_t port_index = 0;
|
||||
|
||||
CheckSize(kRegisterPort_size);
|
||||
CheckRead(&ref_num, sizeof(int));
|
||||
CheckRead(name, sizeof(name));
|
||||
CheckRead(port_type, sizeof(port_type));
|
||||
CheckRead(&flags, sizeof(unsigned int));
|
||||
CheckRead(&buffer_size, sizeof(unsigned int));
|
||||
|
||||
pw_log_error("protocol-jack %p: kRegisterPort %d %s %s %u %u", client->impl,
|
||||
ref_num, name, port_type, flags, buffer_size);
|
||||
port_index++;
|
||||
|
||||
CheckWrite(&result, sizeof(int));
|
||||
CheckWrite(&port_index, sizeof(jack_port_id_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_activate_client(struct client *client)
|
||||
{
|
||||
int result = 0;
|
||||
int ref_num;
|
||||
int is_real_time;
|
||||
|
||||
CheckSize(kActivateClient_size);
|
||||
CheckRead(&ref_num, sizeof(int));
|
||||
CheckRead(&is_real_time, sizeof(int));
|
||||
|
||||
pw_log_error("protocol-jack %p: kActivateClient %d %d", client->impl,
|
||||
ref_num, is_real_time);
|
||||
|
||||
CheckWrite(&result, sizeof(int));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_deactivate_client(struct client *client)
|
||||
{
|
||||
int result = 0;
|
||||
int ref_num;
|
||||
|
||||
CheckSize(kDeactivateClient_size);
|
||||
CheckRead(&ref_num, sizeof(int));
|
||||
|
||||
pw_log_error("protocol-jack %p: kDeactivateClient %d", client->impl,
|
||||
ref_num);
|
||||
|
||||
CheckWrite(&result, sizeof(int));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_client_check(struct client *client)
|
||||
{
|
||||
char name[JACK_CLIENT_NAME_SIZE+1];
|
||||
int protocol;
|
||||
int options;
|
||||
int UUID;
|
||||
int open;
|
||||
int result = 0;
|
||||
int status;
|
||||
|
||||
CheckSize(kClientCheck_size);
|
||||
CheckRead(name, sizeof(name));
|
||||
CheckRead(&protocol, sizeof(int));
|
||||
CheckRead(&options, sizeof(int));
|
||||
CheckRead(&UUID, sizeof(int));
|
||||
CheckRead(&open, sizeof(int));
|
||||
|
||||
pw_log_error("protocol-jack %p: kClientCheck %s %d %d %d %d", client->impl,
|
||||
name, protocol, options, UUID, open);
|
||||
|
||||
status = 0;
|
||||
if (protocol != JACK_PROTOCOL_VERSION) {
|
||||
status |= (JackFailure | JackVersionError);
|
||||
pw_log_error("protocol-jack: protocol mismatch (%d vs %d)", protocol, JACK_PROTOCOL_VERSION);
|
||||
result = -1;
|
||||
goto reply;
|
||||
}
|
||||
/* TODO check client name and uuid */
|
||||
|
||||
reply:
|
||||
CheckWrite(&result, sizeof(int));
|
||||
CheckWrite(name, sizeof(name));
|
||||
CheckWrite(&status, sizeof(int));
|
||||
|
||||
if (open)
|
||||
return process_messages(client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_client_open(struct client *client)
|
||||
{
|
||||
struct impl *impl = client->impl;
|
||||
struct jack_server *server = &impl->server;
|
||||
int PID, UUID;
|
||||
char name[JACK_CLIENT_NAME_SIZE+1];
|
||||
int result, ref_num, shared_engine, shared_client, shared_graph;
|
||||
struct jack_client *jc;
|
||||
|
||||
CheckSize(kClientOpen_size);
|
||||
CheckRead(&PID, sizeof(int));
|
||||
CheckRead(&UUID, sizeof(int));
|
||||
CheckRead(name, sizeof(name));
|
||||
|
||||
ref_num = jack_server_allocate_ref_num(server);
|
||||
if (ref_num == -1) {
|
||||
result = -1;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
jc = calloc(1,sizeof(struct jack_client));
|
||||
jc->owner = client;
|
||||
jc->ref_num = ref_num;
|
||||
|
||||
if (jack_synchro_init(&server->synchro_table[ref_num],
|
||||
name,
|
||||
server->engine_control->server_name,
|
||||
0,
|
||||
false,
|
||||
server->promiscuous) < 0) {
|
||||
result = -1;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
jc->control = jack_client_control_alloc(name, client->client->ucred.pid, ref_num, -1);
|
||||
if (jc->control == NULL) {
|
||||
result = -1;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
server->client_table[ref_num] = jc;
|
||||
|
||||
result = 0;
|
||||
shared_engine = impl->server.engine_control->info.index;
|
||||
shared_client = jc->control->info.index;
|
||||
shared_graph = impl->server.graph_manager->info.index;
|
||||
|
||||
reply:
|
||||
CheckWrite(&result, sizeof(int));
|
||||
CheckWrite(&shared_engine, sizeof(int));
|
||||
CheckWrite(&shared_client, sizeof(int));
|
||||
CheckWrite(&shared_graph, sizeof(int));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_client_close(struct client *client)
|
||||
{
|
||||
int ref_num;
|
||||
CheckSize(kClientClose_size);
|
||||
CheckRead(&ref_num, sizeof(int));
|
||||
int result = 0;
|
||||
|
||||
CheckWrite(&result, sizeof(int));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_connect_name_ports(struct client *client)
|
||||
{
|
||||
int ref_num;
|
||||
char src[REAL_JACK_PORT_NAME_SIZE+1];
|
||||
char dst[REAL_JACK_PORT_NAME_SIZE+1];
|
||||
int result = 0;
|
||||
|
||||
CheckSize(kConnectNamePorts_size);
|
||||
CheckRead(&ref_num, sizeof(int));
|
||||
CheckRead(src, sizeof(src));
|
||||
CheckRead(dst, sizeof(dst));
|
||||
|
||||
CheckWrite(&result, sizeof(int));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_get_UUID_by_client(struct client *client)
|
||||
{
|
||||
char name[JACK_CLIENT_NAME_SIZE+1];
|
||||
char UUID[JACK_UUID_SIZE];
|
||||
int result = 0;
|
||||
|
||||
CheckSize(kGetUUIDByClient_size);
|
||||
CheckRead(name, sizeof(name));
|
||||
|
||||
CheckWrite(&result, sizeof(int));
|
||||
CheckWrite(UUID, sizeof(UUID));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
process_messages(struct client *client)
|
||||
{
|
||||
struct pw_client *c = client->client;
|
||||
int type, res = -1;
|
||||
|
||||
if (read(client->fd, &type, sizeof(enum jack_request_type)) != sizeof(enum jack_request_type)) {
|
||||
pw_log_error("protocol-jack %p: failed to read type", client->impl);
|
||||
goto error;
|
||||
}
|
||||
pw_log_info("protocol-jack %p: got type %d", client->impl, type);
|
||||
|
||||
switch(type) {
|
||||
case jack_request_RegisterPort:
|
||||
res = handle_register_port(client);
|
||||
break;
|
||||
case jack_request_UnRegisterPort:
|
||||
case jack_request_ConnectPorts:
|
||||
case jack_request_DisconnectPorts:
|
||||
case jack_request_SetTimeBaseClient:
|
||||
case jack_request_ActivateClient:
|
||||
res = handle_activate_client(client);
|
||||
break;
|
||||
case jack_request_DeactivateClient:
|
||||
res = handle_deactivate_client(client);
|
||||
break;
|
||||
case jack_request_DisconnectPort:
|
||||
case jack_request_SetClientCapabilities:
|
||||
case jack_request_GetPortConnections:
|
||||
case jack_request_GetPortNConnections:
|
||||
case jack_request_ReleaseTimebase:
|
||||
case jack_request_SetTimebaseCallback:
|
||||
case jack_request_SetBufferSize:
|
||||
case jack_request_SetFreeWheel:
|
||||
break;
|
||||
case jack_request_ClientCheck:
|
||||
res = handle_client_check(client);
|
||||
break;
|
||||
case jack_request_ClientOpen:
|
||||
res = handle_client_open(client);
|
||||
break;
|
||||
case jack_request_ClientClose:
|
||||
res = handle_client_close(client);
|
||||
break;
|
||||
case jack_request_ConnectNamePorts:
|
||||
res = handle_connect_name_ports(client);
|
||||
break;
|
||||
case jack_request_DisconnectNamePorts:
|
||||
case jack_request_GetInternalClientName:
|
||||
case jack_request_InternalClientHandle:
|
||||
case jack_request_InternalClientLoad:
|
||||
case jack_request_InternalClientUnload:
|
||||
case jack_request_PortRename:
|
||||
case jack_request_Notification:
|
||||
case jack_request_SessionNotify:
|
||||
case jack_request_SessionReply:
|
||||
case jack_request_GetClientByUUID:
|
||||
case jack_request_ReserveClientName:
|
||||
break;
|
||||
case jack_request_GetUUIDByClient:
|
||||
res = handle_get_UUID_by_client(client);
|
||||
break;
|
||||
case jack_request_ClientHasSessionCallback:
|
||||
case jack_request_ComputeTotalLatencies:
|
||||
break;
|
||||
default:
|
||||
pw_log_error("protocol-jack %p: invalid type %d", client->impl, type);
|
||||
goto error;
|
||||
}
|
||||
if (res != 0)
|
||||
goto error;
|
||||
|
||||
return res;
|
||||
|
||||
error:
|
||||
pw_log_error("protocol-jack %p: error handling type %d", client->impl, type);
|
||||
pw_client_destroy(c);
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
on_busy_changed(struct pw_listener *listener,
|
||||
struct pw_client *client)
|
||||
{
|
||||
struct client *c = SPA_CONTAINER_OF(listener, struct client, busy_changed);
|
||||
enum spa_io mask = SPA_IO_ERR | SPA_IO_HUP;
|
||||
|
||||
if (!client->busy)
|
||||
mask |= SPA_IO_IN;
|
||||
|
||||
pw_loop_update_io(c->impl->core->main_loop, c->source, mask);
|
||||
|
||||
if (!client->busy)
|
||||
process_messages(c);
|
||||
}
|
||||
|
||||
static void
|
||||
connection_data(struct spa_loop_utils *utils,
|
||||
struct spa_source *source, int fd, enum spa_io mask, void *data)
|
||||
{
|
||||
struct client *client = data;
|
||||
|
||||
if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
|
||||
pw_log_error("protocol-native %p: got connection error", client->impl);
|
||||
pw_client_destroy(client->client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mask & SPA_IO_IN)
|
||||
process_messages(client);
|
||||
}
|
||||
|
||||
static struct client *client_new(struct impl *impl, int fd)
|
||||
{
|
||||
struct client *this;
|
||||
struct pw_client *client;
|
||||
socklen_t len;
|
||||
struct ucred ucred, *ucredp;
|
||||
|
||||
len = sizeof(ucred);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
|
||||
pw_log_error("no peercred: %m");
|
||||
ucredp = NULL;
|
||||
} else {
|
||||
ucredp = &ucred;
|
||||
}
|
||||
|
||||
client = pw_client_new(impl->core, ucredp, NULL, sizeof(struct client));
|
||||
if (client == NULL)
|
||||
goto no_client;
|
||||
|
||||
client->destroy = client_destroy;
|
||||
|
||||
this = client->user_data;
|
||||
this->impl = impl;
|
||||
this->fd = fd;
|
||||
this->source = pw_loop_add_io(impl->core->main_loop,
|
||||
this->fd,
|
||||
SPA_IO_ERR | SPA_IO_HUP, false, connection_data, this);
|
||||
if (this->source == NULL)
|
||||
goto no_source;
|
||||
|
||||
this->client = client;
|
||||
|
||||
spa_list_insert(impl->client_list.prev, &this->link);
|
||||
|
||||
pw_signal_add(&client->busy_changed, &this->busy_changed, on_busy_changed);
|
||||
|
||||
pw_log_error("module-jack %p: added new client", impl);
|
||||
|
||||
return this;
|
||||
|
||||
no_source:
|
||||
free(this);
|
||||
no_client:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
make_int_client(struct impl *impl, struct pw_node *node)
|
||||
{
|
||||
struct jack_server *server = &impl->server;
|
||||
struct jack_connection_manager *conn;
|
||||
int ref_num;
|
||||
struct jack_client *jc;
|
||||
jack_port_id_t port_id;
|
||||
|
||||
ref_num = jack_server_allocate_ref_num(server);
|
||||
if (ref_num == -1)
|
||||
return -1;
|
||||
|
||||
if (jack_synchro_init(&server->synchro_table[ref_num],
|
||||
"system",
|
||||
server->engine_control->server_name,
|
||||
0,
|
||||
false,
|
||||
server->promiscuous) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
jc = calloc(1,sizeof(struct jack_client));
|
||||
jc->ref_num = ref_num;
|
||||
jc->node = node;
|
||||
jc->control = jack_client_control_alloc("system", -1, ref_num, -1);
|
||||
jc->control->active = true;
|
||||
|
||||
server->client_table[ref_num] = jc;
|
||||
|
||||
impl->server.engine_control->driver_num++;
|
||||
|
||||
conn = jack_graph_manager_next_start(server->graph_manager);
|
||||
|
||||
jack_connection_manager_init_ref_num(conn, ref_num);
|
||||
|
||||
port_id = jack_graph_manager_allocate_port(server->graph_manager,
|
||||
ref_num, "system:playback_1", 2,
|
||||
JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal);
|
||||
|
||||
jack_connection_manager_add_port(conn, true, ref_num, port_id);
|
||||
|
||||
jack_graph_manager_next_stop(server->graph_manager);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool init_nodes(struct impl *impl)
|
||||
{
|
||||
struct pw_core *core = impl->core;
|
||||
struct pw_node *n;
|
||||
|
||||
spa_list_for_each(n, &core->node_list, link) {
|
||||
const char *str;
|
||||
|
||||
if (n->global == NULL)
|
||||
continue;
|
||||
|
||||
if (n->properties == NULL)
|
||||
continue;
|
||||
|
||||
if ((str = pw_properties_get(n->properties, "media.class")) == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp(str, "Audio/Sink") != 0)
|
||||
continue;
|
||||
|
||||
make_int_client(impl, n);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct socket *create_socket(void)
|
||||
{
|
||||
struct socket *s;
|
||||
|
||||
if ((s = calloc(1, sizeof(struct socket))) == NULL)
|
||||
return NULL;
|
||||
|
||||
s->fd = -1;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void destroy_socket(struct socket *s)
|
||||
{
|
||||
if (s->source)
|
||||
pw_loop_destroy_source(s->loop, s->source);
|
||||
if (s->addr.sun_path[0])
|
||||
unlink(s->addr.sun_path);
|
||||
if (s->fd >= 0)
|
||||
close(s->fd);
|
||||
if (s->lock_addr[0])
|
||||
unlink(s->lock_addr);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static bool init_socket_name(struct socket *s, const char *name, bool promiscuous, int which)
|
||||
{
|
||||
int name_size;
|
||||
const char *runtime_dir;
|
||||
|
||||
runtime_dir = JACK_SOCKET_DIR;
|
||||
|
||||
s->addr.sun_family = AF_UNIX;
|
||||
if (promiscuous) {
|
||||
name_size = snprintf(s->addr.sun_path, sizeof(s->addr.sun_path),
|
||||
"%s/jack_%s_%d", runtime_dir, name, which) + 1;
|
||||
} else {
|
||||
name_size = snprintf(s->addr.sun_path, sizeof(s->addr.sun_path),
|
||||
"%s/jack_%s_%d_%d", runtime_dir, name, getuid(), which) + 1;
|
||||
}
|
||||
|
||||
s->core_name = (s->addr.sun_path + name_size - 1) - strlen(name);
|
||||
|
||||
if (name_size > (int) sizeof(s->addr.sun_path)) {
|
||||
pw_log_error("socket path \"%s/%s\" plus null terminator exceeds 108 bytes",
|
||||
runtime_dir, name);
|
||||
*s->addr.sun_path = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
socket_data(struct spa_loop_utils *utils,
|
||||
struct spa_source *source, int fd, enum spa_io mask, void *data)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
struct client *client;
|
||||
struct sockaddr_un name;
|
||||
socklen_t length;
|
||||
int client_fd;
|
||||
|
||||
length = sizeof(name);
|
||||
client_fd = accept4(fd, (struct sockaddr *) &name, &length, SOCK_CLOEXEC);
|
||||
if (client_fd < 0) {
|
||||
pw_log_error("failed to accept: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
client = client_new(impl, client_fd);
|
||||
if (client == NULL) {
|
||||
pw_log_error("failed to create client");
|
||||
close(client_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
pw_loop_update_io(impl->core->main_loop,
|
||||
client->source, SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP);
|
||||
}
|
||||
|
||||
static bool add_socket(struct impl *impl, struct socket *s)
|
||||
{
|
||||
socklen_t size;
|
||||
|
||||
if ((s->fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0)
|
||||
return false;
|
||||
|
||||
size = offsetof(struct sockaddr_un, sun_path) + strlen(s->addr.sun_path);
|
||||
if (bind(s->fd, (struct sockaddr *) &s->addr, size) < 0) {
|
||||
pw_log_error("bind() failed with error: %m");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (listen(s->fd, 100) < 0) {
|
||||
pw_log_error("listen() failed with error: %m");
|
||||
return false;
|
||||
}
|
||||
|
||||
s->loop = impl->core->main_loop;
|
||||
s->source = pw_loop_add_io(s->loop, s->fd, SPA_IO_IN, false, socket_data, impl);
|
||||
if (s->source == NULL)
|
||||
return false;
|
||||
|
||||
spa_list_insert(impl->socket_list.prev, &s->link);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int init_server(struct impl *impl, const char *name, bool promiscuous)
|
||||
{
|
||||
struct jack_server *server = &impl->server;
|
||||
int i;
|
||||
struct socket *s;
|
||||
|
||||
pthread_mutex_init(&server->lock, NULL);
|
||||
|
||||
if (jack_register_server(name, 1) != 0)
|
||||
return -1;
|
||||
|
||||
jack_cleanup_shm();
|
||||
|
||||
/* graph manager */
|
||||
server->graph_manager = jack_graph_manager_alloc(2048);
|
||||
|
||||
/* engine control */
|
||||
server->engine_control = jack_engine_control_alloc(name);
|
||||
|
||||
for (i = 0; i < CLIENT_NUM; i++)
|
||||
server->synchro_table[i] = JACK_SYNCHRO_INIT;
|
||||
|
||||
if (!init_nodes(impl))
|
||||
return -1;
|
||||
|
||||
s = create_socket();
|
||||
|
||||
if (!init_socket_name(s, name, promiscuous, 0))
|
||||
goto error;
|
||||
|
||||
if (!add_socket(impl, s))
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
destroy_socket(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static struct impl *module_new(struct pw_core *core, struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl;
|
||||
const char *name, *str;
|
||||
bool promiscuous;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
pw_log_debug("protocol-jack %p: new", impl);
|
||||
|
||||
impl->core = core;
|
||||
impl->properties = properties;
|
||||
|
||||
spa_list_init(&impl->socket_list);
|
||||
spa_list_init(&impl->client_list);
|
||||
|
||||
str = NULL;
|
||||
if (impl->properties)
|
||||
str = pw_properties_get(impl->properties, "jack.default.server");
|
||||
if (str == NULL)
|
||||
str = getenv("JACK_DEFAULT_SERVER");
|
||||
|
||||
name = str ? str : JACK_DEFAULT_SERVER_NAME;
|
||||
|
||||
str = NULL;
|
||||
if (impl->properties)
|
||||
str = pw_properties_get(impl->properties, "jack.promiscuous.server");
|
||||
if (str == NULL)
|
||||
str = getenv("JACK_PROMISCUOUS_SERVER");
|
||||
|
||||
promiscuous = str ? atoi(str) != 0 : false;
|
||||
|
||||
if (init_server(impl, name, promiscuous) < 0)
|
||||
goto error;
|
||||
|
||||
return impl;
|
||||
|
||||
error:
|
||||
free(impl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void module_destroy(struct impl *impl)
|
||||
{
|
||||
struct impl *object, *tmp;
|
||||
|
||||
pw_log_debug("module %p: destroy", impl);
|
||||
|
||||
free(impl);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
module_new(module->core, NULL);
|
||||
return true;
|
||||
}
|
||||
143
src/modules/module-jack/defs.h
Normal file
143
src/modules/module-jack/defs.h
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2017 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 "pipewire/log.h"
|
||||
|
||||
#define USE_POSIX_SHM
|
||||
#undef JACK_MONITOR
|
||||
|
||||
#define JACK_DEFAULT_SERVER_NAME "default"
|
||||
#define JACK_SOCKET_DIR "/dev/shm"
|
||||
#define JACK_SHM_DIR "/dev/shm"
|
||||
#define JACK_SERVER_NAME_SIZE 256
|
||||
#define JACK_CLIENT_NAME_SIZE 64
|
||||
#define JACK_PORT_NAME_SIZE 256
|
||||
#define JACK_PORT_TYPE_SIZE 32
|
||||
#define JACK_PROTOCOL_VERSION 8
|
||||
|
||||
#define PORT_NUM_MAX 4096
|
||||
#define PORT_NUM_FOR_CLIENT 2048
|
||||
#define CONNECTION_NUM_FOR_PORT PORT_NUM_FOR_CLIENT
|
||||
|
||||
#define REAL_JACK_PORT_NAME_SIZE JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE
|
||||
|
||||
#define BUFFER_SIZE_MAX 8192
|
||||
|
||||
#define CLIENT_NUM 256
|
||||
|
||||
#define JACK_ENGINE_ROLLING_COUNT 32
|
||||
#define JACK_ENGINE_ROLLING_INTERVAL 1024
|
||||
|
||||
#define TIME_POINTS 100000
|
||||
#define FAILURE_TIME_POINTS 10000
|
||||
#define FAILURE_WINDOW 10
|
||||
#define MEASURED_CLIENTS 32
|
||||
|
||||
#define SYNC_MAX_NAME_SIZE 256
|
||||
|
||||
#define JACK_UUID_SIZE 36
|
||||
#define JACK_UUID_STRING_SIZE (JACK_UUID_SIZE+1)
|
||||
|
||||
#define JACK_SESSION_COMMAND_SIZE 256
|
||||
|
||||
#define NO_PORT 0xFFFE
|
||||
#define EMPTY 0xFFFD
|
||||
#define FREE 0xFFFC
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
JACK_TIMER_SYSTEM_CLOCK,
|
||||
JACK_TIMER_HPET,
|
||||
} jack_timer_type_t;
|
||||
|
||||
enum jack_request_type {
|
||||
jack_request_RegisterPort = 1,
|
||||
jack_request_UnRegisterPort = 2,
|
||||
jack_request_ConnectPorts = 3,
|
||||
jack_request_DisconnectPorts = 4,
|
||||
jack_request_SetTimeBaseClient = 5,
|
||||
jack_request_ActivateClient = 6,
|
||||
jack_request_DeactivateClient = 7,
|
||||
jack_request_DisconnectPort = 8,
|
||||
jack_request_SetClientCapabilities = 9,
|
||||
jack_request_GetPortConnections = 10,
|
||||
jack_request_GetPortNConnections = 11,
|
||||
jack_request_ReleaseTimebase = 12,
|
||||
jack_request_SetTimebaseCallback = 13,
|
||||
jack_request_SetBufferSize = 20,
|
||||
jack_request_SetFreeWheel = 21,
|
||||
jack_request_ClientCheck = 22,
|
||||
jack_request_ClientOpen = 23,
|
||||
jack_request_ClientClose = 24,
|
||||
jack_request_ConnectNamePorts = 25,
|
||||
jack_request_DisconnectNamePorts = 26,
|
||||
jack_request_GetInternalClientName = 27,
|
||||
jack_request_InternalClientHandle = 28,
|
||||
jack_request_InternalClientLoad = 29,
|
||||
jack_request_InternalClientUnload = 30,
|
||||
jack_request_PortRename = 31,
|
||||
jack_request_Notification = 32,
|
||||
jack_request_SessionNotify = 33,
|
||||
jack_request_SessionReply = 34,
|
||||
jack_request_GetClientByUUID = 35,
|
||||
jack_request_ReserveClientName = 36,
|
||||
jack_request_GetUUIDByClient = 37,
|
||||
jack_request_ClientHasSessionCallback = 38,
|
||||
jack_request_ComputeTotalLatencies = 39
|
||||
};
|
||||
|
||||
enum jack_notification_type {
|
||||
jack_notify_AddClient = 0,
|
||||
jack_notify_RemoveClient = 1,
|
||||
jack_notify_ActivateClient = 2,
|
||||
jack_notify_XRunCallback = 3,
|
||||
jack_notify_GraphOrderCallback = 4,
|
||||
jack_notify_BufferSizeCallback = 5,
|
||||
jack_notify_SampleRateCallback = 6,
|
||||
jack_notify_StartFreewheelCallback = 7,
|
||||
jack_notify_StopFreewheelCallback = 8,
|
||||
jack_notify_PortRegistrationOnCallback = 9,
|
||||
jack_notify_PortRegistrationOffCallback = 10,
|
||||
jack_notify_PortConnectCallback = 11,
|
||||
jack_notify_PortDisconnectCallback = 12,
|
||||
jack_notify_PortRenameCallback = 13,
|
||||
jack_notify_RealTimeCallback = 14,
|
||||
jack_notify_ShutDownCallback = 15,
|
||||
jack_notify_QUIT = 16,
|
||||
jack_notify_SessionCallback = 17,
|
||||
jack_notify_LatencyCallback = 18,
|
||||
jack_notify_max = 64 // To keep some room in JackClientControl fCallback table
|
||||
};
|
||||
|
||||
#define kActivateClient_size (2*sizeof(int))
|
||||
#define kDeactivateClient_size (sizeof(int))
|
||||
#define kRegisterPort_size (sizeof(int) + JACK_PORT_NAME_SIZE+1 + JACK_PORT_TYPE_SIZE+1 + 2*sizeof(unsigned int))
|
||||
#define kClientCheck_size (JACK_CLIENT_NAME_SIZE+1 + 4 * sizeof(int))
|
||||
#define kClientOpen_size (JACK_CLIENT_NAME_SIZE+1 + 2 * sizeof(int))
|
||||
#define kClientClose_size (sizeof(int))
|
||||
#define kConnectNamePorts_size (sizeof(int) + REAL_JACK_PORT_NAME_SIZE+1 + REAL_JACK_PORT_NAME_SIZE+1)
|
||||
#define kGetUUIDByClient_size (JACK_CLIENT_NAME_SIZE+1)
|
||||
|
||||
#define CheckRead(var,size) if(read(client->fd,var,size)!=size) {pw_log_error("read error"); return -1; }
|
||||
#define CheckWrite(var,size) if(write(client->fd,var,size)!=size) {pw_log_error("write error"); return -1; }
|
||||
#define CheckSize(expected) { int __size; CheckRead(&__size, sizeof(int)); if (__size != expected) { pw_log_error("CheckSize error size %d != %d", __size, (int)expected); return -1; } }
|
||||
|
||||
#define jack_error pw_log_error
|
||||
#define jack_log pw_log_info
|
||||
54
src/modules/module-jack/server.h
Normal file
54
src/modules/module-jack/server.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
struct jack_client {
|
||||
int ref_num;
|
||||
struct client *owner;
|
||||
struct jack_client_control *control;
|
||||
struct pw_node *node;
|
||||
};
|
||||
|
||||
struct jack_server {
|
||||
pthread_mutex_t lock;
|
||||
|
||||
bool promiscuous;
|
||||
|
||||
struct jack_graph_manager *graph_manager;
|
||||
struct jack_engine_control *engine_control;
|
||||
|
||||
struct jack_client* client_table[CLIENT_NUM];
|
||||
struct jack_synchro synchro_table[CLIENT_NUM];
|
||||
};
|
||||
|
||||
static inline int
|
||||
jack_server_allocate_ref_num(struct jack_server *server)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CLIENT_NUM; i++)
|
||||
if (server->client_table[i] == NULL)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
jack_server_free_ref_num(struct jack_server *server, int ref_num)
|
||||
{
|
||||
server->client_table[ref_num] = NULL;
|
||||
}
|
||||
601
src/modules/module-jack/shared.h
Normal file
601
src/modules/module-jack/shared.h
Normal file
|
|
@ -0,0 +1,601 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
extern int segment_num;
|
||||
|
||||
static inline int jack_shm_alloc(size_t size, jack_shm_info_t *info, int num)
|
||||
{
|
||||
char name[64];
|
||||
|
||||
snprintf(name, sizeof(name), "/jack_shared%d", num);
|
||||
|
||||
if (jack_shmalloc(name, size, info)) {
|
||||
pw_log_error("Cannot create shared memory segment of size = %zd (%s)", size, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (jack_attach_shm(info)) {
|
||||
jack_error("Cannot attach shared memory segment name = %s err = %s", name, strerror(errno));
|
||||
jack_destroy_shm(info);
|
||||
return -1;
|
||||
}
|
||||
info->size = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef uint16_t jack_int_t; // Internal type for ports and refnum
|
||||
|
||||
typedef enum {
|
||||
NotTriggered,
|
||||
Triggered,
|
||||
Running,
|
||||
Finished,
|
||||
} jack_client_state_t;
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_client_timing {
|
||||
jack_time_t signaled_at;
|
||||
jack_time_t awake_at;
|
||||
jack_time_t finished_at;
|
||||
jack_client_state_t status;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
#define JACK_CLIENT_TIMING_INIT (struct jack_client_timing) { 0, 0, 0, NotTriggered }
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_port {
|
||||
int type_id;
|
||||
enum JackPortFlags flags;
|
||||
char name[REAL_JACK_PORT_NAME_SIZE];
|
||||
char alias1[REAL_JACK_PORT_NAME_SIZE];
|
||||
char alias2[REAL_JACK_PORT_NAME_SIZE];
|
||||
int ref_num;
|
||||
|
||||
jack_nframes_t latency;
|
||||
jack_nframes_t total_latency;
|
||||
jack_latency_range_t playback_latency;
|
||||
jack_latency_range_t capture_latency;
|
||||
uint8_t monitor_requests;
|
||||
|
||||
bool in_use;
|
||||
jack_port_id_t tied;
|
||||
jack_default_audio_sample_t buffer[BUFFER_SIZE_MAX + 8];
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
static inline void jack_port_init(struct jack_port *port, int ref_num,
|
||||
const char* port_name, int type_id, enum JackPortFlags flags)
|
||||
{
|
||||
port->type_id = type_id;
|
||||
port->flags = flags;
|
||||
strcpy(port->name, port_name);
|
||||
port->alias1[0] = '\0';
|
||||
port->alias2[0] = '\0';
|
||||
port->ref_num = ref_num;
|
||||
port->latency = 0;
|
||||
port->total_latency = 0;
|
||||
port->playback_latency.min = port->playback_latency.max = 0;
|
||||
port->capture_latency.min = port->capture_latency.max = 0;
|
||||
port->monitor_requests = 0;
|
||||
port->in_use = true;
|
||||
port->tied = NO_PORT;
|
||||
}
|
||||
|
||||
#define MAKE_FIXED_ARRAY(size) \
|
||||
PRE_PACKED_STRUCTURE \
|
||||
struct { \
|
||||
jack_int_t table[size]; \
|
||||
uint32_t counter; \
|
||||
} POST_PACKED_STRUCTURE
|
||||
|
||||
#define INIT_FIXED_ARRAY(arr) ({ \
|
||||
int i; \
|
||||
for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) \
|
||||
arr.table[i] = EMPTY; \
|
||||
arr.counter = 0; \
|
||||
})
|
||||
|
||||
#define ADD_FIXED_ARRAY(arr,item) ({ \
|
||||
int i,ret = -1; \
|
||||
for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) { \
|
||||
if (arr.table[i] == EMPTY) { \
|
||||
arr.table[i] = item; \
|
||||
arr.counter++; \
|
||||
ret = 0; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define MAKE_FIXED_ARRAY1(size) \
|
||||
PRE_PACKED_STRUCTURE \
|
||||
struct { \
|
||||
MAKE_FIXED_ARRAY(size) array; \
|
||||
bool used; \
|
||||
} POST_PACKED_STRUCTURE
|
||||
|
||||
#define INIT_FIXED_ARRAY1(arr) ({ \
|
||||
INIT_FIXED_ARRAY(arr.array); \
|
||||
arr.used = false; \
|
||||
})
|
||||
|
||||
#define ADD_FIXED_ARRAY1(arr,item) ADD_FIXED_ARRAY(arr.array,item)
|
||||
|
||||
#define MAKE_FIXED_MATRIX(size) \
|
||||
PRE_PACKED_STRUCTURE \
|
||||
struct { \
|
||||
jack_int_t table[size][size]; \
|
||||
} POST_PACKED_STRUCTURE
|
||||
|
||||
#define INIT_FIXED_MATRIX(mat,idx) ({ \
|
||||
int i; \
|
||||
for (i = 0; i < SPA_N_ELEMENTS(mat.table[0]); i++){ \
|
||||
mat.table[idx][i] = 0; \
|
||||
mat.table[i][idx] = 0; \
|
||||
} \
|
||||
})
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_activation_count {
|
||||
int32_t value;
|
||||
int32_t count;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
static inline void jack_activation_count_set_value(struct jack_activation_count *cnt, int32_t val) {
|
||||
cnt->value = val;
|
||||
}
|
||||
|
||||
#define MAKE_LOOP_FEEDBACK(size) \
|
||||
PRE_PACKED_STRUCTURE \
|
||||
struct { \
|
||||
int table[size][3]; \
|
||||
} POST_PACKED_STRUCTURE
|
||||
|
||||
#define INIT_LOOP_FEEDBACK(arr,size) ({ \
|
||||
int i; \
|
||||
for (i = 0; i < size; i++) { \
|
||||
arr.table[i][0] = EMPTY; \
|
||||
arr.table[i][1] = EMPTY; \
|
||||
arr.table[i][2] = 0; \
|
||||
} \
|
||||
})
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_connection_manager {
|
||||
MAKE_FIXED_ARRAY(CONNECTION_NUM_FOR_PORT) connection[PORT_NUM_MAX];
|
||||
MAKE_FIXED_ARRAY1(PORT_NUM_FOR_CLIENT) input_port[CLIENT_NUM];
|
||||
MAKE_FIXED_ARRAY(PORT_NUM_FOR_CLIENT) output_port[CLIENT_NUM];
|
||||
MAKE_FIXED_MATRIX(CLIENT_NUM) connection_ref;
|
||||
struct jack_activation_count input_counter[CLIENT_NUM];
|
||||
MAKE_LOOP_FEEDBACK(CONNECTION_NUM_FOR_PORT) loop_feedback;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
static inline void
|
||||
jack_connection_manager_init_ref_num(struct jack_connection_manager *conn, int ref_num)
|
||||
{
|
||||
INIT_FIXED_ARRAY1(conn->input_port[ref_num]);
|
||||
INIT_FIXED_ARRAY(conn->output_port[ref_num]);
|
||||
INIT_FIXED_MATRIX(conn->connection_ref, ref_num);
|
||||
jack_activation_count_set_value (&conn->input_counter[ref_num], 0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
jack_connection_manager_init(struct jack_connection_manager *conn)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < PORT_NUM_MAX; i++)
|
||||
INIT_FIXED_ARRAY(conn->connection[i]);
|
||||
|
||||
INIT_LOOP_FEEDBACK(conn->loop_feedback, CONNECTION_NUM_FOR_PORT);
|
||||
|
||||
for (i = 0; i < CLIENT_NUM; i++)
|
||||
jack_connection_manager_init_ref_num(conn, i);
|
||||
}
|
||||
|
||||
static inline int
|
||||
jack_connection_manager_add_port(struct jack_connection_manager *conn, bool input,
|
||||
int ref_num, jack_port_id_t port_id)
|
||||
{
|
||||
if (input)
|
||||
return ADD_FIXED_ARRAY1(conn->input_port[ref_num], port_id);
|
||||
else
|
||||
return ADD_FIXED_ARRAY(conn->output_port[ref_num], port_id);
|
||||
}
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_atomic_counter {
|
||||
union {
|
||||
struct {
|
||||
uint16_t short_val1; // Cur
|
||||
uint16_t short_val2; // Next
|
||||
} scounter;
|
||||
uint32_t long_val;
|
||||
} info;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
#define Counter(e) (e).info.long_val
|
||||
#define CurIndex(e) (e).info.scounter.short_val1
|
||||
#define NextIndex(e) (e).info.scounter.short_val2
|
||||
|
||||
#define CurArrayIndex(e) (CurIndex(e) & 0x0001)
|
||||
#define NextArrayIndex(e) ((CurIndex(e) + 1) & 0x0001)
|
||||
|
||||
#define MAKE_ATOMIC_STATE(type) \
|
||||
PRE_PACKED_STRUCTURE \
|
||||
struct { \
|
||||
type state[2]; \
|
||||
volatile struct jack_atomic_counter counter; \
|
||||
int32_t call_write_counter; \
|
||||
} POST_PACKED_STRUCTURE
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_atomic_array_counter {
|
||||
union {
|
||||
struct {
|
||||
unsigned char byte_val[4];
|
||||
} scounter;
|
||||
uint32_t long_val;
|
||||
} info;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
#define MAKE_ATOMIC_ARRAY_STATE(type) \
|
||||
PRE_PACKED_STRUCTURE \
|
||||
struct { \
|
||||
type state[3]; \
|
||||
volatile struct jack_atomic_array_counter counter; \
|
||||
} POST_PACKED_STRUCTURE
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_graph_manager {
|
||||
jack_shm_info_t info;
|
||||
MAKE_ATOMIC_STATE(struct jack_connection_manager) state;
|
||||
unsigned int port_max;
|
||||
struct jack_client_timing client_timing[CLIENT_NUM];
|
||||
struct jack_port port_array[0];
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
static inline struct jack_graph_manager *
|
||||
jack_graph_manager_alloc(int port_max)
|
||||
{
|
||||
struct jack_graph_manager *mgr;
|
||||
jack_shm_info_t info;
|
||||
size_t i, size;
|
||||
|
||||
size = sizeof(struct jack_graph_manager) + port_max * sizeof(struct jack_port);
|
||||
|
||||
if (jack_shm_alloc(size, &info, segment_num++) < 0)
|
||||
return NULL;
|
||||
|
||||
mgr = (struct jack_graph_manager *)jack_shm_addr(&info);
|
||||
mgr->info = info;
|
||||
|
||||
jack_connection_manager_init(&mgr->state.state[0]);
|
||||
jack_connection_manager_init(&mgr->state.state[1]);
|
||||
mgr->port_max = port_max;
|
||||
|
||||
for (i = 0; i < port_max; i++) {
|
||||
mgr->port_array[i].in_use = false;
|
||||
mgr->port_array[i].ref_num = -1;
|
||||
}
|
||||
return mgr;
|
||||
}
|
||||
|
||||
static inline jack_port_id_t
|
||||
jack_graph_manager_allocate_port(struct jack_graph_manager *mgr,
|
||||
int ref_num, const char* port_name, int type_id,
|
||||
enum JackPortFlags flags)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < mgr->port_max; i++) {
|
||||
if (!mgr->port_array[i].in_use) {
|
||||
jack_port_init(&mgr->port_array[i], ref_num, port_name, type_id, flags);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return NO_PORT;
|
||||
}
|
||||
|
||||
|
||||
static inline struct jack_connection_manager *
|
||||
jack_graph_manager_next_start(struct jack_graph_manager *manager)
|
||||
{
|
||||
uint32_t next_index;
|
||||
|
||||
if (manager->state.call_write_counter++ == 0) {
|
||||
struct jack_atomic_counter old_val;
|
||||
struct jack_atomic_counter new_val;
|
||||
uint32_t cur_index;
|
||||
bool need_copy;
|
||||
do {
|
||||
old_val = manager->state.counter;
|
||||
new_val = old_val;
|
||||
cur_index = CurArrayIndex(new_val);
|
||||
next_index = NextArrayIndex(new_val);
|
||||
need_copy = (CurIndex(new_val) == NextIndex(new_val));
|
||||
NextIndex(new_val) = CurIndex(new_val); // Invalidate next index
|
||||
}
|
||||
while (!__atomic_compare_exchange_n((uint32_t*)&manager->state.counter,
|
||||
(uint32_t*)&Counter(old_val),
|
||||
Counter(new_val),
|
||||
false,
|
||||
__ATOMIC_SEQ_CST,
|
||||
__ATOMIC_SEQ_CST));
|
||||
|
||||
if (need_copy)
|
||||
memcpy(&manager->state.state[next_index],
|
||||
&manager->state.state[cur_index],
|
||||
sizeof(struct jack_connection_manager));
|
||||
}
|
||||
else {
|
||||
next_index = NextArrayIndex(manager->state.counter);
|
||||
}
|
||||
return &manager->state.state[next_index];
|
||||
}
|
||||
|
||||
static inline void
|
||||
jack_graph_manager_next_stop(struct jack_graph_manager *manager)
|
||||
{
|
||||
if (--manager->state.call_write_counter == 0) {
|
||||
struct jack_atomic_counter old_val;
|
||||
struct jack_atomic_counter new_val;
|
||||
do {
|
||||
old_val = manager->state.counter;
|
||||
new_val = old_val;
|
||||
NextIndex(new_val)++; // Set next index
|
||||
}
|
||||
while (!__atomic_compare_exchange_n((uint32_t*)&manager->state.counter,
|
||||
(uint32_t*)&Counter(old_val),
|
||||
Counter(new_val),
|
||||
false,
|
||||
__ATOMIC_SEQ_CST,
|
||||
__ATOMIC_SEQ_CST));
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
TransportCommandNone = 0,
|
||||
TransportCommandStart = 1,
|
||||
TransportCommandStop = 2,
|
||||
} transport_command_t;
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_transport_engine {
|
||||
MAKE_ATOMIC_ARRAY_STATE(jack_position_t) state;
|
||||
jack_transport_state_t transport_state;
|
||||
volatile transport_command_t transport_cmd;
|
||||
transport_command_t previous_cmd; /* previous transport_cmd */
|
||||
jack_time_t sync_timeout;
|
||||
int sync_time_left;
|
||||
int time_base_master;
|
||||
bool pending_pos;
|
||||
bool network_sync;
|
||||
bool conditionnal;
|
||||
int32_t write_counter;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_timer {
|
||||
jack_nframes_t frames;
|
||||
jack_time_t current_wakeup;
|
||||
jack_time_t current_callback;
|
||||
jack_time_t next_wakeup;
|
||||
float period_usecs;
|
||||
float filter_omega; /* set once, never altered */
|
||||
bool initialized;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_frame_timer {
|
||||
MAKE_ATOMIC_STATE(struct jack_timer) state;
|
||||
bool first_wakeup;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
#ifdef JACK_MONITOR
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_timing_measure_client {
|
||||
int ref_num;
|
||||
jack_time_t signaled_at;
|
||||
jack_time_t awake_at;
|
||||
jack_time_t finished_at;
|
||||
jack_client_state_t status;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_timing_client_interval {
|
||||
int ref_num;
|
||||
char name[JACK_CLIENT_NAME_SIZE+1];
|
||||
int begin_interval;
|
||||
int end_interval;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_timing_measure {
|
||||
unsigned int audio_cycle;
|
||||
jack_time_t period_usecs;
|
||||
jack_time_t cur_cycle_begin;
|
||||
jack_time_t prev_cycle_end;
|
||||
struct jack_timing_measure_client client_table[CLIENT_NUM];
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_engine_profiling {
|
||||
struct jack_timing_measure profile_table[TIME_POINTS];
|
||||
struct jack_timing_client_interval interval_table[MEASURED_CLIENTS];
|
||||
|
||||
unsigned int audio_cycle;
|
||||
unsigned int measured_client;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
#endif
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_engine_control {
|
||||
jack_shm_info_t info;
|
||||
jack_nframes_t buffer_size;
|
||||
jack_nframes_t sample_rate;
|
||||
bool sync_mode;
|
||||
bool temporary;
|
||||
jack_time_t period_usecs;
|
||||
jack_time_t timeout_usecs;
|
||||
float max_delayed_usecs;
|
||||
float xrun_delayed_usecs;
|
||||
bool timeout;
|
||||
bool real_time;
|
||||
bool saved_real_time;
|
||||
int server_priority;
|
||||
int client_priority;
|
||||
int max_client_priority;
|
||||
char server_name[JACK_SERVER_NAME_SIZE];
|
||||
struct jack_transport_engine transport;
|
||||
jack_timer_type_t clock_source;
|
||||
int driver_num;
|
||||
bool verbose;
|
||||
|
||||
jack_time_t prev_cycle_time;
|
||||
jack_time_t cur_cycle_time;
|
||||
jack_time_t spare_usecs;
|
||||
jack_time_t max_usecs;
|
||||
jack_time_t rolling_client_usecs[JACK_ENGINE_ROLLING_COUNT];
|
||||
unsigned int rolling_client_usecs_cnt;
|
||||
int rolling_client_usecs_index;
|
||||
int rolling_interval;
|
||||
float CPU_load;
|
||||
|
||||
uint64_t period;
|
||||
uint64_t computation;
|
||||
uint64_t constraint;
|
||||
|
||||
struct jack_frame_timer frame_timer;
|
||||
|
||||
#ifdef JACK_MONITOR
|
||||
struct jack_engine_profiling profiler;
|
||||
#endif
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
static inline void
|
||||
jack_engine_control_reset_rolling_usecs(struct jack_engine_control *ctrl)
|
||||
{
|
||||
memset(ctrl->rolling_client_usecs, 0, sizeof(ctrl->rolling_client_usecs));
|
||||
ctrl->rolling_client_usecs_index = 0;
|
||||
ctrl->rolling_client_usecs_cnt = 0;
|
||||
ctrl->spare_usecs = 0;
|
||||
ctrl->rolling_interval = floor((JACK_ENGINE_ROLLING_INTERVAL * 1000.f) / ctrl->period_usecs);
|
||||
}
|
||||
|
||||
static inline struct jack_engine_control *
|
||||
jack_engine_control_alloc(const char* name)
|
||||
{
|
||||
struct jack_engine_control *ctrl;
|
||||
jack_shm_info_t info;
|
||||
size_t size;
|
||||
|
||||
size = sizeof(struct jack_engine_control);
|
||||
if (jack_shm_alloc(size, &info, segment_num++) < 0)
|
||||
return NULL;
|
||||
|
||||
ctrl = (struct jack_engine_control *)jack_shm_addr(&info);
|
||||
ctrl->info = info;
|
||||
|
||||
ctrl->buffer_size = 512;
|
||||
ctrl->sample_rate = 48000;
|
||||
ctrl->sync_mode = false;
|
||||
ctrl->temporary = false;
|
||||
ctrl->period_usecs = 1000000.f / ctrl->sample_rate * ctrl->buffer_size;
|
||||
ctrl->timeout_usecs = 0;
|
||||
ctrl->max_delayed_usecs = 0.f;
|
||||
ctrl->xrun_delayed_usecs = 0.f;
|
||||
ctrl->timeout = false;
|
||||
ctrl->real_time = true;
|
||||
ctrl->saved_real_time = false;
|
||||
ctrl->server_priority = 20;
|
||||
ctrl->client_priority = 15;
|
||||
ctrl->max_client_priority = 19;
|
||||
strcpy(ctrl->server_name, name);
|
||||
ctrl->clock_source = 0;
|
||||
ctrl->driver_num = 0;
|
||||
ctrl->verbose = true;
|
||||
|
||||
ctrl->prev_cycle_time = 0;
|
||||
ctrl->cur_cycle_time = 0;
|
||||
ctrl->spare_usecs = 0;
|
||||
ctrl->max_usecs = 0;
|
||||
jack_engine_control_reset_rolling_usecs(ctrl);
|
||||
ctrl->CPU_load = 0.f;
|
||||
|
||||
ctrl->period = 0;
|
||||
ctrl->computation = 0;
|
||||
ctrl->constraint = 0;
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct jack_client_control {
|
||||
jack_shm_info_t info;
|
||||
char name[JACK_CLIENT_NAME_SIZE+1];
|
||||
bool callback[jack_notify_max];
|
||||
volatile jack_transport_state_t transport_state;
|
||||
volatile bool transport_sync;
|
||||
volatile bool transport_timebase;
|
||||
int ref_num;
|
||||
int PID;
|
||||
bool active;
|
||||
|
||||
int session_ID;
|
||||
char session_command[JACK_SESSION_COMMAND_SIZE];
|
||||
jack_session_flags_t session_flags;
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
static inline struct jack_client_control *
|
||||
jack_client_control_alloc(const char* name, int pid, int ref_num, int uuid)
|
||||
{
|
||||
struct jack_client_control *ctrl;
|
||||
jack_shm_info_t info;
|
||||
size_t size;
|
||||
|
||||
size = sizeof(struct jack_client_control);
|
||||
if (jack_shm_alloc(size, &info, segment_num++) < 0)
|
||||
return NULL;
|
||||
|
||||
ctrl = (struct jack_client_control *)jack_shm_addr(&info);
|
||||
ctrl->info = info;
|
||||
|
||||
strcpy(ctrl->name, name);
|
||||
for (int i = 0; i < jack_notify_max; i++)
|
||||
ctrl->callback[i] = false;
|
||||
|
||||
// Always activated
|
||||
ctrl->callback[jack_notify_AddClient] = true;
|
||||
ctrl->callback[jack_notify_RemoveClient] = true;
|
||||
ctrl->callback[jack_notify_ActivateClient] = true;
|
||||
ctrl->callback[jack_notify_LatencyCallback] = true;
|
||||
// So that driver synchro are correctly setup in "flush" or "normal" mode
|
||||
ctrl->callback[jack_notify_StartFreewheelCallback] = true;
|
||||
ctrl->callback[jack_notify_StopFreewheelCallback] = true;
|
||||
ctrl->ref_num = ref_num;
|
||||
ctrl->PID = pid;
|
||||
ctrl->transport_state = JackTransportStopped;
|
||||
ctrl->transport_sync = false;
|
||||
ctrl->transport_timebase = false;
|
||||
ctrl->active = false;
|
||||
ctrl->session_ID = uuid;
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
1303
src/modules/module-jack/shm.c
Normal file
1303
src/modules/module-jack/shm.c
Normal file
File diff suppressed because it is too large
Load diff
216
src/modules/module-jack/shm.h
Normal file
216
src/modules/module-jack/shm.h
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
/* This module provides a set of abstract shared memory interfaces
|
||||
* with support using both System V and POSIX shared memory
|
||||
* implementations. The code is divided into three sections:
|
||||
*
|
||||
* - common (interface-independent) code
|
||||
* - POSIX implementation
|
||||
* - System V implementation
|
||||
* - Windows implementation
|
||||
*
|
||||
* The implementation used is determined by whether USE_POSIX_SHM was
|
||||
* set in the ./configure step.
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (C) 2001-2003 Paul Davis
|
||||
Copyright (C) 2005-2012 Grame
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __jack_shm_h__
|
||||
#define __jack_shm_h__
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <jack/types.h>
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define MAX_SERVERS 8 /* maximum concurrent servers */
|
||||
#define MAX_SHM_ID 256 /* generally about 16 per server */
|
||||
#define JACK_SHM_MAGIC 0x4a41434b /* shm magic number: "JACK" */
|
||||
#define JACK_SHM_NULL_INDEX -1 /* NULL SHM index */
|
||||
#define JACK_SHM_REGISTRY_INDEX -2 /* pseudo SHM index for registry */
|
||||
|
||||
|
||||
/* On Mac OS X, SHM_NAME_MAX is the maximum length of a shared memory
|
||||
* segment name (instead of NAME_MAX or PATH_MAX as defined by the
|
||||
* standard).
|
||||
*/
|
||||
#ifdef USE_POSIX_SHM
|
||||
|
||||
#ifndef NAME_MAX
|
||||
#define NAME_MAX 255
|
||||
#endif
|
||||
|
||||
#ifndef SHM_NAME_MAX
|
||||
#define SHM_NAME_MAX NAME_MAX
|
||||
#endif
|
||||
typedef char shm_name_t[SHM_NAME_MAX];
|
||||
typedef shm_name_t jack_shm_id_t;
|
||||
|
||||
#elif WIN32
|
||||
#define NAME_MAX 255
|
||||
#ifndef SHM_NAME_MAX
|
||||
#define SHM_NAME_MAX NAME_MAX
|
||||
#endif
|
||||
typedef char shm_name_t[SHM_NAME_MAX];
|
||||
typedef shm_name_t jack_shm_id_t;
|
||||
|
||||
#elif __ANDROID__
|
||||
|
||||
#ifndef NAME_MAX
|
||||
#define NAME_MAX 255
|
||||
#endif
|
||||
|
||||
#ifndef SHM_NAME_MAX
|
||||
#define SHM_NAME_MAX NAME_MAX
|
||||
#endif
|
||||
typedef char shm_name_t[SHM_NAME_MAX];
|
||||
typedef shm_name_t jack_shm_id_t;
|
||||
typedef int jack_shm_fd_t;
|
||||
|
||||
#else
|
||||
/* System V SHM */
|
||||
typedef int jack_shm_id_t;
|
||||
#endif /* SHM type */
|
||||
|
||||
/* shared memory type */
|
||||
typedef enum {
|
||||
shm_POSIX = 1, /* POSIX shared memory */
|
||||
shm_SYSV = 2, /* System V shared memory */
|
||||
shm_WIN32 = 3, /* Windows 32 shared memory */
|
||||
shm_ANDROID = 4 /* Android shared memory */
|
||||
} jack_shmtype_t;
|
||||
|
||||
typedef int16_t jack_shm_registry_index_t;
|
||||
|
||||
/**
|
||||
* A structure holding information about shared memory allocated by
|
||||
* JACK. this persists across invocations of JACK, and can be used by
|
||||
* multiple JACK servers. It contains no pointers and is valid across
|
||||
* address spaces.
|
||||
*
|
||||
* The registry consists of two parts: a header including an array of
|
||||
* server names, followed by an array of segment registry entries.
|
||||
*/
|
||||
typedef struct _jack_shm_server {
|
||||
#ifdef WIN32
|
||||
int pid; /* process ID */
|
||||
#else
|
||||
pid_t pid; /* process ID */
|
||||
#endif
|
||||
|
||||
char name[JACK_SERVER_NAME_SIZE];
|
||||
}
|
||||
jack_shm_server_t;
|
||||
|
||||
typedef struct _jack_shm_header {
|
||||
uint32_t magic; /* magic number */
|
||||
uint16_t protocol; /* JACK protocol version */
|
||||
jack_shmtype_t type; /* shm type */
|
||||
jack_shmsize_t size; /* total registry segment size */
|
||||
jack_shmsize_t hdr_len; /* size of header */
|
||||
jack_shmsize_t entry_len; /* size of registry entry */
|
||||
jack_shm_server_t server[MAX_SERVERS]; /* current server array */
|
||||
}
|
||||
jack_shm_header_t;
|
||||
|
||||
typedef struct _jack_shm_registry {
|
||||
jack_shm_registry_index_t index; /* offset into the registry */
|
||||
|
||||
#ifdef WIN32
|
||||
int allocator; /* PID that created shm segment */
|
||||
#else
|
||||
pid_t allocator; /* PID that created shm segment */
|
||||
#endif
|
||||
|
||||
jack_shmsize_t size; /* for POSIX unattach */
|
||||
jack_shm_id_t id; /* API specific, see above */
|
||||
#ifdef __ANDROID__
|
||||
jack_shm_fd_t fd;
|
||||
#endif
|
||||
}
|
||||
jack_shm_registry_t;
|
||||
|
||||
#define JACK_SHM_REGISTRY_SIZE (sizeof (jack_shm_header_t) \
|
||||
+ sizeof (jack_shm_registry_t) * MAX_SHM_ID)
|
||||
|
||||
/**
|
||||
* a structure holding information about shared memory
|
||||
* allocated by JACK. this version is valid only
|
||||
* for a given address space. It contains a pointer
|
||||
* indicating where the shared memory has been
|
||||
* attached to the address space.
|
||||
*/
|
||||
|
||||
PRE_PACKED_STRUCTURE
|
||||
struct _jack_shm_info {
|
||||
jack_shm_registry_index_t index; /* offset into the registry */
|
||||
uint32_t size;
|
||||
#ifdef __ANDROID__
|
||||
jack_shm_fd_t fd;
|
||||
#endif
|
||||
union {
|
||||
void *attached_at; /* address where attached */
|
||||
char ptr_size[8];
|
||||
} ptr; /* a "pointer" that has the same 8 bytes size when compling in 32 or 64 bits */
|
||||
} POST_PACKED_STRUCTURE;
|
||||
|
||||
typedef struct _jack_shm_info jack_shm_info_t;
|
||||
|
||||
/* utility functions used only within JACK */
|
||||
|
||||
void jack_shm_copy_from_registry (jack_shm_info_t*,
|
||||
jack_shm_registry_index_t);
|
||||
void jack_shm_copy_to_registry (jack_shm_info_t*,
|
||||
jack_shm_registry_index_t*);
|
||||
int jack_release_shm_info (jack_shm_registry_index_t);
|
||||
char* jack_shm_addr (jack_shm_info_t* si);
|
||||
|
||||
// here begin the API
|
||||
int jack_register_server (const char *server_name, int new_registry);
|
||||
int jack_unregister_server (const char *server_name);
|
||||
|
||||
int jack_initialize_shm (const char *server_name);
|
||||
int jack_initialize_shm_server (void);
|
||||
int jack_initialize_shm_client (void);
|
||||
int jack_cleanup_shm (void);
|
||||
|
||||
int jack_shmalloc (const char *shm_name, jack_shmsize_t size,
|
||||
jack_shm_info_t* result);
|
||||
void jack_release_shm (jack_shm_info_t*);
|
||||
void jack_release_lib_shm (jack_shm_info_t*);
|
||||
void jack_destroy_shm (jack_shm_info_t*);
|
||||
int jack_attach_shm (jack_shm_info_t*);
|
||||
int jack_attach_lib_shm (jack_shm_info_t*);
|
||||
int jack_attach_shm_read (jack_shm_info_t*);
|
||||
int jack_attach_lib_shm_read (jack_shm_info_t*);
|
||||
int jack_resize_shm (jack_shm_info_t*, jack_shmsize_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __jack_shm_h__ */
|
||||
48
src/modules/module-jack/synchro.h
Normal file
48
src/modules/module-jack/synchro.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
struct jack_synchro {
|
||||
char name[SYNC_MAX_NAME_SIZE];
|
||||
bool flush;
|
||||
sem_t *semaphore;
|
||||
};
|
||||
|
||||
#define JACK_SYNCHRO_INIT (struct jack_synchro) { { 0, }, false, NULL }
|
||||
|
||||
static inline int
|
||||
jack_synchro_init(struct jack_synchro *synchro,
|
||||
const char *client_name,
|
||||
const char *server_name,
|
||||
int value, bool internal,
|
||||
bool promiscuous)
|
||||
{
|
||||
if (promiscuous)
|
||||
snprintf(synchro->name, sizeof(synchro->name),
|
||||
"jack_sem.%s_%s", server_name, client_name);
|
||||
else
|
||||
snprintf(synchro->name, sizeof(synchro->name),
|
||||
"jack_sem.%d_%s_%s", getuid(), server_name, client_name);
|
||||
|
||||
synchro->flush = false;
|
||||
if ((synchro->semaphore = sem_open(synchro->name, O_CREAT | O_RDWR, 0777, value)) == (sem_t*)SEM_FAILED) {
|
||||
pw_log_error("can't check in named semaphore name = %s err = %s", synchro->name, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
183
src/modules/module-mixer.c
Normal file
183
src/modules/module-mixer.c
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2017 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 <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "pipewire/core.h"
|
||||
#include "pipewire/module.h"
|
||||
#include "modules/spa/spa-node.h"
|
||||
|
||||
#define AUDIOMIXER_LIB "audiomixer/libspa-audiomixer"
|
||||
|
||||
struct impl {
|
||||
struct pw_core *core;
|
||||
struct pw_properties *properties;
|
||||
|
||||
void *hnd;
|
||||
const struct spa_handle_factory *factory;
|
||||
};
|
||||
|
||||
static const struct spa_handle_factory *find_factory(struct impl *impl)
|
||||
{
|
||||
spa_handle_factory_enum_func_t enum_func;
|
||||
uint32_t index;
|
||||
const struct spa_handle_factory *factory = NULL;
|
||||
int res;
|
||||
char *filename;
|
||||
const char *dir;
|
||||
|
||||
if ((dir = getenv("SPA_PLUGIN_DIR")) == NULL)
|
||||
dir = PLUGINDIR;
|
||||
|
||||
asprintf(&filename, "%s/%s.so", dir, AUDIOMIXER_LIB);
|
||||
|
||||
if ((impl->hnd = dlopen(filename, RTLD_NOW)) == NULL) {
|
||||
pw_log_error("can't load %s: %s", AUDIOMIXER_LIB, dlerror());
|
||||
goto open_failed;
|
||||
}
|
||||
if ((enum_func = dlsym(impl->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, "audiomixer") == 0)
|
||||
break;
|
||||
}
|
||||
free(filename);
|
||||
return factory;
|
||||
|
||||
enum_failed:
|
||||
no_symbol:
|
||||
dlclose(impl->hnd);
|
||||
impl->hnd = NULL;
|
||||
open_failed:
|
||||
free(filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct pw_node *make_node(struct impl *impl)
|
||||
{
|
||||
struct spa_handle *handle;
|
||||
int res;
|
||||
void *iface;
|
||||
struct spa_node *spa_node;
|
||||
struct spa_clock *spa_clock;
|
||||
struct pw_node *node;
|
||||
|
||||
handle = calloc(1, impl->factory->size);
|
||||
if ((res = spa_handle_factory_init(impl->factory,
|
||||
handle,
|
||||
NULL, impl->core->support, impl->core->n_support)) < 0) {
|
||||
pw_log_error("can't make factory instance: %d", res);
|
||||
goto init_failed;
|
||||
}
|
||||
if ((res = spa_handle_get_interface(handle, impl->core->type.spa_node, &iface)) < 0) {
|
||||
pw_log_error("can't get interface %d", res);
|
||||
goto interface_failed;
|
||||
}
|
||||
spa_node = iface;
|
||||
|
||||
if ((res = spa_handle_get_interface(handle, impl->core->type.spa_clock, &iface)) < 0) {
|
||||
iface = NULL;
|
||||
}
|
||||
spa_clock = iface;
|
||||
|
||||
node = pw_spa_node_new(impl->core, NULL, "audiomixer", false, spa_node, spa_clock, NULL);
|
||||
|
||||
return node;
|
||||
|
||||
interface_failed:
|
||||
spa_handle_clear(handle);
|
||||
init_failed:
|
||||
free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct impl *module_new(struct pw_core *core, struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_node *n;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
pw_log_debug("module %p: new", impl);
|
||||
|
||||
impl->core = core;
|
||||
impl->properties = properties;
|
||||
|
||||
impl->factory = find_factory(impl);
|
||||
|
||||
spa_list_for_each(n, &core->node_list, link) {
|
||||
const char *str;
|
||||
char *error;
|
||||
struct pw_node *node;
|
||||
struct pw_port *ip, *op;
|
||||
|
||||
if (n->global == NULL)
|
||||
continue;
|
||||
|
||||
if (n->properties == NULL)
|
||||
continue;
|
||||
|
||||
if ((str = pw_properties_get(n->properties, "media.class")) == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp(str, "Audio/Sink") != 0)
|
||||
continue;
|
||||
|
||||
if ((ip = pw_node_get_free_port(n, PW_DIRECTION_INPUT)) == NULL)
|
||||
continue;
|
||||
|
||||
node = make_node(impl);
|
||||
op = pw_node_get_free_port(node, PW_DIRECTION_OUTPUT);
|
||||
if (op == NULL)
|
||||
continue;
|
||||
|
||||
n->idle_used_input_links++;
|
||||
node->idle_used_output_links++;
|
||||
|
||||
pw_link_new(core, op, ip, NULL, NULL, &error);
|
||||
}
|
||||
return impl;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void module_destroy(struct impl *impl)
|
||||
{
|
||||
pw_log_debug("module %p: destroy", impl);
|
||||
|
||||
free(impl);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
module_new(module->core, NULL);
|
||||
return true;
|
||||
}
|
||||
632
src/modules/module-protocol-dbus.c
Normal file
632
src/modules/module-protocol-dbus.c
Normal file
|
|
@ -0,0 +1,632 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gunixfdlist.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "pipewire/client/pipewire.h"
|
||||
#include "pipewire/client/log.h"
|
||||
|
||||
#include "pipewire/server/core.h"
|
||||
#include "pipewire/server/node.h"
|
||||
#include "pipewire/server/module.h"
|
||||
#include "pipewire/server/client-node.h"
|
||||
#include "pipewire/server/client.h"
|
||||
#include "pipewire/server/resource.h"
|
||||
#include "pipewire/server/link.h"
|
||||
#include "pipewire/server/node-factory.h"
|
||||
#include "pipewire/server/data-loop.h"
|
||||
#include "pipewire/server/main-loop.h"
|
||||
|
||||
#include "pipewire/dbus/org-pipewire.h"
|
||||
|
||||
#define PIPEWIRE_DBUS_SERVICE "org.pipewire"
|
||||
#define PIPEWIRE_DBUS_OBJECT_PREFIX "/org/pipewire"
|
||||
#define PIPEWIRE_DBUS_OBJECT_SERVER PIPEWIRE_DBUS_OBJECT_PREFIX "/server"
|
||||
#define PIPEWIRE_DBUS_OBJECT_CLIENT PIPEWIRE_DBUS_OBJECT_PREFIX "/client"
|
||||
#define PIPEWIRE_DBUS_OBJECT_NODE PIPEWIRE_DBUS_OBJECT_PREFIX "/node"
|
||||
#define PIPEWIRE_DBUS_OBJECT_LINK PIPEWIRE_DBUS_OBJECT_PREFIX "/link"
|
||||
|
||||
struct impl {
|
||||
struct pw_core *core;
|
||||
struct spa_list link;
|
||||
|
||||
struct pw_properties *properties;
|
||||
|
||||
GDBusConnection *connection;
|
||||
GDBusObjectManagerServer *server_manager;
|
||||
|
||||
struct spa_list client_list;
|
||||
struct spa_list object_list;
|
||||
|
||||
struct pw_listener global_added;
|
||||
struct pw_listener global_removed;
|
||||
};
|
||||
|
||||
struct object {
|
||||
struct impl *impl;
|
||||
struct spa_list link;
|
||||
struct pw_global *global;
|
||||
void *iface;
|
||||
PipeWireObjectSkeleton *skel;
|
||||
const gchar *object_path;
|
||||
pw_destroy_t destroy;
|
||||
};
|
||||
|
||||
struct server {
|
||||
struct object parent;
|
||||
struct spa_list link;
|
||||
guint id;
|
||||
};
|
||||
|
||||
struct client {
|
||||
struct object parent;
|
||||
struct spa_list link;
|
||||
gchar *sender;
|
||||
guint id;
|
||||
};
|
||||
|
||||
struct node {
|
||||
struct object parent;
|
||||
struct pw_listener state_changed;
|
||||
};
|
||||
|
||||
static void object_export(struct object *this)
|
||||
{
|
||||
g_dbus_object_manager_server_export(this->impl->server_manager,
|
||||
G_DBUS_OBJECT_SKELETON(this->skel));
|
||||
this->object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(this->skel));
|
||||
pw_log_debug("protocol-dbus %p: export object %s", this->impl, this->object_path);
|
||||
}
|
||||
|
||||
static void object_unexport(struct object *this)
|
||||
{
|
||||
if (this->object_path)
|
||||
g_dbus_object_manager_server_unexport(this->impl->server_manager,
|
||||
this->object_path);
|
||||
}
|
||||
|
||||
static void *object_new(size_t size,
|
||||
struct impl *impl,
|
||||
struct pw_global *global,
|
||||
void *iface,
|
||||
PipeWireObjectSkeleton * skel, bool export, pw_destroy_t destroy)
|
||||
{
|
||||
struct object *this;
|
||||
|
||||
this = calloc(1, size);
|
||||
this->impl = impl;
|
||||
this->global = global;
|
||||
this->iface = iface;
|
||||
this->skel = skel;
|
||||
this->destroy = destroy;
|
||||
|
||||
spa_list_insert(impl->object_list.prev, &this->link);
|
||||
|
||||
if (export)
|
||||
object_export(this);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
static void object_destroy(struct object *this)
|
||||
{
|
||||
spa_list_remove(&this->link);
|
||||
|
||||
if (this->destroy)
|
||||
this->destroy(this);
|
||||
|
||||
object_unexport(this);
|
||||
g_clear_object(&this->iface);
|
||||
g_clear_object(&this->skel);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static struct object *find_object(struct impl *impl, void *object)
|
||||
{
|
||||
struct object *obj;
|
||||
spa_list_for_each(obj, &impl->object_list, link) {
|
||||
if (obj->global->object == object)
|
||||
return obj;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
struct _struct pw_properties {
|
||||
GHashTable *hashtable;
|
||||
};
|
||||
|
||||
static void add_to_variant(const gchar * key, const gchar * value, GVariantBuilder * b)
|
||||
{
|
||||
g_variant_builder_add(b, "{sv}", key, g_variant_new_string(value));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void pw_properties_init_builder(struct pw_properties *properties, GVariantBuilder * builder)
|
||||
{
|
||||
g_variant_builder_init(builder, G_VARIANT_TYPE("a{sv}"));
|
||||
// g_hash_table_foreach (properties->hashtable, (GHFunc) add_to_variant, builder);
|
||||
}
|
||||
|
||||
static GVariant *pw_properties_to_variant(struct pw_properties *properties)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
pw_properties_init_builder(properties, &builder);
|
||||
return g_variant_builder_end(&builder);
|
||||
}
|
||||
|
||||
static struct pw_properties *pw_properties_from_variant(GVariant * variant)
|
||||
{
|
||||
struct pw_properties *props;
|
||||
GVariantIter iter;
|
||||
//GVariant *value;
|
||||
//gchar *key;
|
||||
|
||||
props = pw_properties_new(NULL, NULL);
|
||||
|
||||
g_variant_iter_init(&iter, variant);
|
||||
// while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
|
||||
//g_hash_table_replace (props->hashtable,
|
||||
// g_strdup (key),
|
||||
// g_variant_dup_string (value, NULL));
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
static void
|
||||
client_name_appeared_handler(GDBusConnection * connection,
|
||||
const gchar * name, const gchar * name_owner, gpointer user_data)
|
||||
{
|
||||
struct client *this = user_data;
|
||||
pw_log_debug("client %p: appeared %s %s", this, name, name_owner);
|
||||
object_export(&this->parent);
|
||||
}
|
||||
|
||||
static void client_destroy(struct client *this)
|
||||
{
|
||||
if (this->sender) {
|
||||
spa_list_remove(&this->link);
|
||||
free(this->sender);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
client_name_vanished_handler(GDBusConnection * connection, const gchar * name, gpointer user_data)
|
||||
{
|
||||
struct client *this = user_data;
|
||||
pw_log_debug("client %p: vanished %s", this, name);
|
||||
g_bus_unwatch_name(this->id);
|
||||
/* destroying the client here will trigger the global_removed, which
|
||||
* will then destroy our wrapper */
|
||||
pw_client_destroy(this->parent.global->object);
|
||||
}
|
||||
|
||||
|
||||
static struct client *client_new(struct impl *impl, const char *sender)
|
||||
{
|
||||
struct client *this;
|
||||
struct pw_client *client;
|
||||
|
||||
client = pw_client_new(impl->core, NULL, NULL);
|
||||
|
||||
if ((this = (struct client *) find_object(impl, client))) {
|
||||
pipewire_client1_set_sender(this->parent.iface, sender);
|
||||
|
||||
this->sender = strdup(sender);
|
||||
this->id = g_bus_watch_name_on_connection(impl->connection,
|
||||
this->sender,
|
||||
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||
client_name_appeared_handler,
|
||||
client_name_vanished_handler, this, NULL);
|
||||
|
||||
spa_list_insert(impl->client_list.prev, &this->link);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
static struct pw_client *sender_get_client(struct impl *impl, const char *sender, bool create)
|
||||
{
|
||||
struct client *client;
|
||||
|
||||
spa_list_for_each(client, &impl->client_list, link) {
|
||||
if (strcmp(client->sender, sender) == 0)
|
||||
return client->parent.global->object;
|
||||
}
|
||||
if (!create)
|
||||
return NULL;
|
||||
|
||||
client = client_new(impl, sender);
|
||||
|
||||
return client->parent.global->object;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_create_node(PipeWireDaemon1 * interface,
|
||||
GDBusMethodInvocation * invocation,
|
||||
const char *arg_factory_name,
|
||||
const char *arg_name, GVariant * arg_properties, gpointer user_data)
|
||||
{
|
||||
struct impl *impl = user_data;
|
||||
struct pw_node_factory *factory;
|
||||
struct pw_node *node;
|
||||
struct pw_client *client;
|
||||
const char *sender, *object_path;
|
||||
struct pw_properties *props;
|
||||
struct object *object;
|
||||
|
||||
sender = g_dbus_method_invocation_get_sender(invocation);
|
||||
client = sender_get_client(impl, sender, TRUE);
|
||||
|
||||
pw_log_debug("protocol-dbus %p: create node: %s", impl, sender);
|
||||
|
||||
props = pw_properties_from_variant(arg_properties);
|
||||
|
||||
factory = pw_core_find_node_factory(impl->core, arg_factory_name);
|
||||
if (factory == NULL)
|
||||
goto no_factory;
|
||||
|
||||
node = pw_node_factory_create_node(factory, client, arg_name, props);
|
||||
pw_properties_free(props);
|
||||
|
||||
if (node == NULL)
|
||||
goto no_node;
|
||||
|
||||
object = find_object(impl, node);
|
||||
if (object == NULL)
|
||||
goto object_failed;
|
||||
|
||||
pw_resource_new(client,
|
||||
SPA_ID_INVALID,
|
||||
impl->core->type.node, node, (pw_destroy_t) pw_node_destroy);
|
||||
|
||||
object_path = object->object_path;
|
||||
pw_log_debug("protocol-dbus %p: added node %p with path %s", impl, node, object_path);
|
||||
g_dbus_method_invocation_return_value(invocation, g_variant_new("(o)", object_path));
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
no_factory:
|
||||
pw_log_debug("protocol-dbus %p: could not find factory named %s", impl,
|
||||
arg_factory_name);
|
||||
g_dbus_method_invocation_return_dbus_error(invocation, "org.pipewire.Error",
|
||||
"can't find factory");
|
||||
return TRUE;
|
||||
no_node:
|
||||
pw_log_debug("protocol-dbus %p: could not create node named %s from factory %s",
|
||||
impl, arg_name, arg_factory_name);
|
||||
g_dbus_method_invocation_return_dbus_error(invocation, "org.pipewire.Error",
|
||||
"can't create node");
|
||||
return TRUE;
|
||||
object_failed:
|
||||
pw_log_debug("protocol-dbus %p: could not create dbus object", impl);
|
||||
g_dbus_method_invocation_return_dbus_error(invocation,
|
||||
"org.pipewire.Error",
|
||||
"can't create object");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_node_state_changed(struct pw_listener *listener,
|
||||
struct pw_node *node, enum pw_node_state old, enum pw_node_state state)
|
||||
{
|
||||
struct node *object = SPA_CONTAINER_OF(listener, struct node, state_changed);
|
||||
|
||||
pw_log_debug("protocol-dbus %p: node %p state change %s -> %s", object->parent.impl, node,
|
||||
pw_node_state_as_string(old), pw_node_state_as_string(state));
|
||||
|
||||
pipewire_node1_set_state(object->parent.iface, node->state);
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_create_client_node(PipeWireDaemon1 * interface,
|
||||
GDBusMethodInvocation * invocation,
|
||||
const char *arg_name, GVariant * arg_properties, gpointer user_data)
|
||||
{
|
||||
struct impl *impl = user_data;
|
||||
struct pw_client_node *node;
|
||||
struct pw_client *client;
|
||||
int res;
|
||||
const char *sender, *object_path, *target_node;
|
||||
struct pw_properties *props;
|
||||
GError *error = NULL;
|
||||
GUnixFDList *fdlist;
|
||||
int ctrl_fd, data_rfd, data_wfd;
|
||||
int ctrl_idx, data_ridx, data_widx;
|
||||
struct object *object;
|
||||
int fd[2];
|
||||
|
||||
sender = g_dbus_method_invocation_get_sender(invocation);
|
||||
client = sender_get_client(impl, sender, TRUE);
|
||||
|
||||
pw_log_debug("protocol-dbus %p: create client-node: %s", impl, sender);
|
||||
props = pw_properties_from_variant(arg_properties);
|
||||
|
||||
target_node = pw_properties_get(props, "pipewire.target.node");
|
||||
if (target_node) {
|
||||
if (strncmp(target_node, "/org/pipewire/node_", strlen("/org/pipewire/node_")) == 0) {
|
||||
pw_properties_setf(props, "pipewire.target.node", "%s",
|
||||
target_node + strlen("/org/pipewire/node_"));
|
||||
}
|
||||
}
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, fd) != 0)
|
||||
goto no_socket_pair;
|
||||
|
||||
ctrl_fd = fd[1];
|
||||
|
||||
node = pw_client_node_new(client, SPA_ID_INVALID, arg_name, props);
|
||||
|
||||
object = find_object(impl, node->node);
|
||||
if (object == NULL)
|
||||
goto object_failed;
|
||||
|
||||
if ((res = pw_client_node_get_fds(node, &data_rfd, &data_wfd)) < 0)
|
||||
goto no_socket;
|
||||
|
||||
object_path = object->object_path;
|
||||
pw_log_debug("protocol-dbus %p: add client-node %p, %s", impl, node, object_path);
|
||||
|
||||
fdlist = g_unix_fd_list_new();
|
||||
ctrl_idx = g_unix_fd_list_append(fdlist, ctrl_fd, &error);
|
||||
data_ridx = g_unix_fd_list_append(fdlist, data_rfd, &error);
|
||||
data_widx = g_unix_fd_list_append(fdlist, data_wfd, &error);
|
||||
|
||||
g_dbus_method_invocation_return_value_with_unix_fd_list(invocation,
|
||||
g_variant_new("(ohhh)", object_path,
|
||||
ctrl_idx, data_ridx,
|
||||
data_widx), fdlist);
|
||||
g_object_unref(fdlist);
|
||||
|
||||
return TRUE;
|
||||
|
||||
object_failed:
|
||||
pw_log_debug("protocol-dbus %p: could not create object", impl);
|
||||
goto exit_error;
|
||||
no_socket_pair:
|
||||
pw_log_debug("protocol-dbus %p: could not create socketpair: %s", impl,
|
||||
strerror(errno));
|
||||
goto exit_error;
|
||||
no_socket:
|
||||
pw_log_debug("protocol-dbus %p: could not create socket: %s", impl,
|
||||
strerror(errno));
|
||||
pw_client_node_destroy(node);
|
||||
goto exit_error;
|
||||
exit_error:
|
||||
g_dbus_method_invocation_return_gerror(invocation, error);
|
||||
g_clear_error(&error);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void bus_acquired_handler(GDBusConnection * connection, const char *name, gpointer user_data)
|
||||
{
|
||||
struct impl *impl = user_data;
|
||||
GDBusObjectManagerServer *manager = impl->server_manager;
|
||||
|
||||
impl->connection = connection;
|
||||
g_dbus_object_manager_server_set_connection(manager, connection);
|
||||
}
|
||||
|
||||
static void
|
||||
name_acquired_handler(GDBusConnection * connection, const char *name, gpointer user_data)
|
||||
{
|
||||
}
|
||||
|
||||
static void name_lost_handler(GDBusConnection * connection, const char *name, gpointer user_data)
|
||||
{
|
||||
struct impl *impl = user_data;
|
||||
GDBusObjectManagerServer *manager = impl->server_manager;
|
||||
|
||||
g_dbus_object_manager_server_set_connection(manager, connection);
|
||||
impl->connection = connection;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_node_remove(PipeWireNode1 * interface,
|
||||
GDBusMethodInvocation * invocation, gpointer user_data)
|
||||
{
|
||||
struct pw_node *this = user_data;
|
||||
|
||||
pw_log_debug("node %p: remove", this);
|
||||
|
||||
g_dbus_method_invocation_return_value(invocation, g_variant_new("()"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
on_global_added(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(listener, struct impl, global_added);
|
||||
PipeWireObjectSkeleton *skel;
|
||||
|
||||
if (global->type == impl->core->type.client) {
|
||||
PipeWireClient1 *iface;
|
||||
struct pw_client *client = global->object;
|
||||
struct pw_properties *props = client->properties;
|
||||
char *path;
|
||||
|
||||
asprintf(&path, "%s_%u", PIPEWIRE_DBUS_OBJECT_CLIENT, global->id);
|
||||
skel = pipewire_object_skeleton_new(path);
|
||||
free(path);
|
||||
|
||||
iface = pipewire_client1_skeleton_new();
|
||||
pipewire_client1_set_properties(iface,
|
||||
props ? pw_properties_to_variant(props) : NULL);
|
||||
pipewire_object_skeleton_set_client1(skel, iface);
|
||||
|
||||
object_new(sizeof(struct client),
|
||||
impl, global, iface, skel, false, (pw_destroy_t) client_destroy);
|
||||
|
||||
} else if (global->type == impl->core->type.node) {
|
||||
PipeWireNode1 *iface;
|
||||
struct pw_node *node = global->object;
|
||||
struct pw_properties *props = node->properties;
|
||||
char *path;
|
||||
struct node *obj;
|
||||
|
||||
asprintf(&path, "%s_%u", PIPEWIRE_DBUS_OBJECT_NODE, global->id);
|
||||
skel = pipewire_object_skeleton_new(path);
|
||||
free(path);
|
||||
|
||||
iface = pipewire_node1_skeleton_new();
|
||||
g_signal_connect(iface, "handle-remove", (GCallback) handle_node_remove, node);
|
||||
pipewire_node1_set_state(iface, node->state);
|
||||
pipewire_node1_set_owner(iface, "/");
|
||||
pipewire_node1_set_name(iface, node->name);
|
||||
pipewire_node1_set_properties(iface,
|
||||
props ? pw_properties_to_variant(props) : NULL);
|
||||
pipewire_object_skeleton_set_node1(skel, iface);
|
||||
|
||||
obj = object_new(sizeof(struct node), impl, global, iface, skel, true, NULL);
|
||||
pw_signal_add(&node->state_changed, &obj->state_changed, on_node_state_changed);
|
||||
} else if (global->object == impl) {
|
||||
struct impl *proto = global->object;
|
||||
struct server *server;
|
||||
PipeWireDaemon1 *iface;
|
||||
char *path;
|
||||
|
||||
iface = pipewire_daemon1_skeleton_new();
|
||||
g_signal_connect(iface, "handle-create-node", (GCallback) handle_create_node,
|
||||
proto);
|
||||
g_signal_connect(iface, "handle-create-client-node",
|
||||
(GCallback) handle_create_client_node, proto);
|
||||
|
||||
asprintf(&path, "%s_%u", PIPEWIRE_DBUS_OBJECT_SERVER, global->id);
|
||||
skel = pipewire_object_skeleton_new(path);
|
||||
free(path);
|
||||
|
||||
pipewire_daemon1_set_user_name(iface, g_get_user_name());
|
||||
pipewire_daemon1_set_host_name(iface, g_get_host_name());
|
||||
pipewire_daemon1_set_version(iface, PACKAGE_VERSION);
|
||||
pipewire_daemon1_set_name(iface, PACKAGE_NAME);
|
||||
pipewire_daemon1_set_cookie(iface, g_random_int());
|
||||
pipewire_daemon1_set_properties(iface, proto->properties ?
|
||||
pw_properties_to_variant(proto->properties) : NULL);
|
||||
pipewire_object_skeleton_set_daemon1(skel, iface);
|
||||
|
||||
server = object_new(sizeof(struct server), impl, global, iface, skel, true, NULL);
|
||||
server->id = g_bus_own_name(G_BUS_TYPE_SESSION,
|
||||
PIPEWIRE_DBUS_SERVICE,
|
||||
G_BUS_NAME_OWNER_FLAGS_REPLACE,
|
||||
bus_acquired_handler,
|
||||
name_acquired_handler, name_lost_handler, proto, NULL);
|
||||
} else if (global->type == impl->core->type.link) {
|
||||
PipeWireLink1 *iface;
|
||||
struct pw_link *link = global->object;
|
||||
struct object *obj;
|
||||
char *path;
|
||||
|
||||
asprintf(&path, "%s_%u", PIPEWIRE_DBUS_OBJECT_LINK, global->id);
|
||||
skel = pipewire_object_skeleton_new(path);
|
||||
free(path);
|
||||
|
||||
iface = pipewire_link1_skeleton_new();
|
||||
|
||||
obj = link->output ? find_object(impl, link->output->node) : NULL;
|
||||
if (obj) {
|
||||
pipewire_link1_set_output_node(iface, obj->object_path);
|
||||
pipewire_link1_set_output_port(iface, link->output->port_id);
|
||||
} else {
|
||||
pipewire_link1_set_output_node(iface, "/");
|
||||
pipewire_link1_set_output_port(iface, SPA_ID_INVALID);
|
||||
}
|
||||
obj = link->input ? find_object(impl, link->input->node) : NULL;
|
||||
if (obj) {
|
||||
pipewire_link1_set_input_node(iface, obj->object_path);
|
||||
pipewire_link1_set_input_port(iface, link->input->port_id);
|
||||
} else {
|
||||
pipewire_link1_set_output_node(iface, "/");
|
||||
pipewire_link1_set_output_port(iface, SPA_ID_INVALID);
|
||||
}
|
||||
pipewire_object_skeleton_set_link1(skel, iface);
|
||||
|
||||
object_new(sizeof(struct object), impl, global, iface, skel, true, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_global_removed(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(listener, struct impl, global_removed);
|
||||
struct object *object;
|
||||
|
||||
if ((object = find_object(impl, global->object)))
|
||||
object_destroy(object);
|
||||
}
|
||||
|
||||
static struct impl *pw_protocol_dbus_new(struct pw_core *core, struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
pw_log_debug("protocol-dbus %p: new", impl);
|
||||
|
||||
impl->core = core;
|
||||
impl->properties = properties;
|
||||
|
||||
spa_list_init(&impl->client_list);
|
||||
spa_list_init(&impl->object_list);
|
||||
|
||||
pw_signal_add(&core->global_added, &impl->global_added, on_global_added);
|
||||
pw_signal_add(&core->global_removed, &impl->global_removed, on_global_removed);
|
||||
|
||||
impl->server_manager = g_dbus_object_manager_server_new(PIPEWIRE_DBUS_OBJECT_PREFIX);
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void pw_protocol_dbus_destroy(struct impl *impl)
|
||||
{
|
||||
struct object *object, *tmp;
|
||||
|
||||
pw_log_debug("protocol-dbus %p: destroy", impl);
|
||||
|
||||
pw_global_destroy(impl->global);
|
||||
|
||||
spa_list_for_each_safe(object, tmp, &impl->object_list, link)
|
||||
object_destroy(object);
|
||||
|
||||
#if 0
|
||||
if (impl->id != 0)
|
||||
g_bus_unown_name(impl->id);
|
||||
#endif
|
||||
|
||||
pw_signal_remove(&impl->global_added);
|
||||
pw_signal_remove(&impl->global_removed);
|
||||
pw_signal_remove(&impl->node_state_changed);
|
||||
|
||||
g_clear_object(&impl->server_manager);
|
||||
|
||||
free(impl);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
pw_protocol_dbus_new(module->core, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
684
src/modules/module-protocol-native.c
Normal file
684
src/modules/module-protocol-native.c
Normal file
|
|
@ -0,0 +1,684 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "pipewire/pipewire.h"
|
||||
#include "pipewire/log.h"
|
||||
#include "pipewire/interfaces.h"
|
||||
#include "pipewire/core.h"
|
||||
#include "pipewire/node.h"
|
||||
#include "pipewire/module.h"
|
||||
#include "pipewire/client.h"
|
||||
#include "pipewire/resource.h"
|
||||
#include "pipewire/link.h"
|
||||
#include "pipewire/node-factory.h"
|
||||
#include "pipewire/data-loop.h"
|
||||
#include "pipewire/main-loop.h"
|
||||
|
||||
#include "modules/module-protocol-native/connection.h"
|
||||
|
||||
#ifndef UNIX_PATH_MAX
|
||||
#define UNIX_PATH_MAX 108
|
||||
#endif
|
||||
|
||||
#define LOCK_SUFFIX ".lock"
|
||||
#define LOCK_SUFFIXLEN 5
|
||||
|
||||
struct pw_protocol *pw_protocol_native_init(void);
|
||||
|
||||
typedef bool(*demarshal_func_t) (void *object, void *data, size_t size);
|
||||
|
||||
struct connection {
|
||||
struct pw_protocol_connection this;
|
||||
|
||||
int fd;
|
||||
|
||||
struct spa_source *source;
|
||||
struct pw_connection *connection;
|
||||
|
||||
bool disconnecting;
|
||||
struct pw_listener need_flush;
|
||||
struct spa_source *flush_event;
|
||||
};
|
||||
|
||||
struct listener {
|
||||
struct pw_protocol_listener this;
|
||||
|
||||
int fd;
|
||||
int fd_lock;
|
||||
struct sockaddr_un addr;
|
||||
char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN];
|
||||
|
||||
struct pw_loop *loop;
|
||||
struct spa_source *source;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
struct pw_core *core;
|
||||
struct spa_list link;
|
||||
|
||||
struct pw_protocol *protocol;
|
||||
struct pw_properties *properties;
|
||||
|
||||
struct spa_list client_list;
|
||||
|
||||
struct spa_loop_control_hooks hooks;
|
||||
};
|
||||
|
||||
struct native_client {
|
||||
struct impl *impl;
|
||||
struct spa_list link;
|
||||
struct pw_client *client;
|
||||
int fd;
|
||||
struct spa_source *source;
|
||||
struct pw_connection *connection;
|
||||
struct pw_listener busy_changed;
|
||||
};
|
||||
|
||||
static void client_destroy(void *data)
|
||||
{
|
||||
struct pw_client *client = data;
|
||||
struct native_client *this = client->user_data;
|
||||
|
||||
pw_loop_destroy_source(this->impl->core->main_loop, this->source);
|
||||
spa_list_remove(&this->link);
|
||||
|
||||
pw_connection_destroy(this->connection);
|
||||
close(this->fd);
|
||||
}
|
||||
|
||||
static void
|
||||
process_messages(struct native_client *client)
|
||||
{
|
||||
struct pw_connection *conn = client->connection;
|
||||
uint8_t opcode;
|
||||
uint32_t id;
|
||||
uint32_t size;
|
||||
struct pw_client *c = client->client;
|
||||
void *message;
|
||||
|
||||
while (pw_connection_get_next(conn, &opcode, &id, &message, &size)) {
|
||||
struct pw_resource *resource;
|
||||
const demarshal_func_t *demarshal;
|
||||
|
||||
pw_log_trace("protocol-native %p: got message %d from %u", client->impl,
|
||||
opcode, id);
|
||||
|
||||
resource = pw_map_lookup(&c->objects, id);
|
||||
if (resource == NULL) {
|
||||
pw_log_error("protocol-native %p: unknown resource %u",
|
||||
client->impl, id);
|
||||
continue;
|
||||
}
|
||||
if (opcode >= resource->iface->n_methods) {
|
||||
pw_log_error("protocol-native %p: invalid method %u %u", client->impl,
|
||||
id, opcode);
|
||||
pw_client_destroy(c);
|
||||
break;
|
||||
}
|
||||
demarshal = resource->iface->methods;
|
||||
if (!demarshal[opcode] || !demarshal[opcode] (resource, message, size)) {
|
||||
pw_log_error("protocol-native %p: invalid message received %u %u",
|
||||
client->impl, id, opcode);
|
||||
pw_client_destroy(c);
|
||||
break;
|
||||
}
|
||||
if (c->busy) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_busy_changed(struct pw_listener *listener,
|
||||
struct pw_client *client)
|
||||
{
|
||||
struct native_client *c = SPA_CONTAINER_OF(listener, struct native_client, busy_changed);
|
||||
enum spa_io mask = SPA_IO_ERR | SPA_IO_HUP;
|
||||
|
||||
if (!client->busy)
|
||||
mask |= SPA_IO_IN;
|
||||
|
||||
pw_loop_update_io(c->impl->core->main_loop, c->source, mask);
|
||||
|
||||
if (!client->busy)
|
||||
process_messages(c);
|
||||
|
||||
}
|
||||
|
||||
static void on_before_hook(const struct spa_loop_control_hooks *hooks)
|
||||
{
|
||||
struct impl *this = SPA_CONTAINER_OF(hooks, struct impl, hooks);
|
||||
struct native_client *client, *tmp;
|
||||
|
||||
spa_list_for_each_safe(client, tmp, &this->client_list, link)
|
||||
pw_connection_flush(client->connection);
|
||||
}
|
||||
|
||||
static void
|
||||
connection_data(struct spa_loop_utils *utils,
|
||||
struct spa_source *source, int fd, enum spa_io mask, void *data)
|
||||
{
|
||||
struct native_client *client = data;
|
||||
|
||||
if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
|
||||
pw_log_error("protocol-native %p: got connection error", client->impl);
|
||||
pw_client_destroy(client->client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mask & SPA_IO_IN)
|
||||
process_messages(client);
|
||||
}
|
||||
|
||||
static struct native_client *client_new(struct impl *impl, int fd)
|
||||
{
|
||||
struct native_client *this;
|
||||
struct pw_client *client;
|
||||
socklen_t len;
|
||||
struct ucred ucred, *ucredp;
|
||||
|
||||
len = sizeof(ucred);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
|
||||
pw_log_error("no peercred: %m");
|
||||
ucredp = NULL;
|
||||
} else {
|
||||
ucredp = &ucred;
|
||||
}
|
||||
|
||||
client = pw_client_new(impl->core, ucredp, NULL, sizeof(struct native_client));
|
||||
if (client == NULL)
|
||||
goto no_client;
|
||||
|
||||
client->destroy = client_destroy;
|
||||
|
||||
this = client->user_data;
|
||||
this->impl = impl;
|
||||
this->fd = fd;
|
||||
this->source = pw_loop_add_io(impl->core->main_loop,
|
||||
this->fd,
|
||||
SPA_IO_ERR | SPA_IO_HUP, false, connection_data, this);
|
||||
if (this->source == NULL)
|
||||
goto no_source;
|
||||
|
||||
this->connection = pw_connection_new(fd);
|
||||
if (this->connection == NULL)
|
||||
goto no_connection;
|
||||
|
||||
client->protocol = impl->protocol;
|
||||
client->protocol_private = this->connection;
|
||||
|
||||
this->client = client;
|
||||
|
||||
spa_list_insert(impl->client_list.prev, &this->link);
|
||||
|
||||
pw_signal_add(&client->busy_changed, &this->busy_changed, on_busy_changed);
|
||||
|
||||
pw_global_bind(impl->core->global, client, 0, 0);
|
||||
|
||||
return this;
|
||||
|
||||
no_connection:
|
||||
pw_loop_destroy_source(impl->core->main_loop, this->source);
|
||||
no_source:
|
||||
free(this);
|
||||
no_client:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void destroy_listener(struct listener *l)
|
||||
{
|
||||
if (l->source)
|
||||
pw_loop_destroy_source(l->loop, l->source);
|
||||
if (l->addr.sun_path[0])
|
||||
unlink(l->addr.sun_path);
|
||||
if (l->fd >= 0)
|
||||
close(l->fd);
|
||||
if (l->lock_addr[0])
|
||||
unlink(l->lock_addr);
|
||||
if (l->fd_lock >= 0)
|
||||
close(l->fd_lock);
|
||||
free(l);
|
||||
}
|
||||
|
||||
static bool init_socket_name(struct listener *l, const char *name)
|
||||
{
|
||||
int name_size;
|
||||
const char *runtime_dir;
|
||||
|
||||
if ((runtime_dir = getenv("XDG_RUNTIME_DIR")) == NULL) {
|
||||
pw_log_error("XDG_RUNTIME_DIR not set in the environment");
|
||||
return false;
|
||||
}
|
||||
|
||||
l->addr.sun_family = AF_LOCAL;
|
||||
name_size = snprintf(l->addr.sun_path, sizeof(l->addr.sun_path),
|
||||
"%s/%s", runtime_dir, name) + 1;
|
||||
|
||||
if (name_size > (int) sizeof(l->addr.sun_path)) {
|
||||
pw_log_error("socket path \"%s/%s\" plus null terminator exceeds 108 bytes",
|
||||
runtime_dir, name);
|
||||
*l->addr.sun_path = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool lock_socket(struct listener *l)
|
||||
{
|
||||
struct stat socket_stat;
|
||||
|
||||
snprintf(l->lock_addr, sizeof(l->lock_addr), "%s%s", l->addr.sun_path, LOCK_SUFFIX);
|
||||
|
||||
l->fd_lock = open(l->lock_addr, O_CREAT | O_CLOEXEC,
|
||||
(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
|
||||
|
||||
if (l->fd_lock < 0) {
|
||||
pw_log_error("unable to open lockfile %s check permissions", l->lock_addr);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (flock(l->fd_lock, LOCK_EX | LOCK_NB) < 0) {
|
||||
pw_log_error("unable to lock lockfile %s, maybe another daemon is running",
|
||||
l->lock_addr);
|
||||
goto err_fd;
|
||||
}
|
||||
|
||||
if (stat(l->addr.sun_path, &socket_stat) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
pw_log_error("did not manage to stat file %s\n", l->addr.sun_path);
|
||||
goto err_fd;
|
||||
}
|
||||
} else if (socket_stat.st_mode & S_IWUSR || socket_stat.st_mode & S_IWGRP) {
|
||||
unlink(l->addr.sun_path);
|
||||
}
|
||||
return true;
|
||||
|
||||
err_fd:
|
||||
close(l->fd_lock);
|
||||
l->fd_lock = -1;
|
||||
err:
|
||||
*l->lock_addr = 0;
|
||||
*l->addr.sun_path = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
socket_data(struct spa_loop_utils *utils,
|
||||
struct spa_source *source, int fd, enum spa_io mask, void *data)
|
||||
{
|
||||
struct impl *impl = data;
|
||||
struct native_client *client;
|
||||
struct sockaddr_un name;
|
||||
socklen_t length;
|
||||
int client_fd;
|
||||
|
||||
length = sizeof(name);
|
||||
client_fd = accept4(fd, (struct sockaddr *) &name, &length, SOCK_CLOEXEC);
|
||||
if (client_fd < 0) {
|
||||
pw_log_error("failed to accept: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
client = client_new(impl, client_fd);
|
||||
if (client == NULL) {
|
||||
pw_log_error("failed to create client");
|
||||
close(client_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
pw_loop_update_io(impl->core->main_loop,
|
||||
client->source, SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP);
|
||||
}
|
||||
|
||||
static bool add_socket(struct impl *impl, struct listener *l)
|
||||
{
|
||||
socklen_t size;
|
||||
|
||||
if ((l->fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0)
|
||||
return false;
|
||||
|
||||
size = offsetof(struct sockaddr_un, sun_path) + strlen(l->addr.sun_path);
|
||||
if (bind(l->fd, (struct sockaddr *) &l->addr, size) < 0) {
|
||||
pw_log_error("bind() failed with error: %m");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (listen(l->fd, 128) < 0) {
|
||||
pw_log_error("listen() failed with error: %m");
|
||||
return false;
|
||||
}
|
||||
|
||||
l->loop = impl->core->main_loop;
|
||||
l->source = pw_loop_add_io(l->loop, l->fd, SPA_IO_IN, false, socket_data, impl);
|
||||
if (l->source == NULL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_name(struct pw_properties *properties)
|
||||
{
|
||||
const char *name = NULL;
|
||||
|
||||
if (properties)
|
||||
name = pw_properties_get(properties, "pipewire.core.name");
|
||||
if (name == NULL)
|
||||
name = getenv("PIPEWIRE_CORE");
|
||||
if (name == NULL)
|
||||
name = "pipewire-0";
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static int impl_connect(struct pw_protocol_connection *conn)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
socklen_t size;
|
||||
const char *runtime_dir, *name = NULL;
|
||||
int name_size, fd;
|
||||
|
||||
if ((runtime_dir = getenv("XDG_RUNTIME_DIR")) == NULL) {
|
||||
pw_log_error("connect failed: XDG_RUNTIME_DIR not set in the environment");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (name == NULL)
|
||||
name = getenv("PIPEWIRE_CORE");
|
||||
if (name == NULL)
|
||||
name = "pipewire-0";
|
||||
|
||||
if ((fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0)
|
||||
return -1;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_LOCAL;
|
||||
name_size = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", runtime_dir, name) + 1;
|
||||
|
||||
if (name_size > (int) sizeof addr.sun_path) {
|
||||
pw_log_error("socket path \"%s/%s\" plus null terminator exceeds 108 bytes",
|
||||
runtime_dir, name);
|
||||
goto error_close;
|
||||
};
|
||||
|
||||
size = offsetof(struct sockaddr_un, sun_path) + name_size;
|
||||
|
||||
if (connect(fd, (struct sockaddr *) &addr, size) < 0) {
|
||||
goto error_close;
|
||||
}
|
||||
|
||||
return conn->connect_fd(conn, fd);
|
||||
|
||||
error_close:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
on_remote_data(struct spa_loop_utils *utils,
|
||||
struct spa_source *source, int fd, enum spa_io mask, void *data)
|
||||
{
|
||||
struct connection *impl = data;
|
||||
struct pw_remote *this = impl->this.remote;
|
||||
struct pw_connection *conn = impl->connection;
|
||||
|
||||
if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mask & SPA_IO_IN) {
|
||||
uint8_t opcode;
|
||||
uint32_t id;
|
||||
uint32_t size;
|
||||
void *message;
|
||||
|
||||
while (!impl->disconnecting
|
||||
&& pw_connection_get_next(conn, &opcode, &id, &message, &size)) {
|
||||
struct pw_proxy *proxy;
|
||||
const demarshal_func_t *demarshal;
|
||||
|
||||
pw_log_trace("protocol-native %p: got message %d from %u", this, opcode, id);
|
||||
|
||||
proxy = pw_map_lookup(&this->objects, id);
|
||||
if (proxy == NULL) {
|
||||
pw_log_error("protocol-native %p: could not find proxy %u", this, id);
|
||||
continue;
|
||||
}
|
||||
if (opcode >= proxy->iface->n_events) {
|
||||
pw_log_error("protocol-native %p: invalid method %u for %u", this, opcode,
|
||||
id);
|
||||
continue;
|
||||
}
|
||||
|
||||
demarshal = proxy->iface->events;
|
||||
if (demarshal[opcode]) {
|
||||
if (!demarshal[opcode] (proxy, message, size))
|
||||
pw_log_error
|
||||
("protocol-native %p: invalid message received %u for %u", this,
|
||||
opcode, id);
|
||||
} else
|
||||
pw_log_error("protocol-native %p: function %d not implemented on %u", this,
|
||||
opcode, id);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void do_flush_event(struct spa_loop_utils *utils, struct spa_source *source, void *data)
|
||||
{
|
||||
struct connection *impl = data;
|
||||
if (impl->connection)
|
||||
if (!pw_connection_flush(impl->connection))
|
||||
impl->this.disconnect(&impl->this);
|
||||
}
|
||||
|
||||
static void on_need_flush(struct pw_listener *listener, struct pw_connection *connection)
|
||||
{
|
||||
struct connection *impl = SPA_CONTAINER_OF(listener, struct connection, need_flush);
|
||||
struct pw_remote *remote = impl->this.remote;
|
||||
pw_loop_signal_event(remote->core->main_loop, impl->flush_event);
|
||||
}
|
||||
|
||||
static int impl_connect_fd(struct pw_protocol_connection *conn, int fd)
|
||||
{
|
||||
struct connection *impl = SPA_CONTAINER_OF(conn, struct connection, this);
|
||||
struct pw_remote *remote = impl->this.remote;
|
||||
|
||||
impl->connection = pw_connection_new(fd);
|
||||
if (impl->connection == NULL)
|
||||
goto error_close;
|
||||
|
||||
conn->remote->protocol_private = impl->connection;
|
||||
|
||||
pw_signal_add(&impl->connection->need_flush, &impl->need_flush, on_need_flush);
|
||||
|
||||
impl->fd = fd;
|
||||
impl->source = pw_loop_add_io(remote->core->main_loop,
|
||||
fd,
|
||||
SPA_IO_IN | SPA_IO_HUP | SPA_IO_ERR,
|
||||
false, on_remote_data, impl);
|
||||
|
||||
return 0;
|
||||
|
||||
error_close:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int impl_disconnect(struct pw_protocol_connection *conn)
|
||||
{
|
||||
struct connection *impl = SPA_CONTAINER_OF(conn, struct connection, this);
|
||||
struct pw_remote *remote = impl->this.remote;
|
||||
|
||||
impl->disconnecting = true;
|
||||
|
||||
if (impl->source)
|
||||
pw_loop_destroy_source(remote->core->main_loop, impl->source);
|
||||
impl->source = NULL;
|
||||
|
||||
if (impl->connection)
|
||||
pw_connection_destroy(impl->connection);
|
||||
impl->connection = NULL;
|
||||
|
||||
if (impl->fd != -1)
|
||||
close(impl->fd);
|
||||
impl->fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_destroy(struct pw_protocol_connection *conn)
|
||||
{
|
||||
struct connection *impl = SPA_CONTAINER_OF(conn, struct connection, this);
|
||||
struct pw_remote *remote = conn->remote;
|
||||
|
||||
pw_loop_destroy_source(remote->core->main_loop, impl->flush_event);
|
||||
|
||||
spa_list_remove(&conn->link);
|
||||
free(impl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pw_protocol_connection *
|
||||
impl_new_connection(struct pw_protocol *protocol,
|
||||
struct pw_remote *remote,
|
||||
struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl = protocol->protocol_private;
|
||||
struct connection *c;
|
||||
struct pw_protocol_connection *this;
|
||||
|
||||
if ((c = calloc(1, sizeof(struct connection))) == NULL)
|
||||
return NULL;
|
||||
|
||||
this = &c->this;
|
||||
this->remote = remote;
|
||||
|
||||
this->connect = impl_connect;
|
||||
this->connect_fd = impl_connect_fd;
|
||||
this->disconnect = impl_disconnect;
|
||||
this->destroy = impl_destroy;
|
||||
|
||||
c->flush_event = pw_loop_add_event(remote->core->main_loop, do_flush_event, c);
|
||||
|
||||
spa_list_insert(impl->protocol->connection_list.prev, &c->this.link);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
static struct pw_protocol_listener *
|
||||
impl_add_listener(struct pw_protocol *protocol,
|
||||
struct pw_core *core,
|
||||
struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl = protocol->protocol_private;
|
||||
struct listener *l;
|
||||
const char *name;
|
||||
|
||||
if ((l = calloc(1, sizeof(struct listener))) == NULL)
|
||||
return NULL;
|
||||
|
||||
l->fd = -1;
|
||||
l->fd_lock = -1;
|
||||
|
||||
name = get_name(properties);
|
||||
|
||||
if (!init_socket_name(l, name))
|
||||
goto error;
|
||||
|
||||
if (!lock_socket(l))
|
||||
goto error;
|
||||
|
||||
if (!add_socket(impl, l))
|
||||
goto error;
|
||||
|
||||
spa_list_insert(impl->protocol->listener_list.prev, &l->this.link);
|
||||
|
||||
impl->hooks.before = on_before_hook;
|
||||
pw_loop_add_hooks(impl->core->main_loop, &impl->hooks);
|
||||
|
||||
pw_log_info("protocol-native %p: Added listener", protocol);
|
||||
|
||||
return &l->this;
|
||||
|
||||
error:
|
||||
destroy_listener(l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static struct impl *pw_protocol_native_new(struct pw_core *core, struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
|
||||
impl->core = core;
|
||||
impl->properties = properties;
|
||||
impl->protocol = pw_protocol_native_init();
|
||||
impl->protocol->new_connection = impl_new_connection;
|
||||
impl->protocol->add_listener = impl_add_listener;
|
||||
impl->protocol->protocol_private = impl;
|
||||
pw_log_debug("protocol-native %p: new %p", impl, impl->protocol);
|
||||
|
||||
spa_list_init(&impl->client_list);
|
||||
|
||||
impl_add_listener(impl->protocol, core, properties);
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void pw_protocol_native_destroy(struct impl *impl)
|
||||
{
|
||||
struct impl *object, *tmp;
|
||||
|
||||
pw_log_debug("protocol-native %p: destroy", impl);
|
||||
|
||||
pw_signal_remove(&impl->before_iterate);
|
||||
|
||||
pw_global_destroy(impl->global);
|
||||
|
||||
spa_list_for_each_safe(object, tmp, &impl->object_list, link)
|
||||
object_destroy(object);
|
||||
|
||||
free(impl);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
pw_protocol_native_new(module->core, NULL);
|
||||
return true;
|
||||
}
|
||||
529
src/modules/module-protocol-native/connection.c
Normal file
529
src/modules/module-protocol-native/connection.c
Normal file
|
|
@ -0,0 +1,529 @@
|
|||
/* PipeWire
|
||||
* 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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <spa/lib/debug.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "connection.h"
|
||||
|
||||
/** \cond */
|
||||
|
||||
#define MAX_BUFFER_SIZE (1024 * 32)
|
||||
#define MAX_FDS 28
|
||||
|
||||
static bool debug_messages = 0;
|
||||
|
||||
struct buffer {
|
||||
uint8_t *buffer_data;
|
||||
size_t buffer_size;
|
||||
size_t buffer_maxsize;
|
||||
int fds[MAX_FDS];
|
||||
uint32_t n_fds;
|
||||
|
||||
off_t offset;
|
||||
void *data;
|
||||
size_t size;
|
||||
|
||||
bool update;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
struct pw_connection this;
|
||||
|
||||
struct buffer in, out;
|
||||
|
||||
uint32_t dest_id;
|
||||
uint8_t opcode;
|
||||
struct spa_pod_builder builder;
|
||||
};
|
||||
|
||||
/** \endcond */
|
||||
|
||||
/** Get an fd from a connection
|
||||
*
|
||||
* \param conn the connection
|
||||
* \param index the index of the fd to get
|
||||
* \return the fd at \a index or -1 when no such fd exists
|
||||
*
|
||||
* \memberof pw_connection
|
||||
*/
|
||||
int pw_connection_get_fd(struct pw_connection *conn, uint32_t index)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
|
||||
|
||||
if (index < 0 || index >= impl->in.n_fds)
|
||||
return -1;
|
||||
|
||||
return impl->in.fds[index];
|
||||
}
|
||||
|
||||
/** Add an fd to a connection
|
||||
*
|
||||
* \param conn the connection
|
||||
* \param fd the fd to add
|
||||
* \return the index of the fd or -1 when an error occured
|
||||
*
|
||||
* \memberof pw_connection
|
||||
*/
|
||||
uint32_t pw_connection_add_fd(struct pw_connection *conn, int fd)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
|
||||
uint32_t index, i;
|
||||
|
||||
for (i = 0; i < impl->out.n_fds; i++) {
|
||||
if (impl->out.fds[i] == fd)
|
||||
return i;
|
||||
}
|
||||
|
||||
index = impl->out.n_fds;
|
||||
if (index >= MAX_FDS) {
|
||||
pw_log_error("connection %p: too many fds", conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
impl->out.fds[index] = fd;
|
||||
impl->out.n_fds++;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static void *connection_ensure_size(struct pw_connection *conn, struct buffer *buf, size_t size)
|
||||
{
|
||||
if (buf->buffer_size + size > buf->buffer_maxsize) {
|
||||
buf->buffer_maxsize = SPA_ROUND_UP_N(buf->buffer_size + size, MAX_BUFFER_SIZE);
|
||||
buf->buffer_data = realloc(buf->buffer_data, buf->buffer_maxsize);
|
||||
|
||||
pw_log_warn("connection %p: resize buffer to %zd %zd %zd",
|
||||
conn, buf->buffer_size, size, buf->buffer_maxsize);
|
||||
}
|
||||
return (uint8_t *) buf->buffer_data + buf->buffer_size;
|
||||
}
|
||||
|
||||
static bool refill_buffer(struct pw_connection *conn, struct buffer *buf)
|
||||
{
|
||||
ssize_t len;
|
||||
struct cmsghdr *cmsg;
|
||||
struct msghdr msg = { 0 };
|
||||
struct iovec iov[1];
|
||||
char cmsgbuf[CMSG_SPACE(MAX_FDS * sizeof(int))];
|
||||
|
||||
iov[0].iov_base = buf->buffer_data + buf->buffer_size;
|
||||
iov[0].iov_len = buf->buffer_maxsize - buf->buffer_size;
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
msg.msg_flags = MSG_CMSG_CLOEXEC;
|
||||
|
||||
while (true) {
|
||||
len = recvmsg(conn->fd, &msg, msg.msg_flags);
|
||||
if (len < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
goto recv_error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
buf->buffer_size += len;
|
||||
|
||||
/* handle control messages */
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
|
||||
continue;
|
||||
|
||||
buf->n_fds =
|
||||
(cmsg->cmsg_len - ((char *) CMSG_DATA(cmsg) - (char *) cmsg)) / sizeof(int);
|
||||
memcpy(buf->fds, CMSG_DATA(cmsg), buf->n_fds * sizeof(int));
|
||||
}
|
||||
pw_log_trace("connection %p: %d read %zd bytes and %d fds", conn, conn->fd, len,
|
||||
buf->n_fds);
|
||||
|
||||
return true;
|
||||
|
||||
/* ERRORS */
|
||||
recv_error:
|
||||
pw_log_error("could not recvmsg on fd %d: %s", conn->fd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
static void clear_buffer(struct buffer *buf)
|
||||
{
|
||||
buf->n_fds = 0;
|
||||
buf->offset = 0;
|
||||
buf->size = 0;
|
||||
buf->buffer_size = 0;
|
||||
}
|
||||
|
||||
/** Make a new connection object for the given socket
|
||||
*
|
||||
* \param fd the socket
|
||||
* \returns a newly allocated connection object
|
||||
*
|
||||
* \memberof pw_connection
|
||||
*/
|
||||
struct pw_connection *pw_connection_new(int fd)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_connection *this;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
if (impl == NULL)
|
||||
return NULL;
|
||||
|
||||
debug_messages = pw_debug_is_category_enabled("connection");
|
||||
|
||||
this = &impl->this;
|
||||
|
||||
pw_log_debug("connection %p: new", this);
|
||||
|
||||
this->fd = fd;
|
||||
pw_signal_init(&this->need_flush);
|
||||
pw_signal_init(&this->destroy_signal);
|
||||
|
||||
impl->out.buffer_data = malloc(MAX_BUFFER_SIZE);
|
||||
impl->out.buffer_maxsize = MAX_BUFFER_SIZE;
|
||||
impl->in.buffer_data = malloc(MAX_BUFFER_SIZE);
|
||||
impl->in.buffer_maxsize = MAX_BUFFER_SIZE;
|
||||
impl->in.update = true;
|
||||
|
||||
if (impl->out.buffer_data == NULL || impl->in.buffer_data == NULL)
|
||||
goto no_mem;
|
||||
|
||||
return this;
|
||||
|
||||
no_mem:
|
||||
free(impl->out.buffer_data);
|
||||
free(impl->in.buffer_data);
|
||||
free(impl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Destroy a connection
|
||||
*
|
||||
* \param conn the connection to destroy
|
||||
*
|
||||
* \memberof pw_connection
|
||||
*/
|
||||
void pw_connection_destroy(struct pw_connection *conn)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
|
||||
|
||||
pw_log_debug("connection %p: destroy", conn);
|
||||
|
||||
pw_signal_emit(&conn->destroy_signal, conn);
|
||||
|
||||
free(impl->out.buffer_data);
|
||||
free(impl->in.buffer_data);
|
||||
free(impl);
|
||||
}
|
||||
|
||||
/** Move to the next packet in the connection
|
||||
*
|
||||
* \param conn the connection
|
||||
* \param opcode addres of result opcode
|
||||
* \param dest_id addres of result destination id
|
||||
* \param dt pointer to packet data
|
||||
* \param sz size of packet data
|
||||
* \return true on success
|
||||
*
|
||||
* Get the next packet in \a conn and store the opcode and destination
|
||||
* id as well as the packet data and size.
|
||||
*
|
||||
* \memberof pw_connection
|
||||
*/
|
||||
bool
|
||||
pw_connection_get_next(struct pw_connection *conn,
|
||||
uint8_t *opcode,
|
||||
uint32_t *dest_id,
|
||||
void **dt,
|
||||
uint32_t *sz)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
|
||||
size_t len, size;
|
||||
uint8_t *data;
|
||||
struct buffer *buf;
|
||||
uint32_t *p;
|
||||
|
||||
buf = &impl->in;
|
||||
|
||||
/* move to next packet */
|
||||
buf->offset += buf->size;
|
||||
|
||||
again:
|
||||
if (buf->update) {
|
||||
if (!refill_buffer(conn, buf))
|
||||
return false;
|
||||
buf->update = false;
|
||||
}
|
||||
|
||||
/* now read packet */
|
||||
data = buf->buffer_data;
|
||||
size = buf->buffer_size;
|
||||
|
||||
if (buf->offset >= size) {
|
||||
clear_buffer(buf);
|
||||
buf->update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
data += buf->offset;
|
||||
size -= buf->offset;
|
||||
|
||||
if (size < 8) {
|
||||
connection_ensure_size(conn, buf, 8);
|
||||
buf->update = true;
|
||||
goto again;
|
||||
}
|
||||
p = (uint32_t *) data;
|
||||
data += 8;
|
||||
size -= 8;
|
||||
|
||||
*dest_id = p[0];
|
||||
*opcode = p[1] >> 24;
|
||||
len = p[1] & 0xffffff;
|
||||
|
||||
if (len > size) {
|
||||
connection_ensure_size(conn, buf, len);
|
||||
buf->update = true;
|
||||
goto again;
|
||||
}
|
||||
buf->size = len;
|
||||
buf->data = data;
|
||||
buf->offset += 8;
|
||||
|
||||
*dt = buf->data;
|
||||
*sz = buf->size;
|
||||
|
||||
|
||||
if (debug_messages) {
|
||||
printf("<<<<<<<<< in:\n");
|
||||
spa_debug_pod((struct spa_pod *)data);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void *begin_write(struct pw_connection *conn, uint32_t size)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
|
||||
uint32_t *p;
|
||||
struct buffer *buf = &impl->out;
|
||||
/* 4 for dest_id, 1 for opcode, 3 for size and size for payload */
|
||||
p = connection_ensure_size(conn, buf, 8 + size);
|
||||
return p + 2;
|
||||
}
|
||||
|
||||
static uint32_t write_pod(struct spa_pod_builder *b, uint32_t ref, const void *data, uint32_t size)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(b, struct impl, builder);
|
||||
|
||||
if (ref == -1)
|
||||
ref = b->offset;
|
||||
|
||||
if (b->size <= b->offset) {
|
||||
b->size = SPA_ROUND_UP_N(b->offset + size, 4096);
|
||||
b->data = begin_write(&impl->this, b->size);
|
||||
}
|
||||
memcpy(b->data + ref, data, size);
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
struct spa_pod_builder *
|
||||
pw_connection_begin_write_resource(struct pw_connection *conn,
|
||||
struct pw_resource *resource,
|
||||
uint8_t opcode)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
|
||||
uint32_t diff, base, i, b;
|
||||
struct pw_client *client = resource->client;
|
||||
struct pw_core *core = client->core;
|
||||
const char **types;
|
||||
|
||||
base = client->n_types;
|
||||
diff = spa_type_map_get_size(core->type.map) - base;
|
||||
if (diff > 0) {
|
||||
types = alloca(diff * sizeof(char *));
|
||||
for (i = 0, b = base; i < diff; i++, b++)
|
||||
types[i] = spa_type_map_get_type(core->type.map, b);
|
||||
|
||||
client->n_types += diff;
|
||||
pw_core_notify_update_types(client->core_resource, base, diff, types);
|
||||
}
|
||||
|
||||
impl->dest_id = resource->id;
|
||||
impl->opcode = opcode;
|
||||
impl->builder = (struct spa_pod_builder) { NULL, 0, 0, NULL, write_pod };
|
||||
|
||||
return &impl->builder;
|
||||
}
|
||||
|
||||
struct spa_pod_builder *
|
||||
pw_connection_begin_write_proxy(struct pw_connection *conn,
|
||||
struct pw_proxy *proxy,
|
||||
uint8_t opcode)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
|
||||
uint32_t diff, base, i, b;
|
||||
const char **types;
|
||||
struct pw_remote *remote = proxy->remote;
|
||||
struct pw_core *core = remote->core;
|
||||
|
||||
base = remote->n_types;
|
||||
diff = spa_type_map_get_size(core->type.map) - base;
|
||||
if (diff > 0) {
|
||||
types = alloca(diff * sizeof(char *));
|
||||
for (i = 0, b = base; i < diff; i++, b++)
|
||||
types[i] = spa_type_map_get_type(core->type.map, b);
|
||||
|
||||
remote->n_types += diff;
|
||||
pw_core_do_update_types(remote->core_proxy, base, diff, types);
|
||||
}
|
||||
|
||||
impl->dest_id = proxy->id;
|
||||
impl->opcode = opcode;
|
||||
impl->builder = (struct spa_pod_builder) { NULL, 0, 0, NULL, write_pod };
|
||||
|
||||
return &impl->builder;
|
||||
}
|
||||
|
||||
void
|
||||
pw_connection_end_write(struct pw_connection *conn,
|
||||
struct spa_pod_builder *builder)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
|
||||
uint32_t *p, size = builder->offset;
|
||||
struct buffer *buf = &impl->out;
|
||||
|
||||
p = connection_ensure_size(conn, buf, 8 + size);
|
||||
*p++ = impl->dest_id;
|
||||
*p++ = (impl->opcode << 24) | (size & 0xffffff);
|
||||
|
||||
buf->buffer_size += 8 + size;
|
||||
|
||||
if (debug_messages) {
|
||||
printf(">>>>>>>>> out:\n");
|
||||
spa_debug_pod((struct spa_pod *)p);
|
||||
}
|
||||
|
||||
pw_signal_emit(&conn->need_flush, conn);
|
||||
}
|
||||
|
||||
/** Flush the connection object
|
||||
*
|
||||
* \param conn the connection object
|
||||
* \return true on success
|
||||
*
|
||||
* Write the queued messages on the connection to the socket
|
||||
*
|
||||
* \memberof pw_connection
|
||||
*/
|
||||
bool pw_connection_flush(struct pw_connection *conn)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
|
||||
ssize_t len;
|
||||
struct msghdr msg = { 0 };
|
||||
struct iovec iov[1];
|
||||
struct cmsghdr *cmsg;
|
||||
char cmsgbuf[CMSG_SPACE(MAX_FDS * sizeof(int))];
|
||||
int *cm, i, fds_len;
|
||||
struct buffer *buf;
|
||||
|
||||
buf = &impl->out;
|
||||
|
||||
if (buf->buffer_size == 0)
|
||||
return true;
|
||||
|
||||
fds_len = buf->n_fds * sizeof(int);
|
||||
|
||||
iov[0].iov_base = buf->buffer_data;
|
||||
iov[0].iov_len = buf->buffer_size;
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
if (buf->n_fds > 0) {
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = CMSG_SPACE(fds_len);
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(fds_len);
|
||||
cm = (int *) CMSG_DATA(cmsg);
|
||||
for (i = 0; i < buf->n_fds; i++)
|
||||
cm[i] = buf->fds[i] > 0 ? buf->fds[i] : -buf->fds[i];
|
||||
msg.msg_controllen = cmsg->cmsg_len;
|
||||
} else {
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
len = sendmsg(conn->fd, &msg, MSG_NOSIGNAL);
|
||||
if (len < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
goto send_error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
pw_log_trace("connection %p: %d written %zd bytes and %u fds", conn, conn->fd, len,
|
||||
buf->n_fds);
|
||||
|
||||
buf->buffer_size -= len;
|
||||
buf->n_fds = 0;
|
||||
|
||||
return true;
|
||||
|
||||
/* ERRORS */
|
||||
send_error:
|
||||
pw_log_error("could not sendmsg: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Clear the connection object
|
||||
*
|
||||
* \param conn the connection object
|
||||
* \return true on success
|
||||
*
|
||||
* Remove all queued messages from \a conn
|
||||
*
|
||||
* \memberof pw_connection
|
||||
*/
|
||||
bool pw_connection_clear(struct pw_connection *conn)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(conn, struct impl, this);
|
||||
|
||||
clear_buffer(&impl->out);
|
||||
clear_buffer(&impl->in);
|
||||
impl->in.update = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
89
src/modules/module-protocol-native/connection.h
Normal file
89
src/modules/module-protocol-native/connection.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* PipeWire
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __PIPEWIRE_CONNECTION_H__
|
||||
#define __PIPEWIRE_CONNECTION_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <spa/defs.h>
|
||||
#include <pipewire/sig.h>
|
||||
|
||||
/** \class pw_connection
|
||||
*
|
||||
* \brief Manages the connection between client and server
|
||||
*
|
||||
* The \ref pw_connection handles the connection between client
|
||||
* and server on a given socket.
|
||||
*/
|
||||
struct pw_connection {
|
||||
int fd; /**< the socket */
|
||||
|
||||
/** Emited when data has been written that needs to be flushed */
|
||||
PW_SIGNAL(need_flush, (struct pw_listener *listener, struct pw_connection *conn));
|
||||
/** Emited when the connection is destroyed */
|
||||
PW_SIGNAL(destroy_signal, (struct pw_listener *listener, struct pw_connection *conn));
|
||||
};
|
||||
|
||||
struct pw_connection *
|
||||
pw_connection_new(int fd);
|
||||
|
||||
void
|
||||
pw_connection_destroy(struct pw_connection *conn);
|
||||
|
||||
uint32_t
|
||||
pw_connection_add_fd(struct pw_connection *conn, int fd);
|
||||
|
||||
int
|
||||
pw_connection_get_fd(struct pw_connection *conn, uint32_t index);
|
||||
|
||||
bool
|
||||
pw_connection_get_next(struct pw_connection *conn,
|
||||
uint8_t *opcode,
|
||||
uint32_t *dest_id,
|
||||
void **data, uint32_t *size);
|
||||
|
||||
struct spa_pod_builder *
|
||||
pw_connection_begin_write_resource(struct pw_connection *conn,
|
||||
struct pw_resource *resource,
|
||||
uint8_t opcode);
|
||||
|
||||
struct spa_pod_builder *
|
||||
pw_connection_begin_write_proxy(struct pw_connection *conn,
|
||||
struct pw_proxy *proxy,
|
||||
uint8_t opcode);
|
||||
|
||||
|
||||
void
|
||||
pw_connection_end_write(struct pw_connection *conn,
|
||||
struct spa_pod_builder *builder);
|
||||
|
||||
bool
|
||||
pw_connection_flush(struct pw_connection *conn);
|
||||
|
||||
bool
|
||||
pw_connection_clear(struct pw_connection *conn);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* __PIPEWIRE_CONNECTION_H__ */
|
||||
1077
src/modules/module-protocol-native/protocol-native.c
Normal file
1077
src/modules/module-protocol-native/protocol-native.c
Normal file
File diff suppressed because it is too large
Load diff
195
src/modules/module-suspend-on-idle.c
Normal file
195
src/modules/module-suspend-on-idle.c
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "pipewire/core.h"
|
||||
#include "pipewire/module.h"
|
||||
|
||||
struct impl {
|
||||
struct pw_core *core;
|
||||
struct pw_properties *properties;
|
||||
|
||||
struct pw_listener global_added;
|
||||
struct pw_listener global_removed;
|
||||
|
||||
struct spa_list node_list;
|
||||
};
|
||||
|
||||
struct node_info {
|
||||
struct impl *impl;
|
||||
struct pw_node *node;
|
||||
struct spa_list link;
|
||||
struct pw_listener node_state_request;
|
||||
struct pw_listener node_state_changed;
|
||||
struct spa_source *idle_timeout;
|
||||
};
|
||||
|
||||
static struct node_info *find_node_info(struct impl *impl, struct pw_node *node)
|
||||
{
|
||||
struct node_info *info;
|
||||
|
||||
spa_list_for_each(info, &impl->node_list, link) {
|
||||
if (info->node == node)
|
||||
return info;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void remove_idle_timeout(struct node_info *info)
|
||||
{
|
||||
if (info->idle_timeout) {
|
||||
pw_loop_destroy_source(info->impl->core->main_loop, info->idle_timeout);
|
||||
info->idle_timeout = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void node_info_free(struct node_info *info)
|
||||
{
|
||||
spa_list_remove(&info->link);
|
||||
remove_idle_timeout(info);
|
||||
pw_signal_remove(&info->node_state_request);
|
||||
pw_signal_remove(&info->node_state_changed);
|
||||
free(info);
|
||||
}
|
||||
|
||||
static void idle_timeout(struct spa_loop_utils *utils, struct spa_source *source, void *data)
|
||||
{
|
||||
struct node_info *info = data;
|
||||
|
||||
pw_log_debug("module %p: node %p idle timeout", info->impl, info->node);
|
||||
remove_idle_timeout(info);
|
||||
pw_node_set_state(info->node, PW_NODE_STATE_SUSPENDED);
|
||||
}
|
||||
|
||||
static void
|
||||
on_node_state_request(struct pw_listener *listener, struct pw_node *node, enum pw_node_state state)
|
||||
{
|
||||
struct node_info *info = SPA_CONTAINER_OF(listener, struct node_info, node_state_request);
|
||||
remove_idle_timeout(info);
|
||||
}
|
||||
|
||||
static void
|
||||
on_node_state_changed(struct pw_listener *listener,
|
||||
struct pw_node *node, enum pw_node_state old, enum pw_node_state state)
|
||||
{
|
||||
struct node_info *info = SPA_CONTAINER_OF(listener, struct node_info, node_state_changed);
|
||||
struct impl *impl = info->impl;
|
||||
|
||||
if (state != PW_NODE_STATE_IDLE) {
|
||||
remove_idle_timeout(info);
|
||||
} else {
|
||||
struct timespec value;
|
||||
|
||||
pw_log_debug("module %p: node %p became idle", impl, node);
|
||||
info->idle_timeout = pw_loop_add_timer(impl->core->main_loop,
|
||||
idle_timeout, info);
|
||||
value.tv_sec = 3;
|
||||
value.tv_nsec = 0;
|
||||
pw_loop_update_timer(impl->core->main_loop,
|
||||
info->idle_timeout, &value, NULL, false);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_global_added(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(listener, struct impl, global_added);
|
||||
|
||||
if (global->type == impl->core->type.node) {
|
||||
struct pw_node *node = global->object;
|
||||
struct node_info *info;
|
||||
|
||||
info = calloc(1, sizeof(struct node_info));
|
||||
info->impl = impl;
|
||||
info->node = node;
|
||||
spa_list_insert(impl->node_list.prev, &info->link);
|
||||
pw_signal_add(&node->state_request, &info->node_state_request,
|
||||
on_node_state_request);
|
||||
pw_signal_add(&node->state_changed, &info->node_state_changed,
|
||||
on_node_state_changed);
|
||||
|
||||
pw_log_debug("module %p: node %p added", impl, node);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_global_removed(struct pw_listener *listener, struct pw_core *core, struct pw_global *global)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(listener, struct impl, global_removed);
|
||||
|
||||
if (global->type == impl->core->type.node) {
|
||||
struct pw_node *node = global->object;
|
||||
struct node_info *info;
|
||||
|
||||
if ((info = find_node_info(impl, node)))
|
||||
node_info_free(info);
|
||||
|
||||
pw_log_debug("module %p: node %p removed", impl, node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* module_new:
|
||||
* @core: #struct pw_core
|
||||
* @properties: #struct pw_properties
|
||||
*
|
||||
* Make a new #struct impl object with given @properties
|
||||
*
|
||||
* Returns: a new #struct impl
|
||||
*/
|
||||
static struct impl *module_new(struct pw_core *core, struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
pw_log_debug("module %p: new", impl);
|
||||
|
||||
impl->core = core;
|
||||
impl->properties = properties;
|
||||
|
||||
spa_list_init(&impl->node_list);
|
||||
|
||||
pw_signal_add(&core->global_added, &impl->global_added, on_global_added);
|
||||
pw_signal_add(&core->global_removed, &impl->global_removed, on_global_removed);
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void module_destroy(struct impl *impl)
|
||||
{
|
||||
pw_log_debug("module %p: destroy", impl);
|
||||
|
||||
pw_global_destroy(impl->global);
|
||||
|
||||
free(impl);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
module_new(module->core, NULL);
|
||||
return true;
|
||||
}
|
||||
34
src/modules/spa/meson.build
Normal file
34
src/modules/spa/meson.build
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
pipewire_module_spa_c_args = [
|
||||
'-DHAVE_CONFIG_H',
|
||||
'-D_GNU_SOURCE',
|
||||
]
|
||||
|
||||
pipewire_module_spa_monitor = shared_library('pipewire-module-spa-monitor',
|
||||
[ 'module-monitor.c', 'spa-monitor.c', 'spa-node.c' ],
|
||||
c_args : pipewire_module_spa_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
link_with : spalib,
|
||||
install : true,
|
||||
install_dir : '@0@/pipewire-0.1'.format(get_option('libdir')),
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
pipewire_module_spa_node = shared_library('pipewire-module-spa-node',
|
||||
[ 'module-node.c', 'spa-node.c' ],
|
||||
c_args : pipewire_module_spa_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
link_with : spalib,
|
||||
install : true,
|
||||
install_dir : '@0@/pipewire-0.1'.format(get_option('libdir')),
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
|
||||
pipewire_module_spa_node_factory = shared_library('pipewire-module-spa-node-factory',
|
||||
[ 'module-node-factory.c', 'spa-node.c' ],
|
||||
c_args : pipewire_module_spa_c_args,
|
||||
include_directories : [configinc, spa_inc],
|
||||
link_with : spalib,
|
||||
install : true,
|
||||
install_dir : '@0@/pipewire-0.1'.format(get_option('libdir')),
|
||||
dependencies : [mathlib, dl_lib, pipewire_dep],
|
||||
)
|
||||
63
src/modules/spa/module-monitor.c
Normal file
63
src/modules/spa/module-monitor.c
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2016 Axis Communications <dev-gstreamer@axis.com>
|
||||
* @author Linus Svensson <linus.svensson@axis.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 <getopt.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <spa/lib/props.h>
|
||||
|
||||
#include <pipewire/utils.h>
|
||||
#include <pipewire/core.h>
|
||||
#include <pipewire/module.h>
|
||||
|
||||
#include "spa-monitor.h"
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
const char *dir;
|
||||
char **argv;
|
||||
int n_tokens;
|
||||
|
||||
if (args == NULL)
|
||||
goto wrong_arguments;
|
||||
|
||||
argv = pw_split_strv(args, " \t", INT_MAX, &n_tokens);
|
||||
if (n_tokens < 3)
|
||||
goto not_enough_arguments;
|
||||
|
||||
if ((dir = getenv("SPA_PLUGIN_DIR")) == NULL)
|
||||
dir = PLUGINDIR;
|
||||
|
||||
pw_spa_monitor_load(module->core, dir, argv[0], argv[1], argv[2]);
|
||||
|
||||
pw_free_strv(argv);
|
||||
|
||||
return true;
|
||||
|
||||
not_enough_arguments:
|
||||
pw_free_strv(argv);
|
||||
wrong_arguments:
|
||||
pw_log_error("usage: module-spa-monitor <plugin> <factory> <name>");
|
||||
return false;
|
||||
}
|
||||
118
src/modules/spa/module-node-factory.c
Normal file
118
src/modules/spa/module-node-factory.c
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2017 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 <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "pipewire/interfaces.h"
|
||||
#include "pipewire/core.h"
|
||||
#include "pipewire/module.h"
|
||||
|
||||
#include "spa-node.h"
|
||||
|
||||
struct impl {
|
||||
struct pw_node_factory this;
|
||||
struct pw_properties *properties;
|
||||
};
|
||||
|
||||
static struct pw_node *create_node(struct pw_node_factory *factory,
|
||||
struct pw_resource *resource,
|
||||
const char *name,
|
||||
struct pw_properties *properties)
|
||||
{
|
||||
struct pw_node *node;
|
||||
const char *lib, *factory_name;
|
||||
|
||||
if (properties == NULL)
|
||||
goto no_properties;
|
||||
|
||||
lib = pw_properties_get(properties, "spa.library.name");
|
||||
factory_name = pw_properties_get(properties, "spa.factory.name");
|
||||
|
||||
if(lib == NULL || factory_name == NULL)
|
||||
goto no_properties;
|
||||
|
||||
node = pw_spa_node_load(factory->core,
|
||||
NULL,
|
||||
lib,
|
||||
factory_name,
|
||||
name,
|
||||
properties);
|
||||
if (node == NULL)
|
||||
goto no_mem;
|
||||
|
||||
return node;
|
||||
|
||||
no_properties:
|
||||
pw_log_error("missing properties");
|
||||
if (resource) {
|
||||
pw_core_notify_error(resource->client->core_resource,
|
||||
resource->client->core_resource->id,
|
||||
SPA_RESULT_INVALID_ARGUMENTS, "missing properties");
|
||||
}
|
||||
return NULL;
|
||||
no_mem:
|
||||
pw_log_error("can't create node");
|
||||
if (resource) {
|
||||
pw_core_notify_error(resource->client->core_resource,
|
||||
resource->client->core_resource->id,
|
||||
SPA_RESULT_NO_MEMORY, "no memory");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct impl *module_new(struct pw_core *core, struct pw_properties *properties)
|
||||
{
|
||||
struct impl *impl;
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
pw_log_debug("module %p: new", impl);
|
||||
|
||||
impl->properties = properties;
|
||||
|
||||
impl->this.core = core;
|
||||
impl->this.name = "spa-node-factory";
|
||||
pw_signal_init(&impl->this.destroy_signal);
|
||||
impl->this.create_node = create_node;
|
||||
|
||||
spa_list_insert(core->node_factory_list.prev, &impl->this.link);
|
||||
|
||||
pw_core_add_global(core, NULL, core->type.node_factory, 0, impl, NULL, &impl->this.global);
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void module_destroy(struct impl *impl)
|
||||
{
|
||||
pw_log_debug("module %p: destroy", impl);
|
||||
|
||||
free(impl);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
module_new(module->core, NULL);
|
||||
return true;
|
||||
}
|
||||
74
src/modules/spa/module-node.c
Normal file
74
src/modules/spa/module-node.c
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* PipeWire
|
||||
* Copyright (C) 2016 Axis Communications <dev-gstreamer@axis.com>
|
||||
* @author Linus Svensson <linus.svensson@axis.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 <getopt.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <spa/lib/props.h>
|
||||
|
||||
#include <pipewire/utils.h>
|
||||
#include <pipewire/core.h>
|
||||
#include <pipewire/module.h>
|
||||
|
||||
#include "spa-monitor.h"
|
||||
#include "spa-node.h"
|
||||
|
||||
bool pipewire__module_init(struct pw_module *module, const char *args)
|
||||
{
|
||||
struct pw_properties *props = NULL;
|
||||
char **argv;
|
||||
int i, n_tokens;
|
||||
|
||||
if (args == NULL)
|
||||
goto wrong_arguments;
|
||||
|
||||
argv = pw_split_strv(args, " \t", INT_MAX, &n_tokens);
|
||||
if (n_tokens < 3)
|
||||
goto not_enough_arguments;
|
||||
|
||||
props = pw_properties_new(NULL, NULL);
|
||||
|
||||
for (i = 3; i < n_tokens; i++) {
|
||||
char **prop;
|
||||
int n_props;
|
||||
|
||||
prop = pw_split_strv(argv[i], "=", INT_MAX, &n_props);
|
||||
if (n_props >= 2)
|
||||
pw_properties_set(props, prop[0], prop[1]);
|
||||
|
||||
pw_free_strv(prop);
|
||||
}
|
||||
|
||||
pw_spa_node_load(module->core, NULL, argv[0], argv[1], argv[2], props);
|
||||
|
||||
pw_free_strv(argv);
|
||||
|
||||
return true;
|
||||
|
||||
not_enough_arguments:
|
||||
pw_free_strv(argv);
|
||||
wrong_arguments:
|
||||
pw_log_error("usage: module-spa-node <plugin> <factory> <name> [key=value ...]");
|
||||
return false;
|
||||
}
|
||||
315
src/modules/spa/spa-monitor.c
Normal file
315
src/modules/spa/spa-monitor.c
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <spa/node.h>
|
||||
#include <spa/monitor.h>
|
||||
#include <spa/pod-iter.h>
|
||||
|
||||
#include <pipewire/log.h>
|
||||
#include <pipewire/node.h>
|
||||
|
||||
#include "spa-monitor.h"
|
||||
#include "spa-node.h"
|
||||
|
||||
struct monitor_item {
|
||||
char *id;
|
||||
struct spa_list link;
|
||||
struct pw_node *node;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
struct pw_spa_monitor this;
|
||||
|
||||
struct pw_core *core;
|
||||
|
||||
void *hnd;
|
||||
|
||||
struct spa_list item_list;
|
||||
};
|
||||
|
||||
static void add_item(struct pw_spa_monitor *this, struct spa_monitor_item *item)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
|
||||
int res;
|
||||
struct spa_handle *handle;
|
||||
struct monitor_item *mitem;
|
||||
void *node_iface;
|
||||
void *clock_iface;
|
||||
struct pw_properties *props = NULL;
|
||||
const char *name, *id, *klass;
|
||||
struct spa_handle_factory *factory;
|
||||
struct spa_pod *info = NULL;
|
||||
|
||||
spa_pod_object_query(&item->object,
|
||||
impl->core->type.monitor.name, SPA_POD_TYPE_STRING, &name,
|
||||
impl->core->type.monitor.id, SPA_POD_TYPE_STRING, &id,
|
||||
impl->core->type.monitor.klass, SPA_POD_TYPE_STRING, &klass,
|
||||
impl->core->type.monitor.factory, SPA_POD_TYPE_POINTER, &factory,
|
||||
impl->core->type.monitor.info, SPA_POD_TYPE_STRUCT, &info, 0);
|
||||
|
||||
pw_log_debug("monitor %p: add: \"%s\" (%s)", this, name, id);
|
||||
|
||||
props = pw_properties_new(NULL, NULL);
|
||||
|
||||
if (info) {
|
||||
struct spa_pod_iter it;
|
||||
|
||||
spa_pod_iter_pod(&it, info);
|
||||
while (true) {
|
||||
const char *key, *val;
|
||||
if (!spa_pod_iter_get
|
||||
(&it, SPA_POD_TYPE_STRING, &key, SPA_POD_TYPE_STRING, &val, 0))
|
||||
break;
|
||||
pw_properties_set(props, key, val);
|
||||
}
|
||||
}
|
||||
pw_properties_set(props, "media.class", klass);
|
||||
|
||||
handle = calloc(1, factory->size);
|
||||
if ((res = spa_handle_factory_init(factory,
|
||||
handle,
|
||||
&props->dict,
|
||||
impl->core->support, impl->core->n_support)) < 0) {
|
||||
pw_log_error("can't make factory instance: %d", res);
|
||||
return;
|
||||
}
|
||||
if ((res = spa_handle_get_interface(handle, impl->core->type.spa_node, &node_iface)) < 0) {
|
||||
pw_log_error("can't get NODE interface: %d", res);
|
||||
return;
|
||||
}
|
||||
if ((res = spa_handle_get_interface(handle, impl->core->type.spa_clock, &clock_iface)) < 0) {
|
||||
pw_log_info("no CLOCK interface: %d", res);
|
||||
}
|
||||
|
||||
|
||||
mitem = calloc(1, sizeof(struct monitor_item));
|
||||
mitem->id = strdup(id);
|
||||
mitem->node = pw_spa_node_new(impl->core, NULL, name, false, node_iface, clock_iface, props);
|
||||
|
||||
spa_list_insert(impl->item_list.prev, &mitem->link);
|
||||
}
|
||||
|
||||
static struct monitor_item *find_item(struct pw_spa_monitor *this, const char *id)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
|
||||
struct monitor_item *mitem;
|
||||
|
||||
spa_list_for_each(mitem, &impl->item_list, link) {
|
||||
if (strcmp(mitem->id, id) == 0) {
|
||||
return mitem;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void destroy_item(struct monitor_item *mitem)
|
||||
{
|
||||
pw_node_destroy(mitem->node);
|
||||
spa_list_remove(&mitem->link);
|
||||
free(mitem->id);
|
||||
free(mitem);
|
||||
}
|
||||
|
||||
static void remove_item(struct pw_spa_monitor *this, struct spa_monitor_item *item)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
|
||||
struct monitor_item *mitem;
|
||||
const char *name, *id;
|
||||
|
||||
spa_pod_object_query(&item->object,
|
||||
impl->core->type.monitor.name, SPA_POD_TYPE_STRING, &name,
|
||||
impl->core->type.monitor.id, SPA_POD_TYPE_STRING, &id, 0);
|
||||
|
||||
pw_log_debug("monitor %p: remove: \"%s\" (%s)", this, name, id);
|
||||
mitem = find_item(this, id);
|
||||
if (mitem)
|
||||
destroy_item(mitem);
|
||||
}
|
||||
|
||||
static void on_monitor_event(struct spa_monitor *monitor, struct spa_event *event, void *user_data)
|
||||
{
|
||||
struct impl *impl = user_data;
|
||||
struct pw_spa_monitor *this = &impl->this;
|
||||
|
||||
if (SPA_EVENT_TYPE(event) == impl->core->type.monitor.Added) {
|
||||
struct spa_monitor_item *item = SPA_POD_CONTENTS(struct spa_event, event);
|
||||
add_item(this, item);
|
||||
} else if (SPA_EVENT_TYPE(event) == impl->core->type.monitor.Removed) {
|
||||
struct spa_monitor_item *item = SPA_POD_CONTENTS(struct spa_event, event);
|
||||
remove_item(this, item);
|
||||
} else if (SPA_EVENT_TYPE(event) == impl->core->type.monitor.Changed) {
|
||||
struct spa_monitor_item *item = SPA_POD_CONTENTS(struct spa_event, event);
|
||||
const char *name;
|
||||
|
||||
spa_pod_object_query(&item->object,
|
||||
impl->core->type.monitor.name, SPA_POD_TYPE_STRING, &name, 0);
|
||||
|
||||
pw_log_debug("monitor %p: changed: \"%s\"", this, name);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_monitor(struct pw_core *core, const char *name)
|
||||
{
|
||||
const char *monitors;
|
||||
struct spa_dict_item item;
|
||||
struct spa_dict dict = SPA_DICT_INIT(1, &item);
|
||||
|
||||
if (core->properties)
|
||||
monitors = pw_properties_get(core->properties, "monitors");
|
||||
else
|
||||
monitors = NULL;
|
||||
|
||||
item.key = "monitors";
|
||||
if (monitors == NULL)
|
||||
item.value = name;
|
||||
else
|
||||
asprintf((char **) &item.value, "%s,%s", monitors, name);
|
||||
|
||||
pw_core_update_properties(core, &dict);
|
||||
|
||||
if (monitors != NULL)
|
||||
free((void *) item.value);
|
||||
}
|
||||
|
||||
static const struct spa_monitor_callbacks callbacks = {
|
||||
SPA_VERSION_MONITOR_CALLBACKS,
|
||||
on_monitor_event,
|
||||
};
|
||||
|
||||
struct pw_spa_monitor *pw_spa_monitor_load(struct pw_core *core,
|
||||
const char *dir,
|
||||
const char *lib,
|
||||
const char *factory_name, const char *system_name)
|
||||
{
|
||||
struct impl *impl;
|
||||
struct pw_spa_monitor *this;
|
||||
struct spa_handle *handle;
|
||||
int res;
|
||||
void *iface;
|
||||
void *hnd;
|
||||
uint32_t index;
|
||||
spa_handle_factory_enum_func_t enum_func;
|
||||
const struct spa_handle_factory *factory;
|
||||
char *filename;
|
||||
|
||||
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;
|
||||
}
|
||||
handle = calloc(1, factory->size);
|
||||
if ((res = spa_handle_factory_init(factory,
|
||||
handle, NULL, core->support, core->n_support)) < 0) {
|
||||
pw_log_error("can't make factory instance: %d", res);
|
||||
goto init_failed;
|
||||
}
|
||||
if ((res = spa_handle_get_interface(handle, core->type.spa_monitor, &iface)) < 0) {
|
||||
free(handle);
|
||||
pw_log_error("can't get MONITOR interface: %d", res);
|
||||
goto interface_failed;
|
||||
}
|
||||
|
||||
impl = calloc(1, sizeof(struct impl));
|
||||
impl->core = core;
|
||||
impl->hnd = hnd;
|
||||
|
||||
this = &impl->this;
|
||||
pw_signal_init(&this->destroy_signal);
|
||||
this->monitor = iface;
|
||||
this->lib = filename;
|
||||
this->factory_name = strdup(factory_name);
|
||||
this->system_name = strdup(system_name);
|
||||
this->handle = handle;
|
||||
|
||||
update_monitor(core, this->system_name);
|
||||
|
||||
spa_list_init(&impl->item_list);
|
||||
|
||||
for (index = 0;; index++) {
|
||||
struct spa_monitor_item *item;
|
||||
int res;
|
||||
|
||||
if ((res = spa_monitor_enum_items(this->monitor, &item, index)) < 0) {
|
||||
if (res != SPA_RESULT_ENUM_END)
|
||||
pw_log_debug("spa_monitor_enum_items: got error %d\n", res);
|
||||
break;
|
||||
}
|
||||
add_item(this, item);
|
||||
}
|
||||
spa_monitor_set_callbacks(this->monitor, &callbacks, impl);
|
||||
|
||||
return this;
|
||||
|
||||
interface_failed:
|
||||
spa_handle_clear(handle);
|
||||
init_failed:
|
||||
free(handle);
|
||||
enum_failed:
|
||||
no_symbol:
|
||||
dlclose(hnd);
|
||||
open_failed:
|
||||
free(filename);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
void pw_spa_monitor_destroy(struct pw_spa_monitor *monitor)
|
||||
{
|
||||
struct impl *impl = SPA_CONTAINER_OF(monitor, struct impl, this);
|
||||
struct monitor_item *mitem, *tmp;
|
||||
|
||||
pw_log_debug("spa-monitor %p: dispose", impl);
|
||||
pw_signal_emit(&monitor->destroy_signal, monitor);
|
||||
|
||||
spa_list_for_each_safe(mitem, tmp, &impl->item_list, link)
|
||||
destroy_item(mitem);
|
||||
|
||||
spa_handle_clear(monitor->handle);
|
||||
free(monitor->handle);
|
||||
free(monitor->lib);
|
||||
free(monitor->factory_name);
|
||||
free(monitor->system_name);
|
||||
|
||||
dlclose(impl->hnd);
|
||||
free(impl);
|
||||
}
|
||||
54
src/modules/spa/spa-monitor.h
Normal file
54
src/modules/spa/spa-monitor.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef __PIPEWIRE_SPA_MONITOR_H__
|
||||
#define __PIPEWIRE_SPA_MONITOR_H__
|
||||
|
||||
#include <spa/monitor.h>
|
||||
|
||||
#include <pipewire/core.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct pw_spa_monitor {
|
||||
struct spa_monitor *monitor;
|
||||
|
||||
char *lib;
|
||||
char *factory_name;
|
||||
char *system_name;
|
||||
struct spa_handle *handle;
|
||||
|
||||
PW_SIGNAL(destroy_signal, (struct pw_listener *listener, struct pw_spa_monitor *monitor));
|
||||
};
|
||||
|
||||
struct pw_spa_monitor *
|
||||
pw_spa_monitor_load(struct pw_core *core,
|
||||
const char *dir,
|
||||
const char *lib,
|
||||
const char *factory_name, const char *system_name);
|
||||
void
|
||||
pw_spa_monitor_destroy(struct pw_spa_monitor *monitor);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PIPEWIRE_SPA_MONITOR_H__ */
|
||||
588
src/modules/spa/spa-node.c
Normal file
588
src/modules/spa/spa-node.c
Normal file
|
|
@ -0,0 +1,588 @@
|
|||
/* 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 <spa/graph-scheduler3.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <spa/node.h>
|
||||
|
||||
#include "spa-node.h"
|
||||
|
||||
struct impl {
|
||||
struct pw_node *this;
|
||||
|
||||
bool async_init;
|
||||
|
||||
void *hnd;
|
||||
struct spa_handle *handle;
|
||||
struct spa_node *node; /**< handle to SPA node */
|
||||
char *lib;
|
||||
char *factory_name;
|
||||
};
|
||||
|
||||
struct port {
|
||||
struct pw_port *port;
|
||||
|
||||
struct spa_node *node;
|
||||
};
|
||||
|
||||
|
||||
static int port_impl_enum_formats(struct pw_port *port,
|
||||
struct spa_format **format,
|
||||
const struct spa_format *filter,
|
||||
int32_t index)
|
||||
{
|
||||
struct port *p = port->user_data;
|
||||
return spa_node_port_enum_formats(p->node, port->direction, port->port_id, format, filter, index);
|
||||
}
|
||||
|
||||
static int port_impl_set_format(struct pw_port *port, uint32_t flags, struct spa_format *format)
|
||||
{
|
||||
struct port *p = port->user_data;
|
||||
return spa_node_port_set_format(p->node, port->direction, port->port_id, flags, format);
|
||||
}
|
||||
|
||||
static int port_impl_get_format(struct pw_port *port, const struct spa_format **format)
|
||||
{
|
||||
struct port *p = port->user_data;
|
||||
return spa_node_port_get_format(p->node, port->direction, port->port_id, format);
|
||||
}
|
||||
|
||||
static int port_impl_get_info(struct pw_port *port, const struct spa_port_info **info)
|
||||
{
|
||||
struct port *p = port->user_data;
|
||||
return spa_node_port_get_info(p->node, port->direction, port->port_id, info);
|
||||
}
|
||||
|
||||
static int port_impl_enum_params(struct pw_port *port, uint32_t index, struct spa_param **param)
|
||||
{
|
||||
struct port *p = port->user_data;
|
||||
return spa_node_port_enum_params(p->node, port->direction, port->port_id, index, param);
|
||||
}
|
||||
|
||||
static int port_impl_set_param(struct pw_port *port, struct spa_param *param)
|
||||
{
|
||||
struct port *p = port->user_data;
|
||||
return spa_node_port_set_param(p->node, port->direction, port->port_id, param);
|
||||
}
|
||||
|
||||
static int port_impl_use_buffers(struct pw_port *port, struct spa_buffer **buffers, uint32_t n_buffers)
|
||||
{
|
||||
struct port *p = port->user_data;
|
||||
return spa_node_port_use_buffers(p->node, port->direction, port->port_id, buffers, n_buffers);
|
||||
}
|
||||
|
||||
static int port_impl_alloc_buffers(struct pw_port *port,
|
||||
struct spa_param **params, uint32_t n_params,
|
||||
struct spa_buffer **buffers, uint32_t *n_buffers)
|
||||
{
|
||||
struct port *p = port->user_data;
|
||||
return spa_node_port_alloc_buffers(p->node, port->direction, port->port_id,
|
||||
params, n_params, buffers, n_buffers);
|
||||
}
|
||||
|
||||
static int port_impl_reuse_buffer(struct pw_port *port, uint32_t buffer_id)
|
||||
{
|
||||
struct port *p = port->user_data;
|
||||
return spa_node_port_reuse_buffer(p->node, port->port_id, buffer_id);
|
||||
}
|
||||
|
||||
static int port_impl_send_command(struct pw_port *port, struct spa_command *command)
|
||||
{
|
||||
struct port *p = port->user_data;
|
||||
return spa_node_port_send_command(p->node,
|
||||
port->direction,
|
||||
port->port_id,
|
||||
command);
|
||||
}
|
||||
|
||||
const struct pw_port_implementation port_impl = {
|
||||
PW_VERSION_PORT_IMPLEMENTATION,
|
||||
port_impl_enum_formats,
|
||||
port_impl_set_format,
|
||||
port_impl_get_format,
|
||||
port_impl_get_info,
|
||||
port_impl_enum_params,
|
||||
port_impl_set_param,
|
||||
port_impl_use_buffers,
|
||||
port_impl_alloc_buffers,
|
||||
port_impl_reuse_buffer,
|
||||
port_impl_send_command,
|
||||
};
|
||||
|
||||
static struct pw_port *
|
||||
make_port(struct pw_node *node, enum pw_direction direction, uint32_t port_id)
|
||||
{
|
||||
struct impl *impl = node->user_data;
|
||||
struct pw_port *port;
|
||||
struct port *p;
|
||||
|
||||
port = pw_port_new(direction, port_id, sizeof(struct port));
|
||||
if (port == NULL)
|
||||
return NULL;
|
||||
|
||||
p = port->user_data;
|
||||
p->node = impl->node;
|
||||
|
||||
port->implementation = &port_impl;
|
||||
|
||||
spa_node_port_set_io(impl->node, direction, port_id, &port->io);
|
||||
|
||||
pw_port_add(port, node);
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
static void update_port_ids(struct impl *impl)
|
||||
{
|
||||
struct pw_node *this = impl->this;
|
||||
uint32_t *input_port_ids, *output_port_ids;
|
||||
uint32_t n_input_ports, n_output_ports, max_input_ports, max_output_ports;
|
||||
uint32_t i;
|
||||
struct spa_list *ports;
|
||||
|
||||
spa_node_get_n_ports(impl->node,
|
||||
&n_input_ports, &max_input_ports, &n_output_ports, &max_output_ports);
|
||||
|
||||
this->info.max_input_ports = max_input_ports;
|
||||
this->info.max_output_ports = max_output_ports;
|
||||
|
||||
input_port_ids = alloca(sizeof(uint32_t) * n_input_ports);
|
||||
output_port_ids = alloca(sizeof(uint32_t) * n_output_ports);
|
||||
|
||||
spa_node_get_port_ids(impl->node,
|
||||
max_input_ports, input_port_ids, max_output_ports, output_port_ids);
|
||||
|
||||
pw_log_debug("node %p: update_port ids %u/%u, %u/%u", this,
|
||||
n_input_ports, max_input_ports, n_output_ports, max_output_ports);
|
||||
|
||||
i = 0;
|
||||
ports = &this->input_ports;
|
||||
while (true) {
|
||||
struct pw_port *p = (ports == &this->input_ports) ? NULL :
|
||||
SPA_CONTAINER_OF(ports, struct pw_port, link);
|
||||
|
||||
if (p && i < n_input_ports && p->port_id == input_port_ids[i]) {
|
||||
pw_log_debug("node %p: exiting input port %d", this, input_port_ids[i]);
|
||||
i++;
|
||||
ports = ports->next;
|
||||
} else if ((p && i < n_input_ports && input_port_ids[i] < p->port_id)
|
||||
|| i < n_input_ports) {
|
||||
struct pw_port *np;
|
||||
pw_log_debug("node %p: input port added %d", this, input_port_ids[i]);
|
||||
np = make_port(this, PW_DIRECTION_INPUT, input_port_ids[i]);
|
||||
|
||||
ports = np->link.next;
|
||||
i++;
|
||||
} else if (p) {
|
||||
ports = ports->next;
|
||||
pw_log_debug("node %p: input port removed %d", this, p->port_id);
|
||||
pw_port_destroy(p);
|
||||
} else {
|
||||
pw_log_debug("node %p: no more input ports", this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
ports = &this->output_ports;
|
||||
while (true) {
|
||||
struct pw_port *p = (ports == &this->output_ports) ? NULL :
|
||||
SPA_CONTAINER_OF(ports, struct pw_port, link);
|
||||
|
||||
if (p && i < n_output_ports && p->port_id == output_port_ids[i]) {
|
||||
pw_log_debug("node %p: exiting output port %d", this, output_port_ids[i]);
|
||||
i++;
|
||||
ports = ports->next;
|
||||
} else if ((p && i < n_output_ports && output_port_ids[i] < p->port_id)
|
||||
|| i < n_output_ports) {
|
||||
struct pw_port *np;
|
||||
pw_log_debug("node %p: output port added %d", this, output_port_ids[i]);
|
||||
np = make_port(this, PW_DIRECTION_OUTPUT, output_port_ids[i]);
|
||||
ports = np->link.next;
|
||||
i++;
|
||||
} else if (p) {
|
||||
ports = ports->next;
|
||||
pw_log_debug("node %p: output port removed %d", this, p->port_id);
|
||||
pw_port_destroy(p);
|
||||
} else {
|
||||
pw_log_debug("node %p: no more output ports", this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int node_impl_get_props(struct pw_node *node, struct spa_props **props)
|
||||
{
|
||||
struct impl *impl = node->user_data;
|
||||
return spa_node_get_props(impl->node, props);
|
||||
}
|
||||
|
||||
static int node_impl_set_props(struct pw_node *node, const struct spa_props *props)
|
||||
{
|
||||
struct impl *impl = node->user_data;
|
||||
return spa_node_set_props(impl->node, props);
|
||||
}
|
||||
|
||||
static int node_impl_send_command(struct pw_node *node, struct spa_command *command)
|
||||
{
|
||||
struct impl *impl = node->user_data;
|
||||
return spa_node_send_command(impl->node, command);
|
||||
}
|
||||
|
||||
static struct pw_port*
|
||||
node_impl_add_port(struct pw_node *node,
|
||||
enum pw_direction direction,
|
||||
uint32_t port_id)
|
||||
{
|
||||
struct impl *impl = node->user_data;
|
||||
int res;
|
||||
|
||||
if ((res = spa_node_add_port(impl->node, direction, port_id)) < 0) {
|
||||
pw_log_error("node %p: could not add port %d %d", node, port_id, res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return make_port(node, direction, port_id);
|
||||
}
|
||||
|
||||
static int node_impl_schedule_input(struct pw_node *node)
|
||||
{
|
||||
struct impl *impl = node->user_data;
|
||||
return spa_node_process_input(impl->node);
|
||||
}
|
||||
|
||||
static int node_impl_schedule_output(struct pw_node *node)
|
||||
{
|
||||
struct impl *impl = node->user_data;
|
||||
return spa_node_process_output(impl->node);
|
||||
}
|
||||
|
||||
static const struct pw_node_implementation node_impl = {
|
||||
PW_VERSION_NODE_IMPLEMENTATION,
|
||||
node_impl_get_props,
|
||||
node_impl_set_props,
|
||||
node_impl_send_command,
|
||||
node_impl_add_port,
|
||||
node_impl_schedule_input,
|
||||
node_impl_schedule_output,
|
||||
};
|
||||
|
||||
static void pw_spa_node_destroy(void *object)
|
||||
{
|
||||
struct pw_node *node = object;
|
||||
struct impl *impl = node->user_data;
|
||||
|
||||
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;
|
||||
update_port_ids(impl);
|
||||
pw_node_export(this);
|
||||
}
|
||||
|
||||
static void on_node_done(struct spa_node *node, int seq, int res, void *user_data)
|
||||
{
|
||||
struct impl *impl = user_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);
|
||||
pw_signal_emit(&this->async_complete, this, seq, res);
|
||||
}
|
||||
|
||||
static void on_node_event(struct spa_node *node, struct spa_event *event, void *user_data)
|
||||
{
|
||||
struct impl *impl = user_data;
|
||||
struct pw_node *this = impl->this;
|
||||
|
||||
pw_signal_emit(&this->event, this, event);
|
||||
}
|
||||
|
||||
static void on_node_need_input(struct spa_node *node, void *user_data)
|
||||
{
|
||||
struct impl *impl = user_data;
|
||||
struct pw_node *this = impl->this;
|
||||
|
||||
spa_graph_scheduler_pull(this->rt.sched, &this->rt.node);
|
||||
while (spa_graph_scheduler_iterate(this->rt.sched));
|
||||
}
|
||||
|
||||
static void on_node_have_output(struct spa_node *node, void *user_data)
|
||||
{
|
||||
struct impl *impl = user_data;
|
||||
struct pw_node *this = impl->this;
|
||||
|
||||
spa_graph_scheduler_push(this->rt.sched, &this->rt.node);
|
||||
while (spa_graph_scheduler_iterate(this->rt.sched));
|
||||
}
|
||||
|
||||
static void
|
||||
on_node_reuse_buffer(struct spa_node *node, uint32_t port_id, uint32_t buffer_id, void *user_data)
|
||||
{
|
||||
struct impl *impl = user_data;
|
||||
struct pw_node *this = impl->this;
|
||||
struct spa_graph_port *p, *pp;
|
||||
|
||||
spa_list_for_each(p, &this->rt.node.ports[SPA_DIRECTION_INPUT], link) {
|
||||
if (p->port_id != port_id)
|
||||
continue;
|
||||
|
||||
pp = p->peer;
|
||||
if (pp && pp->methods->reuse_buffer)
|
||||
pp->methods->reuse_buffer(pp, buffer_id, pp->user_data);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct spa_node_callbacks node_callbacks = {
|
||||
SPA_VERSION_NODE_CALLBACKS,
|
||||
&on_node_done,
|
||||
&on_node_event,
|
||||
&on_node_need_input,
|
||||
&on_node_have_output,
|
||||
&on_node_reuse_buffer,
|
||||
};
|
||||
|
||||
struct pw_node *
|
||||
pw_spa_node_new(struct pw_core *core,
|
||||
struct pw_resource *owner,
|
||||
const char *name,
|
||||
bool async,
|
||||
struct spa_node *node,
|
||||
struct spa_clock *clock,
|
||||
struct pw_properties *properties)
|
||||
{
|
||||
struct pw_node *this;
|
||||
struct impl *impl;
|
||||
|
||||
if (node->info) {
|
||||
uint32_t i;
|
||||
|
||||
if (properties == NULL)
|
||||
properties = pw_properties_new(NULL, NULL);
|
||||
|
||||
if (properties)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < node->info->n_items; i++)
|
||||
pw_properties_set(properties,
|
||||
node->info->items[i].key,
|
||||
node->info->items[i].value);
|
||||
}
|
||||
|
||||
this = pw_node_new(core, owner, name, properties, sizeof(struct impl));
|
||||
if (this == NULL)
|
||||
return NULL;
|
||||
|
||||
this->destroy = pw_spa_node_destroy;
|
||||
this->implementation = &node_impl;
|
||||
this->clock = clock;
|
||||
|
||||
impl = this->user_data;
|
||||
impl->this = this;
|
||||
impl->node = node;
|
||||
impl->async_init = async;
|
||||
|
||||
if (spa_node_set_callbacks(impl->node, &node_callbacks, impl) < 0)
|
||||
pw_log_warn("spa-node %p: error setting callback", this);
|
||||
|
||||
if (!async) {
|
||||
complete_init(impl);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_props(struct pw_core *core, struct spa_node *spa_node, struct pw_properties *pw_props)
|
||||
{
|
||||
int res;
|
||||
struct spa_props *props;
|
||||
void *state = NULL;
|
||||
const char *key;
|
||||
|
||||
if ((res = spa_node_get_props(spa_node, &props)) != SPA_RESULT_OK) {
|
||||
pw_log_debug("spa_node_get_props failed: %d", res);
|
||||
return SPA_RESULT_ERROR;
|
||||
}
|
||||
|
||||
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(core->type.map, key);
|
||||
if (id == SPA_ID_INVALID)
|
||||
continue;
|
||||
|
||||
if ((prop = spa_pod_object_find_prop(&props->object, 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_ID:
|
||||
SPA_POD_VALUE(struct spa_pod_id, &prop->body.value) =
|
||||
spa_type_map_get_id(core->type.map, value);
|
||||
break;
|
||||
case SPA_POD_TYPE_INT:
|
||||
SPA_POD_VALUE(struct spa_pod_int, &prop->body.value) = atoi(value);
|
||||
break;
|
||||
case SPA_POD_TYPE_LONG:
|
||||
SPA_POD_VALUE(struct spa_pod_long, &prop->body.value) = atoi(value);
|
||||
break;
|
||||
case SPA_POD_TYPE_FLOAT:
|
||||
SPA_POD_VALUE(struct spa_pod_float, &prop->body.value) = atof(value);
|
||||
break;
|
||||
case SPA_POD_TYPE_DOUBLE:
|
||||
SPA_POD_VALUE(struct spa_pod_double, &prop->body.value) = atof(value);
|
||||
break;
|
||||
case SPA_POD_TYPE_STRING:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((res = spa_node_set_props(spa_node, 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_resource *owner,
|
||||
const char *lib,
|
||||
const char *factory_name,
|
||||
const char *name,
|
||||
struct pw_properties *properties)
|
||||
{
|
||||
struct pw_node *this;
|
||||
struct impl *impl;
|
||||
struct spa_node *spa_node;
|
||||
struct spa_clock *spa_clock;
|
||||
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;
|
||||
bool async;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
handle = calloc(1, factory->size);
|
||||
if ((res = spa_handle_factory_init(factory,
|
||||
handle, NULL, core->support, core->n_support)) < 0) {
|
||||
pw_log_error("can't make factory instance: %d", res);
|
||||
goto init_failed;
|
||||
}
|
||||
async = SPA_RESULT_IS_ASYNC(res);
|
||||
|
||||
if ((res = spa_handle_get_interface(handle, core->type.spa_node, &iface)) < 0) {
|
||||
pw_log_error("can't get node interface %d", res);
|
||||
goto interface_failed;
|
||||
}
|
||||
spa_node = iface;
|
||||
|
||||
if ((res = spa_handle_get_interface(handle, core->type.spa_clock, &iface)) < 0) {
|
||||
iface = NULL;
|
||||
}
|
||||
spa_clock = 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, name, async, spa_node, spa_clock, properties);
|
||||
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;
|
||||
}
|
||||
51
src/modules/spa/spa-node.h
Normal file
51
src/modules/spa/spa-node.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef __PIPEWIRE_SPA_NODE_H__
|
||||
#define __PIPEWIRE_SPA_NODE_H__
|
||||
|
||||
#include <pipewire/core.h>
|
||||
#include <pipewire/node.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct pw_node *
|
||||
pw_spa_node_new(struct pw_core *core,
|
||||
struct pw_resource *owner, /**< optional owner */
|
||||
const char *name,
|
||||
bool async,
|
||||
struct spa_node *node,
|
||||
struct spa_clock *clock,
|
||||
struct pw_properties *properties);
|
||||
|
||||
struct pw_node *
|
||||
pw_spa_node_load(struct pw_core *core,
|
||||
struct pw_resource *owner, /**< optional owner */
|
||||
const char *lib,
|
||||
const char *factory_name,
|
||||
const char *name,
|
||||
struct pw_properties *properties);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PIPEWIRE_SPA_NODE_H__ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue