【问题标题】:Using CMSampleTimingInfo, CMSampleBuffer and AudioBufferList from raw PCM stream使用原始 PCM 流中的 CMSampleTimingInfo、CMSampleBuffer 和 AudioBufferList
【发布时间】:2015-12-21 14:32:16
【问题描述】:

我从 Google 的 WebRTC C++ 参考实现(插入到VoEBaseImpl::GetPlayoutData 的钩子)接收原始 PCM 流。音频似乎是线性 PCM,签名为 int16,但在使用 AssetWriter 录制时,它会保存到音频文件中高度失真和更高的音调。

我假设这是输入参数的某个错误,很可能是关于将 stereo-int16 转换为 AudioBufferList,然后再转换为 CMSampleBuffer。下面的代码有问题吗?

void RecorderImpl::RenderAudioFrame(void* audio_data, size_t number_of_frames, int sample_rate, int64_t elapsed_time_ms, int64_t ntp_time_ms) {
    OSStatus status;

    AudioChannelLayout acl;
    bzero(&acl, sizeof(acl));
    acl.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;

    AudioStreamBasicDescription audioFormat;
    audioFormat.mSampleRate = sample_rate;
    audioFormat.mFormatID = kAudioFormatLinearPCM;
    audioFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
    audioFormat.mFramesPerPacket = 1;
    audioFormat.mChannelsPerFrame = 2;
    audioFormat.mBitsPerChannel = 16;
    audioFormat.mBytesPerPacket = audioFormat.mFramesPerPacket * audioFormat.mChannelsPerFrame * audioFormat.mBitsPerChannel / 8;
    audioFormat.mBytesPerFrame = audioFormat.mBytesPerPacket / audioFormat.mFramesPerPacket;

    CMSampleTimingInfo timing = { CMTimeMake(1, sample_rate), CMTimeMake(elapsed_time_ms, 1000), kCMTimeInvalid };

    CMFormatDescriptionRef format = NULL;
    status = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &audioFormat, sizeof(acl), &acl, 0, NULL, NULL, &format);
    if(status != 0) {
        NSLog(@"Failed to create audio format description");
        return;
    }

    CMSampleBufferRef buffer;
    status = CMSampleBufferCreate(kCFAllocatorDefault, NULL, false, NULL, NULL, format, (CMItemCount)number_of_frames, 1, &timing, 0, NULL, &buffer);
    if(status != 0) {
        NSLog(@"Failed to allocate sample buffer");
        return;
    }

    AudioBufferList bufferList;
    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0].mNumberChannels = audioFormat.mChannelsPerFrame;
    bufferList.mBuffers[0].mDataByteSize = (UInt32)(number_of_frames * audioFormat.mBytesPerFrame);
    bufferList.mBuffers[0].mData = audio_data;
    status = CMSampleBufferSetDataBufferFromAudioBufferList(buffer, kCFAllocatorDefault, kCFAllocatorDefault, 0, &bufferList);
    if(status != 0) {
        NSLog(@"Failed to convert audio buffer list into sample buffer");
        return;
    }

    [recorder writeAudioFrames:buffer];

    CFRelease(buffer);
}

作为参考,我在 iPhone 6S+ / iOS 9.2 上从 WebRTC 接收的采样率为 48kHz,每次调用此挂钩时有 480 个样本,我每 10 毫秒接收一次数据。

【问题讨论】:

  • 你好汤姆,我的项目也发生了同样的事情。可以输入avassetwriter配置吗?我在 AudioStreamBasicDescription 上做的和你一样,但仍然失真。什么是 elapsed_time_ms?

标签: ios objective-c webrtc core-audio


【解决方案1】:

首先,恭喜你冒昧地从头开始创建音频CMSampleBuffer。对于大多数人来说,它们既不是被创造也不是被毁灭,而是从CoreMediaAVFoundation传下来的完美而神秘的。

您的计时信息中的presentationTimeStamps 以​​整数毫秒为单位,不能及时代表您的 48kHz 样本的位置。

试试CMTimeMake(elapsed_frames, sample_rate),而不是CMTimeMake(elapsed_time_ms, 1000),其中elapsed_frames 是您之前写入的帧数。

这可以解释失真,但不能解释音高,因此请确保AudioStreamBasicDescription 与您的AVAssetWriterInput 设置相匹配。没有看到你的AVAssetWriter 代码很难说。

p.s 注意writeAudioFrames - 如果它是异步的,您将遇到audio_data 的所有权问题。

p.p.s.看起来你正在泄露CMFormatDescriptionRef

【讨论】:

  • 在这里感谢您的帮助。事实上,这证实了我尝试的设置的排列之一是有效的,这背后的原因很清楚。但它并没有解决我的问题,请看我的回答...
  • 当您期待立体声时,您得到的是单声道?很好的发现。图片对音频很有帮助。
  • 我也有同样的问题,只有我的输入是 16000 采样率,输入 (const void *)audioData frames:(UInt32)len ,像 CMTime time = CMTimeMake(len / 2 , kSamplingRate); CMSampleTimingInfo timing = { CMTimeMake(1, kSamplingRate), time, kCMTimeInvalid }; error = CMSampleBufferCreate(kCFAllocatorDefault, NULL, false, NULL, NULL, format, len / 2, 1, &timing, 0, NULL, &buff); 一样使用,但仍然高度失真和更高的音调。跨度>
  • 你能开始一个新问题并在这里链接吗?
  • @RhythmicFistman 感谢您的回复,stackoverflow.com/questions/42708431/…
【解决方案2】:

我最终打开了在 Audacity 中生成的音频文件,发现每一帧都有一半被丢弃,如下图所示:

acl.mChannelLayoutTag 更改为kAudioChannelLayoutTag_Mono 并将audioFormat.mChannelsPerFrame 更改为1 解决了这个问题,现在音频质量非常完美。万岁!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-16
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多