source/Sources/AirPlayReceiverSession.c
static OSStatus
	_GeneralAudioProcessPacket( 
		AirPlayReceiverSessionRef	inSession, 
		AirTunesBufferNode *		inNode, 
		size_t 						inSize, 
		Boolean						inIsRetransmit )
{
	OSStatus					err;
	RTPPacket *					rtp;
	uint16_t					pktSeq;
	uint32_t					pktTS;
	AirTunesBufferNode *		curr;
	AirTunesBufferNode *		stop;
	
	// Validate and parse the packet.
	
	if( inSize < kRTPHeaderSize ) { dlogassert( "Packet too small: %zu bytes", inSize ); err = kSizeErr; goto exit; }
	rtp					= (RTPPacket *) inNode->data;
	rtp->header.seq		= pktSeq = ntohs( rtp->header.seq );
	rtp->header.ts		= pktTS  = ntohl( rtp->header.ts );
	rtp->header.ssrc	= ntohl( rtp->header.ssrc );
	inNode->rtp			= rtp;
	inNode->ptr			= inNode->data + kRTPHeaderSize;
	inNode->size		= inSize - kRTPHeaderSize;
	inNode->ts			= pktTS;
	
	if( _GeneralAudioTrackDups( inSession, pktSeq ) )	{ err = kDuplicateErr; goto exit; }
	if( !inIsRetransmit )								_GeneralAudioTrackLosses( inSession, inNode );
	if( !inSession->source.rtcpRTDisable )				_RetransmitsUpdate( inSession, inNode, inIsRetransmit );
	if( !inIsRetransmit )								increment_wrap( inSession->source.receiveCount, 1 );
	
	// If we're flushing then drop packets with timestamps before the end of the flush. Because of retranmissions
	// and out-of-order packets, we may receive pre-flush packets *after* post-flush packets. We want to drop all
	// pre-flush packets, but keep post-flush packets so we keep flush mode active until we hit the flush timeout.
	
	if( inSession->flushing )
	{
		if( Mod32_LT( pktTS, inSession->flushUntilTS ) )
		{
			err = kSkipErr;
			goto exit;
		}
		if( Mod32_GE( pktTS, inSession->flushTimeoutTS ) )
		{
			inSession->flushing = false;
			atr_ulog( kLogLevelInfo, "Flush complete ts=%u until=%u\n", pktTS, inSession->flushUntilTS );
		}
	}
	
	// Insert the new node. Start at the end since it'll usually be at or near the end. Drop if time slot already taken.
	
	stop = inSession->busyListSentinel;
	for( curr = stop->prev; ( curr != stop ) && Mod32_GT( curr->rtp->header.ts, pktTS ); curr = curr->prev ) {}
	if( ( curr != stop ) && ( curr->rtp->header.ts == pktTS ) )
	{
		dlogassert( "Duplicate timestamp not caught earlier? seq %u ts %u", pktSeq, pktTS );
		err = kDuplicateErr;
		goto exit;
	}
	inNode->prev		= curr;
	inNode->next		= curr->next;
	inNode->next->prev	= inNode;
	inNode->prev->next	= inNode;
	++inSession->busyNodeCount;
	err = kNoErr;
	
exit:
	return( err );
}
//===========================================================================================================================
//	_GeneralAudioProcessPacket
//
//	Warning: Assumes the AirTunes lock is held.
//
//	Warning: this function MUST either queue the node onto the busy queue and return kNoErr or it MUST return an error 
//	and NOT queue the packet. Doing anything else will lead to leaks and/or crashes.
//
//	Note: The "buf" field of "inNode" is expected to be filled in, but all other fields are filled in by this function.
//===========================================================================================================================
_GeneralAudioProcessPacket
Warning: Assumes the AirTunes lock is held.

this function MUST either queue the node onto the busy queue and return kNoErr or it MUST return an error
and NOT queue the packet. Doing anything else will lead to leaks and/or crashes.

