I have been trying to decode Ogg/Opus file but I can't do it.
extractAndDecodeAudioData() function in AudioUtil.swift, cannot decode the file. opus_decode function always return minus values means OPUS_INVALID_PACKET.
Usage.swift
guard let url = Bundle.main.url(forResource: "sample", withExtension: "opus") else { return }
let fileData = try Data(contentsOf: url)
let extractedPCM = AudioUtil.extractPCM(from: fileData, for: OpusAudioSetting(/*defaults*/))
let wavHeader = AudioUtil.createWavHeader(pcmInfo: PCMInfo(/*defaults*/), pcmDataSizeInBytes: Int32(extractedPCM.count))
let wavAudioData = AudioUtil.generateWav(header: wavHeader, pcmData: extractedPCM)
AudioUtil.swift
import Foundation
public class AudioUtil {
private init(){}
public static func createWavHeader(pcmInfo: PCMInfo, pcmDataSizeInBytes dataSize: Int32) -> Data {
let WAV_HEADER_SIZE:Int32 = 44
let fileSize:Int32 = dataSize + WAV_HEADER_SIZE
let sampleRate:Int32 = 16000
let subChunkSize:Int32 = 16
let format:Int16 = 1
let channels:Int16 = 1
let bitsPerSample:Int16 = 16
let byteRate:Int32 = sampleRate * Int32(channels * bitsPerSample / 8)
let blockAlign: Int16 = (bitsPerSample * channels) / 8
let header = NSMutableData()
header.append([UInt8]("RIFF".utf8), length: 4)
header.append(byteArray(from: fileSize), length: 4)
//WAVE
header.append([UInt8]("WAVE".utf8), length: 4)
//FMT
header.append([UInt8]("fmt ".utf8), length: 4)
header.append(byteArray(from: subChunkSize), length: 4)
header.append(byteArray(from: format), length: 2)
header.append(byteArray(from: channels), length: 2)
header.append(byteArray(from: sampleRate), length: 4)
header.append(byteArray(from: byteRate), length: 4)
header.append(byteArray(from: blockAlign), length: 2)
header.append(byteArray(from: bitsPerSample), length: 2)
header.append([UInt8]("data".utf8), length: 4)
header.append(byteArray(from: dataSize), length: 4)
return header as Data
}
public static func extractPCM(from audioData: Data, for setting: OpusAudioSetting) -> Data {
OpusKit.shared.initialize(
sampleRate: setting.sampleRate,
numberOfChannels: setting.channels,
packetSize: setting.packetSize,
encodeBlockSize: setting.encodeBlockSize)
let decodedPCMData = extractAndDecodeAudioData(from: audioData)
return decodedPCMData
}
public static func extractAndDecodeAudioData(from fileData: Data, headerSize: Int = 1) -> Data {
// can not share this implementation
// return OpusKit.shared.decodeData(fileData) ?? Data()
var decodedData: Data = Data()
var headerData: Data
var opusChunkSizeFromHeader = 0
var readIndex = 0
var readStart = 0
var readEnd = 0
var extractedOpusChunk: Data
while readIndex < fileData.count {
headerData = fileData[readIndex..<(readIndex + headerSize)]
//Logger.logIt("headerData: ([UInt8](headerData))")
opusChunkSizeFromHeader = Int([UInt8](headerData)[0])
readStart = readIndex + headerSize
readEnd = readStart + opusChunkSizeFromHeader
extractedOpusChunk = fileData[readStart..<readEnd]
//Logger.logIt("extracted: (extractedOpusChunk)")
if let decodedDataChunk = OpusKit.shared.decodeData(extractedOpusChunk) {
//Logger.logIt("decodedDataChunk: (decodedDataChunk)")
decodedData.append(decodedDataChunk)
} else {
print("failed to decode at index: (readStart)")
}
readIndex += (headerSize + opusChunkSizeFromHeader)
}
return decodedData
}
private static func byteArray<T>(from value: T) -> [UInt8] where T: FixedWidthInteger {
// .littleEndian is required
return withUnsafeBytes(of: value.littleEndian) { Array($0) }
}
public static func generateWav(header wavHeader: Data, pcmData: Data) -> Data {
var wavData = Data()
wavData.append(wavHeader)
wavData.append(pcmData)
return wavData
}
}
public struct OpusAudioSetting {
var sampleRate: opus_int32 = 16000
var channels: opus_int32 = 1
var packetSize: opus_int32 = 320
var encodeBlockSize: opus_int32 = 320
}
public struct PCMInfo {
var sampleRate:Int32 = 16000
var channels:Int16 = 1
var bitsPerSample:Int16 = 16
}
Opus wrapper, OpusKit.swift
// Imports.
import UIKit
import AVFoundation
// Start of class.
public class OpusKit: NSObject
{
// MARK: - Singleton instance
public static let shared = OpusKit()
// MARK: - Properties
var decoder: OpaquePointer!
var encoder: OpaquePointer!
var sampleRate: opus_int32 = 8000
var numberOfChannels: Int32 = 1
var packetSize: opus_int32 = 320
var encodeBlockSize: opus_int32 = 160
// MARK: - Initialization
public func initialize(sampleRate: opus_int32 = 48000, numberOfChannels: Int32 = 1, packetSize: opus_int32 = 320, encodeBlockSize: opus_int32 = 160) {
// Store variables.
self.sampleRate = sampleRate
self.numberOfChannels = numberOfChannels
self.packetSize = packetSize
self.encodeBlockSize = encodeBlockSize
// Create status var.
var status = Int32(0)
// Create decoder.
decoder = opus_decoder_create(sampleRate, numberOfChannels, &status)
if (status != OPUS_OK) {
print("OpusKit - Something went wrong while creating opus decoder: (opusErrorMessage(errorCode: status))")
}
// Create encoder.
encoder = opus_encoder_create(sampleRate, numberOfChannels, OPUS_APPLICATION_VOIP, &status)
if (status != OPUS_OK) {
print("OpusKit - Something went wrong while creating opus encoder: (opusErrorMessage(errorCode: status))")
}
}
// MARK: - Decode
public func decodeData(_ data: Data) -> Data? {
var encodedData = [CUnsignedChar](repeating: 0, count: 2048)
var decodedData = [opus_int16](repeating: 0, count: 2048)
_ = data.withUnsafeBytes {
memcpy(&encodedData, $0, data.count)
}
let outputData: NSMutableData = NSMutableData()
let ret = opus_decode(decoder, encodedData, (opus_int32)(data.count), &decodedData, packetSize, 0)
if ret > 0 {
let length: Int = Int(ret) * MemoryLayout<opus_int16>.size
outputData.append(decodedData, length: length)
} else {
print("OpusKit - Something went wrong while decoding data: (opusErrorMessage(errorCode: ret))")
}
return outputData as Data
}
// MARK: - Encode
public func encodeData(_ data: Data) -> Data? {
var encodedData = [CUnsignedChar](repeating: 0, count: 2048)
var pcmData = [opus_int16](repeating: 0, count: 2048)
_ = data.withUnsafeBytes {
memcpy(&pcmData, $0, data.count)
}
let outputData: NSMutableData = NSMutableData()
let ret = opus_encode(encoder, pcmData, encodeBlockSize, &encodedData, packetSize)
if ret > 0 {
let length: Int = Int(ret)
outputData.append(encodedData, length: length)
} else {
print("OpusKit - Something went wrong while encoding data: (opusErrorMessage(errorCode: ret))")
}
return outputData as Data
}
// MARK: - Error handling
private func opusErrorMessage(errorCode: Int32) -> String {
switch (errorCode) {
case OPUS_BAD_ARG:
return "One or more invalid/out of range arguments."
case OPUS_BUFFER_TOO_SMALL:
return "The mode struct passed is invalid."
case OPUS_INTERNAL_ERROR:
return "The compressed data passed is corrupted."
case OPUS_INVALID_PACKET:
return "Invalid/unsupported request number."
case OPUS_INVALID_STATE:
return "An encoder or decoder structure is invalid or already freed."
case OPUS_UNIMPLEMENTED:
return "Invalid/unsupported request number."
case OPUS_ALLOC_FAIL:
return "Memory allocation has failed."
default:
return "Unknown error."
}
}
}
Something is wrong in extractAndDecodeAudioData() function. Can not figure put how I can solve it.
question from:
https://stackoverflow.com/questions/65853081/decoding-ogg-opus-file