Skip to content

Commit a958b0c

Browse files
committed
Add UDP audio output for Linux
1 parent 7ae584d commit a958b0c

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

@@ -413,7 +416,7 @@ int audio_open (struct audio_s *pa)
413416
//int data_size = 0;
414417

415418
//Create UDP Socket
416-
if ((adev[a].udp_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
419+
if ((adev[a].udp_in_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
417420
text_color_set(DW_COLOR_ERROR);
418421
dw_printf ("Couldn't create socket, errno %d\n", errno);
419422
return -1;
@@ -425,7 +428,7 @@ int audio_open (struct audio_s *pa)
425428
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
426429

427430
//Bind to the socket
428-
if (bind(adev[a].udp_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) {
431+
if (bind(adev[a].udp_in_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) {
429432
text_color_set(DW_COLOR_ERROR);
430433
dw_printf ("Couldn't bind socket, errno %d\n", errno);
431434
return -1;
@@ -454,7 +457,7 @@ int audio_open (struct audio_s *pa)
454457
}
455458

456459
/*
457-
* Output device. Only "soundcard" and "stdout" are supported at this time.
460+
* Output device. Soundcard, stdout, and UDP are supported at this time.
458461
*/
459462
if (strcasecmp(pa->adev[a].adevice_out, "stdout") == 0 || strcmp(pa->adev[a].adevice_out, "-") == 0) {
460463
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_STDOUT;
@@ -463,6 +466,16 @@ int audio_open (struct audio_s *pa)
463466
dw_printf ("stdout must only be used with the -O option\n");
464467
return (-1);
465468
}
469+
} else if (strncasecmp(pa->adev[a].adevice_out, "udp:", 4) == 0) {
470+
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SDR_UDP;
471+
/* User must supply address and port */
472+
if (strcasecmp(pa->adev[a].adevice_out,"udp:") == 0 ||
473+
strlen(pa->adev[a].adevice_out) < 7 ||
474+
strstr(pa->adev[a].adevice_out+5, ":") == 0) {
475+
text_color_set (DW_COLOR_ERROR);
476+
dw_printf ("Destination address and port must be supplied for UDP output\n");
477+
return (-1);
478+
}
466479
} else {
467480
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD;
468481
}
@@ -514,7 +527,63 @@ int audio_open (struct audio_s *pa)
514527
audio_out_name);
515528
return (-1);
516529
}
530+
break;
517531
#endif
532+
533+
case AUDIO_OUT_TYPE_SDR_UDP:;
534+
535+
struct addrinfo ai_out;
536+
struct addrinfo *ai_res;
537+
char udp_outhost[256];
538+
char *udp_outport;
539+
int res;
540+
541+
// Initialize structure for addrinfo restrictions
542+
memset((char *) &ai_out, 0, sizeof(ai_out));
543+
ai_out.ai_socktype = SOCK_DGRAM;
544+
ai_out.ai_protocol = IPPROTO_UDP;
545+
546+
// Separate out the host and port strings
547+
strncpy(udp_outhost, pa->adev[a].adevice_out+4, 255);
548+
udp_outhost[255] = 0;
549+
udp_outport = strstr(udp_outhost,":");
550+
*udp_outport++ = 0;
551+
552+
if (strlen(udp_outport) == 0) {
553+
text_color_set(DW_COLOR_ERROR);
554+
dw_printf ("UDP output destination port must be supplied\n");
555+
return -1;
556+
}
557+
558+
// Get the sockaddr to represent the host/port provided
559+
res = getaddrinfo(udp_outhost, udp_outport, &ai_out, &ai_res);
560+
if (res != 0) {
561+
text_color_set(DW_COLOR_ERROR);
562+
dw_printf ("Error parsing/resolving UDP output address\n");
563+
return -1;
564+
}
565+
566+
// IPv4 and IPv6 structs are different sizes
567+
if (ai_res->ai_family == AF_INET6) {
568+
res = sizeof(struct sockaddr_in6);
569+
} else {
570+
res = sizeof(struct sockaddr_in);
571+
}
572+
573+
//Create UDP Socket for the right address family
574+
if ((adev[a].udp_out_sock=socket(ai_res->ai_family, SOCK_DGRAM, IPPROTO_UDP))==-1) {
575+
text_color_set(DW_COLOR_ERROR);
576+
dw_printf ("Couldn't create socket, errno %d\n", errno);
577+
return -1;
578+
}
579+
580+
// Save sockaddr needed later to send the data and set buffer size
581+
memcpy(&adev[a].udp_dest_addr, ai_res->ai_addr, res);
582+
adev[a].outbuf_size_in_bytes = SDR_UDP_BUF_MAXLEN;
583+
freeaddrinfo(ai_res);
584+
585+
break;
586+
518587
}
519588
/*
520589
* Finally allocate buffer for each direction.
@@ -1222,8 +1291,8 @@ int audio_get (int a)
12221291
while (adev[a].inbuf_next >= adev[a].inbuf_len) {
12231292
int res;
12241293

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

13511420
int audio_flush (int a)
13521421
{
1422+
int res;
1423+
unsigned char *ptr;
1424+
int len;
1425+
13531426
switch (adev[a].g_audio_out_type) {
13541427
case AUDIO_OUT_TYPE_STDOUT:;
1355-
int res;
1356-
unsigned char *ptr;
1357-
int len;
13581428

13591429
ptr = adev[a].outbuf_ptr;
13601430
len = adev[a].outbuf_len;
@@ -1544,6 +1614,32 @@ int audio_flush (int a)
15441614
adev[a].outbuf_len = 0;
15451615
return (0);
15461616
#endif
1617+
1618+
case AUDIO_OUT_TYPE_SDR_UDP:;
1619+
1620+
ptr = adev[a].outbuf_ptr;
1621+
len = adev[a].outbuf_len;
1622+
1623+
while (len > 0) {
1624+
1625+
assert (adev[a].udp_out_sock > 0);
1626+
1627+
res = sendto(adev[a].udp_out_sock, adev[a].outbuf_ptr, len, 0, (struct sockaddr *)&adev[a].udp_dest_addr, sizeof(struct sockaddr_storage));
1628+
1629+
if (res <= 0) {
1630+
text_color_set(DW_COLOR_INFO);
1631+
dw_printf ("\nError %d writing to UDP socket. Exiting.\n", errno);
1632+
exit (0);
1633+
}
1634+
1635+
ptr += res;
1636+
len -= res;
1637+
1638+
}
1639+
1640+
adev[a].outbuf_len = 0;
1641+
return 0;
1642+
15471643
}
15481644
return (0);
15491645
} /* end audio_flush */
@@ -1589,7 +1685,7 @@ void audio_wait (int a)
15891685
{
15901686
audio_flush (a);
15911687

1592-
if (adev[a].g_audio_out_type == AUDIO_OUT_TYPE_STDOUT) {
1688+
if (adev[a].g_audio_out_type != AUDIO_OUT_TYPE_SOUNDCARD) {
15931689
return;
15941690
}
15951691

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)