Skip to content

Audio output via stdout and UDP #440

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add UDP audio output for Linux
  • Loading branch information
ars-ka0s committed Sep 25, 2023
commit 3b6a105b5ac3a362479289bffc9840150937ba96
118 changes: 107 additions & 11 deletions src/audio.c
Original file line number Diff line number Diff line change
@@ -75,6 +75,7 @@
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>


@@ -131,7 +132,9 @@ static struct adev_s {
enum audio_in_type_e g_audio_in_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];

@@ -239,7 +242,7 @@ int audio_open (struct audio_s *pa)
#else
adev[a].oss_audio_device_fd = -1;
#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;

//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);
dw_printf ("Couldn't create socket, errno %d\n", errno);
return -1;
@@ -425,7 +428,7 @@ int audio_open (struct audio_s *pa)
si_me.sin_addr.s_addr = htonl(INADDR_ANY);

//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);
dw_printf ("Couldn't bind socket, errno %d\n", errno);
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) {
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");
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 {
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD;
}
@@ -514,7 +527,63 @@ int audio_open (struct audio_s *pa)
audio_out_name);
return (-1);
}
break;
#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.
@@ -1222,8 +1291,8 @@ int audio_get (int a)
while (adev[a].inbuf_next >= adev[a].inbuf_len) {
int res;

assert (adev[a].udp_sock > 0);
res = recv(adev[a].udp_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
assert (adev[a].udp_in_sock > 0);
res = recv(adev[a].udp_in_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
if (res < 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Can't read from udp socket, res=%d", res);
@@ -1350,11 +1419,12 @@ int audio_put (int a, int c)

int audio_flush (int a)
{
int res;
unsigned char *ptr;
int len;

switch (adev[a].g_audio_out_type) {
case AUDIO_OUT_TYPE_STDOUT:;
int res;
unsigned char *ptr;
int len;

ptr = adev[a].outbuf_ptr;
len = adev[a].outbuf_len;
@@ -1544,6 +1614,32 @@ int audio_flush (int a)
adev[a].outbuf_len = 0;
return (0);
#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);
} /* end audio_flush */
@@ -1589,7 +1685,7 @@ void audio_wait (int 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;
}

1 change: 1 addition & 0 deletions src/audio.h
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ enum audio_in_type_e {

enum audio_out_type_e {
AUDIO_OUT_TYPE_SOUNDCARD,
AUDIO_OUT_TYPE_SDR_UDP,
AUDIO_OUT_TYPE_STDOUT };

/* For option to try fixing frames with bad CRC. */