将PCM转为WAV文件

6 阅读2分钟

方法一: 命令行方式

使用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");