/*
	File:		RTPRssmIMAAudio.c

	Contains:	Definition of IMA Audio RTPReassembler

	Copyright:	© 1997-1998 by Apple Computer, Inc., all rights reserved.

	
	
	OVERVIEW
	
	QuickTime Streaming software uses an RTPReassembler to extract sample data
	from incoming RTP packets.  A base RTPReassembler defined by QuickTime
	Streaming does much of the work of reassembling.  A derived RTPReassembler
	extends the base to interpret a specific RTP payload format, to select an
	appropriate StreamHandler to process reassembled sample data, and to
	handle network packet loss.
	
	This reassembler extends or fully implements the following interface and
	delegates all other calls to its base.
		
		
		STANDARD COMPONENT INTERFACE
		----------------------------
		
		CallComponentOpen()				Allocate and initialize storage for
										instance variables.
		
		CallComponentClose()			Reverse the effects of
										CallComponentOpen().
		
		CallComponentVersion()			Return the instance's version.
		
		CallComponentTarget()			Update the instance's inheritance graph.
		
		
		
		RTP REASSEMBLER INTERFACE
		------------------------------
		
		RTPRssmInitialize()				Prepare to reassemble sample data.
		
		RTPRssmComputeChunkSize()		Determine the size of buffer needed for
										the data in a list of network packets.
		
		RTPRssmAdjustPacketParams()		Adjust fields of an RTPRssmPacket to
										reflect the properties of its payload.
		
		RTPRssmCopyDataToChunk()		Copy sample data from a packet list to
										a data buffer.
		
		RTPRssmSendPacketList()			Prepare to process or discard a list of
										packets.
		
		RTPRssmHasCharacteristic()		Indicate whether the reassembler has the
										specified characteristic.
		
		RTPRssmReset()					End processing of the current stream of
										network packets.
*/



/* ---------------------------------------------------------------------------
 *		H E A D E R S
 * ---------------------------------------------------------------------------
 */

#include "RTPRssmIMAAudio.h"
#include "RTPRssmIMAAudioResources.h"
#include <FixMath.h>
#include <string.h>



/* ---------------------------------------------------------------------------
 *		R T P    R E A S S E M B L E R    P R O T O T Y P E S
 * ---------------------------------------------------------------------------
 *
 *	QTStreamingComponents.k.h uses these macros to declare prototypes for
 *	the RTPReassembler calls defined in this file.
 *
 */

#define RTPRSSM_BASENAME()				RTPRssmIMAAudio_
#define RTPRSSM_GLOBALS()				RTPRssmIMAAudioInstanceData **

#include <QTStreamingComponents.k.h>



/* ---------------------------------------------------------------------------
 *		C O M P O N E N T    D I S P A T C H    H E L P E R
 * ---------------------------------------------------------------------------
 *
 *	ComponentDispatchHelper.c uses these macros to define a dispatcher and to
 *	declare prototypes for the core component calls defined in this file.  For
 *	Mac OS, it defines the routine descriptor that serves as the component
 *	entry point.  The name of the routine descriptor is the macro expansion of
 *		
 *		CALLCOMPONENT_BASENAME()##ComponentDispatchRD
 *	
 *	The name of the dispatcher is the macro expansion of
 *	
 *		CALLCOMPONENT_BASENAME()##ComponentDispatch
 *
 */

#define CALLCOMPONENT_BASENAME()		RTPRSSM_BASENAME()
#define CALLCOMPONENT_GLOBALS()			RTPRSSM_GLOBALS() storage
#define COMPONENT_DISPATCH_FILE			"RTPRssmIMAAudioDispatch.h"
#define COMPONENT_C_DISPATCHER			1
#define COMPONENT_UPP_SELECT_ROOT()		RTPRssm
#define GET_DELEGATE_COMPONENT()		( ( **storage ).itsBase )

#include <ComponentDispatchHelper.c>



#pragma mark *        INTERNAL IMPLEMENTATION
#pragma mark -
/* ---------------------------------------------------------------------------
 *		I N T E R N A L    I M P L E M E N T A T I O N
 * ---------------------------------------------------------------------------
 */



/* ---------------------------------------------------------------------------
 *		__GetNewSampleDescription()
 * ---------------------------------------------------------------------------
 *
 *	Create a new SampleDescription for this reassembler's payload using the
 *	given payload attributes.
 *
 */

