Skip to content

Commit e37ae99

Browse files
committed
Add UDP audio output for Linux
1 parent 3dcc520 commit e37ae99

File tree

2 files changed

+108
-11
lines changed

2 files changed

+108
-11
lines changed

Diff for: src/audio.c

+107-11
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
#include <sys/socket.h>
7676
#include <arpa/inet.h>
7777
#include <netinet/in.h>
78+
#include <netdb.h>
7879
#include <errno.h>
7980

8081

@@ -131,7 +132,9 @@ static struct adev_s {
131132
enum audio_in_type_e g_audio_in_type;
132133
enum audio_out_type_e g_audio_out_type;
133134

134-
int udp_sock; /* UDP socket for receiving data */
135+
int udp_in_sock; /* UDP socket for receiving data */
136+
int udp_out_sock; /* UDP socket for sending data */
137+
struct sockaddr_storage udp_dest_addr; /* Destination address for UDP socket sending */
135138

136139
} adev[MAX_ADEVS];
137140

@@ -239,7 +242,7 @@ int audio_open (struct audio_s *pa)
239242
#else
240243
adev[a].oss_audio_device_fd = -1;
241244
#endif
242-
adev[a].udp_sock = -1;
245+
adev[a].udp_in_sock = -1;
243246
}
244247

245248

@@ -409,7 +412,7 @@ int audio_open (struct audio_s *pa)
409412
//int data_size = 0;
410413

