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:
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user