static
ComponentResult
__GetNewSampleDescription(
	const IMAAudioPayload *		inPayloadAttributes,
	SampleDescriptionHandle *	outDescription )
{
	ComponentResult			theResult = noErr;
	SoundDescriptionHandle	theDescription;
	
	
	theDescription =
		REINTERPRET_CAST( SoundDescriptionHandle )(
			NewHandleClear( sizeof( **theDescription ) ) );
	
	if( theDescription )
	{
		*outDescription = REINTERPRET_CAST( SampleDescriptionHandle )( theDescription );
		
		( **theDescription ).descSize = sizeof( **theDescription );
		( **theDescription ).dataFormat = kIMAAudioDataFormat;
		( **theDescription ).resvd1 = 0;
		( **theDescription ).resvd2 = 0;
		( **theDescription ).dataRefIndex = 0;
		( **theDescription ).version = 0;
		( **theDescription ).revlevel = 0;
		( **theDescription ).vendor = kComponentManufactureType;
		
		( **theDescription ).numChannels =
			IMAAudioPayloadChannelCount( inPayloadAttributes );
		
		( **theDescription ).sampleSize = 16;
		( **theDescription ).compressionID = 0;
		( **theDescription ).packetSize = 0;
		( **theDescription ).sampleRate =
			IMAAudioPayloadSampleRate( inPayloadAttributes );;
	}
	
	else
	{
		*outDescription = 0;
		
		theResult = MemError();
		
		if( theResult == noErr )
			theResult = memFullErr;
	}
	
	return( theResult );
}



/* ---------------------------------------------------------------------------
 *		__GetPayload()
 * ---------------------------------------------------------------------------
 *
 *	Return a pointer to the start of a packet's payload.
 *
 */

static
IMAAudioPayload *
__GetPayload(
	RTPRssmPacket *		inPacket )
{
	return( 
		REINTERPRET_CAST( IMAAudioPayload * )(
			&inPacket->streamBuffer->rptr[ inPacket->transportHeaderLength ] ) );
}



/* ---------------------------------------------------------------------------
 *		__FillMissingData()
 * ---------------------------------------------------------------------------
 *
 *	Synthesize data for missing audio frames.  This implementation zeros out
 *	missing frames to synthesize silence.
 */

static
void
__FillMissingData(
	RTPRssmPacket *		inPackets,
	UInt16				inGroupStartSequenceNumber,
	UInt32				inInterleaveCount,
	SHChunkRecord *		inChunk )
{
	IMAAudioFrame *		theFrames;
	UInt32				theFrameCount;
	UInt16				theExpectedSequenceNumber;
	RTPRssmPacket *		thePacket;
	UInt16				theMissingPacketCount;
	UInt32				theIndex;
	
	
	/*
		For now, this function is just for looks, as it doesn't actually
		produce the desired audible effect.  The problem is that the
		decompressor doesn't use all data in each frame, so it doesn't
		necessarily decode zeroed frames as silence, and inserting zeroed
		frames into the data stream usually distorts the decoding of some
		subsequent frames.
		
		IMPORTANT	The sequence number computations in this function work
					because the packets are in order and the separation
					between the packets is guaranteed to be within the
					precision of the RTP sequence number field (i.e., less
					than 16 bits, or 65536 packets).  When computing with
					sequence numbers of packets that might be separated by
					more than 65535 packets, a reassembler must extend the
					precision of the sequence numbers, for instance by
					using the inNumWraparounds parameter of
					RTPRssmHandleNewPacket() or by using the timeStamp
					field of RTPRssmPacket structures.
	*/
	
	theFrames =
		REINTERPRET_CAST( IMAAudioFrame * )( CONST_CAST( UInt8 * )( inChunk->dataPtr ) );
	
	theFrameCount = inChunk->dataSize / sizeof( IMAAudioFrame );
	
	theExpectedSequenceNumber = inGroupStartSequenceNumber;
	
	for( thePacket = inPackets; thePacket; thePacket = thePacket->next )
	{
		theMissingPacketCount = thePacket->sequenceNum - theExpectedSequenceNumber;
		
		if( theMissingPacketCount )
		{
			for(
				theIndex = theExpectedSequenceNumber - inGroupStartSequenceNumber;
				theIndex < theFrameCount; theIndex += inInterleaveCount )
			{
				memset(
					&theFrames[ theIndex ], 0,
					theMissingPacketCount * sizeof( IMAAudioFrame ) );
			}
		}
		
		theExpectedSequenceNumber = thePacket->sequenceNum + 1;
	}
	
	theMissingPacketCount =
		inGroupStartSequenceNumber + inInterleaveCount - theExpectedSequenceNumber;
	
	if( theMissingPacketCount )
	{
		for(
			theIndex = theExpectedSequenceNumber - inGroupStartSequenceNumber;
			theIndex < theFrameCount; theIndex += inInterleaveCount )
		{
			memset(
				&theFrames[ theIndex ], 0,
				theMissingPacketCount * sizeof( IMAAudioFrame ) );
		}
	}
}



