音視訊之播放PCM(七)
阿新 • • 發佈:2021-11-01
使用命令列播放-ffplay
可以使用ffplay播放我們在上面部落格中錄製好的PCm檔案,測試一下是否錄製成功。
播放PCM需要指定相關引數:
- ar: 取樣率
- ac: 聲道數
- f: 取樣格式
- s16le: PCM signed 16-bit little-endian
- 更多PCM的取樣格式可以使用命令檢視
- Windows: ffmpeg -formats | findstr OCM
- Mac: ffmpeg -formats | grep PCM
播放命令如下:
ffplay -ar 44100 -ac 2 -f s16le /Users/muzi/Desktop/out.pcm
藉助SDL使用程式碼播放
簡介
SDL全稱 Simple DirectMedia Layer,是一個跨平臺的C語言多媒體開發庫。
- 支援Windows、Mac OSX、Linux、iOS、Android
- 提供對音訊、鍵盤、滑鼠、遊戲操縱桿、圖形硬體的底層訪問
- 很多的視訊播放軟體、模擬器、受歡迎的遊戲都在使用它
- 目前最新的穩定版本是: 2.0.16
- API文件:wiki
安裝環境
從brew官網可以看的出來:之前執行 brew install ffmpeg時,已經順帶安裝了SDL,安裝目錄是: /usr/local/Cellar/sdl2。如果沒有這個目錄,就執行brew install sdl2進行安裝即可。
配置
.pro檔案
win32 { SDL_HOME = /muzi } mac { SDL_HOME = /usr/local/Cellar/sdl2/2.0.16 } INCLUDEPATH += $${SDL_HOME}/include LIBS += -L $${SDL_HOME}/lib \ -lSDL2
播放PCM
初始化子系統
SDL分成好多個子系統:
- Video: 顯示和視窗管理
- Audio: 音訊裝置管理
- Joystick: 遊戲搖桿控制
- Timers: 定時器
- ...
目前只用到了音訊功能,所以只需要通過SDL_init函式初始化Audio子系統即可。
開啟音訊裝置
// 音訊引數 SDL_AudioSpec spec; // 取樣率 spec.freq = SAMPLE_RATE; // 取樣格式 (s16le) spec.format = AUDIO_S16LSB; // 聲道數 spec.channels = CHANNELS; // 音訊緩衝區的樣本數量(這個值必須是2的冪) spec.samples = 1024; // 回撥 spec.callback = pull_audio_data; AudioBuffer buffer; spec.userdata = &buffer; // 開啟裝置 if (SDL_OpenAudio(&spec, nullptr)) { qDebug() << "SDL_OpenAudio error" << SDL_GetError(); // 清除所有的子系統 SDL_Quit(); return; }
開啟檔案
// 開啟檔案 QFile file(FILENAME); if (!file.open(QFile::ReadOnly)) { qDebug() << "file open error" << FILENAME; // 關閉裝置 SDL_CloseAudio(); // 清除所有的子系統 SDL_Quit(); return; }
開始播放
// 開始播放 (0是取消暫停) SDL_PauseAudio(0); qDebug() << BUFFER_SIZE << "BUFFER_SIZE"; // 存放從檔案中讀取的資料 Uint8 data[BUFFER_SIZE]; while (!isInterruptionRequested()) { // 只要從檔案中讀取的音訊資料,還沒有填充完畢,就跳過 if (buffer.len > 0) continue; buffer.len = file.read((char *) data, BUFFER_SIZE); // 檔案資料已經讀取完畢 if (buffer.len <= 0) { // 剩餘的樣本數量 int samples = buffer.pullLen / BYTES_PER_SAMPLE; int ms = samples * 1000 / SAMPLE_RATE; SDL_Delay(ms); break; } // 讀取到了檔案資料 buffer.data = data; }
回撥函式
// 等待音訊裝置回撥(會回撥多次) void pull_audio_data(void *userdata, Uint8 *stream, // 需要往stream中填充PCM資料 int len) { // 希望填充的大小(samples * format * channels / 8) qDebug() << "pull_audio_data" << len; // 清空stream (靜音處理) SDL_memset(stream, 0 , len); // 取出AudioBuffer AudioBuffer *buffer = (AudioBuffer *)userdata; // 檔案資料還沒準備好 if (buffer->len <= 0) return; // 取len、bufferLen的最小值(為了保證資料安全,防止指標越界) buffer->pullLen = (len > buffer->len) ? buffer->len : len; // 填充資料 SDL_MixAudio(stream, buffer->data, buffer->pullLen, SDL_MIX_MAXVOLUME); buffer->data += buffer->pullLen; buffer->len -= buffer->pullLen; }
釋放資源
// 關閉檔案 file.close(); // 關閉裝置 SDL_CloseAudio(); // 清除所有的子系統 SDL_Quit();