FFmpeg 混音学习【二】FFmpeg aac音频多路混流普适情况filter初始化

FFmpeg学习记录 专栏收录该内容
4 篇文章 1 订阅

前言

接上一篇博客,上一篇中介绍了单路和双路混流,要是输入输出流多了像之前那样写就会很麻烦,于是考虑普适的情况,输入一个n初始化好对应的filter。

具体实现

定义要用到的结构体和类(里面一些参数的含义可以看我上一篇博客)

  • 输入流编码信息结构体:
struct codec_info {
  int sample_rate;

  int channels;

  int64_t channel_layout;

  enum AVSampleFormat sample_fmt;

  AVRational time_base;

  codec_info() {  //构造函数,根据输入可自己定义,我这里默认给了一些数据
    sample_rate = 48000;
    channels = 2;
    channel_layout = av_get_default_channel_layout(channels);
    sample_fmt = AV_SAMPLE_FMT_FLTP;
    time_base = {1, sample_rate};
  }
};
  • 一个输入流的filter信息结构体:
struct src_filter {
    char args[512];   

    std::string pad_name;

    const AVFilter *abuffersrc;

    AVFilterContext *buffersrc_ctx;

    codec_info src_codec_info;

    AVFilterInOut *outputs;

    src_filter::src_filter() {}

    void src_filter::init(int n) { //根据序号初始化pad_name、abuffersrc和outputs
      abuffersrc = avfilter_get_by_name("abuffer");
      outputs = avfilter_inout_alloc();
      if (!outputs || !abuffersrc) {
        std::cout << "init filter input output error!!!" << std::endl;
      }
      std::string p = "in" + std::to_string(n);
      pad_name = p;
    }
  };
  • 整个混流器filter对应的类:
class filter_info {
   public:
    std::string filter_desc = "[in0][in1]amix=inputs=2[out]";
    //"aresample=48000,aformat=sample_fmts=fltp:channel_layouts=stereo";

    std::map<int32_t, std::shared_ptr<src_filter>> src_filter_map; //保存所有输入流的filter信息

    AVFilterContext *buffersink_ctx;

    AVFilterGraph *filter_graph;

    filter_info::filter_info() { }

    void filter_info::init(int n) { //根据输入流个数初始化filter_desc和src_filter
      filter_desc = gen_filter_desc(n);
      std::cout << "init filter_desc: " << filter_desc << std::endl;
      int i = 0;
      for (i = 0; i < n; i++) {
        std::shared_ptr<src_filter> filter_ptr(new src_filter);
        filter_ptr->init(i);
        src_filter_map.insert(std::make_pair((int32_t)i, std::move(filter_ptr)));
      }
    }
  };
  • 生成filter_descr的函数:
std::string Mixer::gen_filter_desc(int n) {
  std::string prefix = "";
  for (int i = 0; i < n; i++) {
    prefix = prefix + "[in" + std::to_string(i) + "]";
  }

  prefix = prefix + "amix=inputs=" + std::to_string(n) + "[out]";
  return prefix;
}

初始化混流器

这部分是核心代码,原理也和上一篇博客一样,实际实现有些许差别,应该看一下就懂了。

