#define EAGAIN 35 // Resource temporarily unavailable
複製代碼
資源暫時不可讀
else if (reslutCode == AVERROR(EAGAIN)){
qDebug() << "錄音設備還未準備好" << reslutCode;
continue;
}
複製代碼
在項目底下添加 Info.plist 文件,文件內容以下:安全
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSMicrophoneUsageDescription</key>
<string>申請用戶麥克風</string>
<key>NSCameraUsageDescription</key>
<string>申請用戶攝像頭</string>
</dict>
</plist>
複製代碼
而後在 .pro
中引入 Info.plist
文件markdown
QMAKE_INFO_PLIST = Info.plist
複製代碼
debug
模式運行便可採樣率和聲道
?(瞭解便可)mac 左上角蘋果圖標
→ 關於本機
→ 系統報告
→ 音頻
// 文件名
QString filename = FILEPATH;
filename += QDateTime::currentDateTime().toString("MM_dd____HH:mm:ss");
filename += ".pcm";
QFile file(filename);
複製代碼
if (!_audioThread) { // 點擊了「開始錄音」
_audioThread = new AudioThread (this);
_audioThread->start();
connect(_audioThread, &AudioThread::finished, [this]() {
_audioThread = nullptr;
ui->startRecordAudioButton->setText("開始錄音");
});
ui->startRecordAudioButton->setText("結束錄音");
}
複製代碼
ffplay -ar 44100 -ac 2 -f s16le in.pcm
複製代碼
-ar
:採樣率-ac
:聲道數-f
:採樣格式,s16le
: signed 16-bit little-endianffmpeg -formats | grep PCM
跨平臺
的 C 語言多媒體開發庫。INCLUDEPATH += /usr/local/Cellar/sdl2/2.0.14_1/include
LIBS += -L /usr/local/Cellar/sdl2/2.0.14_1/lib -lSDL2
複製代碼
if (SDL_Init(SDL_INIT_AUDIO) != 0) {
qDebug() << "初始化 SDL 報錯" << SDL_GetError();
return 1;
}
qDebug() << "初始化 SDL 成功";
SDL_Quit();
複製代碼
in.pcm
的核心步驟(思想)子線程
中作的主要操做① SDL_Init
初始化 SDL_INIT_AUDIO
子系統app
② SDL_OpenAudio
打開音頻子系統函數
③ 建立 SDL_AudioSpec
對象,配置音頻參數
和設備拉流的回調函數
ui
④ file.open
打開文件this
⑤ SDL_PauseAudio(0)
開始播放音頻spa
⑥ 讀取 PCM 數據,提供給 音頻播放設備播放
線程
⑦ 所有播放完畢,關閉文件、關閉設備、清除音頻子系統debug
音頻播放線程(會不斷調用pull_audio_data進行拉流)
的主要操做void pull_audio_data(void *userdata,
// 須要往 stream 中填充 PCM 數據
Uint8 * stream,
// 但願填充的大小(samples * format * channels / 8)
int len)
複製代碼
① 清空 stream(靜音處理)SDL_memset(stream, 0, len);
指針
② 調用 SDL_MixAudio(stream, buffer->data, buffer->pullLen, SDL_MIX_MAXVOLUME);
爲音頻子系統提供數據
③ 調整 buffer 中的 data 和 len
in.pcm
的核心代碼#include "playthread.h"
#include <SDL2/SDL.h>
#include <QDebug>
#include <QFile>
#define FILENAME "F:/in.pcm"
// 採樣率
#define SAMPLE_RATE 44100
// 採樣格式
#define SAMPLE_FORMAT AUDIO_S16LSB
// 採樣大小
#define SAMPLE_SIZE SDL_AUDIO_BITSIZE(SAMPLE_FORMAT)
// 聲道數
#define CHANNELS 2
// 音頻緩衝區的樣本數量
#define SAMPLES 1024
// 每一個樣本佔用多少個字節
#define BYTES_PER_SAMPLE ((SAMPLE_SIZE * CHANNELS) >> 3)
// 文件緩衝區的大小
#define BUFFER_SIZE (SAMPLES * BYTES_PER_SAMPLE)
typedef struct {
int len = 0;
int pullLen = 0;
Uint8 *data = nullptr;
} AudioBuffer;
PlayThread::PlayThread(QObject *parent) : QThread(parent) {
connect(this, &PlayThread::finished,
this, &PlayThread::deleteLater);
}
PlayThread::~PlayThread() {
disconnect();
requestInterruption();
quit();
wait();
qDebug() << this << "析構了";
}
// 等待音頻設備回調(會回調屢次)
void pull_audio_data(void *userdata, // 須要往stream中填充PCM數據 Uint8 *stream, // 但願填充的大小(samples * format * channels / 8) int len ) {
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;
}
void PlayThread::run() {
// 初始化Audio子系統
if (SDL_Init(SDL_INIT_AUDIO)) {
qDebug() << "SDL_Init error" << SDL_GetError();
return;
}
// 音頻參數
SDL_AudioSpec spec;
// 採樣率
spec.freq = SAMPLE_RATE;
// 採樣格式(s16le)
spec.format = SAMPLE_FORMAT;
// 聲道數
spec.channels = CHANNELS;
// 音頻緩衝區的樣本數量(這個值必須是2的冪)
spec.samples = SAMPLES;
// 回調
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);
// 存放從文件中讀取的數據
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;
}
// 關閉文件
file.close();
// 關閉設備
SDL_CloseAudio();
// 清除全部的子系統
SDL_Quit();
}
複製代碼