ffmpeg基于avfilter-avfilergraph之重采样

这是一个关于使用FFmpeg进行音频重采样的C语言实现,包括枚举类型定义、重采样句柄初始化、销毁、数据发送与接收函数。代码中包含了错误处理、日志打印功能,并提供了测试程序,用于读取输入PCM文件,经过重采样后写入输出PCM文件。
摘要由CSDN通过智能技术生成

对应ffmpeg的版本:

  configuration: --prefix=/home/zeng/share/ffmpeg/ffmpeg/build/ --enable-ffplay
  libavutil      57. 24.101 / 57. 24.101
  libavcodec     59. 25.100 / 59. 25.100
  libavformat    59. 20.101 / 59. 20.101
  libavdevice    59.  6.100 / 59.  6.100
  libavfilter     8. 29.100 /  8. 29.100
  libswscale      6.  6.100 /  6.  6.100
  libswresample   4.  6.100 /  4.  6.100

下面是头文件、接口封装及相关的测试程序

#ifndef __ANDROID_AMIXER_H__
#define __ANDROID_AMIXER_H__ 

typedef enum {
    LogAresample_None = 0,  //关闭日志输出
    LogAresample_Urgent,    //必须打的
    LogAresample_Fatal,     //致使级
    LogAresample_Error,     //错误级
    LogAresample_Warning,   //告警级
    LogAresample_Info,      //业务级
    LogAresample_Debug,     //调试级
    LogAresample_Trace,     //跟踪级
    LogAresample_Detail,    //详细级
    LogAresample_Cnt
} LogAresample;


typedef enum {
    Aresample_SAMPLE_FMT_NONE = -1,
    Aresample_SAMPLE_FMT_U8,          ///< unsigned 8 bits
    Aresample_SAMPLE_FMT_S16,         ///< signed 16 bits
    Aresample_SAMPLE_FMT_S32,         ///< signed 32 bits
    Aresample_SAMPLE_FMT_FLT,         ///< float
    Aresample_SAMPLE_FMT_DBL,         ///< double

    Aresample_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
    Aresample_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
    Aresample_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
    Aresample_SAMPLE_FMT_FLTP,        ///< float, planar
    Aresample_SAMPLE_FMT_DBLP,        ///< double, planar
    Aresample_SAMPLE_FMT_S64,         ///< signed 64 bits
    Aresample_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar
} AresampleSampleFormat;

typedef int32_t (*AresamplePrint)(void *, const char *);

//从句柄获取重采样后的一帧数据
int32_t recvAresampleFilter(void *oObj, void *data, int32_t *size);
//将一帧待重采样的数据添加到句柄
int32_t sendAresampleFilter(void *oObj, void *data, int32_t size);
//销毁重采样句柄
void destoryAresampleFilter(void *oObj);
//初始化重采样句柄
void *InitAresampleFilter(int32_t outFrameMs,  
        int32_t inRate, int32_t inChan, AresampleSampleFormat inFormat, 
        int32_t outRate, int32_t outChan, AresampleSampleFormat outFormat);
//设置重采样日志相关的内容
int32_t setAresampleLog(void *priv, AresamplePrint print);

#endif /*__ANDROID_AMIXER_H__*/
#include <unistd.h>
#include <stdint.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil/mem.h>
#include <libavutil/avstring.h>

#include "aresample.h"
/* #include <memwatch.h> */

/*************************************************/
/**************LogPrintf**************************/
/*************************************************/
static void *gPriv = NULL;
static AresamplePrint gPrint;
static LogAresample gLevel = LogAresample_Info;

static int AresampleLogPrintf(LogAresample level,
        const char *file, const char *func,
        int line, const char *format, ...) {
    char logBuf[1024];
    va_list args;
    int funcLine        = 0;

    if (level > gLevel) return -1;

    snprintf (logBuf, sizeof(logBuf), "[%s][%s][%d]", file, func, line);
    funcLine = strlen(logBuf);

    /*va_list*/
    va_start(args, format);
    vsnprintf(&logBuf[funcLine], sizeof(logBuf) - funcLine, format, args);
    va_end(args);
    /*va_list*/

    if (gPrint) {
        return gPrint(gPriv, logBuf);
    }

    return -1;
}

#define LogPrintf(level, ...)\
    AresampleLogPrintf(level, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__);

#ifndef DEBUG0
#define DEBUG0(...)
#endif
#ifndef DEBUG1
#define DEBUG1(...)     LogPrintf(LogAresample_Error, __VA_ARGS__);
#endif
#ifndef DEBUG2
#define DEBUG2(...)     LogPrintf(LogAresample_Debug, __VA_ARGS__);
#endif

#ifndef ERRP
#define ERRP(con, ret, flag, ...)               \
    if (con) {                              \
        DEBUG##flag(__VA_ARGS__)            \
        ret;                                \
    }
#endif

static enum AVSampleFormat getAresampleFormat(AresampleSampleFormat aformat) {
    enum AVSampleFormat format = AV_SAMPLE_FMT_NONE; 

    switch (aformat) {
        case Aresample_SAMPLE_FMT_U8:
            format = AV_SAMPLE_FMT_U8;
            break;
        case Aresample_SAMPLE_FMT_S16:
            format = AV_SAMPLE_FMT_S16;
            break;
        case Aresample_SAMPLE_FMT_S32:
            format = AV_SAMPLE_FMT_S32;
            break;
        case Aresample_SAMPLE_FMT_FLT:
            format = AV_SAMPLE_FMT_FLT;
            break;
        case Aresample_SAMPLE_FMT_DBL:
            format = AV_SAMPLE_FMT_DBL;
            break;
        case Aresample_SAMPLE_FMT_U8P:
            format = AV_SAMPLE_FMT_U8P;
            break;
        case Aresample_SAMPLE_FMT_S16P:
            format = AV_SAMPLE_FMT_S16P;
            break;
        case Aresample_SAMPLE_FMT_S32P:
            format = AV_SAMPLE_FMT_S32P;
            break;
        case Aresample_SAMPLE_FMT_FLTP:
            format = AV_SAMPLE_FMT_FLTP;
            break;
        case Aresample_SAMPLE_FMT_DBLP:
            format = AV_SAMPLE_FMT_DBLP;
            break;
        case Aresample_SAMPLE_FMT_S64:
            format = AV_SAMPLE_FMT_S64;
            break;
        case Aresample_SAMPLE_FMT_S64P:
            format = AV_SAMPLE_FMT_S64P;
            break;
        default:break;
    }

    return format;
}

int32_t setAresampleLog(void *priv, AresamplePrint print) {
    gPriv   = priv;
    gPrint  = print;
    return 0;
}

typedef struct {
    void *handle;
    void *sink;
    void *source;

    int32_t inRate;
    enum AVSampleFormat inFormat;
    AVChannelLayout inChLayout;
    int32_t  outRate;
    enum AVSampleFormat outFormat;
    AVChannelLayout outChLayout;
} AresampleFilter;

/* 
 * 参数1:输出帧长时长 
 * 参数2:输入采样率
 * 参数3:输入通道数
 * 参数4:输入采样格式
 * 参数5:输出采样率
 * 参数6:输出通道数
 * 参数7:输出采样格式
 */
void *InitAresampleFilter(int32_t outFrameMs, 
        int32_t inRate, int32_t inChan, AresampleSampleFormat inFormat, 
        int32_t outRate, int32_t outChan, AresampleSampleFormat outFormat)
{
    int32_t status                          = -1;
    enum AVSampleFormat iFormat             = AV_SAMPLE_FMT_NONE;
    enum AVSampleFormat oFormat             = AV_SAMPLE_FMT_NONE;
    AresampleFilter *aresample              = NULL;
    AVFilterGraph *graph                    = NULL;      
    const char *inName                      = NULL;
    const char *outName                     = NULL;
    AVFilterContext *buffersinkCtx          = NULL;
	AVFilterInOut *input                    = NULL;
    AVFilterInOut *output                   = NULL;
    const AVFilter *abuffersink             = NULL;
    AVFilterContext *buffersrcCtx           = NULL;
    const AVFilter *abuffersrc              = NULL;
    AVChannelLayout inChLayout;
    AVChannelLayout outChLayout;
    char args[512];
    char buf[64];

    ERRP(1 != inChan && 2 != inChan, goto ERR0, 1, 
            "set channes [%d] invalid, range to [1-2]\n", inChan);
    ERRP(1 != outChan && 2 != outChan, goto ERR0, 1, 
            "set channes [%d] invalid, range to [1-2]\n", outChan);
    ERRP(8000 != inRate 
            && 16000 != inRate 
            && 32000 != inRate 
            && 44100 != inRate 
            && 48000 != inRate 
            && 96000 != inRate, goto ERR0, 1, 
            "set inRate [%d] invalid, range to "
            "[8000-16000-32000-44100-48000-96000]\n", inRate);
    ERRP(8000 != outRate 
            && 16000 != outRate 
            && 32000 != outRate 
            && 44100 != outRate 
            && 48000 != outRate 
            && 96000 != outRate, goto ERR0, 1, 
            "set outRate [%d] invalid, range to "
            "[8000-16000-32000-44100-48000-96000]\n", outRate);

    abuffersrc = avfilter_get_by_name("abuffer");
    ERRP(abuffersrc == NULL, goto ERR0, 1, 
            "input get abuffer failure\n");

    abuffersink = avfilter_get_by_name("abuffersink");
    ERRP(abuffersink == NULL, goto ERR0, 1, "get abuffersin failure");

    iFormat = getAresampleFormat(inFormat);
    ERRP(AV_SAMPLE_FMT_NONE == iFormat, goto ERR0, 1, "get iFormat [%d] failure\n", inFormat);
    printf ("iFormat:%d\n", iFormat);

    oFormat = getAresampleFormat(outFormat);
    ERRP(AV_SAMPLE_FMT_NONE == oFormat, goto ERR0, 1, "get oFormat [%d] failure\n", outFormat);
    printf ("oFormat:%d\n", oFormat);

    av_channel_layout_default(&inChLayout, inChan);
    av_channel_layout_describe(&inChLayout, buf, sizeof(buf));
    snprintf (args, sizeof(args) - 1, 
            "sample_rate=%d:sample_fmt=%s:channel_layout=%s", 
            inRate, av_get_sample_fmt_name(iFormat), buf);
    printf ("args:%s\n", args);

    graph = avfilter_graph_alloc();
    ERRP(graph == NULL, goto ERR0, 1, "get graph instance failure\n");

    inName = av_strdup("in0");
    ERRP(inName == NULL, goto ERR1, 1, "inName strdup failure\n");

    LogPrintf(LogAresample_Info, "get in:%s\n", inName);
    status = avfilter_graph_create_filter(&buffersrcCtx, 
            abuffersrc, inName, args, NULL, graph);
    ERRP(status < 0, goto ERR1, 1, "create source filter %s failure\n", inName);

    outName = av_strdup("out0");
    ERRP(outName == NULL, goto ERR1, 1, "parse out failure\n");

    status = avfilter_graph_create_filter(&buffersinkCtx, 
            abuffersink, outName, NULL, NULL, graph);
    ERRP(status < 0, goto ERR1, 1, "create sink filter %s failrue\n", outName);

    status = av_opt_set_bin(buffersinkCtx, "sample_fmts",
            (uint8_t*)&oFormat, sizeof(oFormat),
            AV_OPT_SEARCH_CHILDREN);
    ERRP (status < 0, goto ERR1, 1, "Cannot set output sample oFormat\n");

    av_channel_layout_default(&outChLayout, outChan);
    av_channel_layout_describe(&outChLayout, buf, sizeof(buf));
    status = av_opt_set(buffersinkCtx, "ch_layouts", buf, AV_OPT_SEARCH_CHILDREN);
    ERRP (status < 0, goto ERR1, 1, "Cannot set output sample format\n");

    status = av_opt_set_bin(buffersinkCtx, "sample_rates",
            (uint8_t*)&outRate, sizeof(outRate), AV_OPT_SEARCH_CHILDREN);
    ERRP (status < 0, goto ERR1, 1, "Cannot set output sample rate\n");

    output               = avfilter_inout_alloc();
    ERRP(output == NULL, goto ERR1, 1, "alloc inout failrue\n");

    output->name         = (char *)inName;
    output->filter_ctx   = buffersrcCtx;
    output->pad_idx      = 0;
    output->next         = NULL;

    input               = avfilter_inout_alloc();
    ERRP(input == NULL, goto ERR1, 1, "alloc input inout failrue\n");
    input->name         = (char *)outName;
    input->filter_ctx   = buffersinkCtx;
    input->pad_idx      = 0;
    input->next         = NULL;

    //已经将所有权交给了output和input
    inName              = NULL;
    outName             = NULL;

    snprintf(args, sizeof(args), "[in0]aresample=%d[out0]", outRate);
    args[sizeof(args) - 1] = '\0';
    printf ("args:%s\n", args);

    status = avfilter_graph_parse_ptr(graph, args, &input, &output, NULL);
    ERRP(status < 0, goto ERR1, 1, "parse ptr(%s) failure\n", args);

    status = avfilter_graph_config(graph, NULL);
    ERRP(status < 0, goto ERR1, 1, "config ptr(%s) failure\n", args);

    aresample = (AresampleFilter *) av_mallocz (sizeof(*aresample));
    ERRP(aresample == NULL, goto ERR1, 1, "malloc AresampleFilter instance failure\n");

    aresample->handle       = graph;
    aresample->sink         = buffersinkCtx;

    aresample->inRate       = inRate;
    aresample->inChLayout   = inChLayout;
    aresample->inFormat     = iFormat;

    aresample->outRate      = outRate;
    aresample->outChLayout  = outChLayout;
    aresample->outFormat    = oFormat;

    aresample->source       = buffersrcCtx;

    av_buffersink_set_frame_size(buffersinkCtx, outFrameMs * outRate / 1000);

    LogPrintf(LogAresample_Info, "InitAresampleFilter suc\n");
    return aresample;
ERR1:
    if (input) avfilter_inout_free(&input);
    if (output) avfilter_inout_free(&output);
    if (outName) free((void *)outName);
    if (inName) free((void *)inName);
    if (graph) avfilter_graph_free(&graph);
ERR0:
    return NULL;
}

void destoryAresampleFilter(void *oObj) {
    AVFilterGraph *graph = NULL;
    AresampleFilter *aresample = (AresampleFilter *)oObj;

    if (aresample) {
        graph = (AVFilterGraph *)aresample->handle;
        if (graph) {
            avfilter_graph_free(&graph);
        }

        av_free(aresample);
    }
}

int32_t sendAresampleFilter(void *oObj, void *data, int32_t size) {
    int32_t status          = -1;
    AVFrame *frame          = NULL;
    AVFilterContext *inCtx  = NULL;
    AresampleFilter *aresample    = (AresampleFilter *)oObj;

    ERRP(oObj == NULL, return -1, 0);
    ERRP(data == NULL, return -1, 0);

    frame = av_frame_alloc();
    ERRP(frame == NULL, goto ERR0, 1, "av_frame_alloc failrue\n");

    if (aresample) {
        inCtx   = (AVFilterContext *)aresample->source;
        if (inCtx) {
            frame->sample_rate      = aresample->inRate;
            frame->ch_layout        = aresample->inChLayout;
            frame->format           = aresample->inFormat;
            frame->nb_samples       = size / (av_get_bytes_per_sample(aresample->inFormat) * aresample->inChLayout.nb_channels);
            status = av_frame_get_buffer(frame, 0);
            ERRP(status < 0, goto ERR1, 1, "av_frame_get_buffer failure\n");
            memcpy(frame->data[0], data, size);
            status = av_buffersrc_add_frame_flags(inCtx, frame, 0);
            ERRP(status < 0, goto ERR1, 1, "av_buffersrc_add_frame_flags %d", status);
        }
    }

ERR1:
    av_frame_free(&frame);
ERR0:
    return status;
}

