modules: add more docs and options to pipe-tunnel

This commit is contained in:
Wim Taymans 2022-06-03 10:30:19 +02:00
parent af41d45e1a
commit fe1652e843

View file

@ -59,11 +59,26 @@
* *
* ## Module Options * ## Module Options
* *
* - `tunnel.mode`: the desired tunnel to create, must be `capture` or `playback`. * - `tunnel.mode`: the desired tunnel to create. (Default `playback`)
* (Default `playback`)
* - `pipe.filename`: the filename of the pipe. * - `pipe.filename`: the filename of the pipe.
* - `stream.props`: Extra properties for the local stream. * - `stream.props`: Extra properties for the local stream.
* *
* When `tunnel.mode` is `capture`, a capture stream on the default source is
* created. Samples read from the pipe will be the contents of the captured source.
*
* When `tunnel.mode` is `sink`, a sink node is created. Samples read from the
* pipe will be the samples played on the sink.
*
* When `tunnel.mode` is `playback`, a playback stream on the default sink is
* created. Samples written to the pipe will be played on the sink.
*
* When `tunnel.mode` is `source`, a source node is created. Samples written to
* the pipe will be made available to streams connected to the source.
*
* When `pipe.filename` is not given, a default fifo in `/tmp/fifo_input` or
* `/tmp/fifo_output` will be created that can be written and read respectively,
* depending on the selected `tunnel.mode`.
*
* ## General options * ## General options
* *
* Options with well-known behavior. * Options with well-known behavior.
@ -80,7 +95,10 @@
* - \ref PW_KEY_MEDIA_CLASS * - \ref PW_KEY_MEDIA_CLASS
* - \ref PW_KEY_NODE_TARGET to specify the remote name or id to link to * - \ref PW_KEY_NODE_TARGET to specify the remote name or id to link to
* *
* ## Example configuration of a virtual pipe sink * When not otherwise specified, the pipe will accept or produce a
* 16 bits, stereo, 48KHz sample stream.
*
* ## Example configuration of a pipe playback stream
* *
*\code{.unparsed} *\code{.unparsed}
* context.modules = [ * context.modules = [
@ -118,8 +136,8 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
"[ audio.rate=<sample rate> ] " \ "[ audio.rate=<sample rate> ] " \
"[ audio.channels=<number of channels> ] " \ "[ audio.channels=<number of channels> ] " \
"[ audio.position=<channel map> ] " \ "[ audio.position=<channel map> ] " \
"[ tunnel.mode=capture|playback " \ "[ tunnel.mode=capture|playback|sink|source " \
"pipe.filename=<filename> " \ "[ pipe.filename=<filename> ]" \
"[ stream.props=<properties> ] " "[ stream.props=<properties> ] "
@ -135,6 +153,8 @@ struct impl {
#define MODE_PLAYBACK 0 #define MODE_PLAYBACK 0
#define MODE_CAPTURE 1 #define MODE_CAPTURE 1
#define MODE_SINK 2
#define MODE_SOURCE 3
uint32_t mode; uint32_t mode;
struct pw_properties *props; struct pw_properties *props;
@ -151,6 +171,7 @@ struct impl {
int fd; int fd;
struct pw_properties *stream_props; struct pw_properties *stream_props;
enum pw_direction direction;
struct pw_stream *stream; struct pw_stream *stream;
struct spa_hook stream_listener; struct spa_hook stream_listener;
struct spa_audio_info_raw info; struct spa_audio_info_raw info;
@ -299,7 +320,7 @@ static int create_stream(struct impl *impl)
if (impl->stream == NULL) if (impl->stream == NULL)
return -errno; return -errno;
if (impl->mode == MODE_CAPTURE) { if (impl->direction == PW_DIRECTION_OUTPUT) {
pw_stream_add_listener(impl->stream, pw_stream_add_listener(impl->stream,
&impl->stream_listener, &impl->stream_listener,
&capture_stream_events, impl); &capture_stream_events, impl);
@ -315,7 +336,7 @@ static int create_stream(struct impl *impl)
SPA_PARAM_EnumFormat, &impl->info); SPA_PARAM_EnumFormat, &impl->info);
if ((res = pw_stream_connect(impl->stream, if ((res = pw_stream_connect(impl->stream,
impl->mode == MODE_CAPTURE ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT, impl->direction,
PW_ID_ANY, PW_ID_ANY,
PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_MAP_BUFFERS |
@ -334,7 +355,7 @@ static int create_fifo(struct impl *impl)
int fd = -1, res; int fd = -1, res;
if ((filename = pw_properties_get(impl->props, "pipe.filename")) == NULL) if ((filename = pw_properties_get(impl->props, "pipe.filename")) == NULL)
filename = impl->mode == MODE_CAPTURE ? filename = impl->direction == PW_DIRECTION_INPUT ?
DEFAULT_CAPTURE_FILENAME : DEFAULT_CAPTURE_FILENAME :
DEFAULT_PLAYBACK_FILENAME; DEFAULT_PLAYBACK_FILENAME;
@ -372,6 +393,8 @@ static int create_fifo(struct impl *impl)
pw_log_error("'%s' is not a FIFO.", filename); pw_log_error("'%s' is not a FIFO.", filename);
goto error; goto error;
} }
pw_log_info("opened fifo '%s' for %s", filename,
impl->direction == PW_DIRECTION_INPUT ? "writing" : "reading");
impl->filename = strdup(filename); impl->filename = strdup(filename);
impl->unlink_fifo = do_unlink_fifo; impl->unlink_fifo = do_unlink_fifo;
impl->fd = fd; impl->fd = fd;
@ -556,7 +579,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
struct pw_context *context = pw_impl_module_get_context(module); struct pw_context *context = pw_impl_module_get_context(module);
struct pw_properties *props = NULL; struct pw_properties *props = NULL;
struct impl *impl; struct impl *impl;
const char *str; const char *str, *media_class = NULL;
int res; int res;
PW_LOG_TOPIC_INIT(mod_topic); PW_LOG_TOPIC_INIT(mod_topic);
@ -565,6 +588,8 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
if (impl == NULL) if (impl == NULL)
return -errno; return -errno;
impl->fd = -1;
pw_log_debug("module %p: new %s", impl, args); pw_log_debug("module %p: new %s", impl, args);
if (args == NULL) if (args == NULL)
@ -588,25 +613,33 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
impl->module = module; impl->module = module;
impl->context = context; impl->context = context;
if ((str = pw_properties_get(props, "tunnel.mode")) != NULL) { if ((str = pw_properties_get(props, "tunnel.mode")) == NULL)
if (spa_streq(str, "capture")) { str = "playback";
impl->mode = MODE_CAPTURE;
} else if (spa_streq(str, "playback")) { if (spa_streq(str, "capture")) {
impl->mode = MODE_PLAYBACK; impl->mode = MODE_CAPTURE;
} else { impl->direction = PW_DIRECTION_INPUT;
pw_log_error("invalid tunnel.mode '%s'", str); } else if (spa_streq(str, "playback")) {
res = -EINVAL; impl->mode = MODE_PLAYBACK;
goto error; impl->direction = PW_DIRECTION_OUTPUT;
} }else if (spa_streq(str, "sink")) {
impl->mode = MODE_SINK;
impl->direction = PW_DIRECTION_INPUT;
media_class = "Audio/Sink";
} else if (spa_streq(str, "source")) {
impl->mode = MODE_SOURCE;
impl->direction = PW_DIRECTION_OUTPUT;
media_class = "Audio/Source";
} else {
pw_log_error("invalid tunnel.mode '%s'", str);
res = -EINVAL;
goto error;
} }
if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL) if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL)
pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true"); pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
if (pw_properties_get(props, PW_KEY_MEDIA_CLASS) == NULL) if (pw_properties_get(props, PW_KEY_MEDIA_CLASS) == NULL)
pw_properties_set(props, PW_KEY_MEDIA_CLASS, pw_properties_set(props, PW_KEY_MEDIA_CLASS, media_class);
impl->mode == MODE_PLAYBACK ?
"Audio/Sink" : "Audio/Source");
if ((str = pw_properties_get(props, "stream.props")) != NULL) if ((str = pw_properties_get(props, "stream.props")) != NULL)
pw_properties_update_string(impl->stream_props, str, strlen(str)); pw_properties_update_string(impl->stream_props, str, strlen(str));