如何使用CoreAudio的的AudioConverter为en实时code AAC? [英] How do I use CoreAudio's AudioConverter to encode AAC in real-time?

查看:555
本文介绍了如何使用CoreAudio的的AudioConverter为en实时code AAC?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所有样品code我能找到一个使用 AudioConverterRef 专注于使用情况下,我把所有的数据前期(如磁盘转换文件)。他们通常所说的 AudioConverterFillComplexBuffer 与PCM要转换为 inInputDataProcUserData 键,只需填写其在回调。 (那是真的它应该如何使用?为什么它需要一个回调,然后呢?)对我的使用情况下,我想从麦克风流AAC音频,所以我没有文件,我的PCM缓冲正在实时填充

All the sample code I can find that uses AudioConverterRef focuses on use cases where I have all the data up-front (such as converting a file on disk). They commonly call AudioConverterFillComplexBuffer with the PCM to be converted as the inInputDataProcUserData and just fill it in in the callback. (Is that really how it's supposed to be used? Why does it need a callback, then?) For my use case, I'm trying to stream aac audio from the microphone, so I have no file, and my PCM buffer is being filled in in real time.

由于我没有所有数据的前期,我试着做 * ioNumberDataPackets = 0 在回调一旦我输入的数据已经出来了,但刚放AudioConverter在它需要的 AudioConverterReset()特德死状态,我没有得到任何数据出来。

Since I don't have all the data up-front, I've tried doing *ioNumberDataPackets = 0 in the callback once my input data is out, but that just puts the AudioConverter in a dead state where it needs to be AudioConverterReset()ted, and I don't get any data out of it.

一种方法,我在网上看到建议是从回调返回一个错误,如果我已经存储的数据太小,只是再试一次,我有更多的数据,但是,似乎是等资源的浪费,我不能让自己,甚至尝试它。

One approach I've seen suggested online is to return an error from the callback if the data I have stored is too small, and just try again once I have more data, but that seems like such a waste of resources that I can't bring myself to even try it.

我真的需要做到重试,直到我的输入缓冲区足够大,还是有更好的办法?

Do I really need to do the "retry until my input buffer is big enough", or is there a better way?

推荐答案

AudioConverterFillComplexBuffer 实际上并不意味着我的输入缓冲区填满EN codeR 我在这里。它的意思是填补了这一输出缓冲这里有连接从EN codeR codeD数据。有了这个角度来看,回调突然有意义 - 它是用来获取源数据,以满足填补了这一输出缓冲区为我的请求。也许这是有目共睹的人,但我花了一个的的时间来了解这一点(从所有AudioConverter样品code我看到漂浮在那里的人们通过 inInputDataProcUserData ,我猜,我不是唯一的一个)。

AudioConverterFillComplexBuffer does not actually mean "fill the encoder with my input buffer that I have here". It means "fill this output buffer here with encoded data from the encoder". With this perspective, the callback suddenly makes sense -- it is used to fetch source data to satisfy the "fill this output buffer for me" request. Maybe this is obvious to others, but it took me a long time to understand this (and from all the AudioConverter sample code I see floating around where people send input data through inInputDataProcUserData, I'm guessing I'm not the only one).

AudioConverterFillComplexBuffer 呼叫阻塞,并期待您从回调同步传送数据给它。如果你是实时编码,你将因此需要调用 FillComplexBuffer 上一个单独的线程,你建立你自己。在回调,然后你可以检查可用的输入数据,如果没有,则需要一个信号量阻塞。使用NSCondition的EN codeR线程随后将是这个样子:

The AudioConverterFillComplexBuffer call is blocking, and is expecting you to deliver data to it synchronously from the callback. If you are encoding in real time, you will thus need to call FillComplexBuffer on a separate thread that you set up yourself. In the callback, you can then check for available input data, and if it is not available, you need to block on a semaphore. Using an NSCondition, the encoder thread would then look something like this:

- (void)startEncoder
{
    OSStatus creationStatus = AudioConverterNew(&_fromFormat, &_toFormat, &_converter);

    _running = YES;
    _condition = [[NSCondition alloc] init];
    [self performSelectorInBackground:@selector(_encoderThread) withObject:nil];
}

- (void)_encoderThread
{
    while(_running) {
        // Make quarter-second buffers.
        size_t bufferSize = (_outputBitrate/8) * 0.25;
        NSMutableData *outAudioBuffer = [NSMutableData dataWithLength:bufferSize];
        AudioBufferList outAudioBufferList;
        outAudioBufferList.mNumberBuffers = 1;
        outAudioBufferList.mBuffers[0].mNumberChannels = _toFormat.mChannelsPerFrame;
        outAudioBufferList.mBuffers[0].mDataByteSize = (UInt32)bufferSize;
        outAudioBufferList.mBuffers[0].mData = [outAudioBuffer mutableBytes];

        UInt32 ioOutputDataPacketSize = 1;

        _currentPresentationTime = kCMTimeInvalid; // you need to fill this in during FillComplexBuffer
        const OSStatus conversionResult = AudioConverterFillComplexBuffer(_converter, FillBufferTrampoline, (__bridge void*)self, &ioOutputDataPacketSize, &outAudioBufferList, NULL);

        // here I convert the AudioBufferList into a CMSampleBuffer, which I've omitted for brevity.
        // Ping me if you need it.
        [self.delegate encoder:self encodedSampleBuffer:outSampleBuffer];
    }
}

和回调看起来是这样的:(请注意,我通常用这个蹦床立即转发给方法在我的实例(由 inUserData 转发我的实例,这省略步骤为简洁起见)):

And the callback could look like this: (note that I normally use this trampoline to immediately forward to a method on my instance (by forwarding my instance in inUserData; this step is omitted for brevity)):

static OSStatus FillBufferTrampoline(AudioConverterRef               inAudioConverter,
                                        UInt32*                         ioNumberDataPackets,
                                        AudioBufferList*                ioData,
                                        AudioStreamPacketDescription**  outDataPacketDescription,
                                        void*                           inUserData)
{
    [_condition lock];

    UInt32 countOfPacketsWritten = 0;

    while (true) {
        // If the condition fires and we have shut down the encoder, just pretend like we have written 0 bytes and are done.
        if(!_running) break;

        // Out of input data? Wait on the condition.
        if(_inputBuffer.length == 0) {
            [_condition wait];
            continue;
        }

        // We have data! Fill ioData from your _inputBuffer here.
        // Also save the input buffer's start presentationTime here.

        // Exit out of the loop, since we're done waiting for data
        break;
    }

    [_condition unlock];

        // 2. Set ioNumberDataPackets to the amount of data remaining


    // if running is false, this will be 0, indicating EndOfStream
    *ioNumberDataPackets = countOfPacketsWritten;

    return noErr;
}

和完整性,这里是你将如何再与数据养活这个连接codeR,以及如何将其关闭正常:

And for completeness, here's how you would then feed this encoder with data, and how to shut it down properly:

- (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
    [_condition lock];
    // Convert sampleBuffer and put it into _inputBuffer here
    [_condition broadcast];
    [_condition unlock];
}

- (void)stopEncoding
{
    [_condition lock];
    _running = NO;
    [_condition broadcast];
    [_condition unlock];
}

这篇关于如何使用CoreAudio的的AudioConverter为en实时code AAC?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