本系列筆記為博主學習李超老師課程的課堂筆記,僅供參閱
往期課程筆記傳送門:
- 音視頻小白系統入門筆記-0
- 音視頻小白系統入門筆記-1
課程實踐代碼倉庫:傳送門
音視頻編解碼
可以通過ffmpeg -f avfoundation -list_devices true -i ""
查看Mac設備支持的設備編號
編解碼器
上下行網絡一般是非對稱的(下行帶寬一般更大,因為大多數終端都是拉取數據)
壓縮-質量 trade off
壓縮方法:
-
消除冗余信息:有損壓縮
-
剔除人類聽覺范圍外的音頻 (20Hz-20kHz)
-
被遮蔽的音頻信號(心理聲學模型)
-
頻域遮蔽
-
時域遮蔽
-
-
-
無損壓縮:熵編碼
- 哈夫曼編碼
- 算數編碼
- 香農編碼
常見的音頻編解碼器:
- OPUS:新興、延遲小、壓縮率高,WebRTC
- AAC:應用廣泛,支持好,取代mp3
- Ogg:收費
- Speex:混音消除,以前流行
- G.711:窄帶音頻,固定電話,聲音損耗嚴重
壓縮效果:OPUS > AAC > Ogg
AAC編解碼器:
- AAC LC(基礎,128k)
- AAC HE V1(廢棄,按64k左右)+ SBR技術,按頻譜分開保存
- AAC HE V2(添加新技術)+ PS技術,多聲道差異保存
AAC格式:
- ADIF(Audio Data Interchange Format):從頭解碼,多用在磁盤文件
- ADTS(Audio Data Transport Stream):每一幀有一個同步字,大一些,可以在音頻流的任意位置解碼
ADTS結構:7/9個字節,2字節CRC校驗
Audio Object Types:
1 AAC Main
2 AAC LC
5 SBR : AAC HE V1
29 PS:AAC HE V2
Sampling Frequency Index:
4 44100 Hz
11 48000 HZ
ADTS頭規范驗證:http://p23.nl/projects/aac-header
ffmpeg -i demo.mp3 -vn -c:a libfdk_aac -ar 44100 -channel_layout mono/stereo -profile:a aac_he_v2 demo_mp3.aac
-i
指定輸入源-vn
過濾視頻-c:a libfdk_aac
codec:audio 音頻編碼器指定為fdk_aac-ar 44100
采樣率44.1kHz-channel_layout stereo
立體聲采樣-profile:a aac_he_v2
設置音頻編碼格式
為了支持libfdk_aac庫,對于brew安裝的ffmpeg需要使用homebrew-ffmpeg第三方庫安裝支持fdk-aac的版本;對于源碼安裝的ffmpeg,需要在configure時打開libfdk-aac選項重新編譯
音頻重采樣:轉換音頻三元組(采樣率、位深/采樣大小、通道數)
什么是重采樣?
- 目標:將音頻從一種采樣格式(如
48000Hz F32LE 單聲道
)轉換為另一種(如44100Hz S16LE 單聲道
)。 - 關鍵操作:
- 采樣率轉換(如 48kHz → 44.1kHz):通過插值/抽取算法(如線性插值、sinc 濾波)調整樣本數量。
- 格式轉換(如
F32LE
→S16LE
):量化位深,可能涉及縮放(如float [-1,1]
→int16 [-32768,32767]
)。 - 聲道布局調整(如 立體聲 → 單聲道):混合或選擇聲道。
為什么要重采樣:
- 音頻設備采集數據與編碼器要求數據不一致
- 揚聲器要求的音頻數據和播放數據不一致
- 方便運算:混音消除等場景使用單聲道會方便運算
如何知道對應設備要求的規格?
- 了解音頻設備的參數
- 查看ffmpeg的源碼
為什么不把swr_init
合并到swr_alloc_set_opts2
中?
(1) 靈活性:允許動態修改配置- 用戶可能在 `alloc` 后需要 調整參數(例如根據實際輸入動態修改聲道布局),再調用 `swr_init()`。如果合并,每次修改都要重新分配內存,效率更低。(2) 延遲初始化:節省資源- 某些場景下,`SwrContext` 可能被創建但 不立即使用(如預初始化一組轉換器)。合并會導致無用的計算(如濾波器系數)提前執行。(3) 錯誤處理的清晰性- 分離設計允許:- 先檢查 `alloc` 是否成功(內存分配問題)。- 再檢查 `init` 是否成功(參數兼容性問題)。合并后難以區分錯誤類型。
為什么可以逐幀處理?
- 狀態保持:`SwrContext` 內部會緩存部分樣本,處理跨幀的連續性(例如 48kHz → 44.1kHz 時,一幀輸入可能對應不完整輸出幀)。
- 增量處理:每次調用 `swr_convert()` 時:- 輸入:當前幀的音頻數據(如 `2048字節 F32LE`)。- 輸出:盡可能多的重采樣后數據(可能比輸入少/多,取決于采樣率比)。- 剩余未處理的樣本會暫存在 `SwrContext` 中,等待下一幀輸入。
nb_sample
的作用
(1)定義- `nb_sample` 表示 單次處理的音頻樣本數(注意是“樣本數”而非“字節數”)。- 例如:若音頻是單聲道 `F32LE`(每個樣本占4字節),`2048字節` 對應 `2048 / 4 = 512` 個樣本,此時 `nb_sample = 512`。
- 它決定了每次調用 `swr_convert()` 時,輸入/輸出緩沖區的有效數據量。(2)為什么需要它?- 分塊處理:音頻數據通常是流式分塊傳輸的(比如每次從設備讀取一幀),`nb_sample` 告訴重采樣器當前塊有多少有效樣本需要處理。
- 緩沖區管理:輸入/輸出緩沖區需要預分配足夠空間,`nb_sample` 用于計算緩沖區大小(如 `av_samples_alloc_array_and_samples()`)。
為什么 swr_src_data
是 uint8_t**
(二級指針)?
根本原因:FFmpeg 對多聲道音頻的通用設計FFmpeg 的音頻處理 API(如 `swr_convert`、`av_samples_alloc_array_and_samples`)需要兼容 多聲道音頻的平面(Planar)存儲格式。對于多聲道音頻(如立體聲、5.1聲道),數據可能按以下兩種方式存儲:- 交錯(Interleaved):`[LRLRLR...]`(左右聲道數據交替排列)
- 平面(Planar):`[LLLL...]` + `[RRRR...]`(每個聲道單獨連續存儲)**內存布局示例**假設立體聲(2聲道)音頻:- **Planar 模式**:```cswr_src_data[0] = 左聲道數據指針 (LLLL...)swr_src_data[1] = 右聲道數據指針 (RRRR...)```- **Interleaved 模式**:```cswr_src_data[0] = 所有聲道交織數據指針 (LRLRLR...)swr_src_data[1] = NULL (未使用)```
AAC 編碼器的輸入要求
AAC 編碼器(如 `libfdk_aac` 或 FFmpeg 內置的 `aac`)通常支持以下格式:**采樣格式(Sample Format)**:- 必須為 **`AV_SAMPLE_FMT_S16`(16位整型)** 或 **`AV_SAMPLE_FMT_FLTP`(32位浮點平面格式)**。
- 如果設備采集的是其他格式(如 `AV_SAMPLE_FMT_U8`、`AV_SAMPLE_FMT_S32`),需轉換。**聲道布局(Channel Layout)**:- 支持單聲道(`AV_CH_LAYOUT_MONO`)或立體聲(`AV_CH_LAYOUT_STEREO`)。
- 若設備采集的是多聲道(如5.1),需降混(Downmix)或明確編碼器是否支持。**采樣率(Sample Rate)**:- 常見支持 16kHz、32kHz、44.1kHz、48kHz。
- 若設備采集的采樣率不匹配(如8kHz),需重采樣。
AVFrame
:編碼前的數據
AVPacket
:編碼后的數據
調用libfdk_aac編碼時運行報錯:
[libfdk_aac @ 0x138e82c90] frame_size (2048) was not respected for a non-last frame
avcodec_send_frame error -22: Invalid argument
經過查閱資料發現,libfkd-aac編碼器對每次發送幀的采樣數有要求:
- 單通道:必須是2048個采樣
- 雙通道:必須是1024個采樣
前兩幀和最后一幀可以不滿足條件。我們音頻設備采集的一幀數據經過重采樣轉換往往不滿足條件,因此必須做一定的緩沖處理。
Swift調用C++:https://juejin.cn/post/7265999062242033724