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.
IMAAudioRTP/RTPMPIMAAudio/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 ); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14