mirror of https://github.com/wb2osz/direwolf.git
				
				
				
			Add UDP audio output for Linux
This commit is contained in:
		
							parent
							
								
									74d6cc6bc2
								
							
						
					
					
						commit
						3b6a105b5a
					
				
							
								
								
									
										118
									
								
								src/audio.c
								
								
								
								
							
							
						
						
									
										118
									
								
								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,11 +1419,12 @@ int audio_put (int a, int c) | ||||||
| 
 | 
 | ||||||
| int audio_flush (int a) | int audio_flush (int a) | ||||||
| { | { | ||||||
|  | 	int res; | ||||||
|  | 	unsigned char *ptr; | ||||||
|  | 	int len; | ||||||
|  | 
 | ||||||
| 	switch (adev[a].g_audio_out_type) { | 	switch (adev[a].g_audio_out_type) { | ||||||
| 	  case AUDIO_OUT_TYPE_STDOUT:; | 	  case AUDIO_OUT_TYPE_STDOUT:; | ||||||
| 	    int res; |  | ||||||
| 	    unsigned char *ptr; |  | ||||||
| 	    int len; |  | ||||||
| 
 | 
 | ||||||
| 	    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