2015-07-19 11 views
9

Sto provando a prendere un file video per leggerlo usando AVAssetReader e passare l'audio a CoreAudio per l'elaborazione (aggiungendo effetti e cose) prima di salvarlo su disco usando AVAssetWriter. Vorrei sottolineare che, se imposto ComponentSubType su AudioComponentDescription del mio nodo di uscita come RemoteIO, le cose vengono riprodotte correttamente attraverso gli altoparlanti. Questo mi rende fiducioso che il mio AUGRAPH sia configurato correttamente, in quanto sento che le cose funzionano. Sto impostando il sottotipo su GenericOutput anche se così posso eseguire il rendering da solo e ripristinare l'audio regolato.errore di conversione di AudioBufferList in CMBlockBufferRef

Sto leggendo nell'audio e passo il CMSampleBufferRef su copyBuffer. Questo mette l'audio in un buffer circolare che verrà letto in seguito.

- (void)copyBuffer:(CMSampleBufferRef)buf { 
    if (_readyForMoreBytes == NO) 
    { 
     return; 
    } 

    AudioBufferList abl; 
    CMBlockBufferRef blockBuffer; 
    CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(buf, NULL, &abl, sizeof(abl), NULL, NULL, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer); 

    UInt32 size = (unsigned int)CMSampleBufferGetTotalSampleSize(buf); 
    BOOL bytesCopied = TPCircularBufferProduceBytes(&circularBuffer, abl.mBuffers[0].mData, size); 

    if (!bytesCopied){ 
     / 
     _readyForMoreBytes = NO; 

     if (size > kRescueBufferSize){ 
      NSLog(@"Unable to allocate enought space for rescue buffer, dropping audio frame"); 
     } else { 
      if (rescueBuffer == nil) { 
       rescueBuffer = malloc(kRescueBufferSize); 
      } 

      rescueBufferSize = size; 
      memcpy(rescueBuffer, abl.mBuffers[0].mData, size); 
     } 
    } 

    CFRelease(blockBuffer); 
    if (!self.hasBuffer && bytesCopied > 0) 
    { 
     self.hasBuffer = YES; 
    } 
} 

Successivamente chiamo processOutput. Questo farà un reder manuale su outputUnit. Quando viene chiamato AudioUnitRender, richiama il playbackCallback qui sotto, che è ciò che è collegato come callback di input sul mio primo nodo. playbackCallback estrae i dati dal buffer circolare e li alimenta nella cartella AudioBuffer passata. Come ho detto prima, se l'uscita è impostata come RemoteIO, l'audio verrà riprodotto correttamente sugli altoparlanti. Al termine di AudioUnitRender, restituisce noErr e l'oggetto bufferList contiene dati validi. Quando chiamo CMSampleBufferSetDataBufferFromAudioBufferList, tuttavia ottengo kCMSampleBufferError_RequiredParameterMissing (-12731).

-(CMSampleBufferRef)processOutput 
{ 
    if(self.offline == NO) 
    { 
     return NULL; 
    } 

    AudioUnitRenderActionFlags flags = 0; 
    AudioTimeStamp inTimeStamp; 
    memset(&inTimeStamp, 0, sizeof(AudioTimeStamp)); 
    inTimeStamp.mFlags = kAudioTimeStampSampleTimeValid; 
    UInt32 busNumber = 0; 

    UInt32 numberFrames = 512; 
    inTimeStamp.mSampleTime = 0; 
    UInt32 channelCount = 2; 

    AudioBufferList *bufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList)+sizeof(AudioBuffer)*(channelCount-1)); 
    bufferList->mNumberBuffers = channelCount; 
    for (int j=0; j<channelCount; j++) 
    { 
     AudioBuffer buffer = {0}; 
     buffer.mNumberChannels = 1; 
     buffer.mDataByteSize = numberFrames*sizeof(SInt32); 
     buffer.mData = calloc(numberFrames,sizeof(SInt32)); 

     bufferList->mBuffers[j] = buffer; 

    } 
    CheckError(AudioUnitRender(outputUnit, &flags, &inTimeStamp, busNumber, numberFrames, bufferList), @"AudioUnitRender outputUnit"); 

    CMSampleBufferRef sampleBufferRef = NULL; 
    CMFormatDescriptionRef format = NULL; 
    CMSampleTimingInfo timing = { CMTimeMake(1, 44100), kCMTimeZero, kCMTimeInvalid }; 
    AudioStreamBasicDescription audioFormat = self.audioFormat; 
    CheckError(CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &audioFormat, 0, NULL, 0, NULL, NULL, &format), @"CMAudioFormatDescriptionCreate"); 
    CheckError(CMSampleBufferCreate(kCFAllocatorDefault, NULL, false, NULL, NULL, format, numberFrames, 1, &timing, 0, NULL, &sampleBufferRef), @"CMSampleBufferCreate"); 
    CheckError(CMSampleBufferSetDataBufferFromAudioBufferList(sampleBufferRef, kCFAllocatorDefault, kCFAllocatorDefault, 0, bufferList), @"CMSampleBufferSetDataBufferFromAudioBufferList"); 

    return sampleBufferRef; 
} 


