1. 如何使用命令行对 PCM 文件进行重采样?
ffmpeg -ar 44100 -ac 2 -f s16le -i 44100_s16le_2.pcm -ar 44100 -ac 1 -f f32le 44100_f32le_1.pcm
2. ffmepg在描述采样格式时,比如 s16le、f32le
中的 s
、u
、f
、le
、be
分别代表什么意思?
s
:有符号(Signed)
u
:无符号(Unsigned)
f
:浮点型(Float)
le
:小端模式(Little-Endian)
be
:大端模式(Big-Endian)
3. 在 SDL 中描述采样格式时,比如 AUDIO_S16LSB、AUDIO_F32MSB
中的 LSB
、MSB
分别代表什么意思?
LSB
:小端模式(Least Significant Bit/Byte,最低有效位/字节)
MSB
:大端模式(Most Significant Bit/Byte,最高有效位/字节)
4. 什么是音频重采样?为什么需要音频重采样?
- 音频重采样(Audio Resample):将音频 A 转成音频 B,并且音频 A、B 的参数(采样率、采样格式、声道数)并不完全相同。
- 有些音频编码器对输入的原始 PCM 数据是有特点参数要求的。
5. 简述音频代码重采样的主流程?
- in.pcm → 输入缓冲区 → 输出缓冲区 → out.pcm
6. 在.pro 文件中,LIBS
同时添加 FFmpeg 和 SDL 的库时要注意什么?

7. int a[] = {1, 2, 3, 4, 5}
中 a[0]
和 a[3]
,本质上是什么意思?
a[0]
等价于 *(a+0)
等价于 *a
值等于 1
a[3]
等价于 *(a+3)
值等于 4
8. 在 C 语言中,指针和数据是可以相互切换的。如何理解这句话?(非常重要)
int a[] = {1, 2, 3, 4, 5};
int *p = a;
printf("%d, %d, %d, \n", *a, *(a + 1), a[1]);
printf("%d, %d, %d, \n", *p, *(p + 1), p[1]);

9. 输入输出流的内存结构图(有助于代码理解)

10. 音频重采样关键代码
#include "ffmpegs.h"
#include <QFile>
#include <QDebug>
extern "C" {
#include <libswresample/swresample.h>
#include <libavutil/avutil.h>
}
#define ERROR_BUF(ret) \
char errbuf[1024]; \
av_strerror(ret, errbuf, sizeof (errbuf));
FFmpegs::FFmpegs()
{
}
void FFmpegs::resampleAudio(ResampleAudioSpec &in,
ResampleAudioSpec &out) {
resampleAudio(in.filename, in.sampleRate, in.sampleFmt, in.chLayout,
out.filename, out.sampleRate, out.sampleFmt, out.chLayout);
}
void FFmpegs::resampleAudio(const char *inFilename,
int inSampleRate,
AVSampleFormat inSampleFmt,
int inChLayout,
const char *outFilename,
int outSampleRate,
AVSampleFormat outSampleFmt,
int outChLayout) {
QFile inFile(inFilename);
QFile outFile(outFilename);
uint8_t **inData = nullptr;
int inLinesize = 0;
int inChs = av_get_channel_layout_nb_channels(inChLayout);
int inBytesPerSample = inChs * av_get_bytes_per_sample(inSampleFmt);
int inSamples = 1024;
int len = 0;
uint8_t **outData = nullptr;
int outLinesize = 0;
int outChs = av_get_channel_layout_nb_channels(outChLayout);
int outBytesPerSample = outChs * av_get_bytes_per_sample(outSampleFmt);
int outSamples = av_rescale_rnd(outSampleRate, inSamples, inSampleRate, AV_ROUND_UP);
qDebug() << "输入缓冲区" << inSampleRate << inSamples;
qDebug() << "输出缓冲区" << outSampleRate << outSamples;
int ret = 0;
SwrContext *ctx = swr_alloc_set_opts(nullptr,
outChLayout, outSampleFmt, outSampleRate,
inChLayout, inSampleFmt, inSampleRate, 0, nullptr);
if (!ctx) {
qDebug() << "swr_alloc_set_opts error";
goto end;
}
ret = swr_init(ctx);
if (ret < 0) {
ERROR_BUF(ret);
qDebug() << "swr_init error" << errbuf;
goto end;
}
ret = av_samples_alloc_array_and_samples(&inData,
&inLinesize,
inChs,
inSamples,
inSampleFmt,
1);
if (ret < 0) {
ERROR_BUF(ret);
qDebug() << "av_samples_alloc_array_and_samples error:" << errbuf;
goto end;
}
ret = av_samples_alloc_array_and_samples(&outData,
&outLinesize,
outChs,
outSamples,
outSampleFmt,
1);
if (ret < 0) {
ERROR_BUF(ret);
qDebug() << "av_samples_alloc_array_and_samples error:" << errbuf;
goto end;
}
if (!inFile.open(QFile::ReadOnly)) {
qDebug() << "file open error:" << inFilename;
goto end;
}
if (!outFile.open(QFile::WriteOnly)) {
qDebug() << "file open error:" << outFilename;
goto end;
}
while ((len = inFile.read((char *)inData[0], inLinesize)) > 0) {
inSamples = len / inBytesPerSample;
ret = swr_convert(ctx, outData, outSamples, (const uint8_t **)inData, inSamples);
if (ret < 0) {
ERROR_BUF(ret);
qDebug() << "swr_convert error:" << errbuf;
goto end;
}
outFile.write((char *)outData[0], ret * outBytesPerSample);
}
while ((ret = swr_convert(ctx, outData, outSamples, nullptr, 0)) > 0) {
outFile.write((char *)outData[0], ret * outBytesPerSample);
}
end:
inFile.close();
outFile.close();
if (inData) {
av_freep(&inData[0]);
}
av_freep(&inData);
if (outData) {
av_freep(&outData[0]);
}
av_freep(&outData);
swr_free(&ctx);
}