代码之家  ›  专栏  ›  技术社区  ›  Vladimir Prigarin

使用AvaudioConverter Swift将AAC解码为PCM格式

  •  8
  • Vladimir Prigarin  · 技术社区  · 7 年前

    如何在swift上使用avaudioconverter、avaudiocompressedbuffer和avaudiopcmbuffer将aac转换为pcm?

    在WWDC 2015上,507会话被称,avaudioConverter可以编码和解码PCM缓冲区,显示了编码示例,但没有显示解码示例。 我试过解码,但有些东西不管用。我不知道是什么:(

    电话:

    //buffer - it's AVAudioPCMBuffer from AVAudioInputNode(AVAudioEngine)
    let aacBuffer = AudioBufferConverter.convertToAAC(from: buffer, error: nil) //has data
    let data = Data(bytes: aacBuffer!.data, count: Int(aacBuffer!.byteLength)) //has data
    let aacReverseBuffer = AudioBufferConverter.convertToAAC(from: data) //has data
    let pcmReverseBuffer = AudioBufferConverter.convertToPCM(from: aacBuffer2!, error: nil) //zeros data. data object exist, but filled by zeros
    

    它是用于转换的代码:

    class AudioBufferFormatHelper {
    
        static func PCMFormat() -> AVAudioFormat? {
    
            return AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: false)
        }
    
        static func AACFormat() -> AVAudioFormat? {
    
            var outDesc = AudioStreamBasicDescription(
                    mSampleRate: 44100,
                    mFormatID: kAudioFormatMPEG4AAC,
                    mFormatFlags: 0,
                    mBytesPerPacket: 0,
                    mFramesPerPacket: 0,
                    mBytesPerFrame: 0,
                    mChannelsPerFrame: 1,
                    mBitsPerChannel: 0,
                    mReserved: 0)
            let outFormat = AVAudioFormat(streamDescription: &outDesc)
            return outFormat
        }
    }
    
    class AudioBufferConverter {
    
        static func convertToAAC(from buffer: AVAudioBuffer, error outError: NSErrorPointer) -> AVAudioCompressedBuffer? {
    
            let outputFormat = AudioBufferFormatHelper.AACFormat()
            let outBuffer = AVAudioCompressedBuffer(format: outputFormat!, packetCapacity: 8, maximumPacketSize: 768)
    
            self.convert(from: buffer, to: outBuffer, error: outError)
    
            return outBuffer
        }
    
        static func convertToPCM(from buffer: AVAudioBuffer, error outError: NSErrorPointer) -> AVAudioPCMBuffer? {
    
            let outputFormat = AudioBufferFormatHelper.PCMFormat()
            guard let outBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat!, frameCapacity: 4410) else {
                return nil
            }
    
            outBuffer.frameLength = 4410
            self.convert(from: buffer, to: outBuffer, error: outError)
    
            return outBuffer
        }
    
        static func convertToAAC(from data: Data) -> AVAudioCompressedBuffer? {
    
            let nsData = NSData(data: data)
            let inputFormat = AudioBufferFormatHelper.AACFormat()
            let buffer = AVAudioCompressedBuffer(format: inputFormat!, packetCapacity: 8, maximumPacketSize: 768)
            buffer.byteLength = UInt32(data.count)
            buffer.packetCount = 8
    
            buffer.data.copyMemory(from: nsData.bytes, byteCount: nsData.length)
            buffer.packetDescriptions!.pointee.mDataByteSize = 4
    
            return buffer
        }
    
        private static func convert(from sourceBuffer: AVAudioBuffer, to destinationBuffer: AVAudioBuffer, error outError: NSErrorPointer) {
    
            //init converter
            let inputFormat = sourceBuffer.format
            let outputFormat = destinationBuffer.format
            let converter = AVAudioConverter(from: inputFormat, to: outputFormat)
    
            converter!.bitRate = 32000
    
            let inputBlock : AVAudioConverterInputBlock = { inNumPackets, outStatus in
    
                outStatus.pointee = AVAudioConverterInputStatus.haveData
                return sourceBuffer
            }
    
            _ = converter!.convert(to: destinationBuffer, error: outError, withInputFrom: inputBlock)
        }
    }
    

    结果avaudiopcmbuffer的数据为零。 在消息中我看到错误:

    AACDecoder.cpp:192:Deserialize:  Unmatched number of channel elements in payload
    AACDecoder.cpp:220:DecodeFrame:  Error deserializing packet
    [ac] ACMP4AACBaseDecoder.cpp:1337:ProduceOutputBufferList: (0x14f81b840) Error decoding packet 1: err = -1, packet length: 0
    AACDecoder.cpp:192:Deserialize:  Unmatched number of channel elements in payload
    AACDecoder.cpp:220:DecodeFrame:  Error deserializing packet
    [ac] ACMP4AACBaseDecoder.cpp:1337:ProduceOutputBufferList: (0x14f81b840) Error decoding packet 3: err = -1, packet length: 0
    AACDecoder.cpp:192:Deserialize:  Unmatched number of channel elements in payload
    AACDecoder.cpp:220:DecodeFrame:  Error deserializing packet
    [ac] ACMP4AACBaseDecoder.cpp:1337:ProduceOutputBufferList: (0x14f81b840) Error decoding packet 5: err = -1, packet length: 0
    AACDecoder.cpp:192:Deserialize:  Unmatched number of channel elements in payload
    AACDecoder.cpp:220:DecodeFrame:  Error deserializing packet
    [ac] ACMP4AACBaseDecoder.cpp:1337:ProduceOutputBufferList: (0x14f81b840) Error decoding packet 7: err = -1, packet length: 0
    
    1 回复  |  直到 7 年前
        1
  •  4
  •   Gordon Childs    7 年前

    您的尝试有一些问题:

    1. 你没有设置 倍数 转换数据时的数据包说明-> AVAudioCompressedBuffer . 您需要创建它们,因为AAC数据包的大小是可变的。您可以从原始AAC缓冲区复制它们,也可以手动(ouch)或使用 AudioFileStream 应用程序编程接口。

    2. 你重新创造了你的 AVAudioConverter 一次又一次地-对每个缓冲区一次,丢弃它们的状态。例如,由于AAC编码器自身的原因,需要添加2112帧的静默,然后才能重现您的音频,因此重新创建转换器可以让您获得完全的静默。

    3. 你一次又一次地向 音频转换器 的输入块。您应该只呈现每个缓冲区一次。

    4. 32000的比特率不起作用(对我来说)

    这就是我现在能想到的。尝试对您的代码进行以下修改,现在您可以这样调用它:

    (另外,我把一些单声道改成了立体声,这样我就可以在我的Mac电脑上播放往返缓冲区,它的麦克风输入奇怪地是立体声的——你可能需要把它改回来)

    (P.P.S.显然这里有一些往返/序列化/反序列化的尝试,但是您究竟想做什么呢?是否要将AAC音频从一个设备传输到另一个设备?因为它可能更容易让另一个API喜欢 AVPlayer 播放结果流,而不是自己处理数据包)

    let aacBuffer = AudioBufferConverter.convertToAAC(from: buffer, error: nil)!
    let data = Data(bytes: aacBuffer.data, count: Int(aacBuffer.byteLength))
    let packetDescriptions = Array(UnsafeBufferPointer(start: aacBuffer.packetDescriptions, count: Int(aacBuffer.packetCount)))
    let aacReverseBuffer = AudioBufferConverter.convertToAAC(from: data, packetDescriptions: packetDescriptions)!
    // was aacBuffer2
    let pcmReverseBuffer = AudioBufferConverter.convertToPCM(from: aacReverseBuffer, error: nil)
    
    class AudioBufferFormatHelper {
    
        static func PCMFormat() -> AVAudioFormat? {
            return AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: false)
        }
    
        static func AACFormat() -> AVAudioFormat? {
    
            var outDesc = AudioStreamBasicDescription(
                mSampleRate: 44100,
                mFormatID: kAudioFormatMPEG4AAC,
                mFormatFlags: 0,
                mBytesPerPacket: 0,
                mFramesPerPacket: 0,
                mBytesPerFrame: 0,
                mChannelsPerFrame: 1,
                mBitsPerChannel: 0,
                mReserved: 0)
            let outFormat = AVAudioFormat(streamDescription: &outDesc)
            return outFormat
        }
    }
    
    class AudioBufferConverter {
        static var lpcmToAACConverter: AVAudioConverter! = nil
    
        static func convertToAAC(from buffer: AVAudioBuffer, error outError: NSErrorPointer) -> AVAudioCompressedBuffer? {
    
            let outputFormat = AudioBufferFormatHelper.AACFormat()
            let outBuffer = AVAudioCompressedBuffer(format: outputFormat!, packetCapacity: 8, maximumPacketSize: 768)
    
            //init converter once
            if lpcmToAACConverter == nil {
                let inputFormat = buffer.format
    
                lpcmToAACConverter = AVAudioConverter(from: inputFormat, to: outputFormat!)
    //            print("available rates \(lpcmToAACConverter.applicableEncodeBitRates)")
    //          lpcmToAACConverter!.bitRate = 96000
                lpcmToAACConverter.bitRate = 32000    // have end of stream problems with this, not sure why
            }
    
            self.convert(withConverter:lpcmToAACConverter, from: buffer, to: outBuffer, error: outError)
    
            return outBuffer
        }
    
        static var aacToLPCMConverter: AVAudioConverter! = nil
    
        static func convertToPCM(from buffer: AVAudioBuffer, error outError: NSErrorPointer) -> AVAudioPCMBuffer? {
    
            let outputFormat = AudioBufferFormatHelper.PCMFormat()
            guard let outBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat!, frameCapacity: 4410) else {
                return nil
            }
    
            //init converter once
            if aacToLPCMConverter == nil {
                let inputFormat = buffer.format
    
                aacToLPCMConverter = AVAudioConverter(from: inputFormat, to: outputFormat!)
            }
    
            self.convert(withConverter: aacToLPCMConverter, from: buffer, to: outBuffer, error: outError)
    
            return outBuffer
        }
    
        static func convertToAAC(from data: Data, packetDescriptions: [AudioStreamPacketDescription]) -> AVAudioCompressedBuffer? {
    
            let nsData = NSData(data: data)
            let inputFormat = AudioBufferFormatHelper.AACFormat()
            let maximumPacketSize = packetDescriptions.map { $0.mDataByteSize }.max()!
            let buffer = AVAudioCompressedBuffer(format: inputFormat!, packetCapacity: AVAudioPacketCount(packetDescriptions.count), maximumPacketSize: Int(maximumPacketSize))
            buffer.byteLength = UInt32(data.count)
            buffer.packetCount = AVAudioPacketCount(packetDescriptions.count)
    
            buffer.data.copyMemory(from: nsData.bytes, byteCount: nsData.length)
            buffer.packetDescriptions!.pointee.mDataByteSize = UInt32(data.count)
            buffer.packetDescriptions!.initialize(from: packetDescriptions, count: packetDescriptions.count)
    
            return buffer
        }
    
    
        private static func convert(withConverter: AVAudioConverter, from sourceBuffer: AVAudioBuffer, to destinationBuffer: AVAudioBuffer, error outError: NSErrorPointer) {
            // input each buffer only once
            var newBufferAvailable = true
    
            let inputBlock : AVAudioConverterInputBlock = {
                inNumPackets, outStatus in
                if newBufferAvailable {
                    outStatus.pointee = .haveData
                    newBufferAvailable = false
                    return sourceBuffer
                } else {
                    outStatus.pointee = .noDataNow
                    return nil
                }
            }
    
            let status = withConverter.convert(to: destinationBuffer, error: outError, withInputFrom: inputBlock)
            print("status: \(status.rawValue)")
        }
    }
    
    推荐文章