Modify/simplify the TEARDOWN handling in AP2 mode. Allow the threads to natrually terminate when ports are closed by the client.

This commit is contained in:
Mike Brady
2025-12-03 15:56:38 +00:00
parent 3e57f8c706
commit 93bafe304a
4 changed files with 188 additions and 283 deletions
+57 -14
View File
@@ -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 <syscall.h>
// 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);
}
+8 -5
View File
@@ -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);
}
+9 -7
View File
@@ -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
+114 -257
View File
@@ -68,6 +68,7 @@
#include <polarssl/md5.h>
#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