引子

我需要实现一个功能

就是把一些段的视频片段批量打乱并且剪在一起

并不要求逻辑性以及完整性

找了很久都没有相关的轮子

我想也是

这个需求确实比较古怪

所以只好自己重新造轮子

大致思路

怎样才能把视频片段打散呢?

  1. 我想先把视频合并成一个完整的整体,然后导出一个长视频。这样的话,至少不会存在格式之间的冲突。
  2. 从中截取一些固定时长的小段落,比如10秒一个。
  3. 随机选择片段并且进行合并。

实现方法

安装ffmpeg

视频处理这方面还是绕不过ffmpeg

mac的话使用brew可以直接安装:

brew install ffmpeg

安装过程不是那么顺利,网络需要比较稳定。还有叫做gcc的依赖不能安装,根据提示下载了开发者工具才装上。

如果是win的话,安装过程相对来说是要简单一些。可以直接去ffmpeg的官网下载,然后在环境变量添加变量就行。

安装ffmpy

这是pyhton的一个库,可以使用python对ffmeg进行调用,使用pip直接安装即可:

pip install ffmpy

分割视频

这里参照了知乎上的一篇文章,讲的非常清楚。

使用Python+FFMPEG实现视频分割与合并

我按照操作写了代码:

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 数值则可以固定不变为需要的长度。

所以我需要一个可以方便计算时间的函数。

计算时间

我参照了这篇博客

python datetime库使用和时间加减计算

这里面使用了一个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批量统一音量,学会这个,一万条音频也可以搞定,_哔哩哔哩_bilibili

流程总结

所有的操作已经理顺了,最后把流程理一遍,方便自己记住。

  1. 首先把视频全部导入fcpx,然后批量调整音量。
  2. 筛选、剪辑最后导出视频。
  3. 在handbrake中进行压缩,并转换成mp4格式的文件。
  4. 启动程序进行分割。
  5. 移动到NAS中的固定文件夹,以方便合并程序调用。
  6. 根据需要随机合并视频。

最后

ffmpeg实在是太难懂了

我以为看看官方的文档就可以掌握

事实上都是借助网上的中文资料才学会一些操作

最后修改:2022 年 12 月 30 日
如果觉得我的文章对你有用,请随意赞赏