/* ---------------------------------------------------------------------------
 *		__InterleaveGroupDataSize()
 * ---------------------------------------------------------------------------
 *
 *	Compute the number of octets of data in the interleave group to which the
 *	given packet belongs.
 *
 */

static
UInt32
__InterleaveGroupDataSize(
	RTPRssmPacket *		inPacket )
{
	UInt32						theResult;
	const IMAAudioPayload *		thePayload;
	UInt32						theInterleaveCount;
	UInt32						theInterleaveIndex;
	UInt32						theInterleaveTail;
	
	
	thePayload = __GetPayload( inPacket );
	
	theInterleaveCount = IMAAudioPayloadInterleaveCount( thePayload );
	theInterleaveIndex = IMAAudioPayloadInterleaveIndex( thePayload );
	theInterleaveTail = IMAAudioPayloadInterleaveTail( thePayload );
	
	theResult = theInterleaveCount * inPacket->dataLength;
	
	if( theInterleaveIndex <= theInterleaveTail )
	{
		theResult -=
			sizeof( IMAAudioFrame ) * ( theInterleaveCount - theInterleaveTail - 1 );
	}
	
	else
	{
		theResult += sizeof( IMAAudioFrame ) * ( theInterleaveTail + 1 );
	}
	
	return( theResult );
}



#pragma mark -
#pragma mark *        STANDARD COMPONENT INTERFACE
#pragma mark -
/* ---------------------------------------------------------------------------
 *		S T A N D A R D    C O M P O N E N T    I N T E R F A C E
 * ---------------------------------------------------------------------------
 */



/* ---------------------------------------------------------------------------
 *		+ CallComponentOpen() implementation
 * ---------------------------------------------------------------------------
 *
 *	Allocate and initialize storage for instance variables.  When a
 *	reassembler is opened, it is not always called to reassemble data, so this
 *	function doesn't perform any allocations or time-consuming operations that
 *	are needed only to reassemble sample data.  The RTPRssmInitialize()
 *	implementation performs such operations.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_Open(
	RTPRssmIMAAudioInstanceData **	inGlobals,
	ComponentInstance				self )
{
	ComponentResult		theResult = noErr;
	RTPReassembler		theBase;
	
	
	inGlobals =
		REINTERPRET_CAST( RTPRssmIMAAudioInstanceData ** )(
			NewHandleClear( sizeof( **inGlobals ) ) );
	
	if( inGlobals )
	{
		( **inGlobals ).itself = self;
		( **inGlobals ).itsFinalDerivation = self;
		
		SetComponentInstanceStorage( self, REINTERPRET_CAST( Handle )( inGlobals ) );
		
		theResult =
			OpenADefaultComponent(
				kRTPReassemblerType, kRTPBaseReassemblerType, &theBase );
		
		if( theResult == noErr )
		{
			( **inGlobals ).itsBase = theBase;
			theResult = CallComponentTarget( ( **inGlobals ).itsBase, self );
		}
	}
	
	else
	{
		theResult = MemError();
		
		if( theResult == noErr )
			theResult = memFullErr;
	}
	
	return( theResult );
}



/* ---------------------------------------------------------------------------
 *		+ CallComponentClose() implementation
 * ---------------------------------------------------------------------------
 *
 *	Reverse the effects of the CallComponentOpen() implementation.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_Close(
	RTPRssmIMAAudioInstanceData **	inGlobals,
	ComponentInstance				self )
{
#pragma unused( self )
	
	if( inGlobals )
	{
		if( ( **inGlobals ).itsBase )
			CloseComponent( ( **inGlobals ).itsBase );
		
		DisposeHandle( REINTERPRET_CAST( Handle )( inGlobals ) );
	}
	
	return( noErr );
}



/* ---------------------------------------------------------------------------
 *		+ CallComponentVersion() implementation
 * ---------------------------------------------------------------------------
 *
 *	Return the instance's version.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_Version(
	RTPRssmIMAAudioInstanceData **	inGlobals )
{
#pragma unused( inGlobals )
	
	return( kComponentVersion );
}



/* ---------------------------------------------------------------------------
 *		+ CallComponentTarget() implementation
 * ---------------------------------------------------------------------------
 *
 *	Update the instance's inheritance graph with a new most-derived instance.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_Target(
	RTPRssmIMAAudioInstanceData **	inGlobals,
	ComponentInstance				target )
{
	ComponentResult		theResult;
	
	
	if( ( **inGlobals ).itsBase )
		theResult = CallComponentTarget( ( **inGlobals ).itsBase, target );
	else
		theResult = noErr;
	
	if( theResult == noErr )
		( **inGlobals ).itsFinalDerivation = target;
	
	return( theResult );
}



#pragma mark -
#pragma mark *        RTP REASSEMBLER INTERFACE
#pragma mark -
/* ---------------------------------------------------------------------------
 *		R T P    R E A S S E M B L E R    I N T E R F A C E
 * ---------------------------------------------------------------------------
 */



