2012-05-30 9 views
7

Sto scrivendo un'app per iOS che trasmette video e audio attraverso la rete.Posso usare AVCaptureSession per codificare un flusso AAC in memoria?

Sto usando AVCaptureSession per afferrare fotogrammi video grezzi usando AVCaptureVideoDataOutput e codificarli nel software using x264. Funziona alla grande

Volevo fare lo stesso per l'audio, solo che non ho bisogno di tanto controllo sul lato audio, quindi volevo usare l'encoder hardware integrato per produrre un flusso AAC. Questo significava usare Audio Converter dal livello Audio Toolbox. Per fare così ho messo in un gestore per frame audio AVCaptudeAudioDataOutput s':

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

In questo caso la funzione di callback per il convertitore audio è piuttosto semplice (assumendo dimensioni dei pacchetti e conta sono installati correttamente):

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

E la configurazione del convertitore audio si presenta così:

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

questo sembra abbastanza dritto inoltra solo il NON FUNZIONA. Una volta che AVCaptureSession è in esecuzione, il convertitore audio (in particolare AudioConverterFillComplexBuffer) restituisce un errore "hwiu" (hardware in uso). La conversione funziona correttamente se la sessione viene interrotta ma non riesco a catturare nulla ...

Mi chiedevo se esistesse un modo per ottenere un flusso AAC da AVCaptureSession. Le opzioni che sto considerando sono:

  1. In qualche modo usando AVAssetWriterInput per codificare i campioni audio in AAC e quindi ottenere i pacchetti codificati in qualche modo (non attraverso AVAssetWriter, che solo scrivere in un file).

  2. Riorganizzare la mia app in modo che utilizzi AVCaptureSession solo sul lato video ed utilizza Audio Queues sul lato audio. Questo renderà il controllo di flusso (avvio e l'arresto della registrazione, rispondendo alle interruzioni) più complicato e ho paura che potrebbe causare problemi synching tra l'audio e il video. Inoltre, non sembra un buon design.

Qualcuno sa se è possibile ottenere AAC da AVCaptureSession? Devo usare le code audio qui? Questo potrebbe portarmi a problemi di sincronizzazione o di controllo?

+0

Sei sicuro che il tuo AudioConverter funzioni? Hai provato a disattivare l'acquisizione e codificare alcuni zeri, ad esempio? –

+0

Sì, l'ho fatto (penso di averlo menzionato anche nella domanda). L'encoder funziona correttamente se AVCaptureSession non è in stato "in esecuzione". – Avner

+0

oops, mi dispiace. sembra che tu sia in un vicolo cieco. l'aggiunta di un ingresso audio alla sessione di acquisizione sembra legare il codificatore AAC. –

risposta

5

ho finito per chiedere di Apple per un consiglio (si scopre che si può fare se si dispone di un account di sviluppatore a pagamento).

Sembra che AVCaptureSession afferra una sospensione del codificatore hardware AAC, ma permette solo lo si utilizza per scrivere direttamente su file.

È possibile utilizzare l'encoder software, ma si deve chiedere specificamente invece di utilizzare 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; 
} 

con

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

L'encoder software assumerà le risorse della CPU, ovviamente, ma farà il lavoro.

+0

Ti dispiacerebbe pubblicare il codice di accompagnamento per la conversione? Principalmente l'implementazione della funzione callback e le definizioni _aacBuffer e _pcmBuffer. Molte grazie. –

+0

Sto cercando un equivalente Mac per lo stesso, ma mManufacturer non è stato trovato per mac. qualche idea ? – Dinesh

+0

C'è un errore in AudioConverterFillComplexBuffer che si verifica solo negli iPhone. non in iPad –

Problemi correlati