diff --git a/src/audio.c b/src/audio.c index 1884d5c..2220658 100644 --- a/src/audio.c +++ b/src/audio.c @@ -75,6 +75,7 @@ #include #include #include +#include #include @@ -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; } diff --git a/src/audio.h b/src/audio.h index 2022328..dc8444e 100644 --- a/src/audio.h +++ b/src/audio.h @@ -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. */