音视频过滤器实战--音频混音

1、添加并初始化音频输入、输出流

2、打开输入、输出音频文件

3、添加音频帧数据,然后循环获取输出的音频帧,将音频数据写文件保存

4、代码实例

audiomixer.h

#ifndef AUDIOMIXER_H
#define AUDIOMIXER_H

#include <map>
#include <mutex>
#include <cstdio>
#include <cstdint>
#include <string>
#include <memory>

extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
}

class AudioMixer
{
public:
    AudioMixer();
    virtual ~AudioMixer();

    int addAudioInput(uint32_t index, uint32_t samplerate, uint32_t channels, uint32_t bitsPerSample, AVSampleFormat format);
    int addAudioOutput(const uint32_t samplerate, const uint32_t channels,
                       const uint32_t bitsPerSample, const AVSampleFormat format);

    int init(const  char *duration = "longest");
    int exit();

    int addFrame(uint32_t index, uint8_t *inBuf, uint32_t size);
    int getFrame(uint8_t *outBuf, uint32_t maxOutBufSize);

private:

    struct AudioInfo
    {
        AudioInfo()
        {
            filterCtx = nullptr;
        }

        uint32_t samplerate;
        uint32_t channels;
        uint32_t bitsPerSample;
        AVSampleFormat format;
        std::string name;

        AVFilterContext *filterCtx;
    };

    bool initialized_ = false;
    std::mutex mutex_;
    std::map<uint32_t, AudioInfo> audio_input_info_;
    std::shared_ptr<AudioInfo> audio_output_info_;
    std::shared_ptr<AudioInfo> audio_mix_info_;
    std::shared_ptr<AudioInfo> audio_sink_info_;

    AVFilterGraph *filter_graph_ = nullptr;
};
#endif // AUDIOMIXER_H

audiomixer.cpp

#include "audiomixer.h"

AudioMixer::AudioMixer()
    : initialized_(false)
    , filter_graph_(nullptr)
    , audio_output_info_(nullptr)
{

    audio_mix_info_.reset(new AudioInfo);
    audio_mix_info_->name = "amix";     // 混音用的

    audio_sink_info_.reset(new AudioInfo);
    audio_sink_info_->name = "sink";    // 输出
}

AudioMixer::~AudioMixer()
{
    if(initialized_) {
        exit();
    }
}

int AudioMixer::addAudioInput(uint32_t index, uint32_t samplerate, uint32_t channels,
                              uint32_t bitsPerSample, AVSampleFormat format)
{
    std::lock_guard<std::mutex> locker(mutex_);

    if (initialized_)
    {
        return -1;
    }

    if (audio_input_info_.find(index) != audio_input_info_.end())
    {
        return -1;      // 已经存在则返回-1
    }

    // 初始化一个input 可以有多个输入
    auto& filterInfo = audio_input_info_[index];
    // 初始化音频相关的参数
    filterInfo.samplerate = samplerate;
    filterInfo.channels = channels;
    filterInfo.bitsPerSample = bitsPerSample;
    filterInfo.format = format;
    filterInfo.name = std::string("input") + std::to_string(index);

    return 0;
}

int AudioMixer::addAudioOutput(const uint32_t samplerate, const uint32_t channels,
                               const uint32_t bitsPerSample, const AVSampleFormat format)
{
    std::lock_guard<std::mutex> locker(mutex_);

    if (initialized_)
    {
        return -1;
    }
    // 初始化输出相关的参数       只有一个输出
    audio_output_info_.reset(new AudioInfo);
    audio_output_info_->samplerate = samplerate;
    audio_output_info_->channels = channels;
    audio_output_info_->bitsPerSample = bitsPerSample;
    audio_output_info_->format = format;
    audio_output_info_->name = "output";
    return 0;
}
/*
inputs
    The number of inputs. If unspecified, it defaults to 2.//输入的数量,如果没有指明,默认为2.
duration
    How to determine the end-of-stream.//决定了流的结束
    longest
    The duration of the longest input. (default)//最长输入的持续时间
    shortest
    The duration of the shortest input.//最短输入的持续时间
    first
    The duration of the first input.//第一个输入的持续时间 比如直播,mic为主的时候 first,
dropout_transition
    The transition time, in seconds, for volume renormalization when an input stream ends.
    The default value is 2 seconds.
*/
/**
 * @brief 初始化
 * @param duration longest最长输入时间,shortest最短,first第一个输入持续的时间
 * @return
 */
