音視頻 Day 09 PCM 轉(zhuǎn) WAV

1. 在 FFmpeg 的源碼中,經(jīng)常能看到 >>3 是什么意思?

  • 二進(jìn)制右移三位,表示 除以 8

2. 如何通過命名行得知錄音設(shè)備的采樣率、聲道數(shù)采樣格式?

  • 讓錄音設(shè)備進(jìn)行錄音 ffmpeg -f avfoundation -i :0 out.wav
Input #0, avfoundation, from ':0':
Duration: N/A, start: 87533.086750, bitrate: 256 kb/s
Stream #0:0: Audio: pcm_f32le, 8000 Hz, mono, flt, 256 kb/s
  • 采樣率:8000Hz
  • 聲道數(shù) mono(單聲道)
  • 采樣格式:f32le(浮點 32 位小端模式)

3. 如何在代碼中獲取錄音設(shè)備的采樣率聲道數(shù)、采樣格式?

  • ctx → streams → streams → codec → sample_rate = 8000 (采樣率)
  • ctx → streams → streams → codec → sample_fmt = 1 (聲道數(shù))
  • ctx → streams → streams → codec_id = AV_CODEC_ID_PCM_F32LE (采樣格式)

4. RIFF 是什么?哪兩個比較主流的音頻格式是遵守 RIFF 的?

  • RIFF(Resource Interchange File Format,資源交換文件格式)由 Microsoft 和 IBM 提出
  • WAV、AVI 文件都是基于 RIFF 標(biāo)準(zhǔn)的文件格式,所以 WAV、AVI 文件的最前面 4 個字節(jié)都是 RIFF 四個字符

5. chunk 是什么意思?一個 chunk 由哪三部分組成?

  • chunk:數(shù)據(jù)塊的意思
  • 每個 chunk 主要包含三部分:id(chunk 的標(biāo)識)、data-size(chunk 的數(shù)據(jù)部分大小,字節(jié)為單位)、data(chunk 的數(shù)據(jù)部分)

6. 解剖 WVA 文件

  • 使用前面命令行錄制音頻,可以獲得 out.wav 文件,文件大?。?code>348204 字節(jié)
  • 我們將 out.wav 使用 vscode 以二進(jìn)制文件的格式打開,然后我們分析其頭部數(shù)據(jù)
// out.wav 的首部部分16 進(jìn)制數(shù)據(jù)
00000000: 52 49 46 46 24 50 05 00 57 41 56 45 66 6D 74 20    RIFF$P..WAVEfmt.
00000010: 10 00 00 00 01 00 02 00 44 AC 00 00 10 B1 02 00    ........D,...1..
00000020: 04 00 10 00 64 61 74 61 00 50 05 00 00 00 0E BB    ....data.P.....;
  • out.wav 的前 44 個字節(jié)進(jìn)行解析如下:
52 49 46 46(ChunkID,4 字節(jié)) = ‘RIFF’

24 50 05 00 (ChunkSize,4 字節(jié))=  348196  = 348204 - 8

57 41 56 45 (Format,4 字節(jié)) = ‘WAVE’

66 6D 74 20 (Subchunk1ID,4 字節(jié)) = ‘fmt ’

10 00 00 00 (Subchunk1 Size,4 字節(jié)) = 16

01 00(AudioFormate,2 字節(jié)) = 音頻格式 = 1

02 00 (NumChannels,2 字節(jié))= 聲道數(shù) = 2

44 AC 00 00(SampleRate,4 字節(jié))= 采樣率 = 44100

10 B1 02 00(ByteRate,4 字節(jié))= 字節(jié)率 = 176400

04 00 (BlockAlign,2 字節(jié))= 內(nèi)存對齊 = 4

10 00 (BitsPerSample)= 每個樣本的位深度 =  16

64 61 74 61(Subchunk2ID,4 字節(jié)) = ‘data’

00 50 05 00 (Subchunk2 Size,4 字節(jié)) = 音頻PCM數(shù)據(jù)大小 = 348160 = 348204 - 44

有資料需求的可以了解下,有關(guān)于包括 數(shù)據(jù)結(jié)構(gòu)、底層進(jìn)階、圖形視覺、音視頻、架構(gòu)設(shè)計、逆向安防、RxSwift、flutter,算法等方面的資料,面試資料就在群文件里面可自行下載,891點擊 488進(jìn)入 181有什么問題也可以直接在里面提出來,互相交流,同時內(nèi)有好友發(fā)內(nèi)推機會,一起共同進(jìn)步!

7. 需要理解的三幅圖

WAV 文件格式
WAV 文件格式

8. 命令行實現(xiàn) PCM 轉(zhuǎn) WAV

ffmpeg -ar 44100 -ac 2 -f s16le -i out.pcm -bitexact out2.wav

9. 代碼實現(xiàn) PCM 轉(zhuǎn) WAV

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 數(shù)據(jù)
 char buf[1024];
 int size;
 while ((size = pcmFile.read(buf, sizeof(buf))) > 0) {
     wavFile.write(buf, size);
 }

 // 關(guān)閉文件
 pcmFile.close();
 wavFile.close();
}
  • 調(diào)用代碼如下:
WAVHeader header;
header.sampleRate = 44100;
header.bitsPerSample = 16;
header.numChannels = 2;

FFmpegs::pcm2wav(header,
              "/Users/linsipei/Documents/ffmpeg_workspcace/05_tmp/in.pcm",
              "/Users/linsipei/Documents/ffmpeg_workspcace/05_tmp/out.wav");

10. 代碼實現(xiàn) 錄制的PCM 直接轉(zhuǎn) WAV(包含動態(tài)獲取錄音設(shè)備的相關(guān)信息操作)

// 獲取輸入流
AVStream *stream = ctx->streams[0];
// 獲取音頻參數(shù)
AVCodecParameters *params = stream->codecpar;

//pcm 轉(zhuǎn) 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());

作者:carrot__lsp
鏈接:https://juejin.cn/post/6955032831466684453

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容