網站首頁 編程語言 正文
什么是PCM
PCM(Pulse Code Modulation,脈沖編碼調制)音頻數據是未經壓縮的音頻采樣數據裸流,它是由模擬信號經過采樣、量化、編碼轉換成的標準數字音頻數據。
描述PCM數據的6個參數:
- Sample Rate : 采樣頻率。8kHz(電話)、44.1kHz(CD)、48kHz(DVD)。
- Sample Size : 量化位數。通常該值為16-bit。
- Number of Channels : 通道個數。常見的音頻有立體聲(stereo)和單聲道(mono)兩種類型,立體聲包含左聲道和右聲道。另外還有環繞立體聲等其它不太常用的類型。
- Sign : 表示樣本數據是否是有符號位,比如用一字節表示的樣本數據,有符號的話表示范圍為-128 ~ 127,無符號是0 ~ 255。
- Byte Ordering : 字節序。字節序是little-endian還是big-endian。通常均為little-endian。字節序說明見第4節。
- Integer Or Floating Point : 整形或浮點型。大多數格式的PCM樣本數據使用整形表示,而在一些對精度要求高的應用方面,使用浮點類型表示PCM樣本數據。
字節序
字節序是指多字節數據在計算機內存中存儲或者網絡傳輸時各字節的存儲順序。
大端字節序(Big Endian):將多個字節值的最高有效字節儲存于較低的內存位置。在大端處理器的機器上,數值0xABCD1234在內存存儲為連續字節0xAB、0xCD、0x12、0x34。
小端字節序(Little endian):將多個字節值的最低有效字節存儲于較低的內存位置。比如在小段處理器的機器上,數值0xABCD1234在內存中存儲為連續的字節0x34、0x12、0xCD、0x341。
FFmpeg支持的PCM數據格式
在cmder中使用ffmpeg -formats | grep PCM命令,獲取ffmpeg支持的音視頻格式,其中我們可以找到支持的PCM格式。
?DE alaw ? ? ? ? ? ?PCM A-law
?DE f32be ? ? ? ? ? PCM 32-bit floating-point big-endian
?DE f32le ? ? ? ? ? PCM 32-bit floating-point little-endian
?DE f64be ? ? ? ? ? PCM 64-bit floating-point big-endian
?DE f64le ? ? ? ? ? PCM 64-bit floating-point little-endian
?DE mulaw ? ? ? ? ? PCM mu-law
?DE s16be ? ? ? ? ? PCM signed 16-bit big-endian
?DE s16le ? ? ? ? ? PCM signed 16-bit little-endian
?DE s24be ? ? ? ? ? PCM signed 24-bit big-endian
?DE s24le ? ? ? ? ? PCM signed 24-bit little-endian
?DE s32be ? ? ? ? ? PCM signed 32-bit big-endian
?DE s32le ? ? ? ? ? PCM signed 32-bit little-endian
?DE s8 ? ? ? ? ? ? ?PCM signed 8-bit
?DE u16be ? ? ? ? ? PCM unsigned 16-bit big-endian
?DE u16le ? ? ? ? ? PCM unsigned 16-bit little-endian
?DE u24be ? ? ? ? ? PCM unsigned 24-bit big-endian
?DE u24le ? ? ? ? ? PCM unsigned 24-bit little-endian
?DE u32be ? ? ? ? ? PCM unsigned 32-bit big-endian
?DE u32le ? ? ? ? ? PCM unsigned 32-bit little-endian
?DE u8 ? ? ? ? ? ? ?PCM unsigned 8-bit
?DE vidc ? ? ? ? ? ?PCM Archimedes VIDC
s是有符號,u是無符號,f是浮點數。
be是大端,le是小端。
FFmpeg中Packed和Planar的PCM數據區別
FFmpeg中音視頻數據基本上都有Packed(打包格式)和Planar(平面格式)兩種存儲方式,對于雙聲道音頻來說,Packed方式為兩個聲道的數據交錯存儲;Planar方式為兩個聲道分開存儲。假設一個L/R為一個采樣點,數據存儲的方式如下所示:
- Packed: L R L R L R L R
- Planar: L L L L R R R R
FFmpeg音頻解碼后的數據是存放在AVFrame結構中的。
- Packed格式,frame.data[0]或frame.extended_data[0]包含所有的音頻數據中。
- Planar格式,frame.data[i]或者frame.extended_data[i]表示第i個聲道的數據(假設聲道0是第一個), AVFrame.data數組大小固定為8,如果聲道數超過8,需要從frame.extended_data獲取聲道數據。
下面為FFmpeg內部存儲音頻使用的采樣格式,所有的Planar格式后面都有字母P標識。
enum AVSampleFormat { AV_SAMPLE_FMT_NONE = -1, AV_SAMPLE_FMT_U8, ///< unsigned 8 bits AV_SAMPLE_FMT_S16, ///< signed 16 bits AV_SAMPLE_FMT_S32, ///< signed 32 bits AV_SAMPLE_FMT_FLT, ///< float AV_SAMPLE_FMT_DBL, ///< double AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar AV_SAMPLE_FMT_FLTP, ///< float, planar AV_SAMPLE_FMT_DBLP, ///< double, planar AV_SAMPLE_FMT_S64, ///< signed 64 bits AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar ??????? AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically };
說明:
Planar模式是ffmpeg內部存儲模式,我們實際使用的音頻文件都是Packed模式的。
FFmpeg解碼不同格式的音頻輸出的音頻采樣格式不是一樣。測試發現,其中AAC解碼輸出的數據為浮點型的 AV_SAMPLE_FMT_FLTP 格式,MP3解碼輸出的數據為 AV_SAMPLE_FMT_S16P 格式(使用的mp3文件為16位深)。具體采樣格式可以查看解碼后的AVFrame中的format成員或解碼器的AVCodecContext中的sample_fmt成員。
Planar或者Packed模式直接影響到保存文件時寫文件的操作,操作數據的時候一定要先檢測音頻采樣格式。
實戰FFmpeg分離出PCM數據
先用ffprobe命令查看文件詳情
ffprobe -i input.mp4
詳情
#音頻 Stream #0:0(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default) Metadata: creation_time : 2020-10-12T15:12:33.000000Z vendor_id : [0][0][0][0] #視頻 Stream #0:1(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 368x384, 383 kb/s, 29.95 fps, 29.97 tbr, 90k tbn, 60 tbc (default) Metadata: creation_time : 2020-10-12T15:12:33.000000Z vendor_id : [0][0][0][0] encoder : JVT/AVC Coding
用ffmpeg命令轉換
ffmpeg -i input.mp4 -ar 44100 -ac 2 -f s16le output.pcm
其中
# 輸入文件 -i # 格式 -f fmt force format #設置音頻采樣率 -ar rate set audio sampling rate (in Hz) #設置音頻通道數 -ac channels set number of audio channels
輸出
Audicity播放
文件-原理音頻
分離雙聲道PCM音頻數據左右聲道的數據
按照雙聲道的LRLRLR的PCM音頻數據可以通過將它們交叉的讀出來的方式來分離左右聲道的數據。
int pcm_s16le_split(const char* file, const char* out_lfile, const char* out_rfile) { FILE *fp = fopen(file, "rb+"); if (fp == NULL) { printf("open %s failed\n", file); return -1; } FILE *fp1 = fopen(out_lfile, "wb+"); if (fp1 == NULL) { printf("open %s failed\n", out_lfile); return -1; } FILE *fp2 = fopen(out_rfile, "wb+"); if (fp2 == NULL) { printf("open %s failed\n", out_rfile); return -1; } char * sample = (char *)malloc(4); while(!feof(fp)) { fread(sample, 1, 4, fp); //L fwrite(sample, 1, 2, fp1); //R fwrite(sample + 2, 1, 2, fp2); } free(sample); fclose(fp); fclose(fp1); fclose(fp2); return 0; }
函數說明
fread函數
描述
C 庫函數 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 從給定流 stream 讀取數據到 ptr 所指向的數組中。
聲明
下面是 fread() 函數的聲明。
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
參數
- ptr – 這是指向帶有最小尺寸 size*nmemb 字節的內存塊的指針。
- size – 這是要讀取的每個元素的大小,以字節為單位。
- nmemb – 這是元素的個數,每個元素的大小為 size 字節。
- stream – 這是指向 FILE 對象的指針,該 FILE 對象指定了一個輸入流。
返回值
成功讀取的元素總數會以 size_t 對象返回,size_t 對象是一個整型數據類型。如果總數與 nmemb 參數不同,則可能發生了一個錯誤或者到達了文件末尾。
fwrite函數
描述
C 庫函數 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 把 ptr 所指向的數組中的數據寫入到給定流 stream 中。
聲明
下面是 fwrite() 函數的聲明。
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
參數
- ptr – 這是指向要被寫入的元素數組的指針。
- size – 這是要被寫入的每個元素的大小,以字節為單位。
- nmemb – 這是元素的個數,每個元素的大小為 size 字節。
- stream – 這是指向 FILE 對象的指針,該 FILE 對象指定了一個輸出流。
返回值
如果成功,該函數返回一個 size_t 對象,表示元素的總數,該對象時一個整型數據類型。如果該數字與 nmemb 參數不同,則會顯示一個錯誤。
fopen函數
描述
C 庫函數 FILE *fopen(const char *filename, const char *mode) 使用給定的模式 mode 打開 filename 所指向的文件。
聲明
下面是 fopen() 函數的聲明。
FILE *fopen(const char *filename, const char *mode)
參數
- filename – 這是 C 字符串,包含了要打開的文件名稱。
- mode – 這是 C 字符串,包含了文件訪問模式,模式如下:
模式 | 描述 |
---|---|
“r” | 打開一個用于讀取的文件。該文件必須存在。 |
“w” | 創建一個用于寫入的空文件。如果文件名稱與已存在的文件相同,則會刪除已有文件的內容,文件被視為一個新的空文件。 |
“a” | 追加到一個文件。寫操作向文件末尾追加數據。如果文件不存在,則創建文件。 |
“r+” | 打開一個用于更新的文件,可讀取也可寫入。該文件必須存在。 |
“w+” | 創建一個用于讀寫的空文件。 |
“a+” | 打開一個用于讀取和追加的文件。 |
返回值
該函數返回一個?FILE
?指針。否則返回 NULL,且設置全局變量 errno 來標識錯誤。
原文鏈接:https://blog.csdn.net/e891377/article/details/127794560
相關推薦
- 2022-08-11 利用python繪制線型圖_python
- 2021-12-01 C語言system函數使用方法詳解_C 語言
- 2022-05-15 Web?API中使用Autofac實現依賴注入_實用技巧
- 2022-12-04 pyecharts如何旋轉折線圖的X軸標簽_python
- 2022-06-10 在vscode中快速新建html文件的2種方法總結_C 語言
- 2022-11-14 解決“您的連接不是私密鏈接”的問題
- 2022-07-07 python中列表對象pop()方法的使用說明_python
- 2022-09-25 C語言數據存儲歸類介紹_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支