mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-18 08:56:45 -05:00
module-netjack2: small improvements
This commit is contained in:
parent
52bc7451fd
commit
f8aa18c88b
2 changed files with 86 additions and 79 deletions
|
|
@ -33,7 +33,6 @@
|
||||||
#include <pipewire/impl.h>
|
#include <pipewire/impl.h>
|
||||||
#include <pipewire/i18n.h>
|
#include <pipewire/i18n.h>
|
||||||
#include <pipewire/private.h>
|
#include <pipewire/private.h>
|
||||||
#include <pipewire/thread.h>
|
|
||||||
|
|
||||||
#include "module-netjack2/packets.h"
|
#include "module-netjack2/packets.h"
|
||||||
|
|
||||||
|
|
@ -49,7 +48,7 @@
|
||||||
*
|
*
|
||||||
* ## Module Options
|
* ## Module Options
|
||||||
*
|
*
|
||||||
* - `driver.mode`: the tunnel mode, sink|source|duplex, default duplex
|
* - `driver.mode`: the driver mode, sink|source|duplex, default duplex
|
||||||
* - `jack.client-name`: the name of the JACK client.
|
* - `jack.client-name`: the name of the JACK client.
|
||||||
* - `local.ifname = <str>`: interface name to use
|
* - `local.ifname = <str>`: interface name to use
|
||||||
* - `net.ip =<str>`: multicast IP address, default "225.3.19.154"
|
* - `net.ip =<str>`: multicast IP address, default "225.3.19.154"
|
||||||
|
|
@ -82,7 +81,7 @@
|
||||||
*
|
*
|
||||||
*\code{.unparsed}
|
*\code{.unparsed}
|
||||||
* context.modules = [
|
* context.modules = [
|
||||||
* { name = libpipewire-module-jack-tunnel
|
* { name = libpipewire-module-netjack2-driver
|
||||||
* args = {
|
* args = {
|
||||||
* #driver.mode = duplex
|
* #driver.mode = duplex
|
||||||
* #jack.client-name = PipeWire
|
* #jack.client-name = PipeWire
|
||||||
|
|
@ -125,8 +124,6 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
|
||||||
#define DEFAULT_POSITION "[ FL FR ]"
|
#define DEFAULT_POSITION "[ FL FR ]"
|
||||||
#define DEFAULT_MIDI_PORTS 1
|
#define DEFAULT_MIDI_PORTS 1
|
||||||
|
|
||||||
#define NETWORK_PROTOCOL 8
|
|
||||||
|
|
||||||
#define FOLLOWER_INIT_TIMEOUT 1
|
#define FOLLOWER_INIT_TIMEOUT 1
|
||||||
#define FOLLOWER_INIT_RETRY 5
|
#define FOLLOWER_INIT_RETRY 5
|
||||||
|
|
||||||
|
|
@ -201,8 +198,12 @@ struct impl {
|
||||||
uint32_t mode;
|
uint32_t mode;
|
||||||
struct pw_properties *props;
|
struct pw_properties *props;
|
||||||
|
|
||||||
struct pw_impl_module *module;
|
bool loop;
|
||||||
|
int ttl;
|
||||||
|
int dscp;
|
||||||
|
int mtu;
|
||||||
|
|
||||||
|
struct pw_impl_module *module;
|
||||||
struct spa_hook module_listener;
|
struct spa_hook module_listener;
|
||||||
|
|
||||||
struct pw_core *core;
|
struct pw_core *core;
|
||||||
|
|
@ -220,8 +221,6 @@ struct impl {
|
||||||
uint32_t pw_xrun;
|
uint32_t pw_xrun;
|
||||||
uint32_t nj2_xrun;
|
uint32_t nj2_xrun;
|
||||||
|
|
||||||
pthread_t thread;
|
|
||||||
|
|
||||||
struct sockaddr_storage dst_addr;
|
struct sockaddr_storage dst_addr;
|
||||||
socklen_t dst_len;
|
socklen_t dst_len;
|
||||||
struct sockaddr_storage src_addr;
|
struct sockaddr_storage src_addr;
|
||||||
|
|
@ -368,11 +367,13 @@ static int32_t netjack2_sync_wait(struct impl *impl)
|
||||||
if (len < (ssize_t)sizeof(*sync))
|
if (len < (ssize_t)sizeof(*sync))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
//nj2_dump_packet_header(sync);
|
||||||
|
|
||||||
if (strcmp(sync->type, "header") != 0)
|
if (strcmp(sync->type, "header") != 0)
|
||||||
continue;
|
continue;
|
||||||
if (ntohl(sync->data_type) != 's')
|
if (ntohl(sync->data_type) != 's' ||
|
||||||
continue;
|
ntohl(sync->data_stream) != 's' ||
|
||||||
if (ntohl(sync->data_stream) != 's')
|
ntohl(sync->id) != impl->params.id)
|
||||||
continue;
|
continue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -636,12 +637,13 @@ static int netjack2_recv_data(struct stream *s, struct data_info *info, uint32_t
|
||||||
if (len < (ssize_t)sizeof(*header))
|
if (len < (ssize_t)sizeof(*header))
|
||||||
goto receive_error;
|
goto receive_error;
|
||||||
|
|
||||||
|
//nj2_dump_packet_header(header);
|
||||||
|
|
||||||
if (ntohl(header->data_stream) != 's' ||
|
if (ntohl(header->data_stream) != 's' ||
|
||||||
ntohl(header->id) != impl->params.id) {
|
ntohl(header->id) != impl->params.id) {
|
||||||
pw_log_debug("not our packet");
|
pw_log_debug("not our packet");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//nj2_dump_packet_header(header);
|
|
||||||
|
|
||||||
switch (ntohl(header->data_type)) {
|
switch (ntohl(header->data_type)) {
|
||||||
case 'm':
|
case 'm':
|
||||||
|
|
@ -1088,7 +1090,6 @@ static int make_socket(struct sockaddr_storage *src, socklen_t src_len,
|
||||||
if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) < 0)
|
if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof(val)) < 0)
|
||||||
pw_log_warn("setsockopt(IP_TOS) failed: %m");
|
pw_log_warn("setsockopt(IP_TOS) failed: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bind(fd, (struct sockaddr*)src, src_len) < 0) {
|
if (bind(fd, (struct sockaddr*)src, src_len) < 0) {
|
||||||
res = -errno;
|
res = -errno;
|
||||||
pw_log_error("bind() failed: %m");
|
pw_log_error("bind() failed: %m");
|
||||||
|
|
@ -1110,7 +1111,7 @@ error:
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_ip(const struct sockaddr_storage *sa, char *ip, size_t len)
|
static const char *get_ip(const struct sockaddr_storage *sa, char *ip, size_t len)
|
||||||
{
|
{
|
||||||
if (sa->ss_family == AF_INET) {
|
if (sa->ss_family == AF_INET) {
|
||||||
struct sockaddr_in *in = (struct sockaddr_in*)sa;
|
struct sockaddr_in *in = (struct sockaddr_in*)sa;
|
||||||
|
|
@ -1119,8 +1120,8 @@ static int get_ip(const struct sockaddr_storage *sa, char *ip, size_t len)
|
||||||
struct sockaddr_in6 *in = (struct sockaddr_in6*)sa;
|
struct sockaddr_in6 *in = (struct sockaddr_in6*)sa;
|
||||||
inet_ntop(sa->ss_family, &in->sin6_addr, ip, len);
|
inet_ntop(sa->ss_family, &in->sin6_addr, ip, len);
|
||||||
} else
|
} else
|
||||||
return -EIO;
|
snprintf(ip, len, "invalid ip");
|
||||||
return 0;
|
return ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_timer(struct impl *impl, uint64_t timeout)
|
static void update_timer(struct impl *impl, uint64_t timeout)
|
||||||
|
|
@ -1159,6 +1160,17 @@ static int handle_follower_setup(struct impl *impl, struct nj2_session_params *p
|
||||||
impl->params.follower_sync_mode = ntohl(params->follower_sync_mode);
|
impl->params.follower_sync_mode = ntohl(params->follower_sync_mode);
|
||||||
impl->params.network_latency = ntohl(params->network_latency);
|
impl->params.network_latency = ntohl(params->network_latency);
|
||||||
|
|
||||||
|
if (impl->params.send_audio_channels < 0 ||
|
||||||
|
impl->params.recv_audio_channels < 0 ||
|
||||||
|
impl->params.send_midi_channels < 0 ||
|
||||||
|
impl->params.recv_midi_channels < 0 ||
|
||||||
|
impl->params.sample_rate == 0 ||
|
||||||
|
impl->params.period_size == 0 ||
|
||||||
|
impl->params.sample_encoder != NJ2_ENCODER_FLOAT) {
|
||||||
|
pw_log_warn("invalid follower setup");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
update_timer(impl, 0);
|
update_timer(impl, 0);
|
||||||
pw_loop_update_io(impl->main_loop, impl->setup_socket, 0);
|
pw_loop_update_io(impl->main_loop, impl->setup_socket, 0);
|
||||||
|
|
||||||
|
|
@ -1194,62 +1206,76 @@ connect_error:
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_packet(struct impl *impl, void *data, int len,
|
|
||||||
struct sockaddr_storage *addr, socklen_t addr_len)
|
|
||||||
{
|
|
||||||
struct nj2_session_params *params;
|
|
||||||
|
|
||||||
if (len < (int)sizeof(struct nj2_session_params))
|
|
||||||
goto short_packet;
|
|
||||||
|
|
||||||
params = data;
|
|
||||||
if (strcmp(params->type, impl->params.type) != 0) {
|
|
||||||
pw_log_info("wrong packet type '%s' != '%s'",
|
|
||||||
params->type, impl->params.type);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
if (ntohl(params->packet_id) != NJ2_ID_FOLLOWER_SETUP) {
|
|
||||||
pw_log_info("wrong packet id %d != %d",
|
|
||||||
htonl(params->packet_id), NJ2_ID_FOLLOWER_SETUP);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
return handle_follower_setup(impl, data, addr, addr_len);
|
|
||||||
|
|
||||||
short_packet:
|
|
||||||
pw_log_warn("short packet received");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_socket_io(void *data, int fd, uint32_t mask)
|
on_socket_io(void *data, int fd, uint32_t mask)
|
||||||
{
|
{
|
||||||
struct impl *impl = data;
|
struct impl *impl = data;
|
||||||
ssize_t len;
|
|
||||||
uint8_t buffer[2048];
|
|
||||||
struct sockaddr_storage addr;
|
|
||||||
socklen_t addr_len;
|
|
||||||
|
|
||||||
if (mask & SPA_IO_IN) {
|
if (mask & SPA_IO_IN) {
|
||||||
if ((len = recvfrom(fd, buffer, sizeof(buffer), 0,
|
struct sockaddr_storage addr;
|
||||||
|
socklen_t addr_len = sizeof(addr);
|
||||||
|
ssize_t len;
|
||||||
|
struct nj2_session_params params;
|
||||||
|
|
||||||
|
if ((len = recvfrom(fd, ¶ms, sizeof(params), 0,
|
||||||
(struct sockaddr *)&addr, &addr_len)) < 0)
|
(struct sockaddr *)&addr, &addr_len)) < 0)
|
||||||
goto receive_error;
|
goto receive_error;
|
||||||
|
|
||||||
handle_packet(impl, (void *)buffer, len, &addr, addr_len);
|
if (len < (int)sizeof(struct nj2_session_params))
|
||||||
|
goto short_packet;
|
||||||
|
|
||||||
|
if (strcmp(params.type, "params") != 0)
|
||||||
|
goto wrong_type;
|
||||||
|
|
||||||
|
switch(ntohl(params.packet_id)) {
|
||||||
|
case NJ2_ID_FOLLOWER_SETUP:
|
||||||
|
handle_follower_setup(impl, ¶ms, &addr, addr_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
receive_error:
|
receive_error:
|
||||||
pw_log_warn("recv error: %m");
|
pw_log_warn("recv error: %m");
|
||||||
return;
|
return;
|
||||||
|
short_packet:
|
||||||
|
pw_log_warn("short packet received");
|
||||||
|
return;
|
||||||
|
wrong_type:
|
||||||
|
pw_log_warn("wrong packet type received");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int send_follower_available(struct impl *impl)
|
static int send_follower_available(struct impl *impl)
|
||||||
{
|
{
|
||||||
|
char buffer[256];
|
||||||
|
struct nj2_session_params params;
|
||||||
|
const char *client_name;
|
||||||
|
|
||||||
pw_loop_update_io(impl->main_loop, impl->setup_socket, SPA_IO_IN);
|
pw_loop_update_io(impl->main_loop, impl->setup_socket, SPA_IO_IN);
|
||||||
|
|
||||||
pw_log_info("sending AVAILABLE");
|
pw_log_info("sending AVAILABLE to %s", get_ip(&impl->dst_addr, buffer, sizeof(buffer)));
|
||||||
impl->params.packet_id = htonl(NJ2_ID_FOLLOWER_AVAILABLE);
|
|
||||||
sendto(impl->setup_socket->fd, &impl->params, sizeof(impl->params), 0,
|
client_name = pw_properties_get(impl->props, "jack.client-name");
|
||||||
|
if (client_name == NULL)
|
||||||
|
client_name = DEFAULT_CLIENT_NAME;
|
||||||
|
|
||||||
|
spa_zero(params);
|
||||||
|
strcpy(params.type, "params");
|
||||||
|
params.version = htonl(NJ2_NETWORK_PROTOCOL);
|
||||||
|
params.packet_id = htonl(NJ2_ID_FOLLOWER_AVAILABLE);
|
||||||
|
snprintf(params.name, sizeof(params.name), "%s", client_name);
|
||||||
|
snprintf(params.follower_name, sizeof(params.follower_name), "%s", pw_get_host_name());
|
||||||
|
params.mtu = htonl(impl->mtu);
|
||||||
|
params.transport_sync = htonl(0);
|
||||||
|
params.send_audio_channels = htonl(-1);
|
||||||
|
params.recv_audio_channels = htonl(-1);
|
||||||
|
params.send_midi_channels = htonl(-1);
|
||||||
|
params.recv_midi_channels = htonl(-1);
|
||||||
|
params.sample_encoder = htonl(NJ2_ENCODER_FLOAT);
|
||||||
|
params.follower_sync_mode = htonl(1);
|
||||||
|
params.network_latency = htonl(NETWORK_DEFAULT_LATENCY);
|
||||||
|
sendto(impl->setup_socket->fd, ¶ms, sizeof(params), 0,
|
||||||
(struct sockaddr*)&impl->dst_addr, impl->dst_len);
|
(struct sockaddr*)&impl->dst_addr, impl->dst_len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1268,11 +1294,9 @@ static void on_timer_event(void *data, uint64_t expirations)
|
||||||
|
|
||||||
static int create_netjack2_socket(struct impl *impl)
|
static int create_netjack2_socket(struct impl *impl)
|
||||||
{
|
{
|
||||||
const char *client_name, *str;
|
const char *str;
|
||||||
uint32_t port;
|
uint32_t port;
|
||||||
int fd, res;
|
int fd, res;
|
||||||
bool loop;
|
|
||||||
int ttl, dscp, mtu;
|
|
||||||
|
|
||||||
port = pw_properties_get_uint32(impl->props, "net.port", 0);
|
port = pw_properties_get_uint32(impl->props, "net.port", 0);
|
||||||
if (port == 0)
|
if (port == 0)
|
||||||
|
|
@ -1288,13 +1312,13 @@ static int create_netjack2_socket(struct impl *impl)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
mtu = pw_properties_get_uint32(impl->props, "net.mtu", DEFAULT_NET_MTU);
|
impl->mtu = pw_properties_get_uint32(impl->props, "net.mtu", DEFAULT_NET_MTU);
|
||||||
ttl = pw_properties_get_uint32(impl->props, "net.ttl", DEFAULT_NET_TTL);
|
impl->ttl = pw_properties_get_uint32(impl->props, "net.ttl", DEFAULT_NET_TTL);
|
||||||
loop = pw_properties_get_bool(impl->props, "net.loop", DEFAULT_NET_LOOP);
|
impl->loop = pw_properties_get_bool(impl->props, "net.loop", DEFAULT_NET_LOOP);
|
||||||
dscp = pw_properties_get_uint32(impl->props, "net.dscp", DEFAULT_NET_DSCP);
|
impl->dscp = pw_properties_get_uint32(impl->props, "net.dscp", DEFAULT_NET_DSCP);
|
||||||
|
|
||||||
fd = make_socket(&impl->src_addr, impl->src_len,
|
fd = make_socket(&impl->src_addr, impl->src_len,
|
||||||
&impl->dst_addr, impl->dst_len, loop, ttl, dscp);
|
&impl->dst_addr, impl->dst_len, impl->loop, impl->ttl, impl->dscp);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
res = -errno;
|
res = -errno;
|
||||||
pw_log_error("can't create socket: %s", spa_strerror(res));
|
pw_log_error("can't create socket: %s", spa_strerror(res));
|
||||||
|
|
@ -1326,24 +1350,6 @@ static int create_netjack2_socket(struct impl *impl)
|
||||||
}
|
}
|
||||||
update_timer(impl, FOLLOWER_INIT_TIMEOUT);
|
update_timer(impl, FOLLOWER_INIT_TIMEOUT);
|
||||||
|
|
||||||
client_name = pw_properties_get(impl->props, "jack.client-name");
|
|
||||||
if (client_name == NULL)
|
|
||||||
client_name = DEFAULT_CLIENT_NAME;
|
|
||||||
|
|
||||||
strcpy(impl->params.type, "params");
|
|
||||||
impl->params.version = htonl(NETWORK_PROTOCOL);
|
|
||||||
snprintf(impl->params.name, sizeof(impl->params.name), "%s", client_name);
|
|
||||||
impl->params.mtu = htonl(mtu);
|
|
||||||
impl->params.sample_encoder = htonl(NJ2_ENCODER_FLOAT);
|
|
||||||
get_ip(&impl->src_addr, impl->params.follower_name, sizeof(impl->params.follower_name));
|
|
||||||
impl->params.transport_sync = htonl(0);
|
|
||||||
impl->params.network_latency = htonl(NETWORK_DEFAULT_LATENCY);
|
|
||||||
impl->params.follower_sync_mode = htonl(1);
|
|
||||||
impl->params.send_audio_channels = htonl(-1);
|
|
||||||
impl->params.recv_audio_channels = htonl(-1);
|
|
||||||
impl->params.send_midi_channels = htonl(-1);
|
|
||||||
impl->params.recv_midi_channels = htonl(-1);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
out:
|
out:
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -1535,7 +1541,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
||||||
impl->sink.direction = PW_DIRECTION_INPUT;
|
impl->sink.direction = PW_DIRECTION_INPUT;
|
||||||
|
|
||||||
impl->mode = MODE_DUPLEX;
|
impl->mode = MODE_DUPLEX;
|
||||||
if ((str = pw_properties_get(props, "tunnel.mode")) != NULL) {
|
if ((str = pw_properties_get(props, "driver.mode")) != NULL) {
|
||||||
if (spa_streq(str, "source")) {
|
if (spa_streq(str, "source")) {
|
||||||
impl->mode = MODE_SOURCE;
|
impl->mode = MODE_SOURCE;
|
||||||
} else if (spa_streq(str, "sink")) {
|
} else if (spa_streq(str, "sink")) {
|
||||||
|
|
@ -1543,7 +1549,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
||||||
} else if (spa_streq(str, "duplex")) {
|
} else if (spa_streq(str, "duplex")) {
|
||||||
impl->mode = MODE_DUPLEX;
|
impl->mode = MODE_DUPLEX;
|
||||||
} else {
|
} else {
|
||||||
pw_log_error("invalid tunnel.mode '%s'", str);
|
pw_log_error("invalid driver.mode '%s'", str);
|
||||||
res = -EINVAL;
|
res = -EINVAL;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ extern "C" {
|
||||||
|
|
||||||
struct nj2_session_params {
|
struct nj2_session_params {
|
||||||
char type[8]; /* packet type ('param') */
|
char type[8]; /* packet type ('param') */
|
||||||
|
#define NJ2_NETWORK_PROTOCOL 8
|
||||||
uint32_t version; /* version */
|
uint32_t version; /* version */
|
||||||
#define NJ2_ID_FOLLOWER_AVAILABLE 0 /* a follower is available */
|
#define NJ2_ID_FOLLOWER_AVAILABLE 0 /* a follower is available */
|
||||||
#define NJ2_ID_FOLLOWER_SETUP 1 /* follower configuration */
|
#define NJ2_ID_FOLLOWER_SETUP 1 /* follower configuration */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue