24.FFmpeg study notes - the original audio format converter with libavfilter API

This article will use libavfilter API to convert original audio format. A program input original audio file: pcm format, sample rate 48000,2 channel, AV_SAMPLE_FMT_FLT format. Output: PCM format, the sampling rate 44100,1 channel, AV_SAMPLE_FMT_S16P format.

Program flow is as follows:

Above all FFmpeg built-in filter, set the parameters, then calls can be. All filter sequentially connected to form a filter chain.

abuffer filter: filter the beginning end of the chain, is provided for the input format of audio data, including channels, sampling rate, and other storage format.

volume filter: used to set the volume. 

aformat filter: used to set the output format of the audio data, including channels, sampling rate, and other storage format.

abuffersink filter: filter chain terminating end for end of the entire filter chain.

#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#include "libavutil/channel_layout.h"
#include "libavutil/md5.h"
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"

#include "libavfilter/avfilter.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"



#define INPUT_SAMPLERATE     48000
#define INPUT_FORMAT         AV_SAMPLE_FMT_FLT
#define INPUT_CHANNEL_LAYOUT AV_CH_LAYOUT_STEREO

#define OUTPUT_SAMPLERATE     44100
#define OUTPUT_FORMAT         AV_SAMPLE_FMT_S16P
#define OUTPUT_CHANNEL_LAYOUT AV_CH_LAYOUT_MONO

#define VOLUME_VAL 0.90

#define FRAME_SIZE 1024


static int init_filter_graph(AVFilterGraph **graph, AVFilterContext **src,
                             AVFilterContext **sink)
{
    AVFilterGraph *filter_graph;
    AVFilterContext *abuffer_ctx;
    const AVFilter  *abuffer;
    AVFilterContext *volume_ctx;
    const AVFilter  *volume;
    AVFilterContext *aformat_ctx;
    const AVFilter  *aformat;
    AVFilterContext *abuffersink_ctx;
    const AVFilter  *abuffersink;
    
    AVDictionary *options_dict = NULL;
    char options_str[1024];
    char ch_layout[64];
    
    int err;
    
    /* 创建filtergraph,包含所有的filter */
    filter_graph = avfilter_graph_alloc();
    if (!filter_graph) {
        fprintf(stderr, "Unable to create filter graph.\n");
        return AVERROR(ENOMEM);
    }
    
    /*  创建abuffer filter,用于输入数据(链的起始点) */
    abuffer = avfilter_get_by_name("abuffer");
    if (!abuffer) {
        fprintf(stderr, "Could not find the abuffer filter.\n");
        return AVERROR_FILTER_NOT_FOUND;
    }
    
    abuffer_ctx = avfilter_graph_alloc_filter(filter_graph, abuffer, "src");
    if (!abuffer_ctx) {
        fprintf(stderr, "Could not allocate the abuffer instance.\n");
        return AVERROR(ENOMEM);
    }
    
    /* 设置abuffer filter的参数,也就是输入音频数据的参数 */
    av_get_channel_layout_string(ch_layout, sizeof(ch_layout), 0, INPUT_CHANNEL_LAYOUT);
    av_opt_set    (abuffer_ctx, "channel_layout", ch_layout,                            AV_OPT_SEARCH_CHILDREN);
    av_opt_set    (abuffer_ctx, "sample_fmt",     av_get_sample_fmt_name(INPUT_FORMAT), AV_OPT_SEARCH_CHILDREN);
    av_opt_set_q  (abuffer_ctx, "time_base",      (AVRational){ 1, INPUT_SAMPLERATE },  AV_OPT_SEARCH_CHILDREN);
    av_opt_set_int(abuffer_ctx, "sample_rate",    INPUT_SAMPLERATE,                     AV_OPT_SEARCH_CHILDREN);
    
    /* 用上面的参数初始化abuffer filter,因为上面设置了参数,所以第二个参数传NULL*/
    err = avfilter_init_str(abuffer_ctx, NULL);
    if (err < 0) {
        fprintf(stderr, "Could not initialize the abuffer filter.\n");
        return err;
    }
    
    /* 创建volume filter. */
    volume = avfilter_get_by_name("volume");
    if (!volume) {
        fprintf(stderr, "Could not find the volume filter.\n");
        return AVERROR_FILTER_NOT_FOUND;
    }
    
    volume_ctx = avfilter_graph_alloc_filter(filter_graph, volume, "volume");
    if (!volume_ctx) {
        fprintf(stderr, "Could not allocate the volume instance.\n");
        return AVERROR(ENOMEM);
    }
    
    /* 通过avfilter_init_dict设置volume filter */
    av_dict_set(&options_dict, "volume", AV_STRINGIFY(VOLUME_VAL), 0);
    err = avfilter_init_dict(volume_ctx, &options_dict);
    av_dict_free(&options_dict);
    if (err < 0) {
        fprintf(stderr, "Could not initialize the volume filter.\n");
        return err;
    }
    
    /* 创建aformat filter,用于设置想要的最终输出格式 */
    aformat = avfilter_get_by_name("aformat");
    if (!aformat) {
        fprintf(stderr, "Could not find the aformat filter.\n");
        return AVERROR_FILTER_NOT_FOUND;
    }
    
    aformat_ctx = avfilter_graph_alloc_filter(filter_graph, aformat, "aformat");
    if (!aformat_ctx) {
        fprintf(stderr, "Could not allocate the aformat instance.\n");
        return AVERROR(ENOMEM);
    }
    
    /* 另外一种方式设置aformat filter的参数(键值对且中间用冒号的形式)*/
    snprintf(options_str, sizeof(options_str),
             "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%"PRIx64,
             av_get_sample_fmt_name(OUTPUT_FORMAT), OUTPUT_SAMPLERATE,
             (uint64_t)OUTPUT_CHANNEL_LAYOUT);
    err = avfilter_init_str(aformat_ctx, options_str);
    if (err < 0) {
        av_log(NULL, AV_LOG_ERROR, "Could not initialize the aformat filter.\n");
        return err;
    }
    
    /* 最后创建abuffersink filter,用于获取graph输出的最终转换好的数据 */
    abuffersink = avfilter_get_by_name("abuffersink");
    if (!abuffersink) {
        fprintf(stderr, "Could not find the abuffersink filter.\n");
        return AVERROR_FILTER_NOT_FOUND;
    }
    
    abuffersink_ctx = avfilter_graph_alloc_filter(filter_graph, abuffersink, "sink");
    if (!abuffersink_ctx) {
        fprintf(stderr, "Could not allocate the abuffersink instance.\n");
        return AVERROR(ENOMEM);
    }
    
    /* 不需要设置参数 */
    err = avfilter_init_str(abuffersink_ctx, NULL);
    if (err < 0) {
        fprintf(stderr, "Could not initialize the abuffersink instance.\n");
        return err;
    }
    
    /* 连接filters */
    err = avfilter_link(abuffer_ctx, 0, volume_ctx, 0);
    if (err >= 0)
        err = avfilter_link(volume_ctx, 0, aformat_ctx, 0);
    if (err >= 0)
        err = avfilter_link(aformat_ctx, 0, abuffersink_ctx, 0);
    if (err < 0) {
        fprintf(stderr, "Error connecting filters\n");
        return err;
    }
    
    /* 配置graph. */
    err = avfilter_graph_config(filter_graph, NULL);
    if (err < 0) {
        av_log(NULL, AV_LOG_ERROR, "Error configuring the filter graph\n");
        return err;
    }
    
    *graph = filter_graph;
    *src   = abuffer_ctx;
    *sink  = abuffersink_ctx;
    
    return 0;
}


