新闻资讯

新闻资讯 行业动态

音频包 packet >> 音频缓冲 buffer

编辑:008     时间:2020-02-26

音频包 packet >> 音频缓冲 buffer

public required init(parser: Parsing, readFormat: AVAudioFormat) throws {
        // 从之前负责解析的,拿音频数据
        self.parser = parser
        
        guard let dataFormat = parser.dataFormat else {
            throw ReaderError.parserMissingDataFormat
        } let sourceFormat = dataFormat.streamDescription let commonFormat = readFormat.streamDescription
        // 创建音频格式转换器 converter
        // 通过指定输入格式,和输出格式
        // 输入格式是上一步解析出来的,从 paser 里面拿
        // 输出格式,开发指定的 let result = AudioConverterNew(sourceFormat, commonFormat, &converter)
        guard result == noErr else {
            throw ReaderError.unableToCreateConverter(result)
        }
        self.readFormat = readFormat
    } 

开发指定的输出格式

public var readFormat: AVAudioFormat { return AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 2, interleaved: false)!
    }

// 位深,采用 Float32
// 采样率 44100 Hz, 标准 CD 音质
// 分左右声道 复制代码

上一步解析出音频包 packet,读取音频缓冲 buffer

public func read(_ frames: AVAudioFrameCount) throws -> AVAudioPCMBuffer { let framesPerPacket = readFormat.streamDescription.pointee.mFramesPerPacket
        var packets = frames / framesPerPacket
        
       // 创建空白的、指定格式和容量的,音频缓冲 AVAudioPCMBuffer
        guard let buffer = AVAudioPCMBuffer(pcmFormat: readFormat, frameCapacity: frames) else {
            throw ReaderError.failedToCreatePCMBuffer
        }
        buffer.frameLength = frames
        
        // 把解析出的音频包 packet, 转换成 AVAudioPCMBuffer,这样 AVAudioEngine 播放
        try queue.sync { let context = unsafeBitCast(self, to: UnsafeMutableRawPointer.self)
            // 设置好的转换器 converter,使用回调方法 ReaderConverterCallback,填充创建的 buffer 中的数据 buffer.mutableAudioBufferList let status = AudioConverterFillComplexBuffer(converter!, ReaderConverterCallback, context, &packets, buffer.mutableAudioBufferList, nil)
            guard status == noErr else {
                switch status { case ReaderMissingSourceFormatError:
                    throw ReaderError.parserMissingDataFormat case ReaderReachedEndOfDataError:
                    throw ReaderError.reachedEndOfFile case ReaderNotEnoughDataError:
                    throw ReaderError.notEnoughData
                default:
                    throw ReaderError.converterFailed(status)
                }
            }
        } return buffer
    }

  • AudioConverterFillComplexBuffer 的使用姿势:

AudioConverterFillComplexBuffer(格式转换器,回调函数,自定义参数指针,包的个数指针,接收转换后数据的指针,接收 ASPD 的指针)

AudioConverterFillComplexBuffer(converter!, ReaderConverterCallback, context, &packets, buffer.mutableAudioBufferList, nil) 复制代码
  • AudioConverterFillComplexBuffer 的回调函数 ReaderConverterCallback, 的使用姿势:

回调函数(格式转换器, 包的个数指针,接收转换后数据的指针, 接收 ASPD 的指针, 自定义参数指针 )

可看出,传递给 AudioConverterFillComplexBuffer 的 6 个参数,

除了其回调参数本身,其他 5 个参数,其回调函数都有用到


转换 buffer 的回调函数,之前创建了空白的音频缓冲 buffer,现往 buffer 里面,填数据

func ReaderConverterCallback(_ converter: AudioConverterRef,
                             _ packetCount: UnsafeMutablePointer<UInt32>,
                             _ ioData: UnsafeMutablePointer<AudioBufferList>,
                             _ outPacketDescriptions: UnsafeMutablePointer<UnsafeMutablePointer<AudioStreamPacketDescription>?>?,
                             _ context: UnsafeMutableRawPointer?) -> OSStatus {

    // 还原出 self ( reader ) let reader = Unmanaged<Reader>.fromOpaque(context!).takeUnretainedValue()
    
    // 确保输入格式可用
    guard let sourceFormat = reader.parser.dataFormat else { return ReaderMissingSourceFormatError
    }
    
    //  这个类 Reader, 里面记录了一个播放到的位置 currentPacket, 
    //  播放相对位置,就是一个 offset
    //   判断播放到包尾的情况
     
    //     播放到包尾,根据下载解析情况,分两种情况
    //     1, 下载解析完成,播放到了结尾
    //     2, 下载没完成,解析好了的,都播放完了
    //     (仅此两种状况,因为解析的时间,远比不上下载的时间。下载完成 = 解析完成 ) let packetIndex = Int(reader.currentPacket) let packets = reader.parser.packets let isEndOfData = packetIndex >= packets.count - 1 if isEndOfData { if reader.parser.isParsingComplete {
            packetCount.pointee = 0 return ReaderReachedEndOfDataError
        } else { return ReaderNotEnoughDataError
        }
    }
    
    // 之前的设置,每次只处理一个包 packet 的音频数据 let packet = packets[packetIndex]
    var data = packet.0 let dataCount = data.count
    ioData.pointee.mNumberBuffers = 1
    // 音频数据拷贝过来:先分配内存,再拷贝地址的数据
    ioData.pointee.mBuffers.mData = UnsafeMutableRawPointer.allocate(byteCount: dataCount, alignment: 0)

    _ = data.withUnsafeMutableBytes { (rawMutableBufferPointer) in let bufferPointer = rawMutableBufferPointer.bindMemory(to: UInt8.self) if let address = bufferPointer.baseAddress{
            memcpy((ioData.pointee.mBuffers.mData?.assumingMemoryBound(to: UInt8.self))!, address, dataCount)
        }
    }
    
    ioData.pointee.mBuffers.mDataByteSize = UInt32(dataCount)
    
    // 处理压缩文件 MP3, AAC 的 ASPD let sourceFormatDescription = sourceFormat.streamDescription.pointee if sourceFormatDescription.mFormatID != kAudioFormatLinearPCM { if outPacketDescriptions?.pointee == nil {
            outPacketDescriptions?.pointee = UnsafeMutablePointer<AudioStreamPacketDescription>.allocate(capacity: 1)
        }
        outPacketDescriptions?.pointee?.pointee.mDataByteSize = UInt32(dataCount)
        outPacketDescriptions?.pointee?.pointee.mStartOffset = 0
        outPacketDescriptions?.pointee?.pointee.mVariableFramesInPacket = 0
    }
    packetCount.pointee = 1

    // 更新播放到的位置 currentPacket
    reader.currentPacket = reader.currentPacket + 1 return noErr;
} 





原文链接:https://juejin.im/post/5e52a5c55188254940670156
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

回复列表

相关推荐