The "buf" field of "inNode" is expected to be filled in, but all other fields are filled in by this function.
_GeneralAudioProcessPacket Warning : Assumes the AirTunes lock is held . this function MUST either queue the node onto the busy queue and return kNoErr or it MUST return an error and NOT queue the packet . Doing anything else will lead to leaks and / or crashes . The " buf " field of " inNode " is expected to be filled in but all other fields are filled in by this function .
static OSStatus
	_GeneralAudioProcessPacket( 
		AirPlayReceiverSessionRef	inSession, 
		AirTunesBufferNode *		inNode, 
		size_t 						inSize, 
		Boolean						inIsRetransmit )
{
	OSStatus					err;
	RTPPacket *					rtp;
	uint16_t					pktSeq;
	uint32_t					pktTS;
	AirTunesBufferNode *		curr;
	AirTunesBufferNode *		stop;
	if( inSize < kRTPHeaderSize ) { dlogassert( "Packet too small: %zu bytes", inSize ); err = kSizeErr; goto exit; }
	rtp					= (RTPPacket *) inNode->data;
	rtp->header.seq		= pktSeq = ntohs( rtp->header.seq );
	rtp->header.ts		= pktTS  = ntohl( rtp->header.ts );
	rtp->header.ssrc	= ntohl( rtp->header.ssrc );
	inNode->rtp			= rtp;
	inNode->ptr			= inNode->data + kRTPHeaderSize;
	inNode->size		= inSize - kRTPHeaderSize;
	inNode->ts			= pktTS;
	if( _GeneralAudioTrackDups( inSession, pktSeq ) )	{ err = kDuplicateErr; goto exit; }
	if( !inIsRetransmit )								_GeneralAudioTrackLosses( inSession, inNode );
	if( !inSession->source.rtcpRTDisable )				_RetransmitsUpdate( inSession, inNode, inIsRetransmit );
	if( !inIsRetransmit )								increment_wrap( inSession->source.receiveCount, 1 );
	if( inSession->flushing )
	{
		if( Mod32_LT( pktTS, inSession->flushUntilTS ) )
		{
			err = kSkipErr;
			goto exit;
		}
		if( Mod32_GE( pktTS, inSession->flushTimeoutTS ) )
		{
			inSession->flushing = false;
			atr_ulog( kLogLevelInfo, "Flush complete ts=%u until=%u\n", pktTS, inSession->flushUntilTS );
		}
	}
	stop = inSession->busyListSentinel;
	for( curr = stop->prev; ( curr != stop ) && Mod32_GT( curr->rtp->header.ts, pktTS ); curr = curr->prev ) {}
	if( ( curr != stop ) && ( curr->rtp->header.ts == pktTS ) )
	{
		dlogassert( "Duplicate timestamp not caught earlier? seq %u ts %u", pktSeq, pktTS );
		err = kDuplicateErr;
		goto exit;
	}
	inNode->prev		= curr;
	inNode->next		= curr->next;
	inNode->next->prev	= inNode;
	inNode->prev->next	= inNode;
	++inSession->busyNodeCount;
	err = kNoErr;
exit:
	return( err );
}
static OSStatus _GeneralAudioProcessPacket ( AirPlayReceiverSessionRef inSession , AirTunesBufferNode * inNode , size_t inSize , Boolean inIsRetransmit ) { OSStatus err ; RTPPacket * rtp ; uint16_t pktSeq ; uint32_t pktTS ; AirTunesBufferNode * curr ; AirTunesBufferNode * stop ; if ( inSize < kRTPHeaderSize ) { dlogassert ( " " , inSize ) ; err = kSizeErr ; goto exit ; } rtp = ( RTPPacket * ) inNode -> data ; rtp -> header . seq = pktSeq = ntohs ( rtp -> header . seq ) ; rtp -> header . ts = pktTS = ntohl ( rtp -> header . ts ) ; rtp -> header . ssrc = ntohl ( rtp -> header . ssrc ) ; inNode -> rtp = rtp ; inNode -> ptr = inNode -> data + kRTPHeaderSize ; inNode -> size = inSize - kRTPHeaderSize ; inNode -> ts = pktTS ; if ( _GeneralAudioTrackDups ( inSession , pktSeq ) ) { err = kDuplicateErr ; goto exit ; } if ( ! inIsRetransmit ) _GeneralAudioTrackLosses ( inSession , inNode ) ; if ( ! inSession -> source . rtcpRTDisable ) _RetransmitsUpdate ( inSession , inNode , inIsRetransmit ) ; if ( ! inIsRetransmit ) increment_wrap ( inSession -> source . receiveCount , 1 ) ; if ( inSession -> flushing ) { if ( Mod32_LT ( pktTS , inSession -> flushUntilTS ) ) { err = kSkipErr ; goto exit ; } if ( Mod32_GE ( pktTS , inSession -> flushTimeoutTS ) ) { inSession -> flushing = false ; atr_ulog ( kLogLevelInfo , " \n " , pktTS , inSession -> flushUntilTS ) ; } } stop = inSession -> busyListSentinel ; for ( curr = stop -> prev ; ( curr != stop ) && Mod32_GT ( curr -> rtp -> header . ts , pktTS ) ; curr = curr -> prev ) { } if ( ( curr != stop ) && ( curr -> rtp -> header . ts == pktTS ) ) { dlogassert ( " " , pktSeq , pktTS ) ; err = kDuplicateErr ; goto exit ; } inNode -> prev = curr ; inNode -> next = curr -> next ; inNode -> next -> prev = inNode ; inNode -> prev -> next = inNode ; ++ inSession -> busyNodeCount ; err = kNoErr ; exit : return ( err ) ; }
_GeneralAudioProcessPacket
Warning: Assumes the AirTunes lock is held.
_GeneralAudioProcessPacket Warning : Assumes the AirTunes lock is held .
// Validate and parse the packet. // If we're flushing then drop packets with timestamps before the end of the flush. Because of retranmissions // and out-of-order packets, we may receive pre-flush packets *after* post-flush packets. We want to drop all // pre-flush packets, but keep post-flush packets so we keep flush mode active until we hit the flush timeout. // Insert the new node. Start at the end since it'll usually be at or near the end. Drop if time slot already taken.