Обратный вызов AudioQueue получает пустой буфер только на iOS 7

У меня возникла странная проблема. Мой код отлично работает как на iOS 5, так и на 6, но при работе на iOS 7 я получаю пустые буферы при обратном вызове AudioQueue.

Возможный соответствующий код:

- (void)setUpAudioFormat
{
audioFormat.mFormatID         = kAudioFormatLinearPCM;
audioFormat.mSampleRate       = SAMPLE_RATE;//16000.0;
audioFormat.mChannelsPerFrame = CHANNELS;//1;
audioFormat.mBitsPerChannel   = 16;
audioFormat.mFramesPerPacket  = 1;
audioFormat.mBytesPerFrame    = audioFormat.mChannelsPerFrame * sizeof(SInt16);
audioFormat.mBytesPerPacket   = audioFormat.mBytesPerFrame * audioFormat.mFramesPerPacket;
audioFormat.mFormatFlags      = kLinearPCMFormatFlagIsSignedInteger
| kLinearPCMFormatFlagIsPacked;

bufferNumPackets = 2048;  // must be power of 2 for FFT!
bufferByteSize = [self byteSizeForNumPackets:bufferNumPackets];

}

- (UInt32)numPacketsForTime:(Float64)seconds
{
return (UInt32) (seconds * audioFormat.mSampleRate / audioFormat.mFramesPerPacket);
}

- (UInt32)byteSizeForNumPackets:(UInt32)numPackets
{
return numPackets * audioFormat.mBytesPerPacket;
}

- (void)setUpRecordQueue
{
NSLog(@"\n+++ setUpRecordQueue");
OSStatus errorStatus = AudioQueueNewInput(
                   &audioFormat,
                   recordCallback,
                   self,                // userData
                   CFRunLoopGetMain(),  // run loop
                   NULL,                // run loop mode
                   0,                   // flags
                   &recordQueue);

if (errorStatus) {
    NSLog(@"\n\n ERROR : Error %ld on AudioQueueNewInput\n", errorStatus );
}


if (recordQueue == nil) {
    NSLog(@"\n\n ----- Record Queue is nil! -----");
}

UInt32 trueValue = true;
       AudioQueueSetProperty(recordQueue,kAudioQueueProperty_EnableLevelMetering,&trueValue,sizeof (UInt32));
}

- (void)setUpRecordQueueBuffers
{
NSLog(@"\n+++ setUpRecordQueueBuffers");
assert(recordQueue != nil);
for (int t = 0; t < NUMBER_AUDIO_DATA_BUFFERS; ++t)
{
    OSStatus errorStatus = AudioQueueAllocateBuffer(
                             recordQueue,
                             bufferByteSize,
                             &recordQueueBuffers[t]);
    if (errorStatus) {
        NSLog(@"\n\n ERROR : Error %ld on AudioQueueAllocateBuffer\n", errorStatus );
    }
}
}

- (void)primeRecordQueueBuffers
{
NSLog(@"\n+++ primeRecordQueueBuffers");
assert(recordQueue != nil);
for (int t = 0; t < NUMBER_AUDIO_DATA_BUFFERS; ++t)
{
    OSStatus errorStatus = AudioQueueEnqueueBuffer(
                            recordQueue,
                            recordQueueBuffers[t],
                            0,
                            NULL);
    if (errorStatus) {
        NSLog(@"\n\n ERROR : Error %ld on AudioQueueEnqueueBuffer\n", errorStatus );
    }
}
}

- (void)startRecording
{
[self startRecording:FALSE];
}

- (void)startRecording:(BOOL) autoStop
{
NSLog(@"Starting to record");

recording = YES;
shouldStopRecording = NO;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
               , ^{
    NSLog(@"PPPP C1");
    _frameIndex= 0;
    self.fileWasCreated = NO;
    [self setUpRecordQueue];
    NSLog(@"PPPP C2");
    [self setUpRecordQueueBuffers];
    NSLog(@"PPPP C3");
    [self primeRecordQueueBuffers];
    NSLog(@"PPPP C4");

    AudioQueueStart(recordQueue, NULL);
    NSLog(@"PPPP C5");

    if (autoStop) {
        [self stopRecording];
    }

});

}

