From 120f9a246711c05f9a359a399dadaf8f02c44c54 Mon Sep 17 00:00:00 2001 From: KubaPro010 Date: Sat, 17 May 2025 17:28:08 +0200 Subject: [PATCH] a good vban receiver!!!???? impossible!!!! --- .vscode/settings.json | 3 +- io/audio.c | 39 ++++++ io/audio.h | 2 + src/vban95.c | 318 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 src/vban95.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 92b431d..be7d890 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,7 +24,8 @@ "gain_control.h": "c", "optimization.h": "c", "string.h": "c", - "getopt.h": "c" + "getopt.h": "c", + "audio.h": "c" }, "C_Cpp.errorSquiggles": "disabled" } \ No newline at end of file diff --git a/io/audio.c b/io/audio.c index 4925839..8b19e0f 100644 --- a/io/audio.c +++ b/io/audio.c @@ -79,6 +79,38 @@ int init_PulseOutputDevice(PulseOutputDevice* dev, int sample_rate, int channels return 0; } +int init_PulseOutputDevicef(PulseOutputDevice* dev, int sample_rate, int channels, char* app_name, char *stream_name, char* device, pa_buffer_attr* buffer_attr, enum pa_sample_format format) { + if (dev->initialized) return -1; + pa_sample_spec sample_spec = { + .format = format, + .channels = channels, + .rate = sample_rate + }; + pa_buffer_attr new_buffer_attr = *buffer_attr; + dev->sample_spec = sample_spec; + dev->buffer_attr = new_buffer_attr; + + dev->app_name = strdup(app_name); + dev->stream_name = strdup(stream_name); + dev->device = strdup(device); + + int error; + dev->dev = pa_simple_new( + NULL, + app_name, + PA_STREAM_PLAYBACK, + device, + stream_name, + &sample_spec, + NULL, + &new_buffer_attr, + &error + ); + if (!dev->dev) return error; + dev->initialized = 1; + return 0; +} + int write_PulseOutputDevice(PulseOutputDevice* dev, float* buffer, size_t size) { if (!dev->initialized) return -1; int error; @@ -86,6 +118,13 @@ int write_PulseOutputDevice(PulseOutputDevice* dev, float* buffer, size_t size) return 0; } +int write_PulseOutputDevicef(PulseOutputDevice* dev, void* buffer, size_t size) { + if (!dev->initialized) return -1; + int error; + if (pa_simple_write(dev->dev, buffer, size, &error) < 0) return error; + return 0; +} + void free_PulseOutputDevice(PulseOutputDevice* dev) { if (dev->dev && dev->initialized) pa_simple_free(dev->dev); free(dev->app_name); diff --git a/io/audio.h b/io/audio.h index feab6ee..634da0f 100644 --- a/io/audio.h +++ b/io/audio.h @@ -22,5 +22,7 @@ void free_PulseInputDevice(PulseInputDevice *dev); typedef PulseInputDevice PulseOutputDevice; int init_PulseOutputDevice(PulseOutputDevice *dev, int sample_rate, int channels, char *app_name, char *stream_name, char *device, pa_buffer_attr *buffer_attr); +int init_PulseOutputDevicef(PulseOutputDevice *dev, int sample_rate, int channels, char *app_name, char *stream_name, char *device, pa_buffer_attr *buffer_attr, pa_sample_format_t format); int write_PulseOutputDevice(PulseOutputDevice *dev, float *buffer, size_t size); +int write_PulseOutputDevicef(PulseOutputDevice *dev, void *buffer, size_t size); void free_PulseOutputDevice(PulseOutputDevice *dev); \ No newline at end of file diff --git a/src/vban95.c b/src/vban95.c new file mode 100644 index 0000000..c1f2d7d --- /dev/null +++ b/src/vban95.c @@ -0,0 +1,318 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define VBAN_SR_MAXNUMBER 21 +static long VBAN_SRList[VBAN_SR_MAXNUMBER] = { + 6000, 12000, 24000, 48000, 96000, 192000, 384000, + 8000, 16000, 32000, 64000, 128000, 256000, 512000, + 11025, 22050, 44100, 88200, 176400, 352800, 705600 +}; + +#include "../io/audio.h" +#include + +#define VBAN_BIT_MAXNUMBER 5 // 7 in the standard but pa does these 5 +static enum pa_sample_format VBAN_BITList[VBAN_BIT_MAXNUMBER] = { + PA_SAMPLE_U8, + PA_SAMPLE_S16NE, + PA_SAMPLE_S24NE, + PA_SAMPLE_S32NE, + PA_SAMPLE_FLOAT32NE, +}; +static char VBAN_TextBITList[VBAN_BIT_MAXNUMBER][4] = { + "U08", + "S16", + "S24", + "S32", + "F32", +}; + +#define VBAN_PROTOCOL_AUDIO 0x00 +#define VBAN_PROTOCOL_SERIAL 0x20 +#define VBAN_PROTOCOL_TXT 0x40 +#define VBAN_PROTOCOL_SERVICE 0x60 + +#define BUF_SIZE 2048 +#define MAX_AUDIO_DATA_SIZE (BUF_SIZE - sizeof(VBANHeader)) +#define MAX_BUFFER_PACKETS 64 + +#pragma pack(1) +typedef struct { + char vban[4]; + uint8_t sample_rate_idx; + uint8_t samples_per_frame; + uint8_t sample_channels; + uint8_t format_type; + char streamname[16]; + uint32_t frame_num; +} VBANHeader; + +typedef union { + VBANHeader packet_data; + char raw_data[sizeof(VBANHeader)]; +} VBANHeaderUnion; +#pragma pack() + +typedef struct { + char data[MAX_AUDIO_DATA_SIZE]; + size_t size; +} AudioPacket; + +typedef struct { + AudioPacket* packets; + int capacity; + int count; + VBANHeader header; +} AudioBuffer; + +AudioBuffer* create_audio_buffer(int capacity) { + AudioBuffer* buffer = (AudioBuffer*)malloc(sizeof(AudioBuffer)); + if (!buffer) { + perror("Failed to allocate audio buffer"); + return NULL; + } + + buffer->packets = (AudioPacket*)malloc(capacity * sizeof(AudioPacket)); + if (!buffer->packets) { + perror("Failed to allocate packet buffer"); + free(buffer); + return NULL; + } + + buffer->capacity = capacity; + buffer->count = 0; + + return buffer; +} + +void destroy_audio_buffer(AudioBuffer* buffer) { + if (buffer) { + free(buffer->packets); + free(buffer); + } +} + +int add_to_buffer(AudioBuffer* buffer, const char* data, size_t size, const VBANHeader* header) { + if (buffer->count >= buffer->capacity) { + return 0; + } + + if (size > MAX_AUDIO_DATA_SIZE) { + fprintf(stderr, "Audio data too large for buffer\n"); + return -1; + } + + memcpy(buffer->packets[buffer->count].data, data, size); + buffer->packets[buffer->count].size = size; + memcpy(&buffer->header, header, sizeof(VBANHeader)); + buffer->count++; + + return 1; +} + +volatile uint8_t to_run = 1; + +static void stop(int signum) { + (void)signum; + printf("\nReceived stop signal.\n"); + to_run = 0; +} + +static PulseOutputDevice output = {0}; + +void process_audio_buffer(AudioBuffer* buffer, PulseOutputDevice* output_device) { + if (buffer->count == 0) { + return; + } + + for(int i = 0; i < buffer->count; i++) { + write_PulseOutputDevicef(&output, buffer->packets[i].data, buffer->packets[i].size); + } + + buffer->count = 0; +} + +int main(int argc, char *argv[]) { + if (argc != 6) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + char *remote_ip = argv[1]; + int listen_port = atoi(argv[2]); + char *stream_name = argv[3]; + int buffer_size = atoi(argv[4]); + char *pulse_device = argv[5]; + + if (buffer_size <= 0 || buffer_size > MAX_BUFFER_PACKETS) { + fprintf(stderr, "Buffer size must be between 1 and %d\n", MAX_BUFFER_PACKETS); + return 1; + } + + printf("Starting VBAN receiver with buffer size: %d packets\n", buffer_size); + + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + perror("socket"); + return 1; + } + + struct sockaddr_in local_addr; + memset(&local_addr, 0, sizeof(local_addr)); + local_addr.sin_family = AF_INET; + local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + local_addr.sin_port = htons(listen_port); + + if (bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) { + perror("bind"); + close(sockfd); + return 1; + } + + struct in_addr remote_addr_bin; + if (inet_pton(AF_INET, remote_ip, &remote_addr_bin) != 1) { + fprintf(stderr, "Invalid remote IP address: %s\n", remote_ip); + close(sockfd); + return 1; + } + + char buffer[BUF_SIZE]; + struct sockaddr_in sender_addr; + socklen_t sender_len = sizeof(sender_addr); + + uint32_t vban_frame = 0; + uint8_t vban_last_sr = 0; + uint8_t vban_last_format = 0; + uint8_t vban_last_channels = 0; + uint8_t vban_audio_reset = 0; + + AudioBuffer* audio_buffer = create_audio_buffer(buffer_size); + if (!audio_buffer) { + close(sockfd); + return 1; + } + + signal(SIGINT, stop); + signal(SIGTERM, stop); + + while (to_run) { + ssize_t recv_len = recvfrom(sockfd, buffer, BUF_SIZE - 1, 0, + (struct sockaddr *)&sender_addr, &sender_len); + if (recv_len < 0) { + perror("recvfrom"); + break; + } + + if (sender_addr.sin_addr.s_addr == remote_addr_bin.s_addr || strcmp(remote_ip, "0.0.0.0") == 0) { + VBANHeaderUnion data; + memcpy(&data.raw_data, buffer, sizeof(VBANHeader)); + + if (memcmp(data.packet_data.vban, "VBAN", 4) != 0) { + fprintf(stderr, "Invalid VBAN header\n"); + continue; + } + + if (memcmp(data.packet_data.streamname, stream_name, strlen(stream_name)) != 0) { + continue; + } + + if (vban_frame == 0) { + vban_frame = data.packet_data.frame_num; + } else { + uint32_t expected_frame = vban_frame + 1; + + if (data.packet_data.frame_num != expected_frame) { + uint32_t diff = data.packet_data.frame_num - expected_frame; + + if (diff < 0x80000000) { + printf("Dropped %u packet(s)\n", diff); + } else { + printf("Packet out of order (late or duplicate)\n"); + } + + vban_frame = data.packet_data.frame_num; + } else { + vban_frame = expected_frame; + } + } + + if(vban_last_sr != data.packet_data.sample_rate_idx) { + vban_last_sr = data.packet_data.sample_rate_idx; + printf("New sample rate of %ld\n", VBAN_SRList[vban_last_sr % VBAN_SR_MAXNUMBER]); + vban_audio_reset = 1; + audio_buffer->count = 0; + } + + if(vban_last_format != data.packet_data.format_type) { + vban_last_format = data.packet_data.format_type; + printf("New data format of %s\n", VBAN_TextBITList[vban_last_format % VBAN_BIT_MAXNUMBER]); + vban_audio_reset = 1; + audio_buffer->count = 0; + } + + if(vban_last_channels != data.packet_data.sample_channels) { + vban_last_channels = data.packet_data.sample_channels; + printf("New channel count of %d\n", vban_last_channels + 1); // Add 1 because VBAN channels are 0-based + vban_audio_reset = 1; + audio_buffer->count = 0; + } + + // Handle audio reset if needed + if(vban_audio_reset) { + if (output.initialized) { + free_PulseOutputDevice(&output); + } + + pa_buffer_attr buffer_attr = { + .maxlength = (uint32_t) -1, + .tlength = (uint32_t) -1, + .prebuf = (uint32_t) -1, + .minreq = (uint32_t) -1, + .fragsize = (uint32_t) -1 + }; + + int result = init_PulseOutputDevicef( + &output, + VBAN_SRList[vban_last_sr % VBAN_SR_MAXNUMBER], + vban_last_channels + 1, // Add 1 because VBAN channels are 0-based + "vban95", + stream_name, + pulse_device, + &buffer_attr, + VBAN_BITList[vban_last_format % VBAN_BIT_MAXNUMBER] + ); + + if (result != 0) { + fprintf(stderr, "Failed to initialize PulseAudio output device: %s\n", pa_strerror(result)); + } + + vban_audio_reset = 0; + } + + char* audio_data = buffer + sizeof(VBANHeader); + size_t audio_data_size = recv_len - sizeof(VBANHeader); + + if (add_to_buffer(audio_buffer, audio_data, audio_data_size, &data.packet_data) > 0) { + if (audio_buffer->count >= audio_buffer->capacity) { + process_audio_buffer(audio_buffer, &output); + } + } + } + } + + // Clean up + printf("Cleaning up...\n"); + if (output.initialized) { + free_PulseOutputDevice(&output); + } + destroy_audio_buffer(audio_buffer); + close(sockfd); + + return 0; +} \ No newline at end of file