我正在尝试播放我在一系列UDP数据包中收到的音频。它们被解码为具有以下属性的PCM帧:
每个UDP数据包包含480个帧,因此缓冲区的大小为480 * 2(通道)* 2(每个通道的字节数)。
我需要设置一个音频单元来播放这些数据包。所以,我的第一个问题是,我应该如何为音频单元设置AudioStreamBasicDescription结构?查看文档,我甚至不确定交错PCM是否是可接受的格式。
这是我到目前为止所得到的:
struct AudioStreamBasicDescription {
Float64 mSampleRate; //48000
UInt32 mFormatID; //?????
UInt32 mFormatFlags; //?????
UInt32 mBytesPerPacket; //Not sure what "packet" means here
UInt32 mFramesPerPacket; //Same as above
UInt32 mBytesPerFrame; //Same
UInt32 mChannelsPerFrame; //2?
UInt32 mBitsPerChannel; //16?
UInt32 mReserved; //???
};
typedef struct AudioStreamBasicDescription AudioStreamBasicDescription;
其次,在设置之后,我不确定如何将帧从UDP回调中获取到实际的音频单元渲染功能。
我目前有一个来自套接字侦听器的回调函数,我在其中生成包含我想要播放的音频的int16 *缓冲区。据我了解,我还必须为以下形式的音频单元实现渲染回调:
OSStatus RenderFrames(
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
//No idea what I should do here.
return noErr;
}
总而言之,我认为我的套接字接收回调应该做的是解码帧,并将它们放在缓冲区结构中,以便RenderFrames回调可以从该缓冲区中获取帧,并播放它们。 这是正确的吗?如果是这样,一旦我在RenderFrames函数中获取下一帧,我如何实际“提交”以进行播放?
答案 0 :(得分:6)
一次拍摄一部分
Apple的ASBD文档是here。澄清:
2
。 mBytesPerPacket = mBytesPerFrame
,mFramesPerPacket=1
,但我不确定这是否真的被使用过。mReserved
未使用且必须为0
mFormatID
和mFormatFlags
的信息,请参阅The documentation。 CoreAudioTypes.h中有一个方便的帮助函数CalculateLPCMFlags
,用于在CoreAudioTypes.h
中计算后者。 mFormatFlags
设置一个位。)FillOutASBDForLPCM()
用于线性PCM的常见情况。mFormatID
和mFormatFlags
的许多组合 - 我发现在iOS上需要进行实验。 以下是我的一个项目的一些工作代码:
AudioStreamBasicDescription inputASBL = {0};
inputASBL.mSampleRate = static_cast<Float64>(sampleRate);
inputASBL.mFormatID = kAudioFormatLinearPCM;
inputASBL.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger,
inputASBL.mFramesPerPacket = 1;
inputASBL.mChannelsPerFrame = 2;
inputASBL.mBitsPerChannel = sizeof(short) * 8;
inputASBL.mBytesPerPacket = sizeof(short) * 2;
inputASBL.mBytesPerFrame = sizeof(short) * 2;
inputASBL.mReserved = 0;
CoreAudio运行Apple所描述的 pull 模型。也就是说,当CoreAudio需要缓冲区填充时,渲染回调被称为实时线程。根据您的问题,您似乎期待相反的情况 - 将数据推送到音频输出。
基本上有两种实现选择:
第二种可能是更好的选择,但是您需要自己管理缓冲区过度和欠量运行。
ioData
参数指向分散 - 聚集控制结构。在最简单的情况下,它指向一个包含所有帧的缓冲区,但可以包含几个在它们之间具有足够帧以满足inNumberFrames
的帧。通常,为inNumberFrames
预先分配一个足够大的缓冲区,将样本复制到其中,然后修改指向buy AudioBufferList
的{{1}}对象以指向它。
在您的应用程序中,您可能对解码后的音频数据包采用分散 - 聚集方法,在解码时分配缓冲区。但是,您并不总是得到所需的延迟,并且可能无法安排ioData
与解码的UDP音频帧相同。