works in AP1-compatible AP2 mode, but resend isn't working.

This commit is contained in:
Mike Brady
2021-05-02 12:49:41 +01:00
parent a68f28ac2b
commit c2940eb23b
4 changed files with 86 additions and 35 deletions
+7 -1
View File
@@ -1922,11 +1922,17 @@ void *player_thread_func(void *arg) {
}
}
#ifndef CONFIG_AIRPLAY_2
#ifdef CONFIG_AIRPLAY_2
if (conn->timing_type == ts_ntp) {
#endif
// create and start the timing, control and audio receiver threads
pthread_create(&conn->rtp_audio_thread, NULL, &rtp_audio_receiver, (void *)conn);
pthread_create(&conn->rtp_control_thread, NULL, &rtp_control_receiver, (void *)conn);
pthread_create(&conn->rtp_timing_thread, NULL, &rtp_timing_receiver, (void *)conn);
#ifdef CONFIG_AIRPLAY_2
}
#endif
pthread_cleanup_push(player_thread_cleanup_handler, arg); // undo what's been done so far
+6 -2
View File
@@ -81,7 +81,6 @@ typedef enum {
ast_apple_lossless,
} audio_stream_type;
typedef enum { ts_ntp, ts_ptp } timing_source_type;
typedef struct {
int encrypted;
@@ -91,6 +90,9 @@ typedef struct {
} stream_cfg;
#ifdef CONFIG_AIRPLAY_2
typedef enum { ts_ntp, ts_ptp } timing_t;
typedef enum { ap_1, ap_2 } airplay_t;
typedef struct file_cipher_context {
struct pair_cipher_context *cipher_context;
int active; // can be created during a pair setup but not activated until next read
@@ -103,7 +105,6 @@ typedef struct file_cipher_context {
typedef struct {
int connection_number; // for debug ID purposes, nothing else...
timing_source_type type_of_timing; // are we using NTP or PTP?
int resend_interval; // this is really just for debugging
char *UserAgent; // free this on teardown
int AirPlayVersion; // zero if not an AirPlay session. Used to help calculate latency
@@ -240,6 +241,9 @@ typedef struct {
uint32_t anchor_rtptime;
#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?
pthread_t rtp_event_thread;
pthread_t rtp_ap2_control_thread;
pthread_t rtp_realtime_audio_thread;
+31 -12
View File
@@ -120,6 +120,7 @@ void rtp_audio_receiver_cleanup_handler(__attribute__((unused)) void *arg) {
}
void *rtp_audio_receiver(void *arg) {
debug(1,"rtp_audio_receiver start");
pthread_cleanup_push(rtp_audio_receiver_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
@@ -258,6 +259,7 @@ void rtp_control_handler_cleanup_handler(__attribute__((unused)) void *arg) {
}
void *rtp_control_receiver(void *arg) {
debug(1,"rtp_control_receiver start");
pthread_cleanup_push(rtp_control_handler_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
@@ -288,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
@@ -300,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 "
@@ -506,6 +508,7 @@ void rtp_timing_sender_cleanup_handler(void *arg) {
}
void *rtp_timing_sender(void *arg) {
debug(1,"rtp_timing_sender start");
pthread_cleanup_push(rtp_timing_sender_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
struct timing_request {
@@ -607,6 +610,7 @@ void rtp_timing_receiver_cleanup_handler(void *arg) {
}
void *rtp_timing_receiver(void *arg) {
debug(1,"rtp_timing_receiver start");
pthread_cleanup_push(rtp_timing_receiver_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
@@ -672,7 +676,7 @@ void *rtp_timing_receiver(void *arg) {
if (packet[1] == 0xd3) { // timing reply
return_time = arrival_time - conn->departure_time;
debug(3, "clock synchronisation request: return time is %8.3f milliseconds.",
debug(1, "clock synchronisation request: return time is %8.3f milliseconds.",
0.000001 * return_time);
if (return_time < 200000000) { // must be less than 0.2 seconds
@@ -2358,20 +2362,35 @@ void *rtp_buffered_audio_processor(void *arg) {
pthread_exit(NULL);
}
int frame_to_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *conn) {
return frame_to_ptp_local_time(timestamp, time, conn);
if (conn->timing_type == ts_ptp)
return frame_to_ptp_local_time(timestamp, time, conn);
else
return frame_to_ntp_local_time(timestamp, time, conn);
}
int local_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) {
return local_ptp_time_to_frame(time, frame, conn);
if (conn->timing_type == ts_ptp)
return local_ptp_time_to_frame(time, frame, conn);
else
return local_ntp_time_to_frame(time, frame, conn);
}
void reset_anchor_info(rtsp_conn_info *conn) { reset_ptp_anchor_info(conn); }
void reset_anchor_info(rtsp_conn_info *conn) {
if (conn->timing_type == ts_ptp)
reset_ptp_anchor_info(conn);
else
reset_ntp_anchor_info(conn);
}
int have_timestamp_timing_information(rtsp_conn_info *conn) {
return have_ptp_timing_information(conn);
if (conn->timing_type == ts_ptp)
return have_ptp_timing_information(conn);
else
return have_ntp_timestamp_timing_information(conn);
}
#else
int frame_to_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *conn) {
return frame_to_ntp_local_time(timestamp, time, conn);
}
+42 -20
View File
@@ -2090,7 +2090,7 @@ void handle_options(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *
"OPTIONS, GET_PARAMETER, SET_PARAMETER, GET");
}
void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
rtsp_message *resp) {
debug(2, "Connection %d: TEARDOWN", conn->connection_number);
@@ -2198,8 +2198,8 @@ void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message
resp->respcode = 451; // don't know what to do here
}
}
#endif
#else
void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
rtsp_message *resp) {
debug_log_rtsp_message(2, "TEARDOWN request", req);
@@ -2220,7 +2220,7 @@ void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message
resp->respcode = 451;
}
}
#endif
void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
// TODO -- don't know what this is for in AP2
@@ -3571,6 +3571,11 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag
}
}
*/
// In AirPlay 2, an ANNOUNCE signifies the start of an AirPlay 1 session.
#ifdef CONFIG_AIRPLAY_2
conn->airplay_type = ap_1;
conn->timing_type = ts_ntp;
#endif
conn->stream.type = ast_unknown;
resp->respcode = 456; // 456 - Header Field Not Valid for Resource
@@ -3788,34 +3793,39 @@ out:
}
}
#ifdef CONFIG_AIRPLAY_2
static struct method_handler {
char *method;
void (*handler)(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp);
void (*ap1_handler)(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp); // for AirPlay 1
void (*ap2_handler)(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp); // for AirPlay 2
} method_handlers[] = {{"OPTIONS", handle_options, handle_options},
{"ANNOUNCE", handle_announce, handle_announce},
{"FLUSH", handle_flush, handle_flush},
{"TEARDOWN", handle_teardown, handle_teardown_2},
{"SETUP", handle_setup, handle_setup_2},
{"GET_PARAMETER", handle_get_parameter, handle_get_parameter},
{"SET_PARAMETER", handle_set_parameter, handle_set_parameter},
{"RECORD", handle_record, handle_record_2},
{"GET", handle_get, handle_get},
{"POST", handle_post, handle_post},
{"SETPEERS", handle_setpeers, handle_setpeers},
{"SETRATEANCHORTI", handle_setrateanchori, handle_setrateanchori},
{"FLUSHBUFFERED", handle_flushbuffered, handle_flushbuffered},
{NULL, NULL, NULL}};
#else
static struct method_handler {
char *method;
void (*handler)(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp); // for AirPlay 1 only
} method_handlers[] = {{"OPTIONS", handle_options},
{"ANNOUNCE", handle_announce},
{"FLUSH", handle_flush},
{"TEARDOWN", handle_teardown},
#ifdef CONFIG_AIRPLAY_2
{"SETUP", handle_setup_2},
#else
{"SETUP", handle_setup},
#endif
{"GET_PARAMETER", handle_get_parameter},
{"SET_PARAMETER", handle_set_parameter},
#ifdef CONFIG_AIRPLAY_2
{"RECORD", handle_record_2},
#else
{"RECORD", handle_record},
#endif
#ifdef CONFIG_AIRPLAY_2
{"GET", handle_get},
{"POST", handle_post},
{"SETPEERS", handle_setpeers},
{"SETRATEANCHORTI", handle_setrateanchori},
{"FLUSHBUFFERED", handle_flushbuffered},
#endif
{NULL, NULL}};
#endif
static void apple_challenge(int fd, rtsp_message *req, rtsp_message *resp) {
char *hdr = msg_get_header(req, "Apple-Challenge");
@@ -4247,7 +4257,14 @@ static void *rtsp_conversation_thread_func(void *pconn) {
for (mh = method_handlers; mh->method; mh++) {
if (!strcmp(mh->method, req->method)) {
method_selected = 1;
#ifdef CONFIG_AIRPLAY_2
if (conn->airplay_type == ap_1)
mh->ap1_handler(conn, req, resp);
else
mh->ap2_handler(conn, req, resp);
#else
mh->handler(conn, req, resp);
#endif
break;
}
}
@@ -4613,6 +4630,11 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
die("Couldn't allocate memory for an rtsp_conn_info record.");
memset(conn, 0, sizeof(rtsp_conn_info));
conn->connection_number = RTSP_connection_index++;
#ifdef CONFIG_AIRPLAY_2
conn->airplay_type = ap_2; // changed if an ANNOUNCE is received
conn->timing_type = ts_ptp; // changed if an ANNOUNCE is received
#endif
socklen_t size_of_reply = sizeof(SOCKADDR);
conn->fd = accept(acceptfd, (struct sockaddr *)&conn->remote, &size_of_reply);