将 AudioBufferList 转换为 CMSampleBuffer 会产生意外结果

Posted

技术标签:

【中文标题】将 AudioBufferList 转换为 CMSampleBuffer 会产生意外结果【英文标题】:Converting an AudioBufferList to a CMSampleBuffer Produces Unexpected Results 【发布时间】:2015-07-10 02:55:56 【问题描述】:

我正在尝试将从音频单元获得的 AudioBufferList 转换为 CMSampleBuffer,我可以将其传递到 AVAssetWriter 以保存来自麦克风的音频。这种转换是有效的,因为我为执行转换而进行的调用不会失败,但录制最终会失败,而且我在日志中看到一些似乎值得关注的输出。

我使用的代码如下所示:

- (void)handleAudiosamples:(AudioBufferList*)samples numSamples:(UInt32)numSamples hostTime:(UInt64)hostTime 
    // Create a CMSampleBufferRef from the list of samples, which we'll own
    AudioStreamBasicDescription monoStreamFormat;
    memset(&monoStreamFormat, 0, sizeof(monoStreamFormat));
    monoStreamFormat.mSampleRate = 48000;
    monoStreamFormat.mFormatID = kAudioFormatLinearPCM;
    monoStreamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
    monoStreamFormat.mBytesPerPacket = 2;
    monoStreamFormat.mFramesPerPacket = 1;
    monoStreamFormat.mBytesPerFrame = 2;
    monoStreamFormat.mChannelsPerFrame = 1;
    monoStreamFormat.mBitsPerChannel = 16;

    CMFormatDescriptionRef format = NULL;
    OSStatus status = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &monoStreamFormat, 0, NULL, 0, NULL, NULL, &format);
    if (status != noErr) 
        // really shouldn't happen
        return;
    

    CMSampleTimingInfo timing =  CMTimeMake(1, 48000), kCMTimeZero, kCMTimeInvalid ;

    CMSampleBufferRef sampleBuffer = NULL;
    status = CMSampleBufferCreate(kCFAllocatorDefault, NULL, false, NULL, NULL, format, numSamples, 1, &timing, 0, NULL, &sampleBuffer);
    if (status != noErr) 
        // couldn't create the sample buffer
        PTKLogError(@"Failed to create sample buffer");
        CFRelease(format);
      return;
    

    // add the samples to the buffer
    status = CMSampleBufferSetDataBufferFromAudioBufferList(sampleBuffer,
                                                            kCFAllocatorDefault,
                                                            kCFAllocatorDefault,
                                                            0,
                                                            samples);
    if (status != noErr) 
        PTKLogError(@"Failed to add samples to sample buffer");
        CFRelease(sampleBuffer);
        CFRelease(format);
        return;
    

    NSLog(@"Original sample buf size: %ld for %d samples from %d buffers, first buffer has size %d", CMSampleBufferGetTotalSampleSize(sampleBuffer), numSamples, samples->mNumberBuffers, samples->mBuffers[0].mDataByteSize);
    NSLog(@"Original sample buf has %ld samples", CMSampleBufferGetNumSamples(sampleBuffer));

正如我所提到的,代码本身似乎并没有失败,但 AVAssetWriter 不喜欢它,而且我正在创建的 CMSampleBuffer 的大小似乎为 0,基于事实正在记录以下日志条目:

2015-07-09 19:34:00.710 xxxx[1481:271334] Original sample buf size: 0 for 1024 samples from 1 buffers, first buffer has size 2048
2015-07-09 19:34:00.710 xxxx[1481:271334] Original sample buf has 1024 samples

奇怪的是,样本缓冲区报告它有 1024 个样本,但大小为 0。原始 AudioBufferList 有 2048 个字节的数据,这是我对 1024 个 2 字节样本的预期。

我在初始化和填充CMSampleBuffer 的方式上做错了吗?

【问题讨论】:

【参考方案1】:

事实证明,样本量恢复为 0 的事实是一个红鲱鱼。一旦我清理了一些东西——值得注意的是,我正确设置了时间戳,如下所示:

uint64_t timeNS = (uint64_t)(hostTime * _hostTimeToNSFactor);
CMTime presentationTime = CMTimeMake(timeNS, 1000000000);
CMSampleTimingInfo timing =  CMTimeMake(1, 48000), presentationTime, kCMTimeInvalid ;

录音开始工作了。

因此,如果有人被报告的 0 样本缓冲区大小吓跑了,请注意这是可以的,至少在您将数据输入到 AVAssetWriter 的情况下。

【讨论】:

@PabloMartinez 我们通过调用mach_timebase_info 和这个数学计算它:_hostTimeToNSFactor = (double)tinfo.numer / tinfo.denom。但是,可能有更好的方法:developer.apple.com/library/ios/qa/qa1643/_index.html 谢谢 Jim,您在 hostTime 上传递给函数的值是什么? 你好 Jim 你能告诉我什么是 hostTime 吗?我不知道从哪里得到那个参数。我只有一个 AudioBufferList Pablo,抱歉回复晚了:我以为我已经回答了你的问题,但显然我没有。在这段代码中,hostTime 是我们在音频单元回调中得到的时间戳:[sampleDelegate handleAudioSamples:&list numSamples:inNumberFrames hostTime:inTimeStamp->mHostTime]; 嗨,你有完整的代码示例可以展示吗?我有同样的问题,但并不真正了解不同的部分是如何结合在一起的。 tnx

以上是关于将 AudioBufferList 转换为 CMSampleBuffer 会产生意外结果的主要内容,如果未能解决你的问题,请参考以下文章

AudioBufferList to float ** 转换 EZAudio EZMicrophone for Visual Plotting

aurioTouch2 录音问题。我需要将数据从一个 AudioBufferList 添加到另一个

iPhone:AudioBufferList 初始化和释放

如何在渲染回调中交错非交错的 AudioBufferList?

MAGNOLIA CMS - 使用 cmsu:simpleSearch 和 cmsu:searchResultSnippet 链接无法正确显示并且无法将日期转换为可读格式

AudioBufferList 的 AurioTouch 用途