}
}
-#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
ast_apple_lossless,
} audio_stream_type;
-typedef enum { ts_ntp, ts_ptp } timing_source_type;
typedef struct {
int encrypted;
} 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
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
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;
}
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;
}
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;
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
// 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 "
}
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 {
}
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;
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
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);
}
"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);
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);
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
}
}
*/
+ // 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
}
}
+#ifdef CONFIG_AIRPLAY_2
+static struct method_handler {
+ char *method;
+ 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);
+ 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");
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;
}
}
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);