使用命令行播放-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();