我可以使用AVCaptureSession编码的AAC流内存?(Can I use AVCaptur

2019-07-29 15:23发布

我正在写一个iOS应用程序,可通过网络视频和音频流。

我使用AVCaptureSession使用AVCaptureVideoDataOutput抢原始视频帧和编码它们的软件使用X264 。 这个伟大的工程。

我想为音频做同样的,只是我不需要那么在音频方面的控制程度,所以我想使用内置的硬件编码器产生的AAC音频流。 这意味着使用音频转换器从音频工具箱层。 为了做到这一点,我把在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”(硬件在使用中)错误。 如果会话停止转换工作正常,但我不能捕获任何...

我不知道是否有一种方式来获得一个AAC流出来AVCaptureSession的。 我正在考虑的选项包括:

  1. 不知怎的,使用AVAssetWriterInput编码音频样本插入到AAC,然后(不通过AVAssetWriter,这将只写入一个文件)以某种方式获取编码的数据包。

  2. 重新组织我的应用程序,使其只在视频方面采用AVCaptureSession并使用音频队列在音频方面。 这将使流量控制(启动和停止录音,响应中断)更复杂,我担心这样可能会导致音频与视频之间synching问题。 此外,它只是似乎并不像一个很好的设计。

有谁知道,如果得到AAC出AVCaptureSession是可能的? 我一定要在这里使用音频队列? 难道这让我进入synching或控制的问题?

Answer 1:

最后我问苹果公司的意见(事实证明,你可以做,如果你有一个付费开发商帐户)。

看来,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资源,当然,但会完成这项工作。



文章来源: Can I use AVCaptureSession to encode an AAC stream to memory?