【秒懂音影片開發】09_音頻錄製02_編程
通過編程錄音
開發錄音功能的主要步驟是:
- 註冊設備
- 獲取輸入格式對象
- 打開設備
- 採集數據
- 釋放資源

需要用到的FFmpeg庫有3個。
extern "C" {
// 設備相關API
#include <libavdevice/avdevice.h>
// 格式相關API
#include <libavformat/avformat.h>
// 工具相關API(比如錯誤處理)
#include <libavutil/avutil.h>
}
註冊設備
在整個程式的運行過程中,只需要執行1次註冊設備的程式碼。
// 初始化libavdevice並註冊所有輸入和輸出設備
avdevice_register_all();
獲取輸入格式對象
宏定義
Windows和Mac環境的格式名稱、設備名稱都是不同的,所以使用條件編譯實現跨平台。
// 格式名稱、設備名稱目前暫時使用宏定義固定死
#ifdef Q_OS_WIN
// 格式名稱
#define FMT_NAME "dshow"
// 設備名稱
#define DEVICE_NAME "audio=麥克風陣列 (Realtek(R) Audio)"
#else
#define FMT_NAME "avfoundation"
#define DEVICE_NAME ":0"
#endif
核心程式碼
根據格式名稱獲取輸入格式對象,後面需要利用輸入格式對象打開設備。
AVInputFormat *fmt = av_find_input_format(FMT_NAME);
if (!fmt) {
// 如果找不到輸入格式
qDebug() << "找不到輸入格式" << FMT_NAME;
return;
}
打開設備
// 格式上下文(後面通過格式上下文操作設備)
AVFormatContext *ctx = nullptr;
// 打開設備
int ret = avformat_open_input(&ctx, DEVICE_NAME, fmt, nullptr);
// 如果打開設備失敗
if (ret < 0) {
char errbuf[1024] = {0};
// 根據函數返回的錯誤碼獲取錯誤資訊
av_strerror(code, errbuf, sizeof (errbuf));
qDebug() << "打開設備失敗" << errbuf;
return;
}
採集數據
宏定義
#ifdef Q_OS_WIN
// PCM文件的文件名
#define FILENAME "F:/out.pcm"
#else
#define FILENAME "/Users/mj/Desktop/out.pcm"
#endif
核心程式碼
#include <QFile>
// 文件
QFile file(FILENAME);
// WriteOnly:只寫模式。如果文件不存在,就創建文件;如果文件存在,就刪除文件內容
if (!file.open(QFile::WriteOnly)) {
qDebug() << "文件打開失敗" << FILENAME;
// 關閉設備
avformat_close_input(&ctx);
return;
}
// 暫時假定只採集50個數據包
int count = 50;
// 數據包
AVPacket pkt;
// 從設備中採集數據
// av_read_frame返回值為0,代表採集數據成功
while (count-- > 0 && av_read_frame(ctx, &pkt) == 0) {
// 寫入數據
file.write((const char *) pkt.data, pkt.size);
}
釋放資源
// 關閉文件
file.close();
// 關閉設備
avformat_close_input(&ctx);
想要了解每一個函數的具體作用,可以查詢:官方API文檔。
多執行緒
錄音屬於耗時操作,為了避免阻塞主執行緒,最好在子執行緒中進行錄音操作。這裡創建了一個繼承自QThread的執行緒類,執行緒一旦啟動(start),就會自動調用run函數。
.h
#include <QThread>
class AudioThread : public QThread {
Q_OBJECT
private:
void run();
public:
explicit AudioThread(QObject *parent = nullptr);
~AudioThread();
};
.cpp
AudioThread::AudioThread(QObject *parent,
AVInputFormat *fmt,
const char *deviceName)
: QThread(parent), _fmt(fmt), _deviceName(deviceName) {
// 在執行緒結束時自動回收執行緒的記憶體
connect(this, &AudioThread::finished,
this, &AudioThread::deleteLater);
}
AudioThread::~AudioThread() {
// 執行緒對象的記憶體回收時,正常結束執行緒
requestInterruption();
quit();
wait();
}
void AudioThread::run() {
// 錄音操作
// ...
}
開啟執行緒
AudioThread *audioThread = new AudioThread(this);
audioThread->start();
結束執行緒
// 外部調用執行緒的requestInterruption,請求結束執行緒
audioThread->requestInterruption();
// 執行緒內部的邏輯
void AudioThread::run() {
// 可以通過isInterruptionRequested判斷是否要結束執行緒
// 當調用過執行緒的requestInterruption時,isInterruptionRequested返回值就為true,否則為false
while (!isInterruptionRequested()) {
// ...
}
}

