works in AP1-compatible AP2 mode, but resend isn't working.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user