I have an AVAudioCompressedBuffer
instance that gets correctly decoded and played by my AVAudioEngine
.
The problem is that after converting it to Data
and then back to AVAudioCompressedBuffer
it is no longer playable and throws a kAudioCodecBadDataError
.
This is how I'm currently managing the conversion to and from Data
:
// Convert AVAudioCompressedBuffer to Data
let capacity = Int(compressedBuffer.byteLength)
let compressedBufferPointer = compressedBuffer.data.bindMemory(to: UInt8.self, capacity: capacity)
var compressedBytes: [UInt8] = [UInt8].init(repeating: 0, count: capacity)
compressedBufferPointer.withMemoryRebound(to: UInt8.self, capacity: capacity) { sourceBytes in
compressedBytes.withUnsafeMutableBufferPointer {
$0.baseAddress!.initialize(from: sourceBytes, count: capacity)
}
}
let data = Data(compressedBytes)
// Convert Data to AVAudioCompressedBuffer
let compressedBuffer: AVAudioCompressedBuffer = AVAudioCompressedBuffer.init(format: format, packetCapacity: packetCapacity, maximumPacketSize: maximumPacketSize)
compressedBuffer.byteLength = byteLength
compressedBuffer.packetCount = packetCount
data.withUnsafeBytes {
compressedBuffer.data.copyMemory(from: $0.baseAddress!, byteCount: byteLength)
}
let buffer = compressedBuffer
The values for all of the buffer attributes (format, packetCapacity, maximumPacketSize, byteLength, packetCount, byteLength
) are the same on both ends of the conversion.
It turns out that, for some reason, converting AVAudioCompressedBuffer
that way fails to include the buffer's packetDescriptions
. These are stored as a C-Style array of AudioStreamPacketDescriptions
structs in the buffer. By creating a Codable
struct (PacketDescription
) and mapping the descriptions objects separately the reassembled buffer worked as expected.
var packetDescriptions = [PacketDescription]()
for index in 0..<compressedBuffer.packetCount {
if let packetDescription = compressedBuffer.packetDescriptions?[Int(index)] {
packetDescriptions.append(
.init(mStartOffset: packetDescription.mStartOffset,
mVariableFramesInPacket: packetDescription.mVariableFramesInPacket,
mDataByteSize: packetDescription.mDataByteSize))
}
}
packetDescriptions?.enumerated().forEach { index, element in
compressedBuffer.packetDescriptions?[index] = AudioStreamPacketDescription(mStartOffset: element.mStartOffset!,
mVariableFramesInPacket: element.mVariableFramesInPacket!,
mDataByteSize: element.mDataByteSize!)
}