#include "raop.h"
#include "raop_buffer.h"
#include "netutils.h"
+#include "utils.h"
#include "compat.h"
#include "logger.h"
logger_t *logger;
raop_callbacks_t callbacks;
+ /* Buffer to handle all resends */
raop_buffer_t *buffer;
+ /* Remote address as sockaddr */
+ struct sockaddr_storage remote_saddr;
+ socklen_t remote_saddr_len;
+
+ /* MUTEX LOCKED VARIABLES START */
/* These variables only edited mutex locked */
int running;
int joined;
+
float volume;
+ int volume_changed;
+ unsigned char *metadata;
+ int metadata_len;
+ unsigned char *coverart;
+ int coverart_len;
+
int flush;
thread_handle_t thread;
mutex_handle_t run_mutex;
+ /* MUTEX LOCKED VARIABLES END */
/* Remote control and timing ports */
unsigned short control_rport;
unsigned short timing_lport;
unsigned short data_lport;
+ /* Initialized after the first control packet */
struct sockaddr_storage control_saddr;
socklen_t control_saddr_len;
unsigned short control_seqnum;
};
+static int
+raop_rtp_parse_remote(raop_rtp_t *raop_rtp, const char *remote)
+{
+ char *original;
+ char *current;
+ char *tmpstr;
+ int family;
+ int ret;
+
+ assert(raop_rtp);
+
+ current = original = strdup(remote);
+ if (!original) {
+ return -1;
+ }
+ tmpstr = utils_strsep(¤t, " ");
+ if (strcmp(tmpstr, "IN")) {
+ free(original);
+ return -1;
+ }
+ tmpstr = utils_strsep(¤t, " ");
+ if (!strcmp(tmpstr, "IP4") && current) {
+ family = AF_INET;
+ } else if (!strcmp(tmpstr, "IP6") && current) {
+ family = AF_INET6;
+ } else {
+ free(original);
+ return -1;
+ }
+ if (strstr(current, ":")) {
+ /* FIXME: iTunes sends IP4 even with an IPv6 address, does it mean something */
+ family = AF_INET6;
+ }
+ ret = netutils_parse_address(family, current,
+ &raop_rtp->remote_saddr,
+ sizeof(raop_rtp->remote_saddr));
+ if (ret < 0) {
+ free(original);
+ return -1;
+ }
+ raop_rtp->remote_saddr_len = ret;
+ free(original);
+ return 0;
+}
+
raop_rtp_t *
-raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *fmtp,
+raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote,
+ const char *rtpmap, const char *fmtp,
const unsigned char *aeskey, const unsigned char *aesiv)
{
raop_rtp_t *raop_rtp;
assert(logger);
+ assert(callbacks);
+ assert(remote);
+ assert(rtpmap);
+ assert(fmtp);
raop_rtp = calloc(1, sizeof(raop_rtp_t));
if (!raop_rtp) {
}
raop_rtp->logger = logger;
memcpy(&raop_rtp->callbacks, callbacks, sizeof(raop_callbacks_t));
- raop_rtp->buffer = raop_buffer_init(fmtp, aeskey, aesiv);
+ raop_rtp->buffer = raop_buffer_init(rtpmap, fmtp, aeskey, aesiv);
if (!raop_rtp->buffer) {
free(raop_rtp);
return NULL;
}
+ if (raop_rtp_parse_remote(raop_rtp, remote) < 0) {
+ free(raop_rtp);
+ return NULL;
+ }
raop_rtp->running = 0;
raop_rtp->joined = 1;
MUTEX_DESTROY(raop_rtp->run_mutex);
raop_buffer_destroy(raop_rtp->buffer);
+ free(raop_rtp->metadata);
+ free(raop_rtp->coverart);
free(raop_rtp);
}
}
unsigned short ourseqnum;
struct sockaddr *addr;
socklen_t addrlen;
+ int ret;
addr = (struct sockaddr *)&raop_rtp->control_saddr;
addrlen = raop_rtp->control_saddr_len;
- logger_log(raop_rtp->logger, LOGGER_DEBUG, "Got resend request %d %d\n", seqnum, count);
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "Got resend request %d %d", seqnum, count);
ourseqnum = raop_rtp->control_seqnum++;
/* Fill the request buffer */
packet[6] = (count >> 8);
packet[7] = count;
- sendto(raop_rtp->csock, (const char *)packet, sizeof(packet), 0, addr, addrlen);
+ ret = sendto(raop_rtp->csock, (const char *)packet, sizeof(packet), 0, addr, addrlen);
+ if (ret == -1) {
+ logger_log(raop_rtp->logger, LOGGER_WARNING, "Resend failed: %d", SOCKET_GET_ERROR());
+ }
+
+ return 0;
+}
+
+static int
+raop_rtp_process_events(raop_rtp_t *raop_rtp, void *cb_data)
+{
+ int flush;
+ float volume;
+ int volume_changed;
+ unsigned char *metadata;
+ int metadata_len;
+ unsigned char *coverart;
+ int coverart_len;
+
+ assert(raop_rtp);
+
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ if (!raop_rtp->running) {
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+ return 1;
+ }
+
+ /* Read the volume level */
+ volume = raop_rtp->volume;
+ volume_changed = raop_rtp->volume_changed;
+ raop_rtp->volume_changed = 0;
+
+ /* Read the flush value */
+ flush = raop_rtp->flush;
+ raop_rtp->flush = NO_FLUSH;
+
+ /* Read the metadata */
+ metadata = raop_rtp->metadata;
+ metadata_len = raop_rtp->metadata_len;
+ raop_rtp->metadata = NULL;
+ raop_rtp->metadata_len = 0;
+
+ /* Read the coverart */
+ coverart = raop_rtp->coverart;
+ coverart_len = raop_rtp->coverart_len;
+ raop_rtp->coverart = NULL;
+ raop_rtp->coverart_len = 0;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+
+ /* Call set_volume callback if changed */
+ if (volume_changed) {
+ if (raop_rtp->callbacks.audio_set_volume) {
+ raop_rtp->callbacks.audio_set_volume(raop_rtp->callbacks.cls, cb_data, volume);
+ }
+ }
+
+ /* Handle flush if requested */
+ if (flush != NO_FLUSH) {
+ raop_buffer_flush(raop_rtp->buffer, flush);
+ if (raop_rtp->callbacks.audio_flush) {
+ raop_rtp->callbacks.audio_flush(raop_rtp->callbacks.cls, cb_data);
+ }
+ }
+ if (metadata != NULL) {
+ if (raop_rtp->callbacks.audio_set_metadata) {
+ raop_rtp->callbacks.audio_set_metadata(raop_rtp->callbacks.cls, cb_data, metadata, metadata_len);
+ }
+ free(metadata);
+ metadata = NULL;
+ }
+ if (coverart != NULL) {
+ if (raop_rtp->callbacks.audio_set_coverart) {
+ raop_rtp->callbacks.audio_set_coverart(raop_rtp->callbacks.cls, cb_data, coverart, coverart_len);
+ }
+ free(coverart);
+ coverart = NULL;
+ }
return 0;
}
assert(raop_rtp);
config = raop_buffer_get_config(raop_rtp->buffer);
- raop_rtp->callbacks.audio_init(raop_rtp->callbacks.cls, &cb_data,
+ cb_data = raop_rtp->callbacks.audio_init(raop_rtp->callbacks.cls,
config->bitDepth,
config->numChannels,
config->sampleRate);
while(1) {
- int volume_changed;
- float volume = 0.0;
- int flush;
-
fd_set rfds;
struct timeval tv;
int nfds, ret;
- MUTEX_LOCK(raop_rtp->run_mutex);
- if (!raop_rtp->running) {
- MUTEX_UNLOCK(raop_rtp->run_mutex);
+ /* Check if we are still running and process callbacks */
+ if (raop_rtp_process_events(raop_rtp, cb_data)) {
break;
}
- /* Read the volume level */
- volume_changed = (volume != raop_rtp->volume);
- volume = raop_rtp->volume;
-
- /* Read the flush value */
- flush = raop_rtp->flush;
- raop_rtp->flush = NO_FLUSH;
- MUTEX_UNLOCK(raop_rtp->run_mutex);
-
- /* Call set_volume callback if changed */
- if (volume_changed) {
- raop_rtp->callbacks.audio_set_volume(raop_rtp->callbacks.cls, cb_data, volume);
- }
- if (flush != NO_FLUSH) {
- raop_buffer_flush(raop_rtp->buffer, flush);
- raop_rtp->callbacks.audio_flush(raop_rtp->callbacks.cls, cb_data);
- }
/* Set timeout value to 5ms */
tv.tv_sec = 0;
packetlen = recvfrom(raop_rtp->csock, (char *)packet, sizeof(packet), 0,
(struct sockaddr *)&saddr, &saddrlen);
- /* FIXME: Get destination address here */
+ /* Get the destination address here, because we need the sin6_scope_id */
memcpy(&raop_rtp->control_saddr, &saddr, saddrlen);
raop_rtp->control_saddr_len = saddrlen;
if (packetlen >= 12) {
char type = packet[1] & ~0x80;
- logger_log(raop_rtp->logger, LOGGER_DEBUG, "Got control packet of type 0x%02x\n", type);
+ logger_log(raop_rtp->logger, LOGGER_DEBUG, "Got control packet of type 0x%02x", type);
if (type == 0x56) {
/* Handle resent data packet */
int ret = raop_buffer_queue(raop_rtp->buffer, packet+4, packetlen-4, 1);
}
}
} else if (FD_ISSET(raop_rtp->tsock, &rfds)) {
- logger_log(raop_rtp->logger, LOGGER_INFO, "Would have timing packet in queue\n");
+ logger_log(raop_rtp->logger, LOGGER_INFO, "Would have timing packet in queue");
} else if (FD_ISSET(raop_rtp->dsock, &rfds)) {
saddrlen = sizeof(saddr);
packetlen = recvfrom(raop_rtp->dsock, (char *)packet, sizeof(packet), 0,
}
}
}
- logger_log(raop_rtp->logger, LOGGER_INFO, "Exiting thread\n");
+ logger_log(raop_rtp->logger, LOGGER_INFO, "Exiting UDP RAOP thread");
raop_rtp->callbacks.audio_destroy(raop_rtp->callbacks.cls, cb_data);
return 0;
assert(raop_rtp);
config = raop_buffer_get_config(raop_rtp->buffer);
- raop_rtp->callbacks.audio_init(raop_rtp->callbacks.cls, &cb_data,
+ cb_data = raop_rtp->callbacks.audio_init(raop_rtp->callbacks.cls,
config->bitDepth,
config->numChannels,
config->sampleRate);
while (1) {
- int volume_changed;
- float volume = 0.0;
-
fd_set rfds;
struct timeval tv;
int nfds, ret;
- MUTEX_LOCK(raop_rtp->run_mutex);
- if (!raop_rtp->running) {
- MUTEX_UNLOCK(raop_rtp->run_mutex);
+ /* Check if we are still running and process callbacks */
+ if (raop_rtp_process_events(raop_rtp, cb_data)) {
break;
}
- volume_changed = (volume != raop_rtp->volume);
- volume = raop_rtp->volume;
- MUTEX_UNLOCK(raop_rtp->run_mutex);
-
- /* Call set_volume callback if changed */
- if (volume_changed) {
- raop_rtp->callbacks.audio_set_volume(raop_rtp->callbacks.cls, cb_data, volume);
- }
/* Set timeout value to 5ms */
tv.tv_sec = 0;
continue;
} else if (ret == -1) {
/* FIXME: Error happened */
- logger_log(raop_rtp->logger, LOGGER_INFO, "Error in select\n");
+ logger_log(raop_rtp->logger, LOGGER_INFO, "Error in select");
break;
}
if (stream_fd == -1 && FD_ISSET(raop_rtp->dsock, &rfds)) {
struct sockaddr_storage saddr;
socklen_t saddrlen;
- logger_log(raop_rtp->logger, LOGGER_INFO, "Accepting client\n");
+ logger_log(raop_rtp->logger, LOGGER_INFO, "Accepting client");
saddrlen = sizeof(saddr);
stream_fd = accept(raop_rtp->dsock, (struct sockaddr *)&saddr, &saddrlen);
if (stream_fd == -1) {
/* FIXME: Error happened */
- logger_log(raop_rtp->logger, LOGGER_INFO, "Error in accept %d %s\n", errno, strerror(errno));
+ logger_log(raop_rtp->logger, LOGGER_INFO, "Error in accept %d %s", errno, strerror(errno));
break;
}
}
ret = recv(stream_fd, (char *)(packet+packetlen), sizeof(packet)-packetlen, 0);
if (ret == 0) {
/* TCP socket closed */
- logger_log(raop_rtp->logger, LOGGER_INFO, "TCP socket closed\n");
+ logger_log(raop_rtp->logger, LOGGER_INFO, "TCP socket closed");
break;
} else if (ret == -1) {
/* FIXME: Error happened */
- logger_log(raop_rtp->logger, LOGGER_INFO, "Error in recv\n");
+ logger_log(raop_rtp->logger, LOGGER_INFO, "Error in recv");
break;
}
packetlen += ret;
rtplen = (packet[2] << 8) | packet[3];
if (rtplen > sizeof(packet)) {
/* FIXME: Too long packet */
- logger_log(raop_rtp->logger, LOGGER_INFO, "Error, packet too long %d\n", rtplen);
+ logger_log(raop_rtp->logger, LOGGER_INFO, "Error, packet too long %d", rtplen);
break;
}
if (packetlen < 4+rtplen) {
closesocket(stream_fd);
}
- logger_log(raop_rtp->logger, LOGGER_INFO, "Exiting thread\n");
+ logger_log(raop_rtp->logger, LOGGER_INFO, "Exiting TCP RAOP thread");
raop_rtp->callbacks.audio_destroy(raop_rtp->callbacks.cls, cb_data);
return 0;
raop_rtp_start(raop_rtp_t *raop_rtp, int use_udp, unsigned short control_rport, unsigned short timing_rport,
unsigned short *control_lport, unsigned short *timing_lport, unsigned short *data_lport)
{
+ int use_ipv6 = 0;
+
assert(raop_rtp);
MUTEX_LOCK(raop_rtp->run_mutex);
/* Initialize ports and sockets */
raop_rtp->control_rport = control_rport;
raop_rtp->timing_rport = timing_rport;
- if (raop_rtp_init_sockets(raop_rtp, 1, use_udp) < 0) {
- logger_log(raop_rtp->logger, LOGGER_INFO, "Initializing sockets failed\n");
+ if (raop_rtp->remote_saddr.ss_family == AF_INET6) {
+ use_ipv6 = 1;
+ }
+ if (raop_rtp_init_sockets(raop_rtp, use_ipv6, use_udp) < 0) {
+ logger_log(raop_rtp->logger, LOGGER_INFO, "Initializing sockets failed");
MUTEX_UNLOCK(raop_rtp->run_mutex);
return;
}
/* Set volume in thread instead */
MUTEX_LOCK(raop_rtp->run_mutex);
raop_rtp->volume = volume;
+ raop_rtp->volume_changed = 1;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+}
+
+void
+raop_rtp_set_metadata(raop_rtp_t *raop_rtp, const char *data, int datalen)
+{
+ unsigned char *metadata;
+
+ assert(raop_rtp);
+
+ if (datalen <= 0) {
+ return;
+ }
+ metadata = malloc(datalen);
+ assert(metadata);
+ memcpy(metadata, data, datalen);
+
+ /* Set metadata in thread instead */
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ raop_rtp->metadata = metadata;
+ raop_rtp->metadata_len = datalen;
+ MUTEX_UNLOCK(raop_rtp->run_mutex);
+}
+
+void
+raop_rtp_set_coverart(raop_rtp_t *raop_rtp, const char *data, int datalen)
+{
+ unsigned char *coverart;
+
+ assert(raop_rtp);
+
+ if (datalen <= 0) {
+ return;
+ }
+ coverart = malloc(datalen);
+ assert(coverart);
+ memcpy(coverart, data, datalen);
+
+ /* Set coverart in thread instead */
+ MUTEX_LOCK(raop_rtp->run_mutex);
+ raop_rtp->coverart = coverart;
+ raop_rtp->coverart_len = datalen;
MUTEX_UNLOCK(raop_rtp->run_mutex);
}