From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 2 May 2021 11:49:41 +0000 (+0100) Subject: works in AP1-compatible AP2 mode, but resend isn't working. X-Git-Tag: 4.0-dev~70 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c2940eb23bd3f0065cafa9aafe22a973bf5889d5;p=thirdparty%2Fshairport-sync.git works in AP1-compatible AP2 mode, but resend isn't working. --- diff --git a/player.c b/player.c index 6b67ee13..7d6d026e 100644 --- a/player.c +++ b/player.c @@ -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 diff --git a/player.h b/player.h index 9a92c0c4..5766f6fc 100644 --- a/player.h +++ b/player.h @@ -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; diff --git a/rtp.c b/rtp.c index 35e415a9..50166543 100644 --- a/rtp.c +++ b/rtp.c @@ -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); } diff --git a/rtsp.c b/rtsp.c index 41ddff22..962a7626 100644 --- a/rtsp.c +++ b/rtsp.c @@ -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 (*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"); @@ -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);