1. check_classic_mac_basic.yml — update to latest macOS-latest 2. docker-on-push-tag-or-pr — dependant updates 3. FFTConvolver — fix a compilation warning when the DEBUG flag is turned off 4. Reorganise some source files — e.g. move most of the metadata stuff into a subdirectory 5. Remove unused special-purpose AirPlay 2 RC event handler. 6. Reorganise source files and source code relating to the standard event handler. 7. Check configuration settings a little more carefully to offer more meaningful diagnostics. 8. Add password to AirPlay 2 and reorganise initialisation to set AirPlay 2 flags accordingly. Also, remove redundant airplay_pin. 9. Remove redundant debug facility definitions from common.h 10. Make it possible to omit all the dacp stuff from a build. 11. Make it possible to omit metadata multicast from a build. 12. Add AP2-native metadata by setting bit 50 of features to true. 13. Improve stability when pre-empting an existing play session. 14. Fix a few compiler warnings in the pair_ap library. 15. Ensure that the audio format is known before starting synchronisation. 16. Improve handle_configure to set/clear a new config setting. Doesn’t seem to do anything… 17. Experimentally turn off bit 46 of AirPlay 2 features — seems to stop failing pair-verify attempts.
181 lines
6.6 KiB
C
181 lines
6.6 KiB
C
#include "multicast.h"
|
|
#include "core.h"
|
|
#include "pc_queue.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
// A special sub-protocol is used for sending large data items over UDP
|
|
// If the payload exceeded 4 MB, it is chunked using the following format:
|
|
// "ssnc", "chnk", packet_ix, packet_counts, packet_tag, packet_type, chunked_data.
|
|
// Notice that the number of items is different to the standard
|
|
|
|
#define METADATA_SNDBUF (4 * 1024 * 1024)
|
|
static int metadata_sock = -1;
|
|
static struct sockaddr_in metadata_sockaddr;
|
|
static char *metadata_sockmsg;
|
|
pc_queue metadata_multicast_queue;
|
|
#define metadata_multicast_queue_size 500
|
|
metadata_package metadata_multicast_queue_items[metadata_multicast_queue_size];
|
|
pthread_t metadata_multicast_thread;
|
|
|
|
int send_metadata_to_multicast_queue(const uint32_t type, const uint32_t code,
|
|
const char *data, const uint32_t length, rtsp_message *carrier,
|
|
int block) {
|
|
return send_metadata_to_queue(&metadata_multicast_queue, type, code, data, length, carrier, block);
|
|
}
|
|
|
|
void metadata_create_multicast_socket(void) {
|
|
if (config.metadata_enabled == 0)
|
|
return;
|
|
|
|
// Unlike metadata pipe, socket is opened once and stays open,
|
|
// so we can call it in create
|
|
if (config.metadata_sockaddr && config.metadata_sockport) {
|
|
metadata_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (metadata_sock < 0) {
|
|
debug(1, "Could not open metadata socket");
|
|
} else {
|
|
int buffer_size = METADATA_SNDBUF;
|
|
setsockopt(metadata_sock, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof(buffer_size));
|
|
bzero((char *)&metadata_sockaddr, sizeof(metadata_sockaddr));
|
|
metadata_sockaddr.sin_family = AF_INET;
|
|
metadata_sockaddr.sin_addr.s_addr = inet_addr(config.metadata_sockaddr);
|
|
metadata_sockaddr.sin_port = htons(config.metadata_sockport);
|
|
metadata_sockmsg = malloc(config.metadata_sockmsglength);
|
|
if (metadata_sockmsg) {
|
|
memset(metadata_sockmsg, 0, config.metadata_sockmsglength);
|
|
} else {
|
|
die("Could not malloc metadata multicast socket buffer");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void metadata_delete_multicast_socket(void) {
|
|
if (config.metadata_enabled == 0)
|
|
return;
|
|
if (metadata_sock != -1) {
|
|
shutdown(metadata_sock, SHUT_RDWR); // we want to immediately deallocate the buffer
|
|
close(metadata_sock);
|
|
}
|
|
if (metadata_sockmsg)
|
|
free(metadata_sockmsg);
|
|
}
|
|
|
|
void metadata_multicast_process(uint32_t type, uint32_t code, char *data, uint32_t length) {
|
|
// debug(1, "Process multicast metadata with type %x, code %x and length %u.", type, code,
|
|
// length);
|
|
if (metadata_sock >= 0 && length < config.metadata_sockmsglength - 8) {
|
|
char *ptr = metadata_sockmsg;
|
|
uint32_t v;
|
|
v = htonl(type);
|
|
memcpy(ptr, &v, 4);
|
|
ptr += 4;
|
|
v = htonl(code);
|
|
memcpy(ptr, &v, 4);
|
|
ptr += 4;
|
|
memcpy(ptr, data, length);
|
|
sendto(metadata_sock, metadata_sockmsg, length + 8, 0, (struct sockaddr *)&metadata_sockaddr,
|
|
sizeof(metadata_sockaddr));
|
|
} else if (metadata_sock >= 0) {
|
|
// send metadata in numbered chunks using the protocol:
|
|
// ("ssnc", "chnk", packet_ix, packet_counts, packet_tag, packet_type, chunked_data)
|
|
|
|
uint32_t chunk_ix = 0;
|
|
if (config.metadata_sockmsglength == 24)
|
|
die("A divide by zero almost occurred (config.metadata_sockmsglength = 24).");
|
|
uint32_t chunk_total = length / (config.metadata_sockmsglength - 24);
|
|
if (chunk_total * (config.metadata_sockmsglength - 24) < length) {
|
|
chunk_total++;
|
|
}
|
|
uint32_t remaining = length;
|
|
uint32_t v;
|
|
char *data_crsr = data;
|
|
do {
|
|
char *ptr = metadata_sockmsg;
|
|
memcpy(ptr, "ssncchnk", 8);
|
|
ptr += 8;
|
|
v = htonl(chunk_ix);
|
|
memcpy(ptr, &v, 4);
|
|
ptr += 4;
|
|
v = htonl(chunk_total);
|
|
memcpy(ptr, &v, 4);
|
|
ptr += 4;
|
|
v = htonl(type);
|
|
memcpy(ptr, &v, 4);
|
|
ptr += 4;
|
|
v = htonl(code);
|
|
memcpy(ptr, &v, 4);
|
|
ptr += 4;
|
|
size_t datalen = remaining;
|
|
if (datalen > config.metadata_sockmsglength - 24) {
|
|
datalen = config.metadata_sockmsglength - 24;
|
|
}
|
|
memcpy(ptr, data_crsr, datalen);
|
|
data_crsr += datalen;
|
|
sendto(metadata_sock, metadata_sockmsg, datalen + 24, 0,
|
|
(struct sockaddr *)&metadata_sockaddr, sizeof(metadata_sockaddr));
|
|
chunk_ix++;
|
|
remaining -= datalen;
|
|
if (remaining == 0)
|
|
break;
|
|
} while (1);
|
|
}
|
|
}
|
|
|
|
void metadata_multicast_thread_cleanup_function(__attribute__((unused)) void *arg) {
|
|
// debug(2, "metadata_multicast_thread_cleanup_function called");
|
|
metadata_delete_multicast_socket();
|
|
}
|
|
|
|
void *metadata_multicast_thread_function(__attribute__((unused)) void *ignore) {
|
|
// #include <syscall.h>
|
|
// debug(1, "metadata_multicast_thread_function PID %d", syscall(SYS_gettid));
|
|
metadata_create_multicast_socket();
|
|
metadata_package pack;
|
|
pthread_cleanup_push(metadata_multicast_thread_cleanup_function, NULL);
|
|
while (1) {
|
|
pc_queue_get_item(&metadata_multicast_queue, &pack);
|
|
pthread_cleanup_push(metadata_pack_cleanup_function, (void *)&pack);
|
|
if (config.metadata_enabled) {
|
|
if (pack.carrier) {
|
|
debug(4,
|
|
" multicast: type "
|
|
"%x, code %x, length %u, message %d.",
|
|
pack.type, pack.code, pack.length, pack.carrier->index_number);
|
|
} else {
|
|
debug(4,
|
|
" multicast: type "
|
|
"%x, code %x, length %u.",
|
|
pack.type, pack.code, pack.length);
|
|
}
|
|
metadata_multicast_process(pack.type, pack.code, pack.data, pack.length);
|
|
debug(4,
|
|
" multicast: done.");
|
|
}
|
|
pthread_cleanup_pop(1);
|
|
}
|
|
pthread_cleanup_pop(1); // will never happen
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
void metadata_multicast_queue_init() {
|
|
// create a pc_queue for the metadata_multicast_queue
|
|
pc_queue_init(&metadata_multicast_queue, (char *)&metadata_multicast_queue_items,
|
|
sizeof(metadata_package), metadata_multicast_queue_size, "multicast");
|
|
if (named_pthread_create(&metadata_multicast_thread, NULL, metadata_multicast_thread_function,
|
|
NULL, "metadata mcst") != 0)
|
|
debug(1, "Failed to create metadata multicast thread!");
|
|
}
|
|
|
|
void metadata_multicast_queue_stop() {
|
|
if (metadata_multicast_thread) {
|
|
pthread_cancel(metadata_multicast_thread);
|
|
pthread_join(metadata_multicast_thread, NULL);
|
|
pc_queue_delete(&metadata_multicast_queue);
|
|
// debug(2, "metadata stop multicast done.");
|
|
}
|
|
}
|