【问题标题】:Create a silent audio CMSampleBufferRef创建无声音频 CMSampleBufferRef
【发布时间】:2016-03-30 05:46:53
【问题描述】:

如何在 Swift 中创建无声音频CMSampleBufferRef?我希望将静默 CMSampleBufferRefs 附加到 AVAssetWriterInput 的实例中。

【问题讨论】:

  • 还请记住,刚开始播放空声音可能会在您开始时有点流行,因此您可能需要做一些混音样本,或者将在该声音之前播放的任何内容静音...和如果你有一个浮动代表,或者一个有符号的整数代表,一个无符号的整数代表,中间也只会是0,应该用max / 2(ish)填充

标签: ios swift avfoundation


【解决方案1】:

你没有说你想要什么格式的零(整数/浮点,单声道/立体声,采样率),但也许没关系。无论如何,这是一种在 swift 中创建无声 CD 音频样式CMSampleBuffer 的方法。

func createSilentAudio(startFrm: Int64, nFrames: Int, sampleRate: Float64, numChannels: UInt32) -> CMSampleBuffer? {
    let bytesPerFrame = UInt32(2 * numChannels)
    let blockSize = nFrames*Int(bytesPerFrame)

    var block: CMBlockBuffer?
    var status = CMBlockBufferCreateWithMemoryBlock(
        kCFAllocatorDefault,
        nil,
        blockSize,  // blockLength
        nil,        // blockAllocator
        nil,        // customBlockSource
        0,          // offsetToData
        blockSize,  // dataLength
        0,          // flags
        &block
    )
    assert(status == kCMBlockBufferNoErr)

    // we seem to get zeros from the above, but I can't find it documented. so... memset:
    status = CMBlockBufferFillDataBytes(0, block!, 0, blockSize)
    assert(status == kCMBlockBufferNoErr)

    var asbd = AudioStreamBasicDescription(
        mSampleRate: sampleRate,
        mFormatID: kAudioFormatLinearPCM,
        mFormatFlags: kLinearPCMFormatFlagIsSignedInteger,
        mBytesPerPacket: bytesPerFrame,
        mFramesPerPacket: 1,
        mBytesPerFrame: bytesPerFrame,
        mChannelsPerFrame: numChannels,
        mBitsPerChannel: 16,
        mReserved: 0
    )

    var formatDesc: CMAudioFormatDescription?
    status = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &asbd, 0, nil, 0, nil, nil, &formatDesc)
    assert(status == noErr)

    var sampleBuffer: CMSampleBuffer?

    // born ready
    status = CMAudioSampleBufferCreateReadyWithPacketDescriptions(
        kCFAllocatorDefault,
        block,      // dataBuffer
        formatDesc!,
        nFrames,    // numSamples
        CMTimeMake(startFrm, Int32(sampleRate)),    // sbufPTS
        nil,        // packetDescriptions
        &sampleBuffer
    )
    assert(status == noErr)

    return sampleBuffer
}

你问了这个问题,你不觉得很抱歉吗?你真的需要沉默的CMSampleBuffers 吗?您不能通过向前移动演示时间戳将静音插入AVAssetWriterInput 吗?

【讨论】:

  • 谢谢,这是非常有用的 :-) 不幸的是,我确实需要 AVAssetWriter 来导出无声电影。我正在导出不能包含音频的延时摄影。如果我只是导出到相机胶卷,那不会有问题,但我会将视频插入 AVMutableCoposition。 AVMutableCompositionTrack 似乎在 iOS 9 中存在错误,它不喜欢插入没有音轨的 AVAssets。本题触及主题:stackoverflow.com/a/33277354/2526115
  • 向前移动演示时间戳也不是一个选项,因为 AVAssetWriter 将导出具有最后一个时间戳持续时间的视频。
  • 很奇怪。那么上面的代码是否适用于插入静音?
  • 我很抱歉被问到这个问题。所以现在我们有这样一个很好的例子来说明它是如何完成的。也许是为了无声样本,也许是其他的。谢谢。
  • 完全创建音轨就不能得到同样的结果吗?
【解决方案2】:

为 XCode 10.3 更新。斯威夫特 5.0.1。 不要忘记import CoreMedia

import Foundation
import CoreMedia

class CMSampleBufferFactory
{
    static func createSilentAudio(startFrm: Int64, nFrames: Int, sampleRate: Float64, numChannels: UInt32) -> CMSampleBuffer? {
        let bytesPerFrame = UInt32(2 * numChannels)
        let blockSize = nFrames*Int(bytesPerFrame)

        var block: CMBlockBuffer?
        var status = CMBlockBufferCreateWithMemoryBlock(
            allocator: kCFAllocatorDefault,
            memoryBlock: nil,
            blockLength: blockSize,
            blockAllocator: nil,
            customBlockSource: nil,
            offsetToData: 0,
            dataLength: blockSize,
            flags: 0,
            blockBufferOut: &block
        )
        assert(status == kCMBlockBufferNoErr)

        guard var eBlock = block else { return nil }

        // we seem to get zeros from the above, but I can't find it documented. so... memset:
        status = CMBlockBufferFillDataBytes(with: 0, blockBuffer: eBlock, offsetIntoDestination: 0, dataLength: blockSize)
        assert(status == kCMBlockBufferNoErr)


        var asbd = AudioStreamBasicDescription(
            mSampleRate: sampleRate,
            mFormatID: kAudioFormatLinearPCM,
            mFormatFlags: kLinearPCMFormatFlagIsSignedInteger,
            mBytesPerPacket: bytesPerFrame,
            mFramesPerPacket: 1,
            mBytesPerFrame: bytesPerFrame,
            mChannelsPerFrame: numChannels,
            mBitsPerChannel: 16,
            mReserved: 0
        )

        var formatDesc: CMAudioFormatDescription?
        status = CMAudioFormatDescriptionCreate(allocator: kCFAllocatorDefault, asbd: &asbd, layoutSize: 0, layout: nil, magicCookieSize: 0, magicCookie: nil, extensions: nil, formatDescriptionOut: &formatDesc)
        assert(status == noErr)

        var sampleBuffer: CMSampleBuffer?

        status = CMAudioSampleBufferCreateReadyWithPacketDescriptions(
            allocator: kCFAllocatorDefault,
            dataBuffer: eBlock,
            formatDescription: formatDesc!,
            sampleCount: nFrames,
            presentationTimeStamp: CMTimeMake(value: startFrm, timescale: Int32(sampleRate)),  
            packetDescriptions: nil,
            sampleBufferOut: &sampleBuffer
        )
        assert(status == noErr)
        return sampleBuffer
    }
}

【讨论】:

【解决方案3】:

您需要使用CMBlockBufferCreateWithMemoryBlock() 创建一个块缓冲区。 用一堆零填充块缓冲区,然后将其传递给CMAudioSampleBufferCreateWithPacketDescriptions()

免责声明:我实际上并没有在 Swift 中做到这一点,我尝试过,但发现自己每次都在与编译器作斗争,所以我切换到了 obj-c。 Core Media Framework 是一个低级的 C 框架,并且更容易使用而无需使用 Swifts 类型系统。我知道这不是您要购买的答案,希望它能为您指明正确的方向。

Example

【讨论】:

  • 一个 Obj-C 例子就可以了
  • @IvanLesko 我在 github 上放了一个例子。它是一个命令行实用程序,用于创建一秒钟的静音并将其放入文件中。
猜你喜欢
  • 2018-08-13
  • 2019-08-09
  • 2012-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-28
  • 2013-03-08
  • 2012-09-03
相关资源
最近更新 更多