ios - 使用来自原始 PCM 流的 CMSampleTimingInfo、CMSampleBuffer 和 AudioBufferList

标签 ios objective-c webrtc core-audio

我正在从 Google 的 WebRTC C++ 引用实现(一个插入到 VoEBaseImpl::GetPlayoutData 中的 Hook )接收原始 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,每次调用此 Hook 时有 480 个样本,我每 10 毫秒接收一次数据。

最佳答案

首先,恭喜您冒昧地从头开始创建音频 CMSampleBuffer。对于大多数,它们既没有被创建也没有被销毁,而是从 CoreMediaAVFoundation 中完美而神秘地传承下来。

您的时序信息中的presentationTimeStamp是整数毫秒,不能代表您的 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

关于ios - 使用来自原始 PCM 流的 CMSampleTimingInfo、CMSampleBuffer 和 AudioBufferList,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34398058/

相关文章:

ios - 如何将字典作为参数附加到 URL?

iphone - UIGestureRecognizerDelegate 从不显示状态 'recognized' 中的点击

ios - Amazon S3 uploadPart 返回 null

ios - 在 NSMutableArray Parse iOS 中删除 currentUser

iphone - 基准 UIView drawRect : method

google-chrome - Webrtc对等连接使用不同的sdpConstraints创建应答/提供

silverlight - 不带Web RTC的Web RTC

ios - 以这种方式创建 React Native 应用程序有什么问题?

ios - 选择行其他部分在 UITableview 中包含相同的值?

c++ - 如何为初学者开发 WebRTC 应用程序?