From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 3 Dec 2025 15:56:38 +0000 (+0000) Subject: Modify/simplify the TEARDOWN handling in AP2 mode. Allow the threads to natrually... X-Git-Tag: 5.0-post-dev~49 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=93bafe304af7b7a122fe9e49a6bb13c3afc4a72a;p=thirdparty%2Fshairport-sync.git Modify/simplify the TEARDOWN handling in AP2 mode. Allow the threads to natrually terminate when ports are closed by the client. --- diff --git a/ap2_event_receiver.c b/ap2_event_receiver.c index 3dffc0de..29d7d644 100644 --- a/ap2_event_receiver.c +++ b/ap2_event_receiver.c @@ -25,29 +25,70 @@ */ #include "ap2_event_receiver.h" -#include "player.h" +#include "bonjour_strings.h" #include "common.h" +#include "player.h" +#include "ptp-utilities.h" #include "rtsp.h" #include "utilities/structured_buffer.h" void ap2_event_receiver_cleanup_handler(void *arg) { rtsp_conn_info *conn = (rtsp_conn_info *)arg; - debug(2, "Connection %d: AP2 Event Receiver Cleanup.", conn->connection_number); + debug(1, "Connection %d: AP2 Event Receiver Cleanup start.", conn->connection_number); + // only update these things if you're (still) the principal conn + +#ifdef CONFIG_METADATA + // this is here to ensure it's only performed once during a teardown of a ptp stream + send_ssnc_metadata('disc', conn->client_ip_string, strlen(conn->client_ip_string), 1); +#endif + + if (conn->airplay_gid != NULL) { + free(conn->airplay_gid); + conn->airplay_gid = NULL; + } + conn->groupContainsGroupLeader = 0; + if (conn->dacp_active_remote != NULL) { + free(conn->dacp_active_remote); + conn->dacp_active_remote = NULL; + } + if (conn->ap2_client_name) { + free(conn->ap2_client_name); + conn->ap2_client_name = NULL; + } + pthread_rwlock_wrlock(&principal_conn_lock); // don't let the principal_conn be changed + pthread_cleanup_push(rwlock_unlock, (void *)&principal_conn_lock); + if (principal_conn == conn) { + config.airplay_statusflags &= (0xffffffff - (1 << 11)); // DeviceSupportsRelay + build_bonjour_strings(conn); + debug(1, "Connection %d: SETUP mdns_update on %s.", conn->connection_number, + get_category_string(conn->airplay_stream_category)); + mdns_update(NULL, secondary_txt_records); + principal_conn = NULL; + } + pthread_cleanup_pop(1); // release the principal_conn lock + debug(1, "Connection %d: AP2 Event Receiver Cleanup exit.", conn->connection_number); } void *ap2_event_receiver(void *arg) { // #include // debug(1, "rtp_event_receiver PID %d", syscall(SYS_gettid)); rtsp_conn_info *conn = (rtsp_conn_info *)arg; - if (conn->airplay_stream_category == remote_control_stream) - debug(2, "Connection %d (RC): AP2 Event Receiver started", conn->connection_number); - else - debug(2, "Connection %d: AP2 Event Receiver started", conn->connection_number); - + debug(2, "Connection %d: AP2 Event Receiver started", conn->connection_number); structured_buffer *sbuf = sbuf_new(4096); if (sbuf != NULL) { pthread_cleanup_push(sbuf_cleanup, sbuf); - + // only update these things if you're (still) the principal conn + pthread_rwlock_wrlock(&principal_conn_lock); // don't let the principal_conn be changed + pthread_cleanup_push(rwlock_unlock, (void *)&principal_conn_lock); + if (principal_conn == conn) { + config.airplay_statusflags |= 1 << 11; // DeviceSupportsRelay + // config.airplay_statusflags |= 1 << 17; // ReceiverSessionIsActive + build_bonjour_strings(conn); + debug(2, "Connection %d: SETUP mdns_update on %s.", conn->connection_number, + get_category_string(conn->airplay_stream_category)); + mdns_update(NULL, secondary_txt_records); + } + pthread_cleanup_pop(1); // release the principal_conn lock pthread_cleanup_push(ap2_event_receiver_cleanup_handler, arg); // listen(conn->event_socket, 5); // this is now done in the handle_setup_2 code @@ -60,7 +101,7 @@ void *ap2_event_receiver(void *arg) { int fd = accept(conn->event_socket, (struct sockaddr *)&remote_addr, &addr_size); debug(2, - "Connection %d: rtp_event_receiver accepted a connection on socket %d and moved to a new " + "Connection %d: ap2_event_receiver accepted a connection on socket %d and moved to a new " "socket %d.", conn->connection_number, conn->event_socket, fd); intptr_t pfd = fd; @@ -125,10 +166,9 @@ void *ap2_event_receiver(void *arg) { if (nread < 0) { char errorstring[1024]; strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug( - 1, - "Connection %d: error in ap2 rtp_event_receiver %d: \"%s\". Could not recv a packet.", - conn->connection_number, errno, errorstring); + debug(1, + "Connection %d: error in ap2_event_receiver %d: \"%s\". Could not recv a packet.", + conn->connection_number, errno, errorstring); // if ((config.diagnostic_drop_packet_fraction == 0.0) || // (drand48() > config.diagnostic_drop_packet_fraction)) { } else if (nread > 0) { @@ -145,13 +185,16 @@ void *ap2_event_receiver(void *arg) { } } while (finished == 0); + debug(1, "Connection %d: AP2 Event Receiver RTP thread starting \"normal\" exit.", + conn->connection_number); pthread_cleanup_pop(1); // close the socket pthread_cleanup_pop(1); // do the cleanup pthread_cleanup_pop(1); // delete the structured buffer - debug(2, "Connection %d: AP2 Event Receiver RTP thread \"normal\" exit.", + debug(1, "Connection %d: AP2 Event Receiver RTP thread \"normal\" exit.", conn->connection_number); } else { debug(1, "Could not allocate a structured buffer!"); } + conn->ap2_event_receiver_exited = 1; pthread_exit(NULL); } diff --git a/ap2_rc_event_receiver.c b/ap2_rc_event_receiver.c index edb842a9..b57dd346 100644 --- a/ap2_rc_event_receiver.c +++ b/ap2_rc_event_receiver.c @@ -25,8 +25,8 @@ */ #include "ap2_rc_event_receiver.h" -#include "player.h" #include "common.h" +#include "player.h" #include "rtsp.h" #include "utilities/structured_buffer.h" @@ -60,7 +60,8 @@ void *ap2_rc_event_receiver(void *arg) { int fd = accept(conn->event_socket, (struct sockaddr *)&remote_addr, &addr_size); debug(2, - "Connection %d: ap2_rc_event_receiver accepted a connection on socket %d and moved to a new " + "Connection %d: ap2_rc_event_receiver accepted a connection on socket %d and moved to a " + "new " "socket %d.", conn->connection_number, conn->event_socket, fd); intptr_t pfd = fd; @@ -135,7 +136,9 @@ void *ap2_rc_event_receiver(void *arg) { // ssize_t plen = nread; packet[nread] = '\0'; - debug(3, "Connection %d: ap2_rc_event_receiver Packet Received on Event Port with contents: \"%s\".", + debug(2, + "Connection %d: ap2_rc_event_receiver Packet Received on Event Port with contents: " + "\"%s\".", conn->connection_number, packet); } else { debug(1, "Connection %d: ap2_rc_event_receiver Event Port connection closed by client", @@ -148,10 +151,10 @@ void *ap2_rc_event_receiver(void *arg) { pthread_cleanup_pop(1); // close the socket pthread_cleanup_pop(1); // do the cleanup pthread_cleanup_pop(1); // delete the structured buffer - debug(2, "Connection %d: AP2 ap2_rc_event_receiver \"normal\" exit.", - conn->connection_number); + debug(2, "Connection %d: AP2 ap2_rc_event_receiver \"normal\" exit.", conn->connection_number); } else { debug(1, "Could not allocate a structured buffer!"); } + conn->ap2_event_receiver_exited = 1; pthread_exit(NULL); } diff --git a/rtp.c b/rtp.c index eae7e3b5..093e6aa7 100644 --- a/rtp.c +++ b/rtp.c @@ -87,7 +87,6 @@ typedef struct { pthread_cond_t not_full_cv; } buffered_tcp_desc; - /* char obf[4096]; char *obfp = obf; @@ -2015,7 +2014,9 @@ void *buffered_tcp_reader(void *arg) { descriptor->error_code = errno; } else if (nread == 0) { descriptor->closed = 1; - debug(1, "buffered audio port closed by remote end. Terminating the buffered_tcp_reader thread."); + debug( + 1, + "buffered audio port closed by remote end. Terminating the buffered_tcp_reader thread."); finished = 1; } else if (nread > 0) { descriptor->eoq += nread; @@ -2032,10 +2033,10 @@ void *buffered_tcp_reader(void *arg) { usleep(10000); // give other threads a chance to run... } while (finished == 0); - debug(3, "Buffered TCP Reader Thread Exit \"Normal\" Exit Begin."); + debug(1, "Buffered TCP Reader Thread Exit \"Normal\" Exit Begin."); pthread_cleanup_pop(1); // close the socket pthread_cleanup_pop(1); // cleanup - debug(2, "Buffered TCP Reader Thread Exit \"Normal\" Exit."); + debug(1, "Buffered TCP Reader Thread Exit \"Normal\" Exit."); pthread_exit(NULL); } @@ -2132,7 +2133,7 @@ void rtp_buffered_audio_cleanup_handler(__attribute__((unused)) void *arg) { debug(1, "Connection %d: closing TCP Buffered Audio port: %u.", conn->connection_number, conn->local_buffered_audio_port); conn->buffered_audio_socket = 0; - debug(2, "Connection %d: Buffered Audio Receiver Cleanup Done.", conn->connection_number); + debug(1, "Connection %d: rtp_buffered_audio_processor exit.", conn->connection_number); } #define MOD_23BIT 0x7FFFFF // 2^23 - 1 @@ -2162,7 +2163,8 @@ void *rtp_buffered_audio_processor(void *arg) { conn->incoming_ssrc = 0; // reset conn->resampler_ssrc = 0; - // turn off all flush requests that might have been pending in the connection. Not sure if this is right... + // turn off all flush requests that might have been pending in the connection. Not sure if this is + // right... unsigned int fr = 0; for (fr = 0; fr < MAX_DEFERRED_FLUSH_REQUESTS; fr++) { conn->ap2_deferred_flush_requests[fr].inUse = 0; @@ -2643,7 +2645,7 @@ void *rtp_buffered_audio_processor(void *arg) { } } while (finished == 0); debug(2, "Buffered Audio Receiver RTP thread \"normal\" exit."); - pthread_cleanup_pop(1); // thread creation + pthread_cleanup_pop(1); // buffered_tcp_reader thread creation pthread_cleanup_pop(1); // buffer malloc pthread_cleanup_pop(1); // not_full_cv pthread_cleanup_pop(1); // not_empty_cv diff --git a/rtsp.c b/rtsp.c index 2563a13a..6d14ae87 100644 --- a/rtsp.c +++ b/rtsp.c @@ -68,6 +68,7 @@ #include #endif +#include "bonjour_strings.h" #include "common.h" #include "player.h" #include "rtp.h" @@ -88,6 +89,8 @@ #endif #ifdef CONFIG_AIRPLAY_2 +#include "ap2_event_receiver.h" +#include "ap2_rc_event_receiver.h" #include "pair_ap/pair.h" #include "plist/plist.h" #include "plists/get_info_response.h" @@ -109,36 +112,6 @@ #include "mdns.h" -// mDNS advertisement strings - -// Create these strings and then keep them updated. -// When necessary, update the mDNS service records, using e.g. Avahi -// from these sources. - -char *txt_records[128]; -char *secondary_txt_records[128]; - -char fwString[128]; -char ap1_featuresString[128]; -char ap1StatusFlagsString[128]; -char ap1ModelString[128]; -char ap1SrcversString[128]; -char pkString[128]; - -#ifdef CONFIG_AIRPLAY_2 -char deviceIdString[128]; -char featuresString[128]; -char statusflagsString[128]; -char piString[128]; -char gidString[128]; -char psiString[128]; -char fexString[128]; -char modelString[128]; -char srcversString[128]; -char osversString[128]; -char ap1OsversString[128]; -#endif - #define METADATA_SNDBUF (4 * 1024 * 1024) enum rtsp_read_request_response { @@ -248,141 +221,6 @@ int add_pstring_to_malloc(const char *s, void **allocation, size_t *size) { #endif -#ifdef CONFIG_AIRPLAY_2 -void build_bonjour_strings(rtsp_conn_info *conn) { -#else -void build_bonjour_strings(__attribute((unused)) rtsp_conn_info *conn) { -#endif - - // Watch out here, the strings that form each entry - // need to be permanent, because we don't know - // when avahi will look at them. - // bnprintf is (should be) the same as snprintf except that it returns a pointer to the resulting - // character string. so this rather odd arranement below allows you to use a snprintf for - // convenience but get the character string as a result, both as a store for the item so that - // Avahi can see it in future and as a pointer - int entry_number = 0; - - // the txt_records entries are for the _raop._tcp characteristics - // the secondary_txt_records are for the _airplay._tcp items. - -#ifdef CONFIG_AIRPLAY_2 - txt_records[entry_number++] = "cn=0,1"; - txt_records[entry_number++] = "da=true"; - txt_records[entry_number++] = "et=0,1"; - - uint64_t features_hi = config.airplay_features; - features_hi = (features_hi >> 32) & 0xffffffff; - uint64_t features_lo = config.airplay_features; - features_lo = features_lo & 0xffffffff; - - txt_records[entry_number++] = - bnprintf(ap1_featuresString, sizeof(ap1_featuresString), "ft=0x%" PRIX64 ",0x%" PRIX64 "", - features_lo, features_hi); - - txt_records[entry_number++] = - bnprintf(fwString, sizeof(fwString), "fv=%s", config.firmware_version); - txt_records[entry_number++] = bnprintf(ap1StatusFlagsString, sizeof(ap1StatusFlagsString), - "sf=0x%" PRIX32, config.airplay_statusflags); -#ifdef CONFIG_METADATA - if (config.get_coverart == 0) - txt_records[entry_number++] = "md=0,2"; - else - txt_records[entry_number++] = "md=0,1,2"; -#endif - txt_records[entry_number++] = - bnprintf(ap1ModelString, sizeof(ap1ModelString), "am=%s", config.model); - txt_records[entry_number++] = bnprintf(pkString, sizeof(pkString), "pk=%s", config.pk_string); - txt_records[entry_number++] = "tp=UDP"; - txt_records[entry_number++] = "vn=65537"; - txt_records[entry_number++] = - bnprintf(ap1SrcversString, sizeof(ap1SrcversString), "vs=%s", config.srcvers); - txt_records[entry_number++] = - bnprintf(ap1OsversString, sizeof(ap1OsversString), "ov=%s", config.osvers); - txt_records[entry_number++] = NULL; - -#else - // here, just replicate what happens in mdns.h when using those #defines - txt_records[entry_number++] = - bnprintf(ap1StatusFlagsString, sizeof(ap1StatusFlagsString), "sf=0x4"); - txt_records[entry_number++] = - bnprintf(fwString, sizeof(fwString), "fv=%s", config.firmware_version); - txt_records[entry_number++] = - bnprintf(ap1ModelString, sizeof(ap1ModelString), "am=%s", config.model); - txt_records[entry_number++] = bnprintf(ap1SrcversString, sizeof(ap1SrcversString), "vs=105.1"); - txt_records[entry_number++] = "tp=TCP,UDP"; - txt_records[entry_number++] = "vn=65537"; -#ifdef CONFIG_METADATA - if (config.get_coverart == 0) - txt_records[entry_number++] = "md=0,2"; - else - txt_records[entry_number++] = "md=0,1,2"; -#endif - txt_records[entry_number++] = "ss=16"; - txt_records[entry_number++] = "sr=44100"; - txt_records[entry_number++] = "da=true"; - txt_records[entry_number++] = "sv=false"; - txt_records[entry_number++] = "et=0,1"; - txt_records[entry_number++] = "ek=1"; - txt_records[entry_number++] = "cn=0,1"; - txt_records[entry_number++] = "ch=2"; - txt_records[entry_number++] = "txtvers=1"; - if (config.password == 0) - txt_records[entry_number++] = "pw=false"; - else - txt_records[entry_number++] = "pw=true"; - txt_records[entry_number++] = NULL; -#endif - -#ifdef CONFIG_AIRPLAY_2 - // make up a secondary set of text records - entry_number = 0; - - secondary_txt_records[entry_number++] = "acl=0"; - secondary_txt_records[entry_number++] = "btaddr=00:00:00:00:00:00"; - secondary_txt_records[entry_number++] = - bnprintf(deviceIdString, sizeof(deviceIdString), "deviceid=%s", config.airplay_device_id); - secondary_txt_records[entry_number++] = - bnprintf(fexString, sizeof(fexString), "fex=%s", config.airplay_fex); - secondary_txt_records[entry_number++] = - bnprintf(featuresString, sizeof(featuresString), "features=0x%" PRIX64 ",0x%" PRIX64 "", - features_lo, features_hi); // features_hi and features_lo already calculated. - secondary_txt_records[entry_number++] = bnprintf(statusflagsString, sizeof(statusflagsString), - "flags=0x%" PRIX32, config.airplay_statusflags); - if ((conn != NULL) && (conn->airplay_gid != 0)) { - snprintf(gidString, sizeof(gidString), "gid=%s", conn->airplay_gid); - } else { - snprintf(gidString, sizeof(gidString), "gid=%s", config.airplay_pi); - } - secondary_txt_records[entry_number++] = gidString; - - if ((conn != NULL) && (conn->groupContainsGroupLeader != 0)) { - secondary_txt_records[entry_number++] = "igl=0"; - secondary_txt_records[entry_number++] = "gcgl=1"; - } else { - secondary_txt_records[entry_number++] = "igl=0"; - secondary_txt_records[entry_number++] = "gcgl=0"; - } - // if ((conn != NULL) && (conn->airplay_gid != 0)) // if it's in a group - // secondary_txt_records[entry_number++] = "isGroupLeader=0"; - secondary_txt_records[entry_number++] = - bnprintf(modelString, sizeof(modelString), "model=%s", config.model); - secondary_txt_records[entry_number++] = "protovers=1.1"; - secondary_txt_records[entry_number++] = - bnprintf(piString, sizeof(piString), "pi=%s", config.airplay_pi); - secondary_txt_records[entry_number++] = - bnprintf(psiString, sizeof(psiString), "psi=%s", config.airplay_psi); - secondary_txt_records[entry_number++] = pkString; // already calculated - secondary_txt_records[entry_number++] = - bnprintf(srcversString, sizeof(srcversString), "srcvers=%s", config.srcvers); - secondary_txt_records[entry_number++] = - bnprintf(osversString, sizeof(osversString), "osvers=%s", config.osvers); - secondary_txt_records[entry_number++] = "vv=2"; - secondary_txt_records[entry_number++] = fwString; // already calculated - secondary_txt_records[entry_number++] = NULL; -#endif -} - #ifdef CONFIG_METADATA typedef struct { uint32_t type; @@ -588,7 +426,20 @@ int get_play_lock(rtsp_conn_info *conn, int allow_session_interruption) { } else if (allow_session_interruption != 0) { rtsp_conn_info *previous_principal_conn = principal_conn; // important -- demote the principal conn before cancelling it + + if (principal_conn->fd > 0) { + debug(1, + "Connection %d: get_play_lock forced termination. Closing RTSP connection socket %d: " + "from %s:%u to self at " + "%s:%u.", + principal_conn->connection_number, principal_conn->fd, principal_conn->client_ip_string, + principal_conn->client_rtsp_port, principal_conn->self_ip_string, + principal_conn->self_rtsp_port); + close(principal_conn->fd); + // principal_conn->fd = 0; + } principal_conn = NULL; + pthread_cancel(previous_principal_conn->thread); // the previous principal thread will block on the principal conn lock when exiting // so it's important not to wait for it here, e.g. don't put in a pthread_join here. @@ -646,7 +497,7 @@ void cancel_all_RTSP_threads(airplay_stream_c stream_category, int except_this_o ((stream_category == unspecified_stream_category) || (stream_category == conns[i]->airplay_stream_category))) { pthread_cancel(conns[i]->thread); - debug(2, "Connection %d: cancelled.", conns[i]->connection_number); + debug(1, "Connection %d: cancelled.", conns[i]->connection_number); } } for (i = 0; i < nconns; i++) { @@ -654,9 +505,9 @@ void cancel_all_RTSP_threads(airplay_stream_c stream_category, int except_this_o (conns[i]->connection_number != except_this_one) && ((stream_category == unspecified_stream_category) || (stream_category == conns[i]->airplay_stream_category))) { - debug(2, "Connection %d: joining...", conns[i]->connection_number); + debug(1, "Connection %d: joining...", conns[i]->connection_number); pthread_join(conns[i]->thread, NULL); - debug(2, "Connection %d: joined.", conns[i]->connection_number); + debug(1, "Connection %d: joined.", conns[i]->connection_number); free(conns[i]); conns[i] = NULL; } @@ -1195,13 +1046,15 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes } else if (nread == 0) { if (errno == 0) { // a blocking read that returns zero means eof -- implies connection closed by client - debug(1, "Connection %d closed by client.", conn->connection_number); + debug(1, "Connection %d RTSP closed by client.", conn->connection_number); } else { char errorstring[1024]; strerror_r(errno, (char *)errorstring, sizeof(errorstring)); - debug(1, "Connection %d closed by client with error %d: \"%s\".", + debug(1, "Connection %d RTSP port closed by client with error %d: \"%s\".", conn->connection_number, errno, (char *)errorstring); } + close(conn->fd); // close it from our end too... + conn->fd = 0; reply = rtsp_read_request_response_channel_closed; } else { char errorstring[1024]; @@ -1462,31 +1315,7 @@ int msg_write_response(rtsp_conn_info *conn, rtsp_message *resp) { return 0; } -char *get_category_string(airplay_stream_c cat) { - char *category; - switch (cat) { - case unspecified_stream_category: - category = "unspecified stream"; - break; - case ptp_stream: - category = "PTP stream"; - break; - case ntp_stream: - category = "NTP stream"; - break; - case remote_control_stream: - category = "Remote Control stream"; - break; - case classic_airplay_stream: - category = "Classic AirPlay stream"; - break; - default: - category = "Unexpected stream code"; - break; - } - return category; -} - +#ifdef CONFIG_AIRPLAY_2 void handle_record_2(rtsp_conn_info *conn, __attribute((unused)) rtsp_message *req, rtsp_message *resp) { debug(2, "Connection %d: RECORD on %s", conn->connection_number, @@ -1495,7 +1324,7 @@ void handle_record_2(rtsp_conn_info *conn, __attribute((unused)) rtsp_message *r msg_add_header(resp, "Audio-Latency", "0"); resp->respcode = 200; } - +#else void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { debug(2, "Connection %d: RECORD", conn->connection_number); if ((conn != NULL) && (principal_conn == conn)) { @@ -1545,6 +1374,7 @@ void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) resp->respcode = 451; } } +#endif #ifdef CONFIG_AIRPLAY_2 @@ -2494,9 +2324,9 @@ void handle_feedback(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message if (is_playing != 0) { if ((type != 96) && (type != 103)) - debug(1, "Feedback unexpected type: %u.", type); + debug(1, "Connection %d, feedback unexpected type: %u.", conn->connection_number, type); if ((rate != 44100.0) && (rate != 48000.0)) - debug(2, "Feedback unexpected rate: %f.", rate); + debug(2, "Connection %d, feedback unexpected rate: %f.", conn->connection_number, rate); plist_t payload_plist = plist_new_dict(); plist_dict_set_item(payload_plist, "type", plist_new_uint(type)); plist_dict_set_item(payload_plist, "sr", plist_new_real(rate)); @@ -2807,27 +2637,37 @@ void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_messag debug(2, "Connection %d: TEARDOWN 2 %s.", conn->connection_number, get_category_string(conn->airplay_stream_category)); - debug_log_rtsp_message(2, "TEARDOWN: ", req); - resp->respcode = 200; - msg_add_header(resp, "Connection", "close"); + debug_log_rtsp_message(2, "TEARDOWN 2: ", req); + + if (conn->player_thread) { + player_stop(conn); // this nulls the player_thread and cancels the threads... + activity_monitor_signify_activity(0); // inactive, and should be after command_stop() + } + /* + // msg_add_header(resp, "Connection", "close"); plist_t messagePlist = plist_from_rtsp_content(req); if (messagePlist != NULL) { // now see if the incoming plist contains a "streams" array plist_t streams = plist_dict_get_item(messagePlist, "streams"); if (streams) { - debug(3, "Connection %d: TEARDOWN %s Close the stream.", conn->connection_number, + debug(1, "Connection %d: TEARDOWN %s Close the stream.", conn->connection_number, get_category_string(conn->airplay_stream_category)); // we are being asked to close a stream - teardown_phase_one(conn); + + if (conn->player_thread) { + player_stop(conn); // this nulls the player_thread and cancels the threads... + activity_monitor_signify_activity(0); // inactive, and should be after command_stop() + } + plist_free(streams); debug(3, "Connection %d: TEARDOWN %s Close the stream complete", conn->connection_number, get_category_string(conn->airplay_stream_category)); } else { - debug(3, "Connection %d: TEARDOWN %s Close the connection.", conn->connection_number, + debug(1, "Connection %d: TEARDOWN %s Close the connection.", conn->connection_number, get_category_string(conn->airplay_stream_category)); - teardown_phase_one(conn); // try to do phase one anyway - teardown_phase_two(conn); + // teardown_phase_one(conn); // try to do phase one anyway + // teardown_phase_two(conn); } plist_free(messagePlist); @@ -2836,6 +2676,8 @@ void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_messag debug(1, "Connection %d: missing plist!", conn->connection_number); resp->respcode = 451; // don't know what to do here } + */ + resp->respcode = 200; // debug(1,"Bogus exit for valgrind -- remember to comment it out!."); // sps_shutdown(TOE_normal); // ask for a normal exit } @@ -2865,8 +2707,9 @@ void teardown(rtsp_conn_info *conn) { void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req, rtsp_message *resp) { - debug_log_rtsp_message(2, "TEARDOWN request", req); - debug(2, "Connection %d: TEARDOWN", conn->connection_number); + debug(1, "Connection %d: TEARDOWN", conn->connection_number); + debug_log_rtsp_message(1, "TEARDOWN request", req); + debug(3, "TEARDOWN: synchronously terminating the player thread of RTSP conversation thread %d (2).", conn->connection_number); @@ -2973,7 +2816,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) plist_get_string_val(timingProtocol, &timingProtocolString); if (timingProtocolString) { if (strcmp(timingProtocolString, "PTP") == 0) { - debug(2, "Connection %d: AP2 PTP connection from %s:%u (\"%s\") to self at %s:%u.", + debug(1, "Connection %d: AP2 PTP connection from %s:%u (\"%s\") to self at %s:%u.", conn->connection_number, conn->client_ip_string, conn->client_rtsp_port, clientNameString, conn->self_ip_string, conn->self_rtsp_port); conn->airplay_stream_category = ptp_stream; @@ -2987,6 +2830,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) clientNameString, conn->self_ip_string, conn->self_rtsp_port); conn->airplay_stream_category = ntp_stream; conn->timing_type = ts_ntp; + do_pthread_setname(&conn->thread, "ap2_ntp_%d", conn->connection_number); } else if (strcmp(timingProtocolString, "None") == 0) { debug(3, "Connection %d: SETUP: a \"None\" setup detected from %s:%u (\"%s\") to self at " @@ -3005,6 +2849,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) conn->connection_number, conn->client_ip_string, conn->client_rtsp_port, clientNameString, conn->self_ip_string, conn->self_rtsp_port); conn->airplay_stream_category = remote_control_stream; + do_pthread_setname(&conn->thread, "ap2_rc_%d", conn->connection_number); } else { debug(1, "Connection %d: SETUP: a \"None\" setup detected, with " @@ -3030,9 +2875,6 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) #ifdef CONFIG_METADATA send_ssnc_metadata('conn', conn->client_ip_string, strlen(conn->client_ip_string), 1); // before disconnecting an existing play -#endif - -#ifdef CONFIG_METADATA send_ssnc_metadata('clip', conn->client_ip_string, strlen(conn->client_ip_string), 1); send_ssnc_metadata('svip', conn->self_ip_string, strlen(conn->self_ip_string), 1); #endif @@ -3152,8 +2994,8 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) // debug(1,"initial timing peer command: \"%s\".", timing_list_message); // ptp_send_control_message_string(timing_list_message); set_client_as_ptp_clock(conn); - ptp_send_control_message_string( - "B"); // signify clock dependability period is "B"eginning (or continuing) + // ptp_send_control_message_string("B"); // signify clock dependability period is + // "B"eginning (or continuing) plist_dict_set_item(timingPeerInfoPlist, "Addresses", addresses); plist_dict_set_item(timingPeerInfoPlist, "ID", plist_new_string(conn->self_ip_string)); @@ -3178,30 +3020,18 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) if (conn->rtp_event_thread != NULL) debug(1, "previous rtp_event_thread allocation not freed, it seems."); + conn->ap2_event_receiver_exited = 0; conn->rtp_event_thread = malloc(sizeof(pthread_t)); if (conn->rtp_event_thread == NULL) die("Couldn't allocate space for pthread_t"); - named_pthread_create(conn->rtp_event_thread, NULL, &rtp_event_receiver, (void *)conn, + named_pthread_create(conn->rtp_event_thread, NULL, &ap2_event_receiver, (void *)conn, "ap2_ptp_evt_%d", conn->connection_number); plist_dict_set_item(setupResponsePlist, "eventPort", plist_new_uint(conn->local_event_port)); plist_dict_set_item(setupResponsePlist, "timingPort", plist_new_uint(0)); // dummy - cancel_all_RTSP_threads(ptp_stream, - conn->connection_number); // kill all the other listeners - // only update these things if you're (still) the principal conn - pthread_rwlock_wrlock( - &principal_conn_lock); // don't let the principal_conn be changed - pthread_cleanup_push(rwlock_unlock, (void *)&principal_conn_lock); - if (principal_conn == conn) { - config.airplay_statusflags |= 1 << 11; // DeviceSupportsRelay - // config.airplay_statusflags |= 1 << 17; // ReceiverSessionIsActive - build_bonjour_strings(conn); - debug(2, "Connection %d: SETUP mdns_update on %s.", conn->connection_number, - get_category_string(conn->airplay_stream_category)); - mdns_update(NULL, secondary_txt_records); - } - pthread_cleanup_pop(1); // release the principal_conn lock + // cancel_all_RTSP_threads(ptp_stream, + // conn->connection_number); // kill all the other listeners resp->respcode = 200; } else { debug(1, "SETUP on Connection %d: PTP setup -- no timingPeerInfo plist.", @@ -3249,11 +3079,11 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) "Connection %d SETUP (RC): previous rtp_event_thread allocation not freed, it " "seems.", conn->connection_number); - + conn->ap2_event_receiver_exited = 0; conn->rtp_event_thread = malloc(sizeof(pthread_t)); if (conn->rtp_event_thread == NULL) die("Couldn't allocate space for pthread_t"); - named_pthread_create(conn->rtp_event_thread, NULL, &rtp_event_receiver, (void *)conn, + named_pthread_create(conn->rtp_event_thread, NULL, &ap2_rc_event_receiver, (void *)conn, "ap2_rc_evt_%d", conn->connection_number); plist_dict_set_item(setupResponsePlist, "eventPort", plist_new_uint(conn->local_event_port)); @@ -3288,7 +3118,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) conn->connection_number, get_category_string(conn->airplay_stream_category)); debug_log_rtsp_message(3, "SETUP (AirPlay 2) SETUP with streams incoming message", req); if (conn->airplay_stream_category == ptp_stream) { - // get stream[0] + ptp_send_control_message_string( "B"); // signify clock dependability period is "B"eginning (or continuing) plist_t stream0 = plist_array_get_item(streams, 0); @@ -3308,7 +3138,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) } // get the compression type - // this seems to be static -- a stream's encoding can change dyunamically, it seems + // this seems to be static -- a stream's encoding can change dynamically, it seems item = plist_dict_get_item(stream0, "ct"); // compression type if (item != NULL) { plist_get_uint_val(item, &item_value); @@ -3329,9 +3159,6 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) warn("No frames per packet (spf) property found in setup!"); } - // more stuff - // set up a UDP control stream and thread and a UDP or TCP audio stream and thread - // bind a new UDP port and get a socket conn->local_ap2_control_port = 0; // any port err = bind_socket_and_port(SOCK_DGRAM, conn->connection_ip_family, conn->self_ip_string, @@ -3344,7 +3171,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) conn->local_ap2_control_port); named_pthread_create(&conn->rtp_ap2_control_thread, NULL, &rtp_ap2_control_receiver, - (void *)conn, "ap2_ct_%d", conn->connection_number); + (void *)conn, "ap2_cn_%d", conn->connection_number); // get the DACP-ID and Active Remote for remote control stuff @@ -5172,12 +4999,52 @@ void rtsp_conversation_thread_cleanup_function(void *arg) { int oldState; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); - debug(3, "Connection %d: %s rtsp_conversation_thread_func_cleanup_function called.", + debug(1, "Connection %d: %s rtsp_conversation_thread_func_cleanup_function called.", conn->connection_number, get_category_string(conn->airplay_stream_category)); + + if (conn->player_thread) { + player_stop(conn); // this nulls the player_thread and cancels the threads... + activity_monitor_signify_activity(0); // inactive, and should be after command_stop() + } + + if (conn->fd > 0) { + debug( + 1, + "Connection %d: terminating -- closing RTSP connection socket %d: from %s:%u to self at " + "%s:%u.", + conn->connection_number, conn->fd, conn->client_ip_string, conn->client_rtsp_port, + conn->self_ip_string, conn->self_rtsp_port); + close(conn->fd); + conn->fd = 0; + } + +#ifdef CONFIG_AIRPLAY_2 + if (conn->session_key) { + free(conn->session_key); + conn->session_key = NULL; + } + + if (conn->ap2_event_receiver_exited == 0) { + debug(3, "waiting for event receiver to exit"); + usleep(2000000); + if (conn->ap2_event_receiver_exited == 0) { + debug(1, "Connection %d: %s event receiver has not exited, so cancelling it.", + conn->connection_number, get_category_string(conn->airplay_stream_category)); + pthread_cancel(*conn->rtp_event_thread); + } + } + pthread_join(*conn->rtp_event_thread, NULL); + free(conn->rtp_event_thread); + conn->rtp_event_thread = NULL; + conn->ap2_event_receiver_exited = 0; + debug(1, "Connection %d: %s event thread deleted.", conn->connection_number, + get_category_string(conn->airplay_stream_category)); +#endif + #ifdef CONFIG_AIRPLAY_2 // AP2 - teardown_phase_one(conn); - teardown_phase_two(conn); + // teardown_phase_one(conn); + // teardown_phase_two(conn); #else // AP1 teardown(conn); @@ -5186,33 +5053,23 @@ void rtsp_conversation_thread_cleanup_function(void *arg) { debug(3, "Connection %d: terminating -- closing timing, control and audio sockets...", conn->connection_number); if (conn->control_socket) { - debug(3, "Connection %d: terminating -- closing control_socket %d.", conn->connection_number, + debug(1, "Connection %d: terminating -- closing control_socket %d.", conn->connection_number, conn->control_socket); close(conn->control_socket); conn->control_socket = 0; } if (conn->timing_socket) { - debug(3, "Connection %d: terminating -- closing timing_socket %d.", conn->connection_number, + debug(1, "Connection %d: terminating -- closing timing_socket %d.", conn->connection_number, conn->timing_socket); close(conn->timing_socket); conn->timing_socket = 0; } if (conn->audio_socket) { - debug(3, "Connection %d: terminating -- closing audio_socket %d.", conn->connection_number, + debug(1, "Connection %d: terminating -- closing audio_socket %d.", conn->connection_number, conn->audio_socket); close(conn->audio_socket); conn->audio_socket = 0; } - if (conn->fd > 0) { - debug( - 3, - "Connection %d: terminating -- closing RTSP connection socket %d: from %s:%u to self at " - "%s:%u.", - conn->connection_number, conn->fd, conn->client_ip_string, conn->client_rtsp_port, - conn->self_ip_string, conn->self_rtsp_port); - close(conn->fd); - conn->fd = 0; - } if (conn->auth_nonce) { free(conn->auth_nonce); conn->auth_nonce = NULL; @@ -5263,7 +5120,6 @@ void rtsp_conversation_thread_cleanup_function(void *arg) { conn->ap2_client_name = NULL; } #endif - // remove flow control and mutexes int rc = pthread_mutex_destroy(&conn->volume_control_mutex); if (rc) @@ -5279,7 +5135,8 @@ 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(3, "Connection %d: Closed.", conn->connection_number); + + debug(1, "Connection %d: Closed.", conn->connection_number); conn->running = 0; // for the garbage collector pthread_setcancelstate(oldState, NULL); } @@ -5331,7 +5188,7 @@ static void *rtsp_conversation_thread_func(void *pconn) { while (conn->stop == 0) { pthread_testcancel(); - int debug_level = 2; // for printing the request and response + int debug_level = 3; // for printing the request and response // check to see if a conn has been zeroed