聲網Aogra 開源了自研的 SOLO 編解碼器,面向全部音視頻、WebRTC 開發者。本系列源碼解讀將講解底層核心技術,並分享如何集成到本身的 WebRTC 應用中。本文爲第一篇。做者:趙曉涵,聲網Agora 音頻算法工程師算法
SOLO 在 Silk 的基礎上擴展了帶寬擴展模塊,用來分別處理低頻信息(0-8kHz 採樣部分)和高頻信息(8-16kHz 採樣部分),在編碼端,二者使用兩套耦合的分析編碼系統進行碼流生成。在解碼端,利用低頻信號和高頻信息,SOLO 能夠解碼出寬帶信號。SOLO 使用帶寬擴展主要有兩個緣由,首先,帶寬擴展可讓更多的碼率分配到更重要的低頻部分,提高編碼效率;第二個緣由是帶寬擴展能夠減小進入到信號分析模塊的採樣點數,從而減小信號分析部分的複雜度(以前須要分析所有的信號,如今只須要分析低頻部分)。在減小了原有複雜度的前提下,SOLO 纔可以在低頻部分額外增長較多計算以選取最佳的多描述編碼狀態(詳細內容可見下一篇源碼解析),讓編解碼音質達到預期。函數
SOLO 編碼端的大部分操做都是在下述函數中完成的:ui
SKP_int32 AGR_Sate_encode_process( SATEEncCtl *sateCtl, /* I/O SATE Encoder state */ const SKP_int16 *vin, /* I input signal */ NovaBits *bits, /* I bitstream operator */ void *skctrl, void *hbctrl, SKP_int16 *nBytesOut /* I encoded bits */ )
首先,輸入的16kHz 採樣率的語音幀會先進入到一個正交鏡像濾波器組(QMF)裏進行頻帶的劃分:編碼
void AGR_Sate_qmf_decomp( const spx_word16_t *xx, /* I Input signal */ const spx_word16_t *aa, /* I Qmf coefficients */ spx_word16_t *y1, /* O Output low band signal */ spx_word16_t *y2, /* O Output high band signal */ SKP_int32 N, /* I frame size */ SKP_int32 M, /* I Qmf order */ spx_word16_t *mem, /* I/O Qmf state */ SKP_int8 *stack )
該函數的輸出的是兩個時域幀,分別包含低頻信息和高頻信息。低頻信息和高頻信息會在後續分別進行處理,其中,低頻信息會經過函數SKP_Silk_SDK_Encode
進行分析和編碼,這部份內容咱們會在下一期 SOLO 代碼解析裏進行詳細解讀。code
SKP_int SKP_Silk_SDK_Encode( void *encState, /* I/O: State */ const SKP_SILK_SDK_EncControlStruct *encControl, /* I: Control structure */ const SKP_int16 *samplesIn, /* I: Input samples */ SKP_int nSamplesIn, /* I: Number of samples */ SKP_uint8 *outData, /* O: Encoded output */ SKP_int16 *nBytesOut /* I/O: I: Max bytes O:out bytes */ )
高頻信息的編碼以線性濾波分析爲基礎,同時爲了減小碼率,部分依賴於低頻信號的殘差信息,所以在進行高頻信息編碼以前,須要經過下述函數提取低頻編碼信息中的殘差信息:視頻
SKP_int SKP_Silk_SDK_Get_Encoder_Residue( void *encState,SKP_int32 *r )
高頻信息的分析和編碼在函數 AGR_Bwe_encode_frame_FLP
中進行:ip
SKP_int32 AGR_Bwe_encode_frame_FLP( AGR_Sate_HB_encoder_control_FLP *hbEncCtrl, AGR_Sate_encoder_hb_state_FLP *psHBEnc, NovaBits *bits, /* I bitstream operator */ SKP_float *high, SKP_int32 *residue, SKP_int16 *nBytesOut /* I/O: Number of bytes in outData (input: Max bytes) */ )
首先高頻信息經過 AGR_Sate_find_HB_LPC_FLP
進行分析獲得自身的 8 階 LPC 係數,並將其轉化爲編碼偏差較小的 LSP 係數:ci
SKP_int32 AGR_Sate_find_HB_LPC_FLP( AGR_Sate_encoder_hb_state_FLP *psEnc, /* I/O Encoder state FLP */ AGR_Sate_HB_encoder_control_FLP *hbEncCtrl, /* I/O HB Encoder control FLP */ SKP_int32 hb_subfr_length, /* I subframe length */ SKP_int32 hb_lpc_order, /* I high band lpc order */ SKP_int32 first /* I */ )
隨後經過 AGR_Sate_lsp_quant_highband
進行雙碼本量化開發
SKP_int32 AGR_Sate_lsp_quant_highband( SKP_float *lsp, /* I/O lsp coefficients */ SKP_int32 order /* I lpc order */ )
量化後,編碼器會將 LSP 係數轉化爲 1 個 index 來表示:get
idx1 = lsp_weight_quant(qlsp, quant_weight1, AGR_Sate_highband_lsp_cdbk1, HB_LSP_CB1, order); idx2 = lsp_weight_quant(qlsp, quant_weight2, AGR_Sate_highband_lsp_cdbk2, HB_LSP_CB2, order); idx = (idx2<<8)+idx1;
隨後,該幀被分爲 4 個子幀,計算各個子幀的殘差信號,並計算其對應窄帶殘差信號子幀的增益,共計4個,使用單碼本量化。量化後的 LSP index 和 gain 使用下述函數寫入獨立碼流。
void AGR_Sate_bits_pack(NovaBits *bits, int data, int nbBits)
其中,LSP index 使用 12 bits 編碼,每一個子幀 gain 使用 5 bits 編碼,因此高頻信息的碼流共計 12+4*5=32 bits,即 4 bytes,該段碼流位於窄帶碼流以後,和窄帶碼流中的第二組多描述碼流綁定在一塊兒組包。
解碼器能夠看作編碼器的鏡像,解碼器收到碼流後,首先會經過下述函數解碼獲得 0-8kHz 採樣率的低頻信息,這部分也會在下一期 SOLO 代碼解析裏進行詳細解讀。
SKP_int SKP_Silk_SDK_Decode( void* decState, /* I/O: State */ SKP_SILK_SDK_DecControlStruct* decControl, /* I/O: Control structure */ SKP_int lostFlag, /* I: 0: no loss, 1 loss */ const SKP_uint8 *inData, /* I: Encoded input vector */ const SKP_int16 nBytesIn[], /* I: Number of input Bytes */ SKP_int16 *samplesOut, /* O: Decoded output */ SKP_int16 *nSamplesOut /* I/O: Number of samples */ )
隨後,解碼器經過下述函數獲得低頻殘差信息以用來解碼 8-16kHz 的高頻信息。
SKP_int SKP_Silk_SDK_Get_Decoder_Residue(void *decState, SKP_int32 *r)
同時,解碼器會使用如下函數來進行高頻信息的解碼。
SKP_int32 AGR_Bwe_decode_frame_FLP( AGR_Sate_HB_decoder_control_FLP *hbDecCtrl, AGR_Sate_decoder_hb_state_FLP *psHBDec, NovaBits *bits, /* I bitstream operator */ SKP_float *OutHigh, SKP_int32 *residue_Q10, SKP_int32 lostflag /* I lost falg */ )
該函數內的處理總體上能夠分紅兩種 case,第一種是沒有正常接收到包含高頻信息的多描述碼流,這種狀況下會複用上一幀解碼出的 LSP index 和子幀增益;若是正常接收到了包含高頻信息的多描述碼流,則會從 4 bytes 的高頻信息中解碼、反量化出所需的 LPC 濾波器係數和 4 個子幀增益。
恢復高頻信號使用的殘差信號是乘上子幀增益後的低頻殘差信號。使用高頻殘差再加上高頻 LPC 係數,經過如下函數就能夠解碼獲得高頻信號。
void AGR_Sate_LPC_synthesizer( SKP_float *output, /* O output signal */ SKP_float *ipexc, /* I excitation signal */ SKP_float *sLPC, /* I/O state vector */ SKP_float *a_tmp, /* I filter coefficients */ SKP_int32 LPC_order, /* I filter order */ SKP_int32 subfr_length /* I signal length */ )
隨後,低頻信息和高頻信息會進入到如下函數中,進行高低頻的合成,函數輸出的是 16kHz 採樣率的寬帶信號。
void AGR_Sate_qmf_synth( const spx_word16_t *x1, /* I Low band signal */ const spx_word16_t *x2, /* I High band signal */ const spx_word16_t *a, /* I Qmf coefficients */ spx_word16_t *y, /* O Synthesised signal */ SKP_int32 N, /* I Signal size */ SKP_int32 M, /* I Qmf order */ spx_word16_t *mem1, /* I/O Qmf low band state */ spx_word16_t *mem2, /* I/O Qmf high band state */ SKP_int8 *stack )
至此,解碼端就完成了將窄帶信號擴展成寬帶信號的操做。
若有疑問,能夠 點擊這裏,與咱們的工程師交流。