Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
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 ); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14