IMAAudioRTP/Sources/RTPMPIMAAudio.c

/*
    File:       RTPMPIMAAudio.c
 
    Contains:   Definition of IMA Audio RTPMediaPacketizer
 
    Copyright:  © 1997-1999 by Apple Computer, Inc., all rights reserved.
    
    
    
    OVERVIEW
    
    QuickTime Streaming software uses an RTPMediaPacketizer to encapsulate
    sample data in network packets.  When preparing to packetize sample data,
    Streaming software first determines a packetizer's capabilities and
    operational parameters.  If the packetizer is suitable, the Streaming
    software provides the packetizer with an RTPPacketBuilder.  The packetizer
    uses the RTPPacketBuilder to construct network packets from sample data.
    The Streaming software specifies what sample data to packetize by calling
    the packetizer's RTPMPSetSampleData() implementation.
    
    This packetizer implements the following interface and delegates all other
    calls to a base RTPMediaPacketizer defined by QuickTime Streaming.
        
        
        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 MEDIA PACKETIZER INTERFACE
        ------------------------------
        
        RTPMPInitialize()               Prepare to packetize sample data.
        
        RTPMPPreflightMedia()           Determine whether the packetizer can
                                        packetize the described data.
        
        RTPMPSetSampleData()            Use the instance's RTPPacketBuilder to
                                        packetize sample data.
        
        RTPMPFlush()                    Finish any packetization in progress.
        
        RTPMPReset()                    Abort any packetization in progress.
        
        RTPMPGetInfo()                  Return the requested information.
        
        RTPMPSetTimeScale()
        
        RTPMPGetTimeScale()
        
        RTPMPSetTimeBase()
        
        RTPMPGetTimeBase()
        
        RTPMPHasCharacteristic()
        
        RTPMPSetPacketBuilder()
        
        RTPMPGetPacketBuilder()
        
        RTPMPSetMediaType()
        
        RTPMPGetMediaType()
        
        RTPMPSetMaxPacketSize()
        
        RTPMPGetMaxPacketSize()
        
        RTPMPSetMaxPacketDuration()
        
        RTPMPGetMaxPacketDuration()     
        
        RTPMPDoUserDialog()             Present a dialog of user-adjustable
                                        options.
        
        RTPMPSetSettingsFromAtomContainerAtAtom()
                                        Update instance variables with saved
                                        user settings.
        
        RTPMPGetSettingsIntoAtomContainerAtAtom()
                                        Return current settings of user
                                        adjustable options.
                                        
        RTPMPGetMediaSettingsAsText()   Return current settings of user
                                        adjustable options as a string.
*/
 
 
 
/* ---------------------------------------------------------------------------
 *      H E A D E R S
 * ---------------------------------------------------------------------------
 */
 
#include "RTPMPIMAAudio.h"
#include "RTPMPIMAAudioResources.h"
#include <FixMath.h>
#include <Math64.h>
#include <Resources.h>
#include <TextUtils.h>
 
 
 
/* ---------------------------------------------------------------------------
 *      R T P    M E D I A    P A C K E T I Z E R    P R O T O T Y P E S
 * ---------------------------------------------------------------------------
 *
 *  QTStreamingComponents.k.h uses these macros to declare prototypes for
 *  the RTPMediaPacketizer calls defined in this file.
 *
 */
 
#define RTPMP_BASENAME()                RTPMPIMAAudio_
#define RTPMP_GLOBALS()                 RTPMPIMAAudioInstanceData **
 
#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()        RTPMP_BASENAME()
#define CALLCOMPONENT_GLOBALS()         RTPMP_GLOBALS() storage
#define COMPONENT_DISPATCH_FILE         "RTPMPIMAAudioDispatch.h"
#define COMPONENT_C_DISPATCHER          1
#define COMPONENT_UPP_SELECT_ROOT()     RTPMP
#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
 * ---------------------------------------------------------------------------
 */
 
#if TARGET_OS_WIN32
#   define DragWindow( window, startPt, boundsRect )
#endif
 
 
 
enum
{
    __kNoFlags                      = 0,
    __kFlush                        = true,
    __kDontFlush                    = !__kFlush,
    __kDefaultPacketDurationLimit   = 200,          /* 200ms.  See RFC 1890, section 4.1 */
    __kDefaultInterleaveCount       = 3,
    __kTypicalMTUSize               = 1500,         /* Ethernet */
    __kTypicalNetworkHeaderSize     = 20,           /* IP, no options */
    __kTypicalTransportHeaderSize   = 8,            /* UDP */
    __kTypicalRTPHeaderSize         = 12,           /* no CSRCs, no extension */
    __kDefaultPacketSizeLimit       =
        __kTypicalMTUSize - __kTypicalNetworkHeaderSize - __kTypicalTransportHeaderSize -
        __kTypicalRTPHeaderSize
};
 
enum
{
    __kOKButton             = 1,
    __kCancelButton         = 2,
    __kInterleavingMenu     = 3
};
 
enum
{
    __kInterleaveCountAtomType = 'ilvc'
};
 
 
 
typedef UInt32  __TStoredInterleaveCount;
 
 
 
/* ---------------------------------------------------------------------------
 *      __Drag()
 * ---------------------------------------------------------------------------
 *
 *  Drag the dialog window on mouseDown.
 *
 */
 
