ComponentVideoRTP/Sources/RTPRssmComponentVideo.c

/*
    File:       RTPRssmComponentVideo.c
 
    Contains:   Definition of Component Video 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.
        
        RTPRssmGetInfo()                Return the requested information.
        
        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 "RTPRssmComponentVideo.h"
#include "RTPRssmComponentVidResources.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()              RTPRssmComponentVideo_
#define RTPRSSM_GLOBALS()               RTPRssmComponentVideoInstanceData **
 
#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         "RTPRssmComponentVideoDispatch.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
 * ---------------------------------------------------------------------------
 */
 
enum
{
    __kDefaultWidth             = 160,
    __kDefaultHeight            = 120,
    __kHorizontalResolution     = 72,
    __kVertivalResolution       = 72,
    __kDepth                    = 24
};
 
 
 
typedef RTPRSSM_GLOBALS()   __InstanceData;
 
 
 
/* ---------------------------------------------------------------------------
 *      __FrameDataSize()
 * ---------------------------------------------------------------------------
 *
 *  Compute the number of octets in one frame of Component Video data having
 *  the given dimensions.
 *  
 *  YUV 4:2:2 encoding uses four octets to represent a pair of pixels.
 *  The number of octets in a frame is therefore
 *  
 *      padded-width x height x 2 octets
 *  
 *  where padded-width is the row width in pixels padded to an
 *  even number.
 *
 */
 
