ffmpeg编程示例-音频重采样

该博客介绍了一个使用FFmpeg库进行音频重采样的C++程序,将源PCM文件从48000Hz采样率、float格式转换为44100Hz采样率、s16格式。程序首先创建重采样上下文,然后读取源文件,通过swr_convert函数进行转换,并将结果写入新的PCM文件。最后,程序建议使用ffplay播放转换后的文件。
摘要由CSDN通过智能技术生成

提供一个可以运行的ffmpeg工程:https://gitee.com/qiuguolu1108/ffmpeg-study

ffmpeg -i Jasmine.mp4 -vn -ar 48000 -ac 2 -f f32le Jasmine.pcm

进入player目录,使用上述命令生成待处理的pcm文件。源pcm文件的采样率为48000,采样格式为float,经过重采样后,pcm文件的采样率为44100,采样格式为s16。

#include "spdlog/spdlog.h"

extern "C" {
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "libswresample/swresample.h"
}

char error_buf[1024] = {0};
char* av_get_err(int err_num) {
  av_strerror(err_num, error_buf, sizeof(error_buf));
  return error_buf;
}

int get_format_from_sample_fmt(const char** fmt,
                               enum AVSampleFormat sample_fmt) {
  struct sample_fmt_entry {
    enum AVSampleFormat sample_fmt;
    const char *fmt_be, *fmt_le;
  } sample_fmt_entries[] = {
      {AV_SAMPLE_FMT_U8, "u8", "u8"},
      {AV_SAMPLE_FMT_S16, "s16be", "s16le"},
      {AV_SAMPLE_FMT_S32, "s32be", "s32le"},
      {AV_SAMPLE_FMT_FLT, "f32be", "f32le"},
      {AV_SAMPLE_FMT_DBL, "f64be", "f64le"},
  };

  *fmt = NULL;

  for (int i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) {
    struct sample_fmt_entry* entry = &sample_fmt_entries[i];
    if (sample_fmt == entry->sample_fmt) {
      *fmt = AV_NE(entry->fmt_be, entry->fmt_le);
      return 0;
    }
  }

  SPDLOG_ERROR("Sample format {} not supported as output format.",
               av_get_sample_fmt_name(sample_fmt));
  return AVERROR(EINVAL);
}

int main() {
  const char* src_file_name = "src.pcm";
  FILE* src_fd = fopen(src_file_name, "rb");
  if (!src_fd) {
    SPDLOG_ERROR("Could not open {}", src_file_name);
  }

  const char* dst_file_name = "dst.pcm";
  FILE* dst_fd = fopen(dst_file_name, "wb");
  if (!dst_fd) {
    SPDLOG_ERROR("Could not open {}", dst_file_name);
  }

  //输入参数
  int64_t src_ch_layout = AV_CH_LAYOUT_STEREO;             //声道布局
  int src_rate = 48000;                                    //采样率
  enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_FLT;  //采样格式
  int src_nb_channels = 0;                                 //声道数
  uint8_t** src_data = NULL;                               //音频数据
  int src_linesize;           //每个line中的数据大小
  int src_nb_samples = 1024;  //一帧数据的采样点数

  //输出参数
  int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO;
  int dst_rate = 44100;
  enum AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;
  int dst_nb_channels = 0;
  uint8_t** dst_data = NULL;
  int dst_linesize;
  int dst_nb_samples;
  int max_dst_nb_samples;

#if 1
  // 1、2、创建重采样实例,并设置参数,NULL表示内部申请SwrContext。
  struct SwrContext* swr_ctx =
      swr_alloc_set_opts(NULL, dst_ch_layout, dst_sample_fmt, dst_rate,
                         src_ch_layout, src_sample_fmt, src_rate, 0, NULL);
#else
  // 1、创建重采样实例
  struct SwrContext* swr_ctx = swr_alloc();
  if (!swr_ctx) {
    SPDLOG_ERROR("Could not allocate resampler context");
  }

  // 2、设置重采样参数
  //输入参数
  av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0);
  av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, 0);
  av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, 0);

  //输出参数
  av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, 0);
  av_opt_set_int(swr_ctx, "out_sample_rate", dst_rate, 0);
  av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0);
