【秒懂音視頻開發】08_音頻錄製

終於要開始進行FFmpeg實戰了,一塊兒來感覺一下FFmpeg的強大吧。html

命令簡介

FFmpeg的bin目錄中提供了3個命令(可執行程序),能夠直接在命令行上使用。編程

3個命令

ffmpeg

ffmpeg的主要做用:對音視頻進行編解碼。ide

# 將MP3文件轉成WAV文件
ffmpeg -i xx.mp3 yy.wav

當輸入命令ffmpeg時,能夠看到ffmpeg命令的使用格式是:函數

ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...

簡化一下,經常使用格式是:工具

ffmpeg arg1 arg2 -i arg3 arg4 arg5
  • arg1:全局參數
  • arg2:輸入文件參數
  • arg3:輸入文件
  • arg4:輸出文件參數
  • arg5:輸出文件

更多詳細用法,能夠參考官方文檔:ffmpeg-all.html,或者使用如下命令查看:測試

# 簡易版
ffmpeg -h
# 詳細版
ffmpeg -h long
# 完整版
ffmpeg -h full

# 或者使用
# ffmpeg -help
# ffmpeg -help long
# ffmpeg -help full

ffprobe

ffprobe的主要做用:查看音視頻的參數信息。ui

# 能夠查看MP3文件的採樣率、比特率、時長等信息
ffprobe xx.mp3

當輸入命令ffprobe時,能夠看到ffprobe命令的使用格式是:this

ffprobe [OPTIONS] [INPUT_FILE]
# OPTIONS:參數
# INPUT_FILE:輸入文件

更多詳細用法,能夠參考官方文檔:ffprobe-all.html,或者使用如下命令查看:命令行

# 簡易版
ffprobe -h
# 詳細版
ffprobe -h long
# 完整版
ffprobe -h full

# 或者使用
# ffprobe -help
# ffprobe -help long
# ffprobe -help full

ffplay

ffplay的主要做用:播放音視頻。線程

# 播放MP3文件
ffplay xx.mp3

當輸入命令ffplay時,能夠看到ffplay命令的使用格式是:

ffplay [options] input_file
# options:參數
# input_file:輸入文件

更多詳細用法,能夠參考官方文檔:ffplay-all.html,或者使用如下命令查看:

# 簡易版
ffplay -h
# 詳細版
ffplay -h long
# 完整版
ffplay -h full

# 或者使用
# ffplay -help
# ffplay -help long
# ffplay -help full

hide_bannder

增長-hide_bannder參數能夠隱藏一些冗餘的描述信息,能夠去實踐比較如下2條命令的區別:

ffprobe xx.mp3

ffprobe -hide_banner xx.mp3

# ffmpeg、ffprobe、ffplay都適用

經過命令行錄音

查看可用設備

使用命令行查看當前平臺的可用設備:

ffmpeg -devices

Windows的輸出結果以下所示:

  • 列表中有個dshow,全名叫DirectShow,是Windows平臺的多媒體系統庫
  • 咱們可使用dshow去操做多媒體輸入設備(好比錄音設備)
Devices:
 D. = Demuxing supported
 .E = Muxing supported
 --
  E caca            caca (color ASCII art) output device
 D  dshow           DirectShow capture
 D  gdigrab         GDI API Windows frame grabber
 D  lavfi           Libavfilter virtual input device
 D  libcdio
  E sdl,sdl2        SDL2 output device
 D  vfwcap          VfW video capture

Mac的輸出結果以下所示:

  • 列表中有個avfoundation,是Mac平臺的多媒體系統庫
  • 咱們可使用avfoundation去操做多媒體輸入設備(好比錄音設備)
Devices:
 D. = Demuxing supported
 .E = Muxing supported
 --
 D  avfoundation    AVFoundation input device
 D  lavfi           Libavfilter virtual input device
  E sdl,sdl2        SDL2 output device

查看dshow支持的設備

# 查看dshow支持的設備
ffmpeg -f dshow -list_devices true -i dummy

