diff --git a/audio_alsa.c b/audio_alsa.c index 0a03957b..a5c47d8c 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -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); } diff --git a/common.c b/common.c index b0b34b3e..0d76a4f4 100644 --- a/common.c +++ b/common.c @@ -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); } diff --git a/common.h b/common.h index 0a84b725..a043d0f7 100644 --- a/common.h +++ b/common.h @@ -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, ...); diff --git a/configure.ac b/configure.ac index efc335ce..786ed9c4 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [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]) diff --git a/player.c b/player.c index 8b16f3c1..8211c8dc 100644 --- a/player.c +++ b/player.c @@ -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; } } diff --git a/rtp.c b/rtp.c index 8995571f..afee1a4b 100644 --- a/rtp.c +++ b/rtp.c @@ -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;obfccontrol_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!"); } diff --git a/rtsp.c b/rtsp.c index 9f34c5d1..99000174 100644 --- a/rtsp.c +++ b/rtsp.c @@ -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); diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 4697a6a3..55ac4cbf 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -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. diff --git a/shairport.c b/shairport.c index 5e36e911..c9212544 100644 --- a/shairport.c +++ b/shairport.c @@ -31,11 +31,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -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 =