从RemoteIO录制到AAC:数据正在写入但文件无法播放

时间:2012-04-11 21:03:48

标签: ios ios5 core-audio

我一直试图在iPad 2上的iOS 5中的一个renderCallback中直接从RemoteIO单元录制到AAC。我看到有相互矛盾的信息说这是不可能的&这是可能的(在评论here中)。我想要这样做的原因是因为录制到PCM需要如此多的磁盘空间来记录任何长度 - 即使它稍后被转换为AAC。

我准备放弃了。我已经通过谷歌,SO,核心音频书和Apple Core-Audio邮件列表&论坛,并已达到我没有得到任何错误的点 - 并且正在将某些记录到磁盘,但生成的文件无法播放。模拟器和设备都是这种情况。

所以...如果有人有这方面的经验,我真的很感激在正确的方向上轻推。设置是RemoteIO正在播放AUSamplers&这很好。

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

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

  • 创建并指定ExtAudioFileRef的目标格式 通过从RemoteIO设备获取客户端格式

  • 来指定客户端格式
  • 指定RemoteID单元的renderCallback

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

正如我所说,我没有收到任何错误,并且生成的音频文件大小显示正在写入某些内容,但该文件无法播放。也许我的格式搞砸了?

无论如何,这是我在瓶子里的信息和/或“Be Here Dragons”的标志,让任何其他人勇敢地面对Core-Audio的黑暗水域。


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

enter image description here

//部分remoteIO设置

    // 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; 
}

3 个答案:

答案 0 :(得分:14)

所以我终于解决了这个问题!呃,这是一个信息清道夫狩猎。

无论如何,这是我错过的ExtAudioFile文档中的位(参见粗体文本)。我没有设置这个属性。数据正在写入我的.m4a文件,但在播放时无法读取。总结一下:我有一堆AUSamplers - > AUMixer - > RemoteIO。 RemoteIO实例上的渲染回调以压缩的m4a格式将数据写入磁盘。因此可以动态生成压缩音频(iOS 5 / iPad 2)

看起来相当健壮 - 我在rendercallback中有一些printf语句,而且写得很好。

耶!

  

ExtAudioFileProperty_CodecManufacturer   扩展音频文件对象使用的编解码器的制造商。值是读/写UInt32。 您必须在设置kExtAudioFileProperty_ClientDataFormat(页面20)属性之前指定此属性,这又会触发创建编解码器。在iOS中使用此属性可以在硬件或软件编码器之间进行选择,方法是指定kAppleHardwareAudioCodecManufacturer或kAppleSoftwareAudioCodecManufacturer。   适用于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);

答案 1 :(得分:1)

您是否在mpg 4音频文件的开头编写了所需的魔术cookie?

您还需要至少执行音频单元渲染回调之外的第一个文件写入。

添加了:

您最后是否正确刷新并关闭了音频文件? (在AU回调之外)

答案 2 :(得分:0)

除了为输出音频文件ID指定编解码器外,还有更多内容。

根据这个帖子:ExtAudioFileWrite to m4a/aac failing on dual-core devices (ipad 2, iphone 4s)某些iPhone型号(4S),例如,硬件解码器不能很好用。

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

软件编解码器在iPhone 4S上运行良好,但性能较低。

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