int AudioMixer::init(const  char *duration)
{
    std::lock_guard<std::mutex> locker(mutex_);

    if (initialized_)
    {
        return -1;
    }

    if (audio_input_info_.size() == 0)
    {
        return -1;
    }

    filter_graph_ = avfilter_graph_alloc(); // 创建avfilter_graph
    if (filter_graph_ == nullptr)
    {
        return -1;
    }

    char args[512] = {0};

    const AVFilter *amix = avfilter_get_by_name("amix");    // 混音
    audio_mix_info_->filterCtx = avfilter_graph_alloc_filter(filter_graph_, amix, "amix");
    /*inputs=输入流数量, duration=决定流的结束,
     * dropout_transition= 输入流结束时,容量重整时间,
     * (longest最长输入时间,shortest最短,first第一个输入持续的时间))*/
    snprintf(args, sizeof(args), "inputs=%d:duration=%s:dropout_transition=0",
             audio_input_info_.size(), duration);
    if (avfilter_init_str(audio_mix_info_->filterCtx, args) != 0)
    {
        printf("[AudioMixer] avfilter_init_str(amix) failed.\n");
        return -1;
    }

    const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
    audio_sink_info_->filterCtx = avfilter_graph_alloc_filter(filter_graph_, abuffersink, "sink");
    if (avfilter_init_str(audio_sink_info_->filterCtx, nullptr) != 0)
    {
        printf("[AudioMixer] avfilter_init_str(abuffersink) failed.\n");
        return -1;
    }

    for (auto& iter : audio_input_info_)
    {
        const AVFilter *abuffer = avfilter_get_by_name("abuffer");
        snprintf(args, sizeof(args),
                 "sample_rate=%d:sample_fmt=%s:channel_layout=0x%I64x",
                 iter.second.samplerate,
                 av_get_sample_fmt_name(iter.second.format),
                 av_get_default_channel_layout(iter.second.channels));
        printf("[AudioMixer] input(%d) args: %s\n", iter.first, args);

        iter.second.filterCtx = avfilter_graph_alloc_filter(filter_graph_, abuffer,
                                                            audio_output_info_->name.c_str());

        if (avfilter_init_str(iter.second.filterCtx, args) != 0)
        {
            printf("[AudioMixer] avfilter_init_str(abuffer) failed.\n");
            return -1;
        }
        // iter.first 是input index
        if (avfilter_link(iter.second.filterCtx, 0, audio_mix_info_->filterCtx, iter.first) != 0)
        {
            printf("[AudioMixer] avfilter_link(abuffer(%d), amix) failed.", iter.first);
            return -1;
        }
    }

    if (audio_output_info_ != nullptr)
    {
        const AVFilter *aformat = avfilter_get_by_name("aformat");
        snprintf(args, sizeof(args),
                 "sample_rates=%d:sample_fmts=%s:channel_layouts=0x%I64x",
                 audio_output_info_->samplerate,
                 av_get_sample_fmt_name(audio_output_info_->format),
                 av_get_default_channel_layout(audio_output_info_->channels));
        printf("[AudioMixer] output args: %s\n", args);
        audio_output_info_->filterCtx = avfilter_graph_alloc_filter(filter_graph_, aformat,
                                                                    "aformat");

        if (avfilter_init_str(audio_output_info_->filterCtx, args) != 0)
        {
            printf("[AudioMixer] avfilter_init_str(aformat) failed. %s\n", args);
            return -1;
        }

        if (avfilter_link(audio_mix_info_->filterCtx, 0, audio_output_info_->filterCtx, 0) != 0)
        {
            printf("[AudioMixer] avfilter_link(amix, aformat) failed.\n");
            return -1;
        }

        if (avfilter_link(audio_output_info_->filterCtx, 0, audio_sink_info_->filterCtx, 0) != 0)
        {
            printf("[AudioMixer] avfilter_link(aformat, abuffersink) failed.\n");
            return -1;
        }
    }

    if (avfilter_graph_config(filter_graph_, NULL) < 0)
    {
        printf("[AudioMixer] avfilter_graph_config() failed.\n");
        return -1;
    }

    initialized_ = true;
    return 0;
}

