opus編碼的格式概念:
Opus是一個有損聲音編碼的格式,由Xiph.Org基金會開發,之后由IETF(互聯網工程任務組)進行標準化,目標是希望用單一格式包含聲音和語音,取代Speex和Vorbis,且適用于網絡上低延遲的即時聲音傳輸,標準格式定義于RFC 6716文件。Opus格式是一個開放格式,使用上沒有任何專利或限制。
Opus集成了兩種聲音編碼的技術:以語音編碼為導向的SILK和低延遲的CELT。Opus可以無縫調節高低比特率。在編碼器內部它在較低比特率時使用線性預測編碼在高比特率時候使用變換編碼(在高低比特率交界處也使用兩者結合的編碼方式)。Opus具有非常低的算法延遲(默認為22.5 ms),非常適合用于低延遲語音通話的編碼,像是網上上的即時聲音流、即時同步聲音旁白等等,此外Opus也可以透過降低編碼碼率,達成更低的算法延遲,最低可以到5 ms。在多個聽覺盲測中,Opus都比MP3、AAC、HE-AAC等常見格式,有更低的延遲和更好的聲音壓縮率。
需求場景:
最近我在對接一個可以接收聲音的 硬件設備,通信模式為 websocket。
通過測試 ,接收到的音頻格式為 :opus 格式。
我接收到這個格式的 音頻后,首先需要給這個格式的音頻進行解碼,然后得到PCM編碼格式的數據。
解碼后的 PCM 數據則是還原后的音頻信號,是原始音頻的近似表示,可以直接輸出音頻信號。
PCM解釋:
PCM(脈沖編碼調制,Pulse Code Modulation)編碼是一種將模擬信號轉換為數字信號的方法,廣泛用于音頻、視頻和通信系統中。PCM編碼的主要目的是將模擬信號的幅度表示為一系列的數字值,這樣可以在數字系統中更容易地存儲、處理和傳輸。
PCM編碼的基本過程:
采樣:首先,將模擬信號按一定的時間間隔進行采樣。每次采樣得到的值稱為“采樣點”。
量化:將每個采樣點的模擬信號值轉換成最接近的數字值。量化的精度由量化位數(通常是8位、16位或更高)決定,位數越高,表示的數字精度越高,信號的失真越小。
編碼:將量化后的數字信號進行二進制編碼,即將每個采樣點的數字值轉化為二進制形式,形成一串二進制代碼。
PCM的常見應用:
音頻:例如CD音質的音頻數據就是使用PCM編碼的,通常采用44.1kHz的采樣率和16位的量化深度。
通信:PCM也用于電話通信和其他語音傳輸領域。
視頻:PCM有時也用于視頻信號的音頻部分編碼。
對于做網站開發的我,也是第一次處理關于音頻的數據,這篇文章就把我遇到的一些問題做一個分享,希望能幫助看到這篇文章的你。
這個設備提供的代碼是python版本的。
但是我是使用 java語言,springboot 框架 來處理數據的。
在調試的過程中 遇到了一個問題:opus格式的音頻解碼的問題?
對于python來說 這個語言 有現成的包 opuslib 還是比較好處理的
# Opus音頻解碼
import opuslib
import opuslib.api.encoder
import opuslib.api.decoderclass OpusDecoder():def __init__(self, samplerate: int, channels: int, seq_time: float) -> None:self.samplerate = samplerate# 創建解碼器self.decoder = opuslib.Decoder(fs=self.samplerate, channels=channels)self.seq_length = int(seq_time*self.samplerate*2)def decode(self, input_bytes: bytes):# 直接解碼opus數據dec_output = self.decoder.decode(bytes(input_bytes), self.seq_length)# print('decode seq len: {}'.format(len(dec_output))) return dec_output
當我來使用java的時候 發現 網上java對于這個格式音頻處理的相關文章非常的少,相關依賴也非常的少,至于找到的可以用的依賴,也沒找到相關文檔怎么使用。
通過我在 github上的搜索和 maven倉庫里的尋找,終于找到了java對于 opus格式的音頻的解決方案。
下面就直接上代碼了:
第一步引入依賴:
<dependency><groupId>club.minnced</groupId><artifactId>opus-java</artifactId><version>1.1.1</version></dependency><dependency><groupId>net.java.dev.jna</groupId><artifactId>jna</artifactId><version>4.1.0</version></dependency>
第二步封裝 工具類:
package com.agentai.base.utils;/**** opus 解碼器工具類* User: Json* Date: 2025/4/11**/
import club.minnced.opus.util.OpusLibrary;
import com.sun.jna.ptr.PointerByReference;
import tomp2p.opuswrapper.Opus;import java.io.IOException;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.Base64;public class OpusDecoder {private int samplerate;private int seqLength;private PointerByReference opusDecoder;// 加載 Opus 庫 對于這個加載 opus庫的 寫法 文章下方 有截圖的解釋。static {try {// 嘗試通過 OpusLibrary 加載 JAR 內的本地庫if (!OpusLibrary.loadFromJar()) {throw new UnsatisfiedLinkError("未加載到 opus處理文件!");}} catch (IOException e1) {try {// 如果失敗,嘗試通過默認的 System.loadLibrary() 加載 找本地系統的System.loadLibrary("opus");} catch (UnsatisfiedLinkError e2) {e1.printStackTrace();}}}public OpusDecoder(int samplerate, int channels, float seqTime) {this.samplerate = samplerate;// 創建 Opus 解碼器IntBuffer error = IntBuffer.allocate(4);this.opusDecoder = Opus.INSTANCE.opus_decoder_create(samplerate, channels, error);this.seqLength = (int) (seqTime * this.samplerate * 2);}public short[] decode(String base64Audio) {// 1. 解碼 Base64 音頻數據byte[] audioBytes = Base64.getDecoder().decode(base64Audio);// 2. 創建用于存儲解碼后的數據的 ShortBufferShortBuffer decodedData = ShortBuffer.allocate(1024 * 1024);// 3. 解碼 Opus 數據int decoded = Opus.INSTANCE.opus_decode(opusDecoder, audioBytes, audioBytes.length, decodedData, seqLength, 0);// 4. 返回解碼后的短音頻數據short[] result = new short[decoded];decodedData.get(result, 0, decoded);return result;}public byte[] decodeToBytes(String base64Audio) {// 1. 解碼 Base64 音頻數據byte[] audioBytes = Base64.getDecoder().decode(base64Audio);// 2. 創建用于存儲解碼后的數據的 ShortBufferShortBuffer decodedData = ShortBuffer.allocate(1024 * 1024);// 3. 解碼 Opus 數據int decoded = Opus.INSTANCE.opus_decode(opusDecoder, audioBytes, audioBytes.length, decodedData, seqLength, 0);// 4. 轉換 short[] 為 byte[]short[] shortData = new short[decoded];decodedData.get(shortData, 0, decoded);byte[] byteData = convertShortToByte(shortData);return byteData; // 返回 byte[] 類型的音頻數據}private byte[] convertShortToByte(short[] shortArray) {byte[] byteArray = new byte[shortArray.length * 2];for (int i = 0; i < shortArray.length; i++) {byteArray[i * 2] = (byte) (shortArray[i] & 0xFF); // 低字節byteArray[i * 2 + 1] = (byte) ((shortArray[i] >> 8) & 0xFF); // 高字節}return byteArray;}// 銷毀解碼器,釋放資源public void close() {Opus.INSTANCE.opus_decoder_destroy(opusDecoder);}// 測試解碼功能public static void main(String[] args) {// 假設這是接收到的 Base64 編碼的音頻數據 // 因為我的音頻是先base了一下 所以我需要先用base64 解碼,//如果你的音頻沒有 base64 這個步驟跳過即可String base64Audio = ""; // 你的音頻數據流// 創建解碼器對象OpusDecoder decoder = new OpusDecoder(16000, 1, 0.02f);// 解碼音頻byte[] decodedAudio = decoder.decodeToBytes(base64Audio);// 打印解碼結果長度System.out.println("Decoded audio length: " + decodedAudio.length);// 關閉解碼器decoder.close();}
}
這個工具類的封裝 也是我找的依賴 看他們的源碼 自己封裝的,
因為 開源的相關依賴沒有提供非常完整的 使用文檔,所以只能自己看源碼 。
下面就是我在源碼里看到的:
我的理解:
java 沒有現成處理這種opus格式音頻的能力,也許這就是為啥網上關于java處理這個音頻的的文章比較少的原因。
看到源碼后,這個依賴的解決方案就是,把可以處理 opus格式的 擴展打包到 java的jar里,然后通過java 來加載 這些 擴展,然后通過java調用這些擴展里的方法從來 實現 opus格式的音頻解碼。