ffmpeg filter

滤波(filter):去除不想要的干扰,噪声,颜色等。ffmpeg 大大扩展了filter, post-process这两个概念。

后处理去方块效应
problem of block artifacts
 smooth slowing-changing regiongs -->
 ramp-like gradient, piece-wise constant
 平坦区域的亮度跳跃。
 跨边界出现锯齿。
reason:
 DCT retain only one subband, the DC coeff constant signal
平坦相邻宏块的交界处,因量化步长不同导致本来很接近的像素值
重构后产生较大差异。
跨边界相邻宏块:dct高频系数被量化为0。
运动补偿加据了这两种情形。
去块效应滤波,就是使边界平滑,

h264处理办法:假设真实边界两侧的像素值梯度比量化误差造成的虚假边界
要大。跟据经验测试数据,定义两个门限alpha, beta,
设竖直边界两侧为p3,p2,p1,p0|q0,q1,q2,q3。
若|p0-q0| < alpha, |p1-q1| < beta, |q1-q0| < beta,
则认为应当进行滤波,详见h264_template.c:h264_loop_filter_luma_intra()
h264_loopfilter.c还计算了块边界强度bS,用来决定滤波强度,
即多少个像素参与。平坦区域滤波可以强,复杂区域为保留细节,滤波要弱。
文字描述可参考[3]

ffmpeg处理:
制作一个低质量的图片:
ffmpeg -i whale2.jpg -q:v 31 -s 400x300 low-quality.jpg

指定最高质量也有方块:
ffmpeg -i low-quality.jpg -q:v 1 -y xx.jpg
加上方块过滤器:
ffmpeg -i low-quality.jpg -q:v 1 -vf pp=hb/vb -y yy.jpg

对比如下图:

ffmpeg filter_第1张图片


decode_video ffmpeg.c:1983 -->
av_buffersrc_add_frame_flags-->av_buffersrc_add_frame_internal-->
request_frame-->ff_filter_frame-->ff_filter_frame_framed-->
filter_frame-->ff_filter_frame-->ff_filter_frame_framed-->
1.pp_postprocess里面默认只对Y分量postProcess-->postProcess_SSE2(postprocess_template.c:3251)
水平方向9-tap filter,可参考[2]。


把图片放在3x3格子中央:
ffplay cat.jpg -vf 'pad=3*iw:3*ih:iw:ih:blue'
并排显示两张图片:

ffmpeg -i cat.jpg  -i bee.jpg  -filter_complex '[0:v]scale=100:100, pad=2*iw:ih:0:0:blue[a];  [1:v]scale=100:100[b];  [a][b]overlay=102:0' -y 2.jpg


一个大视频叠加上一个小视频就是 Picture In Picture:

ffmpeg -i 0Cannon.f4v  -i slamtv60.264 -i slamtv60.264 -filter_complex '[1:v]scale=100:100[b];  [0:v][b]overlay=80:80' -y xx.mp4

ffmpeg filter_第2张图片


ffmpeg -i master_video.mp4 -vf "movie=smaller_inner_video.mp4,  fade=out:300:30:alpha=1 [inner]; 
 [in][inner] overlay=70:70 [out]" completed.mp4
为了测试video filter 可以采用 ffmpeg -vf ... - | ffplay - 的方式。
但是windows 悲催的管道是文本的。

代码分析
int filter_frame(AVFilterLink *inlink, AVFrame *in)
 in -- 输入数据
 inlink->dst->outputs[0] --out link
crop filter核心操作只是修改一下in->data指针而已,应该算是最简单的filter。

下列两种情形,
transcode_init()。
decode_video() avcodec_decode_video2之后,如果采样率变了。
则调用
configure_filtergraph-->avfilter_graph_parse2

场景1:
 b vf_ crop.c:filter_frame
 r -i r.png -vf crop=400:300:30:30 -y r2.png
(gdb) bt
#0  filter_frame  vf_crop.c:244 最后调ff_filter_frame()递归下去
#1  ff_filter_frame_framed  
#2  ff_filter_frame avfilter.c:1161
#3  request_frame buffersrc.c:499
#4  av_buffersrc_add_frame_internal buffersrc.c:181
#5  av_buffersrc_add_frame_flags buffersrc.c:106
#6  decode_video ffmpeg.c:1974 for(ist->filters[])多个源
#7  output_packet
#8  process_input
#9  transcode_step
#10 transcode

transcode_step-->
 process_input
 reap_filter
  av_buffersink_get_frame_flags(filter) while(1)方式遍历每个输出
  do_video_out-->avcodec_encode_video2

do_wideo_out 断点,
(gdb) p in_picture->linesize 
$4 = {1376, 0, 0, 0, 0, 0, 0, 0} 
源图片宽度是1366, 这样就可以解释为什么crop里面没有设置linesize了。
(gdb) p in_picture->width 
$5 = 400
(gdb) p in_picture->height
$6 = 300
在pngenc.c:encode_frame里面,引用height和linesize[0],png_get_interlaced_row。

场景2( 倒影:多条filter chains)
r -i r2.png -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" -y r3.png

每个filter_frame都断点, 执行顺序:
split-->overlay-->crop-->vflip-->overlay
最后一个断点的栈帧:
#0  filter_frame vf_overlay.c:578
#1  ff_filter_frame_framed ff_filter_frame 
#3  filter_frame vf_scale.c:530
#4  ff_filter_frame_framed ff_filter_frame  
#6  filter_frame gvf_vflip.c:84
#7  ff_filter_frame_framed ff_filter_frame 
#9  filter_frame vf_crop.c:297
#10 ff_filter_frame_framed ff_filter_frame 
#12 filter_frame split.c:86
#13 ff_filter_frame_framed ff_filter_frame  
#15 request_frame av_buffersrc_add_frame_internal 
#17 av_buffersrc_add_frame_flags 
#18 decode_video output_packet process_input
info break 确认overlay被执行了两次,其他都一次。
可以理解为有向无环图的dfs遍历:
split-->overlay
     -->crop-->vflip-->scale-->overlay
在split.c:filter_frame()里面ctx->nb_outputs=2开始分支的。
如何知道一个link结束了?只要看link->dstpad->filter_frame是空就表明结束了,交给
deault_filter_frame-->ff_filter_frame-->ff_filter_frame_framed
-->filter_frame buffersink.c:104 -->add_buffer_ref(ctx, frame);
可以理解为:buffersink是filter graph的虚拟汇点,而request_frame是它的虚拟源点;
分别为总出口和总入口。这个叙述忽略了process_input之前的transcode_from_filter,
我没有完全理解它的意义,只知道像vf_overlay只在process_input栈帧里面。
avfilter_process_command 应该跟-vf sendcmd有关,未验证。

其他一些有趣的filter
o Build a 5.1 output from 6 single-channel streams.
o Make music with  quiet and loud passages suitable for noisy environment
o boxblur --模糊的我的近视眼分不清数字了,可以用来测OCR。
o negate -- 不仅仅是黑白颠倒。
o noise=alls=20:allf=t+u --老电影,还是彩色的喔。
o tile=2x2 --监控大屏幕
o edgedetect -- 铅笔手绘卡通风格

Ref
[1] https://www.ffmpeg.org/ffmpeg-filters.html#pp
[2] http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
[3] 陈靖等,深入理解视频编解码技术--基于H.264标准

你可能感兴趣的:(codec)