diff --git a/audio_alsa.c b/audio_alsa.c index a5c47d8c..98182989 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -70,7 +70,7 @@ audio_output audio_alsa = { .rate_info = &get_rate_information, .mute = NULL, // a function will be provided if it can, and is allowed to, do hardware mute .volume = NULL, // a function will be provided if it can do hardware volume - .parameters = ¶meters}; + .parameters = NULL}; // a function will be provided if it can do hardware volume static pthread_mutex_t alsa_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -136,6 +136,8 @@ static void help(void) { void set_alsa_out_dev(char *dev) { alsa_out_dev = dev; } + +//assuming pthread cancellation is disabled int open_mixer() { int response = 0; if (hardware_mixer) { @@ -179,6 +181,7 @@ int open_mixer() { return response; } +//assuming pthread cancellation is disabled void close_mixer() { if (alsa_mix_handle) { snd_mixer_close(alsa_mix_handle); @@ -186,6 +189,7 @@ void close_mixer() { } } +//assuming pthread cancellation is disabled void do_snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *mix_elem, double vol) { if (snd_mixer_selem_set_playback_dB_all(mix_elem, vol, 0) != 0) { debug(1, "Can't set playback volume accurately to %f dB.", vol); @@ -196,6 +200,7 @@ void do_snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *mix_elem, double v } static int init(int argc, char **argv) { + // for debugging snd_output_stdio_attach(&output, stdout, 0); // debug(2,"audio_alsa init called."); @@ -406,7 +411,7 @@ static int init(int argc, char **argv) { warn("Invalid audio argument: \"%s\" -- ignored", argv[optind]); } - debug(1, "alsa output device name is \"%s\".", alsa_out_dev); + debug(1, "alsa: output device name is \"%s\".", alsa_out_dev); if (hardware_mixer) { int oldState; @@ -459,8 +464,8 @@ static int init(int argc, char **argv) { snd_ctl_elem_id_set_name(elem_id, alsa_mix_ctrl); if (snd_ctl_get_dB_range(ctl, elem_id, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) { - debug(1, "Volume control \"%s\" has dB volume from %f to %f.", alsa_mix_ctrl, - (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0); + debug(1, "alsa: hardware mixer \"%s\" selected, with dB volume from %f to %f.", + alsa_mix_ctrl, (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0); has_softvol = 1; audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff @@ -499,7 +504,7 @@ static int init(int argc, char **argv) { pthread_cleanup_pop(0); pthread_setcancelstate(oldState, NULL); } else { - // debug(1, "Has no mixer and thus no hardware mute."); + debug(1, "alsa: no hardware mixer selected."); } alsa_mix_handle = NULL; @@ -511,6 +516,7 @@ static void deinit(void) { stop(); } +//assuming pthread cancellation is disabled int actual_open_alsa_device(void) { // the alsa mutex is already acquired when this is called const snd_pcm_uframes_t minimal_buffer_headroom = @@ -906,6 +912,7 @@ static void start(int i_sample_rate, int i_sample_format) { measurement_data_is_valid = 0; } +//assuming pthread cancellation is disabled int my_snd_pcm_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) { int ret; snd_pcm_status_t *alsa_snd_pcm_status; @@ -998,7 +1005,7 @@ int delay(long *the_delay) { } } } - debug_mutex_unlock(&alsa_mutex, 3); + debug_mutex_unlock(&alsa_mutex, 0); pthread_cleanup_pop(0); // here, occasionally pretend there's a problem with pcm_get_delay() // if ((random() % 100000) < 3) // keep it pretty rare @@ -1041,7 +1048,7 @@ static int play(void *buf, int samples) { pthread_cleanup_pop(0); // release the mutex } if (ret == 0) { - pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 1); + pthread_cleanup_debug_mutex_lock(&alsa_mutex, 10000, 0); // snd_pcm_sframes_t current_delay = 0; int err, err2; if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_XRUN) { @@ -1061,6 +1068,7 @@ static int play(void *buf, int samples) { if (samples == 0) debug(1, "empty buffer being passed to pcm_writei -- skipping it"); if ((samples != 0) && (buf != NULL)) { + debug(3, "write %d frames.", samples); err = alsa_pcm_write(alsa_handle, buf, samples); if (err < 0) { frame_index = 0; @@ -1125,7 +1133,7 @@ static int play(void *buf, int samples) { frame_index = 0; measurement_data_is_valid = 0; } - debug_mutex_unlock(&alsa_mutex, 3); + debug_mutex_unlock(&alsa_mutex, 0); pthread_cleanup_pop(0); // release the mutex } pthread_setcancelstate(oldState, NULL); @@ -1292,7 +1300,6 @@ void do_mute(int mute_state_requested) { close_mixer(); } } - mute_request_pending = 0; - + mute_request_pending = 0; pthread_setcancelstate(oldState, NULL); } diff --git a/common.c b/common.c index 0d76a4f4..b2c920ad 100644 --- a/common.c +++ b/common.c @@ -94,9 +94,21 @@ volatile int debuglev = 0; sigset_t pselect_sigset; +int usleep_uncancellable(useconds_t usec) { + int response; + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); + response = usleep_uncancellable(usec); + pthread_setcancelstate(oldState, NULL); + return response; +} + static uint16_t UDPPortIndex = 0; -void resetFreeUDPPort() { UDPPortIndex = 0; } +void resetFreeUDPPort() { + debug(3, "Resetting UDP Port Suggestion to %u", config.udp_port_base); + UDPPortIndex = 0; +} uint16_t nextFreeUDPPort() { if (UDPPortIndex == 0) @@ -1130,10 +1142,10 @@ int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time, // this is not pthread_cancellation safe because is contains a cancellation point int oldState; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); - useconds_t time_to_wait = dally_time; + int time_to_wait = dally_time; int r = pthread_mutex_trylock(mutex); while ((r == EBUSY) && (time_to_wait > 0)) { - useconds_t st = time_to_wait; + int st = time_to_wait; if (st > 1000) st = 1000; sps_nanosleep(0, st * 1000); // this contains a cancellation point diff --git a/common.h b/common.h index a043d0f7..ece21b1d 100644 --- a/common.h +++ b/common.h @@ -331,4 +331,6 @@ char *get_version_string(); // mallocs a string space -- remember to free it aft void sps_nanosleep(const time_t sec, const long nanosec); // waits for this time, even through interruptions +int usleep_uncancellable(useconds_t usec); + #endif // _COMMON_H diff --git a/configure.ac b/configure.ac index 786ed9c4..1fda52d5 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.3d29], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [3.3d32], [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 8211c8dc..429a218d 100644 --- a/player.c +++ b/player.c @@ -442,7 +442,7 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in debug_mutex_unlock(&conn->flush_mutex, 3); } - debug_mutex_lock(&conn->ab_mutex, 30000, 1); + debug_mutex_lock(&conn->ab_mutex, 30000, 0); conn->packet_count++; conn->packet_count_since_flush++; conn->time_of_last_audio_packet = get_absolute_time_in_fp(); @@ -614,7 +614,7 @@ void player_put_packet(seq_t seqno, uint32_t actual_timestamp, uint8_t *data, in } } } - debug_mutex_unlock(&conn->ab_mutex, 3); + debug_mutex_unlock(&conn->ab_mutex, 0); } int32_t rand_in_range(int32_t exclusive_range_limit) { @@ -758,7 +758,7 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t void buffer_get_frame_cleanup_handler(void *arg) { rtsp_conn_info *conn = (rtsp_conn_info *)arg; - debug_mutex_unlock(&conn->ab_mutex, 3); + debug_mutex_unlock(&conn->ab_mutex, 0); } // get the next frame, when available. return 0 if underrun/stream reset. @@ -769,7 +769,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { abuf_t *curframe = NULL; int notified_buffer_empty = 0; // diagnostic only - debug_mutex_lock(&conn->ab_mutex, 30000, 1); + debug_mutex_lock(&conn->ab_mutex, 30000, 0); int wait; long dac_delay = 0; // long because alsa returns a long @@ -779,27 +779,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { do { // get the time local_time_now = get_absolute_time_in_fp(); // type okay - debug(3, "buffer_get_frame is iterating"); + // debug(3, "buffer_get_frame is iterating"); - // if config.timeout (default 120) seconds have elapsed since the last audio packet was - // received, then we should stop. - // config.timeout of zero means don't check..., but iTunes may be confused by a long gap - // followed by a resumption... - - if ((conn->time_of_last_audio_packet != 0) && (conn->stop == 0) && - (config.dont_check_timeout == 0)) { - uint64_t ct = config.timeout; // go from int to 64-bit int - // if (conn->packet_count>500) { //for testing -- about 4 seconds of play first - if ((local_time_now > conn->time_of_last_audio_packet) && - (local_time_now - conn->time_of_last_audio_packet >= ct << 32)) { - debug(1, "As Yeats almost said, \"Too long a silence / can make a stone of the heart\" " - "from RTSP conversation %d.", - conn->connection_number); - conn->stop = 1; - pthread_cancel(conn->thread); - // pthread_kill(conn->thread, SIGUSR1); - } - } int rco = get_requested_connection_state_to_output(); if (conn->connection_state_to_output != rco) { @@ -815,12 +796,12 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { if (config.output->is_running) if (config.output->is_running() != 0) { // if the back end isn't running for any reason debug(3, "not running"); - debug_mutex_lock(&conn->flush_mutex, 1000, 1); + debug_mutex_lock(&conn->flush_mutex, 1000, 0); conn->flush_requested = 1; - debug_mutex_unlock(&conn->flush_mutex, 3); + debug_mutex_unlock(&conn->flush_mutex, 0); } - debug_mutex_lock(&conn->flush_mutex, 1000, 1); + debug_mutex_lock(&conn->flush_mutex, 1000, 0); if (conn->flush_requested == 1) { if (config.output->flush) config.output->flush(); // no cancellation points @@ -830,7 +811,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { conn->time_since_play_started = 0; conn->flush_requested = 0; } - debug_mutex_unlock(&conn->flush_mutex, 3); + debug_mutex_unlock(&conn->flush_mutex, 0); if (conn->ab_synced) { curframe = conn->audio_buffer + BUFIDX(conn->ab_read); @@ -1415,6 +1396,8 @@ void player_thread_initial_cleanup_handler(__attribute__((unused)) void *arg) { void player_thread_cleanup_handler(void *arg) { rtsp_conn_info *conn = (rtsp_conn_info *)arg; + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); debug(3, "Connection %d: player thread main loop exit via player_thread_cleanup_handler.", conn->connection_number); @@ -1484,6 +1467,7 @@ void player_thread_cleanup_handler(void *arg) { clear_reference_timestamp(conn); conn->rtp_running = 0; + pthread_setcancelstate(oldState, NULL); } void *player_thread_func(void *arg) { @@ -1744,7 +1728,9 @@ void *player_thread_func(void *arg) { pthread_cleanup_push(player_thread_cleanup_handler, arg); // undo what's been done so far -// stop looking elsewhere for DACP stuff + // stop looking elsewhere for DACP stuff + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); #ifdef CONFIG_DACP_CLIENT // debug(1, "Set dacp server info"); @@ -1761,8 +1747,9 @@ void *player_thread_func(void *arg) { // almost certainly, this has pthread cancellation points in it -- beware conn->dapo_private_storage = mdns_dacp_monitor(conn->dacp_id); #endif + pthread_setcancelstate(oldState, NULL); - // set the default volume to whaterver it was before, as stored in the config airplay_volume + // set the default volume to whatever it was before, as stored in the config airplay_volume debug(2, "Set initial volume to %f.", config.airplay_volume); player_volume(config.airplay_volume, conn); // will contain a cancellation point if asked to wait @@ -2275,6 +2262,14 @@ void *player_thread_func(void *arg) { inframe->given_timestamp = 0; inframe->sequence_number = 0; + // update the watchdog + if ((config.dont_check_timeout == 0) && (config.timeout != 0)) { + uint64_t time_now = get_absolute_time_in_fp(); + debug_mutex_lock(&conn->watchdog_mutex, 1000, 0); + conn->watchdog_bark_time = time_now; + debug_mutex_unlock(&conn->watchdog_mutex, 0); + } + // debug(1,"Sync error %lld frames. Amount to stuff %d." ,sync_error,amount_to_stuff); // new stats calculation. We want a running average of sync error, drift, adjustment, @@ -2783,8 +2778,14 @@ int player_stop(rtsp_conn_info *conn) { debug(2, "player_thread cancel..."); pthread_cancel(*conn->player_thread); debug(2, "player_thread join..."); - pthread_join(*conn->player_thread, NULL); - debug(2, "player_thread joined."); + if (pthread_join(*conn->player_thread, NULL) == -1) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "Connection %d: error %d joining player thread: \"%s\".", conn->connection_number, + errno, (char *)errorstring); + } else { + debug(2, "player_thread joined."); + } free(conn->player_thread); conn->player_thread = NULL; #ifdef CONFIG_METADATA @@ -2799,4 +2800,4 @@ int player_stop(rtsp_conn_info *conn) { // debuglev = dl; return -1; } -} +} \ No newline at end of file diff --git a/player.h b/player.h index 2f1a3284..deb787c6 100644 --- a/player.h +++ b/player.h @@ -85,7 +85,9 @@ typedef struct { int stop; int running; time_t playstart; - pthread_t thread, timer_requester, rtp_audio_thread, rtp_control_thread, rtp_timing_thread; + pthread_t thread, timer_requester, rtp_audio_thread, rtp_control_thread, rtp_timing_thread, + player_watchdog_thread; + int64_t watchdog_bark_time; // pthread_t *ptp; // buffers to delete on exit @@ -213,6 +215,7 @@ typedef struct { // request in flight at the same time pthread_mutex_t reference_time_mutex; + pthread_mutex_t watchdog_mutex; double local_to_remote_time_gradient; // if no drift, this would be exactly 1.0; likely it's // slightly above or below. diff --git a/rtp.c b/rtp.c index afee1a4b..ac1a43b6 100644 --- a/rtp.c +++ b/rtp.c @@ -97,17 +97,8 @@ uint64_t local_to_remote_time_difference_now(rtsp_conn_info *conn) { return conn->local_to_remote_time_difference + (uint64_t)(drift * (uint64_t)0x100000000); } -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(3, "shutdown audio socket."); - shutdown(conn->audio_socket, SHUT_RDWR); - debug(3, "close audio socket."); - close(conn->audio_socket); - debug(3, "Connection %d: Audio Receiver Cleanup.", conn->connection_number); - pthread_setcancelstate(oldState, NULL); +void rtp_audio_receiver_cleanup_handler(__attribute__((unused)) void *arg) { + debug(3, "Audio Receiver Cleanup Done."); } void *rtp_audio_receiver(void *arg) { @@ -241,17 +232,8 @@ void *rtp_audio_receiver(void *arg) { pthread_exit(NULL); } -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(3, "shutdown control socket."); - shutdown(conn->control_socket, SHUT_RDWR); - debug(3, "close control socket."); - close(conn->control_socket); - debug(3, "Connection %d: Control Receiver Cleanup.", conn->connection_number); - pthread_setcancelstate(oldState, NULL); +void rtp_control_handler_cleanup_handler(__attribute__((unused)) void *arg) { + debug(3, "Control Receiver Cleanup Done."); } void *rtp_control_receiver(void *arg) { @@ -571,17 +553,15 @@ void *rtp_timing_sender(void *arg) { } 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; + debug(3, "Cancel Timing Requester."); pthread_cancel(conn->timer_requester); + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); + debug(3, "Join Timing Requester."); pthread_join(conn->timer_requester, NULL); - debug(3, "shutdown timing socket."); - shutdown(conn->timing_socket, SHUT_RDWR); - debug(3, "close timing socket."); - close(conn->timing_socket); - debug(3, "Connection %d: Timing Receiver Cleanup.", conn->connection_number); + debug(3, "Timing Receiver Cleanup Successful."); pthread_setcancelstate(oldState, NULL); } @@ -873,6 +853,17 @@ static uint16_t bind_port(int ip_family, const char *self_ip_address, uint32_t s int local_socket = socket(ip_family, SOCK_DGRAM, IPPROTO_UDP); if (local_socket == -1) die("Could not allocate a socket."); + + /* + int val = 1; + ret = setsockopt(local_socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + if (ret < 0) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "Error %d: \"%s\". Couldn't set SO_REUSEADDR"); + } + */ + SOCKADDR myaddr; int tryCount = 0; uint16_t desired_port; @@ -905,9 +896,14 @@ static uint16_t bind_port(int ip_family, const char *self_ip_address, uint32_t s if (ret < 0) { close(local_socket); - die("error: could not bind a UDP port! Check the udp_port_range is large enough -- it must be " + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + die("error %d: \"%s\". 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!"); + "check for restrictive firewall settings or a bad router! UDP base is %u, range is %u and " + "current suggestion is %u.", + errno, errorstring, config.udp_port_base, config.udp_port_range, desired_port); } uint16_t sport; @@ -1055,7 +1051,7 @@ void rtp_setup(SOCKADDR *local, SOCKADDR *remote, uint16_t cport, uint16_t tport void get_reference_timestamp_stuff(uint32_t *timestamp, uint64_t *timestamp_time, uint64_t *remote_timestamp_time, rtsp_conn_info *conn) { // types okay - debug_mutex_lock(&conn->reference_time_mutex, 1000, 1); + debug_mutex_lock(&conn->reference_time_mutex, 1000, 0); *timestamp = conn->reference_timestamp; *remote_timestamp_time = conn->remote_reference_timestamp_time; *timestamp_time = @@ -1063,7 +1059,7 @@ void get_reference_timestamp_stuff(uint32_t *timestamp, uint64_t *timestamp_time // if ((*timestamp == 0) && (*timestamp_time == 0)) { // debug(1,"Reference timestamp is invalid."); //} - debug_mutex_unlock(&conn->reference_time_mutex, 3); + debug_mutex_unlock(&conn->reference_time_mutex, 0); } void clear_reference_timestamp(rtsp_conn_info *conn) { @@ -1102,7 +1098,7 @@ int sanitised_source_rate_information(uint32_t *frames, uint64_t *time, rtsp_con double calculated_frame_rate = ((1.0 * local_frames) / local_time) * one_fp; if (((calculated_frame_rate / conn->input_rate) > 1.002) || ((calculated_frame_rate / conn->input_rate) < 0.998)) { - debug(1, "input frame rate out of bounds at %.2f fps.", calculated_frame_rate); + debug(2, "input frame rate out of bounds at %.2f fps.", calculated_frame_rate); result = 1; } else { *frames = local_frames; @@ -1118,7 +1114,7 @@ int sanitised_source_rate_information(uint32_t *frames, uint64_t *time, rtsp_con // the reference timestamps are denominated in terms of the input rate int frame_to_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *conn) { - debug_mutex_lock(&conn->reference_time_mutex, 1000, 1); + debug_mutex_lock(&conn->reference_time_mutex, 1000, 0); int result = 0; uint64_t time_difference; uint32_t frame_difference; @@ -1150,12 +1146,12 @@ int frame_to_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *conn // on the specified fps. } *time = remote_time_of_timestamp - local_to_remote_time_difference_now(conn); - debug_mutex_unlock(&conn->reference_time_mutex, 3); + debug_mutex_unlock(&conn->reference_time_mutex, 0); return result; } int local_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) { - debug_mutex_lock(&conn->reference_time_mutex, 1000, 1); + debug_mutex_lock(&conn->reference_time_mutex, 1000, 0); int result = 0; uint64_t time_difference; @@ -1186,7 +1182,7 @@ int local_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) { // debug(1,"Frame interval is %" PRId64 " frames.",-frame_interval); *frame = (conn->reference_timestamp - frame_interval); } - debug_mutex_unlock(&conn->reference_time_mutex, 3); + debug_mutex_unlock(&conn->reference_time_mutex, 0); return result; } diff --git a/rtsp.c b/rtsp.c index 99000174..11266c7d 100644 --- a/rtsp.c +++ b/rtsp.c @@ -170,6 +170,7 @@ int send_ssnc_metadata(uint32_t code, char *data, uint32_t length, int block) { } void pc_queue_cleanup_handler(void *arg) { + // debug(1, "pc_queue_cleanup_handler called."); pc_queue *the_queue = (pc_queue *)arg; int rc = pthread_mutex_unlock(&the_queue->pc_queue_lock); if (rc) @@ -253,6 +254,48 @@ int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) { #endif +int have_player(rtsp_conn_info *conn) { + int response = 0; + debug_mutex_lock(&playing_conn_lock, 1000000, 3); + if (playing_conn == conn) // this connection definitely has the play lock + response = 1; + debug_mutex_unlock(&playing_conn_lock, 3); + return response; +} + +void player_watchdog_thread_cleanup_handler(void *arg) { + rtsp_conn_info *conn = (rtsp_conn_info *)arg; + debug(2, "Connection %d: Watchdog Exit.", conn->connection_number); +} + +void *player_watchdog_thread_code(void *arg) { + pthread_cleanup_push(player_watchdog_thread_cleanup_handler, arg); + rtsp_conn_info *conn = (rtsp_conn_info *)arg; + do { + usleep(2000000); // check every two seconds + debug(3, "Connection %d: Check the thread is doing something...", conn->connection_number); + if ((config.dont_check_timeout == 0) && (config.timeout != 0)) { + debug_mutex_lock(&conn->watchdog_mutex, 1000, 0); + uint64_t last_watchdog_bark_time = conn->watchdog_bark_time; + debug_mutex_unlock(&conn->watchdog_mutex, 0); + if (last_watchdog_bark_time != 0) { + uint64_t time_since_last_bark = (get_absolute_time_in_fp() - last_watchdog_bark_time) >> 32; + uint64_t ct = config.timeout; // go from int to 64-bit int + + if (time_since_last_bark >= ct) { + debug(1, "Connection %d: As Yeats almost said, \"Too long a silence / can make a stone " + "of the heart\".", + conn->connection_number); + conn->stop = 1; + pthread_cancel(conn->thread); + } + } + } + } while (1); + pthread_cleanup_pop(0); // should never happen + pthread_exit(NULL); +} + void ask_other_rtsp_conversation_threads_to_stop(pthread_t except_this_thread); void rtsp_request_shutdown_stream(void) { @@ -425,13 +468,9 @@ static void debug_print_msg_content(int level, rtsp_message *msg) { void msg_free(rtsp_message *msg) { if (msg) { - int rc = pthread_mutex_lock(&reference_counter_lock); - if (rc) - debug(1, "Error %d locking reference counter lock during msg_free()", rc); + debug_mutex_lock(&reference_counter_lock, 1000, 3); msg->referenceCount--; - rc = pthread_mutex_unlock(&reference_counter_lock); - if (rc) - debug(1, "Error %d unlocking reference counter lock during msg_free()", rc); + debug_mutex_unlock(&reference_counter_lock, 3); if (msg->referenceCount == 0) { unsigned int i; for (i = 0; i < msg->nheaders; i++) { @@ -517,18 +556,21 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes int msg_size = -1; while (msg_size < 0) { - fd_set readfds; - FD_ZERO(&readfds); - FD_SET(conn->fd, &readfds); - do { - memory_barrier(); - } while (conn->stop == 0 && - pselect(conn->fd + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); + /* +fd_set readfds; +FD_ZERO(&readfds); +FD_SET(conn->fd, &readfds); +do { + memory_barrier(); +} while (conn->stop == 0 && + pselect(conn->fd + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); +*/ if (conn->stop != 0) { debug(3, "RTSP conversation thread %d shutdown requested.", conn->connection_number); reply = rtsp_read_request_response_immediate_shutdown_requested; goto shutdown; } + nread = read(conn->fd, buf + inbuf, buflen - inbuf); if (nread == 0) { @@ -541,10 +583,12 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes if (nread < 0) { if (errno == EINTR) continue; - char errorstring[1024]; - strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "Connection %d: rtsp_read_request_response_read_error %d: \"%s\".", - conn->connection_number, errno, (char *)errorstring); + if (errno != ECONNRESET) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(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; } @@ -600,6 +644,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes warning_message_sent = 1; } } + + /* fd_set readfds; FD_ZERO(&readfds); FD_SET(conn->fd, &readfds); @@ -607,6 +653,8 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes memory_barrier(); } while (conn->stop == 0 && pselect(conn->fd + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0); + */ + if (conn->stop != 0) { debug(1, "RTSP shutdown requested."); reply = rtsp_read_request_response_immediate_shutdown_requested; @@ -696,50 +744,62 @@ int msg_write_response(int fd, rtsp_message *resp) { debug(1, "Attempted to write overlong RTSP packet 3"); return -3; } - if (write(fd, pkt, p - pkt) != p - pkt) { - debug(1, "Error writing an RTSP packet -- requested bytes not fully written."); + ssize_t reply = write(fd, pkt, p - pkt); + if (reply == -1) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug(1, "msg_write_response error %d: \"%s\".", errno, (char *)errorstring); return -4; } + if (reply != p - pkt) { + debug(1, "msg_write_response error -- requested bytes not fully written."); + return -5; + } return 0; } void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { debug(2, "Connection %d: RECORD", conn->connection_number); + if (have_player(conn)) { + if (conn->player_thread) + warn("Connection %d: RECORD: Duplicate RECORD message -- ignored", conn->connection_number); + else + player_play(conn); // the thread better be 0 - if (conn->player_thread) - warn("Duplicate RECORD message -- ignored"); - else - player_play(conn); // the thread better be 0 + resp->respcode = 200; + // I think this is for telling the client what the absolute minimum latency + // actually is, + // and when the client specifies a latency, it should be added to this figure. - resp->respcode = 200; - // I think this is for telling the client what the absolute minimum latency - // actually is, - // and when the client specifies a latency, it should be added to this figure. + // Thus, [the old version of] AirPlay's latency figure of 77175, when added to 11025 gives you + // exactly 88200 + // and iTunes' latency figure of 88553, when added to 11025 gives you 99578, + // pretty close to the 99400 we guessed. - // Thus, [the old version of] AirPlay's latency figure of 77175, when added to 11025 gives you - // exactly 88200 - // and iTunes' latency figure of 88553, when added to 11025 gives you 99578, - // pretty close to the 99400 we guessed. + msg_add_header(resp, "Audio-Latency", "11025"); - msg_add_header(resp, "Audio-Latency", "11025"); + char *p; + uint32_t rtptime = 0; + char *hdr = msg_get_header(req, "RTP-Info"); - char *p; - uint32_t rtptime = 0; - char *hdr = msg_get_header(req, "RTP-Info"); - - if (hdr) { - // debug(1,"FLUSH message received: \"%s\".",hdr); - // get the rtp timestamp - p = strstr(hdr, "rtptime="); - if (p) { - p = strchr(p, '='); + if (hdr) { + // debug(1,"FLUSH message received: \"%s\".",hdr); + // get the rtp timestamp + p = strstr(hdr, "rtptime="); if (p) { - rtptime = uatoi(p + 1); // unsigned integer -- up to 2^32-1 - // rtptime--; - // debug(1,"RTSP Flush Requested by handle_record: %u.",rtptime); - player_flush(rtptime, conn); + p = strchr(p, '='); + if (p) { + rtptime = uatoi(p + 1); // unsigned integer -- up to 2^32-1 + // rtptime--; + // debug(1,"RTSP Flush Requested by handle_record: %u.",rtptime); + player_flush(rtptime, conn); + } } } + } else { + warn("Connection %d RECORD received without having the player (no ANNOUNCE?)", + conn->connection_number); + resp->respcode = 451; } } @@ -755,152 +815,170 @@ void handle_options(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message * void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req, rtsp_message *resp) { debug(2, "Connection %d: TEARDOWN", conn->connection_number); - // if (!rtsp_playing()) - // debug(1, "This RTSP connection thread (%d) doesn't think it's playing, but " - // "it's sending a response to teardown anyway",conn->connection_number); - resp->respcode = 200; - msg_add_header(resp, "Connection", "close"); - - debug(3, + if (have_player(conn)) { + resp->respcode = 200; + msg_add_header(resp, "Connection", "close"); + debug( + 3, "TEARDOWN: synchronously terminating the player thread of RTSP conversation thread %d (2).", conn->connection_number); - player_stop(conn); - debug(3, "TEARDOWN: successful termination of playing thread of RTSP conversation thread %d.", - conn->connection_number); + player_stop(conn); + debug(3, "TEARDOWN: successful termination of playing thread of RTSP conversation thread %d.", + conn->connection_number); + } else { + warn("Connection %d TEARDOWN received without having the player (no ANNOUNCE?)", + conn->connection_number); + resp->respcode = 451; + } } void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { debug(3, "Connection %d: FLUSH", conn->connection_number); - // if (!rtsp_playing()) - // debug(1, "This RTSP conversation thread (%d) doesn't think it's playing, but " - // "it's sending a response to flush anyway",conn->connection_number); - char *p = NULL; - uint32_t rtptime = 0; - char *hdr = msg_get_header(req, "RTP-Info"); + if (have_player(conn)) { + char *p = NULL; + uint32_t rtptime = 0; + char *hdr = msg_get_header(req, "RTP-Info"); - if (hdr) { - // debug(1,"FLUSH message received: \"%s\".",hdr); - // get the rtp timestamp - p = strstr(hdr, "rtptime="); - if (p) { - p = strchr(p, '='); - if (p) - rtptime = uatoi(p + 1); // unsigned integer -- up to 2^32-1 + if (hdr) { + // debug(1,"FLUSH message received: \"%s\".",hdr); + // get the rtp timestamp + p = strstr(hdr, "rtptime="); + if (p) { + p = strchr(p, '='); + if (p) + rtptime = uatoi(p + 1); // unsigned integer -- up to 2^32-1 + } } - } // debug(1,"RTSP Flush Requested: %u.",rtptime); #ifdef CONFIG_METADATA - if (p) - send_metadata('ssnc', 'flsr', p + 1, strlen(p + 1), req, 1); - else - send_metadata('ssnc', 'flsr', NULL, 0, NULL, 0); + if (p) + send_metadata('ssnc', 'flsr', p + 1, strlen(p + 1), req, 1); + else + send_metadata('ssnc', 'flsr', NULL, 0, NULL, 0); #endif - player_flush(rtptime, conn); // will not crash even it there is no player thread. - resp->respcode = 200; + player_flush(rtptime, conn); // will not crash even it there is no player thread. + resp->respcode = 200; + + } else { + warn("Connection %d FLUSH received without having the player (no ANNOUNCE?)", + conn->connection_number); + resp->respcode = 451; + } } void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { debug(3, "Connection %d: SETUP", conn->connection_number); - uint16_t cport, tport; - - char *ar = msg_get_header(req, "Active-Remote"); - if (ar) { - debug(2, "Connection %d: SETUP -- Active-Remote string seen: \"%s\".", conn->connection_number, - ar); - // get the active remote - char *p; - conn->dacp_active_remote = strtoul(ar, &p, 10); + resp->respcode = 451; // invalid arguments -- expect them + if (have_player(conn)) { + uint16_t cport, tport; + char *ar = msg_get_header(req, "Active-Remote"); + if (ar) { + debug(2, "Connection %d: SETUP -- Active-Remote string seen: \"%s\".", + conn->connection_number, ar); + // get the active remote + char *p; + conn->dacp_active_remote = strtoul(ar, &p, 10); #ifdef CONFIG_METADATA - send_metadata('ssnc', 'acre', ar, strlen(ar), req, 1); + send_metadata('ssnc', 'acre', ar, strlen(ar), req, 1); #endif - } else { - debug(2, "Connection %d: SETUP -- Note: no Active-Remote information the SETUP Record.", - conn->connection_number); - conn->dacp_active_remote = 0; - } - - ar = msg_get_header(req, "DACP-ID"); - if (ar) { - debug(2, "Connection %d: SETUP -- DACP-ID string seen: \"%s\".", conn->connection_number, ar); - if (conn->dacp_id) // this is in case SETUP was previously called - free(conn->dacp_id); - conn->dacp_id = strdup(ar); -#ifdef CONFIG_METADATA - send_metadata('ssnc', 'daid', ar, strlen(ar), req, 1); -#endif - } else { - debug(2, "Connection %d: SETUP doesn't include DACP-ID string information.", - conn->connection_number); - if (conn->dacp_id) // this is in case SETUP was previously called - free(conn->dacp_id); - conn->dacp_id = NULL; - } - - char *hdr = msg_get_header(req, "Transport"); - if (!hdr) { - debug(1, "Connection %d: SETUP doesn't contain a Transport header.", conn->connection_number); - goto error; - } - - char *p; - p = strstr(hdr, "control_port="); - if (!p) { - debug(1, "Connection %d: SETUP doesn't specify a control_port.", conn->connection_number); - goto error; - } - p = strchr(p, '=') + 1; - cport = atoi(p); - - p = strstr(hdr, "timing_port="); - if (!p) { - debug(1, "Connection %d: SETUP doesn't specify a timing_port.", conn->connection_number); - goto error; - } - p = strchr(p, '=') + 1; - tport = atoi(p); - - if (conn->rtp_running) { - if ((conn->remote_control_port != cport) || (conn->remote_timing_port != tport)) { - warn("Connection %d: Duplicate SETUP message with different control (old %u, new %u) or " - "timing (old %u, new " - "%u) ports! This is probably fatal!", - conn->connection_number, conn->remote_control_port, cport, conn->remote_timing_port, - tport); } else { - warn("Connection %d: Duplicate SETUP message with the same control (%u) and timing (%u) " - "ports. This is " - "probably not fatal.", - conn->connection_number, conn->remote_control_port, conn->remote_timing_port); + debug(2, "Connection %d: SETUP -- Note: no Active-Remote information the SETUP Record.", + conn->connection_number); + conn->dacp_active_remote = 0; } + + ar = msg_get_header(req, "DACP-ID"); + if (ar) { + debug(2, "Connection %d: SETUP -- DACP-ID string seen: \"%s\".", conn->connection_number, ar); + if (conn->dacp_id) // this is in case SETUP was previously called + free(conn->dacp_id); + conn->dacp_id = strdup(ar); +#ifdef CONFIG_METADATA + send_metadata('ssnc', 'daid', ar, strlen(ar), req, 1); +#endif + } else { + debug(2, "Connection %d: SETUP doesn't include DACP-ID string information.", + conn->connection_number); + if (conn->dacp_id) // this is in case SETUP was previously called + free(conn->dacp_id); + conn->dacp_id = NULL; + } + + char *hdr = msg_get_header(req, "Transport"); + if (hdr) { + char *p; + p = strstr(hdr, "control_port="); + if (p) { + p = strchr(p, '=') + 1; + cport = atoi(p); + + p = strstr(hdr, "timing_port="); + if (p) { + p = strchr(p, '=') + 1; + tport = atoi(p); + + if (conn->rtp_running) { + if ((conn->remote_control_port != cport) || (conn->remote_timing_port != tport)) { + warn("Connection %d: Duplicate SETUP message with different control (old %u, new %u) " + "or " + "timing (old %u, new " + "%u) ports! This is probably fatal!", + conn->connection_number, conn->remote_control_port, cport, + conn->remote_timing_port, tport); + } else { + warn("Connection %d: Duplicate SETUP message with the same control (%u) and timing " + "(%u) " + "ports. This is " + "probably not fatal.", + conn->connection_number, conn->remote_control_port, conn->remote_timing_port); + } + } else { + rtp_setup(&conn->local, &conn->remote, cport, tport, conn); + } + if (conn->local_audio_port != 0) { + + char resphdr[256] = ""; + snprintf(resphdr, sizeof(resphdr), + "RTP/AVP/" + "UDP;unicast;interleaved=0-1;mode=record;control_port=%d;" + "timing_port=%d;server_" + "port=%d", + conn->local_control_port, conn->local_timing_port, conn->local_audio_port); + + msg_add_header(resp, "Transport", resphdr); + + msg_add_header(resp, "Session", "1"); + + resp->respcode = 200; // it all worked out okay + debug(1, "Connection %d: SETUP with UDP ports Control: %d, Timing: %d and Audio: %d.", + conn->connection_number, conn->local_control_port, conn->local_timing_port, + conn->local_audio_port); + + } else { + debug(1, "Connection %d: SETUP seems to specify a null audio port.", + conn->connection_number); + } + } else { + debug(1, "Connection %d: SETUP doesn't specify a timing_port.", conn->connection_number); + } + } else { + debug(1, "Connection %d: SETUP doesn't specify a control_port.", conn->connection_number); + } + } else { + debug(1, "Connection %d: SETUP doesn't contain a Transport header.", conn->connection_number); + } + if (resp->respcode != 200) { + debug(1, "Connection %d: SETUP error -- releasing the player lock.", conn->connection_number); + debug_mutex_lock(&playing_conn_lock, 1000000, 3); + if (playing_conn == conn) // if we have the player + playing_conn = NULL; // let it go + debug_mutex_unlock(&playing_conn_lock, 3); + } + } else { - rtp_setup(&conn->local, &conn->remote, cport, tport, conn); + warn("Connection %d SETUP received without having the player (no ANNOUNCE?)", + conn->connection_number); } - if (conn->local_audio_port == 0) { - debug(1, "Connection %d: SETUP seems to specify a null audio port.", conn->connection_number); - goto error; - } - - char resphdr[256] = ""; - snprintf(resphdr, sizeof(resphdr), "RTP/AVP/" - "UDP;unicast;interleaved=0-1;mode=record;control_port=%d;" - "timing_port=%d;server_" - "port=%d", - conn->local_control_port, conn->local_timing_port, conn->local_audio_port); - - msg_add_header(resp, "Transport", resphdr); - - msg_add_header(resp, "Session", "1"); - - resp->respcode = 200; - return; - -error: - warn("Connection %d: SETUP -- Error in setup request -- unlocking play lock.", - conn->connection_number); - 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 } /* @@ -1522,7 +1600,7 @@ static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req, rtsp_m } static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { - debug(2, "Connection %d: ANNOUNCE", conn->connection_number); + debug(3, "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 @@ -1539,8 +1617,8 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag have_the_player = 1; warn("Duplicate ANNOUNCE, by the look of it!"); } else if (playing_conn->stop) { - debug(2, "Connection %d: ANNOUNCE: already shutting down; waiting for it...", - playing_conn->connection_number); + debug(1, "Connection %d ANNOUNCE is waiting for connection %d to shut down.", + conn->connection_number, 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.", @@ -1553,7 +1631,7 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag debug_mutex_unlock(&playing_conn_lock, 3); if (should_wait) { - useconds_t time_remaining = 3000000; + int time_remaining = 3000000; // must be signed, as it could go negative... while ((time_remaining > 0) && (have_the_player == 0)) { debug_mutex_lock(&playing_conn_lock, 1000000, 3); // get it @@ -1752,7 +1830,8 @@ out: 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; + if (playing_conn == conn) // if we managed to acquire it + playing_conn = NULL; // let it go debug_mutex_unlock(&playing_conn_lock, 3); } } @@ -1993,14 +2072,26 @@ authenticate: void rtsp_conversation_thread_cleanup_function(void *arg) { rtsp_conn_info *conn = (rtsp_conn_info *)arg; - // debug(1, "Connection %d: rtsp_conversation_thread_func_cleanup_function called.", - // conn->connection_number); + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); + + debug(3, "Connection %d: rtsp_conversation_thread_func_cleanup_function called.", + conn->connection_number); if (conn->player_thread) player_stop(conn); + + debug(3, "Closing timing, control and audio sockets..."); + if (conn->control_socket) + close(conn->control_socket); + if (conn->timing_socket) + close(conn->timing_socket); + if (conn->audio_socket) + close(conn->audio_socket); + if (conn->fd > 0) { - // debug(1, "Connection %d: closing fd %d.", - // conn->connection_number,conn->fd); + debug(3, "Connection %d: closing fd %d.", conn->connection_number, conn->fd); close(conn->fd); + debug(3, "Connection %d: closed fd %d.", conn->connection_number, conn->fd); } if (conn->auth_nonce) { free(conn->auth_nonce); @@ -2025,25 +2116,38 @@ void rtsp_conversation_thread_cleanup_function(void *arg) { if (rc) debug(1, "Connection %d: error %d destroying flush_mutex.", conn->connection_number, rc); + debug(2, "Cancel watchdog thread."); + pthread_cancel(conn->player_watchdog_thread); + debug(2, "Join watchdog thread."); + pthread_join(conn->player_watchdog_thread, NULL); + debug(2, "Delete watchdog mutex."); + pthread_mutex_destroy(&conn->watchdog_mutex); + + debug(3, "Connection %d: Checking play lock.", conn->connection_number); 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; + if (playing_conn == conn) { // if it's ours + debug(2, "Connection %d: Unlocking play lock.", conn->connection_number); + playing_conn = NULL; // let it go } debug_mutex_unlock(&playing_conn_lock, 3); debug(2, "Connection %d: RTSP thread terminated.", conn->connection_number); conn->running = 0; + pthread_setcancelstate(oldState, NULL); } void msg_cleanup_function(void *arg) { - // debug(1, "msg_cleanup_function called."); + debug(3, "msg_cleanup_function called."); msg_free((rtsp_message *)arg); } static void *rtsp_conversation_thread_func(void *pconn) { rtsp_conn_info *conn = pconn; + // create the watchdog mutex and start the watchdog thread; + pthread_mutex_init(&conn->watchdog_mutex, NULL); + pthread_create(&conn->player_watchdog_thread, NULL, &player_watchdog_thread_code, (void *)conn); + int rc = pthread_mutex_init(&conn->flush_mutex, NULL); if (rc) die("Connection %d: error %d initialising flush_mutex.", conn->connection_number, rc); @@ -2064,6 +2168,9 @@ static void *rtsp_conversation_thread_func(void *pconn) { die("Connection %d: error %d initialising flow control condition variable.", conn->connection_number, rc); + // nothing before this is cancellable + pthread_cleanup_push(rtsp_conversation_thread_cleanup_function, (void *)conn); + rtp_initialise(conn); char *hdr = NULL; @@ -2072,7 +2179,6 @@ static void *rtsp_conversation_thread_func(void *pconn) { int rtsp_read_request_attempt_count = 1; // 1 means exit immediately rtsp_message *req, *resp; - pthread_cleanup_push(rtsp_conversation_thread_cleanup_function, (void *)conn); while (conn->stop == 0) { int debug_level = 3; // for printing the request and response reply = rtsp_read_request(conn, &req); @@ -2131,6 +2237,8 @@ static void *rtsp_conversation_thread_func(void *pconn) { } debug(debug_level, "Connection %d: RTSP Response:", conn->connection_number); debug_print_msg_headers(debug_level, resp); + + /* fd_set writefds; FD_ZERO(&writefds); FD_SET(conn->fd, &writefds); @@ -2138,13 +2246,23 @@ static void *rtsp_conversation_thread_func(void *pconn) { memory_barrier(); } while (conn->stop == 0 && pselect(conn->fd + 1, NULL, &writefds, NULL, NULL, &pselect_sigset) <= 0); + */ + 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); + struct linger so_linger; + so_linger.l_onoff = 1; // "true" + so_linger.l_linger = 0; + err = setsockopt(conn->fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger); + if (err) + debug(1, "Could not set the RTSP socket to abort due to a write error on closing."); conn->stop = 1; + // if (debuglev >= 1) + // debuglev = 3; // see what happens next } } pthread_cleanup_pop(1); @@ -2159,7 +2277,14 @@ static void *rtsp_conversation_thread_func(void *pconn) { rtsp_read_request_attempt_count--; if (rtsp_read_request_attempt_count == 0) { tstop = 1; - // if ((reply == rtsp_read_request_response_read_error) && (debuglev != 0)) + if (reply == rtsp_read_request_response_read_error) { + struct linger so_linger; + so_linger.l_onoff = 1; // "true" + so_linger.l_linger = 0; + int err = setsockopt(conn->fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger); + if (err) + debug(1, "Could not set the RTSP socket to abort due to a read error on closing."); + } // debuglev = 3; // see what happens next } else { if (reply == rtsp_read_request_response_channel_closed) @@ -2208,15 +2333,20 @@ static const char *format_address(struct sockaddr *fsa) { */ void rtsp_listen_loop_cleanup_handler(__attribute__((unused)) void *arg) { + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); debug(1, "rtsp_listen_loop_cleanup_handler called."); cancel_all_RTSP_threads(); int *sockfd = (int *)arg; mdns_unregister(); if (sockfd) free(sockfd); + pthread_setcancelstate(oldState, NULL); } void rtsp_listen_loop(void) { + int oldState; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); struct addrinfo hints, *info, *p; char portstr[6]; int *sockfd = NULL; @@ -2315,9 +2445,7 @@ void rtsp_listen_loop(void) { mdns_register(); - // printf("Listening for connections."); - // shairport_startup_complete(); - + pthread_setcancelstate(oldState, NULL); int acceptfd; struct timeval tv; pthread_cleanup_push(rtsp_listen_loop_cleanup_handler, (void *)sockfd); @@ -2407,7 +2535,7 @@ void rtsp_listen_loop(void) { // conn->thread = rtsp_conversation_thread; // conn->stop = 0; // record's memory has been zeroed // conn->authorized = 0; // record's memory has been zeroed - fcntl(conn->fd, F_SETFL, O_NONBLOCK); + // fcntl(conn->fd, F_SETFL, O_NONBLOCK); ret = pthread_create(&conn->thread, NULL, rtsp_conversation_thread_func, conn); // also acts as a memory barrier @@ -2419,6 +2547,6 @@ void rtsp_listen_loop(void) { } } while (1); - pthread_cleanup_pop(0); + pthread_cleanup_pop(1); // should never happen debug(1, "Oops -- fell out of the RTSP select loop"); } diff --git a/shairport.c b/shairport.c index c9212544..11f085b1 100644 --- a/shairport.c +++ b/shairport.c @@ -31,11 +31,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -1483,13 +1483,18 @@ int main(int argc, char **argv) { char *version_dbs = get_version_string(); if (version_dbs) { - debug(1, "Version: \"%s\"", version_dbs); + debug(1, "software version: \"%s\"", version_dbs); free(version_dbs); } else { - debug(1, "Can't print the version information!"); + debug(1, "can't print the version information!"); } /* Print out options */ + debug(1, "log verbosity is %d.", debuglev); + debug(1, "disable resend requests is %s.", config.disable_resend_requests ? "on" : "off"); + debug(1, "diagnostic_drop_packet_fraction is %f. A value of 0.0 means no packets will be dropped " + "deliberately.", + config.diagnostic_drop_packet_fraction); debug(1, "statistics_requester status is %d.", config.statistics_requested); debug(1, "daemon status is %d.", config.daemonise); debug(1, "deamon pid file path is \"%s\".", pid_file_proc()); @@ -1563,10 +1568,6 @@ int main(int argc, char **argv) { #endif debug(1, "loudness is %d.", config.loudness); debug(1, "loudness reference level is %f", config.loudness_reference_volume_db); - debug(1, "disable resend requests is %s.", config.disable_resend_requests ? "on" : "off"); - debug(1, "diagnostic_drop_packet_fraction is %f. A value of 0.0 means no packets will be dropped " - "deliberately.", - config.diagnostic_drop_packet_fraction); uint8_t ap_md5[16];