从RemoteIO录制到AAC:数据正在写入,但文件不可播放

我一直在iPad 2上试图从RemoteIO单元直接录制到iOS 5的renderCallback中。我已经看到冲突的信息,说这是不可能的,这是可能的(在这里的评论)。 我之所以想这样做,是因为录制到PCM需要大量的磁盘空间来logging任何长度 – 即使稍后转换为AAC。

我即将准备放弃。 我已经通过谷歌,核心audio书和苹果核心audio邮件列表和论坛,已经达到了我没有得到任何错误的地步,并正在录制的东西磁盘,但生成的文件是无法播放的。 模拟器和设备都是这种情况。

所以…如果有人有这方面的经验,我真的很感激正确的方向。 设置是RemoteIO正在播放来自AUSamplers的输出并且工作正常。

以下是我在下面的代码中所做的

  • 指定remoteIO单元的AudioStreamBasicDescription格式为kAudioFormatLinearPCM

  • 创build并指定ExtAudioFileRef的目标格式通过从RemoteIO单元中获取指定客户端格式

  • 为RemoteID单位指定renderCallback

  • 在renderCallback中,将数据写入kAudioUnitRenderAction_PostRender阶段

正如我所说,我没有得到任何错误,并由此产生的audio文件大小显示正在编写的东西,但该文件是无法播放的。 也许我有我的格式搞砸了?

无论如何,这是我的信息在一瓶和/或“在这里龙”旗给其他任何人冒着Core-Audio的黑暗水域。


//尝试播放文件时遇到不愉快的消息:

在这里输入图像说明

//远程设置的一部分

  // Enable IO for recording UInt32 flag = 1; result = AudioUnitSetProperty(ioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, // == 1 &flag, sizeof(flag)); if (noErr != result) {[self printErrorMessage: @"Enable IO for recording" withStatus: result]; return;} // Describe format - - - - - - - - - - size_t bytesPerSample = sizeof (AudioUnitSampleType); AudioStreamBasicDescription audioFormat; memset(&audioFormat, 0, sizeof(audioFormat)); audioFormat.mSampleRate = 44100.00; audioFormat.mFormatID = kAudioFormatLinearPCM; audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; audioFormat.mFramesPerPacket = 1; audioFormat.mChannelsPerFrame = 1; audioFormat.mBitsPerChannel = 16; audioFormat.mBytesPerPacket = 2; audioFormat.mBytesPerFrame = 2; result = AudioUnitSetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, // == 1 &audioFormat, sizeof(audioFormat)); result = AudioUnitSetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, // == 0 &audioFormat, sizeof(audioFormat)); 

//设置文件和rendercallback的函数

  - (void)startRecordingAAC { OSStatus result; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *recordFile = [documentsDirectory stringByAppendingPathComponent: @"audio.m4a"]; CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)recordFile, kCFURLPOSIXPathStyle, false); AudioStreamBasicDescription destinationFormat; memset(&destinationFormat, 0, sizeof(destinationFormat)); destinationFormat.mChannelsPerFrame = 2; destinationFormat.mFormatID = kAudioFormatMPEG4AAC; UInt32 size = sizeof(destinationFormat); result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat); if(result) printf("AudioFormatGetProperty %ld \n", result); result = ExtAudioFileCreateWithURL(destinationURL, kAudioFileM4AType, &destinationFormat, NULL, kAudioFileFlags_EraseFile, &extAudioFileRef); if(result) printf("ExtAudioFileCreateWithURL %ld \n", result); AudioStreamBasicDescription clientFormat; memset(&clientFormat, 0, sizeof(clientFormat)); result = AudioUnitGetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, & clientFormat, &size); if(result) printf("AudioUnitGetProperty %ld \n", result); result = ExtAudioFileSetProperty(extAudioFileRef,kExtAudioFileProperty_ClientDataFormat,sizeof(clientFormat),&clientFormat); if(result) printf("ExtAudioFileSetProperty %ld \n", result); result = ExtAudioFileWriteAsync(extAudioFileRef, 0, NULL); if (result) {[self printErrorMessage: @"ExtAudioFileWriteAsync error" withStatus: result];} result = AudioUnitAddRenderNotify(ioUnit, renderCallback, (__bridge void*)self); if (result) {[self printErrorMessage: @"AudioUnitAddRenderNotify" withStatus: result];} } 

//最后,rendercallback

 static OSStatus renderCallback (void * inRefCon, AudioUnitRenderActionFlags * ioActionFlags, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData) { OSStatus result; if (*ioActionFlags == kAudioUnitRenderAction_PostRender){ MusicPlayerController* THIS = (__bridge MusicPlayerController *)inRefCon; result = ExtAudioFileWriteAsync(THIS->extAudioFileRef, inNumberFrames, ioData); if(result) printf("ExtAudioFileWriteAsync %ld \n", result); } return noErr; } 

所以我终于整理出来了! 呃,一个信息清道夫狩猎。

无论如何,这是我错过的ExtAudioFile的文档中的位(见粗体文本)。 我没有设置这个属性。 数据正在写入我的.m4a文件,但播放时无法读取。 所以总结一下:我有一堆AUSampler – > AUMixer – > RemoteIO。 RemoteIO实例上的呈现callback将数据以压缩的m4a格式写入磁盘。 所以可以即时生成压缩audio(iOS 5 / iPad 2)

看起来很健壮 – 我有一些printf语句在rendercallback和写入工作正常。

好极了!

ExtAudioFileProperty_CodecManufacturer扩展audio文件对象使用的编码解码器的制造商。 值是一个读写UInt32。 您必须在设置kExtAudioFileProperty_ClientDataFormat(页面20)属性之前指定此属性,然后触发创build编解码器。 在iOS中使用此属性可通过指定kAppleHardwareAudioCodecManufacturer或kAppleSoftwareAudioCodecManufacturer在硬件或软件编码器之间进行select。 适用于Mac OS X v10.7及更高版本。 在ExtendedAudioFile.h中声明。

 // specify codec UInt32 codec = kAppleHardwareAudioCodecManufacturer; size = sizeof(codec); result = ExtAudioFileSetProperty(extAudioFileRef, kExtAudioFileProperty_CodecManufacturer, size, &codec); if(result) printf("ExtAudioFileSetProperty %ld \n", result); 

您是否在mpg 4audio文件的开始处写入了所需的魔法cookie?

您还需要至less在audio单元渲染callback以外写入第一个文件。

添加:

您是否在最后正确清空并closures了audio文件? (在AUcallback之外)

看起来还有一点比指定输出audio文件ID的编解码器多一点。

根据这个线程: ExtAudioFileWrite以m4a / aac在双核设备(ipad 2,iphone 4s)上某些iPhone型号(4S)为例失败,不要和硬件解码器打好。

根据我对4S的经验,尝试使用硬件编解码器对AAC文件进行编码a)有时可以工作,b)失败,出现错误-66567(kExtAudioFileError_MaxPacketSizeUnknown)或c)写入几个样本,然后挂起而没有可追踪的错误。

软件编解码器在iPhone 4S上工作正常,性能较差。

编辑:有人声称硬件编解码器不喜欢44.1kHz的采样率。 我还没有证实这一点。