# 或者
# ffmpeg -list_devices true -f dshow -i ''
# ffmpeg -list_devices true -f dshow -i ""
  • -f dshow

    • dshow支持的
  • -list_devices true

    • 打印出全部的設備
  • -i dummy-i ''-i ""

    • 當即退出

個人筆記本外接了一隻麥克風。

外接麥克風

所以,命令的執行結果大體以下所示:

DirectShow video devices (some may be both video and audio devices)
  "Integrated Camera"

DirectShow audio devices
  "線路輸入 (3- 魅聲T800)"
  "麥克風陣列 (Realtek(R) Audio)"
  • dshow支持的視頻設備

    • Integrated Camera:筆記本自帶的攝像頭
  • dshow支持的音頻設備

    • 線路輸入 (3- 魅聲T800):外接的麥克風
    • 麥克風陣列 (Realtek(R) Audio):筆記本自帶的麥克風

查看avfoundation支持的設備

在Mac平臺,使用的是avfoundation,而不是dshow。

ffmpeg -f avfoudation -list_devices true -i ''

輸出結果以下所示:

AVFoundation video devices:
 [0] FaceTime高清攝像頭(內建)
 [1] Capture screen 0
AVFoundation audio devices:
 [0] MS-T800
 [1] Edu Audio Device
 [2] MacBook Pro麥克風

列表中的MS-T800是外接的麥克風。在Mac上,FFmpeg還給每個視頻、音頻設備進行了編號,好比MS-T800的編號是0、Mac自帶麥克風的編號是2。

指定設備進行錄音

# 使用外接的麥克風進行錄音,最後生成一個wav文件
ffmpeg -f dshow -i audio="麥克風陣列 (Realtek(R) Audio)" out.wav

# 在Mac上經過編號指定設備
ffmpeg -f avfoundation -i :0 out.wav
# :0表示使用0號音頻設備
# 0:2表示使用0號視頻設備和2號音頻設備
  • 可使用快捷鍵Ctrl + C終止錄音
  • 我這邊的測試結果顯示,默認的音頻參數是:
    • Windows:44100Hz採樣率、16位深度、2聲道、1411Kbps比特率
    • Mac:48000Hz採樣率、16位深度、2聲道、1536Kbps比特率

設置dshow的參數

先經過命令查看一下dshow可使用的參數,詳情能夠查看官方文檔:dshow參數

# 從ffmpeg -devices命令的結果能夠看得出來:dshow屬於demuxer,而不是muxer
ffmpeg -h demuxer=dshow

部分輸出結果以下所示:

# 採樣率
-sample_rate         <int> set audio sample rate (from 0 to INT_MAX)
# 採樣大小(位深度)
-sample_size         <int> set audio sample size (from 0 to 16)
# 聲道數
-channels            <int> set number of audio channels, such as 1 or 2 (from 0 to INT_MAX)
# 列出特定設備支持的參數
-list_options        <boolean> list available options for specified device (default false)

而後再看看你的設備支持哪些參數。

ffmpeg -f dshow -list_options true -i audio="麥克風陣列 (Realtek(R) Audio)"

輸出結果以下所示:

DirectShow audio only device options (from audio devices)
  Pin "Capture" (alternative pin name "Capture")
  min ch=1 bits=8 rate= 11025 max ch=2 bits=16 rate= 44100
  
# 能夠看出來:採樣率範圍是11025~44100Hz

接下來設置錄音時的音頻參數。

ffmpeg -f dshow -sample_rate 15000 -sample_size 16 -channels 1 -i audio="麥克風陣列 (Realtek(R) Audio)" out.wav

經過編程錄音

主要步驟

開發錄音功能的主要步驟是:

  • 註冊設備
  • 獲取輸入格式
  • 打開設備
  • 讀取數據
  • 釋放資源

之後的開發過程當中,常常須要查詢的資料:官方API文檔

AudioThread

