Reputation: 77
Update
I resolved the problem in recording in iOS 10. After adding Audio Session configuration before starting recording, it works as normal. But playback hasn't been resolved.
Here's the solution:
NSError *error = nil;
// the param category depends what you need
BOOL ret = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
if (!ret) {
NSLog(@"Audio session category setup failed");
return;
}
// don't forget to setActive NO when finishing recording
ret = [[AVAudioSession sharedInstance] setActive:YES error:&error];
if (!ret)
{
NSLog(@"Audio session activation failed");
return;
}
Original
I work in audio recording with audio queue service in iOS. I followed apple's official tutorial to realize the recording part and playback part. It was successfully tested in iOS 9.3 in emulator but failed in iOS 10.3.1 in real device iPad.
For the recording part, the callback function invokes AudioFileWritePackets
to save the audio into a file (see the code below). In iOS 9, ioNumPackets
always has a non-zero value but in iOS 10, it is always 0 during the first recording, and from the second time it becomes normal. That is, only from the second time the recording works.
Here's some code about recording:
Callback function:
static void AudioInputCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp * inStartTime, UInt32 inNumPackets, const AudioStreamPacketDescription * inPacketDescs) {
NSLog(@"Input callback called");
RecordState * aqData = (RecordState*)inUserData;
if (aqData->isRecording == 0) return;
if (inNumPackets == 0 && aqData->dataFormat.mBytesPerPacket != 0)
inNumPackets = inBuffer->mAudioDataByteSize / aqData->dataFormat.mBytesPerPacket;
NSLog(@"inNumPackets = %d", inNumPackets);
// handler the data
if (outputToMobile){
OSStatus res = AudioFileWritePackets(aqData->audioFile, false, inBuffer->mAudioDataByteSize, inPacketDescs, aqData->currentPacket, &inNumPackets, inBuffer->mAudioData);
if(res == noErr)
aqData->currentPacket += inNumPackets;
}else{
}
// after handling, re-enqueue de buffer into the queue
AudioQueueEnqueueBuffer(aqData->queue, inBuffer, 0, NULL);
}
Start record function:
-(void)startRecording{
[self setupAudioFormat:&recordState.dataFormat];
recordState.currentPacket = 0;
OSStatus status;
status = AudioQueueNewInput(&recordState.dataFormat, AudioInputCallback, &recordState, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &recordState.queue);
if (status == 0) {
UInt32 dataFormatSize = sizeof (recordState.dataFormat);
AudioQueueGetProperty (recordState.queue,kAudioQueueProperty_StreamDescription,&recordState.dataFormat,&dataFormatSize);
if (outputToMobile) {
[self createFile];
SetMagicCookieForFile(recordState.queue, recordState.audioFile);
}
DeriveBufferSize(recordState.queue, &recordState.dataFormat, 0.5, &recordState.bufferByteSize);
for (int i = 0; i < NUM_BUFFERS; i++) {
AudioQueueAllocateBuffer(recordState.queue, recordState.bufferByteSize, &recordState.buffers[i]);
AudioQueueEnqueueBuffer(recordState.queue, recordState.buffers[i], 0, NULL);
}
recordState.isRecording = true;
AudioQueueStart(recordState.queue, NULL);
}
}
For the playback part, the callback function invokes AudioFileReadPacketData
to read the audio file (see the code below). As well, in iOS 9, ioNumPackets
is always non-zero but in iOS 10, ioNumPackets
is always always 0 so that nothing is output from iOS 10.
Here's some code about playback:
Callback function:
static void AudioOutputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer){
NSLog(@"Output callback called");
PlayState *aqData = (PlayState *)inUserData;
if (aqData->isPlaying == 0) return;
UInt32 numBytesReadFromFile;
UInt32 numPackets = aqData->numPacketsToRead;
AudioFileReadPacketData(aqData->audioFile, false, &numBytesReadFromFile, aqData->packetDesc, aqData->currentPacket, &numPackets, inBuffer->mAudioData);
NSLog(@"outNumPackets = %d", numPackets);
if (numPackets > 0) {
AudioQueueEnqueueBuffer(aqData->queue, inBuffer, aqData->packetDesc ? numPackets : 0, aqData->packetDesc);
aqData->currentPacket += numPackets;
} else {
AudioQueueStop(aqData->queue, false);
aqData->isPlaying = false;
}
}
Start playback function:
- (void)startPlaying{
playState.currentPacket = 0;
[self openFile];
UInt32 dataFormatSize = sizeof(playState.dataFormat);
AudioFileGetProperty(playState.audioFile, kAudioFilePropertyDataFormat, &dataFormatSize, &playState.dataFormat);
OSStatus status;
status = AudioQueueNewOutput(&playState.dataFormat, AudioOutputCallback, &playState, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &playState.queue);
if (status == 0) {
playState.isPlaying = true;
UInt32 maxPacketSize;
UInt32 propertySize = sizeof(maxPacketSize);
AudioFileGetProperty(playState.audioFile,kAudioFilePropertyPacketSizeUpperBound,&propertySize,&maxPacketSize);
DeriveBufferSize(playState.dataFormat, maxPacketSize, 0.5, &playState.bufferByteSize, &playState.numPacketsToRead);
bool isFormatVBR = (playState.dataFormat.mBytesPerPacket == 0 ||playState.dataFormat.mFramesPerPacket == 0);
if (isFormatVBR) {
playState.packetDesc = (AudioStreamPacketDescription*) malloc (playState.numPacketsToRead * sizeof(AudioStreamPacketDescription));
} else {
playState.packetDesc = NULL;
}
//Set a Magic Cookie for a Playback Audio Queue
MyCopyEncoderCookieToQueue(playState.audioFile, playState.queue);
for (int i = 0; i < NUM_BUFFERS; i++) {
AudioQueueAllocateBuffer(playState.queue, playState.bufferByteSize, &playState.buffers[i]);
playState.buffers[i]->mAudioDataByteSize = playState.bufferByteSize;
AudioOutputCallback(&playState, playState.queue, playState.buffers[i]);
}
Float32 gain = 10.0;
AudioQueueSetParameter(playState.queue, kAudioQueueParam_Volume, gain);
AudioQueueStart(playState.queue, NULL);
}
}
This kind of incompatibility really upsets me for several days. Free to ask me if you need more details. I hope someone could help me out. Thanks a lot.
Upvotes: 1
Views: 703