source/Sources/AirPlayReceiverSession.c
static OSStatus	_TimingProcessResponse( AirPlayReceiverSessionRef inSession, RTCPTimeSyncPacket *inPkt, const AirTunesTime *inTime )
{
	AirTunesSource * const	src = &inSession->source;
	OSStatus				err;
	uint64_t				t1;
	uint64_t				t2;
	uint64_t				t3;
	uint64_t				t4;
	double					offset;
	double					rtt;
	unsigned int			i;
	Boolean					useMeasurement;
	Boolean					clockStepped;
	
	inPkt->rtpTimestamp		= ntohl( inPkt->rtpTimestamp );
	inPkt->ntpOriginateHi	= ntohl( inPkt->ntpOriginateHi );
	inPkt->ntpOriginateLo	= ntohl( inPkt->ntpOriginateLo );
	inPkt->ntpReceiveHi		= ntohl( inPkt->ntpReceiveHi );
	inPkt->ntpReceiveLo		= ntohl( inPkt->ntpReceiveLo );
	inPkt->ntpTransmitHi	= ntohl( inPkt->ntpTransmitHi );
	inPkt->ntpTransmitLo	= ntohl( inPkt->ntpTransmitLo );
	
	// Make sure this response is for the last request we made and is not a duplicate response.
	
	if( ( inPkt->ntpOriginateHi != src->rtcpTILastTransmitTimeHi ) || 
		( inPkt->ntpOriginateLo != src->rtcpTILastTransmitTimeLo ) )
	{
		err = kDuplicateErr;
		goto exit;
	}
	src->rtcpTILastTransmitTimeHi = 0; // Zero so we don't try to process a duplicate.
	src->rtcpTILastTransmitTimeLo = 0;
	
	// Calculate the relative offset between clocks.
	//
	// Client:  T1           T4
	// ----------------------------->
	//           \           ^
	//            \         /
	//             v       /
	// ----------------------------->
	// Server:     T2     T3
	// 
	// Clock offset in NTP units     = ((T2 - T1) + (T3 - T4)) / 2.
	// Round-trip delay in NTP units =  (T4 - T1) - (T3 - T2)
	
	t1 = ( ( (uint64_t) inPkt->ntpOriginateHi )	<< 32 ) | inPkt->ntpOriginateLo;
	t2 = ( ( (uint64_t) inPkt->ntpReceiveHi )	<< 32 ) | inPkt->ntpReceiveLo;
	t3 = ( ( (uint64_t) inPkt->ntpTransmitHi )	<< 32 ) | inPkt->ntpTransmitLo;
	t4 = ( ( (uint64_t)( inTime->secs + kNTPvsUnixSeconds ) ) << 32 ) + ( inTime->frac >> 32 );
	
	offset = 0.5 * ( ( ( (double)( (int64_t)( t2 - t1 ) ) ) * kNTPFraction ) + 
					 ( ( (double)( (int64_t)( t3 - t4 ) ) ) * kNTPFraction ) );
	rtt = ( ( (double)( (int64_t)( t4 - t1 ) ) ) * kNTPFraction ) - 
		  ( ( (double)( (int64_t)( t3 - t2 ) ) ) * kNTPFraction );
	
	// Update round trip time stats.
	
	if( rtt < src->rtcpTIClockRTTMin ) src->rtcpTIClockRTTMin = rtt;
	if( rtt > src->rtcpTIClockRTTMax ) src->rtcpTIClockRTTMax = rtt;
	if( src->rtcpTIResponseCount == 0 ) src->rtcpTIClockRTTAvg = rtt;
	src->rtcpTIClockRTTAvg			= ( ( 15.0 * src->rtcpTIClockRTTAvg ) + rtt ) * ( 1.0 / 16.0 );
	
	// Update clock offset stats. If this is first time ever or the first time after a clock step, reset the stats.
	
	if( src->rtcpTIResponseCount == 0 )
	{
		for( i = 0; i < countof( src->rtcpTIClockOffsetArray ); ++i )
		{
			src->rtcpTIClockDelayArray[ i ] = 1000.0;
			src->rtcpTIClockOffsetArray[ i ] = 0.0;
		}
		src->rtcpTIClockIndex = 0;
		src->rctpTIClockUsedIndex = 0;
		src->rtcpTIClockOffsetAvg = 0.0;
		src->rtcpTIClockOffsetMin = offset;
		src->rtcpTIClockOffsetMax = offset;
	}
	
	//Only use measurements with a short RTT (delay)
	//Typical NTP 8 stage shift register as per https://www.eecis.udel.edu/~mills/ntp/html/filter.html

	useMeasurement = true;
	for( i = 0; i < countof( src->rtcpTIClockDelayArray ); ++i )
	{
		if( rtt > src->rtcpTIClockDelayArray[ i ])
		{
			useMeasurement = false;
			break;
		}
	}

	src->rtcpTIClockDelayArray[ src->rtcpTIClockIndex ] = rtt;
	src->rtcpTIClockOffsetArray[ src->rtcpTIClockIndex ] = offset;
	if (useMeasurement) {
		src->rctpTIClockUsedIndex = src->rtcpTIClockIndex;
	}
	src->rtcpTIClockIndex++;
	if( src->rtcpTIClockIndex >= countof( src->rtcpTIClockOffsetArray ) )
	{
		src->rtcpTIClockIndex = 0;
	}
	
	src->rtcpTIClockOffsetAvg = ( ( 15.0 * src->rtcpTIClockOffsetAvg ) + offset ) * ( 1.0 / 16.0 );
	if( offset < src->rtcpTIClockOffsetMin ) src->rtcpTIClockOffsetMin = offset;
	if( offset > src->rtcpTIClockOffsetMax ) src->rtcpTIClockOffsetMax = offset;

	err = kNoErr;
	if (useMeasurement)
	{
		// Sync our local clock to the server's clock. If this is the first sync, always step.
		
		clockStepped = AirTunesClock_Adjust( inSession->airTunesClock, (int64_t)( offset * 1E9 ), src->rtcpTIForceStep );
		if( clockStepped && !src->rtcpTIForceStep ) {
			++src->rtcpTIStepCount;
			err = kRangeErr;
		}
		++src->rtcpTIResponseCount;
	}
	
exit:
	return( err );
}
//===========================================================================================================================
//	_TimingProcessResponse
//
//	Note: This function does not need the AirTunes lock because it only accesses variables from a single thread at a time.
//		  These variables are only accessed once by the RTSP thread during init and then only by the timing thread.
//===========================================================================================================================
_TimingProcessResponse
Note: This function does not need the AirTunes lock because it only accesses variables from a single thread at a time.
These variables are only accessed once by the RTSP thread during init and then only by the timing thread.
_TimingProcessResponse Note : This function does not need the AirTunes lock because it only accesses variables from a single thread at a time . These variables are only accessed once by the RTSP thread during init and then only by the timing thread .
static OSStatus	_TimingProcessResponse( AirPlayReceiverSessionRef inSession, RTCPTimeSyncPacket *inPkt, const AirTunesTime *inTime )
{
	AirTunesSource * const	src = &inSession->source;
	OSStatus				err;
	uint64_t				t1;
	uint64_t				t2;
	uint64_t				t3;
	uint64_t				t4;
	double					offset;
	double					rtt;
	unsigned int			i;
	Boolean					useMeasurement;
	Boolean					clockStepped;
	inPkt->rtpTimestamp		= ntohl( inPkt->rtpTimestamp );
	inPkt->ntpOriginateHi	= ntohl( inPkt->ntpOriginateHi );
	inPkt->ntpOriginateLo	= ntohl( inPkt->ntpOriginateLo );
	inPkt->ntpReceiveHi		= ntohl( inPkt->ntpReceiveHi );
	inPkt->ntpReceiveLo		= ntohl( inPkt->ntpReceiveLo );
	inPkt->ntpTransmitHi	= ntohl( inPkt->ntpTransmitHi );
	inPkt->ntpTransmitLo	= ntohl( inPkt->ntpTransmitLo );
	if( ( inPkt->ntpOriginateHi != src->rtcpTILastTransmitTimeHi ) || 
		( inPkt->ntpOriginateLo != src->rtcpTILastTransmitTimeLo ) )
	{
		err = kDuplicateErr;
		goto exit;
	}
	src->rtcpTILastTransmitTimeHi = 0; 
	src->rtcpTILastTransmitTimeLo = 0;
	 Client:  T1           T4
	 ----------------------------->
	           \           ^
	            \         /
	             v       /
	 ----------------------------->
	 Server:     T2     T3
	 Clock offset in NTP units     = ((T2 - T1) + (T3 - T4)) / 2.
	 Round-trip delay in NTP units =  (T4 - T1) - (T3 - T2)
	t1 = ( ( (uint64_t) inPkt->ntpOriginateHi )	<< 32 ) | inPkt->ntpOriginateLo;
	t2 = ( ( (uint64_t) inPkt->ntpReceiveHi )	<< 32 ) | inPkt->ntpReceiveLo;
	t3 = ( ( (uint64_t) inPkt->ntpTransmitHi )	<< 32 ) | inPkt->ntpTransmitLo;
	t4 = ( ( (uint64_t)( inTime->secs + kNTPvsUnixSeconds ) ) << 32 ) + ( inTime->frac >> 32 );
	offset = 0.5 * ( ( ( (double)( (int64_t)( t2 - t1 ) ) ) * kNTPFraction ) + 
					 ( ( (double)( (int64_t)( t3 - t4 ) ) ) * kNTPFraction ) );
	rtt = ( ( (double)( (int64_t)( t4 - t1 ) ) ) * kNTPFraction ) - 
		  ( ( (double)( (int64_t)( t3 - t2 ) ) ) * kNTPFraction );
	 Update round trip time stats.
	if( rtt < src->rtcpTIClockRTTMin ) src->rtcpTIClockRTTMin = rtt;
	if( rtt > src->rtcpTIClockRTTMax ) src->rtcpTIClockRTTMax = rtt;
	if( src->rtcpTIResponseCount == 0 ) src->rtcpTIClockRTTAvg = rtt;
	src->rtcpTIClockRTTAvg			= ( ( 15.0 * src->rtcpTIClockRTTAvg ) + rtt ) * ( 1.0 / 16.0 );
	 Update clock offset stats. If this is first time ever or the first time after a clock step, reset the stats.
	if( src->rtcpTIResponseCount == 0 )
	{
		for( i = 0; i < countof( src->rtcpTIClockOffsetArray ); ++i )
		{
			src->rtcpTIClockDelayArray[ i ] = 1000.0;
			src->rtcpTIClockOffsetArray[ i ] = 0.0;
		}
		src->rtcpTIClockIndex = 0;
		src->rctpTIClockUsedIndex = 0;
		src->rtcpTIClockOffsetAvg = 0.0;
		src->rtcpTIClockOffsetMin = offset;
		src->rtcpTIClockOffsetMax = offset;
	}
	Only use measurements with a short RTT (delay)
	Typical NTP 8 stage shift register as per https:www.eecis.udel.edu/~mills/ntp/html/filter.html
	useMeasurement = true;
	for( i = 0; i < countof( src->rtcpTIClockDelayArray ); ++i )
	{
		if( rtt > src->rtcpTIClockDelayArray[ i ])
		{
			useMeasurement = false;
			break;
		}
	}
	src->rtcpTIClockDelayArray[ src->rtcpTIClockIndex ] = rtt;
	src->rtcpTIClockOffsetArray[ src->rtcpTIClockIndex ] = offset;
	if (useMeasurement) {
		src->rctpTIClockUsedIndex = src->rtcpTIClockIndex;
	}
	src->rtcpTIClockIndex++;
	if( src->rtcpTIClockIndex >= countof( src->rtcpTIClockOffsetArray ) )
	{
		src->rtcpTIClockIndex = 0;
	}
	src->rtcpTIClockOffsetAvg = ( ( 15.0 * src->rtcpTIClockOffsetAvg ) + offset ) * ( 1.0 / 16.0 );
	if( offset < src->rtcpTIClockOffsetMin ) src->rtcpTIClockOffsetMin = offset;
	if( offset > src->rtcpTIClockOffsetMax ) src->rtcpTIClockOffsetMax = offset;
	err = kNoErr;
	if (useMeasurement)
	{
		 Sync our local clock to the server's clock. If this is the first sync, always step.
		clockStepped = AirTunesClock_Adjust( inSession->airTunesClock, (int64_t)( offset * 1E9 ), src->rtcpTIForceStep );
		if( clockStepped && !src->rtcpTIForceStep ) {
			++src->rtcpTIStepCount;
			err = kRangeErr;
		}
		++src->rtcpTIResponseCount;
	}
exit:
	return( err );
}
static OSStatus _TimingProcessResponse ( AirPlayReceiverSessionRef inSession , RTCPTimeSyncPacket * inPkt , const AirTunesTime * inTime ) { AirTunesSource * const src = & inSession -> source ; OSStatus err ; uint64_t t1 ; uint64_t t2 ; uint64_t t3 ; uint64_t t4 ; double offset ; double rtt ; unsigned int i ; Boolean useMeasurement ; Boolean clockStepped ; inPkt -> rtpTimestamp = ntohl ( inPkt -> rtpTimestamp ) ; inPkt -> ntpOriginateHi = ntohl ( inPkt -> ntpOriginateHi ) ; inPkt -> ntpOriginateLo = ntohl ( inPkt -> ntpOriginateLo ) ; inPkt -> ntpReceiveHi = ntohl ( inPkt -> ntpReceiveHi ) ; inPkt -> ntpReceiveLo = ntohl ( inPkt -> ntpReceiveLo ) ; inPkt -> ntpTransmitHi = ntohl ( inPkt -> ntpTransmitHi ) ; inPkt -> ntpTransmitLo = ntohl ( inPkt -> ntpTransmitLo ) ; if ( ( inPkt -> ntpOriginateHi != src -> rtcpTILastTransmitTimeHi ) || ( inPkt -> ntpOriginateLo != src -> rtcpTILastTransmitTimeLo ) ) { err = kDuplicateErr ; goto exit ; } src -> rtcpTILastTransmitTimeHi = 0 ; src -> rtcpTILastTransmitTimeLo = 0 ; t1 = ( ( ( uint64_t ) inPkt -> ntpOriginateHi ) << 32 ) | inPkt -> ntpOriginateLo ; t2 = ( ( ( uint64_t ) inPkt -> ntpReceiveHi ) << 32 ) | inPkt -> ntpReceiveLo ; t3 = ( ( ( uint64_t ) inPkt -> ntpTransmitHi ) << 32 ) | inPkt -> ntpTransmitLo ; t4 = ( ( ( uint64_t ) ( inTime -> secs + kNTPvsUnixSeconds ) ) << 32 ) + ( inTime -> frac >> 32 ) ; offset = 0.5 * ( ( ( ( double ) ( ( int64_t ) ( t2 - t1 ) ) ) * kNTPFraction ) + ( ( ( double ) ( ( int64_t ) ( t3 - t4 ) ) ) * kNTPFraction ) ) ; rtt = ( ( ( double ) ( ( int64_t ) ( t4 - t1 ) ) ) * kNTPFraction ) - ( ( ( double ) ( ( int64_t ) ( t3 - t2 ) ) ) * kNTPFraction ) ; if ( rtt < src -> rtcpTIClockRTTMin ) src -> rtcpTIClockRTTMin = rtt ; if ( rtt > src -> rtcpTIClockRTTMax ) src -> rtcpTIClockRTTMax = rtt ; if ( src -> rtcpTIResponseCount == 0 ) src -> rtcpTIClockRTTAvg = rtt ; src -> rtcpTIClockRTTAvg = ( ( 15.0 * src -> rtcpTIClockRTTAvg ) + rtt ) * ( 1.0 / 16.0 ) ; if ( src -> rtcpTIResponseCount == 0 ) { for ( i = 0 ; i < countof ( src -> rtcpTIClockOffsetArray ) ; ++ i ) { src -> rtcpTIClockDelayArray [ i ] = 1000.0 ; src -> rtcpTIClockOffsetArray [ i ] = 0.0 ; } src -> rtcpTIClockIndex = 0 ; src -> rctpTIClockUsedIndex = 0 ; src -> rtcpTIClockOffsetAvg = 0.0 ; src -> rtcpTIClockOffsetMin = offset ; src -> rtcpTIClockOffsetMax = offset ; } useMeasurement = true ; for ( i = 0 ; i < countof ( src -> rtcpTIClockDelayArray ) ; ++ i ) { if ( rtt > src -> rtcpTIClockDelayArray [ i ] ) { useMeasurement = false ; break ; } } src -> rtcpTIClockDelayArray [ src -> rtcpTIClockIndex ] = rtt ; src -> rtcpTIClockOffsetArray [ src -> rtcpTIClockIndex ] = offset ; if ( useMeasurement ) { src -> rctpTIClockUsedIndex = src -> rtcpTIClockIndex ; } src -> rtcpTIClockIndex ++ ; if ( src -> rtcpTIClockIndex >= countof ( src -> rtcpTIClockOffsetArray ) ) { src -> rtcpTIClockIndex = 0 ; } src -> rtcpTIClockOffsetAvg = ( ( 15.0 * src -> rtcpTIClockOffsetAvg ) + offset ) * ( 1.0 / 16.0 ) ; if ( offset < src -> rtcpTIClockOffsetMin ) src -> rtcpTIClockOffsetMin = offset ; if ( offset > src -> rtcpTIClockOffsetMax ) src -> rtcpTIClockOffsetMax = offset ; err = kNoErr ; if ( useMeasurement ) { clockStepped = AirTunesClock_Adjust ( inSession -> airTunesClock , ( int64_t ) ( offset * 1E9 ) , src -> rtcpTIForceStep ) ; if ( clockStepped && ! src -> rtcpTIForceStep ) { ++ src -> rtcpTIStepCount ; err = kRangeErr ; } ++ src -> rtcpTIResponseCount ; } exit : return ( err ) ; }
_TimingProcessResponse
Note: This function does not need the AirTunes lock because it only accesses variables from a single thread at a time.
_TimingProcessResponse Note : This function does not need the AirTunes lock because it only accesses variables from a single thread at a time .
// Make sure this response is for the last request we made and is not a duplicate response. // Zero so we don't try to process a duplicate. // Calculate the relative offset between clocks. // // Client:  T1           T4 // -----------------------------> //           \           ^ //            \         / //             v       / // -----------------------------> // Server:     T2     T3 //  // Clock offset in NTP units     = ((T2 - T1) + (T3 - T4)) / 2. // Round-trip delay in NTP units =  (T4 - T1) - (T3 - T2) // Update round trip time stats. // Update clock offset stats. If this is first time ever or the first time after a clock step, reset the stats. //Only use measurements with a short RTT (delay) //Typical NTP 8 stage shift register as per https://www.eecis.udel.edu/~mills/ntp/html/filter.html // Sync our local clock to the server's clock. If this is the first sync, always step.