mirror of
https://gitlab.freedesktop.org/pipewire/pipewire
synced 2024-10-04 15:10:20 +00:00
934ab3036e
SPDX tags make the licensing information easy to understand and clear, and they are machine parseable. See https://spdx.dev for more information.
300 lines
7.4 KiB
C
300 lines
7.4 KiB
C
/* Simple Plugin API */
|
|
/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */
|
|
/* SPDX-License-Identifier: MIT */
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <dlfcn.h>
|
|
|
|
#include <spa/support/plugin.h>
|
|
#include <spa/support/log-impl.h>
|
|
#include <spa/support/loop.h>
|
|
#include <spa/utils/result.h>
|
|
#include <spa/utils/string.h>
|
|
#include <spa/node/node.h>
|
|
#include <spa/node/utils.h>
|
|
#include <spa/pod/parser.h>
|
|
#include <spa/param/param.h>
|
|
#include <spa/param/format.h>
|
|
#include <spa/debug/dict.h>
|
|
#include <spa/debug/pod.h>
|
|
#include <spa/debug/format.h>
|
|
#include <spa/debug/types.h>
|
|
|
|
static SPA_LOG_IMPL(default_log);
|
|
|
|
struct data {
|
|
struct spa_support support[4];
|
|
uint32_t n_support;
|
|
struct spa_log *log;
|
|
struct spa_loop loop;
|
|
struct spa_node *node;
|
|
struct spa_hook listener;
|
|
};
|
|
|
|
static void print_param(void *data, int seq, int res, uint32_t type, const void *result)
|
|
{
|
|
switch (type) {
|
|
case SPA_RESULT_TYPE_NODE_PARAMS:
|
|
{
|
|
const struct spa_result_node_params *r = result;
|
|
|
|
if (spa_pod_is_object_type(r->param, SPA_TYPE_OBJECT_Format))
|
|
spa_debug_format(16, NULL, r->param);
|
|
else
|
|
spa_debug_pod(16, NULL, r->param);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
inspect_node_params(struct data *data, struct spa_node *node,
|
|
uint32_t n_params, struct spa_param_info *params)
|
|
{
|
|
int res;
|
|
uint32_t i;
|
|
struct spa_hook listener;
|
|
static const struct spa_node_events node_events = {
|
|
SPA_VERSION_NODE_EVENTS,
|
|
.result = print_param,
|
|
};
|
|
|
|
for (i = 0; i < n_params; i++) {
|
|
printf("enumerating: %s:\n", spa_debug_type_find_name(spa_type_param, params[i].id));
|
|
|
|
if (!SPA_FLAG_IS_SET(params[i].flags, SPA_PARAM_INFO_READ))
|
|
continue;
|
|
|
|
spa_zero(listener);
|
|
spa_node_add_listener(node, &listener, &node_events, data);
|
|
res = spa_node_enum_params(node, 0, params[i].id, 0, UINT32_MAX, NULL);
|
|
spa_hook_remove(&listener);
|
|
|
|
if (res != 0) {
|
|
printf("error enum_params %d: %s", params[i].id, spa_strerror(res));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
inspect_port_params(struct data *data, struct spa_node *node,
|
|
enum spa_direction direction, uint32_t port_id,
|
|
uint32_t n_params, struct spa_param_info *params)
|
|
{
|
|
int res;
|
|
uint32_t i;
|
|
struct spa_hook listener;
|
|
static const struct spa_node_events node_events = {
|
|
SPA_VERSION_NODE_EVENTS,
|
|
.result = print_param,
|
|
};
|
|
|
|
for (i = 0; i < n_params; i++) {
|
|
printf("param: %s: flags %c%c\n",
|
|
spa_debug_type_find_name(spa_type_param, params[i].id),
|
|
params[i].flags & SPA_PARAM_INFO_READ ? 'r' : '-',
|
|
params[i].flags & SPA_PARAM_INFO_WRITE ? 'w' : '-');
|
|
|
|
if (!SPA_FLAG_IS_SET(params[i].flags, SPA_PARAM_INFO_READ))
|
|
continue;
|
|
|
|
printf("values:\n");
|
|
spa_zero(listener);
|
|
spa_node_add_listener(node, &listener, &node_events, data);
|
|
res = spa_node_port_enum_params(node, 0,
|
|
direction, port_id,
|
|
params[i].id, 0, UINT32_MAX,
|
|
NULL);
|
|
spa_hook_remove(&listener);
|
|
|
|
if (res != 0) {
|
|
printf("error port_enum_params %d: %s", params[i].id, spa_strerror(res));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void node_info(void *_data, const struct spa_node_info *info)
|
|
{
|
|
struct data *data = _data;
|
|
|
|
printf("node info: %08"PRIx64"\n", info->change_mask);
|
|
printf("max input ports: %u\n", info->max_input_ports);
|
|
printf("max output ports: %u\n", info->max_output_ports);
|
|
|
|
if (info->change_mask & SPA_NODE_CHANGE_MASK_PROPS) {
|
|
printf("node properties:\n");
|
|
spa_debug_dict(2, info->props);
|
|
}
|
|
if (info->change_mask & SPA_NODE_CHANGE_MASK_PARAMS) {
|
|
inspect_node_params(data, data->node, info->n_params, info->params);
|
|
}
|
|
}
|
|
|
|
static void node_port_info(void *_data, enum spa_direction direction, uint32_t id,
|
|
const struct spa_port_info *info)
|
|
{
|
|
struct data *data = _data;
|
|
|
|
printf(" %s port: %08x",
|
|
direction == SPA_DIRECTION_INPUT ? "input" : "output",
|
|
id);
|
|
|
|
if (info == NULL) {
|
|
printf(" removed\n");
|
|
}
|
|
else {
|
|
printf(" info:\n");
|
|
if (info->change_mask & SPA_PORT_CHANGE_MASK_PROPS) {
|
|
printf("port properties:\n");
|
|
spa_debug_dict(2, info->props);
|
|
}
|
|
if (info->change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
|
|
inspect_port_params(data, data->node, direction, id,
|
|
info->n_params, info->params);
|
|
}
|
|
}
|
|
}
|
|
|
|
static const struct spa_node_events node_events =
|
|
{
|
|
SPA_VERSION_NODE_EVENTS,
|
|
.info = node_info,
|
|
.port_info = node_port_info,
|
|
};
|
|
|
|
static void inspect_node(struct data *data, struct spa_node *node)
|
|
{
|
|
data->node = node;
|
|
spa_node_add_listener(node, &data->listener, &node_events, data);
|
|
spa_hook_remove(&data->listener);
|
|
}
|
|
|
|
static void inspect_factory(struct data *data, const struct spa_handle_factory *factory)
|
|
{
|
|
int res;
|
|
struct spa_handle *handle;
|
|
void *interface;
|
|
const struct spa_interface_info *info;
|
|
uint32_t index;
|
|
|
|
printf("factory version:\t\t%d\n", factory->version);
|
|
printf("factory name:\t\t'%s'\n", factory->name);
|
|
if (factory->version < 1) {
|
|
printf("\tno further info for version %d < 1\n", factory->version);
|
|
return;
|
|
}
|
|
|
|
printf("factory info:\n");
|
|
if (factory->info)
|
|
spa_debug_dict(2, factory->info);
|
|
else
|
|
printf(" none\n");
|
|
|
|
printf("factory interfaces:\n");
|
|
for (index = 0;;) {
|
|
if ((res = spa_handle_factory_enum_interface_info(factory, &info, &index)) <= 0) {
|
|
if (res != 0)
|
|
printf("error spa_handle_factory_enum_interface_info: %s",
|
|
spa_strerror(res));
|
|
break;
|
|
}
|
|
printf(" interface: '%s'\n", info->type);
|
|
}
|
|
|
|
handle = calloc(1, spa_handle_factory_get_size(factory, NULL));
|
|
if ((res =
|
|
spa_handle_factory_init(factory, handle, NULL, data->support, data->n_support)) < 0) {
|
|
printf("can't make factory instance: %d\n", res);
|
|
goto out;
|
|
}
|
|
|
|
printf("factory instance:\n");
|
|
|
|
for (index = 0;;) {
|
|
if ((res = spa_handle_factory_enum_interface_info(factory, &info, &index)) <= 0) {
|
|
if (res != 0)
|
|
printf("error spa_handle_factory_enum_interface_info: %s",
|
|
spa_strerror(res));
|
|
break;
|
|
}
|
|
printf(" interface: '%s'\n", info->type);
|
|
|
|
if ((res = spa_handle_get_interface(handle, info->type, &interface)) < 0) {
|
|
printf("can't get interface: %s: %d\n", info->type, res);
|
|
continue;
|
|
}
|
|
|
|
if (spa_streq(info->type, SPA_TYPE_INTERFACE_Node))
|
|
inspect_node(data, interface);
|
|
else
|
|
printf("skipping unknown interface\n");
|
|
}
|
|
|
|
if ((res = spa_handle_clear(handle)) < 0)
|
|
printf("failed to clear handle: %s\n", spa_strerror(res));
|
|
|
|
out:
|
|
free(handle);
|
|
}
|
|
|
|
static const struct spa_loop_methods impl_loop = {
|
|
SPA_VERSION_LOOP_METHODS,
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct data data = { 0 };
|
|
int res;
|
|
void *handle;
|
|
spa_handle_factory_enum_func_t enum_func;
|
|
uint32_t index;
|
|
const char *str;
|
|
|
|
if (argc < 2) {
|
|
printf("usage: %s <plugin.so>\n", argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
data.log = &default_log.log;
|
|
data.loop.iface = SPA_INTERFACE_INIT(
|
|
SPA_TYPE_INTERFACE_Loop,
|
|
SPA_VERSION_LOOP,
|
|
&impl_loop, &data);
|
|
|
|
if ((str = getenv("SPA_DEBUG")))
|
|
data.log->level = atoi(str);
|
|
|
|
data.support[0] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, data.log);
|
|
data.support[1] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Loop, &data.loop);
|
|
data.support[2] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataLoop, &data.loop);
|
|
data.n_support = 3;
|
|
|
|
if ((handle = dlopen(argv[1], RTLD_NOW)) == NULL) {
|
|
printf("can't load %s\n", argv[1]);
|
|
return -1;
|
|
}
|
|
if ((enum_func = dlsym(handle, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) {
|
|
printf("can't find function\n");
|
|
return -1;
|
|
}
|
|
|
|
for (index = 0;;) {
|
|
const struct spa_handle_factory *factory;
|
|
|
|
if ((res = enum_func(&factory, &index)) <= 0) {
|
|
if (res != 0)
|
|
printf("error enum_func: %s", spa_strerror(res));
|
|
break;
|
|
}
|
|
inspect_factory(&data, factory);
|
|
}
|
|
return 0;
|
|
}
|