FFmepg--AVFilter过滤器使用以及yuv视频裁剪

文章目录

AVFilter 流程:

⾸先使⽤split滤波器将input流分成两路流(main和tmp),然后分别对两路流进⾏处理。对于tmp流,先经过crop滤波器进⾏裁剪处理,再经过flip滤波器进⾏垂直⽅向上的翻转操作,输出的结果命名为flip流。再将main流和flip流输⼊到overlay滤波器进⾏合成操作

input 为buffer源过滤波器,output为buffersink滤波器,图中每个节点都是⼀个AVFilterContext,每个连线就是AVFliterLink。所有这些信息都统⼀由AVFilterGraph来管理

api

  • 获取FFmpeg中定义的filter,调⽤该⽅法前需要先调⽤avfilter_register_all();进⾏滤波器注册
    AVFilter avfilter_get_by_name(const char name);
  • 往源滤波器buffer中输⼊待处理的数据
    int av_buffersrc_add_frame(AVFilterContext ctx, AVFrame frame);
  • 从⽬的滤波器buffersink中获取处理完的数据
    int av_buffersink_get_frame(AVFilterContext ctx, AVFrame frame);
  • 创建⼀个滤波器图filter graph
    AVFilterGraph *avfilter_graph_alloc(void);
  • 创建⼀个滤波器实例AVFilterContext,并添加到AVFilterGraph中
    int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt,
    const char name, const char args, void *opaque,
    AVFilterGraph *graph_ctx);
  • 连接两个滤波器节点
    int avfilter_link(AVFilterContext *src, unsigned srcpad,
    AVFilterContext *dst, unsigned dstpad);

核心代码

变量

-- AVFilterGraph-对filters系统的整体管理

