Add many improvements, including reliable interruption using new ports, safer pthread_cancel usage.

This commit is contained in:
Mike Brady
2018-11-24 22:40:13 +00:00
9 changed files with 269 additions and 136 deletions
+34 -4
View File
@@ -409,6 +409,8 @@ static int init(int argc, char **argv) {
debug(1, "alsa output device name is \"%s\".", alsa_out_dev);
if (hardware_mixer) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
if (alsa_mix_dev == NULL)
alsa_mix_dev = alsa_out_dev;
@@ -493,8 +495,9 @@ static int init(int argc, char **argv) {
}
close_mixer();
}
debug_mutex_unlock(&alsa_mutex, 3);
pthread_cleanup_pop(0); // release the mutex
debug_mutex_unlock(&alsa_mutex, 3); // release the mutex
pthread_cleanup_pop(0);
pthread_setcancelstate(oldState, NULL);
} else {
// debug(1, "Has no mixer and thus no hardware mute.");
}
@@ -508,7 +511,7 @@ static void deinit(void) {
stop();
}
int open_alsa_device(void) {
int actual_open_alsa_device(void) {
// the alsa mutex is already acquired when this is called
const snd_pcm_uframes_t minimal_buffer_headroom =
352 * 2; // we accept this much headroom in the hardware buffer, but we'll
@@ -878,6 +881,15 @@ int open_alsa_device(void) {
return 0;
}
int open_alsa_device(void) {
int result;
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
result = actual_open_alsa_device();
pthread_setcancelstate(oldState, NULL);
return result;
}
static void start(int i_sample_rate, int i_sample_format) {
// debug(2,"audio_alsa start called.");
if (i_sample_rate == 0)
@@ -937,6 +949,8 @@ int delay(long *the_delay) {
if (alsa_handle == NULL) {
return -ENODEV;
} else {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1);
int derr;
snd_pcm_state_t dac_state = snd_pcm_state(alsa_handle);
@@ -989,6 +1003,7 @@ int delay(long *the_delay) {
// here, occasionally pretend there's a problem with pcm_get_delay()
// if ((random() % 100000) < 3) // keep it pretty rare
// reply = -EIO; // pretend something bad has happened
pthread_setcancelstate(oldState, NULL);
return reply;
}
}
@@ -1008,10 +1023,13 @@ int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played) {
static int play(void *buf, int samples) {
// debug(3,"audio_alsa play called.");
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
int ret = 0;
if (alsa_handle == NULL) {
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1);
ret = open_alsa_device();
ret = actual_open_alsa_device();
if (ret == 0) {
if (audio_alsa.volume)
do_volume(set_volume);
@@ -1110,11 +1128,14 @@ static int play(void *buf, int samples) {
debug_mutex_unlock(&alsa_mutex, 3);
pthread_cleanup_pop(0); // release the mutex
}
pthread_setcancelstate(oldState, NULL);
return ret;
}
static void flush(void) {
// debug(2,"audio_alsa flush called.");
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1);
int derr;
do_mute(1);
@@ -1136,6 +1157,7 @@ static void flush(void) {
}
debug_mutex_unlock(&alsa_mutex, 3);
pthread_cleanup_pop(0); // release the mutex
pthread_setcancelstate(oldState, NULL);
}
static void stop(void) {
@@ -1154,6 +1176,8 @@ static void parameters(audio_parameters *info) {
}
void do_volume(double vol) { // caller is assumed to have the alsa_mutex when using this function
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
debug(3, "Setting volume db to %f.", vol);
set_volume = vol;
if (volume_set_request && (open_mixer() == 1)) {
@@ -1185,6 +1209,7 @@ void do_volume(double vol) { // caller is assumed to have the alsa_mutex when us
volume_set_request = 0; // any external request that has been made is now satisfied
close_mixer();
}
pthread_setcancelstate(oldState, NULL);
}
void volume(double vol) {
@@ -1225,6 +1250,9 @@ static void mute(int mute_state_requested) {
void do_mute(int mute_state_requested) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
// if a mute is requested now, then
// if an external mute request is in place, leave everything muted
// otherwise, if an external mute request is pending, action it
@@ -1265,4 +1293,6 @@ void do_mute(int mute_state_requested) {
}
}
mute_request_pending = 0;
pthread_setcancelstate(oldState, NULL);
}
+14 -7
View File
@@ -94,6 +94,20 @@ volatile int debuglev = 0;
sigset_t pselect_sigset;
static uint16_t UDPPortIndex = 0;
void resetFreeUDPPort() { UDPPortIndex = 0; }
uint16_t nextFreeUDPPort() {
if (UDPPortIndex == 0)
UDPPortIndex = config.udp_port_base;
else if (UDPPortIndex == (config.udp_port_base + config.udp_port_range - 1))
UDPPortIndex = config.udp_port_base;
else
UDPPortIndex++;
return UDPPortIndex;
}
int get_requested_connection_state_to_output() { return requested_connection_state_to_output; }
void set_requested_connection_state_to_output(int v) { requested_connection_state_to_output = v; }
@@ -173,10 +187,6 @@ void debug(int level, const char *format, ...) {
va_start(args, format);
vsnprintf(s, sizeof(s), format, args);
va_end(args);
int currentState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &currentState);
if (currentState == PTHREAD_CANCEL_ENABLE)
daemon_log(LOG_DEBUG, "Warning -- cancellation is enabled before logging");
if ((config.debugger_show_elapsed_time) && (config.debugger_show_relative_time))
daemon_log(LOG_DEBUG, "|% 20.9f|% 20.9f|%s", tss, tsl, s);
else if (config.debugger_show_relative_time)
@@ -185,9 +195,6 @@ void debug(int level, const char *format, ...) {
daemon_log(LOG_DEBUG, "% 20.9f|%s", tss, s);
else
daemon_log(LOG_DEBUG, "%s", s);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &currentState);
if (currentState == PTHREAD_CANCEL_ENABLE)
daemon_log(LOG_DEBUG, "Warning -- cancellation is enabled after logging");
pthread_setcancelstate(oldState, NULL);
}
+11
View File
@@ -243,6 +243,17 @@ void r64arrayinit();
uint64_t ranarray64u();
int64_t ranarray64i();
// if you are breaking in to a session, you need to avoid the ports of the current session
// if you are law-abiding, then you can reuse the ports.
// so, you can reset the free UDP ports minder when you're legit, and leave it otherwise
// the downside of using different ports each time is that it might make the firewall
// rules a bit more complex, as they need to allow more than the minimum three ports.
// a range of 10 is suggested anyway
void resetFreeUDPPort();
uint16_t nextFreeUDPPort();
volatile int debuglev;
void die(const char *format, ...);
void warn(const char *format, ...);
+1 -1
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.50])
AC_INIT([shairport-sync], [3.3d28], [mikebrady@eircom.net])
AC_INIT([shairport-sync], [3.3d29], [mikebrady@eircom.net])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([shairport.c])
AC_CONFIG_HEADERS([config.h])
+35 -26
View File
@@ -457,20 +457,23 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in
if ((conn->flush_rtp_timestamp != 0) && (actual_timestamp != conn->flush_rtp_timestamp) &&
(modulo_32_offset(actual_timestamp, conn->flush_rtp_timestamp) <
conn->input_rate * 10)) { // if it's less than 10 seconds
debug(2, "Dropping flushed packet in player_put_packet, seqno %u, timestamp %" PRIu32
debug(3, "Dropping flushed packet in player_put_packet, seqno %u, timestamp %" PRIu32
", flushing to "
"timestamp: %" PRIu32 ".",
seqno, actual_timestamp, conn->flush_rtp_timestamp);
conn->initial_reference_time = 0;
conn->initial_reference_timestamp = 0;
} else {
if ((conn->flush_rtp_timestamp != 0) &&
(modulo_32_offset(conn->flush_rtp_timestamp, actual_timestamp) > conn->input_rate / 5) &&
(modulo_32_offset(conn->flush_rtp_timestamp, actual_timestamp) < conn->input_rate)) {
// between 0.2 and 1 second
debug(2, "Dropping flush request in player_put_packet");
conn->flush_rtp_timestamp = 0;
}
/*
if ((conn->flush_rtp_timestamp != 0) &&
(modulo_32_offset(conn->flush_rtp_timestamp, actual_timestamp) > conn->input_rate / 5)
&&
(modulo_32_offset(conn->flush_rtp_timestamp, actual_timestamp) < conn->input_rate)) {
// between 0.2 and 1 second
debug(2, "Dropping flush request in player_put_packet");
conn->flush_rtp_timestamp = 0;
}
*/
abuf_t *abuf = 0;
@@ -850,11 +853,15 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
}
}
// if (conn->flush_rtp_timestamp != 0)
// debug(2,"flush_rtp_timestamp is %" PRIx32 " and curframe->given_timestamp is %" PRIx32
// ".", conn->flush_rtp_timestamp , curframe->given_timestamp);
if ((conn->flush_rtp_timestamp != 0) &&
(curframe->given_timestamp != conn->flush_rtp_timestamp) &&
(modulo_32_offset(curframe->given_timestamp, conn->flush_rtp_timestamp) <
conn->input_rate * 10)) { // if it's less than ten seconds
debug(2, "Dropping flushed packet in buffer_get_frame, seqno %u, timestamp %" PRIu32
debug(3, "Dropping flushed packet in buffer_get_frame, seqno %u, timestamp %" PRIu32
", flushing to "
"timestamp: %" PRIu32 ".",
curframe->sequence_number, curframe->given_timestamp, conn->flush_rtp_timestamp);
@@ -869,7 +876,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
conn->input_rate / 5) &&
(modulo_32_offset(conn->flush_rtp_timestamp, curframe->given_timestamp) <
conn->input_rate * 10)) {
debug(2, "Dropping flush request in buffer_get_frame");
debug(3, "Dropping flush request in buffer_get_frame");
conn->flush_rtp_timestamp = 0;
}
}
@@ -945,12 +952,12 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
conn->first_packet_time_to_play = should_be_time;
if (local_time_now >= conn->first_packet_time_to_play) {
debug(
1,
"First packet is late! It should have played before now. Flushing 0.5 seconds");
player_flush(conn->first_packet_timestamp + 5 * 4410 * conn->output_sample_ratio,
conn);
if (local_time_now > conn->first_packet_time_to_play) {
uint64_t lateness = local_time_now - conn->first_packet_time_to_play;
lateness = (lateness * 1000000) >> 32; // microseconds
debug(3, "First packet is %" PRIu64 " microseconds late! Flushing 0.5 seconds",
lateness);
do_flush(conn->first_packet_timestamp + 5 * 4410, conn);
}
}
}
@@ -978,8 +985,10 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
// dynamic adjustment
int64_t filler_size = max_dac_delay;
if (local_time_now >= conn->first_packet_time_to_play) {
// debug(1,"Gone past starting time");
if (local_time_now > conn->first_packet_time_to_play) {
uint64_t lateness = local_time_now - conn->first_packet_time_to_play;
lateness = (lateness * 1000000) >> 32; // microseconds
debug(3, "Gone past starting time by %" PRIu64 " microseconds.", lateness);
have_sent_prefiller_silence = 1;
conn->ab_buffering = 0;
@@ -1633,11 +1642,11 @@ void *player_thread_func(void *arg) {
debug(3, "Output bit depth is %d.", output_bit_depth);
if (conn->input_bit_depth > output_bit_depth) {
debug(1, "Dithering will be enabled because the input bit depth is greater than the output bit "
debug(3, "Dithering will be enabled because the input bit depth is greater than the output bit "
"depth");
}
if (conn->fix_volume != 0x10000) {
debug(1, "Dithering will be enabled because the output volume is being altered in software");
debug(3, "Dithering will be enabled because the output volume is being altered in software");
}
// we need an intermediate "transition" buffer
@@ -2467,8 +2476,8 @@ void *player_thread_func(void *arg) {
debug(1, "This should never be called.");
pthread_cleanup_pop(1); // pop the cleanup handler
// debug(1, "This should never be called either.");
// pthread_cleanup_pop(1); // pop the initial cleanup handler
// debug(1, "This should never be called either.");
// pthread_cleanup_pop(1); // pop the initial cleanup handler
pthread_exit(NULL);
}
@@ -2767,8 +2776,8 @@ int player_play(rtsp_conn_info *conn) {
int player_stop(rtsp_conn_info *conn) {
// note -- this may be called from another connection thread.
int dl = debuglev;
debuglev = 3;
// int dl = debuglev;
// debuglev = 3;
debug(3, "player_stop");
if (conn->player_thread) {
debug(2, "player_thread cancel...");
@@ -2782,12 +2791,12 @@ int player_stop(rtsp_conn_info *conn) {
debug(2, "pend");
send_ssnc_metadata('pend', NULL, 0, 1); // contains cancellation points
#endif
debuglev = dl;
// debuglev = dl;
command_stop();
return 0;
} else {
debug(3, "Connection %d: player thread already deleted.", conn->connection_number);
debuglev = dl;
// debuglev = dl;
return -1;
}
}
+60 -23
View File
@@ -58,7 +58,7 @@ void rtp_initialise(rtsp_conn_info *conn) {
}
void rtp_terminate(rtsp_conn_info *conn) {
conn->reference_timestamp = 0;
// destroy the timer mutex
int rc = pthread_mutex_destroy(&conn->reference_time_mutex);
if (rc)
@@ -98,14 +98,16 @@ uint64_t local_to_remote_time_difference_now(rtsp_conn_info *conn) {
}
void rtp_audio_receiver_cleanup_handler(void *arg) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
debug(3, "Audio Receiver Cleanup.");
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
debug(1, "shutdown audio socket.");
debug(3, "shutdown audio socket.");
shutdown(conn->audio_socket, SHUT_RDWR);
debug(1, "close audio socket.");
debug(3, "close audio socket.");
close(conn->audio_socket);
debug(3, "Audio Receiver Cleanup Successful.");
debug(3, "Connection %d: Audio Receiver Cleanup.", conn->connection_number);
pthread_setcancelstate(oldState, NULL);
}
void *rtp_audio_receiver(void *arg) {
@@ -125,10 +127,13 @@ void *rtp_audio_receiver(void *arg) {
float stat_mean = 0.0;
float stat_M2 = 0.0;
int frame_count = 0;
ssize_t nread;
while (1) {
nread = recv(conn->audio_socket, packet, sizeof(packet), 0);
frame_count++;
uint64_t local_time_now_fp = get_absolute_time_in_fp();
if (time_of_previous_packet_fp) {
float time_interval_us =
@@ -167,6 +172,19 @@ void *rtp_audio_receiver(void *arg) {
// increment last_seqno and see if it's the same as the incoming seqno
if (type == 0x60) { // regular audio data
/*
char obf[4096];
char *obfp = obf;
int obfc;
for (obfc=0;obfc<plen;obfc++) {
snprintf(obfp, 3, "%02X", pktp[obfc]);
obfp+=2;
};
*obfp=0;
debug(1,"Audio Packet Received: \"%s\"",obf);
*/
if (last_seqno == -1)
last_seqno = seqno;
else {
@@ -181,6 +199,9 @@ void *rtp_audio_receiver(void *arg) {
uint32_t actual_timestamp = ntohl(*(uint32_t *)(pktp + 4));
// uint32_t ssid = ntohl(*(uint32_t *)(pktp + 8));
// debug(1, "Audio packet SSID: %08X,%u", ssid,ssid);
// if (packet[1]&0x10)
// debug(1,"Audio packet Extension bit set.");
@@ -221,13 +242,16 @@ void *rtp_audio_receiver(void *arg) {
}
void rtp_control_handler_cleanup_handler(void *arg) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
debug(3, "Control Receiver Cleanup.");
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
debug(1, "shutdown control socket.");
debug(3, "shutdown control socket.");
shutdown(conn->control_socket, SHUT_RDWR);
debug(1, "close control socket.");
debug(3, "close control socket.");
close(conn->control_socket);
debug(3, "Control Receiver Cleanup Successful.");
debug(3, "Connection %d: Control Receiver Cleanup.", conn->connection_number);
pthread_setcancelstate(oldState, NULL);
}
void *rtp_control_receiver(void *arg) {
@@ -284,16 +308,15 @@ void *rtp_control_receiver(void *arg) {
// Best guess is that this delay is 11,025 frames.
// uint32_t rtlt = nctohl(&packet[4]); // raw timestamp less latency
// uint32_t rt = nctohl(&packet[16]); // raw timestamp
uint32_t rtlt = nctohl(&packet[4]); // raw timestamp less latency
uint32_t rt = nctohl(&packet[16]); // raw timestamp
// uint32_t fl = nctohs(&packet[2]); //
uint32_t fl = nctohs(&packet[2]); //
// debug(1,"Sync Packet of %d bytes received: \"%s\", flags: %d, timestamps %u and
%u,
giving a latency of %d frames.",plen,obf,fl,rt,rtlt,rt-rtlt);
// debug(1,"Monotonic timestamps are: %" PRId64 " and %" PRId64 "
respectively.",monotonic_timestamp(rt, conn),monotonic_timestamp(rtlt, conn));
debug(1,"Sync Packet of %d bytes received: \"%s\", flags: %d, timestamps %u and %u,
giving a latency of %d frames.",plen,obf,fl,rt,rtlt,rt-rtlt);
//debug(1,"Monotonic timestamps are: %" PRId64 " and %" PRId64 "
respectively.",monotonic_timestamp(rt, conn),monotonic_timestamp(rtlt, conn));
}
*/
if (conn->local_to_remote_time_difference) { // need a time packet to be interchanged
@@ -477,7 +500,13 @@ void *rtp_control_receiver(void *arg) {
pthread_exit(NULL);
}
void rtp_timing_sender_cleanup_handler(void *arg) {
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
debug(3, "Connection %d: Timing Sender Cleanup.", conn->connection_number);
}
void *rtp_timing_sender(void *arg) {
pthread_cleanup_push(rtp_timing_sender_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
struct timing_request {
char leader;
@@ -537,19 +566,23 @@ void *rtp_timing_sender(void *arg) {
usleep(3000000);
}
debug(3, "rtp_timing_sender thread interrupted. This should never happen.");
pthread_cleanup_pop(0); // don't execute anything here.
pthread_exit(NULL);
}
void rtp_timing_receiver_cleanup_handler(void *arg) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
debug(3, "Timing Receiver Cleanup.");
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
pthread_cancel(conn->timer_requester);
pthread_join(conn->timer_requester, NULL);
debug(1, "shutdown timing socket.");
debug(3, "shutdown timing socket.");
shutdown(conn->timing_socket, SHUT_RDWR);
debug(1, "close timing socket.");
debug(3, "close timing socket.");
close(conn->timing_socket);
debug(3, "Timing Receiver Cleanup Successful.");
debug(3, "Connection %d: Timing Receiver Cleanup.", conn->connection_number);
pthread_setcancelstate(oldState, NULL);
}
void *rtp_timing_receiver(void *arg) {
@@ -597,7 +630,7 @@ void *rtp_timing_receiver(void *arg) {
obfp+=2;
};
*obfp=0;
//debug(1,"Timing Packet Received: \"%s\"",obf);
debug(1,"Timing Packet Received: \"%s\"",obf);
*/
// arrival_time = ((uint64_t)att.tv_sec<<32)+((uint64_t)att.tv_nsec<<32)/1000000000;
@@ -835,14 +868,17 @@ void *rtp_timing_receiver(void *arg) {
static uint16_t bind_port(int ip_family, const char *self_ip_address, uint32_t scope_id,
int *sock) {
// look for a port in the range, if any was specified.
uint16_t desired_port = config.udp_port_base;
int ret = 0;
int local_socket = socket(ip_family, SOCK_DGRAM, IPPROTO_UDP);
if (local_socket == -1)
die("Could not allocate a socket.");
SOCKADDR myaddr;
int tryCount = 0;
uint16_t desired_port;
do {
tryCount++;
desired_port = nextFreeUDPPort();
memset(&myaddr, 0, sizeof(myaddr));
if (ip_family == AF_INET) {
struct sockaddr_in *sa = (struct sockaddr_in *)&myaddr;
@@ -863,13 +899,14 @@ static uint16_t bind_port(int ip_family, const char *self_ip_address, uint32_t s
#endif
} while ((ret < 0) && (errno == EADDRINUSE) && (desired_port != 0) &&
(++desired_port < config.udp_port_base + config.udp_port_range));
(tryCount < config.udp_port_range));
// debug(1,"UDP port chosen: %d.",desired_port);
if (ret < 0) {
close(local_socket);
die("error: could not bind a UDP port! Check the udp_port_range is large enough (>= 10) or "
die("error: could not bind a UDP port! Check the udp_port_range is large enough -- it must be "
"at least 3, and 10 or more is suggested -- or "
"check for restrictive firewall settings or a bad router!");
}
+107 -68
View File
@@ -89,7 +89,9 @@ enum rtsp_read_request_response {
};
// Mike Brady's part...
static pthread_mutex_t play_lock = PTHREAD_MUTEX_INITIALIZER;
// always lock use this when accessing the playing conn value
static pthread_mutex_t playing_conn_lock = PTHREAD_MUTEX_INITIALIZER;
// every time we want to retain or release a reference count, lock it with this
// if a reference count is read as zero, it means the it's being deallocated.
@@ -279,8 +281,6 @@ void cancel_all_RTSP_threads(void) {
for (i = 0; i < nconns; i++) {
debug(1, "Connection %d: joining.", conns[i]->connection_number);
pthread_join(conns[i]->thread, NULL);
if (conns[i] == playing_conn)
playing_conn = NULL;
free(conns[i]);
}
}
@@ -295,8 +295,6 @@ void cleanup_threads(void) {
conns[i]->connection_number);
pthread_join(conns[i]->thread, &retval);
debug(3, "RTSP connection thread %d deleted...", conns[i]->connection_number);
if (conns[i] == playing_conn)
playing_conn = NULL;
free(conns[i]);
nconns--;
if (nconns)
@@ -461,7 +459,7 @@ int msg_handle_line(rtsp_message **pmsg, char *line) {
char *sp, *p;
sp = NULL; // this is to quieten a compiler warning
// debug(1, "received request: %s", line);
debug(3, "RTSP Message Received: \"%s\".", line);
p = strtok_r(line, " ", &sp);
if (!p)
@@ -545,7 +543,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
continue;
char errorstring[1024];
strerror_r(errno, (char *)errorstring, sizeof(errorstring));
debug(1, "rtsp_read_request_response_read_error %d: \"%s\".", errno, (char *)errorstring);
debug(1, "Connection %d: rtsp_read_request_response_read_error %d: \"%s\".",
conn->connection_number, errno, (char *)errorstring);
reply = rtsp_read_request_response_read_error;
goto shutdown;
}
@@ -898,8 +897,9 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
error:
warn("Connection %d: SETUP -- Error in setup request -- unlocking play lock.",
conn->connection_number);
playing_conn = NULL;
pthread_mutex_unlock(&play_lock);
debug_mutex_lock(&playing_conn_lock, 1000000, 3);
playing_conn = NULL; // this connection definitely doesn't have the play lock
debug_mutex_unlock(&playing_conn_lock, 3);
resp->respcode = 451; // invalid arguments
}
@@ -1523,62 +1523,84 @@ 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) {
debug(2, "Connection %d: ANNOUNCE", conn->connection_number);
int have_the_player = 0;
int should_wait = 0; // this will be true if you're trying to break in to the current session
int interrupting_current_session = 0;
// interrupt session if permitted
if (pthread_mutex_trylock(&play_lock) == 0) {
// try to become the current playing_conn
debug_mutex_lock(&playing_conn_lock, 1000000, 3); // get it
if (playing_conn == NULL) {
playing_conn = conn;
have_the_player = 1;
} else if (playing_conn == conn) {
have_the_player = 1;
} else if ((playing_conn) &&
(playing_conn->connection_number == conn->connection_number)) { // duplicate ANNOUNCE
warn("Duplicate ANNOUNCE, by the look of it!");
have_the_player = 1;
} else {
int should_wait = 0;
} else if (playing_conn->stop) {
debug(2, "Connection %d: ANNOUNCE: already shutting down; waiting for it...",
playing_conn->connection_number);
should_wait = 1;
} else if (config.allow_session_interruption == 1) {
debug(2, "Connection %d: ANNOUNCE: asking playing connection %d to shut down.",
conn->connection_number, playing_conn->connection_number);
playing_conn->stop = 1;
interrupting_current_session = 1;
should_wait = 1;
pthread_cancel(playing_conn->thread); // asking the RTSP thread to exit
}
debug_mutex_unlock(&playing_conn_lock, 3);
if (!playing_conn)
die("Non existent playing_conn with play_lock enabled.");
debug(1, "Connection %d: ANNOUNCE: already playing when checked by connection %d.",
playing_conn->connection_number, conn->connection_number);
if (playing_conn->stop) {
debug(1, "Connetion %d: ANNOUNCE: already shutting down; waiting for it...",playing_conn->connection_number);
should_wait = 1;
} else if (config.allow_session_interruption == 1) {
// some thread has the player ... ask it to relinquish the thread
debug(1, "Connection %d: ANNOUNCE: asking playing connection %d to shut down.",
conn->connection_number, playing_conn->connection_number);
if (playing_conn == conn) {
debug(1, "Connection %d: ANNOUNCE: asking to stop itself! Nothing done.",conn->connection_number);
} else {
player_stop(playing_conn);
playing_conn->stop = 1;
pthread_cancel(playing_conn->thread); // asking the RTSP thread to exit
should_wait = 1;
if (should_wait) {
useconds_t time_remaining = 3000000;
while ((time_remaining > 0) && (have_the_player == 0)) {
debug_mutex_lock(&playing_conn_lock, 1000000, 3); // get it
if (playing_conn == NULL) {
playing_conn = conn;
have_the_player = 1;
}
debug_mutex_unlock(&playing_conn_lock, 3);
if (have_the_player == 0) {
usleep(100000);
time_remaining -= 100000;
}
}
if (should_wait) {
useconds_t time_remaining = 3000000;
while ((time_remaining > 0) && (have_the_player == 0)) {
if (pthread_mutex_trylock(&play_lock) == 0)
have_the_player = 1;
else {
usleep(100000);
time_remaining -= 100000;
}
}
if (have_the_player == 1) {
debug(1, "Connection %d: ANNOUNCE got the player", conn->connection_number);
usleep(1000000); // this is just to let everything settle a bit
} else
debug(1, "Connection %d: ANNOUNCE failed to get the player", conn->connection_number);
if ((have_the_player == 1) && (interrupting_current_session == 1)) {
debug(2, "Connection %d: ANNOUNCE got the player", conn->connection_number);
} else {
debug(2, "Connection %d: ANNOUNCE failed to get the player", conn->connection_number);
}
}
if (have_the_player) {
playing_conn = conn; // the present connection is now playing
debug(3, "RTSP conversation thread %d has acquired play lock.", conn->connection_number);
// now, if this new session did not break in, then it's okay to reset the next UDP ports
// to the start of the range
if (interrupting_current_session == 0) { // will be zero if it wasn't waiting to break in
resetFreeUDPPort();
}
/*
{
char *cp = req->content;
int cp_left = req->contentlength;
while (cp_left > 1) {
if (strlen(cp) != 0)
debug(1,">>>>>> %s", cp);
cp += strlen(cp) + 1;
cp_left -= strlen(cp) + 1;
}
}
*/
resp->respcode = 456; // 456 - Header Field Not Valid for Resource
char *pssid = NULL;
char *paesiv = NULL;
char *prsaaeskey = NULL;
char *pfmtp = NULL;
@@ -1601,6 +1623,9 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag
goto out;
}
if (!strncmp(cp, "o=iTunes", 7))
pssid = cp + 8;
if (!strncmp(cp, "a=fmtp:", 7))
pfmtp = cp + 7;
@@ -1619,6 +1644,11 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag
cp = next;
}
if (pssid) {
uint32_t ssid = uatoi(pssid);
debug(3, "Synchronisation Source Identifier: %08X,%u", ssid, ssid);
}
if (pminlatency) {
conn->minimum_latency = atoi(pminlatency);
debug(3, "Minimum latency %d specified", conn->minimum_latency);
@@ -1719,10 +1749,11 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag
out:
if (resp->respcode != 200 && resp->respcode != 453) {
debug(1, "Error in handling ANNOUNCE on conversation thread %d. Unlocking the play lock.",
debug(1, "Connection %d: Error in handling ANNOUNCE. Unlocking the play lock.",
conn->connection_number);
debug_mutex_lock(&playing_conn_lock, 1000000, 3); // get it
playing_conn = NULL;
pthread_mutex_unlock(&play_lock);
debug_mutex_unlock(&playing_conn_lock, 3);
}
}
@@ -1976,20 +2007,12 @@ void rtsp_conversation_thread_cleanup_function(void *arg) {
conn->auth_nonce = NULL;
}
rtp_terminate(conn);
if (playing_conn == conn) {
debug(3, "Connection %d: Unlocking play lock.", conn->connection_number);
playing_conn = NULL;
pthread_mutex_unlock(&play_lock);
}
if (conn->dacp_id) {
free(conn->dacp_id);
conn->dacp_id = NULL;
}
debug(2, "Connection %d: RTSP thread terminated.", conn->connection_number);
conn->running = 0;
// remove flow control and mutexes
int rc = pthread_cond_destroy(&conn->flowcontrol);
if (rc)
@@ -2001,6 +2024,16 @@ void rtsp_conversation_thread_cleanup_function(void *arg) {
rc = pthread_mutex_destroy(&conn->flush_mutex);
if (rc)
debug(1, "Connection %d: error %d destroying flush_mutex.", conn->connection_number, rc);
debug_mutex_lock(&playing_conn_lock, 1000000, 3); // get it
if (playing_conn == conn) {
debug(3, "Connection %d: Unlocking play lock.", conn->connection_number);
playing_conn = NULL;
}
debug_mutex_unlock(&playing_conn_lock, 3);
debug(2, "Connection %d: RTSP thread terminated.", conn->connection_number);
conn->running = 0;
}
void msg_cleanup_function(void *arg) {
@@ -2075,7 +2108,7 @@ static void *rtsp_conversation_thread_func(void *pconn) {
}
}
if (method_selected == 0) {
debug(1, "Connection %d: Unrecognised and unhandled rtsp request \"%s\".",
debug(3, "Connection %d: Unrecognised and unhandled rtsp request \"%s\".",
conn->connection_number, req->method);
int y = req->contentlength;
@@ -2092,7 +2125,7 @@ static void *rtsp_conversation_thread_func(void *pconn) {
obfp += 2;
};
*obfp = 0;
debug(1, "Content: \"%s\".", obf);
debug(3, "Content: \"%s\".", obf);
}
}
}
@@ -2108,7 +2141,9 @@ static void *rtsp_conversation_thread_func(void *pconn) {
if (conn->stop == 0) {
int err = msg_write_response(conn->fd, resp);
if (err) {
debug(1, "Connection %d: Unable to write an RTSP message response. Terminating the connection.", conn->connection_number);
debug(1, "Connection %d: Unable to write an RTSP message response. Terminating the "
"connection.",
conn->connection_number);
conn->stop = 1;
}
}
@@ -2122,11 +2157,14 @@ static void *rtsp_conversation_thread_func(void *pconn) {
(reply == rtsp_read_request_response_read_error)) {
if (conn->player_thread) {
rtsp_read_request_attempt_count--;
if (rtsp_read_request_attempt_count == 0)
if (rtsp_read_request_attempt_count == 0) {
tstop = 1;
else {
// if ((reply == rtsp_read_request_response_read_error) && (debuglev != 0))
// debuglev = 3; // see what happens next
} else {
if (reply == rtsp_read_request_response_channel_closed)
debug(2, "Connection %d: RTSP channel unexpectedly closed -- will try again %d time(s).",
debug(2,
"Connection %d: RTSP channel unexpectedly closed -- will try again %d time(s).",
conn->connection_number, rtsp_read_request_attempt_count);
if (reply == rtsp_read_request_response_read_error)
debug(2, "Connection %d: RTSP channel read error -- will try again %d time(s).",
@@ -2137,7 +2175,8 @@ static void *rtsp_conversation_thread_func(void *pconn) {
tstop = 1;
}
} else {
debug(1, "Connection %d: rtsp_read_request error %d, packet ignored.", conn->connection_number, (int)reply);
debug(1, "Connection %d: rtsp_read_request error %d, packet ignored.",
conn->connection_number, (int)reply);
}
if (tstop) {
debug(3, "Connection %d: Terminate RTSP connection.", conn->connection_number);
+3 -3
View File
@@ -18,7 +18,7 @@ general =
// mdns_backend = "avahi"; // Run "shairport-sync -h" to get a list of all mdns_backends. The default is the first one.
// port = 5000; // Listen for service requests on this port
// udp_port_base = 6001; // start allocating UDP ports from this port number when needed
// udp_port_range = 100; // look for free ports in this number of places, starting at the UDP port base. Allow at least 10, though only three are needed in a steady state.
// udp_port_range = 10; // look for free ports in this number of places, starting at the UDP port base. Allow at least 10, though only three are needed in a steady state.
// drift_tolerance_in_seconds = 0.002; // allow a timing error of this number of seconds of drift away from exact synchronisation before attempting to correct it
// resync_threshold_in_seconds = 0.050; // a synchronisation error greater than this number of seconds will cause resynchronisation; 0 disables it
// ignore_volume_control = "no"; // set this to "yes" if you want the volume to be at 100% no matter what the source's volume control is set to.
@@ -107,9 +107,9 @@ pipe =
// name = "/path/to/pipe"; // there is no default pipe name for the output
};
// These are no configuration file parameters for the "stdout" audio back end. No interpolation is done.
// There are no configuration file parameters for the "stdout" audio back end. No interpolation is done.
// These are no configuration file parameters for the "ao" audio back end. No interpolation is done.
// There are no configuration file parameters for the "ao" audio back end. No interpolation is done.
// Static latency settings are deprecated and the settings have been removed.
+4 -4
View File
@@ -31,11 +31,11 @@
#include <libconfig.h>
#include <libgen.h>
#include <memory.h>
#include <net/if.h>
#include <popt.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -419,8 +419,8 @@ int parse_options(int argc, char **argv) {
/* Get the udp port range setting. This is number of ports that will be tried for free ports ,
* starting at the port base. Only three ports are needed. */
if (config_lookup_int(config.cfg, "general.udp_port_range", &value)) {
if ((value < 0) || (value > 65535))
die("Invalid port range \"%sd\". It should be between 0 and 65535, default is 100",
if ((value < 3) || (value > 65535))
die("Invalid port range \"%sd\". It should be between 3 and 65535, default is 10",
value);
else
config.udp_port_range = value;
@@ -1234,7 +1234,7 @@ int main(int argc, char **argv) {
1); // we expect to be able to connect to the output device
config.audio_backend_buffer_desired_length = 6615; // 0.15 seconds.
config.udp_port_base = 6001;
config.udp_port_range = 100;
config.udp_port_range = 10;
config.output_format = SPS_FORMAT_S16; // default
config.output_rate = 44100; // default
config.decoders_supported =