int AudioMixer::exit()
{
    std::lock_guard<std::mutex> locker(mutex_);

    if (initialized_)
    {
        for (auto iter : audio_input_info_)
        {
            if (iter.second.filterCtx != nullptr)
            {
                avfilter_free(iter.second.filterCtx);
            }
        }

        audio_input_info_.clear();

        if (audio_output_info_ && audio_output_info_->filterCtx)
        {
            avfilter_free(audio_output_info_->filterCtx);
            audio_output_info_->filterCtx = nullptr;
        }

        if (audio_mix_info_->filterCtx)
        {
            avfilter_free(audio_mix_info_->filterCtx);
            audio_mix_info_->filterCtx = nullptr;
        }

        if (audio_sink_info_->filterCtx)
        {
            avfilter_free(audio_sink_info_->filterCtx);
            audio_sink_info_->filterCtx = nullptr;
        }

        avfilter_graph_free(&filter_graph_);
        filter_graph_ = nullptr;
        initialized_ = false;
    }

    return 0;
}

int AudioMixer::addFrame(uint32_t index, uint8_t *inBuf, uint32_t size)
{
    std::lock_guard<std::mutex> locker(mutex_);

    if (!initialized_)
    {
        return -1;
    }

    auto iter = audio_input_info_.find(index);
    if (iter == audio_input_info_.end())
    {
        return -1;
    }

    if(inBuf && size > 0) {
        std::shared_ptr<AVFrame> avFrame(av_frame_alloc(), [](AVFrame *ptr) { av_frame_free(&ptr); });

        avFrame->sample_rate = iter->second.samplerate;
        avFrame->format = iter->second.format;
        avFrame->channel_layout = av_get_default_channel_layout(iter->second.channels);
        avFrame->nb_samples = size * 8 / iter->second.bitsPerSample / iter->second.channels;

        av_frame_get_buffer(avFrame.get(), 1);
        memcpy(avFrame->extended_data[0], inBuf, size);

        if (av_buffersrc_add_frame(iter->second.filterCtx, avFrame.get()) != 0)
        {
            return -1;
        }
    } else {
        if (av_buffersrc_add_frame(iter->second.filterCtx, NULL) != 0)
        {
            return -1;
        }
    }


    return 0;
}

int AudioMixer::getFrame(uint8_t *outBuf, uint32_t maxOutBufSize)
{
    std::lock_guard<std::mutex> locker(mutex_);

    if (!initialized_)
    {
        return -1;
    }

    std::shared_ptr<AVFrame> avFrame(av_frame_alloc(), [](AVFrame *ptr) { av_frame_free(&ptr); });

    int ret = av_buffersink_get_frame(audio_sink_info_->filterCtx, avFrame.get());

    if (ret < 0)
    {
        //        printf("ret = %d, %d\n", ret, AVERROR(EAGAIN));
        return -1;
    }

    int size = av_samples_get_buffer_size(NULL, avFrame->channels, avFrame->nb_samples, (AVSampleFormat)avFrame->format, 1);

    if (size > (int)maxOutBufSize)
    {
        return 0;
    }

    memcpy(outBuf, avFrame->extended_data[0], size);
    return size;
}

