FFmpeg入门教程10.20:视频添加滤镜

系列索引:FFmpeg入门系列索引

上一篇:FFmpeg入门教程10.19:实现简单音视频同步和简单视频播放器

音视频编解码部分介绍完了,接下来简单介绍一下视频处理。

因为视频处理有专业的软件,本文就简单介绍一下FFmpeg支持的视频处理滤镜。FFmpeg的滤镜是在解码之后在YUV数据之上直接处理的。

那么,滤镜主要包括两部分:解码、滤镜。

先看一下流程:

flowchart TB

N --> F
F --读取下一帧--> L
subgraph filter
A[创建滤镜出入口] --> B[创建滤镜通道图]
B --> C[设置出入口参数]
C --> D[解析滤镜命令]
D --> E[滤镜通道图检查]
E --> F[添加滤镜]
direction TB
end

subgraph decode
direction TB
G[打开文件] --> H[查找流信息]
H --> I[查找视频流]
I --> J[查找解码器]
J --> K[打开解码器]
K --> L{读取帧}
L --No--> M[释放资源]
L --Yew--> N[解码帧]
end

解码视频

直接把FFmpeg入门教程10.07:软解并使用QtWidget播放视频(YUV420P->RGB32)代码拿过来就可以了。

初始化滤镜

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/*
* 开始初始化滤波器
* Filter的具体定义,只要是libavfilter中已注册的filter,
* 就可以直接通过查询filter名字的方法获得其具体定义,所谓
* 定义即filter的名称、功能描述、输入输出pad、相关回调函数等
*/
AVFilter *bufSrc = (AVFilter*)avfilter_get_by_name("buffer");/* 输入buffer filter */
AVFilter *bufSink = (AVFilter*)avfilter_get_by_name("buffersink");/* 输出buffer filter */
//AVFilterInOut对应buffer和buffersink这两个首尾端的filter的输入输出
AVFilterInOut *outFilter = avfilter_inout_alloc();
AVFilterInOut *inFilter = avfilter_inout_alloc();

enum AVPixelFormat pixel_fmts[]={AV_PIX_FMT_YUV420P,AV_PIX_FMT_NONE};

filterGraph = avfilter_graph_alloc();
if(!outFilter||!inFilter||!filterGraph){
ret = AVERROR(ENOMEM);
}

QString qargs=QString("video_size=%1x%2:pix_fmt=0:time_base=1/20")
.arg(videoWidth)
.arg(videoHeight);
char* args=qargs.toLocal8Bit().data();

//根据指定的Filter,这里就是buffer,构造对应的初始化参数args,二者结合即可创建Filter的示例,并放入filter_graph中
int ret = avfilter_graph_create_filter(&bufSrcCtx,
bufSrc,
"in",
args,NULL,
filterGraph);
if(ret<0){
printf("Cannot create buffer source.\n");
}

/* buffer video sink: to terminate the filter chain. */
ret = avfilter_graph_create_filter(&bufSinkCtx,
bufSink,
"out",
NULL,
NULL,
filterGraph);
if(ret<0){
printf("Cannot creat buffer sink.\n");
}

ret = av_opt_set_int_list(bufSinkCtx, "pix_fmts", pixel_fmts,
AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
}

/* Endpoints for the filter graph. */
outFilter->name = av_strdup("in");
outFilter->filter_ctx = bufSrcCtx;
outFilter->pad_idx =0;
outFilter->next = NULL;

inFilter->name = av_strdup("out");
inFilter->filter_ctx = bufSinkCtx;
inFilter->pad_idx = 0;
inFilter->next = NULL;

//filter_descr是一个filter命令,例如"overlay=iw:ih",该函数可以解析这个命令,
//然后自动完成FilterGraph中各个Filter之间的联接
ret = avfilter_graph_parse_ptr(filterGraph,
//filterDesc,
filterDescr.toStdString().data(),
&inFilter,
&outFilter,
NULL);
if(ret<0){
printf("Cannot parse filter desc.\n");
}

//检查当前所构造的FilterGraph的完整性与可用性
if(avfilter_graph_config(filterGraph,NULL)<0){
printf("Check config failed.\n");
}

filterDescr是有效的亮度对比度语句,如果此语句不变,那么只需要初始化一次,如果此语句变换(主要是参数),那么每次使用之前都需要初始化。

添加滤镜

原始数据帧经过解码后,在格式转换之前进行添加滤镜,代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
AVFrame *filterFrame = av_frame_alloc();
while(av_read_frame(fmtCtx,pkt)>=0){
if(!runFlag){
break;
}
initFilter();
if(pkt->stream_index == videoStreamIndex){
if(avcodec_send_packet(videoCodecCtx,pkt)>=0){
int ret;
while((ret=avcodec_receive_frame(videoCodecCtx,yuvFrame))>=0){
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
exit(1);
}

/* push the decoded frame into the filtergraph */
if (av_buffersrc_add_frame_flags(bufSrcCtx, yuvFrame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");
break;
}

//把滤波后的视频帧从filter graph取出来
if(av_buffersink_get_frame(bufSinkCtx,filterFrame)<0){
printf("Cannot get frame from bufSinkCtx.\n");
continue;
}

sws_scale(img_ctx,
filterFrame->data,filterFrame->linesize,
0,videoCodecCtx->height,
rgbFrame->data,rgbFrame->linesize);

QImage img(out_buffer,
videoCodecCtx->width,videoCodecCtx->height,
QImage::Format_RGB32);
emit sendQImage(img);
QThread::msleep(30);
}
}
av_packet_unref(pkt);
}
}

效果

对比度(contrast)值必须是一个-2.0-2.0间的浮点数,默认为0。这个值不适合界面控制,我们将其扩展到0-10,5为默认值。

亮度(brightness)值必须是一个-1.0-1.0间的浮点数,默认为0。同样是扩展到0-10,5为默认值。

具体滤镜命令详见:ffmpeg-filters

视频效果:FFmpeg解码添加滤镜Qt显示效果-B站

完整代码在ffmpeg_beginner中的10.20.video_decode_add_filter_display_by_qwidget中。

下一篇:FFmpeg入门教程10.21:音视频解混合(demuxer)为MP3和H264


FFmpeg入门教程10.20:视频添加滤镜
https://blog.jackeylea.com/ffmpeg/ffmpeg-video-filter/
作者
JackeyLea
发布于
2021年4月30日
许可协议