ffmpeg视频添加filter-yuv
直接上代码,具体问题请看注释。
//main.cpp
#define __STDC_CONSTANT_MACROS
#include <stdio.h>
#include <stdlib.h>
extern "C"
{
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/mathematics.h"
#include "libswresample/swresample.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/samplefmt.h"
#include "libavdevice/avdevice.h" //摄像头所用
#include "libavfilter/avfilter.h"
#include "libavutil/error.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
#include "libavutil/fifo.h"
#include "libavutil/audio_fifo.h" //这里是做分片时候重采样编码音频用的
#include "libavfilter/avfiltergraph.h"
#include "libavfilter/buffersink.h" //filter会用到
#include "libavfilter/buffersrc.h" //filter会用到
#include "inttypes.h"
#include "stdint.h"
};
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avdevice.lib")
#pragma comment(lib,"avfilter.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"postproc.lib")
#pragma comment(lib,"swresample.lib")
#pragma comment(lib,"swscale.lib")
#define WIDTH 352
#define HEIGHT 288
#define VIEO_PIX 0 //AV_PIX_FMT_YUV420P
#define TIMEBASE_NUM 1
#define TIMEBASE_DEN 25
/*
SAR_x DAR_x * height
-------- = --------------------
SAR_y DAR_y * width
*/
#define SAR_X 1
#define SAR_Y 1
//滤镜的内容;
//const char *filter_descr = "lutyuv='u=128:v=128'"; //变成灰色
//const char *filter_descr = "boxblur"; //模糊处理
//const char *filter_descr = "hflip";
//const char *filter_descr = "hue='h=60:s=-3'";
//const char *filter_descr = "crop=2/3*in_w:2/3*in_h";
//const char *filter_descr = "drawbox=x=100:y=100:w=100:h=100:color=pink@0.5";
const char *filter_descr = "drawtext=fontfile=../simhei.ttf:fontcolor=black:fontsize=50:text='zhuweigang'"; //字母ttf文件
/* other way:
scale=78:24 [scl]; [scl] transpose=cclock // assumes "[in]" and "[out]" to be input output pads respectively
*/
//图表FilterGraph
static AVFilterGraph *m_filter_graph = NULL;
//输入filter的context
static AVFilterContext *buffersrc_ctx = NULL;
//输出filter的context
static AVFilterContext *buffersink_ctx = NULL;
//初始化filter
static int init_filters(const char *filters_descr)
{
int ret = 0;
char video_info_args[512] = {0}; //视频的基本信息,包括宽高,timebase等填入的字符串数组
AVFilter *buffersrc = avfilter_get_by_name("buffer"); //输入filter
AVFilter *buffersink = avfilter_get_by_name("buffersink"); //输出filter
AVFilterInOut *outputs = avfilter_inout_alloc(); //输入filter的pin,名称out
AVFilterInOut *inputs = avfilter_inout_alloc(); //输出filter的pin,名称in
//初始化图表filter graph,filter分为grarph,输入pin和输出pin
m_filter_graph = avfilter_graph_alloc();
if (!outputs || !inputs || !m_filter_graph)
{
ret = AVERROR(ENOMEM);
goto end;
}
/* buffer video source: the decoded frames from the decoder will be inserted here. */
//宽,高,pix格式,timebase(codec层,如果是裸流可以填写帧率1:25),SAR(通过DAR和宽高算出的,不知道可以填写1:1)
_snprintf(video_info_args, sizeof(video_info_args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
WIDTH, HEIGHT, VIEO_PIX,
TIMEBASE_NUM, TIMEBASE_DEN,
SAR_X,SAR_Y);
//创建输入filter,并通过输入filter获取到该filter的context信息
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
video_info_args, NULL, m_filter_graph);
if (ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
goto end;
}
/* buffer video sink: to terminate the filter chain. */
//创建输出filter,并通过输出filter获取到该filter的context信息
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
NULL, NULL, m_filter_graph);
if (ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
goto end;
}
/*
* Set the endpoints for the filter graph. The m_filter_graph will
* be linked to the graph described by filters_descr.
*/
/*
* The buffer source output must be connected to the input pad of
* the first filter described by filters_descr; since the first
* filter input label is not specified, it is set to "in" by
* default.
*/
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
/*
* The buffer sink input must be connected to the output pad of
* the last filter described by filters_descr; since the last
* filter output label is not specified, it is set to "out" by
* default.
*/
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
//解析连接两个pin的命令
if ((ret = avfilter_graph_parse_ptr(m_filter_graph, filters_descr,
&inputs, &outputs, NULL)) < 0)
{
goto end;
}
//检查并连接filter
if ((ret = avfilter_graph_config(m_filter_graph, NULL)) < 0)
{
goto end;
}
end:
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
return ret;
}
int main(int argc, char **argv)
{
int ret;
AVFrame *frame_in = av_frame_alloc();
AVFrame *frame_out = av_frame_alloc();
unsigned char *frame_buffer_in = NULL;
unsigned char *frame_buffer_out = NULL;
int In_size = 0;
int Out_size = 0;
int Read_size = 0;
//Input YUV
FILE * fp_in = fopen("../352_288_yuv420p.yuv","rb+");
if(fp_in == NULL)
{
printf("Error open input file.\n");
goto end;
}
//Output YUV
FILE * fp_out = fopen("../output.yuv","wb+");
if(fp_out == NULL)
{
printf("Error open output file.\n");
goto end;
}
av_register_all();
avfilter_register_all();
if (!frame_in || !frame_out)
{
printf("Could not allocate frame");
goto end;
}
if ((ret = init_filters(filter_descr)) < 0)
{
printf("Could not init_filters");
goto end;
}
//分配一个AVFrame并设置默认值-输入
frame_in = av_frame_alloc();
if (frame_in == NULL)
{
printf("error av_frame_alloc frame_in\n");
goto end;
}
In_size = avpicture_get_size((AVPixelFormat)VIEO_PIX, WIDTH,HEIGHT);
frame_buffer_in =( uint8_t *)malloc(In_size * 3 * sizeof(char)); //最大分配的空间,能满足yuv的各种格式
avpicture_fill((AVPicture *)frame_in, (unsigned char *)frame_buffer_in, (AVPixelFormat)VIEO_PIX,WIDTH, HEIGHT); //内存关联
frame_in->format=VIEO_PIX;
frame_in->width=WIDTH;
frame_in->height=HEIGHT;
//分配一个AVFrame并设置默认值-输出
frame_out = av_frame_alloc();
if (frame_out == NULL)
{
printf("error av_frame_alloc frame_out\n");
goto end;
}
Out_size = avpicture_get_size((AVPixelFormat)VIEO_PIX, WIDTH,HEIGHT);
frame_buffer_out =( uint8_t *)malloc(Out_size * 3 * sizeof(char)); //最大分配的空间,能满足yuv的各种格式
avpicture_fill((AVPicture *)frame_out, (unsigned char *)frame_buffer_out, (AVPixelFormat)VIEO_PIX,WIDTH, HEIGHT); //内存关联
/* read all yuv data */
while(!feof(fp_in)) //如果未到文件的末尾
{
switch (VIEO_PIX)
{
case PIX_FMT_YUV420P : //和I420相同 ,yuv12的u,v分量位置相反
Read_size = fread(frame_buffer_in,sizeof(char),WIDTH * HEIGHT * 3/2,fp_in); //获取y,u,v偏移量
break;
case AV_PIX_FMT_NV12 :
Read_size = fread(frame_buffer_in,sizeof(char),WIDTH * HEIGHT * 3/2,fp_in); //获取y,u,v偏移量
break;
case AV_PIX_FMT_NV21 :
Read_size = fread(frame_buffer_in,sizeof(char),WIDTH * HEIGHT * 3/2,fp_in); //获取y,u,v偏移量
break;
case PIX_FMT_YUV422P:
Read_size = fread(frame_buffer_in,sizeof(char),WIDTH * HEIGHT * 2,fp_in); //获取y,u,v偏移量
break;
case AV_PIX_FMT_YUYV422 : //YUVY:(实际格式与YUY2相同)
Read_size = fread(frame_buffer_in,sizeof(char),WIDTH * HEIGHT * 2,fp_in); //获取y,u,v偏移量
break;
case AV_PIX_FMT_UYVY422 : //UYVY
Read_size = fread(frame_buffer_in,sizeof(char),WIDTH * HEIGHT * 2,fp_in); //获取y,u,v偏移量
break;
case PIX_FMT_YUV444P:
Read_size = fread(frame_buffer_in,sizeof(char),WIDTH * HEIGHT * 3,fp_in); //获取y,u,v偏移量
break;
default:
printf("文件格式暂时不支持\n");
goto end;
}
/* push the decoded frame into the filtergraph */
if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame_in, AV_BUFFERSRC_FLAG_KEEP_REF) < 0)
{
printf("Error while feeding the filtergraph\n");
break;
}
/* pull filtered frames from the filtergraph */
ret = av_buffersink_get_frame(buffersink_ctx, frame_out);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
printf("av_buffersink_get_frame error\n");
goto end;
}
int i = 0;
int j = 0;
int k = 0;
//output Y,U,V
if (VIEO_PIX == AV_PIX_FMT_YUV420P) //如果是yuv420p的
{
for(i = 0 ; i < HEIGHT ; i++)
{
memcpy(frame_buffer_out+WIDTH*i,
frame_out->data[0]+frame_out->linesize[0]*i,
WIDTH);
}
for(j = 0 ; j < HEIGHT/2 ; j++)
{
memcpy(frame_buffer_out+WIDTH*i+WIDTH/2*j,
frame_out->data[1]+frame_out->linesize[1]*j,
WIDTH/2);
}
for(k =0 ; k < HEIGHT/2 ; k++)
{
memcpy(frame_buffer_out+WIDTH*i+WIDTH/2*j+WIDTH/2*k,
frame_out->data[2]+frame_out->linesize[2]*k,
WIDTH/2);
}
}
else if (VIEO_PIX == AV_PIX_FMT_YUV422P)//如果是yuv422p的
{
for(i = 0 ; i < HEIGHT ; i++)
{
memcpy(frame_buffer_out+WIDTH*i,
frame_out->data[0]+frame_out->linesize[0]*i,
WIDTH);
}
for(j = 0 ; j < HEIGHT ; j++)
{
memcpy(frame_buffer_out+WIDTH*i+WIDTH/2*j,
frame_out->data[1]+frame_out->linesize[1]*j,
WIDTH/2);
}
for(k =0 ; k < HEIGHT ; k++)
{
memcpy(frame_buffer_out+WIDTH*i+WIDTH/2*j+WIDTH/2*k,
frame_out->data[2]+frame_out->linesize[2]*k,
WIDTH/2);
}
}
else
{
//可扩展
}
fwrite(frame_buffer_out, 1, Out_size, fp_out);
}
end:
fclose(fp_in);
fclose(fp_out);
avfilter_graph_free(&m_filter_graph);
av_frame_free(&frame_in);
av_frame_free(&frame_out);
if (frame_buffer_in)
{
free(frame_buffer_in);
frame_buffer_in = NULL;
}
if (frame_buffer_out)
{
free(frame_buffer_out);
frame_buffer_out = NULL;
}
printf("enter any key exit\n");
return getchar();
}
//程序运行效果
如有错误请指正:
交流请加QQ群:62054820
QQ:379969650.