Move AP2 player termination code from handle_teardown into the player cleanup function so that cancelling the player thread cleans up properly.

This commit is contained in:
Mike Brady
2021-05-08 17:46:13 +01:00
parent e270f8a9a5
commit 953ad8ac14
7 changed files with 161 additions and 163 deletions
+103 -63
View File
@@ -443,7 +443,7 @@ void player_put_packet(int original_format, seq_t seqno, uint32_t actual_timesta
conn->ab_read = seqno;
conn->ab_synced = 1;
conn->first_packet_timestamp = 0;
debug(1,"synced by first packet");
debug(1, "synced by first packet");
} else if (original_format == 0) {
// if the packet is coming in original format, the sequence number is important
// otherwise, ignore is by setting it equal to the expected sequence number in ab_write
@@ -1541,12 +1541,12 @@ void player_thread_cleanup_handler(void *arg) {
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
if (pthread_mutex_trylock(&playing_conn_lock) == 0) {
pthread_cleanup_push(mutex_unlock,&playing_conn_lock);
if (playing_conn == conn) {
if (config.output->stop)
config.output->stop();
}
pthread_cleanup_pop(1); // unlock the mutex
pthread_cleanup_push(mutex_unlock, &playing_conn_lock);
if (playing_conn == conn) {
if (config.output->stop)
config.output->stop();
}
pthread_cleanup_pop(1); // unlock the mutex
}
int oldState;
@@ -1554,19 +1554,21 @@ void player_thread_cleanup_handler(void *arg) {
debug(3, "Connection %d: player thread main loop exit via player_thread_cleanup_handler.",
conn->connection_number);
if (config.statistics_requested) {
int rawSeconds = (int)difftime(time(NULL), conn->playstart);
int elapsedHours = rawSeconds / 3600;
int elapsedMin = (rawSeconds / 60) % 60;
int elapsedSec = rawSeconds % 60;
if (conn->frame_rate_status)
inform("Connection %d: Playback Stopped. Total playing time %02d:%02d:%02d. Input: %0.2f, output: %0.2f "
"frames per second.", conn->connection_number,
elapsedHours, elapsedMin, elapsedSec, conn->input_frame_rate, conn->frame_rate);
inform("Connection %d: Playback Stopped. Total playing time %02d:%02d:%02d. Input: %0.2f, "
"output: %0.2f "
"frames per second.",
conn->connection_number, elapsedHours, elapsedMin, elapsedSec, conn->input_frame_rate,
conn->frame_rate);
else
inform("Connection %d: Playback Stopped. Total playing time %02d:%02d:%02d. Input: %0.2f frames per second.", conn->connection_number,
elapsedHours, elapsedMin, elapsedSec, conn->input_frame_rate);
inform("Connection %d: Playback Stopped. Total playing time %02d:%02d:%02d. Input: %0.2f "
"frames per second.",
conn->connection_number, elapsedHours, elapsedMin, elapsedSec, conn->input_frame_rate);
}
#ifdef CONFIG_DACP_CLIENT
@@ -1576,57 +1578,92 @@ void player_thread_cleanup_handler(void *arg) {
mdns_dacp_monitor_set_id(NULL); // say we're not interested in following that DACP id any more
#endif
#ifdef CONFIG_AIRPORT_2
if (conn->rtp_ap2_control_thread) {
debug(3, "Cancelling rtp_ap2_control_thread");
pthread_cancel(conn->rtp_ap2_control_thread);
pthread_join(conn->rtp_ap2_control_thread, NULL);
}
if (conn->ap2_control_socket) {
close(conn->ap2_control_socket);
conn->ap2_control_socket = 0;
}
// four possibilities
// 1 -- regular AirPlay 1
// 2 -- AirPlay 2 in AirPlay 1 mode
// 3 -- AirPlay 2 in Buffered Audio Mode
// 4 -- AirPlay 3 in Realtime Audio Mode.
// realtime stuff
if (conn->rtp_realtime_audio_thread) {
debug(3, "Cancelling rtp_realtime_audio_thread");
pthread_cancel(conn->rtp_realtime_audio_thread);
pthread_join(conn->rtp_realtime_audio_thread, NULL);
}
if (conn->realtime_audio_socket) {
close(conn->realtime_audio_socket);
conn->realtime_audio_socket = 0;
}
#ifdef CONFIG_AIRPLAY_2
if (conn->airplay_type == ap_2) {
debug(1, "In AP2 mode");
// buffered stuff
if (conn->rtp_buffered_audio_thread) {
debug(3, "Cancelling rtp_buffered_audio_thread");
pthread_cancel(conn->rtp_buffered_audio_thread);
pthread_join(conn->rtp_buffered_audio_thread, NULL);
}
if (conn->buffered_audio_socket) {
close(conn->buffered_audio_socket);
conn->buffered_audio_socket = 0;
if (conn->airplay_stream_type == realtime_stream) {
debug(2, "Connection %d: TEARDOWN Delete Realtime Audio Stream Thread",
conn->connection_number);
pthread_cancel(conn->rtp_realtime_audio_thread);
pthread_join(conn->rtp_realtime_audio_thread, NULL);
if (conn->realtime_audio_socket)
close(conn->realtime_audio_socket);
} else if (conn->airplay_stream_type == buffered_stream) {
debug(2, "Connection %d: TEARDOWN Delete Buffered Audio Stream Thread",
conn->connection_number);
pthread_cancel(conn->rtp_buffered_audio_thread);
pthread_join(conn->rtp_buffered_audio_thread, NULL);
if (conn->buffered_audio_socket)
close(conn->buffered_audio_socket);
} else {
die("Unrecognised Stream Type");
}
if (conn->rtp_ap2_control_thread) {
debug(1, "Cancelling rtp_ap2_control_thread");
pthread_cancel(conn->rtp_ap2_control_thread);
pthread_join(conn->rtp_ap2_control_thread, NULL);
}
if (conn->ap2_control_socket) {
close(conn->ap2_control_socket);
conn->ap2_control_socket = 0;
conn->ap2_remote_control_socket_addr_length =
0; // indicates to the control receiver thread that the socket address need to be
// recreated.
}
if (conn->airplay_stream_type == realtime_stream) {
// realtime stuff
if (conn->rtp_realtime_audio_thread) {
debug(1, "Cancelling rtp_realtime_audio_thread");
pthread_cancel(conn->rtp_realtime_audio_thread);
pthread_join(conn->rtp_realtime_audio_thread, NULL);
}
if (conn->realtime_audio_socket) {
close(conn->realtime_audio_socket);
conn->realtime_audio_socket = 0;
}
} else if (conn->airplay_stream_type == buffered_stream) {
// buffered stuff
if (conn->rtp_buffered_audio_thread) {
debug(3, "Cancelling rtp_buffered_audio_thread");
pthread_cancel(conn->rtp_buffered_audio_thread);
pthread_join(conn->rtp_buffered_audio_thread, NULL);
}
if (conn->buffered_audio_socket) {
close(conn->buffered_audio_socket);
conn->buffered_audio_socket = 0;
}
}
} else {
#endif
debug(1, "Cancelling AP1 timing, control and audio threads...");
debug(3, "Cancel timing thread.");
pthread_cancel(conn->rtp_timing_thread);
debug(3, "Join timing thread.");
pthread_join(conn->rtp_timing_thread, NULL);
debug(3, "Timing thread terminated.");
debug(3, "Cancel control thread.");
pthread_cancel(conn->rtp_control_thread);
debug(3, "Join control thread.");
pthread_join(conn->rtp_control_thread, NULL);
debug(3, "Control thread terminated.");
debug(3, "Cancel audio thread.");
pthread_cancel(conn->rtp_audio_thread);
debug(3, "Join audio thread.");
pthread_join(conn->rtp_audio_thread, NULL);
debug(3, "Audio thread terminated.");
#ifdef CONFIG_AIRPLAY_2
}
#endif
debug(3, "Cancelling timing, control and audio threads...");
debug(3, "Cancel timing thread.");
pthread_cancel(conn->rtp_timing_thread);
debug(3, "Join timing thread.");
pthread_join(conn->rtp_timing_thread, NULL);
debug(3, "Timing thread terminated.");
debug(3, "Cancel control thread.");
pthread_cancel(conn->rtp_control_thread);
debug(3, "Join control thread.");
pthread_join(conn->rtp_control_thread, NULL);
debug(3, "Control thread terminated.");
debug(3, "Cancel audio thread.");
pthread_cancel(conn->rtp_audio_thread);
debug(3, "Join audio thread.");
pthread_join(conn->rtp_audio_thread, NULL);
debug(3, "Audio thread terminated.");
if (conn->outbuf) {
free(conn->outbuf);
conn->outbuf = NULL;
@@ -1649,6 +1686,7 @@ void player_thread_cleanup_handler(void *arg) {
terminate_decoders(conn);
reset_anchor_info(conn);
release_play_lock(conn);
conn->rtp_running = 0;
pthread_setcancelstate(oldState, NULL);
}
@@ -2282,7 +2320,8 @@ void *player_thread_func(void *arg) {
int64_t filler_length =
(int64_t)(config.resyncthreshold * config.output_rate); // number of samples
if ((sync_error > 0) && (sync_error > filler_length)) {
debug(1, "Large positive sync error of: %" PRId64 " frames (%f seconds).", sync_error, (sync_error * 1.0)/config.output_rate);
debug(1, "Large positive sync error of: %" PRId64 " frames (%f seconds).",
sync_error, (sync_error * 1.0) / config.output_rate);
int64_t local_frames_to_drop = sync_error / conn->output_sample_ratio;
uint32_t frames_to_drop_sized = local_frames_to_drop;
@@ -2295,9 +2334,10 @@ void *player_thread_func(void *arg) {
} else if ((sync_error < 0) && ((-sync_error) > filler_length)) {
debug(1,
"Large negative sync error of: %" PRId64 " frames (%f seconds), with should_be_frame_32 of %" PRIu32
", nt of %" PRId64 " and current_delay of %" PRId64 ".",
sync_error, (sync_error * 1.0)/config.output_rate, should_be_frame_32,
"Large negative sync error of: %" PRId64
" frames (%f seconds), with should_be_frame_32 of %" PRIu32 ", nt of %" PRId64
" and current_delay of %" PRId64 ".",
sync_error, (sync_error * 1.0) / config.output_rate, should_be_frame_32,
inframe->given_timestamp * conn->output_sample_ratio, current_delay);
int64_t silence_length = -sync_error;
if (silence_length > (filler_length * 5))
+3 -1
View File
@@ -91,6 +91,7 @@ typedef struct {
#ifdef CONFIG_AIRPLAY_2
typedef enum { ts_ntp, ts_ptp } timing_t;
typedef enum { ap_1, ap_2 } airplay_t;
typedef enum { realtime_stream, buffered_stream } airplay_stream_t;
typedef struct {
uint8_t *data;
@@ -259,7 +260,8 @@ typedef struct {
#ifdef CONFIG_AIRPLAY_2
airplay_t airplay_type; // are we using AirPlay 1 or AirPlay 2 protocol on this connection?
timing_t timing_type; // are we using NTP or PTP on this connection?
airplay_stream_t airplay_stream_type; // is it realtime audio or buffered audio...
timing_t timing_type; // are we using NTP or PTP on this connection?
pthread_t rtp_event_thread;
pthread_t rtp_ap2_control_thread;
+14 -13
View File
@@ -120,7 +120,7 @@ void rtp_audio_receiver_cleanup_handler(__attribute__((unused)) void *arg) {
}
void *rtp_audio_receiver(void *arg) {
debug(1, "rtp_audio_receiver start");
debug(3, "rtp_audio_receiver start");
pthread_cleanup_push(rtp_audio_receiver_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
@@ -259,7 +259,7 @@ void rtp_control_handler_cleanup_handler(__attribute__((unused)) void *arg) {
}
void *rtp_control_receiver(void *arg) {
debug(1, "rtp_control_receiver start");
debug(2, "rtp_control_receiver start");
pthread_cleanup_push(rtp_control_handler_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
@@ -290,8 +290,8 @@ void *rtp_control_receiver(void *arg) {
obfp += 2;
};
*obfp = 0;
// get raw timestamp information
// I think that a good way to understand these timestamps is that
// (1) the rtlt below is the timestamp of the frame that should be playing at the
@@ -302,19 +302,19 @@ void *rtp_control_receiver(void *arg) {
// Thus, (3) the latency can be calculated by subtracting the second from the
// first.
// There must be more to it -- there something missing.
// In addition, it seems that if the value of the short represented by the second
// pair of bytes in the packet is 7
// then an extra time lag is expected to be added, presumably by
// the AirPort Express.
// Best guess is that this delay is 11,025 frames.
uint32_t rtlt = nctohl(&packet[4]); // raw timestamp less latency
uint32_t rt = nctohl(&packet[16]); // raw timestamp
uint32_t fl = nctohs(&packet[2]); //
debug(1,"Sync Packet of %d bytes received: \"%s\", flags: %d, timestamps %u and %u,
giving a latency of %d frames.",plen,obf,fl,rt,rtlt,rt-rtlt);
//debug(1,"Monotonic timestamps are: %" PRId64 " and %" PRId64 "
@@ -406,7 +406,7 @@ void *rtp_control_receiver(void *arg) {
if (la != conn->latency) {
conn->latency = la;
debug(1,
debug(2,
"New latency: %" PRIu32 ", sync latency: %" PRIu32
", minimum latency: %" PRIu32 ", maximum "
"latency: %" PRIu32 ", fixed offset: %" PRIu32
@@ -508,7 +508,7 @@ void rtp_timing_sender_cleanup_handler(void *arg) {
}
void *rtp_timing_sender(void *arg) {
debug(1, "rtp_timing_sender start");
debug(2, "rtp_timing_sender start");
pthread_cleanup_push(rtp_timing_sender_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
struct timing_request {
@@ -610,7 +610,7 @@ void rtp_timing_receiver_cleanup_handler(void *arg) {
}
void *rtp_timing_receiver(void *arg) {
debug(1, "rtp_timing_receiver start");
debug(3, "rtp_timing_receiver start");
pthread_cleanup_push(rtp_timing_receiver_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
@@ -2090,7 +2090,8 @@ void *rtp_buffered_audio_processor(void *arg) {
0) {
int64_t lead_time = buffer_should_be_time - get_absolute_time_in_ns();
// debug(1,"lead time in buffered_audio is %f milliseconds.", lead_time * 0.000001);
if ((lead_time >= (int64_t)(reqested_lead_time * 1000000000)) || (streaming_has_started == 1)) {
if ((lead_time >= (int64_t)(reqested_lead_time * 1000000000)) ||
(streaming_has_started == 1)) {
if (streaming_has_started == 0)
debug(1, "rtp lead time is %f ms.", 0.000001 * lead_time);
streaming_has_started = 1;
+34 -77
View File
@@ -845,19 +845,20 @@ static ssize_t read_encrypted(int fd, ap2_pairing *ctx, void *buf, size_t count)
return got;
buf_add(&ctx->encrypted_buf, in, got);
ssize_t consumed = pair_decrypt(&plain, &plain_len, ctx->encrypted_buf.data, ctx->encrypted_buf.len, ctx->cipher_ctx);
ssize_t consumed = pair_decrypt(&plain, &plain_len, ctx->encrypted_buf.data,
ctx->encrypted_buf.len, ctx->cipher_ctx);
if (consumed < 0)
return -1;
buf_drain(&ctx->encrypted_buf, consumed);
} while (plain_len == 0);
// Fast path, avoids some memcpy + allocs in case of the normal, small message
/* if (ctx->plain_buf.len == 0 && plain_len < count) {
memcpy(buf, plain, plain_len);
free(plain);
return plain_len;
}
*/
/* if (ctx->plain_buf.len == 0 && plain_len < count) {
memcpy(buf, plain, plain_len);
free(plain);
return plain_len;
}
*/
buf_add(&ctx->plain_buf, plain, plain_len);
free(plain);
@@ -868,7 +869,8 @@ static ssize_t write_encrypted(rtsp_conn_info *conn, const void *buf, size_t cou
uint8_t *encrypted;
size_t encrypted_len;
ssize_t ret = pair_encrypt(&encrypted, &encrypted_len, buf, count, conn->ap2_control_pairing.cipher_ctx);
ssize_t ret =
pair_encrypt(&encrypted, &encrypted_len, buf, count, conn->ap2_control_pairing.cipher_ctx);
if (ret < 0) {
debug(1, pair_cipher_errmsg(conn->ap2_control_pairing.cipher_ctx));
return -1;
@@ -1245,7 +1247,8 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
debug(1, "GET /info Stage 1: response plist not created from XML!");
} else {
plist_dict_set_item(response_plist, "features", plist_new_uint(config.airplay_features));
plist_dict_set_item(response_plist, "statusFlags", plist_new_uint(config.airplay_statusflags));
plist_dict_set_item(response_plist, "statusFlags",
plist_new_uint(config.airplay_statusflags));
plist_dict_set_item(response_plist, "deviceID", plist_new_string(config.airplay_device_id));
plist_dict_set_item(response_plist, "pi", plist_new_string(config.airplay_pi));
plist_dict_set_item(response_plist, "name", plist_new_string(config.service_name));
@@ -1446,7 +1449,8 @@ void handle_pair_verify(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *r
req->contentlength);
if (!conn->ap2_control_pairing.verify_ctx) {
conn->ap2_control_pairing.verify_ctx = pair_verify_new(PAIR_SERVER_HOMEKIT, NULL, NULL, NULL, config.airplay_device_id);
conn->ap2_control_pairing.verify_ctx =
pair_verify_new(PAIR_SERVER_HOMEKIT, NULL, NULL, NULL, config.airplay_device_id);
if (!conn->ap2_control_pairing.verify_ctx) {
debug(1, "Error creating verify context");
resp->respcode = 500; // Internal Server Error
@@ -1454,7 +1458,8 @@ void handle_pair_verify(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *r
}
}
ret = pair_verify(&body, &body_len, conn->ap2_control_pairing.verify_ctx, (const uint8_t *)req->content, req->contentlength);
ret = pair_verify(&body, &body_len, conn->ap2_control_pairing.verify_ctx,
(const uint8_t *)req->content, req->contentlength);
if (ret < 0) {
debug(1, pair_verify_errmsg(conn->ap2_control_pairing.verify_ctx));
resp->respcode = 470; // Connection Authorization Required
@@ -1463,14 +1468,15 @@ void handle_pair_verify(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *r
ret = pair_verify_result(&result, conn->ap2_control_pairing.verify_ctx);
if (ret == 0 && result->shared_secret_len > 0) {
conn->ap2_control_pairing.cipher_ctx = pair_cipher_new(PAIR_SERVER_HOMEKIT, 2, result->shared_secret, result->shared_secret_len);
conn->ap2_control_pairing.cipher_ctx =
pair_cipher_new(PAIR_SERVER_HOMEKIT, 2, result->shared_secret, result->shared_secret_len);
if (!conn->ap2_control_pairing.cipher_ctx) {
debug(1, "Error setting up rtsp control channel ciphering\n");
goto out;
}
}
out:
out:
resp->content = (char *)body; // these will be freed when the data is sent
resp->contentlength = body_len;
if (body)
@@ -1487,7 +1493,8 @@ void handle_pair_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *re
req->contentlength);
if (!conn->ap2_control_pairing.setup_ctx) {
conn->ap2_control_pairing.setup_ctx = pair_setup_new(PAIR_SERVER_HOMEKIT, config.airplay_pin, NULL, NULL, config.airplay_device_id);
conn->ap2_control_pairing.setup_ctx = pair_setup_new(PAIR_SERVER_HOMEKIT, config.airplay_pin,
NULL, NULL, config.airplay_device_id);
if (!conn->ap2_control_pairing.setup_ctx) {
debug(1, "Error creating setup context");
resp->respcode = 500; // Internal Server Error
@@ -1495,7 +1502,8 @@ void handle_pair_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *re
}
}
ret = pair_setup(&body, &body_len, conn->ap2_control_pairing.setup_ctx, (const uint8_t *)req->content, req->contentlength);
ret = pair_setup(&body, &body_len, conn->ap2_control_pairing.setup_ctx,
(const uint8_t *)req->content, req->contentlength);
if (ret < 0) {
debug(1, pair_setup_errmsg(conn->ap2_control_pairing.setup_ctx));
resp->respcode = 470; // Connection Authorization Required
@@ -1506,14 +1514,15 @@ void handle_pair_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *re
if (ret == 0 && result->shared_secret_len > 0) {
// Transient pairing completed (pair-setup step 2), prepare encryption, but
// don't activate yet, the response to this request is still plaintext
conn->ap2_control_pairing.cipher_ctx = pair_cipher_new(PAIR_SERVER_HOMEKIT, 2, result->shared_secret, result->shared_secret_len);
conn->ap2_control_pairing.cipher_ctx =
pair_cipher_new(PAIR_SERVER_HOMEKIT, 2, result->shared_secret, result->shared_secret_len);
if (!conn->ap2_control_pairing.cipher_ctx) {
debug(1, "Error setting up rtsp control channel ciphering\n");
goto out;
}
}
out:
out:
resp->content = (char *)body; // these will be freed when the data is sent
resp->contentlength = body_len;
if (body)
@@ -1722,68 +1731,12 @@ void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_messag
if (streams) {
// we are being asked to close a stream
// at present, we just close this one
debug(2,
"Connection %d: A \"streams\" array has been found -- stop the player, stop and delete "
"the control "
"and audio threads and close the ports",
conn->connection_number);
// get stream[0]
plist_t stream0 = plist_array_get_item(streams, 0);
plist_t item = plist_dict_get_item(stream0, "type");
uint64_t item_value;
plist_get_uint_val(item, &item_value);
switch (item_value) {
case 96: {
release_play_lock(conn);
debug(2, "Connection %d: TEARDOWN Delete Realtime Audio Stream Thread",
conn->connection_number);
pthread_cancel(conn->rtp_realtime_audio_thread);
pthread_join(conn->rtp_realtime_audio_thread, NULL);
if (conn->realtime_audio_socket)
close(conn->realtime_audio_socket);
} break;
case 103: {
release_play_lock(conn); // shoulda been released when stopped...
debug(2, "Connection %d: TEARDOWN Delete Buffered Audio Stream Thread",
conn->connection_number);
pthread_cancel(conn->rtp_buffered_audio_thread);
pthread_join(conn->rtp_buffered_audio_thread, NULL);
if (conn->buffered_audio_socket)
close(conn->buffered_audio_socket);
} break;
default: {}
}
debug(2, "Connection %d: TEARDOWN Close Control Thread", conn->connection_number);
pthread_cancel(conn->rtp_ap2_control_thread);
pthread_join(conn->rtp_ap2_control_thread, NULL);
debug(2, "Connection %d: TEARDOWN Close Control Socket.", conn->connection_number);
if (conn->ap2_control_socket) {
close(conn->ap2_control_socket);
conn->ap2_remote_control_socket_addr_length =
0; // indicates to the control receiver thread that the socket address need to be
// recreated.
}
debug(2, "Connection %d: TEARDOWN Stop the Player.", conn->connection_number);
player_stop(conn);
if (conn->session_key) {
free(conn->session_key);
conn->session_key = NULL;
}
plist_free(streams);
release_play_lock(conn);
// if (ptp_shm_interface_close() != 0)
// warn("Error closing the nqptp interface.");
debug(2, "Connection %d: Stream TEARDOWN complete", conn->connection_number);
} else {
// we are being asked to disconnect
@@ -1939,7 +1892,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
debug(1, "Connection %d. Realtime Audio Stream Detected.", conn->connection_number);
debug_log_rtsp_message(2, "Realtime Audio Stream SETUP incoming message", req);
get_play_lock(conn);
conn->airplay_stream_type = realtime_stream;
// bind a new UDP port and get a socket
conn->local_realtime_audio_port = 0; // any port
err = bind_socket_and_port(SOCK_DGRAM, conn->connection_ip_family, conn->self_ip_string,
@@ -1992,6 +1945,8 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
case 103: {
debug(1, "Connection %d. Buffered Audio Stream Detected.", conn->connection_number);
debug_log_rtsp_message(2, "Buffered Audio Stream SETUP incoming message", req);
// for buffered audio, delay getting the play lock until we actually start playing...
conn->airplay_stream_type = buffered_stream;
// get needed stuff
// bind a new TCP port and get a socket
@@ -2128,7 +2083,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
debug(1, "Can't find timingPeerInfo addresses");
}
} else {
debug(1, "Can't find timingPeerInfo");
debug_log_rtsp_message(1, "Unrecognised SETUP incoming message", req);
}
// make up the timing peer info list part of the response...
@@ -4137,7 +4092,8 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
features_lo, features_hi);
*p++ = featuresString;
char statusflagsString[32];
snprintf(statusflagsString, sizeof(statusflagsString), "flags=0x%" PRIx32, config.airplay_statusflags);
snprintf(statusflagsString, sizeof(statusflagsString), "flags=0x%" PRIx32,
config.airplay_statusflags);
*p++ = statusflagsString;
*p++ = "protovers=1.1";
*p++ = "acl=0";
@@ -4153,7 +4109,8 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
*p++ = "gcgl=0";
char pkString[128];
snprintf(pkString, sizeof(pkString), "pk=");
pkString_make(pkString + strlen("pk="), sizeof(pkString) - strlen("pk="), config.airplay_device_id);
pkString_make(pkString + strlen("pk="), sizeof(pkString) - strlen("pk="),
config.airplay_device_id);
*p++ = pkString;
*p++ = NULL;
#else
+1 -4
View File
@@ -8,10 +8,7 @@ extern rtsp_conn_info **conns;
void *rtsp_listen_loop(__attribute((unused)) void *arg);
// void rtsp_shutdown_stream(void);
void rtsp_request_shutdown_stream(void);
void cancel_all_RTSP_threads(void);
void release_play_lock(rtsp_conn_info *conn);
// initialise and completely delete the metadata stuff
+1 -1
View File
@@ -40,7 +40,7 @@ general =
// volume_control_profile = "standard" ; // use this advanced setting to specify how the airplay volume is transferred to the mixer volume.
// "standard" makes the volume change more quickly at lower volumes and slower at higher volumes.
// "flat" makes the volume change at the same rate at all volumes.
// volume_range_combined_hardware_priority = "no"; // when extending the volume range by combining the built-in software attenuator with the hardware mixer attenuator, set this to "yes" to reduce volume by using the hardware mixer first, then the built-in software attenuator.
// volume_control_combined_hardware_priority = "no"; // when extending the volume range by combining the built-in software attenuator with the hardware mixer attenuator, set this to "yes" to reduce volume by using the hardware mixer first, then the built-in software attenuator.
// run_this_when_volume_is_set = "/full/path/to/application/and/args"; // Run the specified application whenever the volume control is set or changed.
// The desired AirPlay volume is appended to the end of the command line – leave a space if you want it treated as an extra argument.
// AirPlay volume goes from 0 to -30 and -144 means "mute".
+5 -4
View File
@@ -1577,10 +1577,11 @@ int main(int argc, char **argv) {
// 496155702020608 this setting here is the source of both the plist features response and the
// mDNS string.
config.airplay_features = 0x1C340405F4A00;
// Advertised with mDNS and returned with GET /info, see https://openairplay.github.io/airplay-spec/status_flags.html
// 0x4: Audio cable attached, no PIN required (transient pairing), no Homekit access control
// 0x204: Audio cable attached, OneTimePairingRequired
// 0x604: Audio cable attached, OneTimePairingRequired, allow Homekit access control
// Advertised with mDNS and returned with GET /info, see
// https://openairplay.github.io/airplay-spec/status_flags.html 0x4: Audio cable attached, no PIN
// required (transient pairing), no Homekit access control 0x204: Audio cable attached,
// OneTimePairingRequired 0x604: Audio cable attached, OneTimePairingRequired, allow Homekit
// access control
config.airplay_statusflags = 0x4;
// Set to NULL to work with transient pairing
config.airplay_pin = NULL;