diff --git a/lib/byteutils.c b/lib/byteutils.c index 4a5bdea..4798922 100644 --- a/lib/byteutils.c +++ b/lib/byteutils.c @@ -12,6 +12,8 @@ * Lesser General Public License for more details. */ +#define SECOND_IN_NSECS 1000000000UL + #include #ifdef _WIN32 # include @@ -96,23 +98,23 @@ void byteutils_put_int(unsigned char* b, int offset, uint32_t value) { } /** - * Reads an ntp timestamp and returns it as micro seconds since the Unix epoch + * Reads an ntp timestamp and returns it as nano seconds since the Unix epoch */ uint64_t byteutils_get_ntp_timestamp(unsigned char *b, int offset) { uint64_t seconds = ntohl(((unsigned int) byteutils_get_int(b, offset))) - SECONDS_FROM_1900_TO_1970; uint64_t fraction = ntohl((unsigned int) byteutils_get_int(b, offset + 4)); - return (seconds * 1000000L) + ((fraction * 1000000L) >> 32); + return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32); } /** - * Writes a time given as micro seconds since the Unix time epoch as an ntp timestamp + * Writes a time given as nano seconds since the Unix time epoch as an ntp timestamp * into the buffer at position offset */ -void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t us_since_1970) { - uint64_t seconds = us_since_1970 / 1000000L; - uint64_t microseconds = us_since_1970 % 1000000L; +void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t ns_since_1970) { + uint64_t seconds = ns_since_1970 / SECOND_IN_NSECS; + uint64_t nanoseconds = ns_since_1970 % SECOND_IN_NSECS; seconds += SECONDS_FROM_1900_TO_1970; - uint64_t fraction = (microseconds << 32) / 1000000L; + uint64_t fraction = (nanoseconds << 32) / SECOND_IN_NSECS; // Write in big endian! byteutils_put_int(b, offset, htonl(seconds)); diff --git a/lib/raop_ntp.c b/lib/raop_ntp.c index 7d79284..6318b82 100644 --- a/lib/raop_ntp.c +++ b/lib/raop_ntp.c @@ -33,6 +33,7 @@ #include "byteutils.h" #include "utils.h" +#define SECOND_IN_NSECS 1000000000UL #define RAOP_NTP_DATA_COUNT 8 #define RAOP_NTP_PHI_PPM 15ull // PPM #define RAOP_NTP_R_RHO ((1ull << 32) / 1000u) // packet precision @@ -284,7 +285,8 @@ raop_ntp_thread(void *arg) byteutils_put_ntp_timestamp(request, 24, send_time); int send_len = sendto(raop_ntp->tsock, (char *)request, sizeof(request), 0, (struct sockaddr *) &raop_ntp->remote_saddr, raop_ntp->remote_saddr_len); - logger_log(raop_ntp->logger, LOGGER_DEBUG, "\nraop_ntp send_len = %d, now = %llu", send_len, send_time); + logger_log(raop_ntp->logger, LOGGER_DEBUG, "\nraop_ntp send_len = %d, now = %8.6f", send_len, + (double) send_time / SECOND_IN_NSECS); if (send_len < 0) { logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp error sending request"); } else { @@ -305,7 +307,6 @@ raop_ntp_thread(void *arg) } else { //local time of the server when the NTP response packet returns int64_t t3 = (int64_t) raop_ntp_get_local_time(raop_ntp); - timeout_counter = 0; char *str = utils_data_to_string(response, response_len, 16); logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp receive time type_t=%d packetlen = %d\n%s", @@ -321,15 +322,15 @@ raop_ntp_thread(void *arg) // Local time of the client when the response message leaves the client int64_t t2 = (int64_t) byteutils_get_ntp_timestamp(response, 24); - // The iOS client device sends its time in micro seconds relative to an arbitrary Epoch (the last boot). - // For a little bonus confusion, they add SECONDS_FROM_1900_TO_1970 * 1000000 us. + // The iOS client device sends its time in seconds relative to an arbitrary Epoch (the last boot). + // For a little bonus confusion, they add SECONDS_FROM_1900_TO_1970. // This means we have to expect some rather huge offset, but its growth or shrink over time should be small. raop_ntp->data_index = (raop_ntp->data_index + 1) % RAOP_NTP_DATA_COUNT; raop_ntp->data[raop_ntp->data_index].time = t3; raop_ntp->data[raop_ntp->data_index].offset = ((t1 - t0) + (t2 - t3)) / 2; raop_ntp->data[raop_ntp->data_index].delay = ((t3 - t0) - (t2 - t1)); - raop_ntp->data[raop_ntp->data_index].dispersion = RAOP_NTP_R_RHO + RAOP_NTP_S_RHO + (t3 - t0) * RAOP_NTP_PHI_PPM / 1000000u; + raop_ntp->data[raop_ntp->data_index].dispersion = RAOP_NTP_R_RHO + RAOP_NTP_S_RHO + (t3 - t0) * RAOP_NTP_PHI_PPM / SECOND_IN_NSECS; // Sort by delay memcpy(data_sorted, raop_ntp->data, sizeof(data_sorted)); @@ -341,7 +342,7 @@ raop_ntp_thread(void *arg) // Calculate dispersion for(int i = 0; i < RAOP_NTP_DATA_COUNT; ++i) { - unsigned long long disp = raop_ntp->data[i].dispersion + (t3 - raop_ntp->data[i].time) * RAOP_NTP_PHI_PPM / 1000000u; + unsigned long long disp = raop_ntp->data[i].dispersion + (t3 - raop_ntp->data[i].time) * RAOP_NTP_PHI_PPM / SECOND_IN_NSECS; dispersion += disp / two_pow_n[i]; } @@ -454,54 +455,54 @@ raop_ntp_stop(raop_ntp_t *raop_ntp) } /** - * Converts from a little endian ntp timestamp to micro seconds since the Unix epoch. + * Converts from a little endian ntp timestamp to nano seconds since the Unix epoch. * Does the same thing as byteutils_get_ntp_timestamp, except its input is an uint64_t * and expected to already be in little endian. * Please note this just converts to a different representation, the clock remains the * same. */ -uint64_t raop_ntp_timestamp_to_micro_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff) { +uint64_t raop_ntp_timestamp_to_nano_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff) { uint64_t seconds = ((ntp_timestamp >> 32) & 0xffffffff) - (account_for_epoch_diff ? SECONDS_FROM_1900_TO_1970 : 0); uint64_t fraction = (ntp_timestamp & 0xffffffff); - return (seconds * 1000000) + ((fraction * 1000000) >> 32); + return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32); } /** - * Returns the current time in micro seconds according to the local wall clock. + * Returns the current time in nano seconds according to the local wall clock. * The system Unix time is used as the local wall clock. */ uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp) { struct timespec time; clock_gettime(CLOCK_REALTIME, &time); - return (uint64_t)time.tv_sec * 1000000L + (uint64_t)(time.tv_nsec / 1000); + return ((uint64_t)time.tv_sec) * SECOND_IN_NSECS + (uint64_t)(time.tv_nsec); } /** - * Returns the current time in micro seconds according to the remote wall clock. + * Returns the current time in nano seconds according to the remote wall clock. */ uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp) { MUTEX_LOCK(raop_ntp->sync_params_mutex); int64_t offset = raop_ntp->sync_offset; MUTEX_UNLOCK(raop_ntp->sync_params_mutex); - return (uint64_t) ((int64_t) raop_ntp_get_local_time(raop_ntp)) + ((int64_t) offset); + return (uint64_t) (((int64_t) raop_ntp_get_local_time(raop_ntp)) + offset); } /** - * Returns the local wall clock time in micro seconds for the given point in remote clock time + * Returns the local wall clock time in nano seconds for the given point in remote clock time */ uint64_t raop_ntp_convert_remote_time(raop_ntp_t *raop_ntp, uint64_t remote_time) { MUTEX_LOCK(raop_ntp->sync_params_mutex); - uint64_t offset = raop_ntp->sync_offset; + int64_t offset = raop_ntp->sync_offset; MUTEX_UNLOCK(raop_ntp->sync_params_mutex); - return (uint64_t) ((int64_t) remote_time) - ((int64_t) offset); + return (uint64_t) (((int64_t) remote_time) - offset); } /** - * Returns the remote wall clock time in micro seconds for the given point in local clock time + * Returns the remote wall clock time in nano seconds for the given point in local clock time */ uint64_t raop_ntp_convert_local_time(raop_ntp_t *raop_ntp, uint64_t local_time) { MUTEX_LOCK(raop_ntp->sync_params_mutex); - uint64_t offset = raop_ntp->sync_offset; + int64_t offset = raop_ntp->sync_offset; MUTEX_UNLOCK(raop_ntp->sync_params_mutex); - return (uint64_t) ((int64_t) local_time) + ((int64_t) offset); + return (uint64_t) (((int64_t) local_time) + offset); } diff --git a/lib/raop_ntp.h b/lib/raop_ntp.h index 9983ee8..cd95dda 100644 --- a/lib/raop_ntp.h +++ b/lib/raop_ntp.h @@ -31,7 +31,7 @@ unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp); void raop_ntp_destroy(raop_ntp_t *raop_rtp); -uint64_t raop_ntp_timestamp_to_micro_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff); +uint64_t raop_ntp_timestamp_to_nano_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff); uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp); uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp); diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index 7c21526..f16c871 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -32,12 +32,13 @@ #define NO_FLUSH (-42) -#define RAOP_RTP_SAMPLE_RATE (44100.0 / 1000000.0) +#define SECOND_IN_NSECS 1000000000UL +#define RAOP_RTP_SAMPLE_RATE (44100.0 / SECOND_IN_NSECS) #define RAOP_RTP_SYNC_DATA_COUNT 8 -#define SEC 1000000 +#define SEC SECOND_IN_NSECS -#define DELAY_AAC 500000 //empirical, matches audio latency of about -0.5 sec after first clock sync event -#define DELAY_ALAC 2000000 //empirical, matches audio latency of about -2.0 sec after first clock sync event +#define DELAY_AAC 500000000 //empirical, matches audio latency of about -0.5 sec after first clock sync event +#define DELAY_ALAC 200000000 //empirical, matches audio latency of about -2.0 sec after first clock sync event /* note: it is unclear what will happen in the unlikely event that this code is running at the time of the unix-time * epoch event on 2038-01-19 at 3:14:08 UTC ! (but Apple will surely have removed AirPlay "legacy pairing" by then!) */ @@ -550,7 +551,7 @@ raop_rtp_thread_udp(void *arg) have_synced = true; } uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8); - uint64_t sync_ntp_remote = raop_ntp_timestamp_to_micro_seconds(sync_ntp_raw, true); + uint64_t sync_ntp_remote = raop_ntp_timestamp_to_nano_seconds(sync_ntp_raw, true); uint64_t sync_ntp_local = raop_ntp_convert_remote_time(raop_rtp->ntp, sync_ntp_remote); int64_t shift; switch (raop_rtp->ct) { @@ -631,12 +632,12 @@ raop_rtp_thread_udp(void *arg) offset_estimate_initialized = true; switch (raop_rtp->ct) { case 0x02: - delay = DELAY_ALAC; /* DELAY = 2000000 (2.0 sec) is empirical choice for ALAC */ + delay = DELAY_ALAC; /* DELAY = 2000000000 (2.0 sec) is empirical choice for ALAC */ logger_log(raop_rtp->logger, LOGGER_DEBUG, "Audio is ALAC: using initial latency estimate -%8.6f sec", ((double) delay) / SEC); break; case 0x08: - delay = DELAY_AAC; /* DELAY = 500000 (0.5 sec) is empirical choice for AAC-ELD */ + delay = DELAY_AAC; /* DELAY = 500000000 (0.5 sec) is empirical choice for AAC-ELD */ logger_log(raop_rtp->logger, LOGGER_DEBUG, "Audio is AAC: using initial latency estimate -%8.6f sec", ((double) delay ) / SEC); break; @@ -682,7 +683,7 @@ raop_rtp_thread_udp(void *arg) free(payload); uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp->ntp); int64_t latency = ((int64_t) ntp_now) - ((int64_t) audio_data.ntp_time); - logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp audio: now = %8.6f, npt = %8.6f, latency = %8.6f, rtp_time=%u seqnum = %u", + logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp audio: now = %8.6f, ntp = %8.6f, latency = %8.6f, rtp_time=%u seqnum = %u", ((double) ntp_now ) / SEC, ((double) audio_data.ntp_time) / SEC, ((double) latency) / SEC, (uint32_t) rtp64_timestamp, seqnum); } diff --git a/lib/raop_rtp_mirror.c b/lib/raop_rtp_mirror.c index fcac4bc..93ac87f 100644 --- a/lib/raop_rtp_mirror.c +++ b/lib/raop_rtp_mirror.c @@ -48,7 +48,8 @@ #define CAST #endif -#define SEC 1000000 +#define SECOND_IN_NSECS 1000000000UL +#define SEC SECOND_IN_NSECS /* for MacOS, where SOL_TCP and TCP_KEEPIDLE are not defined */ #if !defined(SOL_TCP) && defined(IPPROTO_TCP) @@ -359,9 +360,9 @@ raop_rtp_mirror_thread(void *arg) // Conveniently, the video data is already stamped with the remote wall clock time, // so no additional clock syncing needed. The only thing odd here is that the video // ntp time stamps don't include the SECONDS_FROM_1900_TO_1970, so it's really just - // counting micro seconds since last boot. + // counting nano seconds since last boot. ntp_timestamp_raw = byteutils_get_long(packet, 8); - uint64_t ntp_timestamp_remote = raop_ntp_timestamp_to_micro_seconds(ntp_timestamp_raw, false); + uint64_t ntp_timestamp_remote = raop_ntp_timestamp_to_nano_seconds(ntp_timestamp_raw, false); uint64_t ntp_timestamp = raop_ntp_convert_remote_time(raop_rtp_mirror->ntp, ntp_timestamp_remote); uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp_mirror->ntp); diff --git a/renderers/audio_renderer_gstreamer.c b/renderers/audio_renderer_gstreamer.c index 5a989bf..9df50bb 100644 --- a/renderers/audio_renderer_gstreamer.c +++ b/renderers/audio_renderer_gstreamer.c @@ -21,6 +21,7 @@ #include #include #include "audio_renderer.h" +#define SECOND_IN_NSECS 1000000000UL /* GStreamer Caps strings for Airplay-defined audio compression types (ct) */ @@ -201,11 +202,12 @@ void audio_renderer_start(unsigned char *ct) { void audio_renderer_render_buffer(unsigned char* data, int *data_len, unsigned short *seqnum, uint64_t *ntp_time) { GstBuffer *buffer; bool valid; - GstClockTime pts = (GstClockTime) (*ntp_time * 1000); /* convert from usec to nsec */ + GstClockTime pts = (GstClockTime) *ntp_time ; /* now in nsecs */ if (pts >= gst_audio_pipeline_base_time) { pts -= gst_audio_pipeline_base_time; } else { - logger_log(logger, LOGGER_ERR, "*** invalid *pts_raw < gst_audio_pipeline_base_time"); + logger_log(logger, LOGGER_ERR, "*** invalid ntp_time < gst_audio_pipeline_base_time\n%8.6f ntp_time\n%8.6f base_time", + ((double) *ntp_time) / SECOND_IN_NSECS, ((double) gst_audio_pipeline_base_time) / SECOND_IN_NSECS); return; } if (data_len == 0 || renderer == NULL) return; diff --git a/renderers/video_renderer_gstreamer.c b/renderers/video_renderer_gstreamer.c index b64e484..dd03a90 100644 --- a/renderers/video_renderer_gstreamer.c +++ b/renderers/video_renderer_gstreamer.c @@ -21,6 +21,7 @@ #include #include +#define SECOND_IN_NSECS 1000000000UL #ifdef X_DISPLAY_FIX #include #include "x_display_fix.h" @@ -220,11 +221,12 @@ void video_renderer_start() { void video_renderer_render_buffer(unsigned char* data, int *data_len, int *nal_count, uint64_t *ntp_time) { GstBuffer *buffer; - GstClockTime pts = (GstClockTime) (*ntp_time * 1000); /*convert from usec to nsec */ + GstClockTime pts = (GstClockTime) *ntp_time; /*now in nsecs */ if (pts >= gst_video_pipeline_base_time) { pts -= gst_video_pipeline_base_time; } else { - logger_log(logger, LOGGER_ERR, "*** invalid *pts_raw < gst_video_pipeline_base_time") ; + logger_log(logger, LOGGER_ERR, "*** invalid ntp_time < gst_video_pipeline_base_time\n%8.6f ntp_time\n%8.6f base_time", + ((double) *ntp_time) / SECOND_IN_NSECS, ((double) gst_video_pipeline_base_time) / SECOND_IN_NSECS); return; } g_assert(data_len != 0);