pipewire/src/pipewire/pipewire.c

394 lines
10 KiB
C
Raw Normal View History

2017-05-23 19:15:33 +02:00
/* 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
2017-05-23 19:15:33 +02:00
#include <unistd.h>
#include <limits.h>
2017-05-23 19:15:33 +02:00
#include <stdio.h>
#include <sys/prctl.h>
#include <pwd.h>
#include <errno.h>
#include <dlfcn.h>
2017-05-23 19:15:33 +02:00
#include <spa/support/dbus.h>
2017-07-11 15:57:20 +02:00
#include "pipewire/pipewire.h"
#include "pipewire/private.h"
2017-05-23 19:15:33 +02:00
static char **categories = NULL;
2018-04-13 19:57:34 +02:00
static struct plugin_info {
void *hnd;
spa_handle_factory_enum_func_t enum_func;
struct spa_support support[16];
uint32_t n_support;
} support_info;
static bool
2018-04-13 19:57:34 +02:00
open_plugin(const char *path,
const char *lib,
struct plugin_info *info)
{
char *filename;
2017-07-04 12:21:01 +02:00
if (asprintf(&filename, "%s/%s.so", path, lib) < 0)
goto no_filename;
if ((info->hnd = dlopen(filename, RTLD_NOW)) == NULL) {
fprintf(stderr, "can't load %s: %s\n", filename, dlerror());
goto open_failed;
}
if ((info->enum_func = dlsym(info->hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) {
fprintf(stderr, "can't find enum function\n");
goto no_symbol;
}
free(filename);
return true;
2017-07-04 12:21:01 +02:00
no_filename:
return false;
no_symbol:
dlclose(info->hnd);
open_failed:
free(filename);
return false;
}
2018-04-13 19:57:34 +02:00
static const struct spa_handle_factory *get_factory(struct plugin_info *info, const char *factory_name)
{
int res;
uint32_t index;
const struct spa_handle_factory *factory;
for (index = 0;;) {
if ((res = info->enum_func(&factory, &index)) <= 0) {
if (res != 0)
fprintf(stderr, "can't enumerate factories: %s\n", spa_strerror(res));
break;
}
if (strcmp(factory->name, factory_name) == 0)
return factory;
}
return NULL;
}
static void *
2018-04-13 19:57:34 +02:00
load_interface(struct plugin_info *info,
const char *factory_name,
const char *type)
{
int res;
struct spa_handle *handle;
uint32_t type_id;
const struct spa_handle_factory *factory;
void *iface;
struct spa_type_map *map = NULL;
factory = get_factory(info, factory_name);
if (factory == NULL)
goto not_found;
2018-04-09 10:06:17 +02:00
handle = calloc(1, spa_handle_factory_get_size(factory, NULL));
if ((res = spa_handle_factory_init(factory,
handle, NULL, info->support, info->n_support)) < 0) {
fprintf(stderr, "can't make factory instance: %d\n", res);
goto init_failed;
}
map = pw_get_support_interface(SPA_TYPE__TypeMap);
type_id = map ? spa_type_map_get_id(map, type) : 0;
if ((res = spa_handle_get_interface(handle, type_id, &iface)) < 0) {
fprintf(stderr, "can't get %s interface %d\n", type, res);
goto interface_failed;
}
return iface;
interface_failed:
spa_handle_clear(handle);
init_failed:
free(handle);
not_found:
return NULL;
}
static void configure_debug(const char *str)
{
char **level;
int n_tokens;
level = pw_split_strv(str, ":", INT_MAX, &n_tokens);
if (n_tokens > 0)
pw_log_set_level(atoi(level[0]));
if (n_tokens > 1)
categories = pw_split_strv(level[1], ",", INT_MAX, &n_tokens);
}
/** Get a support interface
* \param type the interface type
* \return the interface or NULL when not configured
*/
void *pw_get_support_interface(const char *type)
{
int i;
for (i = 0; i < support_info.n_support; i++) {
if (strcmp(support_info.support->type, type) == 0)
return support_info.support->data;
}
return NULL;
}
const struct spa_handle_factory *pw_get_support_factory(const char *factory_name)
{
return get_factory(&support_info, factory_name);
}
const struct spa_support *pw_get_support(uint32_t *n_support)
{
*n_support = support_info.n_support;
return support_info.support;
}
void *pw_load_spa_interface(const char *lib, const char *factory_name, const char *type,
struct spa_support *support, uint32_t n_support)
{
2018-04-13 19:57:34 +02:00
struct plugin_info extra_support_info;
const char *str;
int i;
extra_support_info.n_support = support_info.n_support;
memcpy(extra_support_info.support, support_info.support,
sizeof(struct spa_support) * extra_support_info.n_support);
for (i = 0; i < n_support; i++) {
extra_support_info.support[extra_support_info.n_support++] =
SPA_SUPPORT_INIT(support[i].type, support[i].data);
}
if ((str = getenv("SPA_PLUGIN_DIR")) == NULL)
str = PLUGINDIR;
pw_log_debug("load \"%s\", \"%s\"", lib, factory_name);
2018-04-13 19:57:34 +02:00
if (open_plugin(str, lib, &extra_support_info))
return load_interface(&extra_support_info, factory_name, type);
return NULL;
}
void *pw_get_spa_dbus(struct pw_loop *loop)
{
struct spa_support support = SPA_SUPPORT_INIT(SPA_TYPE__LoopUtils, loop->utils);
return pw_load_spa_interface("support/libspa-dbus", "dbus", SPA_TYPE__DBus,
&support, 1);
}
2017-05-30 19:46:51 +02:00
/** Initialize PipeWire
2017-05-23 19:15:33 +02:00
*
2017-05-30 19:46:51 +02:00
* \param argc pointer to argc
* \param argv pointer to argv
*
* Initialize the PipeWire system, parse and modify any parameters given
* by \a argc and \a argv and set up debugging.
*
* The environment variable \a PIPEWIRE_DEBUG
*
* \memberof pw_pipewire
2017-05-23 19:15:33 +02:00
*/
2017-05-26 08:05:01 +02:00
void pw_init(int *argc, char **argv[])
2017-05-23 19:15:33 +02:00
{
2017-05-26 08:05:01 +02:00
const char *str;
void *iface;
2018-04-13 19:57:34 +02:00
struct plugin_info *info = &support_info;
2017-05-23 19:15:33 +02:00
2017-05-26 08:05:01 +02:00
if ((str = getenv("PIPEWIRE_DEBUG")))
configure_debug(str);
if ((str = getenv("SPA_PLUGIN_DIR")) == NULL)
str = PLUGINDIR;
2017-10-17 10:14:56 +02:00
if (support_info.n_support > 0)
return;
2018-04-13 19:57:34 +02:00
if (open_plugin(str, "support/libspa-support", info)) {
iface = load_interface(info, "mapper", SPA_TYPE__TypeMap);
if (iface != NULL)
info->support[info->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE__TypeMap, iface);
iface = load_interface(info, "logger", SPA_TYPE__Log);
if (iface != NULL) {
info->support[info->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE__Log, iface);
pw_log_set(iface);
}
}
}
2017-05-30 19:46:51 +02:00
/** Check if a debug category is enabled
*
* \param name the name of the category to check
* \return true if enabled
*
* Debugging categories can be enabled by using the PIPEWIRE_DEBUG
* environment variable
*
* \memberof pw_pipewire
*/
bool pw_debug_is_category_enabled(const char *name)
{
int i;
if (categories == NULL)
return false;
for (i = 0; categories[i]; i++) {
if (strcmp (categories[i], name) == 0)
return true;
}
return false;
2017-05-23 19:15:33 +02:00
}
2017-05-30 19:46:51 +02:00
/** Get the application name \memberof pw_pipewire */
2017-05-26 08:05:01 +02:00
const char *pw_get_application_name(void)
2017-05-23 19:15:33 +02:00
{
2017-05-26 08:05:01 +02:00
return NULL;
2017-05-23 19:15:33 +02:00
}
2017-05-30 19:46:51 +02:00
/** Get the program name \memberof pw_pipewire */
2017-05-26 08:05:01 +02:00
const char *pw_get_prgname(void)
2017-05-23 19:15:33 +02:00
{
2017-05-26 08:05:01 +02:00
static char tcomm[16 + 1];
spa_zero(tcomm);
2017-05-23 19:15:33 +02:00
2017-05-26 08:05:01 +02:00
if (prctl(PR_GET_NAME, (unsigned long) tcomm, 0, 0, 0) == 0)
return tcomm;
2017-05-23 19:15:33 +02:00
2017-05-26 08:05:01 +02:00
return NULL;
2017-05-23 19:15:33 +02:00
}
2017-05-30 19:46:51 +02:00
/** Get the user name \memberof pw_pipewire */
2017-05-26 08:05:01 +02:00
const char *pw_get_user_name(void)
2017-05-23 19:15:33 +02:00
{
2017-05-26 08:05:01 +02:00
struct passwd *pw;
2017-05-23 19:15:33 +02:00
2017-05-26 08:05:01 +02:00
if ((pw = getpwuid(getuid())))
return pw->pw_name;
2017-05-23 19:15:33 +02:00
2017-05-26 08:05:01 +02:00
return NULL;
2017-05-23 19:15:33 +02:00
}
2017-05-30 19:46:51 +02:00
/** Get the host name \memberof pw_pipewire */
2017-05-26 08:05:01 +02:00
const char *pw_get_host_name(void)
2017-05-23 19:15:33 +02:00
{
2017-05-26 08:05:01 +02:00
static char hname[256];
2017-05-23 19:15:33 +02:00
2017-05-26 08:05:01 +02:00
if (gethostname(hname, 256) < 0)
return NULL;
2017-05-23 19:15:33 +02:00
2017-05-26 08:05:01 +02:00
hname[255] = 0;
return hname;
2017-05-23 19:15:33 +02:00
}
2017-05-30 19:46:51 +02:00
/** Get the client name
2017-05-23 19:15:33 +02:00
*
2017-07-11 15:57:20 +02:00
* Make a new PipeWire client name that can be used to construct a remote.
2017-05-30 19:46:51 +02:00
*
* \memberof pw_pipewire
2017-05-23 19:15:33 +02:00
*/
2017-05-30 19:46:51 +02:00
char *pw_get_client_name(void)
2017-05-23 19:15:33 +02:00
{
2017-05-26 08:05:01 +02:00
char *c;
const char *cc;
if ((cc = pw_get_application_name()))
return strdup(cc);
else if ((cc = pw_get_prgname()))
return strdup(cc);
else {
2017-07-04 12:21:01 +02:00
if (asprintf(&c, "pipewire-pid-%zd", (size_t) getpid()) < 0)
return NULL;
2017-05-26 08:05:01 +02:00
return c;
}
2017-05-23 19:15:33 +02:00
}
2017-07-11 15:57:20 +02:00
/** Fill remote properties
2017-05-30 19:46:51 +02:00
* \param properties a \ref pw_properties
2017-05-23 19:15:33 +02:00
*
2017-07-11 15:57:20 +02:00
* Fill \a properties with a set of default remote properties.
2017-05-30 19:46:51 +02:00
*
* \memberof pw_pipewire
2017-05-23 19:15:33 +02:00
*/
void pw_fill_remote_properties(struct pw_core *core, struct pw_properties *properties)
2017-05-23 19:15:33 +02:00
{
const char *val;
2017-05-26 08:05:01 +02:00
if (!pw_properties_get(properties, "application.name"))
pw_properties_set(properties, "application.name", pw_get_application_name());
if (!pw_properties_get(properties, "application.prgname"))
pw_properties_set(properties, "application.prgname", pw_get_prgname());
if (!pw_properties_get(properties, "application.language")) {
pw_properties_set(properties, "application.language", getenv("LANG"));
}
if (!pw_properties_get(properties, "application.process.id")) {
pw_properties_setf(properties, "application.process.id", "%zd", (size_t) getpid());
}
if (!pw_properties_get(properties, "application.process.user"))
pw_properties_set(properties, "application.process.user", pw_get_user_name());
if (!pw_properties_get(properties, "application.process.host"))
pw_properties_set(properties, "application.process.host", pw_get_host_name());
if (!pw_properties_get(properties, "application.process.session_id")) {
pw_properties_set(properties, "application.process.session_id",
getenv("XDG_SESSION_ID"));
}
pw_properties_set(properties, PW_CORE_PROP_VERSION, core->info.version);
pw_properties_set(properties, PW_CORE_PROP_NAME, core->info.name);
if ((val = pw_properties_get(core->properties, PW_CORE_PROP_DAEMON)))
pw_properties_set(properties, PW_CORE_PROP_DAEMON, val);
2017-05-23 19:15:33 +02:00
}
2017-05-30 19:46:51 +02:00
/** Fill stream properties
* \param properties a \ref pw_properties
*
* Fill \a properties with a set of default stream properties.
2017-05-23 19:15:33 +02:00
*
2017-05-30 19:46:51 +02:00
* \memberof pw_pipewire
2017-05-23 19:15:33 +02:00
*/
void pw_fill_stream_properties(struct pw_core *core, struct pw_properties *properties)
2017-05-23 19:15:33 +02:00
{
}
2017-05-30 19:46:51 +02:00
/** Reverse the direction \memberof pw_pipewire */
2017-05-26 08:05:01 +02:00
enum pw_direction pw_direction_reverse(enum pw_direction direction)
2017-05-23 19:15:33 +02:00
{
2017-05-26 08:05:01 +02:00
if (direction == PW_DIRECTION_INPUT)
return PW_DIRECTION_OUTPUT;
else if (direction == PW_DIRECTION_OUTPUT)
return PW_DIRECTION_INPUT;
return direction;
2017-05-23 19:15:33 +02:00
}