From 0da133ca23ea4823e5ea3052821e9f3fbac7f57f Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Mon, 15 Jun 2015 11:43:41 +0000 Subject: [PATCH 01/18] Add messages to see what's missing in whaale's announce message --- rtsp.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rtsp.c b/rtsp.c index 3cf03d85..3144eeec 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1112,6 +1112,7 @@ static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req, rtsp_m static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { // interrupt session if permitted if ((config.allow_session_interruption == 1) || (pthread_mutex_trylock(&play_lock) == 0)) { + resp->respcode = 456; // 456 - Header Field Not Valid for Resource char *paesiv = NULL; char *prsaaeskey = NULL; char *pfmtp = NULL; @@ -1133,7 +1134,19 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag cp = next; } + + if (paesiv==NULL) { + warn("AESIV missing from ANNOUNCE"); + } + if (prsaaeskey==NULL) { + warn("RSAAESKEY missing from ANNOUNCE"); + } + + if (pfmtp==NULL) { + warn("FMTP missing from ANNOUNCE"); + } + if (!paesiv || !prsaaeskey || !pfmtp) { warn("required params missing from announce"); goto out; From 5c33488eafb951a3be3f99519e926d422609018c Mon Sep 17 00:00:00 2001 From: Mke Brady Date: Mon, 15 Jun 2015 21:33:35 +0100 Subject: [PATCH 02/18] Print our whole meessage content on error --- rtsp.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/rtsp.c b/rtsp.c index 3144eeec..20c378a5 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1148,7 +1148,19 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag } if (!paesiv || !prsaaeskey || !pfmtp) { - warn("required params missing from announce"); + warn("required params missing from the following ANNOUNCE message:"); + // print each line of the request content + // the problem is that nextline has replace all returns, newlines, etc. by NULLs + char *cp = req->content; + int cp_left = req->contentlength; + while (cp_left>1) { + if (strlen(cp)!=0) + warn(" %s",cp); + cp+=strlen(cp)+1; + cp_left-=strlen(cp)+1; + } + char *next; + goto out; } From 2a1fe7afeda24b3ca4f3a6e6b9a461690bbc2bf4 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 17 Jun 2015 12:10:33 +0100 Subject: [PATCH 03/18] Add ability to deal with an unencrypted audio stream --- player.c | 47 ++++++++++++++++++++------------------- player.h | 1 + rtsp.c | 67 ++++++++++++++++++++++++++------------------------------ 3 files changed, 56 insertions(+), 59 deletions(-) diff --git a/player.c b/player.c index df12eb27..94afea42 100644 --- a/player.c +++ b/player.c @@ -83,9 +83,9 @@ static aes_context dctx; static pthread_t player_thread; static int please_stop; +static int encrypted; // Normally the audio is encrypted, but it may not be -static int - connection_state_to_output; // if true, then play incoming stuff; if false drop everything +static int connection_state_to_output; // if true, then play incoming stuff; if false drop everything static alac_file *decoder_info; @@ -226,23 +226,23 @@ static void alac_decode(short *dest, uint8_t *buf, int len) { unsigned char packet[MAX_PACKET]; unsigned char packetp[MAX_PACKET]; assert(len <= MAX_PACKET); - - unsigned char iv[16]; - int aeslen = len & ~0xf; - memcpy(iv, aesiv, sizeof(iv)); - -#ifdef HAVE_LIBPOLARSSL - aes_crypt_cbc(&dctx, AES_DECRYPT, aeslen, iv, buf, packet); -#endif -#ifdef HAVE_LIBSSL - AES_cbc_encrypt(buf, packet, aeslen, &aes, iv, AES_DECRYPT); -#endif - - memcpy(packet + aeslen, buf + aeslen, len - aeslen); - int outsize; - alac_decode_frame(decoder_info, packet, dest, &outsize); + if (encrypted) { + unsigned char iv[16]; + int aeslen = len & ~0xf; + memcpy(iv, aesiv, sizeof(iv)); +#ifdef HAVE_LIBPOLARSSL + aes_crypt_cbc(&dctx, AES_DECRYPT, aeslen, iv, buf, packet); +#endif +#ifdef HAVE_LIBSSL + AES_cbc_encrypt(buf, packet, aeslen, &aes, iv, AES_DECRYPT); +#endif + memcpy(packet + aeslen, buf + aeslen, len - aeslen); + alac_decode_frame(decoder_info, packet, dest, &outsize); + } else { + alac_decode_frame(decoder_info, buf, dest, &outsize); + } assert(outsize == FRAME_BYTES(frame_size)); } @@ -1236,20 +1236,21 @@ void player_flush(uint32_t timestamp) { int player_play(stream_cfg *stream) { packet_count = 0; + encrypted = stream->encrypted; if (config.buffer_start_fill > BUFFER_FRAMES) die("specified buffer starting fill %d > buffer size %d", config.buffer_start_fill, BUFFER_FRAMES); - + if (encrypted) { #ifdef HAVE_LIBPOLARSSL - memset(&dctx, 0, sizeof(aes_context)); - aes_setkey_dec(&dctx, stream->aeskey, 128); + memset(&dctx, 0, sizeof(aes_context)); + aes_setkey_dec(&dctx, stream->aeskey, 128); #endif #ifdef HAVE_LIBSSL - AES_set_decrypt_key(stream->aeskey, 128, &aes); + AES_set_decrypt_key(stream->aeskey, 128, &aes); #endif - - aesiv = stream->aesiv; + aesiv = stream->aesiv; + } init_decoder(stream->fmtp); // must be after decoder init init_buffer(); diff --git a/player.h b/player.h index 9f4b93d6..b9cc900a 100644 --- a/player.h +++ b/player.h @@ -4,6 +4,7 @@ #include "audio.h" typedef struct { + int encrypted; uint8_t aesiv[16], aeskey[16]; int32_t fmtp[12]; } stream_cfg; diff --git a/rtsp.c b/rtsp.c index 20c378a5..0c7fa9eb 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1135,20 +1135,16 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag cp = next; } - if (paesiv==NULL) { - warn("AESIV missing from ANNOUNCE"); - } - - if (prsaaeskey==NULL) { - warn("RSAAESKEY missing from ANNOUNCE"); - } - - if (pfmtp==NULL) { - warn("FMTP missing from ANNOUNCE"); + if ((paesiv==NULL) && (prsaaeskey==NULL)) { + debug(1,"Unencrypted session requested?"); + conn->stream.encrypted = 0; + } else { + conn->stream.encrypted = 1; + debug(1,"Encrypted session requested?"); } - if (!paesiv || !prsaaeskey || !pfmtp) { - warn("required params missing from the following ANNOUNCE message:"); + if (!pfmtp) { + warn("FMTP params missing from the following ANNOUNCE message:"); // print each line of the request content // the problem is that nextline has replace all returns, newlines, etc. by NULLs char *cp = req->content; @@ -1159,32 +1155,31 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag cp+=strlen(cp)+1; cp_left-=strlen(cp)+1; } - char *next; - goto out; } - - int len, keylen; - uint8_t *aesiv = base64_dec(paesiv, &len); - if (len != 16) { - warn("client announced aeskey of %d bytes, wanted 16", len); + + if (conn->stream.encrypted) { + int len, keylen; + uint8_t *aesiv = base64_dec(paesiv, &len); + if (len != 16) { + warn("client announced aeskey of %d bytes, wanted 16", len); + free(aesiv); + goto out; + } + memcpy(conn->stream.aesiv, aesiv, 16); free(aesiv); - goto out; - } - memcpy(conn->stream.aesiv, aesiv, 16); - free(aesiv); - uint8_t *rsaaeskey = base64_dec(prsaaeskey, &len); - uint8_t *aeskey = rsa_apply(rsaaeskey, len, &keylen, RSA_MODE_KEY); - free(rsaaeskey); - if (keylen != 16) { - warn("client announced rsaaeskey of %d bytes, wanted 16", keylen); + uint8_t *rsaaeskey = base64_dec(prsaaeskey, &len); + uint8_t *aeskey = rsa_apply(rsaaeskey, len, &keylen, RSA_MODE_KEY); + free(rsaaeskey); + if (keylen != 16) { + warn("client announced rsaaeskey of %d bytes, wanted 16", keylen); + free(aeskey); + goto out; + } + memcpy(conn->stream.aeskey, aeskey, 16); free(aeskey); - goto out; } - memcpy(conn->stream.aeskey, aeskey, 16); - free(aeskey); - int i; for (i = 0; i < sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]); i++) conn->stream.fmtp[i] = atoi(strsep(&pfmtp, " \t")); @@ -1446,11 +1441,11 @@ static void *rtsp_conversation_thread_func(void *pconn) { struct method_handler *mh; for (mh = method_handlers; mh->method; mh++) { if (!strcmp(mh->method, req->method)) { - // debug(1,"RTSP Packet received of type \"%s\":",mh->method), - // msg_print_debug_headers(req); + //debug(1,"RTSP Packet received of type \"%s\":",mh->method), + //msg_print_debug_headers(req); mh->handler(conn, req, resp); - // debug(1,"RTSP Response:"); - // msg_print_debug_headers(resp); + //debug(1,"RTSP Response:"); + //msg_print_debug_headers(resp); break; } } From 41f47f79f575843f3f0e507ffac5c5e64c674340 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 17 Jun 2015 18:42:05 +0100 Subject: [PATCH 04/18] Tidy up and move packet counter inside mutex. --- player.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/player.c b/player.c index 94afea42..3ae9bac7 100644 --- a/player.c +++ b/player.c @@ -294,9 +294,8 @@ static void free_buffer(void) { void player_put_packet(seq_t seqno, uint32_t timestamp, uint8_t *data, int len) { - packet_count++; - pthread_mutex_lock(&ab_mutex); + packet_count++; time_of_last_audio_packet = get_absolute_time_in_fp(); if (connection_state_to_output) { // if we are supposed to be processing these packets From 7bc6d08872099124a0ce98e6a23056cb80b2e61d Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 17 Jun 2015 18:43:00 +0100 Subject: [PATCH 05/18] Enable control thread to handle retransmitted audio packets. Seems like a bug that they should show up here, but whaale does it. --- rtp.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/rtp.c b/rtp.c index 37e2eccb..b372d94f 100644 --- a/rtp.c +++ b/rtp.c @@ -152,7 +152,7 @@ static void *rtp_audio_receiver(void *arg) { static void *rtp_control_receiver(void *arg) { // we inherit the signal mask (SIGUSR1) reference_timestamp = 0; // nothing valid received yet - uint8_t packet[2048]; + uint8_t packet[2048], *pktp; struct timespec tn; uint64_t remote_time_of_sync, local_time_now, remote_time_now; uint32_t sync_rtp_timestamp, rtp_timestamp_less_latency; @@ -213,6 +213,24 @@ static void *rtp_control_receiver(void *arg) { } else { debug(1, "Sync packet received before we got a timing packet back."); } + } else if (packet[1] == 0xd6) { // resent audio data in the control path -- whaale only? + // debug(1, "Control Port -- Retransmitted Audio Data Packet received."); + pktp = packet+4; + plen -= 4; + seq_t seqno = ntohs(*(unsigned short *)(pktp + 2)); + + uint32_t timestamp = ntohl(*(unsigned long *)(pktp + 4)); + + pktp += 12; + plen -= 12; + + // check if packet contains enough content to be reasonable + if (plen >= 16) { + player_put_packet(seqno, timestamp, pktp, plen); + continue; + } else { + debug(1, "Too-short retransmitted audio packet received in control port, ignored."); + } } else debug(1, "Control Port -- Unknown RTP packet of type 0x%02X length %d.", packet[1], nread); } From 9d12f52abb467c64b5a5a9e6ffd7937e9636ae2a Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 17 Jun 2015 20:50:52 +0100 Subject: [PATCH 06/18] Quieten a debug message --- rtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtp.c b/rtp.c index b372d94f..06df8ac2 100644 --- a/rtp.c +++ b/rtp.c @@ -442,7 +442,7 @@ static int bind_port(SOCKADDR *remote, int *sock) { } while ((ret<0) && (errno==EADDRINUSE) && (desired_port!=0) && (desired_port++ < config.udp_port_base+config.udp_port_range)); - debug(1,"UDP port chosen: %d.",desired_port); + // debug(1,"UDP port chosen: %d.",desired_port); if (ret < 0) { die("error: could not bind a UDP port!"); From b352e7cccaef13d4d9890a98ec6e9077657c469e Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 17 Jun 2015 20:51:56 +0100 Subject: [PATCH 07/18] Fix .. generation, add DACP-ID and Active-Remote metadata --- rtsp.c | 54 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/rtsp.c b/rtsp.c index 0c7fa9eb..24ff917a 100644 --- a/rtsp.c +++ b/rtsp.c @@ -628,13 +628,18 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message * char *ar = msg_get_header(req, "Active-Remote"); if (ar) { - // debug(1,"Active-Remote string seen: \"%s\".",ar); + debug(1,"Active-Remote string seen: \"%s\".",ar); // get the active remote char *p; active_remote = strtoul(ar, &p, 10); - // debug(1,"Active Remote is %u.",active_remote); + send_metadata('ssnc','acre',ar,strlen(ar),req,1); } + ar = msg_get_header(req, "DACP-ID"); + if (ar) { + debug(1,"DACP-ID string seen: \"%s\".",ar); + send_metadata('ssnc','daid',ar,strlen(ar),req,1); + } // select latency // if iTunes V10 or later is detected, use the iTunes latency setting // if AirPlay is detected, use the AirPlay latency setting @@ -942,13 +947,13 @@ void metadata_process(uint32_t type, uint32_t code, char *data, uint32_t length) if (fd < 0) return; char thestring[1024]; - snprintf(thestring, 1024, "\n%x%x%u\n", type, code, + snprintf(thestring, 1024, "%x%x%u", type, code, length); ret = non_blocking_write(fd, thestring, strlen(thestring)); if (ret < 1) return; if ((data != NULL) && (length > 0)) { - snprintf(thestring, 1024, "\n"); + snprintf(thestring, 1024, "\n\n"); ret = non_blocking_write(fd, thestring, strlen(thestring)); if (ret < 1) // no reader return; @@ -973,11 +978,15 @@ void metadata_process(uint32_t type, uint32_t code, char *data, uint32_t length) remaining_data += towrite_count; remaining_count -= towrite_count; } - snprintf(thestring, 1024, "\n\n"); + snprintf(thestring, 1024, ""); ret = non_blocking_write(fd, thestring, strlen(thestring)); if (ret < 1) // no reader return; } + snprintf(thestring, 1024, "\n"); + ret = non_blocking_write(fd, thestring, strlen(thestring)); + if (ret < 1) // no reader + return; } void *metadata_thread_function(void *ignore) { @@ -1006,6 +1015,24 @@ void metadata_init(void) { int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier, int block) { + + // parameters: type, code, pointer to data or NULL, length of data or NULL, the rtsp_message or + // NULL + // the rtsp_message is sent for 'core' messages, because it contains the data and must not be + // freed until the data has been read. So, it is passed to send_metadata to be retained, + // sent to the thread where metadata is processed and released (and probably freed). + + // The rtsp_message is also sent for certain non-'core' messages. + + // The reading of the parameters is a bit complex + // If the rtsp_message field is non-null, then it represents an rtsp_message which should be freed + // in the thread handler when the parameter pointed to by the pointer and specified by the length + // is finished with + // If the rtsp_message is NULL, then if the pointer is non-null, it points to a malloc'ed block + // and should be freed when the thread is finished with it. The length of the data in the block is + // given in length + // If the rtsp_message is NULL and the pointer is also NULL, nothing further is done. + metadata_package pack; pack.type = type; pack.code = code; @@ -1032,21 +1059,6 @@ static void handle_set_parameter_metadata(rtsp_conn_info *conn, rtsp_message *re // inform the listener that a set of metadata is starting // this doesn't include the cover art though... - // parameters: type, code, pointer to data or NULL, length of data or NULL, the rtsp_message or - // NULL - // the rtsp_message is sent for 'core' messages, because it contains the data and must not be - // freed until the data has been read. So, it is passed to send_metadata to be retained, - // sent to the thread where metadata is processed and released (and probably freed). - - // The reading of the parameters is a bit complex - // If the rtsp_message field is non-null, then it represents an rtsp_message which should be freed - // in the thread handler when the parameter pointed to by the pointer and specified by the length - // is finished with - // If the rtsp_message is NULL, then if the pointer is non-null, it points to a malloc'ed block - // and should be freed when the thread is finished with it. The length of the data in the block is - // given in length - // If the rtsp_message is NULL and the pointer is also NULL, nothing further is done. - send_metadata('ssnc', 'mdst', NULL, 0, NULL, 1); while (off < cl) { @@ -1140,7 +1152,7 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag conn->stream.encrypted = 0; } else { conn->stream.encrypted = 1; - debug(1,"Encrypted session requested?"); + debug(1,"Encrypted session requested"); } if (!pfmtp) { From c4e885047447e1baa58d19ea262058eba863e4ca Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Wed, 17 Jun 2015 21:07:43 +0100 Subject: [PATCH 08/18] Update RELEASENOTES.md --- RELEASENOTES.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d18ee425..f379adfb 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,3 +1,12 @@ +Version 2.3.5 -- forthcoming +---- +* Enhancements + * Extra metadata: when a play session starts, the "Active-Remote" and "DACP-ID" fields -- information that can be used to identify the source -- are provided as metadata, with the type `ssnc` and the codes `acre` and `daid` respectively. The IDs are provided as strings. + * Unencrypted audio data. The iOS player "Whaale" attempts to send unencrypted audio, presumably to save processing effort; if unsuccessful, it will send encrypted audio as normal. Shairport Sync now recognises and handles unencrypted audio data. + * Handle retransmitted audio in the control channel. When a packet of audio is missed, Shairport Sync will ask for it to be retransmitted. Normally the retransmitted audio comes back the audio channel, but "Whaale" sends it back in the control channel. (I think this is a bug in "Whaale".) Shairport Sync will now correctly handle retransmitted audio packets coming back in the control channel. +* Bugfixes + * Generate properly-formed `..` items of information. + Version 2.3.4 ---- * Enhancement From 2627757e005e79043a93f28518e531ea465e93c8 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Thu, 18 Jun 2015 16:12:15 +0000 Subject: [PATCH 09/18] Compile DACP-ID and Active-Remote metadata stuff only if metadata is selected. --- rtsp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rtsp.c b/rtsp.c index 24ff917a..2c4af6f8 100644 --- a/rtsp.c +++ b/rtsp.c @@ -632,14 +632,19 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message * // get the active remote char *p; active_remote = strtoul(ar, &p, 10); +#ifdef CONFIG_METADATA send_metadata('ssnc','acre',ar,strlen(ar),req,1); +#endif } +#ifdef CONFIG_METADATA ar = msg_get_header(req, "DACP-ID"); if (ar) { debug(1,"DACP-ID string seen: \"%s\".",ar); send_metadata('ssnc','daid',ar,strlen(ar),req,1); } +#endif + // select latency // if iTunes V10 or later is detected, use the iTunes latency setting // if AirPlay is detected, use the AirPlay latency setting From 7626cc2f2e65c2aff2e7041604df8dc25b8463ea Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Thu, 18 Jun 2015 16:13:02 +0000 Subject: [PATCH 10/18] Use uint32_t in place of uint for our stuff --- common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index 792dd36c..73b82f8e 100644 --- a/common.h +++ b/common.h @@ -78,9 +78,9 @@ typedef struct { char *logfile; char *errfile; char *configfile; - uint audio_backend_buffer_desired_length; // this will be the desired number of frames in the + uint32_t audio_backend_buffer_desired_length; // this will be the desired number of frames in the // audio backend buffer -- the DAC buffer for ALSA - uint audio_backend_latency_offset; // this will be the offset to compensate for any fixed latency + uint32_t audio_backend_latency_offset; // this will be the offset to compensate for any fixed latency // there might be in the audio } shairport_cfg; From 193fd3f0caefdd3ed6960a81bf526209e1620b32 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Thu, 18 Jun 2015 16:14:40 +0000 Subject: [PATCH 11/18] Use two different buffer sizes for alsa -- a short one for software mixer, a longer one for hardware mixer, giving more resilience against delays, etc. without affecting responsiveness --- audio_alsa.c | 26 ++++++++++++++++++++++---- scripts/shairport-sync.conf | 3 ++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/audio_alsa.c b/audio_alsa.c index 97531b46..7d05fb5e 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -95,23 +95,39 @@ static void help(void) { static int init(int argc, char **argv) { const char *str; int value; + + uint32_t buffer_length_hardware = 22050; int hardware_mixer = 0; - config.audio_backend_buffer_desired_length = 6615; // this is the default for ALSA config.audio_backend_latency_offset = 0; // this is the default for ALSA + config.audio_backend_buffer_desired_length = 6615; // default for alsa with a software mixer + + // get settings from settings file first, allow them to be over-ridden by command line options if (config.cfg != NULL) { /* Get the desired buffer size setting. */ - if (config_lookup_int(config.cfg, "alsa.audio_backend_buffer_desired_length", &value)) { + if (config_lookup_int(config.cfg, "alsa.audio_backend_buffer_desired_length_software", &value)) { if ((value < 0) || (value > 66150)) - die("Invalid alsa audio backend buffer desired length \"%d\". It should be between 0 and " + die("Invalid alsa audio backend buffer desired length (software) \"%d\". It should be between 0 and " "66150, default is 6615", value); - else + else { config.audio_backend_buffer_desired_length = value; + } + } + + /* Get the desired buffer size setting. */ + if (config_lookup_int(config.cfg, "alsa.audio_backend_buffer_desired_length_hardware", &value)) { + if ((value < 0) || (value > 66150)) + die("Invalid alsa audio backend buffer desired length (hardware) \"%d\". It should be between 0 and " + "66150, default is 22050", + value); + else { + buffer_length_hardware = value; + } } /* Get the latency offset. */ @@ -183,6 +199,8 @@ static int init(int argc, char **argv) { if (!hardware_mixer) return 0; + + config.audio_backend_latency_offset = buffer_length_hardware; if (alsa_mix_dev == NULL) alsa_mix_dev = alsa_out_dev; diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 3bfcde5b..d20f91a4 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -59,7 +59,8 @@ alsa = // mixer_device = "default"; //actually, the mixer default is whatever the output_device is. Normally you wouldn't have to use this. // mixer_control_name = "PCM"; // the name of the mixer to use -- there is no default. // audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410. -// audio_backend_buffer_desired_length = 6615; // If set too small, buffer underflow occurs on low-powered machines. Too long and the response times with software mixer become annoying. +// audio_backend_buffer_desired_length_software = 6615; // If set too small, buffer underflow occurs on low-powered machines. Too long and the response times with software mixer become annoying. +// audio_backend_buffer_desired_length_hardware = 22050; // If set too large, there is less opportunity for late packets to be retrieved. }; // These are parameters for the pipe back end, an experimental back end that directs output to a pipe. From 4654d9201dafe81d1254c4c8118f39ccb94f6a91 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Thu, 18 Jun 2015 16:56:04 +0000 Subject: [PATCH 12/18] Fix small bug associating chosen buffer length --- audio_alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio_alsa.c b/audio_alsa.c index 7d05fb5e..2f3a1acb 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -200,7 +200,7 @@ static int init(int argc, char **argv) { if (!hardware_mixer) return 0; - config.audio_backend_latency_offset = buffer_length_hardware; + config.audio_backend_buffer_desired_length = buffer_length_hardware; if (alsa_mix_dev == NULL) alsa_mix_dev = alsa_out_dev; From 2f0a9c01bf8e4c73d14f4c2eb98154eb52717702 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Thu, 18 Jun 2015 18:08:34 +0100 Subject: [PATCH 13/18] Update RELEASENOTES.md --- RELEASENOTES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f379adfb..42eea4ab 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,8 +1,9 @@ Version 2.3.5 -- forthcoming ---- * Enhancements + * Two default buffer lengths for ALSA -- default 6615 frames if a software volume control is used, to minimise the response time to pause and volume control changes; default 22050 frames if a hardware volume control is used, to give more resilience to timing problems, sudden processor loading, etc. This is especially useful if you are processing metadata and artwork on the same machine. * Extra metadata: when a play session starts, the "Active-Remote" and "DACP-ID" fields -- information that can be used to identify the source -- are provided as metadata, with the type `ssnc` and the codes `acre` and `daid` respectively. The IDs are provided as strings. - * Unencrypted audio data. The iOS player "Whaale" attempts to send unencrypted audio, presumably to save processing effort; if unsuccessful, it will send encrypted audio as normal. Shairport Sync now recognises and handles unencrypted audio data. + * Unencrypted audio data. The iOS player "Whaale" attempts to send unencrypted audio, presumably to save processing effort; if unsuccessful, it will send encrypted audio as normal. Shairport Sync now recognises and handles unencrypted audio data. (Apparently it always advertised that it could process unencrypted audio!) * Handle retransmitted audio in the control channel. When a packet of audio is missed, Shairport Sync will ask for it to be retransmitted. Normally the retransmitted audio comes back the audio channel, but "Whaale" sends it back in the control channel. (I think this is a bug in "Whaale".) Shairport Sync will now correctly handle retransmitted audio packets coming back in the control channel. * Bugfixes * Generate properly-formed `..` items of information. From c1198ae243e378fcdb2be849469853225c846fd8 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 19 Jun 2015 13:00:14 +0000 Subject: [PATCH 14/18] Remove a few unused declarations --- common.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/common.h b/common.h index 73b82f8e..7880a9d4 100644 --- a/common.h +++ b/common.h @@ -116,9 +116,6 @@ uint64_t get_absolute_time_in_fp(void); uint32_t uatoi(const char *nptr); shairport_cfg config; -char sender_name[1024]; -char sender_ip[1024]; -char album_name[1024]; // we might need this for picture diagnostics config_t config_file_stuff; void command_start(void); From 15be54e270c6d09f6678cbb65b9e83d873e657ae Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 19 Jun 2015 13:01:29 +0000 Subject: [PATCH 15/18] Add code to announce device name and or user agent when a play session is started. --- rtsp.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/rtsp.c b/rtsp.c index 2c4af6f8..940246a8 100644 --- a/rtsp.c +++ b/rtsp.c @@ -819,7 +819,8 @@ static void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *r // I guess the timestamps wrap at 2^32. // 'mdst' -- a sequence of metadata is about to start // 'mden' -- a sequence of metadata has ended -// 'snam' -- the name of the originator -- e.g. "Joe's iPhone" or "iTunes...". +// 'snam' -- A device -- e.g. "Joe's iPhone" -- has opened a play session. Specifically, it's the "X-Apple-Client-Name" string +// 'snua' -- A "user agent" -- e.g. "iTunes/12..." -- has opened a play session. Specifically, it's the "User-Agent" string // // including a simple base64 encoder to minimise malloc/free activity @@ -1087,9 +1088,6 @@ static void handle_set_parameter_metadata(rtsp_conn_info *conn, rtsp_message *re // inform the listener that a set of metadata is ending send_metadata('ssnc', 'mden', NULL, 0, NULL, 1); - // send the user some shairport-originated metadata - // send the name of the player, e.g. "Joe's iPhone" or "iTunes" - send_metadata('ssnc', 'sndr', strdup(sender_name), strlen(sender_name), NULL, 1); } #endif @@ -1203,15 +1201,17 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag char *hdr = msg_get_header(req, "X-Apple-Client-Name"); if (hdr) { - strncpy(sender_name, hdr, 1024); - debug(1, "Play connection from \"%s\".", hdr); - } else { - hdr = msg_get_header(req, "User-Agent"); - if (hdr) { - debug(1, "Play connection from \"%s\".", hdr); - strncpy(sender_name, hdr, 1024); - } else - sender_name[0] = 0; + debug(1, "Play connection from device named \"%s\".", hdr); + #ifdef CONFIG_METADATA + send_metadata('ssnc','snam',hdr,strlen(hdr),req,1); + #endif + } + hdr = msg_get_header(req, "User-Agent"); + if (hdr) { + debug(1, "Play connection from user agent \"%s\".", hdr); + #ifdef CONFIG_METADATA + send_metadata('ssnc','snua',hdr,strlen(hdr),req,1); + #endif } resp->respcode = 200; } else { From fced2eca655452ce30b72437c8983ac34ffbf29a Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Fri, 19 Jun 2015 14:12:37 +0100 Subject: [PATCH 16/18] Update RELEASENOTES.md --- RELEASENOTES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 42eea4ab..f842d23a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,6 +1,9 @@ Version 2.3.5 -- forthcoming ---- +* Changes + * The metadata item 'sndr' is no longer sent in metadata. It's been replaced by 'snam' and 'snua' -- see below. * Enhancements + * When a play session is initiated by a source, it attempts to reserve the player by sending an "ANNOUNCE" packet. Typically, a source device name and/or a source "user agent" is sent as part of the packet. The "user agent" is usually the name of the sending application along with some more information. If metadata is enabled, the source name, if provided, is emitted as a metadata item with the type `ssnc` and code `snam` and similarly the user agent, if provided, is sent with the type `ssnc` and code `snua`. * Two default buffer lengths for ALSA -- default 6615 frames if a software volume control is used, to minimise the response time to pause and volume control changes; default 22050 frames if a hardware volume control is used, to give more resilience to timing problems, sudden processor loading, etc. This is especially useful if you are processing metadata and artwork on the same machine. * Extra metadata: when a play session starts, the "Active-Remote" and "DACP-ID" fields -- information that can be used to identify the source -- are provided as metadata, with the type `ssnc` and the codes `acre` and `daid` respectively. The IDs are provided as strings. * Unencrypted audio data. The iOS player "Whaale" attempts to send unencrypted audio, presumably to save processing effort; if unsuccessful, it will send encrypted audio as normal. Shairport Sync now recognises and handles unencrypted audio data. (Apparently it always advertised that it could process unencrypted audio!) From 26383dda8701f810965916727881482f3627615d Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 21 Jun 2015 14:27:04 +0100 Subject: [PATCH 17/18] Update configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index d2f4771c..2cbb879f 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [2.3.4], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [2.3.5], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) From 47d6aece5e484e53363d8d5ba991eb5ef10cc599 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Sun, 21 Jun 2015 14:53:03 +0100 Subject: [PATCH 18/18] Update common.h Make config file name conditional on config file support --- common.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common.h b/common.h index 7880a9d4..b0448e64 100644 --- a/common.h +++ b/common.h @@ -77,7 +77,9 @@ typedef struct { char *pidfile; char *logfile; char *errfile; +#ifdef SUPPORT_CONFIG_FILES char *configfile; +#endif uint32_t audio_backend_buffer_desired_length; // this will be the desired number of frames in the // audio backend buffer -- the DAC buffer for ALSA uint32_t audio_backend_latency_offset; // this will be the offset to compensate for any fixed latency