「FFmpeg filter」 音频过滤器amix简化版(处理PCM文件)

FFmpeg版本4.2.5

流程图

关键函数

  • avfilter_graph_alloc():分配过滤器图
  • avfilter_graph_parse_ptr():将由字符串描述的图形添加到图形中。在图形过滤器描述中,如果第一个过滤器的输入标签没有指定,则假定为“in”;如果未指定最后一个过滤器的输出标签,则假定为“out”。
  • avfilter_graph_config():检查有效性并配置图中的所有链接和格式。
  • av_buffersrc_add_frame():将帧添加到缓冲区源。
  • av_buffersink_get_frame():从接收器中获取一个包含过滤数据的框架并将其放入框架中。
  • C++音视频开发学习资料:点击领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)

代码

#include #include extern "C"{#include #include #include #include #include #include }#define PCM1_FRAME_SIZE 4096  //(2*1024*2)#define SAMPLE_RATE 44100#define SAMPLE_FORMAT AV_SAMPLE_FMT_S16#define CHANNEL_LAYOUT AV_CH_LAYOUT_STEREO#define CHANNELS 2#define BIT_PER_SAMPLE 16typedef struct {    char *filename;    FILE *file;    int sample_rate;    AVSampleFormat sample_format;    int channels;    int channel_layout;    char channel_layout_str[20];    int bitPerSample;    AVFilterContext *filterContext;} AudioInfo;std::map audioInfos;std::shared_ptr outAudioInfo;AVFilterGraph *graph;int ret = 0;/** *  打开输入文件 * @param _audioInfo * @return 0 succeed */int openInput(AudioInfo *audioInfo) {    audioInfo->file = fopen(audioInfo->filename, "rb");    if (!audioInfo->file) {        fprintf(stderr, "%s open failed
", audioInfo->filename);        return -1;    }    return 0;}/** * 释放资源 */void exit() {    for (auto audioInfo: audioInfos) {        fclose(audioInfo.second.file);        avfilter_free(audioInfo.second.filterContext);    }    fclose(outAudioInfo->file);    avfilter_free(outAudioInfo->filterContext);    avfilter_graph_free(&graph);    exit(1);}int addFrame(AudioInfo *audioInfo, uint8_t *buffer, size_t size) {    if (buffer && size > 0) {        std::shared_ptr avFrame(av_frame_alloc(), [](AVFrame *ptr) { av_frame_free(&ptr); });        avFrame->sample_rate = audioInfo->sample_rate;        avFrame->format = audioInfo->sample_format;        avFrame->channels = audioInfo->channels;        avFrame->nb_samples = (int) size * 8 / audioInfo->bitPerSample / audioInfo->channels;        if (avFrame->nb_samples) {            if (av_frame_get_buffer(avFrame.get(), 0) < 0) {                fprintf(stderr, "av_frame_get_buffer failed
");                exit();            }        }        //todo 注意:这里要传extended_data[0],不然会把其他数据覆盖?        std::memcpy(avFrame->extended_data[0], buffer, size);        if ((ret = av_buffersrc_add_frame(audioInfo->filterContext, avFrame.get())) != 0) {            return -1;        }    } else {        if ((ret = av_buffersrc_add_frame(audioInfo->filterContext, nullptr)) != 0) {            return -1;        }    }    return 0;}/** * 获取过滤器处理后的帧 * @param outBuffer * @return */int getFrame(uint8_t *outBuffer) {    //这里利用智能指针释放AVFrame    std::shared_ptr avFrame(av_frame_alloc(), [](AVFrame *ptr) { av_frame_free(&ptr); });    ret = av_buffersink_get_frame(outAudioInfo->filterContext, avFrame.get());    if (ret < 0) {        return 0;    }    //写入前判断下frame buffer大小    int size = av_samples_get_buffer_size(nullptr, avFrame->channels, avFrame->nb_samples,                                          static_cast(avFrame->format), 1);    //todo? 这里10000随便给了,这里需要了解下这个判断的意义    if (size > 10000) {        return 0;    }    //注意:这里要传avFrame->extended_data[0]    std::memcpy(outBuffer, avFrame->extended_data[0], size);    return size;}/** * 初始化过滤器图 * @param duration * @return */int initFilter(char *duration) {    graph = avfilter_graph_alloc();    if (!graph) {        fprintf(stderr, "avfilter_graph_alloc failed
");        return -1;    }    AVFilterInOut *in = avfilter_inout_alloc();    AVFilterInOut *out = avfilter_inout_alloc();    char filters[512] = {0};    char sample_format[4];    /**     * 1、这里的Parsed_abuffer_0,Parsed_abuffer_1。。。是自动生成的,规则为[Parsed_过滤器名_索引],索引就是第几个过滤器     * 2、必须有abuffer和abuffersink(视频的话是buffer和buffersink)     */    snprintf(filters, sizeof(filters),             "abuffer=sample_rate=%d:channels=%d:channel_layout=%s:sample_fmt=%s[input1];"//Parsed_abuffer_0             "abuffer=sample_rate=%d:channels=%d:channel_layout=%s:sample_fmt=%s[input2];"//Parsed_abuffer_1             "[input1][input2]amix=inputs=2:duration=%s:dropout_transition=3[amix];"//Parsed_amix_2             "[amix]aformat=sample_rates=%d:sample_fmts=%s:channel_layouts=%s[aformat];"//Parsed_aformat_3             "[aformat]abuffersink",//Parsed_abuffersink_4             audioInfos[0].sample_rate,             audioInfos[0].channels,             audioInfos[0].channel_layout_str,             av_get_sample_fmt_string(sample_format, 4, audioInfos[0].sample_format),             audioInfos[1].sample_rate,             audioInfos[1].channels,             audioInfos[1].channel_layout_str,             av_get_sample_fmt_string(sample_format, 4, audioInfos[1].sample_format),             duration,             outAudioInfo->sample_rate,             av_get_sample_fmt_string(sample_format, 4, outAudioInfo->sample_format),             outAudioInfo->channel_layout_str    );    fprintf(stdout, "filters:%s
", filters);    //通过字符串的方式生成过滤器图    avfilter_graph_parse_ptr(graph, filters, &in, &out, nullptr);    if (avfilter_graph_config(graph, nullptr) < 0) {        fprintf(stderr, "avfilter_graph_alloc failed
");        return -1;    }    audioInfos[0].filterContext = avfilter_graph_get_filter(graph, "Parsed_abuffer_0");    if (!audioInfos[0].filterContext) {        fprintf(stderr, "Parsed_abuffer_1 not find!
");        return -1;    }    audioInfos[1].filterContext = avfilter_graph_get_filter(graph, "Parsed_abuffer_1");    if (!audioInfos[1].filterContext) {        fprintf(stderr, "Parsed_abuffer_2 not find!
");        return -1;    }    outAudioInfo->filterContext = avfilter_graph_get_filter(graph, "Parsed_abuffersink_4");    if (!outAudioInfo->filterContext) {        fprintf(stderr, "Parsed_abuffersink_4 not find!
");        return -1;    }    return 0;}int main(int argc, char **argv) {    if (argc < 4) {        fprintf(stderr, "args need pass 2 audio filename, and outfilename!
");        return 1;    }    uint8_t buffer[PCM1_FRAME_SIZE];    uint8_t outBuffer[PCM1_FRAME_SIZE];    audioInfos[0].filename = argv[1];    audioInfos[1].filename = argv[2];    for (auto &audioInfo: audioInfos) {        ret = openInput(&audioInfo.second);        if (ret != 0) {            fprintf(stderr, "%s openInput failed!
", audioInfo.second.filename);            exit();        }        audioInfo.second.sample_rate = SAMPLE_RATE;        audioInfo.second.channels = CHANNELS;        audioInfo.second.channel_layout = CHANNEL_LAYOUT;        av_get_channel_layout_string(audioInfo.second.channel_layout_str,                                     sizeof(audioInfo.second.channel_layout_str), 0,                                     audioInfo.second.channel_layout);        audioInfo.second.bitPerSample = BIT_PER_SAMPLE;        audioInfo.second.sample_format = SAMPLE_FORMAT;    }    outAudioInfo = std::make_shared();    outAudioInfo->filename = argv[3];    outAudioInfo->file = fopen(outAudioInfo->filename, "wb");    if (!outAudioInfo->file) {        fprintf(stderr, "%s open failed
", outAudioInfo->filename);        return -1;    }    //todo 这里采样率可以改    outAudioInfo->sample_rate = 96000;    outAudioInfo->channels = CHANNELS;    outAudioInfo->channel_layout = CHANNEL_LAYOUT;    av_get_channel_layout_string(outAudioInfo->channel_layout_str,                                 sizeof(outAudioInfo->channel_layout_str), 0,                                 outAudioInfo->channel_layout);    outAudioInfo->bitPerSample = BIT_PER_SAMPLE;    outAudioInfo->sample_format = SAMPLE_FORMAT;    char *duration = "shortest";    if (initFilter(duration) != 0) {        fprintf(stderr, "initFilter failed!
");        exit();    }    size_t len1 = 0, len2 = 0;    int size = 0;    while (true) {        len1 = fread(&buffer, 1, PCM1_FRAME_SIZE, audioInfos[0].file);        if (addFrame(&audioInfos[0], buffer, len1) != 0) {            fprintf(stderr, "%s addFrame failed!
", audioInfos[0].filename);            exit();        }        std::memset(&buffer, 0, sizeof(buffer));        len2 = fread(&buffer, 1, PCM1_FRAME_SIZE, audioInfos[1].file);        if (addFrame(&audioInfos[1], buffer, len2) != 0) {            fprintf(stderr, "%s addFrame failed!
", audioInfos[1].filename);            exit();        }        if ((size = getFrame(outBuffer)) > 0) {            fwrite(&outBuffer, 1, size, outAudioInfo->file);        }        if (len1 < 0 && len2 < 0) {            exit();            break;        }    }}
发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章