【问题标题】:Can I use AVCaptureSession to encode an AAC stream to memory?我可以使用 AVCaptureSession 将 AAC 流编码到内存吗?
【发布时间】:2012-05-30 13:13:05
【问题描述】:

我正在编写一个通过网络传输视频和音频的 iOS 应用。

我正在使用 AVCaptureSession 通过 AVCaptureVideoDataOutput 抓取原始视频帧,并在软件 using x264 中对其进行编码。这很好用。

我想对音频做同样的事情,只是我不需要在音频方面进行太多控制,所以我想使用内置的硬件编码器来生成 AAC 流。这意味着使用来自音频工具箱层的Audio Converter。为此,我为 AVCaptudeAudioDataOutput 的音频帧添加了一个处理程序:

- (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...
    }
}

在这种情况下,音频转换器的回调函数非常简单(假设数据包大小和计数设置正确):

- (void) putPcmSamplesInBufferList:(AudioBufferList *)bufferList withCount:(UInt32 *)count
{
    bufferList->mBuffers[0].mData = _pcmBuffer;         
    bufferList->mBuffers[0].mDataByteSize = _pcmBufferSize;
}

音频转换器的设置如下所示:

{
    // ...
    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);
    // ...
}

这似乎很简单,只是它不起作用。一旦 AVCaptureSession 运行,音频转换器(特别是 AudioConverterFillComplexBuffer)返回一个“hwiu”(使用中的硬件)错误。如果会话停止,转换工作正常,但我无法捕获任何内容...

我想知道是否有办法从 AVCaptureSession 中获取 AAC 流。我正在考虑的选项是:

  1. 以某种方式使用 AVAssetWriterInput 将音频样本编码为 AAC,然后以某种方式获取编码的数据包(而不是通过 AVAssetWriter,它只会写入文件)。

  2. 重新组织我的应用程序,使其仅在视频端使用 AVCaptureSession,在音频端使用 Audio Queues。这将使流控制(开始和停止录制,响应中断)更加复杂,我担心它可能会导致音频和视频之间的同步问题。而且,它似乎不是一个好的设计。

有谁知道是否可以从 AVCaptureSession 中取出 AAC?我必须在这里使用音频队列吗?这会让我陷入同步或控制问题吗?

【问题讨论】:

  • 您确定您的 AudioConverter 可以正常工作吗?例如,您是否尝试过关闭捕获和编码一些零?
  • 是的,我做到了(我想我在问题中也提到了这一点)。如果 AVCaptureSession 未处于“运行”状态,编码器工作正常。
  • 哎呀,对不起。看起来你陷入了困境。向捕获会话添加音频输入似乎会占用 AAC 编码器。
  • 我就是这么想的。我猜想捕获会话使用音频队列或使用编码器的一些较低级别的 API。唉,他们为什么不提供对他们处理表单的数据的访问权限......
  • 所以让 AVAssetWriter 将音频数据编码为文件并(小心地)流式传输该文件。人们使用类似的技术从硬件编码器流式传输 h264 数据。这就是你使用 x264 而不是硬件编码器的原因吗?

标签: ios avfoundation audio-recording avcapturesession aac


【解决方案1】:

我最终向 Apple 寻求建议(事实证明,如果您有付费开发者帐户,您可以这样做)。

似乎 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 资源,但会完成工作。

【讨论】:

  • 您介意发布随附的转换代码吗?主要是回调函数实现和_aacBuffer 和_pcmBuffer 定义。非常感谢。
  • 我正在寻找相同的 Mac 等效项,但找不到适用于 mac 的 mManufacturer。有什么想法吗?
  • AudioConverterFillComplexBuffer 中存在错误,仅在 iphone 中发生。不在 ipad 中
猜你喜欢
  • 1970-01-01
  • 2013-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多