cpp 复制代码
struct AVFilterGraph
{
AVFilterContext **filters;

-- AVFilter结构体

cpp 复制代码
AVFilter ff_vf_overlay = {
	.name = "overlay",
	.inputs = avfilter_vf_overlay_inputs,
	.outputs = avfilter_vf_overlay_outputs,
	// ..
 };

struct AVFilterContext
{
	const AVFilter *filter;
	char *name;
	AVFilterPad *input_pads;
	AVFilterLink **inputs;
	unsigned nb_inputs
	AVFilterPad *output_pads;
	AVFilterLink **outputs;
	unsigned nb_outputs;
	struct AVFilterGraph *graph; // 从属于哪个AVFilterGraph
}

-- AVFilterLink-定义两个filters之间的联接

cpp 复制代码
	struct AVFilterLink
	{
	AVFilterContext *src;
	AVFilterPad *srcpad;
	AVFilterContext *dst;
	AVFilterPad *dstpad;
	struct AVFilterGraph *graph;
	};

-- AVFilterPad-定义filter的输⼊/输出接⼝

cpp 复制代码
struct AVFilterPad
{
	const char *name;
	AVFrame *(*get_video_buffer)(AVFilterLink *link, int w, int h);
	AVFrame *(*get_audio_buffer)(AVFilterLink *link, int nb_samples);
	int (*filter_frame)(AVFilterLink *link, AVFrame *frame);
	AVFilterPad-定义filter的输⼊/输出接⼝
	int (*request_frame)(AVFilterLink *link);
};
yuv视频裁剪

对输入yuv文件,在中间处裁剪,留下上半部分,下半部分为上半部分的镜像

cpp 复制代码
#include <stdio.h>

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>


int main(int argc, char* argv)
{
    int ret = 0;

    // input yuv
    FILE* inFile = NULL;
    const char* inFileName = "768x320.yuv";
    fopen_s(&inFile, inFileName, "rb+");

    int in_width = 768;
    int in_height = 320;

    // output yuv
    FILE* outFile = NULL;
    const char* outFileName = "out_crop_vfilter.yuv";
    fopen_s(&outFile, outFileName, "wb");

    avfilter_register_all();

    AVFilterGraph* filter_graph = avfilter_graph_alloc();
    // source filter
    char args[512];
    AVFilter* bufferSrc = avfilter_get_by_name("buffer");   // AVFilterGraph的输入源
    AVFilterContext* bufferSrc_ctx;
    avfilter_graph_create_filter(&bufferSrc_ctx, bufferSrc, "in", args, NULL, filter_graph);
 

    // sink filter
    AVBufferSinkParams *bufferSink_params;
    AVFilterContext* bufferSink_ctx;
    AVFilter* bufferSink = avfilter_get_by_name("buffersink");
    enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
    bufferSink_params = av_buffersink_params_alloc();
    bufferSink_params->pixel_fmts = pix_fmts;
    ret = avfilter_graph_create_filter(&bufferSink_ctx, bufferSink, "out", NULL,
                                       bufferSink_params, filter_graph);

    // split filter
    AVFilter *splitFilter = avfilter_get_by_name("split");
    AVFilterContext *splitFilter_ctx;
    avfilter_graph_create_filter(&splitFilter_ctx, splitFilter, "split", "outputs=2",
                                       NULL, filter_graph);
 

    // crop filter
    AVFilter *cropFilter = avfilter_get_by_name("crop");
    AVFilterContext *cropFilter_ctx;
    revfilter_graph_create_filter(&cropFilter_ctx, cropFilter, "crop",
                                       "out_w=iw:out_h=ih/2:x=0:y=0", NULL, filter_graph);
    

    // vflip filter
    AVFilter *vflipFilter = avfilter_get_by_name("vflip");
    AVFilterContext *vflipFilter_ctx;
    avfilter_graph_create_filter(&vflipFilter_ctx, vflipFilter, "vflip", NULL, NULL, filter_graph);
   

    // overlay filter
    AVFilter *overlayFilter = avfilter_get_by_name("overlay");
    AVFilterContext *overlayFilter_ctx;
    avfilter_graph_create_filter(&overlayFilter_ctx, overlayFilter, "overlay",
                                       "y=0:H/2", NULL, filter_graph);

    avfilter_link(bufferSrc_ctx, 0, splitFilter_ctx, 0);
    // split filter's first pad to overlay filter's main pad
    avfilter_link(splitFilter_ctx, 0, overlayFilter_ctx, 0);
    
    // split filter's second pad to crop filter
    avfilter_link(splitFilter_ctx, 1, cropFilter_ctx, 0);
 
    // crop filter to vflip filter
    avfilter_link(cropFilter_ctx, 0, vflipFilter_ctx, 0);
    
    // vflip filter to overlay filter's second pad
    avfilter_link(vflipFilter_ctx, 0, overlayFilter_ctx, 1);
   
    // overlay filter to sink filter
    avfilter_link(overlayFilter_ctx, 0, bufferSink_ctx, 0);
    

    // check filter graph
    avfilter_graph_config(filter_graph, NULL);
    

    char *graph_str = avfilter_graph_dump(filter_graph, NULL);
    FILE* graphFile = NULL;
    fopen_s(&graphFile, "graphFile.txt", "w");  // 打印filtergraph的具体情况
    fprintf(graphFile, "%s", graph_str);
    av_free(graph_str);

    AVFrame *frame_in = av_frame_alloc();
    unsigned char *frame_buffer_in = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width, in_height, 1));
    av_image_fill_arrays(frame_in->data, frame_in->linesize, frame_buffer_in,
        AV_PIX_FMT_YUV420P, in_width, in_height, 1);

    AVFrame *frame_out = av_frame_alloc();
    unsigned char *frame_buffer_out = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, in_width, in_height, 1));
    av_image_fill_arrays(frame_out->data, frame_out->linesize, frame_buffer_out,
        AV_PIX_FMT_YUV420P, in_width, in_height, 1);

    frame_in->width = in_width;
    frame_in->height = in_height;
    frame_in->format = AV_PIX_FMT_YUV420P;
    uint32_t frame_count = 0;
    while (1) {
        // 读取yuv数据
        if (fread(frame_buffer_in, 1, in_width*in_height * 3 / 2, inFile) != in_width*in_height * 3 / 2) {
            break;
        }
        //input Y,U,V
        frame_in->data[0] = frame_buffer_in;
        frame_in->data[1] = frame_buffer_in + in_width*in_height;
        frame_in->data[2] = frame_buffer_in + in_width*in_height * 5 / 4;

        if (av_buffersrc_add_frame(bufferSrc_ctx, frame_in) < 0) {
            printf("Error while add frame.\n");
            break;
        }
        // filter内部自己处理
        /* pull filtered pictures from the filtergraph */
        ret = av_buffersink_get_frame(bufferSink_ctx, frame_out);
        if (ret < 0)
            break;

        //output Y,U,V
        if (frame_out->format == AV_PIX_FMT_YUV420P) {
            for (int i = 0; i < frame_out->height; i++) {
                fwrite(frame_out->data[0] + frame_out->linesize[0] * i, 1, frame_out->width, outFile);
            }
            for (int i = 0; i < frame_out->height / 2; i++) {
                fwrite(frame_out->data[1] + frame_out->linesize[1] * i, 1, frame_out->width / 2, outFile);
            }
            for (int i = 0; i < frame_out->height / 2; i++) {
                fwrite(frame_out->data[2] + frame_out->linesize[2] * i, 1, frame_out->width / 2, outFile);
            }
        }
        ++frame_count;
        if(frame_count % 25 == 0)
            printf("Process %d frame!\n",frame_count);
        av_frame_unref(frame_out);
    }

    fclose(inFile);
    fclose(outFile);

    av_frame_free(&frame_in);
    av_frame_free(&frame_out);
    avfilter_graph_free(&filter_graph); // 内部去释放AVFilterContext产生的内存
    return 0;
}
相关推荐
VOOHU_20182 小时前
VOOHU沃虎:音频变压器的主要作用是什么?什么情况下必须使用?
网络·物联网·音视频·电子元器件
APIshop3 小时前
小红书笔记视频详情接口深度解析:smallredbook.item_get_video_pro
数据库·笔记·音视频
AI服务老曹5 小时前
【架构深评】深度解析异构计算下的 AI 视频管理平台:从 GB28181 接入到 X86/ARM 容器化部署的全链路实战
人工智能·架构·音视频
YWamy6 小时前
视频会议如何重构智能硬件生态?适配难点与场景落地全解析
音视频
ai产品老杨6 小时前
【架构深析】打破硬件壁垒:支持 X86/ARM 异构计算与源码交付的 GB28181 视频 AI 平台设计实践
arm开发·架构·音视频
美狐美颜SDK开放平台7 小时前
直播App画面发灰、卡顿严重?美颜SDK接入与优化全解析
人工智能·音视频·美颜sdk·短视频美颜sdk
开开心心就好7 小时前
解决打印机共享难题的实用工具
人工智能·vscode·游戏·macos·音视频·语音识别·媒体
ai产品老杨8 小时前
深度解析:基于异构计算架构的 AI 视频中台(支持 GB28181、RTSP、Docker 部署与源码交付)
人工智能·架构·音视频
做萤石二次开发的哈哈9 小时前
萤石音视频 SDK 对比维度有哪些?
音视频
Cho1yon10 小时前
【第14期:多屏播放dvr视频和其他三方视频黑屏分析思路闪屏
车载系统·音视频