static OSStatus playbackCallback(void *inRefCon, 
           AudioUnitRenderActionFlags *ioActionFlags, 
           const AudioTimeStamp *inTimeStamp, 
           UInt32 inBusNumber, 
           UInt32 inNumberFrames, 
           AudioBufferList *ioData) 
{ 
    int numberOfChannels = ioData->mBuffers[0].mNumberChannels; 
    SInt16 *outSample = (SInt16 *)ioData->mBuffers[0].mData; 

    / 
    memset(outSample, 0, ioData->mBuffers[0].mDataByteSize); 

    MyAudioPlayer *p = (__bridge MyAudioPlayer *)inRefCon; 

    if (p.hasBuffer){ 
     int32_t availableBytes; 
     SInt16 *bufferTail = TPCircularBufferTail([p getBuffer], &availableBytes); 

     int32_t requestedBytesSize = inNumberFrames * kUnitSize * numberOfChannels; 

     int bytesToRead = MIN(availableBytes, requestedBytesSize); 
     memcpy(outSample, bufferTail, bytesToRead); 
     TPCircularBufferConsume([p getBuffer], bytesToRead); 

     if (availableBytes <= requestedBytesSize*2){ 
      [p setReadyForMoreBytes]; 
     } 

     if (availableBytes <= requestedBytesSize) { 
      p.hasBuffer = NO; 
     }  
    } 
    return noErr; 
} 

