使用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] -> cropFilter
ret = avfilter_link(splitFilter_ctx, 1, cropFilter_ctx, 0);
if(ret != 0)
{
printf("avfilter_link() splitFilter[1] -> cropFilter failed!\n");
return -1;
}
\
//cropFilter -> vflipFilter
ret = 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 -> bufferSink
ret = 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;
}```