音视频 Day 10、11 音频重采样

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 中的 suflebe 分别代表什么意思?

  • s:有符号(Signed)
  • u:无符号(Unsigned)
  • f:浮点型(Float)
  • le:小端模式(Little-Endian)
  • be:大端模式(Big-Endian)

3. 在 SDL 中描述采样格式时,比如 AUDIO_S16LSB、AUDIO_F32MSB 中的 LSBMSB 分别代表什么意思?

  • 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 的库时要注意什么?

  • -L-l 后面都不能有空格

image.png

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]); // 这两句代码,a 和 p 的使用几乎完全一致
printf("%d, %d, %d, \n", *p, *(p + 1), p[1]); // 这两句代码,a 和 p 的使用几乎完全一致
复制代码

image.png

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

image.png

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);
    /*
         inSampleRate     inSamples
         ------------- = -----------
         outSampleRate    outSamples

         outSamples = outSampleRate * inSamples / inSampleRate
         */
    
    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;
    }
    
    // 读取文件数据
    // intData[0] == *inData
    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;
        }
        
        // 将转换后的数据写入到输出文件中
        // outData[0] == *outData
        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);

//    void *ptr = malloc(100);
//    freep(&ptr);
//    free(ptr);
//    ptr = nullptr;
}

复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享