Improve NQPTP clock diagnostics, tidy up some messages.
This commit is contained in:
@@ -1593,8 +1593,7 @@ void player_thread_cleanup_handler(void *arg) {
|
||||
debug(2, "Cancelling AP2 timing, control and audio threads...");
|
||||
|
||||
if (conn->airplay_stream_type == realtime_stream) {
|
||||
debug(2, "Connection %d: Delete Realtime Audio Stream thread",
|
||||
conn->connection_number);
|
||||
debug(2, "Connection %d: Delete Realtime Audio Stream thread", conn->connection_number);
|
||||
pthread_cancel(conn->rtp_realtime_audio_thread);
|
||||
pthread_join(conn->rtp_realtime_audio_thread, NULL);
|
||||
|
||||
@@ -1613,9 +1612,9 @@ void player_thread_cleanup_handler(void *arg) {
|
||||
pthread_join(conn->rtp_ap2_control_thread, NULL);
|
||||
|
||||
} else {
|
||||
debug(1, "Cancelling AP1-compatible timing, control and audio threads...");
|
||||
debug(2, "Cancelling AP1-compatible timing, control and audio threads...");
|
||||
#else
|
||||
debug(2, "Cancelling AP1 timing, control and audio threads...");
|
||||
debug(2, "Cancelling AP1 timing, control and audio threads...");
|
||||
#endif
|
||||
debug(3, "Cancel timing thread.");
|
||||
pthread_cancel(conn->rtp_timing_thread);
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#define time_ping_history \
|
||||
(1 << time_ping_history_power_of_two) // 2^7 is 128. At 1 per three seconds, approximately six
|
||||
// minutes of records
|
||||
|
||||
typedef struct time_ping_record {
|
||||
uint64_t dispersion;
|
||||
uint64_t local_time;
|
||||
@@ -42,6 +41,19 @@ typedef struct time_ping_record {
|
||||
int chosen;
|
||||
} time_ping_record;
|
||||
|
||||
// these are for reporting the status of the clock
|
||||
typedef enum {
|
||||
clock_no_anchor_info,
|
||||
clock_ok,
|
||||
clock_service_unavailable,
|
||||
clock_access_error,
|
||||
clock_data_unavailable,
|
||||
clock_no_master,
|
||||
clock_version_mismatch,
|
||||
clock_not_synchronised,
|
||||
clock_not_valid
|
||||
} clock_status_t;
|
||||
|
||||
typedef uint16_t seq_t;
|
||||
|
||||
typedef struct audio_buffer_entry { // decoded audio packets
|
||||
@@ -258,6 +270,8 @@ typedef struct {
|
||||
uint64_t anchor_time; // this is the time according to the clock
|
||||
uint32_t anchor_rtptime;
|
||||
|
||||
clock_status_t clock_status;
|
||||
|
||||
#ifdef CONFIG_AIRPLAY_2
|
||||
airplay_t airplay_type; // are we using AirPlay 1 or AirPlay 2 protocol on this connection?
|
||||
airplay_stream_t airplay_stream_type; // is it realtime audio or buffered audio...
|
||||
|
||||
+13
-33
@@ -42,8 +42,6 @@
|
||||
int shm_fd;
|
||||
void *mapped_addr = NULL;
|
||||
|
||||
int failure_message_sent = 0;
|
||||
|
||||
static pthread_mutex_t ptp_access_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// returns a copy of the shared memory data from the nqptp
|
||||
@@ -63,9 +61,8 @@ int get_nqptp_data(struct shm_structure *nqptp_data) {
|
||||
}
|
||||
|
||||
int ptp_get_clock_info(uint64_t *actual_clock_id, uint64_t *raw_offset) {
|
||||
int response = -1;
|
||||
int response = clock_ok;
|
||||
pthread_cleanup_debug_mutex_lock(&ptp_access_mutex, 10000, 1);
|
||||
// 0 -> valid and working; -1 -> can't connect to nqptp
|
||||
if (actual_clock_id != NULL)
|
||||
*actual_clock_id = 0;
|
||||
if (raw_offset != NULL)
|
||||
@@ -81,39 +78,28 @@ int ptp_get_clock_info(uint64_t *actual_clock_id, uint64_t *raw_offset) {
|
||||
*actual_clock_id = nqptp_data.master_clock_id;
|
||||
if (raw_offset != NULL)
|
||||
*raw_offset = nqptp_data.local_to_master_time_offset;
|
||||
response = 0;
|
||||
} else {
|
||||
debug(1, "clock not valid");
|
||||
response = -2; // clock info not valid
|
||||
response = clock_no_master;
|
||||
}
|
||||
} else {
|
||||
if (failure_message_sent == 0) {
|
||||
warn("This version of Shairport Sync requires an NQPTP with a Shared Memory Interface "
|
||||
"Version %u, but the installed version is %u. Please install the correct version of "
|
||||
"NQPTP.",
|
||||
NQPTP_SHM_STRUCTURES_VERSION, nqptp_data.version);
|
||||
failure_message_sent = 1;
|
||||
}
|
||||
response = clock_version_mismatch;
|
||||
}
|
||||
} else {
|
||||
response = clock_data_unavailable;
|
||||
}
|
||||
if (response != -1)
|
||||
response = ptp_shm_interface_close();
|
||||
if (ptp_shm_interface_close() != 0)
|
||||
response = clock_access_error;
|
||||
} else {
|
||||
if (failure_message_sent == 0) {
|
||||
warn("Can't open the interface to nqptp. Is the service running?");
|
||||
failure_message_sent = 1;
|
||||
}
|
||||
response = clock_service_unavailable;
|
||||
}
|
||||
pthread_cleanup_pop(1); // release the mutex
|
||||
if (response == 0)
|
||||
failure_message_sent = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
int ptp_shm_interface_open() {
|
||||
mapped_addr = NULL;
|
||||
int shared_memory_file_descriptor = shm_open("/nqptp", O_RDWR, 0);
|
||||
int response = -1;
|
||||
int response = 0;
|
||||
if (shared_memory_file_descriptor >= 0) {
|
||||
mapped_addr =
|
||||
// needs to be PROT_READ | PROT_WRITE to allow the mapped memory to be writable for the
|
||||
@@ -121,19 +107,13 @@ int ptp_shm_interface_open() {
|
||||
mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
shared_memory_file_descriptor, 0);
|
||||
if (mapped_addr == MAP_FAILED) {
|
||||
if (failure_message_sent == 0) {
|
||||
debug(1, "unable to open the shared memory interface with nqptp. Is the service running?");
|
||||
failure_message_sent = 1;
|
||||
}
|
||||
response = -1;
|
||||
}
|
||||
if (close(shared_memory_file_descriptor) == -1) {
|
||||
if (failure_message_sent == 0) {
|
||||
debug(1, "error closing \"/nqptp\" after mapping it.");
|
||||
failure_message_sent = 1;
|
||||
}
|
||||
} else {
|
||||
response = 0;
|
||||
response = -1;
|
||||
}
|
||||
} else {
|
||||
response = -1;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ void *rtp_audio_receiver(void *arg) {
|
||||
}
|
||||
|
||||
void rtp_control_handler_cleanup_handler(__attribute__((unused)) void *arg) {
|
||||
debug(1, "Control Receiver Cleanup Done.");
|
||||
debug(2, "Control Receiver Cleanup Done.");
|
||||
}
|
||||
|
||||
void *rtp_control_receiver(void *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 "
|
||||
@@ -1226,13 +1226,13 @@ void reset_ptp_anchor_info(rtsp_conn_info *conn) {
|
||||
|
||||
int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
|
||||
uint64_t *anchorLocalTime) {
|
||||
int response = 0;
|
||||
|
||||
uint64_t actual_clock_id, actual_offset;
|
||||
int response = ptp_get_clock_info(&actual_clock_id, &actual_offset);
|
||||
|
||||
int ptp_status = ptp_get_clock_info(&actual_clock_id, &actual_offset);
|
||||
|
||||
if (ptp_status == 0) {
|
||||
if (conn->anchor_remote_info_is_valid != 0) {
|
||||
if (response == clock_ok) {
|
||||
if (conn->anchor_remote_info_is_valid !=
|
||||
0) { // i.e. if we have anchor clock ID and anchor time / rtptime
|
||||
if (actual_clock_id == conn->anchor_clock) {
|
||||
conn->last_anchor_clock_offset = actual_offset;
|
||||
conn->last_anchor_time_of_update = get_absolute_time_in_ns();
|
||||
@@ -1263,29 +1263,60 @@ int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
|
||||
*anchorLocalTime = conn->anchor_time - conn->last_anchor_clock_offset;
|
||||
}
|
||||
} else {
|
||||
debug(2, "no clock and no old anchor times");
|
||||
response = -1;
|
||||
response = clock_not_valid; // no current clock information and no previous clock info
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug(2, "don't have anchor_remote_time_info");
|
||||
response = -1;
|
||||
response = clock_no_anchor_info; // no anchor information
|
||||
}
|
||||
} else if (ptp_status == -1) {
|
||||
debug(3, "don't have the ptp clock interface");
|
||||
response = -1;
|
||||
} else if (ptp_status == -2) {
|
||||
debug(1, "ptp clock not valid");
|
||||
response = -1;
|
||||
} else {
|
||||
debug(3, "ptp clock error");
|
||||
response = -1;
|
||||
}
|
||||
|
||||
// here, check and update the clock status
|
||||
if ((clock_status_t)response != conn->clock_status) {
|
||||
switch (response) {
|
||||
case clock_ok:
|
||||
debug(1, "Connection %d: NQPTP clock is valid and available.", conn->connection_number);
|
||||
break;
|
||||
case clock_service_unavailable:
|
||||
debug(1, "Connection %d: NQPTP clock is not available.", conn->connection_number);
|
||||
warn("Can't access the NQPTP clock. Is NQPTP running?");
|
||||
break;
|
||||
case clock_access_error:
|
||||
debug(1, "Connection %d: Error accessing the NQPTP clock interface.",
|
||||
conn->connection_number);
|
||||
break;
|
||||
case clock_data_unavailable:
|
||||
debug(1, "Connection %d: Can not access NQPTP clock information.", conn->connection_number);
|
||||
break;
|
||||
case clock_no_master:
|
||||
debug(1, "Connection %d: No NQPTP master clock.", conn->connection_number);
|
||||
break;
|
||||
case clock_no_anchor_info:
|
||||
debug(1, "Connection %d: No Clock Anchor.", conn->connection_number);
|
||||
break;
|
||||
case clock_version_mismatch:
|
||||
debug(1, "Connection %d: NQPTP clock interface mismatch.", conn->connection_number);
|
||||
warn("This version of Shairport Sync is not compatible with the installed version of NQPTP. "
|
||||
"Please update.");
|
||||
break;
|
||||
case clock_not_synchronised:
|
||||
debug(1, "Connection %d: NQPTP clock is not synchronised.", conn->connection_number);
|
||||
break;
|
||||
case clock_not_valid:
|
||||
debug(1, "Connection %d: NQPTP clock information is not valid.", conn->connection_number);
|
||||
break;
|
||||
default:
|
||||
debug(3, "Connection %d: NQPTP clock reports an unrecognised status: %u.",
|
||||
conn->connection_number, response);
|
||||
break;
|
||||
}
|
||||
conn->clock_status = response;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
int have_ptp_timing_information(rtsp_conn_info *conn) {
|
||||
if (get_ptp_anchor_local_time_info(conn, NULL, NULL) == 0)
|
||||
if (get_ptp_anchor_local_time_info(conn, NULL, NULL) == clock_ok)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
@@ -1295,7 +1326,7 @@ int frame_to_ptp_local_time(uint32_t timestamp, uint64_t *time, rtsp_conn_info *
|
||||
int result = -1;
|
||||
uint32_t anchor_rtptime;
|
||||
uint64_t anchor_local_time;
|
||||
if (get_ptp_anchor_local_time_info(conn, &anchor_rtptime, &anchor_local_time) == 0) {
|
||||
if (get_ptp_anchor_local_time_info(conn, &anchor_rtptime, &anchor_local_time) == clock_ok) {
|
||||
int32_t frame_difference = timestamp - anchor_rtptime;
|
||||
int64_t time_difference = frame_difference;
|
||||
time_difference = time_difference * 1000000000;
|
||||
@@ -1313,7 +1344,7 @@ int local_ptp_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn
|
||||
int result = -1;
|
||||
uint32_t anchor_rtptime;
|
||||
uint64_t anchor_local_time;
|
||||
if (get_ptp_anchor_local_time_info(conn, &anchor_rtptime, &anchor_local_time) == 0) {
|
||||
if (get_ptp_anchor_local_time_info(conn, &anchor_rtptime, &anchor_local_time) == clock_ok) {
|
||||
int64_t time_difference = time - anchor_local_time;
|
||||
int64_t frame_difference = time_difference;
|
||||
frame_difference = frame_difference * conn->input_rate; // but this is by 10^9
|
||||
@@ -1355,8 +1386,9 @@ void *rtp_event_receiver(void *arg) {
|
||||
if (nread < 0) {
|
||||
char errorstring[1024];
|
||||
strerror_r(errno, (char *)errorstring, sizeof(errorstring));
|
||||
debug(1, "Connection %d: error in ap2 rtp_event_receiver %d: \"%s\". Could not recv a packet.", conn->connection_number, errno,
|
||||
errorstring);
|
||||
debug(1,
|
||||
"Connection %d: error in ap2 rtp_event_receiver %d: \"%s\". Could not recv a packet.",
|
||||
conn->connection_number, errno, errorstring);
|
||||
// if ((config.diagnostic_drop_packet_fraction == 0.0) ||
|
||||
// (drand48() > config.diagnostic_drop_packet_fraction)) {
|
||||
} else if (nread > 0) {
|
||||
@@ -1364,11 +1396,15 @@ void *rtp_event_receiver(void *arg) {
|
||||
// ssize_t plen = nread;
|
||||
debug(1, "Packet Received on Event Port.");
|
||||
if (packet[1] == 0xD7) {
|
||||
debug(1, "Connection %d: AP2 Event Receiver -- Time Announce RTP packet of type 0x%02X length %d received.", conn->connection_number,
|
||||
packet[1], nread);
|
||||
debug(1,
|
||||
"Connection %d: AP2 Event Receiver -- Time Announce RTP packet of type 0x%02X length "
|
||||
"%d received.",
|
||||
conn->connection_number, packet[1], nread);
|
||||
} else {
|
||||
debug(1, "Connection %d: AP2 Event Receiver -- Unknown RTP packet of type 0x%02X length %d received.", conn->connection_number,
|
||||
packet[1], nread);
|
||||
debug(1,
|
||||
"Connection %d: AP2 Event Receiver -- Unknown RTP packet of type 0x%02X length %d "
|
||||
"received.",
|
||||
conn->connection_number, packet[1], nread);
|
||||
}
|
||||
// } else {
|
||||
// debug(3, "Event Receiver Thread -- dropping incoming packet to simulate a bad network.");
|
||||
@@ -1377,7 +1413,8 @@ void *rtp_event_receiver(void *arg) {
|
||||
finished = 1;
|
||||
}
|
||||
} while (finished == 0);
|
||||
debug(1, "Connection %d: AP2 Event Receiver RTP thread \"normal\" exit.", conn->connection_number);
|
||||
debug(1, "Connection %d: AP2 Event Receiver RTP thread \"normal\" exit.",
|
||||
conn->connection_number);
|
||||
pthread_cleanup_pop(1); // close the socket
|
||||
|
||||
pthread_cleanup_pop(1); // do the cleanup
|
||||
@@ -1680,7 +1717,7 @@ ssize_t buffered_read(buffered_tcp_desc *descriptor, void *buf, size_t count) {
|
||||
#define STANDARD_PACKET_SIZE 4096
|
||||
|
||||
void buffered_tcp_reader_cleanup_handler(__attribute__((unused)) void *arg) {
|
||||
debug(1, "Buffered TCP Reader Thread Exit via Cleanup.");
|
||||
debug(2, "Buffered TCP Reader Thread Exit via Cleanup.");
|
||||
}
|
||||
|
||||
void *buffered_tcp_reader(void *arg) {
|
||||
@@ -1850,11 +1887,11 @@ void addADTStoPacket(uint8_t *packet, int packetLen) {
|
||||
}
|
||||
|
||||
void rtp_buffered_audio_cleanup_handler(__attribute__((unused)) void *arg) {
|
||||
debug(1, "Buffered Audio Receiver Cleanup Start.");
|
||||
debug(2, "Buffered Audio Receiver Cleanup Start.");
|
||||
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
|
||||
close(conn->buffered_audio_socket);
|
||||
conn->buffered_audio_socket = 0;
|
||||
debug(1, "Buffered Audio Receiver Cleanup Done.");
|
||||
debug(2, "Buffered Audio Receiver Cleanup Done.");
|
||||
}
|
||||
|
||||
void *rtp_buffered_audio_processor(void *arg) {
|
||||
@@ -2094,7 +2131,8 @@ void *rtp_buffered_audio_processor(void *arg) {
|
||||
if ((lead_time >= (int64_t)(reqested_lead_time * 1000000000)) ||
|
||||
(streaming_has_started == 1)) {
|
||||
if (streaming_has_started == 0)
|
||||
debug(2, "Connection %d: buffered audio lead time is %f seconds.", 0.000000001 * lead_time);
|
||||
debug(2, "Connection %d: buffered audio lead time is %f seconds.",
|
||||
0.000000001 * lead_time);
|
||||
streaming_has_started = 1;
|
||||
player_put_packet(0, 0, pcm_buffer_read_point_rtptime,
|
||||
pcm_buffer + pcm_buffer_read_point, 352, conn);
|
||||
|
||||
@@ -1395,7 +1395,7 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
|
||||
debug(2, "Connection %d: Start playing.", conn->connection_number);
|
||||
conn->ap2_play_enabled = 1;
|
||||
} else {
|
||||
debug(1, "Connection %d: Stop playing.", conn->connection_number);
|
||||
debug(2, "Connection %d: Stop playing.", conn->connection_number);
|
||||
conn->ap2_play_enabled = 0;
|
||||
// not sure this is needed yet
|
||||
reset_anchor_info(conn); // needed if the player resumes
|
||||
@@ -1934,7 +1934,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
|
||||
conn->input_num_channels = conn->stream.fmtp[7];
|
||||
conn->input_bit_depth = conn->stream.fmtp[3];
|
||||
conn->input_bytes_per_frame = conn->input_num_channels * ((conn->input_bit_depth + 7) / 8);
|
||||
debug(1, "Realtime Stream Play");
|
||||
debug(2, "Realtime Stream Play");
|
||||
if (conn->ap2_timing_peer_list_message)
|
||||
ptp_send_control_message_string(conn->ap2_timing_peer_list_message);
|
||||
else
|
||||
@@ -2215,7 +2215,7 @@ void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
|
||||
msg_add_header(resp, "Session", "1");
|
||||
|
||||
resp->respcode = 200; // it all worked out okay
|
||||
debug(1,
|
||||
debug(2,
|
||||
"Connection %d: SETUP DACP-ID \"%s\" from %s to %s with UDP ports Control: "
|
||||
"%d, Timing: %d and Audio: %d.",
|
||||
conn->connection_number, conn->dacp_id, &conn->client_ip_string,
|
||||
@@ -4239,7 +4239,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
|
||||
inet_ntop(conn->connection_ip_family, self_addr, conn->self_ip_string,
|
||||
sizeof(conn->self_ip_string));
|
||||
|
||||
debug(1, "Connection %d: new connection from %s:%u to self at %s:%u.",
|
||||
debug(2, "Connection %d: new connection from %s:%u to self at %s:%u.",
|
||||
conn->connection_number, conn->client_ip_string, conn->client_rtsp_port,
|
||||
conn->self_ip_string, conn->self_rtsp_port);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user