#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();
}
复制代码