Bring enencrypted audio handling into the development branch
This commit is contained in:
@@ -1,3 +1,16 @@
|
||||
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!)
|
||||
* 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 `<item>..</item>` items of information.
|
||||
|
||||
Version 2.3.4
|
||||
----
|
||||
* Enhancement
|
||||
|
||||
+22
-4
@@ -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_buffer_desired_length = buffer_length_hardware;
|
||||
|
||||
if (alsa_mix_dev == NULL)
|
||||
alsa_mix_dev = alsa_out_dev;
|
||||
|
||||
@@ -80,9 +80,9 @@ typedef struct {
|
||||
#ifdef SUPPORT_CONFIG_FILES
|
||||
char *configfile;
|
||||
#endif
|
||||
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;
|
||||
|
||||
@@ -118,9 +118,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);
|
||||
|
||||
+1
-1
@@ -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])
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1236,20 +1235,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();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "audio.h"
|
||||
|
||||
typedef struct {
|
||||
int encrypted;
|
||||
uint8_t aesiv[16], aeskey[16];
|
||||
int32_t fmtp[12];
|
||||
} stream_cfg;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -424,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!");
|
||||
|
||||
@@ -628,13 +628,23 @@ 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);
|
||||
#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
|
||||
@@ -809,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
|
||||
|
||||
@@ -942,13 +953,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, "<item>\n<type>%x</type><code>%x</code><length>%u</length>\n", type, code,
|
||||
snprintf(thestring, 1024, "<item><type>%x</type><code>%x</code><length>%u</length>", type, code,
|
||||
length);
|
||||
ret = non_blocking_write(fd, thestring, strlen(thestring));
|
||||
if (ret < 1)
|
||||
return;
|
||||
if ((data != NULL) && (length > 0)) {
|
||||
snprintf(thestring, 1024, "<data encoding=\"base64\">\n");
|
||||
snprintf(thestring, 1024, "\n<data encoding=\"base64\">\n");
|
||||
ret = non_blocking_write(fd, thestring, strlen(thestring));
|
||||
if (ret < 1) // no reader
|
||||
return;
|
||||
@@ -973,11 +984,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, "</data>\n</item>\n");
|
||||
snprintf(thestring, 1024, "</data>");
|
||||
ret = non_blocking_write(fd, thestring, strlen(thestring));
|
||||
if (ret < 1) // no reader
|
||||
return;
|
||||
}
|
||||
snprintf(thestring, 1024, "</item>\n");
|
||||
ret = non_blocking_write(fd, thestring, strlen(thestring));
|
||||
if (ret < 1) // no reader
|
||||
return;
|
||||
}
|
||||
|
||||
void *metadata_thread_function(void *ignore) {
|
||||
@@ -1006,6 +1021,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 +1065,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) {
|
||||
@@ -1070,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
|
||||
@@ -1112,6 +1127,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,48 +1149,69 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag
|
||||
|
||||
cp = next;
|
||||
}
|
||||
|
||||
if (!paesiv || !prsaaeskey || !pfmtp) {
|
||||
warn("required params 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 (!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;
|
||||
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;
|
||||
}
|
||||
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"));
|
||||
|
||||
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 {
|
||||
@@ -1421,11 +1458,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user