1. 在 FFmpeg 的源碼中,常常能看到 >>3
是什麼意思?
2. 如何經過命名行
得知錄音設備的採樣率
、聲道數
、採樣格式
?
- 讓錄音設備進行錄音
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
聲道數
mono(單聲道)
採樣格式
:f32le(浮點 32 位小端模式)
3. 如何在代碼中獲取錄音設備的採樣率
、聲道數
、採樣格式
?
- ctx → streams → streams → codec → sample_rate = 8000 (採樣率)
- ctx → streams → streams → codec → sample_fmt = 1 (聲道數)
- ctx → streams → streams → codec_id = AV_CODEC_ID_PCM_F32LE (採樣格式)
4. RIFF 是什麼?哪兩個比較主流的音頻格式是遵照 RIFF 的?
- RIFF(Resource Interchange File Format,資源交換文件格式)由 Microsoft 和 IBM 提出
- WAV、AVI 文件都是基於 RIFF 標準的文件格式,因此 WAV、AVI 文件的最前面 4 個字節都是 RIFF 四個字符
5. chunk 是什麼意思?一個 chunk 由哪三部分組成?
- chunk:數據塊的意思
- 每一個 chunk 主要包含三部分:id(chunk 的標識)、data-size(chunk 的數據部分大小,字節爲單位)、data(chunk 的數據部分)
6. 解剖 WVA 文件
- 使用前面命令行錄製音頻,能夠得到
out.wav
文件,文件大小:348204
字節
- 咱們將
out.wav
使用 vscode 以二進制文件的格式打開,而後咱們分析其頭部數據
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 個字節進行解析以下:
52 49 46 46(ChunkID,4 字節) = ‘RIFF’
24 50 05 00 (ChunkSize,4 字節)= 348196 = 348204 - 8
57 41 56 45 (Format,4 字節) = ‘WAVE’
66 6D 74 20 (Subchunk1ID,4 字節) = ‘fmt ’
10 00 00 00 (Subchunk1 Size,4 字節) = 16
01 00(AudioFormate,2 字節) = 音頻格式 = 1
02 00 (NumChannels,2 字節)= 聲道數 = 2
44 AC 00 00(SampleRate,4 字節)= 採樣率 = 44100
10 B1 02 00(ByteRate,4 字節)= 字節率 = 176400
04 00 (BlockAlign,2 字節)= 內存對齊 = 4
10 00 (BitsPerSample)= 每一個樣本的位深度 = 16
64 61 74 61(Subchunk2ID,4 字節) = ‘data’
00 50 05 00 (Subchunk2 Size,4 字節) = 音頻PCM數據大小 = 348160 = 348204 - 44
複製代碼
7. 須要理解的三幅圖
8. 命令行實現 PCM 轉 WAV
ffmpeg -ar 44100 -ac 2 -f s16le -i out.pcm -bitexact out2.wav
複製代碼
9. 代碼實現 PCM 轉 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;
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);
QFile wavFile(wavFilename);
if (!wavFile.open(QFile::WriteOnly)) {
qDebug() << "文件打開失敗" << wavFilename;
pcmFile.close();
return;
}
wavFile.write((const char*)&header, sizeof(WAVHeader));
char buf[1024];
int size;
while ((size = pcmFile.read(buf, sizeof(buf))) > 0) {
wavFile.write(buf, size);
}
pcmFile.close();
wavFile.close();
}
複製代碼
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. 代碼實現 錄製的PCM 直接轉 WAV(包含動態獲取錄音設備的相關信息操做)
AVStream *stream = ctx->streams[0];
AVCodecParameters *params = stream->codecpar;
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());
複製代碼