使用ffmpeg的filter处理yuv数据包括split filter(分流)、crop filter(裁剪)、vflip filter(垂直向上的翻转)、overlay filter(合成)
#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()
{printf("Hello video mark!\n");int ret = 0;FILE* infile = NULL;const char* infileName = "768x320.yuv";fopen_s(&infile, infileName, "rb+");if(!infile){printf("fopen_s() infile failed!\n");return -1;}int in_width = 768;int in_height = 320;FILE* outfile = NULL;const char* outfileName = "out_mark.yuv";fopen_s(&outfile, outfileName, "wb");if(!outfile){printf("fopen_s() outfile failed!\n");return -1;}//注册初始化所有过滤器avfilter_register_all();//用于整个过滤流程的一个封装AVFilterGraph* filter_grah = avfilter_graph_alloc();if(!filter_grah){printf("avfilter_graph_alloc() failed!\n");return -1;}char args[512];sprintf(args,"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",in_width, in_height, AV_PIX_FMT_YUV420P,1, 25, 1, 1);//获取一个用于AVFilterGraph输入的过滤器AVFilter* buffersSrc = avfilter_get_by_name("buffer");AVFilterContext* bufferSrc_ctx;//将bufferSrc添加到AVFilterGraph中//args是用在bufferSrc的参数ret = avfilter_graph_create_filter(&bufferSrc_ctx, buffersSrc,"in", args, NULL, filter_grah);if(ret < 0){printf("avfilter_graph_create_filter() buffersSrc failed!\n");return -1;}AVBufferSinkParams* bufferSinkParams;AVFilterContext* bufferSink_ctx;//获取一个用于AVFilterGraph输出的过滤器AVFilter* bufferSink = avfilter_get_by_name("buffersink");enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};bufferSinkParams = av_buffersink_params_alloc();bufferSinkParams->pixel_fmts = pix_fmts;ret = avfilter_graph_create_filter(&bufferSink_ctx, bufferSink,"out", NULL, bufferSinkParams, filter_grah);if(ret < 0){printf("avfilter_graph_create_filter() bufferSink failed!\n");return -1;}//split filter(分流)AVFilter* splitFilter = avfilter_get_by_name("split");AVFilterContext* splitFilter_ctx;//outputs=2 分流2通道ret = avfilter_graph_create_filter(&splitFilter_ctx, splitFilter, "split","outputs=2", NULL, filter_grah);if(ret < 0){printf("avfilter_graph_create_filter() splitFilter failed!\n");return -1;}//crop filter(裁剪)AVFilter* cropFilter = avfilter_get_by_name("crop");AVFilterContext* cropFilter_ctx;ret = avfilter_graph_create_filter(&cropFilter_ctx, cropFilter, "crop","out_w=iw:out_h=ih/2:x=0:y=0", NULL, filter_grah);if(ret < 0){printf("avfilter_graph_create_filter() cropFilter failed!\n");return -1;}//vflip filter(垂直向上的翻转)AVFilter* vflipFilter = avfilter_get_by_name("vflip");AVFilterContext* vflipFilter_ctx;ret = avfilter_graph_create_filter(&vflipFilter_ctx, vflipFilter, "vflip",NULL, NULL, filter_grah);if(ret < 0){printf("avfilter_graph_create_filter() vflipFilter failed!\n");return -1;}//overlay filter(合成)AVFilter* overlayFilter = avfilter_get_by_name("overlay");AVFilterContext* overlayFilter_ctx;ret = avfilter_graph_create_filter(&overlayFilter_ctx, overlayFilter, "overlay","y=0:H/2", NULL, filter_grah);if(ret < 0){printf("avfilter_graph_create_filter() overlayFilter failed!\n");return -1;}//srcFilter -> splitFilter//srcpad、dstpad是一个索引,通道的索引ret = avfilter_link(bufferSrc_ctx, 0, splitFilter_ctx, 0);if(ret != 0){printf("avfilter_link() srcFilter -> splitFilter failed!\n");return -1;}//splitFilter[0] -> overlayfilter[0]ret = avfilter_link(splitFilter_ctx, 0, overlayFilter_ctx, 0);if(ret != 0){printf("avfilter_link() splitFilter[0] -> overlayfilter[0] failed!\n");return -1;}//splitFilter[1] -> cropFilterret = avfilter_link(splitFilter_ctx, 1, cropFilter_ctx, 0);if(ret != 0){printf("avfilter_link() splitFilter[1] -> cropFilter failed!\n");return -1;}
\//cropFilter -> vflipFilterret = avfilter_link(cropFilter_ctx, 0, vflipFilter_ctx, 0);if(ret != 0){printf("avfilter_link() cropFilter -> vflipFilter failed!\n");return -1;}//vflipFilter -> overlayfilter[1]ret = avfilter_link(vflipFilter_ctx, 0, overlayFilter_ctx, 1);if(ret != 0){printf("avfilter_link() vflipFilter -> overlayfilter[1] failed!\n");return -1;}//overlayfilter -> bufferSinkret = avfilter_link(overlayFilter_ctx, 0, bufferSink_ctx, 0);if(ret != 0){printf("avfilter_link() overlayfilter -> bufferSink failed!\n");return -1;}//确认所有过滤器的连接ret = avfilter_graph_config(filter_grah, NULL);if(ret < 0){printf("avfilter_graph_config() failed!\n");return -1;}//打印filtergraph的信息char* graph_str = avfilter_graph_dump(filter_grah, NULL);printf("\n%s\n", 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));//输出帧AVFrame* frame_out = av_frame_alloc();frame_in->width = in_width;frame_in->height = in_height;frame_in->format = AV_PIX_FMT_YUV420P;uint32_t frame_size = in_width * in_height * 3 / 2;while (1){//读取yuv数据if(fread(frame_buffer_in, 1, frame_size, infile) != frame_size){break;}av_image_fill_arrays(frame_in->data, frame_in->linesize, frame_buffer_in, AV_PIX_FMT_YUV420P, in_width, in_height, 1);//添加帧数据到过滤器if(av_buffersrc_add_frame(bufferSrc_ctx, frame_in) < 0){printf("av_buffersrc_add_frame() failed!\n");break;}//获取输出帧数据ret = av_buffersink_get_frame(bufferSink_ctx, frame_out);if(ret < 0){printf("av_buffersink_get_frame() failed!\n");break;}//输出文件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);}}av_frame_unref(frame_out);}fclose(infile);fclose(outfile);av_frame_free(&frame_in);av_frame_free(&frame_out);avfilter_graph_free(&filter_grah);printf("end video mark!\n");return 0;
}```