type
Post
status
Published
date
Dec 30, 2022
slug
summary
ffmpeg比想象中难懂很多
tags
python
ffmpeg
category
探索新世界
icon
password
引子
我需要实现一个功能
就是把一些段的视频片段批量打乱并且剪在一起
并不要求逻辑性以及完整性
找了很久都没有相关的轮子
我想也是
这个需求确实比较古怪
所以只好自己重新造轮子
大致思路
怎样才能把视频片段打散呢?
- 我想先把视频合并成一个完整的整体,然后导出一个长视频。这样的话,至少不会存在格式之间的冲突。
- 从中截取一些固定时长的小段落,比如10秒一个。
- 随机选择片段并且进行合并。
实现方法
安装ffmpeg
视频处理这方面还是绕不过
ffmpeg mac的话使用brew可以直接安装:
brew install ffmpeg
安装过程不是那么顺利,网络需要比较稳定。还有叫做gcc的依赖不能安装,根据提示下载了开发者工具才装上。
如果是win的话,安装过程相对来说是要简单一些。可以直接去ffmpeg的官网下载,然后在环境变量添加变量就行。
安装ffmpy
这是pyhton的一个库,可以使用python对ffmeg进行调用,使用pip直接安装即可:
pip install ffmpy
分割视频
这里参照了知乎上的一篇文章,讲的非常清楚。
我按照操作写了代码:
split_video=ffmpy.FFmpeg( inputs={'test.mov': None}, outputs={'test.mov': [ '-ss', '00:00:00', '-t', '120', '-vcodec', 'copy', '-acodec', 'copy' ]} split_video.run()
以上代码可以成功的运行,并且路径可以采用绝对路径。
上述代码运行之后,则是把视频从最开始裁截一段长为120秒的视频。如果我需要批量裁剪,那么就需要加上一个循环,每次都更新裁剪的起点
-ss 参数的数值。而裁剪的长度-t 数值则可以固定不变为需要的长度。所以我需要一个可以方便计算时间的函数。
计算时间
我参照了这篇博客
这里面使用了一个
datetime 的库里面有几点比较重要
strptime可以输入字符串转换成一个日期实例,起到自定义时间。不需要输入所有的数值,如果只输入时、分,那么年、月、日就是默认的数值。
timedelta可以对时间进行加减。
strftime可以格式化输出字符串,例如只输出年月日,或者调整输出顺序等。
于是我写了如下代码:
def time_adjust(complete_time:str,split_time:int): split_point_l=['00:00:00'] # 总时间的实例 complete_time_t=datetime.datetime.strptime(complete_time,'%H:%M:%S') # 初始时间的实例 start_time_t=datetime.datetime.strptime('00:00:00','%H:%M:%S') # 开始循环 while start_time_t<complete_time_t: # 更新后的时间 new_time_t=(start_time_t+datetime.timedelta(seconds=split_time)) # 赋予新的开始时间 start_time_t=new_time_t split_point_l.append(new_time_t.strftime('%H:%M:%S')) # 舍弃列表最后一个元素,并计算出最后还剩多少 split_point_l=split_point_l[:-1] # 计算最后跟总时间差多少秒 gap_time_t=complete_time_t-datetime.datetime.strptime(split_point_l[-1],'%H:%M:%S') final_time=(datetime.datetime.strptime('00:00:00','%H:%M:%S')+gap_time_t).strftime('%H:%M:%S') return split_point_l,final_time
这两天有点头晕,写的有点乱。
不过实现的功能就是,输入视频的总时间,和预期每个视频的长度,最后输出一个列表和一个字符串。
这个列表包括了所有即将被分割的小视频的时间点,而字符串则是代表最后一个视频的长度(因为基本上不可能完美的分割每一个视频,最后一个视频肯定会不够设定的时长。)
输出视频时间
以上的函数可以对输入的文件时长计算时间点,那么能不能自动获取视频文件的时长呢?
我看网上很多多说只需要
ffmpeg -i test.mov
在命令行下确实会输出相关的信息,以及一个报错提示(大概是没有指定输出还是什么意思)
但是在ffmpy的调用下就会报错,而且不能传出任何信息
所以我参考了知乎上一位给的代码。
def get_video_duration(filename): cap = cv2.VideoCapture(filename) if cap.isOpened(): rate = cap.get(5) frame_num =cap.get(7) duration = frame_num/rate return duration return -1
虽然看不明白,但确实可以正确的输出。输出的是视频的秒数,再调用datetime转成正确的格式即可。
循环分割视频
因为已经有了时间点的列表,所以继续写了循环分割视频的代码:
def split(complete_time:str,split_time:int=10): split_point_l,final_time=time_adjust(complete_time,split_time) # 制作一个每次切割时间的列表 split_time_l=[split_time]*(len(split_point_l)-1) split_time_l.append(final_time) print(len(split_point_l)) # 输出分割视频的数量 # 开始循环 i=0 for v1,v2 in zip(split_point_l,split_time_l): i+=1 split_video= ffmpy.FFmpeg( inputs={'test.mov': None}, outputs={'%d.mov'%i: [ '-ss', '%s'%v1, '-t', '%s'%v2, '-vcodec', 'copy', '-acodec', 'copy' ]} ) split_video.run() return
以上代码实现的效果是,输入完整的时长和分割的长度,就会自动循环分割视频。
合并视频
上文中知乎作者的一篇文章中,已经提到了如何合并视频。
不过是采用创建临时文件的方式。
def concat(): concat = ffmpy.FFmpeg( global_options=['-f', 'concat','-safe 0'], inputs={'file_l.txt': None}, outputs={'output.mov': ['-c', 'copy']} ) concat.run() return
以上的代码我稍微做了一定的调整,在合并之前先随机生成一个txt文件即可。
这里有一个非常重要的点,让我卡了很久。
txt文件里面如果放着是文件的相对路径,是可以顺利读取并合并的。
但如果是绝对路径则不行。
我一开始以为是斜杠转义之类的问题,研究了很久。
后来在b站的一篇文章上找到了答案。
需要在参数中添加
'-safe 0' ,同时txt中的文件路径需要用'' 进行包围。
我在windows的系统下做测试,所以路径都是反斜杠,不知道别的平台需不需要。批量调整音量
视频之间会存在一个音量不统一的情况,观看的效果是非常差的。
我参照了下面up主在fcpx的设置。
流程总结
所有的操作已经理顺了,最后把流程理一遍,方便自己记住。
- 首先把视频全部导入fcpx,然后批量调整音量。
- 筛选、剪辑最后导出视频。
- 在handbrake中进行压缩,并转换成mp4格式的文件。
- 启动程序进行分割。
- 移动到NAS中的固定文件夹,以方便合并程序调用。
- 根据需要随机合并视频。
最后
ffmpeg实在是太难懂了
我以为看看官方的文档就可以掌握
事实上都是借助网上的中文资料才学会一些操作
- 作者:xhhdd
- 链接:https://blog.xhhdd.cc/article/d60d0f94-58ed-49df-958f-b7adda963f1d
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章





