为Ubuntu编写一个录屏格式自动转换脚本及学习心路

本文最后更新于:3 个月前

点击获得封面头图

此文章使用CC-BY-NC协议,协议详情介绍请看此文


前言

其实本篇文章的大量内容都来自于我最近的一个仓库中的README.

不过由于README的字数远远大于代码量!!!

于是我决定引用一下,写到博客里.

同时也记录一些学习过程.


起因

如果你也使用Ubuntu系统(Ubuntu 22.04.1 LTS)的话,会发现:

尽管系统自带的录屏工具很好用,但是最后录制的格式却是普通人不太常用的webm.

碰巧最近又了解到了ffmpeg这个可以说是最强大的视频编辑工具(这个编辑并不是像PR那样的编辑,而是纯粹的,就像GoldWave编辑音频那样的编辑).

于是我就着手研究怎么用ffmpeg实时地转换录屏文件格式.


思路

首先检测到新增的webm文件,

主要是检测的方法.

接着调用ffmpeg转换webm文件,为mp4.

重点在线程数的确定,以及可能存在的资源占用问题.

最后删除webm文件.


研究

刚开始我以为,在录屏过程中,录屏文件最后所在的文件夹(系统语言为中文时,即为”录屏”文件夹)会产生一些中间文件(这是必然的,只不过路径无法确定到底是不是在这个文件夹).

所以我便想了步骤一:

实时监测文件夹是否新增含有某些特殊部分的文件(例如下载过程中经常出现的.part文件)是不是就行了?

然后我就录了个屏,同时检查着”录屏”文件夹的状态.

一开始啥也没看到,于是我在想,是不是文件以.开头了,自动隐藏了.

按了一下Ctrl H快捷键来显示隐藏文件,结果发现:

的确产生了中间文件,只不过这个中间文件的名字是录屏结束后完完整整的文件名!

一时间我感到很诧异:为什么不是.开头的文件也能隐藏起来?

这个问题的答案我还是没有找到.

那么通过步骤一是没法判断了:

原因是不知道这个webm文件是否已经完成录制?

接着我想到:

可以利用文件大小是否变化来判断这个webm文件是否完成录制.

理论上说,这个方法是可行的,于是我开始找这个大小变化间的最短时间差值:

第一次发现3秒是一个合适的数字,但是就在刚刚再测试的时候,我又发现当录制时间在20秒以上的时候,会可能起码有3秒时间段文件大小不发生变化,后来我把3改成了5,再测试的时候就没有问题了.

这部分的代码实现可以是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
##l为webm文件的名称.
info=os.stat(l)
##os.stat方法可以获得文件属性.
s=info.st_size
##st_size类是文件大小.
time.sleep(5)
##休眠5秒.
inf=os.stat(l)
ss=inf.st_size
##重复上面操作.
if ss==s:
pass
##如果文件大小没有发生变化,说明录制已经结束.

于是问题一解决.

但多次测试之后,我又发现了另一个问题:

转换的效率太低下,以及死循环导致的CPU占用过高.

第一个问题我通过电脑的逻辑CPU核数为线程数处理了,第二个问题可以通过timesleep函数进行处理.

原因是这个

那么剩下的就是逻辑了,代码贴一下:

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 os
import time

def get_good_name(name):
name=name.replace('(','\(')
name=name.replace(')','\)')
name=name.replace(' ','\ ')
return name

def get_core():
os.system("cat /proc/cpuinfo |grep 'processor'|wc -l > .core")
with open('.core','r')as f:
core=f.read()
os.system('rm -rf .core')
return str(core[0])

def chck(road,time_s):
ls=os.listdir(road)
if '.webm' in str(ls):
for l in ls:
if '.webm' in l:
info=os.stat(l)
s=info.st_size
time.sleep(5)
inf=os.stat(l)
ss=inf.st_size
if ss==s:
t='ffmpeg -threads '+get_core()+' -i '+get_good_name(l)+' '+get_good_name(l[:-4])+'mp4'
r='rm -rf '+get_good_name(l)
n='nohup.out'
os.system(t)
os.system(r)
if os.path.exists(n):
os.system('rm -rf '+n)
else:
pass
else:
time.sleep(5)
else:
pass
else:
time.sleep(time_s)

while True:
chck('.',300)

其中,函数get_good_name()用来格式化名称(识别空格啥的);

get_core()获取逻辑CPU核数;

在这个期间创建了一个临时文件保存核数(cat输出到文件,再读取文件,然后删除).

chck(road,time_s)就是主函数了,road参数为录屏文件夹路径,time_s为休眠间隔.

在chck函数里,先判断整个文件夹是否有webm文件,没有的话直接进入周期为300秒的休眠(if '.webm' in str(ls));

如果有webm文件,就从文件列表中遍历一下,筛选出来这些文件(if '.webm' in l:);

接着判断文件大小是否发生改变(if ss==s:):

如果改变了,就进行ffmpeg转换;

没有改变,则再进入周期为5秒的等待.

加入休眠机制后,CPU占用非常小(跟没有一样):

如图所示


使用

使用方法就是搬的README啦!

首先应该安装ffmpeg:

1
sudo apt install ffmpeg -y

接着下载脚本:

1
wget https://raw.githubusercontent.com/wzk0/aw24/main/webm2mp4.py

随后将脚本移动到/home/用户名/Videos/录屏文件夹(其他语言的话名字会变)

接下来共有三个方法可以使用:


前台挂起

录屏文件所在文件夹直接打开终端输入python3 webm2mp4.py即可,但是期间会输出大量文字,且终端基本上是只读模式,也无法关闭此终端.

不过好处是可以随时关闭.


后台挂起

录屏文件所在文件夹打开终端输入nohup python3 webm2mp4.py &,随后即可回车然后关闭此终端,或在此终端进行其他操作.

若要杀死此进程:

  • 假如此终端未关闭,输入jobs -l即可获取PID:

如图所示

随后输入kill -9 PID即可:

如图所示

  • 假如此终端已关闭,输入ps ux | grep python3\ webm2mp4.py可查看PID.

同理,获取后输入kill -9 PID即可:

如图所示


开机自启

打开/etc/init.d目录,在此编写一个脚本(nano webm2mp4):

1
2
3
#!/bin/sh

python3 /home/用户名/Videos/录屏/webm2mp4.py

或者:

1
2
3
#!/bin/sh

nohup python3 /home/用户名/Videos/录屏/webm2mp4.py &

记得替换用户名和自己的录屏文件夹的名称.

不过我试了开机自启,好像不行,不知道是不是因为权限不够.


尾声

“这是一个只有40行代码的小脚本,但是为什么偏偏是这个非常小代码量的项目要拿出来写文章呢?”

有些读者可能会这么想.

“你不会在冲KPI吧!”

这不可能的!

实际上,光是线程数的判断这个点我就搜了很久(最后是群里的一位大佬告诉的我,我想了一下也挺合理的).

其次就是整个的逻辑:

一开始我以为单纯的死循环就好,然后发现了CPU占用的问题,加入了休眠机制;但是休眠问题解决了,导致了我的另一个逻辑错误,就是检测时间,因为我把休眠这一项加到for循环里了,导致如果列表的第一位不是webm文件,就会进入休眠,这样一来,遍历列表的时间就会长得恐怖.

总之,为了这40行,我搜了很多,同时也有了一个观念,即:

需要从用户层考虑问题!

想想之前自己的很多程序,对资源占用是从来没考虑的,唉…


我是听话的便当.

Bye~❛‿˂̵✧