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 <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; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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. */ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue