方法一: 命令行方式
使用FFmpeg命令行进行转换,我们可以使用ffmpeg -hide_banner -ar 44100 -ac 2 -f s16le -i output.pcm output.wav
将PCM转为WAV
➜ ffmpeg -hide_banner -ar 44100 -ac 2 -f s16le -i output.pcm output.wav
[s16le @ 0x5e4e468bb380] Estimating duration from bitrate, this may be inaccurate
[aist#0:0/pcm_s16le @ 0x5e4e468f8140] Guessed Channel Layout: stereo
Input #0, s16le, from 'output.pcm':
Duration: 00:05:19.04, bitrate: 1411 kb/s
Stream #0:0: Audio: pcm_s16le, 44100 Hz, 2 channels, s16, 1411 kb/s
Stream mapping:
Stream #0:0 -> #0:0 (pcm_s16le (native) -> pcm_s16le (native))
Press [q] to stop, [?] for help
Output #0, wav, to 'output.wav':
Metadata:
ISFT : Lavf60.16.100
Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, stereo, s16, 1411 kb/s
Metadata:
encoder : Lavc60.31.102 pcm_s16le
[out#0/wav @ 0x5e4e468c4ec0] video:0kB audio:54960kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.000139%
size= 54960kB time=00:05:19.01 bitrate=1411.3kbits/s speed= 916x
方法二: 编程方式
我们要实现PCM转WAV只需要在PCM文件前面加上WAV文件的格式头就可以了。
头文件
#ifndef FFMPEGS_H
#define FFMPEGS_H
#include <stdint.h>
// WAV文件头(44字节)
typedef struct WAVHeader {
char riffChunkId[4] = {'R', 'I', 'F', 'F'}; // RIFF chunk的id
uint32_t riffChunkDataSize; // RIFF chunk的data大小, 4个字节, 文件总长度减去8
char format[4] = {'W', 'A', 'V', 'E'}; // "WAVE"
char fmtchunkId[4] = {'f', 'm', 't', ' '}; // fmt chunk的id
uint32_t fmtChunkDataSize = 16; // fmt chunk的data大小,存储PCM数据时是16
uint16_t audioFormat = 1; // 音频编码 1表示PCM
uint16_t numChannels; // 声道数
uint32_t sampleRate; // 采样率
uint32_t byteRate; // 字节率 = sampleRate * blockAlign
uint16_t blockAlign; // 一个样本的字节数 = bitsPerSample * numChannels / 8
uint16_t bitsPerSample; // 位深度
char dataSubChunkId[4] = {'d', 'a', 't', 'a'};
uint32_t dataSubChunkDataSize;
} WAVHeader_t;
class FFmpegs
{
public:
FFmpegs();
static void pcm2wav(WAVHeader_t& header, const char* pcmFilename, const char* wavFilename);
};
#endif // FFMPEGS_H
实现文件
#include <QFile>
#include <QDebug>
#include "ffmpegs.h"
FFmpegs::FFmpegs() {}
void FFmpegs::pcm2wav(WAVHeader_t& header, const char* pcmFilename, const char* wavFilename) {
header.blockAlign = header.bitsPerSample * header.numChannels / 8;
header.byteRate = header.sampleRate * header.blockAlign;
// 打开pcm文件
QFile pcmFile(pcmFilename);
if (!pcmFile.open(QIODevice::ReadOnly)) {
qDebug() << "文件打开失败" << pcmFilename;
return;
}
header.dataSubChunkDataSize = pcmFile.size(); // PCM数据大小
// 打开wav文件
QFile wavFile(wavFilename);
if (!wavFile.open(QIODevice::WriteOnly)) {
qDebug() << "文件打开失败" << wavFilename;
pcmFile.close();
return;
}
header.riffChunkDataSize = header.dataSubChunkDataSize + sizeof(WAVHeader_t) - 8; // 一般WAV头部大小为44, 减去RIFF和记录WAV数据部分的大小即可
// 写入头部
wavFile.write((const char *) &header, sizeof(WAVHeader_t));
// 写入PCM,边读边写
char buf[1024];
int size;
while ((size = pcmFile.read(buf, sizeof(buf))) > 0) {
wavFile.write(buf, size);
}
// 关闭文件
pcmFile.close();
wavFile.close();
}
测试代码
WAVHeader_t header;
header.sampleRate = 44100;
header.bitsPerSample = 16;
header.numChannels = 2;
FFmpegs::pcm2wav(header, "/home/gillbert/Music/output.pcm", "/home/gillbert/Music/output.wav");