Ошибка преобразования AudioBufferList в CMBlockBufferRef
Я пытаюсь взять видеофайл, прочитать его при помощи AVAssetReader и передать аудио в CoreAudio для обработки (добавления эффектов и прочего) перед сохранением его обратно на диск с помощью AVAssetWriter. Я хотел бы отметить, что если я установлю componentSubType для AudioComponentDescription моего выходного узла как RemoteIO, все будет работать правильно через динамики. Это дает мне уверенность, что мой AUGraph настроен правильно, так как я слышу, как все работает. Я устанавливаю подтип как GenericOutput, так что я могу сделать рендеринг самостоятельно и вернуть отрегулированный звук.
Я читаю в аудио, и я передаю CMSampleBufferRef для copyBuffer. Это помещает аудио в кольцевой буфер, который будет прочитан позже.
- (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;
}
}
Далее я вызываю processOutput. Это сделает ручное чтение на outputUnit. Когда вызывается AudioUnitRender, он вызывает описанный ниже воспроизведение Callback, которое подключается как обратный вызов ввода на моем первом узле. PlayCallback извлекает данные из кольцевого буфера и передает их в переданный входящий аудиоустройство. Как я уже говорил, если выход установлен как RemoteIO, это приведет к правильному воспроизведению звука на динамиках. Когда AudioUnitRender завершает работу, он возвращает noErr, и объект bufferList содержит допустимые данные. Когда я вызываю CMSampleBufferSetDataBufferFromAudioBufferList, я получаю 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;
}
CMSampleBufferRef, который я передаю, выглядит корректно (ниже дамп объекта из отладчика)
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
Список буферов выглядит так
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)
}
Действительно в недоумении, надеясь, что кто-то может помочь. Спасибо,
В случае, если это имеет значение, я отлаживаю это в симуляторе ios 8.3, и звук идет с mp4, который я снял на своем iphone 6, а затем сохранил на своем ноутбуке.
Я прочитал следующие вопросы, но все равно безрезультатно, все не работает.
Как конвертировать AudioBufferList в CMSampleBuffer?
Преобразование AudioBufferList в CMSampleBuffer дает неожиданные результаты
CMSampleBufferSetDataBufferFromAudioBufferList возвращает ошибку 12731
ядро офлайн рендеринга GenericOutput
ОБНОВИТЬ
Я осмотрелся еще немного и заметил, что когда мой AudioBufferList прямо перед запуском AudioUnitRender выглядит так:
bufferList->mNumberBuffers = 2,
bufferList->mBuffers[0].mNumberChannels = 1,
bufferList->mBuffers[0].mDataByteSize = 2048
mDataByteSize - это numberFrames*sizeof(SInt32), которое равно 512 * 4. Когда я смотрю на AudioBufferList, переданный в PlayCallback, список выглядит так:
bufferList->mNumberBuffers = 1,
bufferList->mBuffers[0].mNumberChannels = 1,
bufferList->mBuffers[0].mDataByteSize = 1024
не совсем уверен, куда направляется этот другой буфер или другой размер 1024 байта...
если когда я закончу звонить Реднеру, если я сделаю что-то подобное
AudioBufferList newbuff;
newbuff.mNumberBuffers = 1;
newbuff.mBuffers[0] = bufferList->mBuffers[0];
newbuff.mBuffers[0].mDataByteSize = 1024;
и передать newbuff в CMSampleBufferSetDataBufferFromAudioBufferList ошибка исчезнет.
Если я пытаюсь установить для размера BufferList значение 1 mNumberBuffers или его значение mDataByteSize равным numberFrames*sizeof(SInt16), при вызове AudioUnitRender я получу -50
ОБНОВЛЕНИЕ 2
Я подключил обратный вызов рендеринга, чтобы проверить выходной сигнал при воспроизведении звука через динамики. Я заметил, что выход, который идет к динамикам, также имеет AudioBufferList с 2 буферами, и mDataByteSize во время обратного вызова ввода равен 1024, а в обратном вызове рендеринга - 2048, что является тем же, что я видел при ручном вызове AudioUnitRender. Когда я проверяю данные в визуализированном AudioBufferList, я замечаю, что байты в 2 буферах одинаковы, что означает, что я могу просто игнорировать второй буфер. Но я не уверен, как справиться с тем фактом, что после визуализации данные имеют размер 2048, а не 1024. Есть идеи, почему это может происходить? После прохождения звукового графика это в более сырой форме, и поэтому размер удваивается?
1 ответ
Похоже, проблема, с которой вы имеете дело, связана с расхождением в количестве каналов. Причина, по которой вы видите данные в блоках 2048 вместо 1024, заключается в том, что они возвращают вам два канала (стерео). Убедитесь, что все ваши аудиоустройства правильно сконфигурированы для использования моно во всем аудио графике, включая Pitch Unit и любые описания аудиоформатов.
Особо остерегайтесь того, что AudioUnitSetProperty
может потерпеть неудачу - так что обязательно заверните их в CheckError()
также.