// WAV文件头(44字节)
typedef struct {
// RIFF chunk的id
uint8_t riffChunkId[4] = {'R', 'I', 'F', 'F'};
// RIFF chunk的data大小,即文件总长度减去8字节
uint32_t riffChunkDataSize;
// "WAVE"
uint8_t format[4] = {'W', 'A', 'V', 'E'};
/* fmt chunk */
// fmt chunk的id
uint8_t fmtChunkId[4] = {'f', 'm', 't', ' '};
// fmt chunk的data大小:存储PCM数据时,是16
uint32_t fmtChunkDataSize = 16;
// 音频编码,1表示PCM,3表示Floating Point
uint16_t audioFormat = AUDIO_FORMAT_PCM;
// 声道数
uint16_t numChannels;
// 采样率
uint32_t sampleRate;
// 字节率 = sampleRate * blockAlign
uint32_t byteRate;
// 一个样本的字节数 = bitsPerSample * numChannels >> 3
uint16_t blockAlign;
// 位深度
uint16_t bitsPerSample;
/* data chunk */
// data chunk的id
uint8_t dataChunkId[4] = {'d', 'a', 't', 'a'};
// data chunk的data大小:音频数据的总长度,即文件总长度减去文件头的长度(一般是44)
uint32_t dataChunkDataSize;
} WAVHeader;
void FFmpegs::pcm2wav(WAVHeader &header,
const char *pcmFilename,
const char *wavFilename) {
header.blockAlign = header.bitsPerSample * header.numChannels >> 3;
header.byteRate = header.sampleRate * header.blockAlign;
// 打开pcm文件
QFile pcmFile(pcmFilename);
if (!pcmFile.open(QFile::ReadOnly)) {
qDebug() << "文件打开失败" << pcmFilename;
return;
}
header.dataChunkDataSize = pcmFile.size();
header.riffChunkDataSize = header.dataChunkDataSize
+ sizeof (WAVHeader)
- sizeof (header.riffChunkId)
- sizeof (header.riffChunkDataSize);
// 打开wav文件
QFile wavFile(wavFilename);
if (!wavFile.open(QFile::WriteOnly)) {
qDebug() << "文件打开失败" << wavFilename;
pcmFile.close();
return;
}
// 写入头部
wavFile.write((const char *) &header, sizeof (WAVHeader));
// 写入pcm数据
char buf[1024];
int size;
while ((size = pcmFile.read(buf, sizeof (buf))) > 0) {
wavFile.write(buf, size);
}
// 关闭文件
pcmFile.close();
wavFile.close();
}
// 获取输入格式对象
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];
av_strerror(ret, errbuf, sizeof (errbuf));
qDebug() << "打开设备失败" << errbuf;
return;
}
// 文件名
QString filename = FILEPATH;
filename += QDateTime::currentDateTime().toString("MM_dd_HH_mm_ss");
QString wavFilename = filename;
filename += ".pcm";
wavFilename += ".wav";
QFile file(filename);
// 打开文件
// WriteOnly:只写模式。如果文件不存在,就创建文件;如果文件存在,就会清空文件内容
if (!file.open(QFile::WriteOnly)) {
qDebug() << "文件打开失败" << filename;
// 关闭设备
avformat_close_input(&ctx);
return;
}
// 数据包
AVPacket pkt;
while (!isInterruptionRequested()) {
// 不断采集数据
ret = av_read_frame(ctx, &pkt);
if (ret == 0) { // 读取成功
// 将数据写入文件
file.write((const char *) pkt.data, pkt.size);
} else if (ret == AVERROR(EAGAIN)) { // 资源临时不可用
continue;
} else { // 其他错误
char errbuf[1024];
av_strerror(ret, errbuf, sizeof (errbuf));
qDebug() << "av_read_frame error" << errbuf << ret;
break;
}
}
// 关闭文件
file.close();
// 获取输入流
AVStream *stream = ctx->streams[0];
// 获取音频参数
AVCodecParameters *params = stream->codecpar;
// pcm转wav文件
WAVHeader header;
header.sampleRate = params->sample_rate;
header.bitsPerSample = av_get_bits_per_sample(params->codec_id);
header.numChannels = params->channels;
if (params->codec_id >= AV_CODEC_ID_PCM_F32BE) {
header.audioFormat = AUDIO_FORMAT_FLOAT;
}
FFmpegs::pcm2wav(header,
filename.toUtf8().data(),
wavFilename.toUtf8().data());
// 关闭设备
avformat_close_input(&ctx);
qDebug() << this << "正常结束----------";