終於要開始進行FFmpeg實戰了,一起來感受一下FFmpeg的強大吧。
命令簡介
FFmpeg的bin目錄中提供了3個命令(可執行程式),可以直接在命令列上使用。
ffmpeg
ffmpeg的主要作用:對音視訊進行編解碼。
# 將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的主要作用:檢視音視訊的引數資訊。
# 可以檢視MP3檔案的取樣率、位元率、時長等資訊
ffprobe xx.mp3
當輸入命令ffprobe時,可以看到ffprobe命令的使用格式是:
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