Czy Mogę użyć AVCaptureSession do kodowania strumienia AAC do pamięci?

Piszę aplikację na iOS, która przesyła wideo i audio przez sieć.

Używam Avcapturession do przechwytywania surowych klatek wideo za pomocą AVCaptureVideoDataOutput i kodowania ich w oprogramowaniu za pomocą x264. To działa świetnie.

Chciałem zrobić to samo dla audio, tylko, że nie potrzebuję tyle kontroli po stronie audio, więc chciałem użyć wbudowanego kodera sprzętowego do wytworzenia strumienia AAC. Oznaczało to użycie Audio Konwerter z warstwy Audio Toolbox. W tym celu dodałem obsługę ramek audio 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...
    }
}

W tym przypadku funkcja wywołania zwrotnego dla konwertera audio jest dość prosta (zakładając, że wielkości pakietów i liczby są ustawione poprawnie):

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

A konfiguracja konwertera audio wygląda tak:

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

To wydaje się dość proste tylko to nie działa . Po uruchomieniu AVCaptureSession dźwięk konwerter (w szczególności AudioConverterFillComplexBuffer) zwraca błąd "hwiu" (sprzęt w użyciu). Konwersja działa dobrze, jeśli sesja zostanie zatrzymana, ale wtedy nie mogę niczego przechwycić...

Zastanawiałem się, czy jest sposób, aby uzyskać strumień AAC z AVCaptureSession. Opcje, które rozważam to:

  1. W jakiś sposób używając AVAssetWriterInput do kodowania próbek audio do AAC, a następnie uzyskać zakodowane pakiety w jakiś sposób (nie przez AVAssetWriter, który zapisywałby tylko do plik).

  2. Reorganizacja mojej aplikacji tak, aby używała Avcapturession tylko po stronie wideo i używa kolejek Audio po stronie audio. To sprawi, że kontrola przepływu (uruchamianie i zatrzymywanie nagrywania, reagowanie na przerwy) będzie bardziej skomplikowana i obawiam się, że może to spowodować problemy z synchronizacją między audio i wideo. Poza tym, to po prostu nie wygląda na dobry projekt.

Czy ktoś wie czy uzyskanie AAC z AVCaptureSession jest możliwe? Czy Ja musisz tu korzystać z kolejek Audio? Czy to może doprowadzić mnie do problemów z synchronizacją lub kontrolą?

Author: Cœur, 2012-05-30

1 answers

W końcu poprosiłem Apple o radę (okazuje się, że możesz to zrobić, jeśli masz płatne konto dewelopera).

Wygląda na to, że Avcapturession chwyta sprzętowy koder AAC, ale pozwala go używać tylko do zapisu bezpośrednio do pliku.

Możesz użyć kodera oprogramowania, ale musisz poprosić o niego konkretnie, zamiast używać 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;
}

Z

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

Koder oprogramowania oczywiście zajmie zasoby procesora, ale dostanie zadanie załatwione.

 7
Author: Avner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-06 20:33:53