Create a silent audio CMSampleBufferRef

后端 未结 3 680
失恋的感觉
失恋的感觉 2021-01-12 10:20

How do you create a silent audio CMSampleBufferRef in Swift? I am looking to append silent CMSampleBufferRefs to an instance of AVAssetWriter

相关标签:
3条回答
  • 2021-01-12 10:36

    You need to create a block buffer using CMBlockBufferCreateWithMemoryBlock(). Fill the block buffer with a bunch of zeros and then pass it into CMAudioSampleBufferCreateWithPacketDescriptions().

    Disclaimer: I haven't actually done this in Swift, I attempted it but found myself fighting the compiler at every turn so I switched to obj-c. The Core Media Framework is a low level C framework and was a lot easier to use without screwing around with Swifts type system. I know this isn't the answer you're looking for buy hopefully it will point you in the right direction.

    Example

    0 讨论(0)
  • 2021-01-12 10:47

    Updated for XCode 10.3. Swift 5.0.1. Don't forget the 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
        }
    }
    
    0 讨论(0)
  • 2021-01-12 10:52

    You don't say what format you want your zeros (integer/floating point, mono/stereo, sample rate), but maybe it doesn't matter. Anyway, here's one way to create a silent CD audio style CMSampleBuffer in swift.

    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
    }
    

    Doesn't it make you sorry you asked? Do you really need silent CMSampleBuffers? Can't you insert silence into an AVAssetWriterInput by moving the presentation time stamp forward?

    0 讨论(0)
提交回复
热议问题