mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-03-26 07:58:03 -04:00
260 lines
6.5 KiB
C
260 lines
6.5 KiB
C
/* Spa */
|
|
/* SPDX-FileCopyrightText: Copyright © 2026 Arun Raghavan */
|
|
/* SPDX-License-Identifier: MIT */
|
|
|
|
#include "config.h"
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <spa/support/log.h>
|
|
#include <spa/support/loop.h>
|
|
#include <spa/support/plugin.h>
|
|
#include <spa/support/varlink.h>
|
|
#include <spa/utils/dict.h>
|
|
#include <spa/utils/hook.h>
|
|
#include <spa/utils/names.h>
|
|
#include <spa/utils/json-core.h>
|
|
#include <spa/utils/result.h>
|
|
#include <time.h>
|
|
|
|
struct data {
|
|
const char *plugin_dir;
|
|
|
|
struct spa_support support[16];
|
|
uint32_t n_support;
|
|
struct spa_log *log;
|
|
struct spa_loop *loop;
|
|
struct spa_loop_control *loop_control;
|
|
struct spa_loop_utils *loop_utils;
|
|
struct spa_system *system;
|
|
|
|
struct spa_varlink *varlink;
|
|
struct spa_varlink_client *client;
|
|
struct spa_hook listener;
|
|
|
|
bool running;
|
|
};
|
|
|
|
static int load_handle(struct data *data, struct spa_handle **handle, const
|
|
char *lib, const char *name, struct spa_dict *info)
|
|
{
|
|
int res;
|
|
void *hnd;
|
|
spa_handle_factory_enum_func_t enum_func;
|
|
uint32_t i;
|
|
char *path;
|
|
|
|
if ((path = spa_aprintf("%s/%s", data->plugin_dir, lib)) == NULL)
|
|
return -ENOMEM;
|
|
|
|
hnd = dlopen(path, RTLD_NOW);
|
|
free(path);
|
|
|
|
if (hnd == NULL) {
|
|
printf("can't load %s: %s\n", lib, dlerror());
|
|
return -ENOENT;
|
|
}
|
|
if ((enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) {
|
|
printf("can't find enum function\n");
|
|
res = -ENOENT;
|
|
goto exit_cleanup;
|
|
}
|
|
|
|
for (i = 0;;) {
|
|
const struct spa_handle_factory *factory;
|
|
|
|
if ((res = enum_func(&factory, &i)) <= 0) {
|
|
if (res != 0)
|
|
printf("can't enumerate factories: %s\n", spa_strerror(res));
|
|
break;
|
|
}
|
|
if (factory->version < 1)
|
|
continue;
|
|
if (!spa_streq(factory->name, name))
|
|
continue;
|
|
|
|
*handle = calloc(1, spa_handle_factory_get_size(factory, NULL));
|
|
if ((res = spa_handle_factory_init(factory, *handle,
|
|
info, data->support,
|
|
data->n_support)) < 0) {
|
|
printf("can't make factory instance: %d\n", res);
|
|
goto exit_cleanup;
|
|
}
|
|
return 0;
|
|
}
|
|
return -EBADF;
|
|
|
|
exit_cleanup:
|
|
dlclose(hnd);
|
|
return res;
|
|
}
|
|
|
|
static int init(struct data *data)
|
|
{
|
|
struct spa_handle *handle;
|
|
struct spa_dict_item items[1];
|
|
struct spa_dict info;
|
|
const char *str;
|
|
void *iface;
|
|
int res;
|
|
|
|
if ((str = getenv("SPA_PLUGIN_DIR")) == NULL)
|
|
str = PLUGINDIR;
|
|
data->plugin_dir = str;
|
|
|
|
/* enable the debug messages in SPA */
|
|
items[0] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_TIMESTAMP, "true");
|
|
info = SPA_DICT_ARRAY(items);
|
|
if ((res = load_handle(data, &handle, "support/libspa-support.so",
|
|
SPA_NAME_SUPPORT_LOG, &info)) < 0)
|
|
return res;
|
|
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Log, &iface)) < 0) {
|
|
printf("can't get System interface %d\n", res);
|
|
return res;
|
|
}
|
|
|
|
data->log = iface;
|
|
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, data->log);
|
|
|
|
if ((str = getenv("SPA_DEBUG")))
|
|
data->log->level = atoi(str);
|
|
|
|
/* load and set support system */
|
|
if ((res = load_handle(data, &handle,
|
|
"support/libspa-support.so",
|
|
SPA_NAME_SUPPORT_SYSTEM, NULL)) < 0)
|
|
return res;
|
|
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_System, &iface)) < 0) {
|
|
printf("can't get System interface %d\n", res);
|
|
return res;
|
|
}
|
|
data->system = iface;
|
|
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_System, data->system);
|
|
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataSystem, data->system);
|
|
|
|
/* load and set support loop and loop control */
|
|
if ((res = load_handle(data, &handle,
|
|
"support/libspa-support.so",
|
|
SPA_NAME_SUPPORT_LOOP, NULL)) < 0)
|
|
return res;
|
|
|
|
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Loop, &iface)) < 0) {
|
|
printf("can't get interface %d\n", res);
|
|
return res;
|
|
}
|
|
data->loop = iface;
|
|
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Loop, data->loop);
|
|
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataLoop, data->loop);
|
|
|
|
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_LoopControl, &iface)) < 0) {
|
|
printf("can't get interface %d\n", res);
|
|
return res;
|
|
}
|
|
data->loop_control = iface;
|
|
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_LoopControl, data->loop_control);
|
|
|
|
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_LoopUtils, &iface)) < 0) {
|
|
printf("can't get interface %d\n", res);
|
|
return res;
|
|
}
|
|
data->loop_utils = iface;
|
|
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_LoopUtils, data->loop_utils);
|
|
|
|
/* load varlink */
|
|
if ((res = load_handle(data, &handle,
|
|
"support/libspa-varlink.so",
|
|
SPA_NAME_SUPPORT_VARLINK, NULL)) < 0)
|
|
return res;
|
|
|
|
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Varlink, &iface)) < 0) {
|
|
printf("can't get interface %d\n", res);
|
|
return res;
|
|
}
|
|
data->varlink = iface;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
on_reply(void *userdata, const char *params, const char *error, size_t len, bool continues)
|
|
{
|
|
struct data *data = userdata;
|
|
|
|
if (params) {
|
|
printf("Got reply: params: %.*s, continues: %s\n",
|
|
(int) len, params, continues ? "true" : "false");
|
|
} else {
|
|
printf("Got reply: error: %.*s\n", (int) len, error);
|
|
}
|
|
|
|
data->running = false;
|
|
}
|
|
|
|
static void on_disconnect(void *userdata)
|
|
{
|
|
struct data *data = userdata;
|
|
printf("Disconnected\n");
|
|
data->running = false;
|
|
}
|
|
|
|
static void on_destroy(void *userdata)
|
|
{
|
|
printf("Destroyed\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct data data = { 0 };
|
|
struct spa_varlink_client_events events = {
|
|
.disconnect = on_disconnect,
|
|
.destroy = on_destroy,
|
|
};
|
|
int res;
|
|
bool sync = true;
|
|
|
|
if (argc < 4 || argc > 5) {
|
|
printf("usage: %s <socket> <method> <parameters> <sync: 1|0>\n", argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
res = init(&data);
|
|
if (res < 0)
|
|
return res;
|
|
|
|
if (argc == 5)
|
|
sync = argv[4][0] == '1';
|
|
|
|
data.running = true;
|
|
|
|
data.client = spa_varlink_connect(data.varlink, argv[1]);
|
|
if (data.client == NULL) {
|
|
printf("Could not connect to socket: %s\n", spa_strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
spa_varlink_client_add_listener(data.client, &data.listener, &events, &data);
|
|
|
|
if (sync) {
|
|
char *reply;
|
|
res = spa_varlink_client_call_sync(data.client, argv[2], argv[3], &reply);
|
|
if (res < 0) {
|
|
printf("Call failed: %s\n", spa_strerror(res));
|
|
return -1;
|
|
}
|
|
printf("Got reply (%d): %s\n", res, reply);
|
|
} else {
|
|
res = spa_varlink_client_call(data.client, argv[2], argv[3], false, false, on_reply, &data);
|
|
if (res < 0) {
|
|
printf("Could not connect to socket: %s\n", spa_strerror(res));
|
|
return -1;
|
|
}
|
|
|
|
while (data.running) {
|
|
spa_loop_control_iterate(data.loop_control, 1000);
|
|
}
|
|
}
|
|
|
|
spa_varlink_client_destroy(data.client);
|
|
|
|
return 0;
|
|
}
|