/*
此处有关于Planar的新处理,如果想代码更通用,建议采用该接口
int32_t sendAresampleFilter(void *oObj, void *data[], int32_t size) {
    int32_t status          = -1;
    int32_t channels        = 0;
    int32_t isPlanar        = 0;
    AVFrame *frame          = NULL;
    AVFilterContext *inCtx  = NULL;
    AresampleFilter *aresample    = (AresampleFilter *)oObj;

    ERRP(oObj == NULL, return -1, 0);
    ERRP(data == NULL, return -1, 0);

    frame = av_frame_alloc();
    ERRP(frame == NULL, goto ERR0, 1, "av_frame_alloc failrue\n");

    if (aresample) {
        inCtx   = (AVFilterContext *)aresample->source;
        if (inCtx) {
            frame->sample_rate      = aresample->inRate;
            frame->ch_layout        = aresample->inChLayout;
            frame->format           = aresample->inFormat;
            frame->nb_samples       = size / (av_get_bytes_per_sample(aresample->inFormat) * aresample->inChLayout.nb_channels);
            printf ("frame->nb_samples:%d\n", frame->nb_samples);
            status = av_frame_get_buffer(frame, 0);
            ERRP(status < 0, goto ERR1, 1, "av_frame_get_buffer failure\n");
            isPlanar = av_sample_fmt_is_planar(aresample->inFormat);
            if (!isPlanar) {
                memcpy(frame->data[0], data[0], size);
            }
            else {
                for (channels = 0; channels < aresample->inChLayout.nb_channels; channels++) {
                    memcpy(frame->data[channels],
                            data[channels], size/aresample->inChLayout.nb_channels);
                }
            }
            status = av_buffersrc_add_frame_flags(inCtx, frame, 0);
            ERRP(status < 0, goto ERR1, 1, "av_buffersrc_add_frame_flags %d", status);
        }
    }

ERR1:
    av_frame_free(&frame);
ERR0:
    return status;
}
*/

int32_t recvAresampleFilter(void *oObj, void *data, int32_t *size) {
    int32_t status          = -1;
    AVFrame *frame          = NULL;
    AVFilterContext *outCtx = NULL;
    AresampleFilter *aresample    = (AresampleFilter *)oObj;

    ERRP(oObj == NULL, return -1, 0);
    ERRP(data == NULL, return -1, 0);

    frame = av_frame_alloc();
    ERRP(frame == NULL, goto ERR0, 1, "av_frame_alloc failrue\n");

    if (aresample) {
        outCtx   = (AVFilterContext *)aresample->sink;
        if (outCtx) {
            status = av_buffersink_get_frame_flags(outCtx, frame, 0);
            ERRP(status < 0, goto ERR1, 1, "av_buffersink_get_frame_flags:%d", status);

            *size = frame->nb_samples 
                * av_get_bytes_per_sample(frame->format) 
                * frame->ch_layout.nb_channels;
            memcpy(data, frame->data[0], *size);
        }
    }

ERR1:
    av_frame_free(&frame);
ERR0:
    return status;
}

#ifdef TEST_ARESAMPLE
int32_t print(void *priv, const char *strings) {
    printf ("%s", strings);
    return 0;
}

int main(int args, char *argv[]) {
    int32_t ret         = -1;
    void *pObj          = NULL;
    int32_t ibuffer[1920];
    int16_t obuffer[1920];

    if (args != 3) {
        printf ("args == 3 ==> ./aresample in.pcm out.pcm\n");
        return -1;
    }

    FILE *fpIn0 = fopen(argv[1], "r");
    FILE *fpOut = fopen(argv[2], "w+");

    setAresampleLog(NULL, print);

    pObj = InitAresampleFilter(10, 96000, 1, Aresample_SAMPLE_FMT_S32, 48000, 1, Aresample_SAMPLE_FMT_S16);
    if (pObj) {
        printf ("pObj:%p\n", pObj);
        while (1) {
            int size = fread(ibuffer, 1, sizeof(ibuffer), fpIn0);
            if (size != sizeof(ibuffer)) break;
            ret = sendAresampleFilter(pObj, ibuffer, sizeof(ibuffer));
            if (ret < 0) break;

            ret = recvAresampleFilter(pObj, obuffer, &size);
            if (ret < 0) break;


            fwrite(obuffer, 1, size, fpOut);
        }
    }

    fclose(fpIn0);
    fclose(fpOut);
    destoryAresampleFilter(pObj);

    return 0;
}
#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值