/* ---------------------------------------------------------------------------
 *		+ RTPRssmInitialize() implementation
 * ---------------------------------------------------------------------------
 *
 *	Prepare to reassemble sample data.  This implementation initializes
 *	instance variables that represent the payload state, opens a new
 *	StreamHandler to process reassembled sample data, and sets options that
 *	determine how its base reassembler handles network packets.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_Initialize(
	RTPRssmIMAAudioInstanceData **	inGlobals,
	RTPRssmInitParams *				inInitParams )
{
	ComponentResult				theResult = noErr;
	IMAAudioPayload				thePayloadAttributes;
	SampleDescriptionHandle		theDescription;
	
	
	if( CallComponentCanDo( ( **inGlobals ).itsBase, kRTPRssmInitializeSelect ) )
		theResult = RTPRssmInitialize( ( **inGlobals ).itsBase, inInitParams );
	
	if( theResult == noErr )
	{
		IMAAudioPayloadInitialize( &thePayloadAttributes );
		
		theResult =
			__GetNewSampleDescription( &thePayloadAttributes, &theDescription );
		
		( **inGlobals ).itsPayloadAttributes = thePayloadAttributes;
		
		if( theResult == noErr )
		{
			theResult =
				RTPRssmNewStreamHandler(
					( **inGlobals ).itsFinalDerivation, SoundMediaType,
					theDescription, kIMAAudioPayloadRTPTimeScale, NULL );
			
			DisposeHandle( REINTERPRET_CAST( Handle )( theDescription ) );
			
			if( theResult == noErr )
			{
				theResult =
					RTPRssmSetCapabilities(
						( **inGlobals ).itsFinalDerivation,
						kRTPRssmTrackLostPacketsFlag | kRTPRssmQueueAndUseMarkerBitFlag,
						-1L );
			}
		}
	}
	
	return( theResult );
}



/* ---------------------------------------------------------------------------
 *		+ RTPRssmComputeChunkSize() implementation
 * ---------------------------------------------------------------------------
 *
 *	Use the packet list to determine the size of the data buffer needed to
 *	pass data from these network packets to the StreamHandler.
 *	
 *	For this reassembler, the chunk size is the audio data size for the entire
 *	interleave group to which the given packets belong.  (The data is
 *	packetized such that the base reassembler can automatically group
 *	incoming packets by interleave group, so the packets in the list will be
 *	all those packets, and only those packets, from a single interleave
 *	group.)  The size cannot be computed by summing the data sizes of network
 *	packets received, since some packets might be lost.  Instead, the size is
 *	determnined, through a call to __InterleaveGroupDataSize() defined above,
 *	by inspecting the payload header and data size of the first packet.
 *	
 *	This function also updates the StreamHandler's SampleDescription when the
 *	sample rate or channel count changes.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_ComputeChunkSize(
	RTPRssmIMAAudioInstanceData **	inGlobals,
	RTPRssmPacket *					inPacketListHead,
	SInt32 							inFlags,
	UInt32 *						outChunkDataSize )
{
#pragma unused( inFlags )

	ComponentResult				theResult = noErr;
	const IMAAudioPayload *		thePayload;
	UInt32						theChannelCount;
	UnsignedFixed				theSampleRate;
	SampleDescriptionHandle		theDescription;
	
	
	thePayload = __GetPayload( inPacketListHead );
	
	theChannelCount = IMAAudioPayloadChannelCount( &( **inGlobals ).itsPayloadAttributes );
	theSampleRate = IMAAudioPayloadSampleRate( &( **inGlobals ).itsPayloadAttributes );
	
	if(
		IMAAudioPayloadChannelCount( thePayload ) != theChannelCount  ||
		IMAAudioPayloadSampleRate( thePayload ) != theSampleRate )
	{
		theResult = __GetNewSampleDescription( thePayload, &theDescription );
		
		if( theResult == noErr )
		{
			theResult =
				RTPRssmSetSampleDescription(
					( **inGlobals ).itsFinalDerivation, theDescription );
			
			if( theResult == noErr )
				( **inGlobals ).itsPayloadAttributes = *thePayload;
			
			DisposeHandle( REINTERPRET_CAST( Handle )( theDescription ) );
		}
	}
	
	*outChunkDataSize = __InterleaveGroupDataSize( inPacketListHead );
	
	return( theResult );
}



/* ---------------------------------------------------------------------------
 *		+ RTPRssmAdjustPacketParams() implementation
 * ---------------------------------------------------------------------------
 *
 *	Adjust fields of the RTPRssmPacket to reflect the properties of its
 *	payload.  The payload format used by this reassembler defines a
 *	fixed-length header that immediately precedes the audio data.  Overriding
 *	RTPRssmAdjustPacketParams() gives the reassembler a chance to update the
 *	RTPRssmPacket data structure for each packet to reflect its payload header
 *	length and audio data length.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_AdjustPacketParams(
	RTPRssmIMAAudioInstanceData **	inGlobals,
	RTPRssmPacket *					inPacket,
	SInt32							inFlags )
{
#pragma unused( inGlobals, inFlags )
	
	UInt32				theHeaderSize;
	IMAAudioPayload *	thePayload;
	
	
	theHeaderSize = sizeof( thePayload->itsFixedHeader );
	
	inPacket->payloadHeaderLength = theHeaderSize;
	inPacket->dataLength -= theHeaderSize;
	
	return( noErr );
}



/* ---------------------------------------------------------------------------
 *		+ RTPRssmCopyDataToChunk() implementation
 * ---------------------------------------------------------------------------
 *
 *	Copy sample data from a packet list to the data buffer described by an
 *	SHChunkRecord.  The payloads processed by this reassembler indicate where
 *	interleave audio data from each payload.  This function iterates through
 *	the packets, copying the data to the correct position in the chunk.  The
 *	function then calls __FillMissingData(), defined above, to synthesize lost
 *	packet data.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_CopyDataToChunk(
	RTPRssmIMAAudioInstanceData **	inGlobals,
	RTPRssmPacket *					inPacketListHead,
	UInt32							inMaxChunkDataSize,
	SHChunkRecord *					inChunk,
	SInt32							inFlags )
{
#pragma unused( inGlobals, inFlags )
	
	ComponentResult				theResult = noErr;
	UInt32						theFrameCount;
	IMAAudioFrame *				theFrames;
	RTPRssmPacket *				thePacket;
	const IMAAudioPayload *		thePayload;
	UInt16						theGroupStartSequenceNumber;
	UInt32						theInterleaveCount;
	UInt32						theInterleaveIndex;
	UInt32						thePacketFrameCount;
	UInt32						theSourceIndex;
	UInt32						theTargetIndex;
	
	
	/*	IMPORTANT	The sequence number computations in this function work
					because the packets are in order and the separation
					between the packets is guaranteed to be within the
					precision of the RTP sequence number field (i.e., less
					than 16 bits, or 65536 packets).  When computing with
					sequence numbers of packets that might be separated by
					more than 65535 packets, a reassembler must extend
					the precision of the sequence numbers, for instance by
					using the inNumWraparounds parameter of
					RTPRssmHandleNewPacket() or by using the timeStamp
					field of RTPRssmPacket structures.
	*/
	
	inChunk->dataSize = __InterleaveGroupDataSize( inPacketListHead );
	
	if( inChunk->dataSize > inMaxChunkDataSize )
	{
		/* this should never happen */
		
		theResult = buffersTooSmall;
	}
	
	else
	{
		theFrameCount = inChunk->dataSize / sizeof( IMAAudioFrame );
		theFrames =
			REINTERPRET_CAST( IMAAudioFrame * )(
				CONST_CAST( UInt8 * )( inChunk->dataPtr ) );
		
		thePayload = __GetPayload( inPacketListHead );
		
		theInterleaveCount = IMAAudioPayloadInterleaveCount( thePayload );
		
		theGroupStartSequenceNumber =
			inPacketListHead->sequenceNum - IMAAudioPayloadInterleaveIndex( thePayload );
		
		for( thePacket = inPacketListHead; thePacket; thePacket = thePacket->next )
		{
			thePayload = __GetPayload( thePacket );
			
			theInterleaveIndex = thePacket->sequenceNum - theGroupStartSequenceNumber;
			thePacketFrameCount = thePacket->dataLength / sizeof( IMAAudioFrame );
			
			for( theSourceIndex = 0; theSourceIndex < thePacketFrameCount; theSourceIndex++ )
			{
				theTargetIndex =
					( theSourceIndex * theInterleaveCount ) + theInterleaveIndex;
				
				if( theTargetIndex < theFrameCount )
				{
					theFrames[ theTargetIndex ] =
						thePayload->itsAudioFrames[ theSourceIndex ];
				}
			}
		}
		
		__FillMissingData(
			inPacketListHead, theGroupStartSequenceNumber, theInterleaveCount, inChunk );
	}
	
	return( theResult );
}