static void filter_audio(void)
{
    const char *in_filename = "/Users/zhw/Desktop/resource/sintel_f32le_2_48000.pcm";
    const char *out_filename = "/Users/zhw/Desktop/sintel_s16le_3_44100.pcm";
    FILE *in_file = NULL, *out_file = NULL;
    AVFilterGraph *graph;
    AVFilterContext *src, *sink;
    AVFrame *frame;
    int err;
    
    in_file = fopen(in_filename, "rb");
    if (!in_file) {
        printf("open file %s fail\n", in_filename);
        exit(1);
    }
    
    out_file = fopen(out_filename, "wb");
    if (!out_file) {
        printf("open file %s fail\n", out_filename);
        exit(1);
    }
    
    /* 创建frame,存储输入数据 */
    frame  = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Error allocating the frame\n");
        exit(1);
    }
    
    /* 设置filtergraph. */
    err = init_filter_graph(&graph, &src, &sink);
    if (err < 0) {
        fprintf(stderr, "Unable to init filter graph:");
        exit(1);
    }
    
    while (!feof(in_file)) {
        
//        static int frame_num = 0;
        
        /* Set up the frame properties and allocate the buffer for the data. */
        frame->sample_rate    = INPUT_SAMPLERATE;
        frame->format         = INPUT_FORMAT;
        frame->channel_layout = INPUT_CHANNEL_LAYOUT;
        frame->nb_samples     = FRAME_SIZE;
//        frame->pts            = frame_num * FRAME_SIZE;
        
        err = av_frame_get_buffer(frame, 0);
        if (err < 0)
            exit(1);
        
        int nb_channels = av_get_channel_layout_nb_channels(frame->channel_layout);
        int bytes_per_sample = av_get_bytes_per_sample(frame->format);
        //读取数据
        unsigned long bytes = fread(frame->data[0], 1, frame->nb_samples * nb_channels * bytes_per_sample, in_file);
        if (bytes < frame->nb_samples * nb_channels * bytes_per_sample) {
//            printf("*************");
        }
        
//        frame_num++;
        
        //添加frame到buffer,用于转换
        err = av_buffersrc_add_frame(src, frame);
        if (err < 0) {
            av_frame_unref(frame);
            fprintf(stderr, "Error submitting the frame to the filtergraph:");
            exit(1);
        }
        
        //获取输出数据
        while ((err = av_buffersink_get_frame(sink, frame)) >= 0) {
            
            if (av_sample_fmt_is_planar(frame->format)) {
                int data_size = av_get_bytes_per_sample(frame->format);
                for (int i = 0; i < frame->nb_samples; i++)
                    for (int ch = 0; ch < frame->channels; ch++)
                        fwrite(frame->data[ch] + data_size*i, 1, data_size, out_file);
            }else {
                fwrite(frame->extended_data[0], 1, frame->linesize[0], out_file);
            }
           
            
            av_frame_unref(frame);
        }
        
        if (err == AVERROR(EAGAIN)) {
            /* 需要更多的数据 */
            continue;
        } else if (err == AVERROR_EOF) {
            /* 到文件尾*/
            break;
        } else if (err < 0) {
            /* An error occurred. */
            fprintf(stderr, "Error filtering the data:");
            exit(1);
        }
        
    }
    
    
    avfilter_graph_free(&graph);
    av_frame_free(&frame);
    
    printf("finish\n");
    
}

 

Guess you like

Origin blog.csdn.net/whoyouare888/article/details/95305480