411414
//Create UDP Socket
412-
if ((adev[a].udp_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
415+
if ((adev[a].udp_in_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
413416
text_color_set(DW_COLOR_ERROR);
414417
dw_printf ("Couldn't create socket, errno %d\n", errno);
415418
return -1;
@@ -421,7 +424,7 @@ int audio_open (struct audio_s *pa)
421424
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
422425

423426
//Bind to the socket
424-
if (bind(adev[a].udp_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) {
427+
if (bind(adev[a].udp_in_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) {
425428
text_color_set(DW_COLOR_ERROR);
426429
dw_printf ("Couldn't bind socket, errno %d\n", errno);
427430
return -1;
@@ -450,7 +453,7 @@ int audio_open (struct audio_s *pa)
450453
}
451454

452455
/*
453-
* Output device. Only "soundcard" and "stdout" are supported at this time.
456+
* Output device. Soundcard, stdout, and UDP are supported at this time.
454457
*/
455458
if (strcasecmp(pa->adev[a].adevice_out, "stdout") == 0 || strcmp(pa->adev[a].adevice_out, "-") == 0) {
456459
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_STDOUT;
@@ -459,6 +462,16 @@ int audio_open (struct audio_s *pa)
459462
dw_printf ("stdout must only be used with the -O option\n");
460463
return (-1);
461464
}
465+
} else if (strncasecmp(pa->adev[a].adevice_out, "udp:", 4) == 0) {
466+
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SDR_UDP;
467+
/* User must supply address and port */
468+
if (strcasecmp(pa->adev[a].adevice_out,"udp:") == 0 ||
469+
strlen(pa->adev[a].adevice_out) < 7 ||
470+
strstr(pa->adev[a].adevice_out+5, ":") == 0) {
471+
text_color_set (DW_COLOR_ERROR);
472+
dw_printf ("Destination address and port must be supplied for UDP output\n");
473+
return (-1);
474+
}
462475
} else {
463476
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD;
464477
}
@@ -506,7 +519,63 @@ int audio_open (struct audio_s *pa)
506519
audio_out_name);
507520
return (-1);
508521
}
522+
break;
509523
#endif
524+
525+
case AUDIO_OUT_TYPE_SDR_UDP:;
526+
527+
struct addrinfo ai_out;
528+
struct addrinfo *ai_res;
529+
char udp_outhost[256];
530+
char *udp_outport;
531+
int res;
532+
533+
// Initialize structure for addrinfo restrictions
534+
memset((char *) &ai_out, 0, sizeof(ai_out));
535+
ai_out.ai_socktype = SOCK_DGRAM;
536+
ai_out.ai_protocol = IPPROTO_UDP;
537+
538+
// Separate out the host and port strings
539+
strncpy(udp_outhost, pa->adev[a].adevice_out+4, 255);
540+
udp_outhost[255] = 0;
541+
udp_outport = strstr(udp_outhost,":");
542+
*udp_outport++ = 0;
543+
544+
if (strlen(udp_outport) == 0) {
545+
text_color_set(DW_COLOR_ERROR);
546+
dw_printf ("UDP output destination port must be supplied\n");
547+
return -1;
548+
}
549+
550+
// Get the sockaddr to represent the host/port provided
551+
res = getaddrinfo(udp_outhost, udp_outport, &ai_out, &ai_res);
552+
if (res != 0) {
553+
text_color_set(DW_COLOR_ERROR);
554+
dw_printf ("Error parsing/resolving UDP output address\n");
555+
return -1;
556+
}
557+
558+
// IPv4 and IPv6 structs are different sizes
559+
if (ai_res->ai_family == AF_INET6) {
560+
res = sizeof(struct sockaddr_in6);
561+
} else {
562+
res = sizeof(struct sockaddr_in);
563+
}
564+
565+
//Create UDP Socket for the right address family
566+
if ((adev[a].udp_out_sock=socket(ai_res->ai_family, SOCK_DGRAM, IPPROTO_UDP))==-1) {
567+
text_color_set(DW_COLOR_ERROR);
568+
dw_printf ("Couldn't create socket, errno %d\n", errno);
569+
return -1;
570+
}
571+
572+
// Save sockaddr needed later to send the data and set buffer size
573+
memcpy(&adev[a].udp_dest_addr, ai_res->ai_addr, res);
574+
adev[a].outbuf_size_in_bytes = SDR_UDP_BUF_MAXLEN;
575+
freeaddrinfo(ai_res);
576+
577+
break;
578+
510579
}
511580
/*
512581
* Finally allocate buffer for each direction.
@@ -1214,8 +1283,8 @@ int audio_get (int a)
12141283
while (adev[a].inbuf_next >= adev[a].inbuf_len) {
12151284
int res;
12161285

1217-
assert (adev[a].udp_sock > 0);
1218-
res = recv(adev[a].udp_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
1286+
assert (adev[a].udp_in_sock > 0);
1287+
res = recv(adev[a].udp_in_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
12191288
if (res < 0) {
12201289
text_color_set(DW_COLOR_ERROR);
12211290
dw_printf ("Can't read from udp socket, res=%d", res);
@@ -1342,11 +1411,12 @@ int audio_put (int a, int c)
13421411

13431412
int audio_flush (int a)
13441413
{
1414+
int res;
1415+
unsigned char *ptr;
1416+
int len;
1417+
13451418
switch (adev[a].g_audio_out_type) {
13461419
case AUDIO_OUT_TYPE_STDOUT:;
1347-
int res;
1348-
unsigned char *ptr;
1349-
int len;
13501420

13511421
ptr = adev[a].outbuf_ptr;
13521422
len = adev[a].outbuf_len;
@@ -1536,6 +1606,32 @@ int audio_flush (int a)
15361606
adev[a].outbuf_len = 0;
15371607
return (0);
15381608
#endif
1609+
1610+
case AUDIO_OUT_TYPE_SDR_UDP:;
1611+
1612+
ptr = adev[a].outbuf_ptr;
1613+
len = adev[a].outbuf_len;
1614+
1615+
while (len > 0) {
1616+
1617+
assert (adev[a].udp_out_sock > 0);
1618+
1619+
res = sendto(adev[a].udp_out_sock, adev[a].outbuf_ptr, len, 0, (struct sockaddr *)&adev[a].udp_dest_addr, sizeof(struct sockaddr_storage));
1620+
1621+
if (res <= 0) {
1622+
text_color_set(DW_COLOR_INFO);
1623+
dw_printf ("\nError %d writing to UDP socket. Exiting.\n", errno);
1624+
exit (0);
1625+
}
1626+
1627+
ptr += res;
1628+
len -= res;
1629+
1630+
}
1631+
1632+
adev[a].outbuf_len = 0;
1633+
return 0;
1634+
15391635
}
15401636
return (0);
15411637
} /* end audio_flush */
@@ -1581,7 +1677,7 @@ void audio_wait (int a)
15811677
{
15821678
audio_flush (a);
15831679

1584-
if (adev[a].g_audio_out_type == AUDIO_OUT_TYPE_STDOUT) {
1680+
if (adev[a].g_audio_out_type != AUDIO_OUT_TYPE_SOUNDCARD) {
15851681
return;
15861682
}
15871683

Diff for: src/audio.h

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ enum audio_in_type_e {
4545

4646
enum audio_out_type_e {
4747
AUDIO_OUT_TYPE_SOUNDCARD,
48+
AUDIO_OUT_TYPE_SDR_UDP,
4849
AUDIO_OUT_TYPE_STDOUT };
4950

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

0 commit comments

Comments
 (0)