将AudioBufferList转换为CMBlockBufferRef时出错
我正在尝试使用AVAssetReader将视频文件读取,并将音频传递给CoreAudio进行处理(添加效果和内容),然后使用AVAssetWriter将其保存回磁盘。 我想指出的是,如果我将我的输出节点的AudioComponentDescription上的componentSubType设置为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上执行手动reder。 当调用AudioUnitRender时,它会调用下面的playbackCallback,这是我的第一个节点上连接的输入回调。 playbackCallback将数据从循环缓冲区中取出,并将其馈送到传入的audioBufferList中。就像我之前所说的,如果输出设置为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模拟器中进行调试,音频来自我在iphone 6上拍摄的mp4,然后保存到我的笔记本电脑上。
我已阅读以下问题,但仍无济于事,事情并不奏效。
如何将AudioBufferList转换为CMSampleBuffer?
将AudioBufferList转换为CMSampleBuffer会产生意外的结果
CMSampleBufferSetDataBufferFromAudioBufferList返回错误12731
核心音频离线渲染GenericOutput
UPDATE
我探讨了更多,并注意到,当AudioUnitRender运行之前,我的AudioBufferList看起来像这样:
bufferList->mNumberBuffers = 2,
bufferList->mBuffers[0].mNumberChannels = 1,
bufferList->mBuffers[0].mDataByteSize = 2048
mDataByteSize是numberFrames * sizeof(SInt32),它是512 * 4。当我查看在playbackCallback中传递的AudioBufferList时,列表如下所示:
bufferList->mNumberBuffers = 1,
bufferList->mBuffers[0].mNumberChannels = 1,
bufferList->mBuffers[0].mDataByteSize = 1024
不太确定其他缓冲区的位置,或其他1024字节的大小......
如果当我完成调用Redner时,如果我做这样的事情
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
我连接了一个渲染回调函数,以便在扬声器上播放声音时检查输出。 我注意到扬声器的输出也有一个带有2个缓冲区的AudioBufferList,输入回调期间的mDataByteSize是1024,呈现回调是2048,这与我手动调用AudioUnitRender时看到的相同。 当我检查呈现的AudioBufferList中的数据时,我注意到2个缓冲区中的字节是相同的,这意味着我可以忽略第二个缓冲区。 但是我不确定如何处理这样的事实,即在数据被呈现而不是1024之后,数据的大小是2048.关于为什么会出现这种情况的任何想法? 在通过音频图形之后它是否更多是一种原始形式,这就是为什么大小加倍的原因?
听起来你正在处理的问题是由于频道数量的差异。 你在2048块而不是1024块数据中看到的数据是因为它将两个通道(立体声)回馈给你。 检查并确保所有音频单元均已正确配置为在整个音频图中使用单声道,包括音高单元和任何音频格式说明。
要特别注意的一件事是,对AudioUnitSetProperty
调用可能会失败 - 所以一定要将它们封装在CheckError()
中。