#endif

  // 3、初始化重采样
  int ret = swr_init(swr_ctx);
  if (ret < 0) {
    SPDLOG_ERROR("Failed to initialize the resampling context");
  }

  //通过声道布局,计算声道数。
  src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);

  //根据音频参数,申请空间,并计算linesize大小。
  ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize,
                                           src_nb_channels, src_nb_samples,
                                           src_sample_fmt, 0);
  if (ret < 0) {
    SPDLOG_ERROR("Could not allocate source samples");
  }

  //计算转换后一帧音频的采样点数
  max_dst_nb_samples = dst_nb_samples =
      av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);

  //计算声道数
  dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);

  ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize,
                                           dst_nb_channels, dst_nb_samples,
                                           dst_sample_fmt, 0);
  if (ret < 0) {
    SPDLOG_ERROR("Could not allocate destination samples");
  }

  while (!feof(src_fd)) {
    //读取一帧音频数据
    fread(src_data[0], 1024 * 4 * 2, 1, src_fd);

    //计算swr中没有被取走的采样点数
    int64_t delay = swr_get_delay(swr_ctx, src_rate);

    //重新计算输出一帧的采样点数
    dst_nb_samples =
        av_rescale_rnd(delay + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
    if (dst_nb_samples > max_dst_nb_samples) {
      av_freep(&dst_data[0]);
      ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,
                             dst_nb_samples, dst_sample_fmt, 1);
      if (ret < 0) {
        break;
      }
      max_dst_nb_samples = dst_nb_samples;
    }

    // 4、重采样转换   返回采样点数
    ret = swr_convert(swr_ctx, dst_data, dst_nb_samples,
                      (const uint8_t**)src_data, src_nb_samples);
    if (ret < 0) {
      SPDLOG_ERROR("Error while converting");
    }

    //计算输出buffer的大小
    int dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,
                                                 ret, dst_sample_fmt, 1);
    if (dst_bufsize < 0) {
      SPDLOG_ERROR("Could not get sample buffer size");
    }

    SPDLOG_INFO("in: {}, out: {}", src_nb_samples, ret);
    fwrite(dst_data[0], 1, dst_bufsize, dst_fd);
  }

  ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, NULL, 0);
  if (ret < 0) {
    SPDLOG_ERROR("Error while converting");
  }

  int dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,
                                               ret, dst_sample_fmt, 1);
  if (dst_bufsize < 0) {
    SPDLOG_ERROR("Could not get sample buffer size");
  }

  SPDLOG_INFO("flush in: {}, out: {}", src_nb_samples, ret);
  fwrite(dst_data[0], 1, dst_bufsize, dst_fd);

  const char* fmt;
  ret = get_format_from_sample_fmt(&fmt, dst_sample_fmt);
  if (ret < 0) {
    SPDLOG_ERROR("Could not find sample format.");
  }

  SPDLOG_INFO(
      "Resampling succeeded. Play the output file with the command:\n ffplay "
      "-f "
      "{} -channel_layout {} -channels {} -ar {} {}",
      fmt, dst_ch_layout, dst_nb_channels, dst_rate, dst_file_name);

  fclose(dst_fd);

  av_freep(&src_data[0]);
  av_freep(&src_data);

  av_freep(&dst_data[0]);
  av_freep(&dst_data);

  swr_free(&swr_ctx);

  return 0;
}
...
[2021-09-14 21:17:22.776] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.776] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.776] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 940
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 941
[2021-09-14 21:17:22.777] [info] [main.cpp:165] in: 1024, out: 940
[2021-09-14 21:17:22.777] [info] [main.cpp:180] flush in: 1024, out: 17
[2021-09-14 21:17:22.777] [info] [main.cpp:193] Resampling succeeded. Play the output file with the command:
 ffplay -f s16le -channel_layout 3 -channels 2 -ar 44100 dst.pcm

使用如下命令播放重采样后的音频文件:

ffplay -f s16le -channel_layout 3 -channels 2 -ar 44100 dst.pcm
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值