/* ---------------------------------------------------------------------------
 *		+ RTPRssmSendPacketList() implementation
 * ---------------------------------------------------------------------------
 *
 *	Prepare to process or discard a list of packets.  If the base detects that
 *	some network packets were lost, overriding RTPRssmSendPacketList() gives
 *	the reassembler a chance to decide whether it can handle the remaining
 *	packets.  This reassembler tolerates any amount of packet loss, so this
 *	function simply clears the kRTPRssmLostSomePackets flag before delegating
 *	to the base.  When a reassembler is able to recover from packet loss,
 *	turning off the kRTPRssmLostSomePackets flag before delegating the call to
 *	the base reassembler prevents the base from discarding the packets.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_SendPacketList(
	RTPRssmIMAAudioInstanceData **	inGlobals,
	RTPRssmPacket *					inPacketListHead,
	const TimeValue64 *				inLastChunkPresentationTime,
	SInt32							inFlags )
{
	return(
		RTPRssmSendPacketList(
			( **inGlobals ).itsBase, inPacketListHead, inLastChunkPresentationTime,
			inFlags &= ~kRTPRssmLostSomePackets ) );
}



/* ---------------------------------------------------------------------------
 *		+ RTPRssmHasCharacteristic() implementation
 * ---------------------------------------------------------------------------
 *
 *	Indicate whether the reassembler has the specified characteristic.  This
 *	reassembler requires ordered packet lists.  All other characteristics are
 *	delegated to the base.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_HasCharacteristic(
	RTPRssmIMAAudioInstanceData **	inGlobals,
	OSType 							inCharacteristic,
	Boolean *						outHasIt )
{
	ComponentResult		theResult;
	
	
	if( inCharacteristic == kRTPRssmRequiresOrderedPacketsCharacteristic )
	{
		*outHasIt = true;
		theResult = noErr;
	}

	else
	{
		theResult =
			RTPRssmHasCharacteristic( ( **inGlobals ).itsBase, inCharacteristic, outHasIt );
	}
	
	return( theResult );
}



/* ---------------------------------------------------------------------------
 *		+ RTPRssmReset() implementation
 * ---------------------------------------------------------------------------
 *
 *	End processing of the current stream of network packets and prepare to
 *	process a new, possibly unrelated, stream.  This implementation simply
 *	resets its base.
 *
 */

EXTERN_API( ComponentResult )
RTPRssmIMAAudio_Reset(
	RTPRssmIMAAudioInstanceData **	inGlobals,
	SInt32							inFlags )
{
	ComponentResult		theResult;
	
	
	if( CallComponentCanDo( ( **inGlobals ).itsBase, kRTPRssmResetSelect ) )
		theResult = RTPRssmReset( ( **inGlobals ).itsBase, inFlags );
	else
		theResult = noErr;
	
	return( theResult );
}