Il CMSampleBufferRef passo nel sembra valido (qui di seguito è una discarica dell'oggetto dal debugger)

CMSampleBuffer 0x7f87d2a03120 retainCount: 1 allocator: 0x103333180 
    invalid = NO 
    dataReady = NO 
    makeDataReadyCallback = 0x0 
    makeDataReadyRefcon = 0x0 
    formatDescription = <CMAudioFormatDescription 0x7f87d2a02b20 [0x103333180]> { 
    mediaType:'soun' 
    mediaSubType:'lpcm' 
    mediaSpecific: { 
    ASBD: { 
    mSampleRate: 44100.000000 
    mFormatID: 'lpcm' 
    mFormatFlags: 0xc2c 
    mBytesPerPacket: 2 
    mFramesPerPacket: 1 
    mBytesPerFrame: 2 
    mChannelsPerFrame: 1 
    mBitsPerChannel: 16 } 
    cookie: {(null)} 
    ACL: {(null)} 
    } 
    extensions: {(null)} 
} 
    sbufToTrackReadiness = 0x0 
    numSamples = 512 
    sampleTimingArray[1] = { 
    {PTS = {0/1 = 0.000}, DTS = {INVALID}, duration = {1/44100 = 0.000}}, 
    } 
    dataBuffer = 0x0 

La lista dei buffer si presenta così

Printing description of bufferList: 
(AudioBufferList *) bufferList = 0x00007f87d280b0a0 
Printing description of bufferList->mNumberBuffers: 
(UInt32) mNumberBuffers = 2 
Printing description of bufferList->mBuffers: 
(AudioBuffer [1]) mBuffers = { 
    [0] = (mNumberChannels = 1, mDataByteSize = 2048, mData = 0x00007f87d3008c00) 
} 

davvero in perdita qui, sperando che qualcuno possa aiutarti. Grazie,

Nel caso in cui è importante eseguire il debug di questo in simulatore iOS 8.3 e l'audio proviene da un mp4 che ho girato sul mio iphone 6, quindi salvato sul mio laptop.

Ho letto i seguenti problemi, tuttavia ancora inutilmente, le cose non funzionano.

How to convert AudioBufferList to CMSampleBuffer?

Converting an AudioBufferList to a CMSampleBuffer Produces Unexpected Results

CMSampleBufferSetDataBufferFromAudioBufferList returning error 12731

core audio offline rendering GenericOutput

UPDATE

ho curiosato un po 'di più e notare che quando il mio AudioBufferList destra prima Audio UnitRender corre assomiglia a questo:

bufferList->mNumberBuffers = 2, 
bufferList->mBuffers[0].mNumberChannels = 1, 
bufferList->mBuffers[0].mDataByteSize = 2048 

mDataByteSize è numberFrames * sizeof (SInt32), che è 512 * 4. Quando guardo l'AudioBufferList passato in playbackCallback, la lista si presenta così:

bufferList->mNumberBuffers = 1, 
bufferList->mBuffers[0].mNumberChannels = 1, 
bufferList->mBuffers[0].mDataByteSize = 1024 

non proprio sicuro di dove sta andando l'altro buffer o l'altra dimensione di 1024 byte ...

se quando torno a finito chiamando Redner se faccio qualcosa di simile

AudioBufferList newbuff; 
newbuff.mNumberBuffers = 1; 
newbuff.mBuffers[0] = bufferList->mBuffers[0]; 
newbuff.mBuffers[0].mDataByteSize = 1024; 

e passare newbuff off per CMSampleBufferSetDataBufferFromAudioBufferList l'errore va via.

Se provo impostare la dimensione della BufferList di avere 1 mNumberBuffers o la sua mDataByteSize per essere numberFrames * sizeof (SInt16) ottengo un -50 quando si chiama AudioUnitRender

UPDATE 2

ho collegato una richiamata di rendering in modo da poter controllare l'output quando suono l'audio sopra gli altoparlanti. Ho notato che l'uscita che va agli altoparlanti ha anche un AudioBufferList con 2 buffer, e il mDataByteSize durante il callback dell'ingresso è 1024 e nel callback del rendering è il 2048, che è lo stesso che ho visto quando chiamavo manualmente AudioUnitRender. Quando controllo i dati nella AudioBufferList visualizzata, noto che i byte nei 2 buffer sono gli stessi, il che significa che posso semplicemente ignorare il secondo buffer. Ma non sono sicuro di come gestire il fatto che i dati hanno una dimensione di 2048 dopo essere stati renderizzati invece di 1024 mentre vengono caricati. Qualche idea sul perché ciò potrebbe accadere? È più di una forma grezza dopo aver attraversato il grafico audio ed è per questo che la dimensione è raddoppiata?

risposta

1

Sembra che il problema da affrontare sia dovuto a una discrepanza nel numero di canali. Il motivo per cui visualizzi i dati nei blocchi di 2048 anziché 1024 è perché ti riporta indietro di due canali (stereo). Verificare che tutte le unità audio siano configurate correttamente per utilizzare mono in tutto l'intero grafico audio, inclusa l'unità di tono e qualsiasi descrizione del formato audio.

Una cosa da tenere in particolare considerazione è che le chiamate a AudioUnitSetProperty non possono riuscire, quindi assicurati di avvolgere anche quelle in CheckError().

+0

Capito cosa sta succedendo qui. Stavo ascoltando il suono mono -> inviandolo all'unità di pitch che lo stava convertendo in stereo. Il formato che stavo passando a CMAudioFormatDescriptionCreate, era mono, e doveva essere stereo poiché era quello che erano i dati in AudioBufferList. – odyth

Problemi correlati