Add many improvements, including reliable interruption using new ports, safer pthread_cancel usage.
This commit is contained in:
+34
-4
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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, ¤tState);
|
||||
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, ¤tState);
|
||||
if (currentState == PTHREAD_CANCEL_ENABLE)
|
||||
daemon_log(LOG_DEBUG, "Warning -- cancellation is enabled after logging");
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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])
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user