錄音是個耗時操做,所以最好放到一個子線程中進行。這裏建立了一個繼承自QThread的線程類,線程一旦啓動(start),就會自動調用run函數。

  • AudioThread.h
#include <QThread>

class AudioThread : public QThread
{
    Q_OBJECT
private:
    void run();

public:
    explicit AudioThread(QObject *parent = nullptr);
    ~AudioThread();
};
  • AudioThread.cpp
#include "audiothread.h"
#include <QDebug>
#include <QFile>

extern "C" {
    // 編解碼相關API
    #include <libavcodec/avcodec.h>
    // 格式相關API
    #include <libavformat/avformat.h>
    // 工具API(好比錯誤處理)
    #include <libavutil/avutil.h>
}

AudioThread::AudioThread(QObject *parent) : QThread(parent)
{
    // 在線程結束時自動回收線程的內存
    connect(this, &AudioThread::finished,
            this, &AudioThread::deleteLater);
}

AudioThread::~AudioThread()
{
    // 線程銷燬時,正常退出線程
    quit();
    requestInterruption();
    wait();
}

void AudioThread::run()
{
    // 格式名稱
    const char *fmtName;
#if defined (Q_OS_WIN)
    fmtName = "dshow";
#else
    fmtName = "avfoundation";
#endif

    // 根據名稱找到輸入格式對象
    AVInputFormat *fmt = av_find_input_format(fmtName);
    if (fmt == NULL) {
        qDebug() << "找不到輸入格式:" << fmtName;
        return;
    }

    // 設備名稱
    const char *deviceName;
#if defined (Q_OS_WIN)
    deviceName = "audio=麥克風陣列 (Realtek(R) Audio)";
#else
    deviceName = ":0";
#endif

    // 格式上下文
    AVFormatContext *ctx = NULL;
    // 選項
    AVDictionary *options = NULL;

    // 打開輸入流
    int ret = avformat_open_input(&ctx, deviceName, fmt, &options);

    // 若是打開輸入流失敗
    if (ret < 0) {
        char errbuf[1024] = {0};
        av_strerror(ret, errbuf, sizeof(errbuf));
        qDebug() << "打開輸入流失敗:" << errbuf;
        return;
    }

    // 定義文件
    QFile file("F:/out.pcm");

    // 刪除舊文件
    if (file.exists()) {
        file.remove();
    }

    // 打開文件
    if (!file.open(QIODevice::WriteOnly)){
        qDebug() << "文件打開失敗";
        // 關閉輸入流
        avformat_close_input(&ctx);
        return;
    }

    // 數據包
    AVPacket pkt;
    while (!isInterruptionRequested()
           && av_read_frame(ctx, &pkt) == 0) {
        // 寫入數據
        file.write((char *) pkt.data, pkt.size);
    }

    // 關閉文件
    file.close();

    // 關閉輸入流
    avformat_close_input(&ctx);
}

MainWindow

在MainWindow控制開始、結束錄音。

  • MainWindow.h
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_audioBtn_clicked();

private:
    Ui::MainWindow *ui;
    AudioThread *_audioThread = nullptr;
};
  • MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include "audiothread.h"

extern "C" {
    // 設備相關API
    #include <libavdevice/avdevice.h>
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 初始化libavdevice並註冊全部輸入和輸出設備
    avdevice_register_all();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_audioBtn_clicked()
{
    // 沒有線程
    if (_audioThread == nullptr) {
        // 開啓新線程
        _audioThread = new AudioThread(this);
        _audioThread->start();

        // 設置按鈕文字
        ui->audioBtn->setText("中止錄音");
    } else {
        // 中止錄音
        _audioThread->requestInterruption();
        _audioThread = nullptr;

        // 設置按鈕文字
        ui->audioBtn->setText("開始錄音");
    }
}

播放PCM

播放PCM時須要制定相關參數:

  • ar:採樣率
  • ac:聲道數
  • f:採樣格式
ffplay -ar 44100 -ac 2 -f s16le out.pcm
相關文章
相關標籤/搜索