前几天钻研IOS音频技术的时候接触到了AudioUnit与AudioQueue两个系统的播放库,前面一个博客写了关于AudioUnit的使用方法,这次发一篇关于AudioQueue的使用方法,AudioQueue的使用相对于AudioUnit来说比较简单一点,可以直接定义目标buffer的大小,对于一般的音频数据的处理没有任何问题,下面贴出一个自己写的AudioQueue的播放器的代码
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#define QUEUE_BUFFER_SIZE 3 //队列缓冲个数
#define MIN_SIZE_PER_FRAME 1280 //每帧最小数据长度
@interface PCMDataPlayer : NSObject {
AudioStreamBasicDescription audioDescription; ///音频参数
AudioQueueRef audioQueue; //音频播放队列
AudioQueueBufferRef audioQueueBuffers[QUEUE_BUFFER_SIZE]; //音频缓存
BOOL audioQueueUsed[QUEUE_BUFFER_SIZE];
UInt32 mNumPacketsToRead;
UInt32 bufferByteSize;
NSLock* sysnLock;
}
- (instancetype)initWithAudioFomart:(AudioFormatID)audioFormatID;
/*!
* @author 15-02-10 16:02:27
*
* @brief 重置播放器
*
* @since v1.0
*/
- (void)reset;
/*!
* @author 15-02-10 17:02:52
*
* @brief 停止播放
*
* @since v1.0
*/
- (void)stop;
/*!
* @author 15-02-10 16:02:56
*
* @brief 播放PCM数据
*
* @param pcmData pcm字节数据
*
* @since v1.0
*/
- (void)play:(void*)pcmData length:(unsigned int)length;
@end
#import "PCMDataPlayer.h"
@implementation PCMDataPlayer
{
AudioFormatID playerAudioFormatID;
}
- (instancetype)initWithAudioFomart:(AudioFormatID)audioFormatID
{
self = [super init];
if (self) {
playerAudioFormatID = audioFormatID;
[self reset];
}
return self;
}
- (void)dealloc
{
NSLog(@"%s",__func__);
if (audioQueue != nil) {
AudioQueueStop(audioQueue, true);
}
audioQueue = nil;
sysnLock = nil;
NSLog(@"PCMDataPlayer dealloc...");
}
static void AudioPlayerAQInputCallback(void* inUserData, AudioQueueRef outQ, AudioQueueBufferRef outQB)
{
NSLog(@"%s",__func__);
PCMDataPlayer* player = (__bridge PCMDataPlayer*)inUserData;
[player playerCallback:outQB];
}
void CalculateBytesForTime1 (AudioStreamBasicDescription inDesc, UInt32 inMaxPacketSize, Float64 inSeconds, UInt32 *outBufferSize, UInt32 *outNumPackets)
{
NSLog(@"%s",__func__);
// we only use time here as a guideline
// we're really trying to get somewhere between 16K and 64K buffers, but not allocate too much if we don't need it
static const int maxBufferSize = 0x1000; // limit size to 4K 4096
static const int minBufferSize = 0x400; // limit size to 1K 1024
if (inDesc.mFramesPerPacket) {
Float64 numPacketsForTime = inDesc.mSampleRate / inDesc.mFramesPerPacket * inSeconds;
*outBufferSize = numPacketsForTime * inMaxPacketSize;
} else {
// if frames per packet is zero, then the codec has no predictable packet == time
// so we can't tailor this (we don't know how many Packets represent a time period
// we'll just return a default buffer size
*outBufferSize = maxBufferSize > inMaxPacketSize ? maxBufferSize : inMaxPacketSize;
}
// we're going to limit our size to our default
if (*outBufferSize > maxBufferSize && *outBufferSize > inMaxPacketSize)
*outBufferSize = maxBufferSize;
else {
// also make sure we're not too small - we don't want to go the disk for too small chunks
if (*outBufferSize < minBufferSize)
*outBufferSize = minBufferSize;
}
*outNumPackets = *outBufferSize / inMaxPacketSize;
}
- (void)reset
{
NSLog(@"%s",__func__);
[self stop];
sysnLock = [[NSLock alloc] init];
//设置音频参数
audioDescription.mSampleRate = 8000; //采样率
audioDescription.mFormatID = kAudioFormatLinearPCM;
audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;;
audioDescription.mChannelsPerFrame = 1; ///单声道
audioDescription.mFramesPerPacket = 1; //每一个packet一侦数据
audioDescription.mBitsPerChannel = 16; // 0 for compressed format
audioDescription.mBytesPerFrame = audioDescription.mChannelsPerFrame * (audioDescription.mBitsPerChannel/8);
audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame;
audioDescription.mReserved = 0;
OSStatus error = AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, (__bridge void * _Nullable)self, nil, nil, 0, &audioQueue); //使用player的内部线程播放
if(error!=noErr) {
NSLog(@"error:%d",error);
return;
}
UInt32 maxPacketSize = 1;
UInt32 size = sizeof(maxPacketSize);
AudioQueueGetProperty(audioQueue, kAudioQueueProperty_MaximumOutputPacketSize, &size, &maxPacketSize);
CalculateBytesForTime1 (audioDescription, maxPacketSize, 0.01, &bufferByteSize, &mNumPacketsToRead);
bool isFormatVBR = (audioDescription.mBytesPerPacket == 0 || audioDescription.mFramesPerPacket == 0);
//初始化音频缓冲区
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
error = AudioQueueAllocateBufferWithPacketDescriptions(audioQueue, MIN_SIZE_PER_FRAME, (isFormatVBR ? mNumPacketsToRead : 0), &audioQueueBuffers[i]);
if(error!=noErr) {
NSLog(@"Audio Queue alloc buffer error %d %d",i,(int)error);
return;
}
}
UInt32 value = kAudioQueueHardwareCodecPolicy_UseSoftwareOnly;
error = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_HardwareCodecPolicy, &value, sizeof(value));
if(error!=noErr) {
NSLog(@"software code not use");
}
Float32 gain=1.0;
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, gain);
}
- (void)stop
{
NSLog(@"%s",__func__);
if (audioQueue != nil) {
AudioQueueStop(audioQueue, true);
AudioQueueReset(audioQueue);
}
audioQueue = nil;
sysnLock = nil;
}
- (void)play:(void*)pcmData length:(unsigned int)length
{
NSLog(@"%s",__func__);
if (audioQueue == nil || ![self checkBufferHasUsed]) {
[self reset];
Float32 gain=1.0;
//设置音量
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, gain);
OSStatus error = AudioQueueStart(audioQueue, NULL);
if(error!=noErr) {
NSLog(@"error:%d",error);
return;
}
}
[sysnLock lock];
AudioQueueBufferRef audioQueueBuffer = NULL;
//获取可用buffer
while (true) {
audioQueueBuffer = [self getNotUsedBuffer];
if (audioQueueBuffer != NULL) {
break;
}
}
memcpy(audioQueueBuffer->mAudioData, pcmData, length);//将pcmdata按长度length赋值给audioQueueBuffer->mAudioData,内存拷贝
audioQueueBuffer->mAudioDataByteSize = length;
AudioQueueEnqueueBuffer(audioQueue, audioQueueBuffer, 0, NULL);
[sysnLock unlock];
}
- (BOOL)checkBufferHasUsed
{
NSLog(@"%s",__func__);
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
if (YES == audioQueueUsed[i]) {
return YES;
}
}
NSLog(@"PCMDataPlayer 播放中断............");
return NO;
}
- (AudioQueueBufferRef)getNotUsedBuffer
{
NSLog(@"%s",__func__);
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
if (NO == audioQueueUsed[i]) {
audioQueueUsed[i] = YES;
// NSLog(@"PCMDataPlayer play buffer index:%d", i);
return audioQueueBuffers[i];
}
}
return NULL;
}
- (void)playerCallback:(AudioQueueBufferRef)outQB
{
NSLog(@"%s",__func__);
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
if (outQB == audioQueueBuffers[i]) {
//清空缓存
// outQB->mAudioDataByteSize = 1;
// Byte* byte = (Byte *)outQB->mAudioData;
// byte = 0;
// AudioQueueFreeBuffer(audioQueue, outQB);
// outQB = nil;
audioQueueUsed[i] = NO;
}
}
}