mirror of https://github.com/wb2osz/direwolf.git
Add UDP audio output for Linux
This commit is contained in:
parent
74d6cc6bc2
commit
3b6a105b5a
116
src/audio.c
116
src/audio.c
|
@ -75,6 +75,7 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,7 +132,9 @@ static struct adev_s {
|
||||||
enum audio_in_type_e g_audio_in_type;
|
enum audio_in_type_e g_audio_in_type;
|
||||||
enum audio_out_type_e g_audio_out_type;
|
enum audio_out_type_e g_audio_out_type;
|
||||||
|
|
||||||
int udp_sock; /* UDP socket for receiving data */
|
int udp_in_sock; /* UDP socket for receiving data */
|
||||||
|
int udp_out_sock; /* UDP socket for sending data */
|
||||||
|
struct sockaddr_storage udp_dest_addr; /* Destination address for UDP socket sending */
|
||||||
|
|
||||||
} adev[MAX_ADEVS];
|
} adev[MAX_ADEVS];
|
||||||
|
|
||||||
|
@ -239,7 +242,7 @@ int audio_open (struct audio_s *pa)
|
||||||
#else
|
#else
|
||||||
adev[a].oss_audio_device_fd = -1;
|
adev[a].oss_audio_device_fd = -1;
|
||||||
#endif
|
#endif
|
||||||
adev[a].udp_sock = -1;
|
adev[a].udp_in_sock = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -413,7 +416,7 @@ int audio_open (struct audio_s *pa)
|
||||||
//int data_size = 0;
|
//int data_size = 0;
|
||||||
|
|
||||||
//Create UDP Socket
|
//Create UDP Socket
|
||||||
if ((adev[a].udp_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
|
if ((adev[a].udp_in_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Couldn't create socket, errno %d\n", errno);
|
dw_printf ("Couldn't create socket, errno %d\n", errno);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -425,7 +428,7 @@ int audio_open (struct audio_s *pa)
|
||||||
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
|
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
|
||||||
//Bind to the socket
|
//Bind to the socket
|
||||||
if (bind(adev[a].udp_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) {
|
if (bind(adev[a].udp_in_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Couldn't bind socket, errno %d\n", errno);
|
dw_printf ("Couldn't bind socket, errno %d\n", errno);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -454,7 +457,7 @@ int audio_open (struct audio_s *pa)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Output device. Only "soundcard" and "stdout" are supported at this time.
|
* Output device. Soundcard, stdout, and UDP are supported at this time.
|
||||||
*/
|
*/
|
||||||
if (strcasecmp(pa->adev[a].adevice_out, "stdout") == 0 || strcmp(pa->adev[a].adevice_out, "-") == 0) {
|
if (strcasecmp(pa->adev[a].adevice_out, "stdout") == 0 || strcmp(pa->adev[a].adevice_out, "-") == 0) {
|
||||||
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_STDOUT;
|
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_STDOUT;
|
||||||
|
@ -463,6 +466,16 @@ int audio_open (struct audio_s *pa)
|
||||||
dw_printf ("stdout must only be used with the -O option\n");
|
dw_printf ("stdout must only be used with the -O option\n");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
} else if (strncasecmp(pa->adev[a].adevice_out, "udp:", 4) == 0) {
|
||||||
|
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SDR_UDP;
|
||||||
|
/* User must supply address and port */
|
||||||
|
if (strcasecmp(pa->adev[a].adevice_out,"udp:") == 0 ||
|
||||||
|
strlen(pa->adev[a].adevice_out) < 7 ||
|
||||||
|
strstr(pa->adev[a].adevice_out+5, ":") == 0) {
|
||||||
|
text_color_set (DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Destination address and port must be supplied for UDP output\n");
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD;
|
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD;
|
||||||
}
|
}
|
||||||
|
@ -514,7 +527,63 @@ int audio_open (struct audio_s *pa)
|
||||||
audio_out_name);
|
audio_out_name);
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case AUDIO_OUT_TYPE_SDR_UDP:;
|
||||||
|
|
||||||
|
struct addrinfo ai_out;
|
||||||
|
struct addrinfo *ai_res;
|
||||||
|
char udp_outhost[256];
|
||||||
|
char *udp_outport;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
// Initialize structure for addrinfo restrictions
|
||||||
|
memset((char *) &ai_out, 0, sizeof(ai_out));
|
||||||
|
ai_out.ai_socktype = SOCK_DGRAM;
|
||||||
|
ai_out.ai_protocol = IPPROTO_UDP;
|
||||||
|
|
||||||
|
// Separate out the host and port strings
|
||||||
|
strncpy(udp_outhost, pa->adev[a].adevice_out+4, 255);
|
||||||
|
udp_outhost[255] = 0;
|
||||||
|
udp_outport = strstr(udp_outhost,":");
|
||||||
|
*udp_outport++ = 0;
|
||||||
|
|
||||||
|
if (strlen(udp_outport) == 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("UDP output destination port must be supplied\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the sockaddr to represent the host/port provided
|
||||||
|
res = getaddrinfo(udp_outhost, udp_outport, &ai_out, &ai_res);
|
||||||
|
if (res != 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Error parsing/resolving UDP output address\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv4 and IPv6 structs are different sizes
|
||||||
|
if (ai_res->ai_family == AF_INET6) {
|
||||||
|
res = sizeof(struct sockaddr_in6);
|
||||||
|
} else {
|
||||||
|
res = sizeof(struct sockaddr_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create UDP Socket for the right address family
|
||||||
|
if ((adev[a].udp_out_sock=socket(ai_res->ai_family, SOCK_DGRAM, IPPROTO_UDP))==-1) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Couldn't create socket, errno %d\n", errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save sockaddr needed later to send the data and set buffer size
|
||||||
|
memcpy(&adev[a].udp_dest_addr, ai_res->ai_addr, res);
|
||||||
|
adev[a].outbuf_size_in_bytes = SDR_UDP_BUF_MAXLEN;
|
||||||
|
freeaddrinfo(ai_res);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Finally allocate buffer for each direction.
|
* Finally allocate buffer for each direction.
|
||||||
|
@ -1222,8 +1291,8 @@ int audio_get (int a)
|
||||||
while (adev[a].inbuf_next >= adev[a].inbuf_len) {
|
while (adev[a].inbuf_next >= adev[a].inbuf_len) {
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
assert (adev[a].udp_sock > 0);
|
assert (adev[a].udp_in_sock > 0);
|
||||||
res = recv(adev[a].udp_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
|
res = recv(adev[a].udp_in_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
text_color_set(DW_COLOR_ERROR);
|
text_color_set(DW_COLOR_ERROR);
|
||||||
dw_printf ("Can't read from udp socket, res=%d", res);
|
dw_printf ("Can't read from udp socket, res=%d", res);
|
||||||
|
@ -1350,12 +1419,13 @@ int audio_put (int a, int c)
|
||||||
|
|
||||||
int audio_flush (int a)
|
int audio_flush (int a)
|
||||||
{
|
{
|
||||||
switch (adev[a].g_audio_out_type) {
|
|
||||||
case AUDIO_OUT_TYPE_STDOUT:;
|
|
||||||
int res;
|
int res;
|
||||||
unsigned char *ptr;
|
unsigned char *ptr;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
switch (adev[a].g_audio_out_type) {
|
||||||
|
case AUDIO_OUT_TYPE_STDOUT:;
|
||||||
|
|
||||||
ptr = adev[a].outbuf_ptr;
|
ptr = adev[a].outbuf_ptr;
|
||||||
len = adev[a].outbuf_len;
|
len = adev[a].outbuf_len;
|
||||||
|
|
||||||
|
@ -1544,6 +1614,32 @@ int audio_flush (int a)
|
||||||
adev[a].outbuf_len = 0;
|
adev[a].outbuf_len = 0;
|
||||||
return (0);
|
return (0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case AUDIO_OUT_TYPE_SDR_UDP:;
|
||||||
|
|
||||||
|
ptr = adev[a].outbuf_ptr;
|
||||||
|
len = adev[a].outbuf_len;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
|
||||||
|
assert (adev[a].udp_out_sock > 0);
|
||||||
|
|
||||||
|
res = sendto(adev[a].udp_out_sock, adev[a].outbuf_ptr, len, 0, (struct sockaddr *)&adev[a].udp_dest_addr, sizeof(struct sockaddr_storage));
|
||||||
|
|
||||||
|
if (res <= 0) {
|
||||||
|
text_color_set(DW_COLOR_INFO);
|
||||||
|
dw_printf ("\nError %d writing to UDP socket. Exiting.\n", errno);
|
||||||
|
exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr += res;
|
||||||
|
len -= res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
adev[a].outbuf_len = 0;
|
||||||
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
return (0);
|
return (0);
|
||||||
} /* end audio_flush */
|
} /* end audio_flush */
|
||||||
|
@ -1589,7 +1685,7 @@ void audio_wait (int a)
|
||||||
{
|
{
|
||||||
audio_flush (a);
|
audio_flush (a);
|
||||||
|
|
||||||
if (adev[a].g_audio_out_type == AUDIO_OUT_TYPE_STDOUT) {
|
if (adev[a].g_audio_out_type != AUDIO_OUT_TYPE_SOUNDCARD) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ enum audio_in_type_e {
|
||||||
|
|
||||||
enum audio_out_type_e {
|
enum audio_out_type_e {
|
||||||
AUDIO_OUT_TYPE_SOUNDCARD,
|
AUDIO_OUT_TYPE_SOUNDCARD,
|
||||||
|
AUDIO_OUT_TYPE_SDR_UDP,
|
||||||
AUDIO_OUT_TYPE_STDOUT };
|
AUDIO_OUT_TYPE_STDOUT };
|
||||||
|
|
||||||
/* For option to try fixing frames with bad CRC. */
|
/* For option to try fixing frames with bad CRC. */
|
||||||
|
|
Loading…
Reference in New Issue