http://stackoverflow.com/questions/17963435/ios-audio-queue-services-queue-starvation-causes-silence
#define kNumAQBufs 1
#define kAudioBufferSeconds 3
void audioQueueOutputCallback(void *inClientData, AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer)
{
BaseSecurityMediaView *baseSecurity = (__bridge BaseSecurityMediaView*)inClientData;
[baseSecurity audioQueueOutputCallback:inAQ inBuffer:inBuffer];
}
/**
* This is basically called in response to getting audio frames over the network
* asynchronously.
*/
- (void)doAudioRendering:(NSData *)data
{
[m_audioPacketQueue addObject:data];
m_audioPacketQueueSize += [data length];
// must start the audio queue once enough queued
if(!m_audioQueueStarted && [m_audioPacketQueue count] >= kNumAQBufs) {
[self startAudioQueue];
}
// manually enqueue buffers?
if(m_audioQueueStarted) {
for (NSInteger i = 0; i < kNumAQBufs; ++i) {
[self enqueueAudioBuffer:m_audioQueueBuffer[i]];
}
}
}
- (void)startAudioQueue
{
if(m_audioQueueStarted) {
AudioQueueStart(m_audioQueue, NULL);
}
else {
[self createAudioQueue];
[self startQueue];
}
// prime the buffers?
for(NSInteger i = 0; i < kNumAQBufs; i++) {
[self enqueueAudioBuffer:m_audioQueueBuffer[i]];
}
m_audioQueueState = AUDIO_STATE_PLAYING;
}
- (BOOL)createAudioQueue
{
m_audioQueueState = AUDIO_STATE_READY;
m_audioQueueFinished = NO;
m_audioStreamBasicDesc.mFormatID = kAudioFormatULaw;
m_audioStreamBasicDesc.mSampleRate = m_audioSampleRate;
m_audioStreamBasicDesc.mFormatFlags = 0;
m_audioStreamBasicDesc.mFramesPerPacket = 1;
m_audioStreamBasicDesc.mChannelsPerFrame = m_audioNumChannels;
m_audioStreamBasicDesc.mBitsPerChannel = 8;
m_audioStreamBasicDesc.mBytesPerPacket = 1;
m_audioStreamBasicDesc.mBytesPerFrame = 1;
OSStatus status = AudioQueueNewOutput(&m_audioStreamBasicDesc, audioQueueOutputCallback, (__bridge void*)self, NULL, NULL, 0, &m_audioQueue);
if(status != noErr) {
NSLog(@"Could not create new output.");
return NO;
}
// allocate buffers
for(NSInteger i = 0; i < kNumAQBufs; i++) {
status = AudioQueueAllocateBufferWithPacketDescriptions(m_audioQueue,
m_audioStreamBasicDesc.mSampleRate * kAudioBufferSeconds / 8,
m_audioStreamBasicDesc.mSampleRate * kAudioBufferSeconds /
m_audioStreamBasicDesc.mBytesPerFrame + 1,
&m_audioQueueBuffer[i]);
if(status != noErr) {
NSLog(@"Could not allocate buffer.");
return NO;
}
}
return YES;
}
- (void)audioQueueOutputCallback:(AudioQueueRef)inAQ inBuffer:(AudioQueueBufferRef)inBuffer
{
if(m_audioQueueState == AUDIO_STATE_PLAYING) {
[self enqueueAudioBuffer:inBuffer];
}
}
- (OSStatus)enqueueAudioBuffer:(AudioQueueBufferRef)buffer {
OSStatus status = noErr;
buffer->mAudioDataByteSize = 0;
buffer->mPacketDescriptionCount = 0;
// this seems to happen a lot
if(m_audioPacketQueue.count <= 0) {
return status;
}
while(m_audioPacketQueue.count && buffer->mPacketDescriptionCount < buffer->mPacketDescriptionCapacity) {
// grab packet that was received from network
NSData *packet = [m_audioPacketQueue objectAtIndex:0];
m_audioPacketQueueSize -= [packet length];
[m_audioPacketQueue removeObjectAtIndex:0];
// move data in
if(buffer->mAudioDataBytesCapacity - buffer->mAudioDataByteSize >= [packet length]) {
memcpy((uint8_t *)buffer->mAudioData + buffer->mAudioDataByteSize, [packet bytes], [packet length]);
buffer->mPacketDescriptions[buffer->mPacketDescriptionCount].mStartOffset = buffer->mAudioDataByteSize;
buffer->mPacketDescriptions[buffer->mPacketDescriptionCount].mDataByteSize = [packet length];
buffer->mPacketDescriptions[buffer->mPacketDescriptionCount].mVariableFramesInPacket = m_audioStreamBasicDesc.mBytesPerFrame;
buffer->mAudioDataByteSize += [packet length];
buffer->mPacketDescriptionCount++;
}
else {
break;
}
}
if(buffer->mPacketDescriptionCount > 0) {
status = AudioQueueEnqueueBuffer(m_audioQueue, buffer, 0, NULL);
if(status != noErr) {
NSLog(@"Could not enqueue buffer.");
}
}
return status;
}
- (OSStatus)startQueue
{
OSStatus status = noErr;
if(!m_audioQueueStarted) {
status = AudioQueueStart(m_audioQueue, NULL);
if(status == noErr) {
m_audioQueueStarted = YES;
}
else {
NSLog(@"Could not start audio queue.");
}
}
return status;
}