- (void)stopRecording
{
NSLog(@"Stoping to record");
if (recordQueue != nil) {
    NSString *osVersion = [[UIDevice currentDevice]  systemVersion];

    if ([osVersion doubleValue]<6){
        AudioQueueDispose(recordQueue, TRUE);
    }
    else {
        AudioQueueStop(recordQueue, FALSE);
    }

    recordQueue = nil;
}

NSLog(@"Stopped recording");

shouldStopRecording = YES;
recording = NO; 

}

Обратный вызов:

static void recordCallback(
                       void* inUserData,
                       AudioQueueRef inAudioQueue,
                       AudioQueueBufferRef inBuffer,
                       const AudioTimeStamp* inStartTime,
                       UInt32 inNumPackets,
                       const AudioStreamPacketDescription* inPacketDesc)
{
NSLog(@"recordCallback %u", (unsigned int)inBuffer->mAudioDataByteSize);
// I get always zero here...

}

Кстати, разрешение микрофона в порядке (включить доступ к микрофону).

ОБНОВЛЕНИЕ: похоже, что AudioQueueStart завершился с ошибкой -50. Это происходит только на iOS 7. Есть ли проблема с параметрами, которые я установил?


person Idan    schedule 23.09.2013    source источник
comment
Вы когда-нибудь находили решение своей проблемы с ошибкой -50?   -  person Saliom    schedule 04.03.2015
comment
просто хотел сказать большое спасибо, чувак, благодаря твоему примеру кода и книге, на которую ты дал ссылку в своем ответе, я смог сделать то, что мне было нужно :)   -  person Saliom    schedule 05.03.2015
comment
просто будьте осторожны, если кто-то когда-либо попытается заставить ваш код работать и в конечном итоге получит сбой на ios 7: попробуйте это: audioFormat.mBytesPerFrame = (audioFormat.mBitsPerChannel/8) * audioFormat.mChannelsPerFrame; audioFormat.mBytesPerPacket = (audioFormat.mBitsPerChannel/8) * audioFormat.mChannelsPerFrame;   -  person Saliom    schedule 06.03.2015
comment
Для версии iOS выше 7 попробуйте это   -  person Clint Lin    schedule 01.03.2016


Ответы (2)


Я нашел проблему! похоже, что на iOS 7 это тоже нужно установить (я предполагаю, что это только практически, поэтому его трудно найти, нигде не написано). Просто добавьте этот код перед вызовом любой функции AudioQueue:

AudioSessionInitialize(NULL,
                       NULL,
                       nil,
                       ( void *)(self)
                       );

UInt32 sessionCategory = kAudioSessionCategory_PlayAndRecord;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
                        sizeof(sessionCategory),
                        &sessionCategory
                        );

AudioSessionSetActive(true);

Надеюсь, это поможет другим.

Другой полезный ресурс можно найти здесь.

person Idan    schedule 24.09.2013
comment
У меня также есть аналогичная проблема, и я решил ее, добавив указанную выше строку кода, но эти методы устарели в iOS 7. - person Muhammad Zeeshan; 15.12.2013
comment
@MuhammadZeeshan Это старый ответ, но он все еще работает. Вы нашли новое решение? (Я буду рад обновить это) - person Idan; 27.05.2014

@Idan, ваш ответ правильный и рабочий, но он показывает предупреждение только в том случае, если целью развертывания минимального приложения является iOS 7. Для iOS 7 мы можем сделать что-то вроде этого:

NSError *audioSessionError;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&audioSessionError];

if(audioSessionError)
{
    NSLog(@"AVAudioSession error setting category:%@",audioSessionError);
}
else
{
    [audioSession setActive:YES error:&audioSessionError];
    if(audioSessionError)
        NSLog(@"AVAudioSession error activating: %@",audioSessionError);
}
person Muhammad Zeeshan    schedule 27.05.2014