Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
176 views
in Technique[技术] by (71.8m points)

ios - Decoding .ogg/opus file

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.9k users

...