音視頻之播放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();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM