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?
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