缘起

compton 是 X11 下给程序加透明背景的 compositor。前阵子,为了得到一个透明背景模糊的效果,我使用了 compton 的一个 dual_kawase 的分支。虽然平时使用时感觉不出来,但在录屏出来的视频则会不停地闪烁。这样的视频看完估计眼睛就得废掉!

寻找问题

逐帧研究发现,大概每秒都会有一帧只录到了背景,而本应有的 terminal/chrome 啥的都神奇地消失不见了。估计大概是模糊背景的性能原因,导致有的帧没能在限定的时间内完成渲染。

解决问题

事已至此,由于是会议,重新录显然是不可能的。幸好正常帧和出错帧的特征都比较明显:

  1. 出错帧背景都比较明亮
  2. 而正常帧都有一个 terminal 在运行,色彩则比较暗

结论是,我只要在图像上取一个特定点,判定它的亮度,若太明亮则用上一帧代替当前帧,这样就可以基本解决闪烁的问题了。

实现

 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
import subprocess as sp
import signal

X = 1500
Y = 450
WIDTH = 1920
HEIGHT = 1080
LENGTH = WIDTH*HEIGHT*3
OFFSET = (WIDTH*(Y-1)+X)*3
FFMPEG_BIN = 'ffmpeg'

# 读取视频解压成图片流
in_cmd = [FFMPEG_BIN,
            '-i', 'zoom_0.mp4',
            '-f', 'image2pipe', # 输出成图片格式
            '-pix_fmt', 'rgb24',
            '-vcodec','rawvideo', '-']
in_pipe = sp.Popen(in_cmd, stdout=sp.PIPE, stdin=sp.PIPE, bufsize=LENGTH*10)

# 输出到输出流进行压缩
out_cmd = [FFMPEG_BIN,
            '-f', 'rawvideo',
            '-pix_fmt', 'rgb24',
            '-s', '%dx%d' % (WIDTH, HEIGHT), # 由于是图片流,需要显式指定尺寸
            '-r', '25',
            '-y',
            '-an',
            '-i', '-',
            'output.mp4']
out_pipe = sp.Popen(out_cmd, stdout=sp.PIPE, stdin=sp.PIPE, bufsize=LENGTH*10)

# 处理逻辑
i = 0
prev_raw = None
while True:
    raw_image = in_pipe.stdout.read(LENGTH)
    if not raw_image:
        break
    r = raw_image[OFFSET]
    if r > 80:
        raw_image = prev_raw
    out_pipe.stdin.write(raw_image)
    in_pipe.stdout.flush()
    prev_raw = raw_image
out_pipe.send_signal(signal.SIGINT)