static
Boolean
__Drag(
    DialogPtr               aDialog,
    const EventRecord *     anEvent )
{
    Boolean     theResult = false;
    WindowPtr   theWindow;
    short       thePart;
    Rect        theBoundary;
    
    
    if( anEvent->what == mouseDown )
    {
        thePart = MacFindWindow( anEvent->where, &theWindow );
        
        if( theWindow == aDialog  &&  thePart == inDrag )
        {
            theBoundary = ( **GetGrayRgn() ).rgnBBox;
            
            DragWindow( aDialog, anEvent->where, &theBoundary );
            
            theResult = true;
        }
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __SettingsDialogFilter()
 * ---------------------------------------------------------------------------
 *
 *  Drag the dialog window if appropriate.  Otherwise dispatch first to the
 *  custom event filter, if any, passed to RTPMPDoUserDialog(), and then, if
 *  necessary, to the standard event filter.
 *
 */
 
static
pascal
Boolean
__SettingsDialogFilter(
    DialogPtr       aDialog,
    EventRecord *   anEvent,
    short *         anItem )
{
    Boolean         theResult = false;
    ModalFilterUPP  theCustomFilter;
    
    
    if( __Drag( aDialog, anEvent ) )
    {
        *anItem = 0;
        theResult = true;
    }
    
    else
    {
        theCustomFilter = REINTERPRET_CAST( ModalFilterUPP )( GetWRefCon( aDialog ) );
        
        if( theCustomFilter )
            theResult = CallModalFilterProc( theCustomFilter, aDialog, anEvent, anItem );
        
        if( !theResult )
            theResult = StdFilterProc( aDialog, anEvent, anItem );
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __GreatestCommonFactor()
 * ---------------------------------------------------------------------------
 *
 *  Compute the greatest common factor of two positive integers.
 *
 */
 
static
UInt32
__GreatestCommonFactor(
    UInt32  inDividend,
    UInt32  inDivisor )
{
    UInt32  theRemainder;
    
    
    while( inDivisor )
    {
        theRemainder = inDividend % inDivisor;
        inDividend = inDivisor;
        inDivisor = theRemainder;
    }
    
    return( inDividend );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __LockInstanceData()
 * ---------------------------------------------------------------------------
 *
 *  Lock relocatable block containing instance data.
 *
 */
 
static
void
__LockInstanceData(
    RTPMPIMAAudioInstanceData **    inGlobals )
{
    if( !( **inGlobals ).itsLockCount )
    {
        if( ( **inGlobals ).itsInSystemHeap )
            HLock( REINTERPRET_CAST( Handle )( inGlobals ) );
        else
            HLockHi( REINTERPRET_CAST( Handle )( inGlobals ) );
    }
    
    ( **inGlobals ).itsLockCount++;
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __UnlockInstanceData()
 * ---------------------------------------------------------------------------
 *
 *  Unlock relocatable block containing instance data.
 *
 */
 
static
void
__UnlockInstanceData(
    RTPMPIMAAudioInstanceData **    inGlobals )
{
    --( **inGlobals ).itsLockCount;
    
    if( !( **inGlobals ).itsLockCount )
        HUnlock( REINTERPRET_CAST( Handle )( inGlobals ) );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __InterleaveGroupFrameCount()
 * ---------------------------------------------------------------------------
 *
 *  Compute the number of frames in an interleave group according to the
 *  following constraints:
 *
 *      - no packet exceeds the packet size limit
 *
 *      - no packet exceeds the packet duration limit
 *
 *      - the frame count is a multiple of the channel count
 *
 *      - the group duration is an integer number of clock cycles
 *
 */
 
static
UInt32
__InterleaveGroupFrameCount(
    RTPMPIMAAudioInstanceData **    inGlobals )
{
    UInt32  theResult;
    UInt64  theDurationLimit;
    UInt64  theSampleRate;
    UInt32  theTimewiseLimit;
    UInt32  theInterleaveCount;
    UInt32  theChannelCount;
    UInt32  theFrameDurationRemainder;
    UInt32  theIntegerDurationFrameCount;
    UInt32  theConstrainingFactor;
    UInt32  theCommonFactor;
    
    
    /* Compute size-wise limit per payload. */
    
    theResult =
        ( ( **inGlobals ).itsPayloadSizeLimit -
            ( sizeof( IMAAudioPayload ) - sizeof( IMAAudioFrame ) ) ) /
        sizeof( IMAAudioFrame );
    
    
    /* Compute time-wise limit per payload. */
    
    theDurationLimit = U64SetU( ( **inGlobals ).itsPayloadDurationLimit );
    theSampleRate =
        U64SetU( IMAAudioPayloadSampleRate( &( **inGlobals ).itsPayloadAttributes ) );
    
    theTimewiseLimit =
        Fix2Long(
            U32SetU(
                U64Div(
                    U64Multiply( theDurationLimit, theSampleRate ),
                    U64SetU( 1000UL * kIMAAudioPayloadFrameSampleCount ) ) ) );
    
    
    /* Select lesser limit. */
    
    if( theResult > theTimewiseLimit )
        theResult = theTimewiseLimit;
    
    
    /* Compute limit for entire interleave group. */
    
    theInterleaveCount =
        IMAAudioPayloadInterleaveCount( &( **inGlobals ).itsPayloadAttributes );
    
    theResult *= theInterleaveCount;
    
    
    /*  Compute the least number of frames that, given their sample rate,
        have an integer duration with respect to the RTP clock rate of
        55125 Hz.  As explained in IMAAudioPayload.h, the integer-duration
        frame count will be no greater than four.  An interleave group has
        integer duration when its frame count is a multiple of the integer-
        duration frame count. */
    
    theFrameDurationRemainder =
        U64Mod(
            U64Multiply(
                U64SetU( STATIC_CAST( UInt32 )( kIMAAudioPayloadRTPTimeScale ) << 16 ),
                U64SetU( kIMAAudioPayloadFrameSampleCount ) ),
            theSampleRate );
    
    if( theFrameDurationRemainder )
    {
        theIntegerDurationFrameCount =
            U32SetU( U64Div( theSampleRate, theFrameDurationRemainder ) );
    }
    
    else
    {
        theIntegerDurationFrameCount = 1;
    }
    
    
    /*  Restrict group frame count to a multiple of both the channel count and
        the integer-duration frame count. */
    
    theChannelCount = IMAAudioPayloadChannelCount( &( **inGlobals ).itsPayloadAttributes );
    
    theCommonFactor =
        __GreatestCommonFactor( theIntegerDurationFrameCount, theChannelCount );
    
    theConstrainingFactor =
        ( theIntegerDurationFrameCount / theCommonFactor ) *
            ( theChannelCount / theCommonFactor );
    
    theResult = ( theResult / theConstrainingFactor ) * theConstrainingFactor;
    
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __UpdateLimits()
 * ---------------------------------------------------------------------------
 *
 *  Update values used to construct network packets.
 *
 */
 
static
void
__UpdateLimits(
    RTPMPIMAAudioInstanceData **    inGlobals )
{
    if( ( **inGlobals ).itsPayloadAttributesInitialized )
    {
        /*  Update count of frames in each interleave group. */
        
        ( **inGlobals ).itsInterleaveGroupFrameCount =
            __InterleaveGroupFrameCount( inGlobals );
    }
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __UpdateSampleDescription()
 * ---------------------------------------------------------------------------
 *
 *  Determine whether the SampleDescription of incoming sample data has
 *  changed and update instance variables accordingly.
 *
 */
 
static
ComponentResult
__UpdateSampleDescription(
    RTPMPIMAAudioInstanceData **    inGlobals,
    const RTPMPSampleDataParams *   inSampleData )
{
    ComponentResult         theError = noErr;
    SInt32                  theFlags;
    SoundDescriptionHandle  theDescription;
    CompressionInfo         theCompressionInfo;
    UnsignedFixed           thePayloadSampleRate;
    
    
    /*  Check the SampleDescription if it is the first description or if the cached
        sample description seed doesn't match the current seed. */
    
    if(
        !( **inGlobals ).itsPayloadAttributesInitialized  ||
        ( **inGlobals ).itsSampleDescriptionSeed != inSampleData->sampleDescSeed )
    {
        /*  If the RTPMPSetSampleData() implementation queues data, then any
            queued sample data, which uses the obsolete SampleDescription, must
            be flushed before updating to the new SampleDescription. */
        
        theError = RTPMPFlush( ( **inGlobals ).itsFinalDerivation, 0, &theFlags );
        
        if( !theError )
        {
            theDescription =
                REINTERPRET_CAST( SoundDescriptionHandle )(
                    inSampleData->sampleDescription );
            
            theError =
                GetCompressionInfo(
                    fixedCompression, ( **theDescription ).dataFormat,
                    ( **theDescription ).numChannels, ( **theDescription ).sampleSize,
                    &theCompressionInfo );
            
            
            /*  Update the payload header that the packetizer includes in each network
                packet. */
            
            IMAAudioPayloadSetChannelCount(
                &( **inGlobals ).itsPayloadAttributes, ( **theDescription ).numChannels );
            
            thePayloadSampleRate =
                IMAAudioPayloadSetSampleRate(
                    &( **inGlobals ).itsPayloadAttributes,
                    ( **theDescription ).sampleRate );
            
            ( **inGlobals ).itsPayloadAttributesInitialized = true;
            
            
            IMAAudioQueueSetFlowControl(
                &( **inGlobals ).itsAudioQueue, ( **theDescription ).sampleRate,
                thePayloadSampleRate, ( **theDescription ).numChannels );
            
            
            /*  Update values used to construct network packets. */
            
            __UpdateLimits( inGlobals );
            
            
            /*  Update the cached sample description seed to indicate that the
                packetizer state is now consistent with the new SampleDescription. */
            
            ( **inGlobals ).itsSampleDescriptionSeed = inSampleData->sampleDescSeed;
        }
    }
    
    return( theError );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __SampleBlockDuration()
 * ---------------------------------------------------------------------------
 *
 *  Compute the duration of the described sample block.
 *
 */
 
static
UInt32
__SampleBlockDuration(
    const RTPMPSampleDataParams *   inSampleData )
{
    UInt32                  theResult;
    UInt32                  theSampleCount;
    SoundDescriptionHandle  theDescription;
    UInt64                  theFixedSampleCount;
    
    
    if( inSampleData->duration )
    {
        theResult = inSampleData->duration;
    }
    
    else
    {
        theDescription =
            REINTERPRET_CAST( SoundDescriptionHandle )( inSampleData->sampleDescription );
        
        theSampleCount =
            kIMAAudioPayloadFrameSampleCount *
            ( inSampleData->dataLength / sizeof( IMAAudioFrame ) );
        
        theFixedSampleCount = U64Multiply( U64SetU( theSampleCount ), U64SetU( fixed1 ) );
        
        theResult =
            U32SetU(
                U64Div(
                    U64Multiply(
                        theFixedSampleCount, U64SetU( kIMAAudioPayloadRTPTimeScale ) ),
                    U64SetU( ( **theDescription ).sampleRate ) ) );
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __BeginInterleaveGroup()
 * ---------------------------------------------------------------------------
 *
 *  Use the RTPPacketBuilder to create a new packet group and all the packets
 *  in that group.  For this packetizer, a packet group contains one
 *  interleave group.
 *  
 *  Each network packet consists of a fixed header followed by sample data.
 *  This function adds the fixed header to each packet.
 *
 */
 
static
ComponentResult
__BeginInterleaveGroup(
    const IMAAudioPayload *     inPayloadAttributes,
    TimeValue64                 inTimestamp,
    UInt32                      inFrameCount,
    RTPPacketBuilder            inPacketBuilder,
    RTPPacketGroupRef *         outPacketGroup,
    RTPPacketRef *              outPackets )
{
    ComponentResult     theResult;
    UInt32              theInterleaveCount;
    UInt32              thePayloadFrameCount;
    UInt32              theIndex;
    IMAAudioPayload     theHeader;
    UInt32              theHeaderSize;
    UInt32              thePayloadSizeLimit;
    
    
    theResult =
        RTPPBBeginPacketGroup( inPacketBuilder, __kNoFlags, inTimestamp, outPacketGroup );
    
    theInterleaveCount = IMAAudioPayloadInterleaveCount( inPayloadAttributes );
    thePayloadFrameCount = ( inFrameCount + theInterleaveCount - 1 ) / theInterleaveCount;
    
    theHeader = *inPayloadAttributes;
    theHeaderSize = sizeof( theHeader ) - sizeof( IMAAudioFrame );
    
    thePayloadSizeLimit =
        theHeaderSize + ( thePayloadFrameCount * sizeof( IMAAudioFrame ) );
    
    
    /*  If there aren't enough frames to fill an entire interleave group, omit the
        empty packets. */
    
    if( theInterleaveCount > inFrameCount )
        theInterleaveCount = inFrameCount;
    
    for( theIndex = 0; theIndex < theInterleaveCount  &&  theResult == noErr; theIndex++ )
    {
        theResult =
            RTPPBBeginPacket(
                inPacketBuilder, __kNoFlags, *outPacketGroup, thePayloadSizeLimit,
                &outPackets[ theIndex ] );
        
        if( theResult == noErr )
        {
            /*  The header is added to the network packet as literal data.  Literal
                data is written directly to the network packet.  If the
                RTPPacketBuilder is storing network packet data to disk, it must
                store a copy of literal data. */
            
            theResult =
                RTPPBAddPacketLiteralData(
                    inPacketBuilder, __kNoFlags, *outPacketGroup, outPackets[ theIndex ],
                    REINTERPRET_CAST( UInt8 * )( &theHeader ), theHeaderSize, NULL );
            
            IMAAudioPayloadIncrementInterleaveIndex( &theHeader );
        }
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __EndInterleaveGroup()
 * ---------------------------------------------------------------------------
 *
 *  Use the RTPPacketBuilder to close the current packet group and all its
 *  packets.
 *
 */
 
static
ComponentResult
__EndInterleaveGroup(
    const IMAAudioPayload *     inPayloadAttributes,
    UInt32                      inFrameCount,
    RTPPacketBuilder            inPacketBuilder,
    RTPPacketGroupRef           inPacketGroup,
    const RTPPacketRef *        inPackets )
{
    ComponentResult     theResult = noErr;
    UnsignedFixed       theSampleRate;
    UInt32              theDuration;
    UInt32              theInterleaveCount;
    UInt32              theIndex;
    
    
    theSampleRate = IMAAudioPayloadSampleRate( inPayloadAttributes );
    
    theDuration =
        U32SetU(
            U64Div(
                U64Multiply(
                    U64SetU( inFrameCount * kIMAAudioPayloadFrameSampleCount ),
                    U64SetU( STATIC_CAST( UInt32 )( kIMAAudioPayloadRTPTimeScale ) << 16 ) ),
                U64SetU( theSampleRate ) ) );
    
    theDuration /= IMAAudioPayloadChannelCount( inPayloadAttributes );
    
    theInterleaveCount = IMAAudioPayloadInterleaveCount( inPayloadAttributes );
    
    
    /*  If there aren't enough frames to fill an entire interleave group, the empty
        packets have been omitted. */
    
    if( theInterleaveCount > inFrameCount )
        theInterleaveCount = inFrameCount;
    
    
    /*  To simplify reassembly, this packetizer uses the timestamp of the start
        of the group as the timestamp for each network packet in the group.  When
        reassembling, the derived RTPReassembler relies on the QuickTime Streaming
        base RTPReassembler to group together packets that have the same timestamp.
        
        A packet's duration is the time between its timestamp and the timestamp
        of the next packet.  Since these packets share the same timestamp, most
        have zero duration.  The last packet in the group reflects the duration
        of the entire group. */
    
    for(
        theIndex = 0; theIndex < ( theInterleaveCount - 1 )  &&  theResult == noErr;
        theIndex++ )
    {
        theResult =
            RTPPBEndPacket(
                inPacketBuilder, __kNoFlags, inPacketGroup, inPackets[ theIndex ],
                0 /* inTimeOffset */ , 0 /* inDuration */ );
    }
    
    if( theResult == noErr )
    {
        /*  The packetizer sets the RTP/AVP marker bit in the last network
            packet of an interleave group.  The QuickTime Streaming base
            RTPReassembler can better assist in reassembling the payload data
            if this bit is used to mark the end of a packet group. */
        
        theResult =
            RTPPBEndPacket(
                inPacketBuilder, kRTPPBSetMarkerFlag, inPacketGroup, inPackets[ theIndex ],
                0, theDuration );
        
        if( theResult == noErr )
        {
            /*  For this packetizer, every group contains only sync samples.  That
                means the sample data for the group can be decoded independently of
                any previous sample data.  When randomly accessing stored movies,
                a streaming server can look for sync samples. */
            
            theResult =
                RTPPBEndPacketGroup( inPacketBuilder, kRTPPBSyncSampleFlag, inPacketGroup );
        }
        
        else
        {
            RTPPBEndPacketGroup( inPacketBuilder, kRTPPBDontSendFlag, inPacketGroup );
        }
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __PacketizeInterleaveGroup()
 * ---------------------------------------------------------------------------
 *
 *  Use the RTPPacketBuilder and the queued sample data to construct a single
 *  interleave group of network packets.
 *  
 */
 
static
ComponentResult
__PacketizeInterleaveGroup(
    IMAAudioQueue *             inAudioQueue,
    const IMAAudioPayload *     inPayloadAttributes,
    UInt32                      inInterleaveGroupFrameCount,
    RTPPacketBuilder            inPacketBuilder )
{
    ComponentResult         theResult;
    IMAAudioPayload         thePayloadAttributes;
    UInt32                  theFrameCount;
    UInt32                  theInterleaveCount;
    IMAAudioQueueElement    theFrameInfo;
    RTPPacketGroupRef       thePacketGroup;
    RTPPacketRef            thePackets[ kIMAAudioPayloadInterleaveCountLimit ];
    UInt32                  theFrameIndex;
    
    
    /* Get the total queued frame count. */
    
    theFrameCount = IMAAudioQueueCount( inAudioQueue );
    
    if( theFrameCount )
    {
        /* Limit the frame count to a single interleave group. */
        
        if( theFrameCount >= inInterleaveGroupFrameCount )
            theFrameCount = inInterleaveGroupFrameCount;
        
        thePayloadAttributes = *inPayloadAttributes;
        
        theInterleaveCount = IMAAudioPayloadInterleaveCount( &thePayloadAttributes );
        
        IMAAudioPayloadSetInterleaving(
            &thePayloadAttributes, theInterleaveCount, theFrameCount );
        
        
        /* Initialize the interleave group. */
        
        IMAAudioQueueDequeue( inAudioQueue, &theFrameInfo );
        
        theResult =
            __BeginInterleaveGroup(
                &thePayloadAttributes, theFrameInfo.itsTimestamp, theFrameCount,
                inPacketBuilder, &thePacketGroup, thePackets );
        
        
        /* Interleave audio frames into network packets. */
        
        theFrameIndex = 0;
        
        do
        {
            /*  The RTPPacketBuilder provides a routine specifically for adding
                sample data.  For stored movies, the RTPPacketBuilder need not
                store a copy of sample data, since the data is already stored
                in the movie. */
            
            theResult =
                RTPPBAddPacketSampleData(
                    inPacketBuilder, __kNoFlags, thePacketGroup,
                    thePackets[ theFrameIndex % theInterleaveCount ],
                    theFrameInfo.itsSampleDataParams, theFrameInfo.itsOffset,
                    sizeof( IMAAudioFrame ), NULL );
            
            theFrameIndex++;
        }
        while(
            theResult == noErr  &&  theFrameIndex < theFrameCount  &&
            IMAAudioQueueDequeue( inAudioQueue, &theFrameInfo ) );
        
        
        /* End the interleave group. */
        
        if( theResult == noErr )
        {
            theResult =
                __EndInterleaveGroup(
                    &thePayloadAttributes, theFrameCount, inPacketBuilder,
                    thePacketGroup, thePackets );
        }
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __ProcessAudioQueue()
 * ---------------------------------------------------------------------------
 *
 *  When flushing, packetize all queued audio data.  Otherwise, packetize
 *  interleave groups continuously until there is not enough data to fill an
 *  interleave group.
 *
 */
 
static
ComponentResult
__ProcessAudioQueue(
    RTPMPIMAAudioInstanceData **    inGlobals,
    Boolean                         inFlush )
{
    ComponentResult     theResult = noErr;
    IMAAudioPayload     thePayloadAttributes = ( **inGlobals ).itsPayloadAttributes;
    UInt32              theMinimumFrameCount;
    UInt32              theFrameCount;
    
    
    __LockInstanceData( inGlobals );
    
    if( inFlush )
        theMinimumFrameCount = 1;
    else
        theMinimumFrameCount = ( **inGlobals ).itsInterleaveGroupFrameCount;
    
    for(
        theFrameCount = IMAAudioQueueCount( &( **inGlobals ).itsAudioQueue );
        theFrameCount >= theMinimumFrameCount  &&  theResult == noErr;
        theFrameCount = IMAAudioQueueCount( &( **inGlobals ).itsAudioQueue ) )
    {
        if( theFrameCount > ( **inGlobals ).itsInterleaveGroupFrameCount )
            theFrameCount = ( **inGlobals ).itsInterleaveGroupFrameCount;
        
        theResult =
            __PacketizeInterleaveGroup(
                &( **inGlobals ).itsAudioQueue, &thePayloadAttributes, theFrameCount,
                ( **inGlobals ).itsPacketBuilder );
    }
    
    __UnlockInstanceData( inGlobals );
    
    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 packetizer
 *  is opened, it is not always called to packetize data, so this function
 *  doesn't perform any allocations or time-consuming operations that are
 *  needed only for packetizing sample data.  The RTPMPInitialize()
 *  implementation performs such operations.
 *
 *  Since the RTPMPGetSettingsAsText() implementation and the
 *  RTPMPDoUserDialog() implementation might be called before calling the
 *  RTPMPInitialize() implementation, this function must initialize any
 *  instance variables used by those functions.
 *  
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_Open(
    RTPMPIMAAudioInstanceData **    inGlobals,
    ComponentInstance               self )
{
    ComponentResult     theResult = noErr;
    RTPMediaPacketizer  theBase;
    
    
    inGlobals =
        REINTERPRET_CAST( RTPMPIMAAudioInstanceData ** )(
            NewHandleClear( sizeof( **inGlobals ) ) );
    
    if( inGlobals )
    {
        ( **inGlobals ).itself = self;
        ( **inGlobals ).itsFinalDerivation = self;
        ( **inGlobals ).itsInSystemHeap =
            ( HandleZone( REINTERPRET_CAST( Handle )( inGlobals ) ) == SystemZone() );
        ( **inGlobals ).itsLockCount = 0;
        ( **inGlobals ).itsInitialized = false;
        ( **inGlobals ).itsPayloadAttributesInitialized = false;
        
        IMAAudioPayloadInitialize( &( **inGlobals ).itsPayloadAttributes );
        
        SetComponentInstanceStorage( self, REINTERPRET_CAST( Handle )( inGlobals ) );
        
        theResult =
            OpenADefaultComponent(
                kRTPMediaPacketizerType, kRTPBaseMediaPacketizerType, &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 )
RTPMPIMAAudio_Close(
    RTPMPIMAAudioInstanceData **    inGlobals,
    ComponentInstance               self )
{
#pragma unused( self )
    
    IMAAudioQueue   theAudioQueue;
    
    
    if( inGlobals )
    {
        if( ( **inGlobals ).itsInitialized )
        {
            theAudioQueue = ( **inGlobals ).itsAudioQueue;
            IMAAudioQueueFlush( &theAudioQueue );
        }
        
        if( ( **inGlobals ).itsBase )
            CloseComponent( ( **inGlobals ).itsBase );
        
        DisposeHandle( REINTERPRET_CAST( Handle )( inGlobals ) );
    }
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + CallComponentVersion() implementation
 * ---------------------------------------------------------------------------
 *
 *  Return the instance's version.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_Version(
    RTPMPIMAAudioInstanceData **    inGlobals )
{
#pragma unused( inGlobals )
    
    return( kComponentVersion );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + CallComponentTarget() implementation
 * ---------------------------------------------------------------------------
 *
 *  Update the instance's inheritance graph with a new most-derived instance.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_Target(
    RTPMPIMAAudioInstanceData **    inGlobals,
    ComponentInstance               target )
{
    ComponentResult     theResult;
    
    
    if( ( **inGlobals ).itsBase )
        theResult = ComponentSetTarget( ( **inGlobals ).itsBase, target );
    else
        theResult = noErr;
    
    if( theResult == noErr )
        ( **inGlobals ).itsFinalDerivation = target;
    
    return( theResult );
}
 
 
 
#pragma mark -
#pragma mark *        RTP MEDIA PACKETIZER INTERFACE
#pragma mark -
/* ---------------------------------------------------------------------------
 *      R T P    M E D I A    P A C K E T I Z E R    I N T E R F A C E
 * ---------------------------------------------------------------------------
 */
 
/* ---------------------------------------------------------------------------
 *      + RTPMPInitialize() implementation
 * ---------------------------------------------------------------------------
 *
 *  Prepare to packetize sample data.  This implementation initializes
 *  instance variables that represent the packetization state.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_Initialize(
    RTPMPIMAAudioInstanceData **    inGlobals,
    SInt32                          inFlags )
{
    ComponentResult     theResult;
    
    
    if( CallComponentCanDo( ( **inGlobals ).itsBase, kRTPMPInitializeSelect ) )
        theResult = RTPMPInitialize( ( **inGlobals ).itsBase, inFlags );
    else
        theResult = noErr;
    
    if( theResult == noErr )
    {
        ( **inGlobals ).itsExpectedTimestamp = 0;
        ( **inGlobals ).itsPacketBuilder = NULL;
        ( **inGlobals ).itsPayloadSizeLimit = __kDefaultPacketSizeLimit;
        ( **inGlobals ).itsPayloadDurationLimit = __kDefaultPacketDurationLimit;
        ( **inGlobals ).itsInterleaveGroupFrameCount = 0;
        
        IMAAudioQueueInitialize( &( **inGlobals ).itsAudioQueue );
        
        ( **inGlobals ).itsInitialized = true;
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPPreflightMedia() implementation
 * ---------------------------------------------------------------------------
 *
 *  Determine whether the packetizer can packetize data described by the
 *  given SampleDescription.  This implementation verifies that the sample
 *  data is in IMA Audio format.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_PreflightMedia(
    RTPMPIMAAudioInstanceData **    inGlobals,
    OSType                          inMediaType,
    SampleDescriptionHandle         inSampleDescription )
{
#pragma unused( inGlobals )
    
    ComponentResult         theResult;
    SoundDescriptionHandle  theDescription;
    
    
    theDescription = REINTERPRET_CAST( SoundDescriptionHandle )( inSampleDescription );
    
    if(
        inMediaType != SoundMediaType  ||
        ( **theDescription ).dataFormat != kIMACompression )
    {
        theResult = qtsUnsupportedDataTypeErr;
    }
    
    else
    {
        theResult = noErr;
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPSetSampleData() implementation
 * ---------------------------------------------------------------------------
 *
 *  Use the instance's RTPPacketBuilder to packetize the sample data described
 *  by the RTPMPSampleDataParams parameter.  The sample time of the data in
 *  successive calls is non-decreasing, but could be non-contiguous--that is,
 *  the data might have gaps.  This implementation guarantees that any queued
 *  data is always contiguous by flushing when it detects non-contiguous data.
 *  
 *  The RTPMPSampleDataParams structure includes a SampleDescription.  This
 *  implementation calls __UpdateSampleDescription() to detect when the
 *  SampleDescription changes and to make any updates necessary to accomodate
 *  the new SampleDescription.
 *
 *  The function then queues the sample data parameters and calls
 *  __ProcessAudioQueue() to packetize queued sample data.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_SetSampleData(
    RTPMPIMAAudioInstanceData **    inGlobals,
    const RTPMPSampleDataParams *   inSampleData,
    SInt32 *                        outFlags )
{
    ComponentResult     theResult;
    SInt32              theFlags;
    
    
    /* Ignore requests that have no data */
    
    if( inSampleData->dataLength )
    {
        /* Flush before queueing non-contiguous data. */
        
        if( inSampleData->timeStamp !=  ( **inGlobals ).itsExpectedTimestamp )
            theResult = RTPMPFlush( ( **inGlobals ).itsFinalDerivation, 0, &theFlags );
        else
            theResult = noErr;
        
        if( theResult == noErr )
        {
            theResult = __UpdateSampleDescription( inGlobals, inSampleData );
            
            if( theResult == noErr )
            {
                __LockInstanceData( inGlobals );
                IMAAudioQueueEnqueue( &( **inGlobals ).itsAudioQueue, inSampleData );
                __UnlockInstanceData( inGlobals );
                
                theResult = __ProcessAudioQueue( inGlobals, __kDontFlush );
                
                if( theResult == noErr )
                {
                    ( **inGlobals ).itsExpectedTimestamp =
                        inSampleData->timeStamp + __SampleBlockDuration( inSampleData );
                }
            }
        }
    }
    
    *outFlags = 0;
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPFlush() implementation
 * ---------------------------------------------------------------------------
 *
 *  Finish any packetization in progress.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_Flush(
    RTPMPIMAAudioInstanceData **    inGlobals,
    SInt32                          inFlags,
    SInt32 *                        outFlags )
{
    ComponentResult     theResult;
    
    
    theResult = __ProcessAudioQueue( inGlobals, __kFlush );
    
    __LockInstanceData( inGlobals );
    IMAAudioQueueFlush( &( **inGlobals ).itsAudioQueue );
    __UnlockInstanceData( inGlobals );
    
    ( **inGlobals ).itsExpectedTimestamp = 0;
    
    if(
        theResult == noErr  &&
        CallComponentCanDo( ( **inGlobals ).itsBase, kRTPMPFlushSelect ) )
    {
        theResult = RTPMPFlush( ( **inGlobals ).itsBase, inFlags, outFlags );
    }
    
    else
    {
        *outFlags = 0;
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPReset() implementation
 * ---------------------------------------------------------------------------
 *
 *  Abort any packetization in progress and prepare to packetize a new,
 *  possibly unrelated, data stream.  This implementation flushes its audio
 *  data queue, invalidates its cached payload attributes, and resets its
 *  base.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_Reset(
    RTPMPIMAAudioInstanceData **    inGlobals,
    SInt32                          inFlags )
{
    ComponentResult     theResult;
    
    
    __LockInstanceData( inGlobals );
    IMAAudioQueueFlush( &( **inGlobals ).itsAudioQueue );
    __UnlockInstanceData( inGlobals );
    
    ( **inGlobals ).itsExpectedTimestamp = 0;
    ( **inGlobals ).itsPayloadAttributesInitialized = false;
    
    if( CallComponentCanDo( ( **inGlobals ).itsBase, kRTPMPResetSelect ) )
        theResult = RTPMPReset( ( **inGlobals ).itsBase, inFlags );
    else
        theResult = noErr;
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPGetInfo() implementation
 * ---------------------------------------------------------------------------
 *
 *  Return the information indicated by the selector.  This implemenation
 *  computes a result for the following selectors and delegates all others to
 *  its base.
 *
 *
 *      kRTPMPPayloadTypeInfo   ioParams points to an RTPMPPayloadTypeParams
 *                              structure.  This implementation fills in this
 *                              structure to indicate it uses a dynamic AVP
 *                              payload type.  It copies its payload encoding
 *                              name to a buffer described by this structure.
 *
 *      kRTPMPRTPTimeScaleInfo  ioParams points to a TimeScale where the
 *                              implementation returns the clock rate, in
 *                              Hertz, to be used for RTP timestamps.
 *
 *      kRTPMPMinPayloadSize    ioParams points to a UInt32 where the
 *                              implementation returns the number of octets
 *                              needed for the fixed header and payload
 *                              description used by this packetizer.
 *
 *      kRTPMPPayloadNameInfo   ioParams points to a Str255 where the
 *                              implementation returns a human-readable name
 *                              for the payload encoding used by this
 *                              packetizer.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_GetInfo(
    RTPMPIMAAudioInstanceData **    inGlobals,
    OSType                          inSelector,
    void *                          ioParams )
{
    ComponentResult             theError = noErr;
    RTPMPPayloadTypeParams *    thePayloadInfo;
    Str255                      theEncodingName;
    
    
    switch( inSelector )
    {
        case kRTPMPPayloadTypeInfo:
            thePayloadInfo = STATIC_CAST( RTPMPPayloadTypeParams * )( ioParams );
            thePayloadInfo->flags = kRTPPayloadTypeDynamicFlag;
            thePayloadInfo->payloadNumber = kRTPPayload_Unknown;
            
            theError =
                GetComponentIndString(
                    REINTERPRET_CAST( Component )( ( **inGlobals ).itself ),
                    theEncodingName, kRTPMPIMAAudioStringListResource,
                    kRTPMPIMAAudioProtocolEncodingString );
            
            if( !theError )
            {
                if( thePayloadInfo->nameLength < ( theEncodingName[ 0 ] + 1 ) )
                {
                    theError = paramErr;
                }
                
                else
                {
                    BlockMoveData(
                        &theEncodingName[ 1 ], thePayloadInfo->payloadName,
                        theEncodingName[ 0 ] );
                    
                    thePayloadInfo->payloadName[ theEncodingName[ 0 ] ] = '\0';
                }
                
                thePayloadInfo->nameLength = theEncodingName[ 0 ] + 1;
            }
        break;
        
        
        case kRTPMPRTPTimeScaleInfo:
            *STATIC_CAST( TimeScale * )( ioParams ) = kIMAAudioPayloadRTPTimeScale;
        break;
        
        
        case kRTPMPMinPayloadSize:
            *STATIC_CAST( UInt32 * )( ioParams ) = sizeof( IMAAudioPayload );
        break;
        
        
        case kRTPMPPayloadNameInfo:
            theError =
                GetComponentIndString(
                    REINTERPRET_CAST( Component )( ( **inGlobals ).itself ),
                    STATIC_CAST( StringPtr )( ioParams ),
                    kRTPMPIMAAudioStringListResource, kRTPMPIMAAudioHIEncodingString );
        break;
        
        
        case kRTPMPMinPacketDuration:
            theError = qtsBadSelectorErr;
        break;
        
        
        case kRTPMPRequiredSampleDescriptionInfo:
        case kRTPMPSuggestedRepeatPktCountInfo:
        case kRTPMPSuggestedRepeatPktSpacingInfo:
        case kRTPMPMaxPartialSampleSizeInfo:
        case kRTPMPPreferredBufferDelayInfo:
        default:
            theError = RTPMPGetInfo( ( **inGlobals ).itsBase, inSelector, ioParams );
        break;
    }
    
    return( theError );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPSetTimeScale() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_SetTimeScale(
    RTPMPIMAAudioInstanceData **    inGlobals,
    TimeScale                       inTimeScale )
{
    ( **inGlobals ).itsMediaTimeScale = inTimeScale;
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPGetTimeScale() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_GetTimeScale(
    RTPMPIMAAudioInstanceData **    inGlobals,
    TimeScale *                     outTimeScale )
{
    *outTimeScale = ( **inGlobals ).itsMediaTimeScale;
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPSetTimeBase() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_SetTimeBase(
    RTPMPIMAAudioInstanceData **    inGlobals,
    TimeBase                        inTimeBase )
{
    ( **inGlobals ).itsMediaTimeBase = inTimeBase;
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPGetTimeBase() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_GetTimeBase(
    RTPMPIMAAudioInstanceData **    inGlobals,
    TimeBase *                      outTimeBase )
{
    *outTimeBase = ( **inGlobals ).itsMediaTimeBase;
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPHasCharacteristic() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_HasCharacteristic(
    RTPMPIMAAudioInstanceData **    inGlobals,
    OSType                          inSelector,
    Boolean *                       outHasIt )
{
    ComponentResult     theResult = noErr;
    
    
    switch( inSelector )
    {
        case kRTPMPNoSampleDataRequiredCharacteristic:
        case kRTPMPHasUserSettingsDialogCharacteristic:
            *outHasIt = true;
        break;
        
        
        case kRTPMPPartialSamplesRequiredCharacteristic:
        case kRTPMPPrefersReliableTransportCharacteristic:
        case kRTPMPRequiresOutOfBandDimensionsCharacteristic:
            *outHasIt = false;
        break;
        
        
        default:
            theResult =
                RTPMPHasCharacteristic( ( **inGlobals ).itsBase, inSelector, outHasIt );
        break;
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPSetPacketBuilder() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_SetPacketBuilder(
    RTPMPIMAAudioInstanceData **    inGlobals,
    ComponentInstance               inPacketBuilder )
{
    ComponentResult     theError;
    SInt32              theFlags;
    
    
    if( ( **inGlobals ).itsInitialized  &&  ( **inGlobals ).itsPacketBuilder )
        theError = RTPMPFlush( ( **inGlobals ).itsFinalDerivation, 0, &theFlags );
    else
        theError = noErr;
    
    if( !theError )
        ( **inGlobals ).itsPacketBuilder = inPacketBuilder;
    
    return( theError );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPGetPacketBuilder() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_GetPacketBuilder(
    RTPMPIMAAudioInstanceData **    inGlobals,
    ComponentInstance *             outPacketBuilder )
{
    *outPacketBuilder = ( **inGlobals ).itsPacketBuilder;
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPSetMediaType() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_SetMediaType(
    RTPMPIMAAudioInstanceData **    inGlobals,
    OSType                          inMediaType )
{
#pragma unused( inGlobals )
    ComponentResult     theResult;
    
    
    if( inMediaType == SoundMediaType )
        theResult = noErr;
    else
        theResult = qtsBadDataErr;
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPGetMediaType() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_GetMediaType(
    RTPMPIMAAudioInstanceData **    inGlobals,
    OSType *                        outMediaType )
{
#pragma unused( inGlobals )
 
    *outMediaType = SoundMediaType;
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPSetMaxPacketSize() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_SetMaxPacketSize(
    RTPMPIMAAudioInstanceData **    inGlobals,
    UInt32                          inMaxPacketSize )
{
    ( **inGlobals ).itsPayloadSizeLimit = inMaxPacketSize;
    
    __UpdateLimits( inGlobals );
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPGetMaxPacketSize() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_GetMaxPacketSize(
    RTPMPIMAAudioInstanceData **    inGlobals,
    UInt32 *                        outMaxPacketSize )
{
    *outMaxPacketSize = ( **inGlobals ).itsPayloadSizeLimit;
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPSetMaxPacketDuration() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_SetMaxPacketDuration(
    RTPMPIMAAudioInstanceData **    inGlobals,
    UInt32                          inMaxPacketDuration )
{
    ( **inGlobals ).itsPayloadDurationLimit = inMaxPacketDuration;
    
    __UpdateLimits( inGlobals );
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPGetMaxPacketDuration() implementation
 * ---------------------------------------------------------------------------
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_GetMaxPacketDuration(
    RTPMPIMAAudioInstanceData **    inGlobals,
    UInt32 *                        outMaxPacketDuration )
{
    *outMaxPacketDuration = ( **inGlobals ).itsPayloadDurationLimit;
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPDoUserDialog() implementation
 * ---------------------------------------------------------------------------
 *
 *  Present a dialog of user-adjustable options and update instance variables
 *  according to the user's choices.  This implementation allows the user to
 *  select an interleave count.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_DoUserDialog(
    RTPMPIMAAudioInstanceData **    inGlobals,
    ModalFilterUPP                  inFilterUPP,
    Boolean *                       outCanceled )
{
    ComponentResult     theResult;
    ModalFilterUPP      theModalFilter;
    short               theItem;
    DialogPtr           theDialog;
    short               theSavedResources;
    short               theResources;
    short               theItemType;
    Rect                theItemRect;
    ControlHandle       theInterleavingMenu;
    UInt32              theInterleaveCount;
    
    
    theModalFilter = NewModalFilterProc( __SettingsDialogFilter );
    
    if( theModalFilter )
    {
        theSavedResources = CurResFile();
        
        theResources =
            OpenComponentResFile( REINTERPRET_CAST( Component )( ( **inGlobals ).itself ) );
        
        if( theResources > 0 )
        {
            theDialog =
                GetNewDialog(
                    kRTPMPIMAAudioSettingsDialogResource, NULL,
                    REINTERPRET_CAST( WindowPtr )( -1L ) );
            
            if( theDialog )
            {
                SetDialogDefaultItem( theDialog, __kOKButton );
                SetDialogCancelItem( theDialog, __kCancelButton );
                
                GetDialogItem(
                    theDialog, __kInterleavingMenu, &theItemType,
                    REINTERPRET_CAST( Handle * )( &theInterleavingMenu ), &theItemRect );
                
                theInterleaveCount =
                    IMAAudioPayloadInterleaveCount( &( **inGlobals ).itsPayloadAttributes );
                
                if( theInterleaveCount > 1 )
                    theInterleaveCount++;
                
                SetControlValue( theInterleavingMenu, theInterleaveCount );
                HiliteControl( theInterleavingMenu, kControlNoPart );
                
                MacShowWindow( theDialog );
                
                if( theModalFilter )
                    SetWRefCon( theDialog, REINTERPRET_CAST( long )( inFilterUPP ) );
                else
                    theModalFilter = inFilterUPP;
                
                do
                {
                    ModalDialog( theModalFilter, &theItem );
                }
                while( theItem != __kOKButton  && theItem != __kCancelButton );
                
                if( theItem == __kOKButton )
                {
                    theInterleaveCount = GetControlValue( theInterleavingMenu );
                    
                    if( theInterleaveCount > 2 )
                        --theInterleaveCount;
                    
                    IMAAudioPayloadSetInterleaving(
                        &( **inGlobals ).itsPayloadAttributes, theInterleaveCount, 0 );
                }
                
                *outCanceled = ( theItem == __kCancelButton );
                
                DisposeDialog( theDialog );
            }
            
            else
            {
                theResult = ResError();
                
                if( theResult == noErr )
                    theResult = memFullErr;
            }
            
            UseResFile( theSavedResources );
            CloseComponentResFile( theResources );
            
            theResult = noErr;
        }
        
        else
        {
            theResult = ResError();
            
            if( theResult == noErr )
                theResult = memFullErr;
        }
        
        DisposeRoutineDescriptor( theModalFilter );
    }
    
    else
    {
        theResult = MemError();
        
        if( theResult == noErr )
            theResult = memFullErr;
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPSetSettingsFromAtomContainerAtAtom() implementation
 * ---------------------------------------------------------------------------
 *
 *  Update instance variables according to the saved user settings in the
 *  given atom container.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_SetSettingsFromAtomContainerAtAtom(
    RTPMPIMAAudioInstanceData **    inGlobals,
    QTAtomContainer                 inAtomContainer,
    QTAtom                          inParentAtom )
{
    ComponentResult             theResult = noErr;
    QTAtom                      theAtom;
    QTAtomID                    theAtomID;
    Ptr                         theAtomData;
    long                        theDataSize;
    __TStoredInterleaveCount    theInterleaveCount;
    
    
    if( inAtomContainer )
    {
        theAtom =
            QTFindChildByIndex(
                inAtomContainer, inParentAtom, __kInterleaveCountAtomType, 1, &theAtomID );
        
        if( theAtom )
        {
            QTLockContainer( inAtomContainer );
            
            QTGetAtomDataPtr(
                inAtomContainer, theAtom, &theDataSize, &theAtomData );
            
            if( theDataSize == sizeof( theInterleaveCount ) )
            {
                /* Convert from storage byte order to platform byte order. */
                
                theInterleaveCount =
                    EndianU32_BtoN(
                        *REINTERPRET_CAST( __TStoredInterleaveCount * )(
                            theAtomData ) );
                
                if( theInterleaveCount > kIMAAudioPayloadInterleaveCountLimit )
                {
                    theResult = qtsBadDataErr;
                }
                
                else
                {
                    IMAAudioPayloadSetInterleaving(
                        &( **inGlobals ).itsPayloadAttributes, theInterleaveCount, 0 );
                }
            }
            
            else
            {
                theResult = qtsBadDataErr;
            }
            
            QTUnlockContainer( inAtomContainer );
        }
    }
    
    else
    {
        theResult = paramErr;
    }
 
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPGetSettingsIntoAtomContainerAtAtom() implementation
 * ---------------------------------------------------------------------------
 *
 *  Return current settings of user-adjustable options in the given atom
 *  container.
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_GetSettingsIntoAtomContainerAtAtom(
    RTPMPIMAAudioInstanceData **    inGlobals,
    QTAtomContainer                 inOutAtomContainer,
    QTAtom                          inParentAtom )
{
    ComponentResult             theResult;
    QTAtom                      theAtom;
    QTAtomID                    theAtomID;
    __TStoredInterleaveCount    theInterleaveCount;
    
    
    if( inOutAtomContainer )
    {
        /* Convert from platform byte order to storage byte order. */
        
        theInterleaveCount =
            EndianU32_NtoB(
                IMAAudioPayloadInterleaveCount( &( **inGlobals ).itsPayloadAttributes ) );
        
        
        /* If the atom already exists, update it.  Otherwise, insert a new one. */
        
        theAtom =
            QTFindChildByIndex(
                inOutAtomContainer, inParentAtom, __kInterleaveCountAtomType, 1, &theAtomID );
        
        if( theAtom )
        {
            theResult =
                QTSetAtomData(
                    inOutAtomContainer, theAtom, sizeof( theInterleaveCount ),
                    &theInterleaveCount );
        }
        
        else
        {
            theResult =
                QTInsertChild(
                    inOutAtomContainer, inParentAtom, __kInterleaveCountAtomType,
                    0 /* id */, 0 /* index */, sizeof( theInterleaveCount ),
                    &theInterleaveCount, NULL /* atom */);
        }
    }
    
    else
    {
        theResult = paramErr;
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPMPGetMediaSettingsAsText() implementation
 * ---------------------------------------------------------------------------
 *
 *  Return current settings of user-adjustable options as a string.  This
 *  implementation returns the interleave count as colon-delimited name-value
 *  pair:
 *
 *      "Interleaving: <interleave count>"
 *
 *          or
 *
 *      "Interleaving: None"
 *
 */
 
EXTERN_API( ComponentResult )
RTPMPIMAAudio_GetSettingsAsText(
    RTPMPIMAAudioInstanceData **    inGlobals,
    Handle *                        text )
{
    ComponentResult     theResult;
    Str255              theSettings;
    UInt32              theInterleaveCount;
    unsigned char       theSavedCharacter;
    int                 theLength;
    
    
    theResult =
        GetComponentIndString(
            REINTERPRET_CAST( Component )( ( **inGlobals ).itself ), theSettings,
            kRTPMPIMAAudioStringListResource, kRTPMPIMAAudioSettingsString );
    
    if( theResult == noErr )
    {
        theLength = theSettings[ 0 ];
        theSavedCharacter = theSettings[ theLength ];
        
        theInterleaveCount =
            IMAAudioPayloadInterleaveCount( &( **inGlobals ).itsPayloadAttributes );
        
        if( theInterleaveCount > 1 )
        {
            NumToString( theInterleaveCount, &theSettings[ theLength ] );
        }
        
        else
        {
            GetComponentIndString(
                REINTERPRET_CAST( Component )( ( **inGlobals ).itself ),
                &theSettings[ theLength ], kRTPMPIMAAudioStringListResource,
                kRTPMPIMAAudioNoInterleavingString );
        }
        
        theSettings[ 0 ] += theSettings[ theLength ];
        theSettings[ theLength ] = theSavedCharacter;
        theLength = theSettings[ 0 ];
        
        *text = NewHandle( theLength );
        
        if( *text )
            BlockMoveData( &theSettings[ 1 ], **text, theLength );
        else
            theResult = MemError();
    }
    
    else
    {
        *text = 0;
    }
    
    return( theResult );
}