新闻资讯
音频包 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
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。
回复列表