我可以使用AVCaptureSession将AACstream编码到内存吗?
我正在编写一个iOS应用程序,通过networking传输video和audio。
我正在使用AVCaptureSession抓取使用AVCaptureVideoDataOutput的原始video帧,并使用x264在软件中进行编码。 这很好。
我想为audio做同样的事情,只是我不需要太多的audio控制,所以我想用内置的硬件编码器来产生一个AACstream。 这意味着从audio工具箱层使用audio转换器 。 为了做到这一点,我把一个AVCaptudeAudioDataOutput的audio帧的处理程序:
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { // get the audio samples into a common buffer _pcmBuffer CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer); CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &_pcmBufferSize, &_pcmBuffer); // use AudioConverter to UInt32 ouputPacketsCount = 1; AudioBufferList bufferList; bufferList.mNumberBuffers = 1; bufferList.mBuffers[0].mNumberChannels = 1; bufferList.mBuffers[0].mDataByteSize = sizeof(_aacBuffer); bufferList.mBuffers[0].mData = _aacBuffer; OSStatus st = AudioConverterFillComplexBuffer(_converter, converter_callback, (__bridge void *) self, &ouputPacketsCount, &bufferList, NULL); if (0 == st) { // ... send bufferList.mBuffers[0].mDataByteSize bytes from _aacBuffer... } }
在这种情况下,audio转换器的callback函数非常简单(假设数据包大小和计数设置正确):
- (void) putPcmSamplesInBufferList:(AudioBufferList *)bufferList withCount:(UInt32 *)count { bufferList->mBuffers[0].mData = _pcmBuffer; bufferList->mBuffers[0].mDataByteSize = _pcmBufferSize; }
而audio转换器的设置如下所示:
{ // ... AudioStreamBasicDescription pcmASBD = {0}; pcmASBD.mSampleRate = ((AVAudioSession *) [AVAudioSession sharedInstance]).currentHardwareSampleRate; pcmASBD.mFormatID = kAudioFormatLinearPCM; pcmASBD.mFormatFlags = kAudioFormatFlagsCanonical; pcmASBD.mChannelsPerFrame = 1; pcmASBD.mBytesPerFrame = sizeof(AudioSampleType); pcmASBD.mFramesPerPacket = 1; pcmASBD.mBytesPerPacket = pcmASBD.mBytesPerFrame * pcmASBD.mFramesPerPacket; pcmASBD.mBitsPerChannel = 8 * pcmASBD.mBytesPerFrame; AudioStreamBasicDescription aacASBD = {0}; aacASBD.mFormatID = kAudioFormatMPEG4AAC; aacASBD.mSampleRate = pcmASBD.mSampleRate; aacASBD.mChannelsPerFrame = pcmASBD.mChannelsPerFrame; size = sizeof(aacASBD); AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &aacASBD); AudioConverterNew(&pcmASBD, &aacASBD, &_converter); // ... }
这看起来很简单,只有IT不行 。 一旦AVCaptureSession运行,audio转换器(特别是AudioConverterFillComplexBuffer)返回一个'hwiu'(硬件使用中)错误。 转换工作正常,如果会话被停止,但我不能捕获任何东西…
我想知道是否有办法从AVCaptureSession中获取AACstream。 我正在考虑的选项是:
-
以某种方式使用AVAssetWriterInput将audio样本编码到AAC中,然后以某种方式获取编码的数据包(而不是通过仅写入文件的AVAssetWriter)。
-
重新组织我的应用程序,使其仅在video端使用AVCaptureSession,并在audio端使用audio队列 。 这将使stream量控制(开始和停止录制,响应中断)更加复杂,恐怕可能会导致audio和video之间的同步问题。 另外,它似乎不是一个好的devise。
有谁知道是否有可能从AVCaptureSession获得AAC? 我必须在这里使用audio队列吗? 这可以让我进入同步或控制问题?
我最终向苹果公司征求意见(事实certificate,如果您有付费开发者账户,您可以这样做)。
看来,AVCaptureSession抓住了AAC硬件编码器,但只能让你用它直接写入文件。
你可以使用软件编码器,但你必须要求它而不是使用AudioConverterNew:
AudioClassDescription *description = [self getAudioClassDescriptionWithType:kAudioFormatMPEG4AAC fromManufacturer:kAppleSoftwareAudioCodecManufacturer]; if (!description) { return false; } // see the question as for setting up pcmASBD and arc ASBD OSStatus st = AudioConverterNewSpecific(&pcmASBD, &aacASBD, 1, description, &_converter); if (st) { NSLog(@"error creating audio converter: %s", OSSTATUS(st)); return false; }
同
- (AudioClassDescription *)getAudioClassDescriptionWithType:(UInt32)type fromManufacturer:(UInt32)manufacturer { static AudioClassDescription desc; UInt32 encoderSpecifier = type; OSStatus st; UInt32 size; st = AudioFormatGetPropertyInfo(kAudioFormatProperty_Encoders, sizeof(encoderSpecifier), &encoderSpecifier, &size); if (st) { NSLog(@"error getting audio format propery info: %s", OSSTATUS(st)); return nil; } unsigned int count = size / sizeof(AudioClassDescription); AudioClassDescription descriptions[count]; st = AudioFormatGetProperty(kAudioFormatProperty_Encoders, sizeof(encoderSpecifier), &encoderSpecifier, &size, descriptions); if (st) { NSLog(@"error getting audio format propery: %s", OSSTATUS(st)); return nil; } for (unsigned int i = 0; i < count; i++) { if ((type == descriptions[i].mSubType) && (manufacturer == descriptions[i].mManufacturer)) { memcpy(&desc, &(descriptions[i]), sizeof(desc)); return &desc; } } return nil; }
软件编码器当然会占用CPU资源,但是会完成工作。