static
UInt32
__FrameDataSize(
    UInt16  inWidth,
    UInt16  inHeight )
{
    return( ( ( ( inWidth + 1 ) & ( ~1L ) ) * inHeight ) << 1 );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __ReleaseChunk()
 * ---------------------------------------------------------------------------
 *
 *  Release any previously saved chunk.
 *
 */
 
static
SHChunkRecord *
__ReleaseChunk(
    __InstanceData      inGlobals )
{
    SHChunkRecord *     theResult = ( **inGlobals ).itsSavedChunk;
    
    
    if( ( **inGlobals ).itsSavedChunk )
    {
        RTPRssmDecrChunkRefCount(
            ( **inGlobals ).itsFinalDerivation, ( **inGlobals ).itsSavedChunk );
        
        ( **inGlobals ).itsSavedChunk = NULL;
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __SaveChunk()
 * ---------------------------------------------------------------------------
 *
 *  Release any previously saved chunk and save the given chunk.
 *
 */
 
static
ComponentResult
__SaveChunk(
    __InstanceData      inGlobals,
    SHChunkRecord *     inChunk )
{
    ComponentResult     theResult;
    SHChunkRecord *     thePreviousChunk;
    
    
    thePreviousChunk = __ReleaseChunk( inGlobals );
    
    if( inChunk )
    {
        theResult = RTPRssmIncrChunkRefCount( ( **inGlobals ).itsFinalDerivation, inChunk );
        
        if( theResult == noErr )
            ( **inGlobals ).itsSavedChunk = inChunk;
        else
            theResult = __SaveChunk( inGlobals, thePreviousChunk );
    }
    
    else
    {
        theResult = noErr;
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __GetNewSampleDescription()
 * ---------------------------------------------------------------------------
 *
 *  Create a new SampleDescription for this reassembler's payload format using
 *  the given dimensions.
 *
 */
 
static
ComponentResult
__GetNewSampleDescription(
    __InstanceData              inGlobals,
    UInt16                      inWidth,
    UInt16                      inHeight,
    SampleDescriptionHandle *   outDescription )
{
    ComponentResult         theResult = noErr;
    ImageDescriptionHandle  theDescription;
    Str255                  theCodecName;
    
    
    theDescription =
        REINTERPRET_CAST( ImageDescriptionHandle )(
            NewHandleClear( sizeof( **theDescription ) ) );
    
    if( theDescription )
    {
        *outDescription = REINTERPRET_CAST( SampleDescriptionHandle )( theDescription );
        
        ( **theDescription ).idSize = sizeof( **theDescription );
        ( **theDescription ).cType = kComponentVideoDataFormat;
        ( **theDescription ).version = 1;
        ( **theDescription ).revisionLevel = 1;
        ( **theDescription ).vendor = kComponentManufactureType;
        ( **theDescription ).temporalQuality = codecNormalQuality;
        ( **theDescription ).spatialQuality = codecNormalQuality;
        ( **theDescription ).width = inWidth;
        ( **theDescription ).height = inHeight;
        ( **theDescription ).hRes = Long2Fix( __kHorizontalResolution );
        ( **theDescription ).vRes = Long2Fix( __kVertivalResolution );
        ( **theDescription ).dataSize = 0;
        ( **theDescription ).frameCount = 1;
        ( **theDescription ).depth = __kDepth;
        ( **theDescription ).clutID = -1;
        
        theCodecName[ 0 ] = '\0';   /* in case string resource doesn't load */
        
        GetComponentIndString(
            REINTERPRET_CAST( Component )( ( **inGlobals ).itself ),
            theCodecName, kRTPRssmComponentVideoStringListResource,
            kRTPRssmComponentVideoCodecNameString );
        
        BlockMoveData(
            theCodecName, ( **theDescription ).name, sizeof( ( **theDescription ).name ) );
    }
    
    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
ComponentVideoPayload *
__GetPayload(
    RTPRssmPacket *     inPacket )
{
    return( 
        REINTERPRET_CAST( ComponentVideoPayload * )(
            &inPacket->streamBuffer->rptr[ inPacket->transportHeaderLength ] ) );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __GetData()
 * ---------------------------------------------------------------------------
 *
 *  Return a pointer to a packet's image data.
 *
 */
 
static
UInt8 *
__GetData(
    RTPRssmPacket *     inPacket )
{
    return( 
        &inPacket->streamBuffer->rptr[
                inPacket->transportHeaderLength + inPacket->payloadHeaderLength ] );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      __FillMissingData()
 * ---------------------------------------------------------------------------
 *
 *  If the source chunk exists, copy the specified interval from the source
 *  chunk to the target chunk.  Otherwise, set the specified interval of the
 *  target chunk to black.
 *
 */
 
static
void
__FillMissingData(
    UInt32                  inOffset,
    UInt32                  inLength,
    const SHChunkRecord *   inSourceChunk,
    SHChunkRecord *         inTargetChunk )
{
    if( inSourceChunk )
    {
        BlockMoveData(
            &inSourceChunk->dataPtr[ inOffset ],
            CONST_CAST( UInt8 * )( &inTargetChunk->dataPtr[ inOffset ] ),
            inLength );
    }
    
    else
    {
        memset(
            CONST_CAST( UInt8 * )( &inTargetChunk->dataPtr[ inOffset ] ),
            0, inLength );
    }
}
 
 
 
#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 )
RTPRssmComponentVideo_Open(
    __InstanceData      inGlobals,
    ComponentInstance   self )
{
    ComponentResult     theResult = noErr;
    RTPReassembler      theBase;
    
    
    inGlobals =
        REINTERPRET_CAST( __InstanceData )( 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 )
RTPRssmComponentVideo_Close(
    __InstanceData      inGlobals,
    ComponentInstance   self )
{
#pragma unused( self )
    
    if( inGlobals )
    {
        __ReleaseChunk( inGlobals );
        
        if( ( **inGlobals ).itsBase )
            CloseComponent( ( **inGlobals ).itsBase );
        
        DisposeHandle( REINTERPRET_CAST( Handle )( inGlobals ) );
    }
    
    return( noErr );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + CallComponentVersion() implementation
 * ---------------------------------------------------------------------------
 *
 *  Return the instance's version.
 *
 */
 
EXTERN_API( ComponentResult )
RTPRssmComponentVideo_Version(
    __InstanceData      inGlobals )
{
#pragma unused( inGlobals )
    
    return( kComponentVersion );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + CallComponentTarget() implementation
 * ---------------------------------------------------------------------------
 *
 *  Update the instance's inheritance graph with a new most-derived instance.
 *
 */
 
EXTERN_API( ComponentResult )
RTPRssmComponentVideo_Target(
    __InstanceData      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 )
RTPRssmComponentVideo_Initialize(
    __InstanceData          inGlobals,
    RTPRssmInitParams *     inInitParams )
{
    ComponentResult             theResult = noErr;
    SampleDescriptionHandle     theDescription;
    
    
    if( CallComponentCanDo( ( **inGlobals ).itsBase, kRTPRssmInitializeSelect ) )
        theResult = RTPRssmInitialize( ( **inGlobals ).itsBase, inInitParams );
    
    if( theResult == noErr )
    {
        ( **inGlobals ).itsSavedChunk = NULL;
        
        /*  The reassembler hasn't received a payload description yet, but it can't
            create a new StreamHandler without a SampleDescription.  The reassembler
            initializes its payload attributes with a default description that it
            will use to create a new StreamHandler, then immediately invalidates the
            description.  This causes the reassembler to search incoming network
            packets for a payload description.  If the incoming payload description
            matches the default, the StreamHandler need not be updated. */
        
        ComponentVideoPayloadInitialize(
            &( **inGlobals ).itsPayloadAttributes, __kDefaultWidth, __kDefaultHeight );
        
        ComponentVideoPayloadSetDescription( &( **inGlobals ).itsPayloadAttributes, 0, 0 );
        
        ComponentVideoPayloadSetOffset(
            &( **inGlobals ).itsPayloadAttributes,
            __FrameDataSize( __kDefaultWidth, __kDefaultHeight ) );
        
        theResult =
            __GetNewSampleDescription(
                inGlobals, __kDefaultWidth, __kDefaultHeight, &theDescription );
        
        if( theResult == noErr )
        {
            theResult =
                RTPRssmNewStreamHandler(
                    ( **inGlobals ).itsFinalDerivation, VideoMediaType,
                    theDescription, kComponentVideoRTPTimeScale, 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 packets to the StreamHandler.  The chunk size for
 *  this reassembler is the image data size for one frame. The size cannot be
 *  computed by summing the data sizes of network packets received, since some
 *  packets might be lost.  Overriding RTPRssmComputeChunkSize() gives the
 *  reassembler a chance to compute the chunk size from the current payload
 *  description.
 *
 *  If the description of the received data is not known, this function
 *  searches the given packet list for a description.  The payload description
 *  is not known if no payload description is cached, or if the description
 *  seed of the incoming network packets doesn't match that of the cached
 *  description.
 *  
 *  (The payload description is also unknown if too many packets have been
 *  lost since the last frame.  Specifically, if the description seeds could
 *  have recycled since the last packet received, then the cached description
 *  is not guaranteed to match the current description, even if it shares the
 *  same seed.  However, since the payload format for this reassembler cycles
 *  through 2^31 seeds, this implementation does not bother to detect this
 *  condition.)
 *  
 *  If a description is found, this function caches the payload description,
 *  updates the StreamHandler's SampleDescription, and updates the Offset
 *  field of the instance's payload attributes with the frame data size
 *  required for that SampleDescription.
 *  
 *  This function always returns the pre-computed data size stored in the
 *  Offset field of the instance's payload attributes instance variable.
 *  Even when the current payload description cannot be determined, the
 *  Offset field gives the chunk size appropriate for the StreamHandler's
 *  current SampleDescription.
 *
 */
 
EXTERN_API( ComponentResult )
RTPRssmComponentVideo_ComputeChunkSize(
    __InstanceData      inGlobals,
    RTPRssmPacket *     inPacketListHead,
    SInt32              inFlags,
    UInt32 *            outChunkDataSize )
{
#pragma unused( inFlags )
 
    ComponentResult             theResult = noErr;
    RTPRssmPacket *             thePacket;
    ComponentVideoPayload *     thePayload;
    UInt16                      theWidth;
    UInt16                      theHeight;
    SampleDescriptionHandle     theDescription;
    
    
    thePayload = __GetPayload( inPacketListHead );
    
    if(
        ComponentVideoPayloadDescriptionSeed( &( **inGlobals ).itsPayloadAttributes ) !=
            ComponentVideoPayloadDescriptionSeed( thePayload ) )
    {
        ComponentVideoPayloadSetDescription( &( **inGlobals ).itsPayloadAttributes, 0, 0 );
    }
    
    
    /*  If the instance doesn't have a cached payload description, iterate through the
        packet list until it caches an incoming payload description. */
    
    for(
        thePacket = inPacketListHead;
        thePacket  &&
            !ComponentVideoPayloadHasDescription( &( **inGlobals ).itsPayloadAttributes );
        thePacket = thePacket->next )
    {
        thePayload = __GetPayload( thePacket );
        
        if( ComponentVideoPayloadHasDescription( thePayload ) )
        {
            theWidth = ComponentVideoPayloadWidth( thePayload );
            theHeight = ComponentVideoPayloadHeight( thePayload );
            
            
            /*  If the imcoming payload description matches the description last used,
                simply cache the incoming description.  If they don't match, update
                the StreamHandler's SampleDescription, update the Offset field of the
                reassembler's payload attributes, and release any saved chunk as well. */
            
            if(
                theWidth ==
                    ComponentVideoPayloadWidth( &( **inGlobals ).itsPayloadAttributes )  &&
                theHeight ==
                    ComponentVideoPayloadHeight( &( **inGlobals ).itsPayloadAttributes ) )
            {
                ComponentVideoPayloadCopyDescription(
                    &( **inGlobals ).itsPayloadAttributes, thePayload );
            }
            
            else
            {
                theResult =
                    __GetNewSampleDescription(
                        inGlobals, theWidth, theHeight, &theDescription );
                
                if( theResult == noErr )
                {
                    ComponentVideoPayloadSetOffset(
                        &( **inGlobals ).itsPayloadAttributes,
                        __FrameDataSize( theWidth, theHeight ) );
                    
                    ComponentVideoPayloadCopyDescription(
                        &( **inGlobals ).itsPayloadAttributes, thePayload );
                    
                    __ReleaseChunk( inGlobals );
                    
                    theResult =
                        RTPRssmSetSampleDescription(
                            ( **inGlobals ).itsFinalDerivation, theDescription );
                    
                    DisposeHandle( REINTERPRET_CAST( Handle )( theDescription ) );
                }
            }
        }
    }
    
    /*  The current data size for incoming frames is always stored in the Offset
        field of the reassembler's payload attributes instance variable. */
    
    *outChunkDataSize =
        ComponentVideoPayloadOffset( &( **inGlobals ).itsPayloadAttributes );
    
    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
 *  variable-length header that immediately precedes the image data.
 *  Overriding RTPRssmAdjustPacketParams() gives the reassembler a chance to
 *  update the RTPRssmPacket data structure for each packet to reflect its
 *  payload header length and image data length.
 *
 */
 
EXTERN_API( ComponentResult )
RTPRssmComponentVideo_AdjustPacketParams(
    __InstanceData      inGlobals,
    RTPRssmPacket *     inPacket,
    SInt32              inFlags )
{
#pragma unused( inGlobals, inFlags )
    
    UInt32                      theHeaderSize;
    ComponentVideoPayload *     thePayload;
    
    
    thePayload = __GetPayload( inPacket );
    
    if( ComponentVideoPayloadHasDescription( thePayload ) )
        theHeaderSize = sizeof( *thePayload );
    else
        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
 *  in the frame the image data from each payload belongs.  This function
 *  iterates through the packets, copying the data to the correct position in
 *  the chunk.  The packet list is ordered by the base reassembler.  Where
 *  packets are missing, this function calls __FillMissingData(), defined
 *  above, to fill in lost packet data with data from the previous frame.
 *
 *  If the payload description has changed without being detected, the image
 *  data might overflow or underflow the buffer.  This function detects these
 *  conditions and invalidates the cached payload description.
 *  
 */
 
EXTERN_API( ComponentResult )
RTPRssmComponentVideo_CopyDataToChunk(
    __InstanceData      inGlobals,
    RTPRssmPacket *     inPacketListHead,
    UInt32              inMaxChunkDataSize,
    SHChunkRecord *     inChunk,
    SInt32              inFlags )
{
#pragma unused( inFlags )
    
    ComponentResult             theResult = noErr;
    RTPRssmPacket *             thePacket;
    ComponentVideoPayload *     thePayload;
    UInt32                      theExpectedOffset;
    UInt32                      theOffset;
    
    
    if(
        inMaxChunkDataSize <
            ComponentVideoPayloadOffset( &( **inGlobals ).itsPayloadAttributes ) )
    {
        /* This should never happen. */
        
        theResult = buffersTooSmall;
    }
    
    
    /* The first packet should contain data for the start of the image buffer. */
    
    theExpectedOffset = 0;
    
    for(
        thePacket = inPacketListHead; thePacket  &&  theResult == noErr;
        thePacket = thePacket->next )
    {
        thePayload = __GetPayload( thePacket );
        theOffset = ComponentVideoPayloadOffset( thePayload );
        
        if( theOffset + thePacket->dataLength <= inMaxChunkDataSize )
        {
            /*  The base reassembler keeps the packets ordered.  If a network packet's
                data doesn't immediately follow the data from the previous packet in
                the list, then one or more packets must have been lost. */
            
            if( theExpectedOffset < theOffset )
            {
                __FillMissingData(
                    theExpectedOffset, theOffset - theExpectedOffset,
                    ( **inGlobals ).itsSavedChunk, inChunk );
            }
            
            BlockMoveData(
                __GetData( thePacket ),
                CONST_CAST( UInt8 * )( &inChunk->dataPtr[ theOffset ] ),
                thePacket->dataLength );
            
            theExpectedOffset = theOffset + thePacket->dataLength;
        }
        
        else
        {
            /*  The image data for the current packet would overflow the image buffer.
                Therefore the cached payload description is inconsistent with the
                incoming packet. */
            
            ComponentVideoPayloadSetDescription(
                &( **inGlobals ).itsPayloadAttributes, 0, 0 );
        }
        
        if( !thePacket->next )
        {
            inChunk->dataSize =
                ComponentVideoPayloadOffset( &( **inGlobals ).itsPayloadAttributes );
            
            
            /*  If the last packet's data doesnt fill the rest of the image buffer, one
                or more packets might be missing.  If the RTP/AVP marker bit is set,
                indicating the last packet of the frame, then the incoming data has
                underflowed the data buffer, so the cached payload description is
                inconsistent with the incoming packet. */
            
            if( theExpectedOffset < inChunk->dataSize )
            {
                __FillMissingData(
                    theExpectedOffset, inChunk->dataSize - theExpectedOffset,
                    ( **inGlobals ).itsSavedChunk, inChunk );
                
                if( thePacket->flags & kRTPRssmPacketHasMarkerBitSet )
                {
                    ComponentVideoPayloadSetDescription(
                        &( **inGlobals ).itsPayloadAttributes, 0, 0 );
                }
            }
            
            __SaveChunk( inGlobals, inChunk );
        }
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPRssmSendPacketList() implementation
 * ---------------------------------------------------------------------------
 *
 *  Prepare to process or discard a list of packets.  This reassembler
 *  tolerates packet loss only if it knows the current payload description.
 *  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 implementation inspects the
 *  packet list to determine whether its payload description is known and
 *  updates the kRTPRssmLostSomePackets flag accordingly.  When the
 *  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 )
RTPRssmComponentVideo_SendPacketList(
    __InstanceData          inGlobals,
    RTPRssmPacket *         inPacketListHead,
    const TimeValue64 *     inLastChunkPresentationTime,
    SInt32                  inFlags )
{
    ComponentVideoPayload *     thePayload;
    RTPRssmPacket *             thePacket;
    
    
    if( inFlags & kRTPRssmLostSomePackets )
    {
        thePayload = __GetPayload( inPacketListHead );
        
        
        /*  If the current payload description is already cached, turn off the packet
            loss flag.  Otherwise, turn off the flag only if the packet list
            contains a payload description. */
        
        if(
            ComponentVideoPayloadHasDescription( &( **inGlobals ).itsPayloadAttributes )  &&
            ComponentVideoPayloadDescriptionSeed( &( **inGlobals ).itsPayloadAttributes ) ==
                ComponentVideoPayloadDescriptionSeed( thePayload ) )
        {
            inFlags &= ~kRTPRssmLostSomePackets;
        }
        
        else
        {
            for(
                thePacket = inPacketListHead;
                thePacket  &&  ( inFlags & kRTPRssmLostSomePackets );
                thePacket = thePacket->next )
            {
                thePayload = __GetPayload( thePacket );
                
                
                /*  If a payload description is found, clear the kRTPRssmLostSomePackets
                    flag.  The reassembler will compensate for lost packet data. */
                
                if( ComponentVideoPayloadHasDescription( thePayload ) )
                    inFlags &= ~kRTPRssmLostSomePackets;
            }
        }
    }
    
    
    /* Delegate the remaining work to the base reassembler. */
    
    return(
        RTPRssmSendPacketList(
            ( **inGlobals ).itsBase, inPacketListHead, inLastChunkPresentationTime,
            inFlags ) );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + RTPRssmGetInfo() implementation
 * ---------------------------------------------------------------------------
 *
 *  Return the information indicated by the selector.  This implemenation
 *  computes a result for the following selectors and delegates all others to
 *  its base.
 *
 *
 *      kQTSSourceDimensionsInfo    ioParams points to an QTSDimensionParams
 *                                  structure where the implementation returns
 *                                  the width and height from its cached
 *                                  payload description.
 *
 *      kQTSSourceClipRectInfo
 *      kQTSSourceBoundingRectInfo  ioParams points to a Rect.  The
 *                                  implementation returns a Rect whose top
 *                                  left corner is at the origin and whose
 *                                  width and height are those from its cached
  *                                 payload description.
*
 */
 
EXTERN_API( ComponentResult )
RTPRssmComponentVideo_GetInfo(
    __InstanceData      inGlobals,
    OSType              inSelector,
    void *              ioParams )
{
    ComponentResult         theResult = noErr;
    QTSDimensionParams *    theDimensions;
    Rect *                  theRect;
    
    
    switch( inSelector )
    {
        case kQTSSourceDimensionsInfo:
            theDimensions = STATIC_CAST( QTSDimensionParams * )( ioParams );
            
            theDimensions->width =
                Long2Fix(
                    ComponentVideoPayloadWidth( &( **inGlobals ).itsPayloadAttributes ) );
            
            theDimensions->height =
                Long2Fix(
                    ComponentVideoPayloadHeight( &( **inGlobals ).itsPayloadAttributes ) );
        break;
        
        
        case kQTSSourceClipRectInfo:
        case kQTSSourceBoundingRectInfo:
            theRect = STATIC_CAST( Rect * )( ioParams );
            
            MacSetRect(
                theRect, 0, 0,
                ComponentVideoPayloadWidth( &( **inGlobals ).itsPayloadAttributes ),
                ComponentVideoPayloadHeight( &( **inGlobals ).itsPayloadAttributes ) );
        break;
        
        
        default:
            theResult =
                RTPRssmGetInfo( ( **inGlobals ).itsBase, inSelector, ioParams );
        break;
    }
    
    return( theResult );
}
 
 
 
/* ---------------------------------------------------------------------------
 *      + 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 )
RTPRssmComponentVideo_HasCharacteristic(
    __InstanceData      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 releases
 *  any saved chunk and invalidates its cached payload description before
 *  resetting its base.
 *
 */
 
EXTERN_API( ComponentResult )
RTPRssmComponentVideo_Reset(
    __InstanceData      inGlobals,
    SInt32              inFlags )
{
    ComponentResult     theResult;
    
    
    __ReleaseChunk( inGlobals );
    ComponentVideoPayloadSetDescription( &( **inGlobals ).itsPayloadAttributes, 0, 0 );
    
    if( CallComponentCanDo( ( **inGlobals ).itsBase, kRTPRssmResetSelect ) )
        theResult = RTPRssmReset( ( **inGlobals ).itsBase, inFlags );
    else
        theResult = noErr;
    
    return( theResult );
}