1. 程式人生 > 其它 >音視訊之播放PCM(七)

音視訊之播放PCM(七)

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