int Mixer::init_filters(int n) {
  filter.init(n);

  int ret = 0;

  const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");

  AVFilterInOut *inputs = avfilter_inout_alloc();

  static const enum AVSampleFormat out_sample_fmts[] = { out_codec_info.sample_fmt, (enum AVSampleFormat) - 1};

  static const int64_t out_channel_layouts[] = {out_codec_info.channel_layout, -1};

  static const int out_sample_rates[] = {out_codec_info.sample_rate, -1};
  const AVFilterLink *outlink;
  
  filter.filter_graph = avfilter_graph_alloc();
  if (!inputs || !filter.filter_graph) {
    ret = AVERROR(ENOMEM);
    E_LOG("init filter input output error");
    return -1;
  }

  int i = 0;
  for (i = 0; i < n; i++) {
    auto iter = filter.src_filter_map.find(i);
    if (iter != filter.src_filter_map.end()) {

      std::cout << "sample_fmt: "
                << av_get_sample_fmt_name(iter->second->src_codec_info.sample_fmt)
                << " channel_layout: " << iter->second->src_codec_info.channel_layout
                << " sample_rates: " << iter->second->src_codec_info.sample_rate << std::endl;

      AVRational time_base = iter->second->src_codec_info.time_base;

      snprintf(iter->second->args, sizeof(iter->second->args),
               "time_base=%d/"
               "%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
               time_base.num, time_base.den,
               iter->second->src_codec_info.sample_rate,
               av_get_sample_fmt_name(iter->second->src_codec_info.sample_fmt),
               iter->second->src_codec_info.channel_layout);
      ret = avfilter_graph_create_filter(
          &(iter->second->buffersrc_ctx), iter->second->abuffersrc,
                                         iter->second->pad_name.c_str(), iter->second->args, NULL,
                                         filter.filter_graph);
      if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer source1\n");
        return ret;
      }
    } else {
      E_LOG("Can't find buffersrc, index: {}", i);
    }
  }


  /* buffer audio sink: to terminate the filter chain. */
  ret = avfilter_graph_create_filter(&filter.buffersink_ctx, abuffersink, "out",
                                     NULL, NULL, filter.filter_graph);
  if (ret < 0) {
    av_log(NULL, AV_LOG_ERROR, "Cannot create audio buffer sink\n");
    return ret;
  }

  ret = av_opt_set_int_list(filter.buffersink_ctx, "sample_fmts",
                            out_sample_fmts, -1, AV_OPT_SEARCH_CHILDREN);
  if (ret < 0) {
    av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n");
    return ret;
  }

  ret = av_opt_set_int_list(filter.buffersink_ctx, "channel_layouts",
                            out_channel_layouts, -1, AV_OPT_SEARCH_CHILDREN);
  if (ret < 0) {
    av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n");
    return ret;
  }

  ret = av_opt_set_int_list(filter.buffersink_ctx, "sample_rates",
                            out_sample_rates, -1, AV_OPT_SEARCH_CHILDREN);
  if (ret < 0) {
    av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n");
    return ret;
  }

  AVFilterInOut **filter_outputs;
  filter_outputs = new AVFilterInOut*[n];

  for (i = 0; i < n; i++) {
    auto iter = filter.src_filter_map.find(i);
    auto _iter = filter.src_filter_map.find(i + 1);
    const char *p;
    std::string pad = "in" + std::to_string(i);
    p = pad.c_str();
   
    if (iter != filter.src_filter_map.end() &&
        _iter != filter.src_filter_map.end()) {

      iter->second->outputs->name = av_strdup(iter->second->pad_name.c_str());
      iter->second->outputs->filter_ctx = iter->second->buffersrc_ctx;
      iter->second->outputs->pad_idx = 0;
      iter->second->outputs->next = _iter->second->outputs;
      filter_outputs[i] = iter->second->outputs;

    } else if (iter != filter.src_filter_map.end() &&
               _iter == filter.src_filter_map.end()) {

      iter->second->outputs->name = av_strdup(iter->second->pad_name.c_str());
      iter->second->outputs->filter_ctx = iter->second->buffersrc_ctx;
      iter->second->outputs->pad_idx = 0;
      iter->second->outputs->next = NULL;
      filter_outputs[i] = iter->second->outputs;

    } else {
      E_LOG("Can't find buffersrc 222, index: {}", i);
    }
  }


  inputs->name = av_strdup("out");
  inputs->filter_ctx = filter.buffersink_ctx;
  inputs->pad_idx = 0;
  inputs->next = NULL;


  if ((ret = avfilter_graph_parse_ptr(filter.filter_graph, filter.filter_desc.c_str(),
                                      &inputs, filter_outputs, NULL)) < 0)  // filter_outputs
  {
    av_log(NULL, AV_LOG_ERROR, "parse ptr fail, ret: %d\n", ret);
    return ret;
  }

  if ((ret = avfilter_graph_config(filter.filter_graph, NULL)) < 0) {
    av_log(NULL, AV_LOG_ERROR, "config graph fail, ret: %d\n", ret);
    return ret;
  }

  /* Print summary of the sink buffer
   * Note: args buffer is reused to store channel layout string */
  //outlink = filter.buffersink_ctx->inputs[0];
  //av_get_channel_layout_string(filter.src_filter_map[1]->args,
  //                             sizeof(filter.src_filter_map[1]->args), -1,
  //                             outlink->channel_layout);
  //av_log(NULL, AV_LOG_INFO, "Output: srate:%dHz fmt:%s chlayout:%s\n",
  //       (int)outlink->sample_rate,
  //       (char *)av_x_if_null(
  //           av_get_sample_fmt_name((enum AVSampleFormat)outlink->format), "?"),
  //       filter.src_filter_map[1]->args);


  avfilter_inout_free(&inputs);
  avfilter_inout_free(filter_outputs);

  char *temp = avfilter_graph_dump(filter.filter_graph, NULL);
  printf("%s\n", temp);

  return ret;
}

向buffer里放入和取出数据

  • 放数据:遍历一下src_filter_map就可以了,这里以第一个为例。
    /* push the audio data from decoded frame into the filtergraph */
    auto iter = filter.src_filter_map.find(0); 
    if (av_buffersrc_add_frame_flags(iter->second->buffersrc_ctx, frame, 0) <
        0) {
      av_log(NULL, AV_LOG_ERROR,
             "Error while feeding the audio filtergraph1\n");
      break;
    }
  • 取数据:几乎没有区别。
    /* pull filtered audio from the filtergraph */
    while (1) {
      ret = av_buffersink_get_frame(filter.buffersink_ctx, filt_frame);
      if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break;
      if (ret < 0) {
        E_LOG("Failed to av_buffersink_get_frame_flags");
        return -1;
      }
      if (filt_frame->data[0] != NULL) {
        encode_write_frame(filt_frame, stream_index);
      } else {
        W_LOG("No data");
      }
      av_frame_unref(filt_frame);
    }

转载请注明本文url,引用请注明出处,谢谢支持!!!需要完整代码后续有空时会贴出。

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值