作者:李晓飞 来源:Python技术 最近有了一个新任务,需要将赛事视频,拆分成两分钟以内的小段,用于发布到短视频平台上。 本以为是个一次性的工作,结果赛事视频数据巨大,视频文件长短不一,完全没法手工处理,于是Python又一次拯救了我。 还等什么,开始干吧!最重要的事 无论做什么事情,都要去分析一下最重要的是什么,然后集中精力攻克,再继续找最重要的事。 对我们这个任务来说,不算是个大项目,不过呢,还是要找最重要的事开始,步步为营,最终将整个问题解决了。 整体来来看,我们需要从一个目录中读取视频文件,然后,对每个视频文件进行裁剪,最后将处理好的文件保存好。 在这个过程中,最重要的是什么呢?我觉得,是视频裁剪,如果不能方便的裁剪视频,其他的一切工作都是白费的,是吧。裁剪视频 现在短视频很流行,有很多视频编辑软件,功能丰富,而我们需要的只是裁剪功能,而且需要用编程的方式调用,那么最合适的莫过于ffmpeg〔1〕了。 ffmpeg是一个命令行工具,功能强大,可以编程调用。 从ffmpeg官网上下载对应操作系统的版本,我下的是Windows版〔2〕。 下载后解压到一个目录,然后将目录下的bin,配置到环境变量里。然后打开一个命令行,输入:ffmpegversionffmpegversion20211007gitb6aeee2d8bfullbuild。。。 测试一下,能显示出版本信息,说明配置好了。 现在读一下文档,发现拆分视频文件的命令是:ffmpegi〔filename〕ss〔starttime〕t〔length〕ccopy〔newfilename〕i为需要裁剪的文件ss为裁剪开始时间t为裁剪结束时间或者长度c为裁剪好的文件存放 好了,用Python写一个调用:importsubprocessasspdefcutvideo(filename,outfile,start,length90):cmdffmpegisssdtdccopys(filename,start,length,outfile)psp。Popen(cmd,shellTrue)p。wait()return定义了一个函数,通过参数传入ffmpeg需要的信息将裁剪命令写成一个字符串模板,将参数替换到其中用subprocess的Popen执行命令,其中参数shellTrue表示将命令作为一个整体执行p。wait()很重要,因为裁剪需要一会儿,而且是另起进程执行的,所以需要等执行完成再做后续工作,否则可能找不到裁剪好的文件 这样视频裁剪工作就完成了,然后再看看什么是最重要的。计算分段 视频裁剪时,需要一些参数,特别是开始时间,如何确定呢?如果这件事做不好,裁剪工作就很麻烦。 所以看看如何计算裁剪分段。 我需要将视频裁剪成一分半的小段,那么将需要知道目标视频文件的时间长度。获取视频长度 如何获得长度呢?ffmpeg提供了另一个命令ffprobe。 找了一下,可以合成一个命令来获取:ffprobeverrorshowentriesformatdurationofdefaultnoprintwrappers1:nokey1ia。flv920。667 命令比较复杂哈,可以先不用管其他参数,只要将要分析的视频文件传入就好了。命令的结果是显示一行视频文件的长度。 于是可以编写一个函数:importsubprocessasspdefgetvideoduration(filename):cmdffprobeverrorshowentriesformatdurationofdefaultnoprintwrappers1:nokey1isfilenamepsp。Popen(cmd,stdoutsp。PIPE,stderrsp。PIPE)p。wait()strout,strerrp。communicate()去掉最后的回车retstrout。decode(utf8)。split()〔0〕returnret函数只有一个参数,就是视频文件路径合成命令语句,将视频文件路径替换进去用subprocess来执行,注意这里需要设置一下命令执行后的输出用wait等待命令执行完成通过communicate提取输出结果从结果中提取视频文件的长度,返回分段 得到了视频长度,确定好每个分段的长度,就可以计算出需要多少分段了。 代码很简单:importmathdurationmath。floor(float(getvideoduration(filename)))partmath。ceil(durationlength) 注意,计算分段时,需要进行向上取整,即用ceil,以包含最后的一点尾巴。 得到了需要的分段数,用一个循环就可以计算出每一段的起始时间了。获取文件 因为处理的文件很多,所以需要自动获取需要处理的文件。 方法很简单,也很常用,一般可以用os。walk递归获取文件,还可以自己写,具体根据实际情况。forfnameinos。listdir(dir):fnameos。path。join(dir,os。path。join(dir,fname))basenamesos。path。basename(fname)。split(。)mainnamebasenames〔0〕。split()〔0〕。。。 提供视频文件所在的目录,通过os。listdir获取目录中的文件,然后,合成文件的绝对路径,因为调用裁剪命令时需要绝对路径比较方便。 获取文件名,是为了在后续对裁剪好的文件进行命名。代码集成 现在每个部分都写好了,可以将代码集成起来了:defmain(dir):outdiros。path。join(dir,output)ifnotos。path。exists(outdir):os。mkdir(outdir)forfnameinos。listdir(dir):fnameos。path。join(dir,os。path。join(dir,fname))ifos。path。isfile(fname):splitvideo(fname,outdir)main方法是集成后的方法先创建一个裁剪好的存储目录,放在视频文件目录中的output目录里通过listdir获取到文件后,对每个文件进行处理,其中判断了一下是否为文件调用splitvideo方法开始对一个视频文件进行裁剪总结 总体而言,这是个很简单的应用,核心功能就是调用了一个ffmpeg命令。 相对于技术,更重要的是如何对一个项目进行分析和分解,以及从什么地方开始。 这里的方式起始时,不断地找最重要地事情,以最重要的事情为线索不断地推进,最终以自下而上地方式解决整个问题。 期望这篇文章对你有所启发,比心。