main.cpp

#include "audiomixer.h"

//ffmpeg -i buweishui_1m.mp3 -i huiguniang.mp3 -filter_complex amix=inputs=2:duration=longest:dropout_transition=3 out.mp3 -y

#define PCM1_FRAME_SIZE (4096*2)        // 需要和格式匹配 4*1024*2*
#define PCM2_FRAME_SIZE (4096)
#define PCM_OUT_FRAME_SIZE (40000)
int main(int argc, char **argv)
{
    AudioMixer amix;
    // 输入流
    amix.addAudioInput(0, 48000, 2, 32, AV_SAMPLE_FMT_FLT); // 48000_2_f32le.pcm
    amix.addAudioInput(1, 48000, 2, 16, AV_SAMPLE_FMT_S16); // 48000_2_s16le.pcm
    // 输出流
    amix.addAudioOutput(96000, 2, 16, AV_SAMPLE_FMT_S16);

    // init之前,要先添加输入源和输出源
    if (amix.init("longest") < 0) {
        return -1;
    }

    int len1 = 0, len2 = 0;
    uint8_t buf1[PCM1_FRAME_SIZE];
    uint8_t buf2[PCM2_FRAME_SIZE];
    FILE *file1 = fopen("48000_2_f32le.pcm", "rb");
    if(!file1) {
        printf("fopen 48000_2_f32le.pcm failed\n");
        return -1;
    }
    FILE *file2 = fopen("48000_2_s16le.pcm", "rb");
    if(!file2) {
        printf("fopen 48000_2_s16le.pcm failed\n");
        return -1;
    }
    FILE* file_out = fopen("output.pcm", "wb");
    if(!file_out) {
        printf("fopen output.pcm failed\n");
        return -1;
    }

    uint8_t out_buf[PCM_OUT_FRAME_SIZE];
    uint32_t out_size = 0;

    int file1_finish = 0;
    int file2_finish = 0;
    while (1) {
        len1 = fread(buf1, 1, PCM1_FRAME_SIZE, file1);
        len2 = fread(buf2, 1, PCM2_FRAME_SIZE, file2);
        if (len1 > 0 || len2 > 0) {
            if (len1 > 0) {
                if(amix.addFrame(0, buf1, len1) < 0) {
                    printf("amix.addFrame(0, buf1, len1) failed\n");
                    break;
                }
            } else {
                if(file1_finish == 0) {
                    file1_finish = 1;
                    if(amix.addFrame(0, NULL, 0) < 0) {     // 空包冲刷,人家才知道你某一路某一数据
                        printf("amix.addFrame(0, buf1, len1) failed\n");
                    }
                }
            }

            if (len2 > 0)
            {
                if(amix.addFrame(1, buf2, len2) < 0) {   // 空包冲刷,人家才知道你某一路某一数据
                    printf("amix.addFrame(1, buf2, len2) failed\n");
                    break;
                }
            } else {
                if(file2_finish == 0) {
                    file2_finish = 1;
                    if(amix.addFrame(1, NULL, 0) < 0) {
                        printf("amix.addFrame(1, buf2, len2) failed\n");
                    }
                }
            }

            int ret = 0; //amix.getFrame(out_buf, 10240);

            while ((ret = amix.getFrame(out_buf, 10240)) >=0) {
                out_size += ret;
                if(out_size % (1024*1024) ==0)
                    printf("mix audio: %d, out_size:%u\n", ret, out_size);
                fwrite(out_buf, 1, ret, file_out);
            }
        }
        else
        {
            printf("two file finish\n");
            break;
        }
    }
    printf("end, out_size:%u\n", out_size);
    amix.exit();
    if(file_out)
        fclose(file_out);
    if(file1)
        fclose(file1);
    if(file2)
        fclose(file2);
    getchar();
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值