Python调用ffmpeg截取视频片段并进行批量处理的方法
目录
- 背景
- 用到的ffmpeg命令
- python调用bash指令的方法
- python处理代码
- 准备函数
- python批量处理
- 特殊情况处理
- 可选操作:填补csv数据
- 视频具有多个片段的处理 [TODO]
背景
我本地下载了一些番剧,但是片头片尾无用还占空间,因此决定使用ffmpeg对视频切割,只保留中间的正片内容。
用到的ffmpeg命令
ffmpeg
中文文档:https://ffmpeg.github.net.cn/ffmpeg.html
先说用到的ffmpeg
命令,你可以自行在cmd窗口中执行以处理单个视频。
- 获取视频时长:
ffprobe -show_entries format=duration -v error -select_streams v:0 <视频路径>
示例输出:
[FORMAT] duration=1200.981333 [/FORMAT]
ffmpeg -threads 4 -i <视频路径> -ss <开始时间点> -t <持续时间(s)> -c copy -y <输出路径>
-threads 4
:多线程,感觉作用不是很大<开始时间点>
:格式可以为hh:mm:ss
,也可以为具体的视频第几秒<持续时间(s)>
:从指定的开始时间点往后截取多少秒,不是结束时间点-c copy
:音视频都直接复制,不重新编码,速度快-y
:如果指定的输出文件已经存在,则覆盖- 注意:如果输出路径在其他文件夹内,则必须提前创建好文件夹,比如输出文件为
./trimed_编程客栈video/video.mp4
,则需要提前创建好trimed_video
文件夹,否则报错
python调用bash指令的方法
使用subprocess
包,详细信息可以自己查,简单用法如下:
import subprocess command = "ffprobe -show_entries format=duration -v error -select_streams v:0 video.mp4" result = subprocess.run(command, check=True,capture_output=True,text=True) print(result.stdout) # 获取命令执行后的输出数据
参数说明:
command
:要执行的命令字符串,可以通过字符串操作来拼接需要的命令check=True
:如果进程退出码不为0,则抛出异常subprocess.CalledProcessError
,可以通过try-catch进行错误处理capture_output=True
:捕获标准输出或标准错误,获取命令执行的输出信息text=True
:默认标准输出为字节类型,这个可以改为字符串类型,方便字符串解析
python处理代码
注意,我所有代码都没考虑时长超过1小时的视频,如果需要操作1小时以上的视频,请自行修改相关代码
准备函数
由于这两个函数可能在多个文件中使用,因此单独创建了一个文件,其他文件需要调用时通过import myfunc
即可
"""myfunc.py""" import os,re from pathlib import Path def match_files(dir,extension,content_range=[], not_content_range=[]): """在指定目录下找符合扩展名的文件,不递归 用法示例:match_files("./",[".mp4", ".mkv"],range(74,80+1)) extension:需要的扩展名,字符串列表 content_range:包含的范围,为空则不限制,整数列表 not_content_range:不包含的范围,整数列表 """ matchs = [] files = os.listdir(dir) for f in files: if Path(f).suffix in extension: # 检查文件扩展名是否在指定的扩展名列表中 # 提取文件名中的第一个数字序列作为编号 number = int(re.findall(r'\d+',f)[0]) if content_range:# 判断是否指定了包含范围,如果指定则判断是否在范围内 if number in content_range and number not in not_content_range : matchs.append(f) else: # 如果不指定范围,则匹配所有 if number not in not_content_range : matchs.append(f) return matchs def time_to_seconds(time_str): """将时间字符串转换为秒,格式mm:ss""" minutes, seconds = map(int, time_str.split(':')) return minutes * 60 + seconds
python批量处理
import myfunc import subprocess import re """ 注意写好路径,扩展名,以及需要处理的序号范围,排除的序号范围 """ videos = myfunc.match_files("./",[".mp4", ".mkv"],[140]) start_time_point = "02:35" end_time_point = "17:42" for video in videos: # 如果文件名有空格,需要加引号 command1 = "ffprobe -show_entries format=duration -v error -select_streams v:0 \""+video+"\"" try: # 先获取视频时长 result = subprocess.run(command1, check=True,capture_output=True,text=True) duration = round(float(re.search(r"duration=([\d.]+)",result.stdout).group(1))) """注意修改command2的参数, 00默认小时为00,即不考虑时长超过1小时的情况,按需修改 "\"./trimed_video/"+video+"\""是输出视频路径 需要根据自己的视频情况修改 """ command2 = "ffmpeg -threads 4 -i "+"\""+ video +"\""+ " -ss 00:" + start_time_point + " -t "+str(myfunc.time_to_seconds(end_time_point)-myfunc.time_to_seconds(start_time_point)) +" -c copy -y "+"\"./trimed_video/"+video+"\"" try: # 运行FFmpeg命令 subprocess.run(command2, check=True,capture_output=True) print(f"视频已成功裁剪到 {video}") except subprocess.CalledProcessError as e: print(f"FFmpeg命令执行失败: {e}", video) except subprocess.CalledProcessError as e: print(f"FFmpeg命令执行失败: {e}", video)
特殊情况处理
可能视频的片头和片尾时长并不总是固定的,导致不能方便地按照 python批量处理的代码,直接按相同的片头长度和片尾长度操作,因此我使用了csv表格来记录视频的片头片尾长度,并使用python按照csv表格内的数据裁剪视频。
csv表格示例内容如下片头片尾信息.csv
序号
,片头时间点
,片尾时间点
,总时长
是必填项,剩余两项可以空着,但是必须填写英文分号。
总时长
可以通过ffmpeg命令获取,但是既然是特殊情况,手动打开视频了,填一下总时长也不麻烦
可选操作:填补csv数据
有时候需要填写几个视频信息,来判断这两个序号之间的视频是不是片头片尾时长一样,如果一样就可以通过python批量处理的代码来操作,因此写了下面的代码,可以自动计算csv表格中的最后两列数据,观察片头时间点
和片尾长度
是否一直可以粗略判断
import csv # 文件路径 file_path = "./片头片尾信息.csv" # 将时间字符串转换为秒 def time_to_seconds(time_str): minutes, seconds = map(int, time_str.split(':')) return minutes * 60 + seconds # 读取CSV文件 with open(file_path, mode='r', encoding='utf-8') as file: reader = csv.reader(file) rows = list(reader) rows = [row for row in rows if row] for i in range(1, len(rows)): end_time_str = rows[i][2] if rows[i][4] != '': continue # 如果已经有值了,则不再计算 total_duration_str = rows[i][3] end_time_seconds = time_to_seconds(end_time_str) total_duration_seconds = time_to_seconds(total_duration_str) tail_length_seconds = total_duration_seconds - end_time_seconds rows[i][4] = str(tail_length_seconds) start_time_seconds = time_to_seconds(rows[i][1]) rows[i][5] = str(end_time_seconds - start_time_seconds) # 将更新后的内容写回CSV文件 with open(file_path, mode='w', newline='', encoding='utf-8') as file: writer = csv.writer(file) writer.writerows(rows)
python根据csv数据裁剪视频
import csv # 文件路径 file_path = "./片头片尾信息.csv" # 将时间字符串转换为秒 def time_to_seconds(time_str): minutes, seconds = map(int, time_str.split(':')) return minutes * 60 + seconds # 读取CSV文件 with open(file_path, mode='r', encoding='utf-8') as file: reader = csv.reader(file) rows = list(reader) rows = [row for row in rows if row] for i in range(1, len(rows)): end_time_str = rows[i][2] if rows[i][4] != '': continue # 如果已经有值了,则不再计算 total_duration_str = rows[i][3] end_time_seconds = time_to_seconds(end_time_str) total_duration_seconds = time_to_seconds(total_duration_str) tail_length_seconds = total_duration_seconds - end_time_seconds rows[i][4] = str(tail_length_seconds) start_time_seconds = time_to_seconds(rows[i][1]) rows[i][5] = str(end_time_seconds - start_time_seconds) # 将更新后的内容写回CSV文件 with open(file_path, mode='w', newline='', encoding='utf-8') as file: writer = csv.writer(file) writer.writerows(rows)
python根据csv数据裁剪视频
import myfunc import csv, re,subprocess """注意修改你想要匹配的文件扩展名""" videos = myfunc.match_files("./",[".mp4", ".mkv"]) """注意改成你的csv文件路径""" with open("./片头片尾信息.csv", mode='r', encoding='utf-8') as file: reader = csv.reader(file) rows = list(reader) # 提取第一列数据 del rows[0]# 删除表头 first_column = [int(row[0]) for编程客栈 row in rows if row] # 使用列表推导式,跳过空行 videos = [video for video in videos if int(re.findall(r'\d+',video)[0]) in first_column] count = 0 for video in videos: command1 = "ffprobe -show_entries format=duration -v error -select_streams v:0 \""+video+"\"" try: # 先获取视频时长 result = subprocess.run(command1, check=True,capture_output=True,text=True) duration = round(float(re.search(r"duration=([\d.]+)",result.stdout).group(1))) start_time_pint = myfunc.time_to_seconds(rows[count][1]) end_time_pount = myfunc.time_to_seconds(rows[count][2]) """注意替换你想要的输出路径""" command2 = "ffmpeg -threads 4 -i "+video + " -ss " + str(start_time_pint) + " -t "+str(end_time_pount-start_time_pint) +" -c copy -y "+"\"./trimed_video/"+video+"\"" # print(command2) try: # 运行FFmpeg命令 subprocess.run(command2, check=True,capture_output=True) print(f"视频已成功裁剪到 {video}") except subprocess.CalledProcessError as e: print(f"FFmpeg命令执行失败: {e}", video) except subprocess.CalledProcessError as e: print(f"FFmpeg命令执行失败: {e}", video) count += 1
视频具有多个片段的处理编程客栈 [TODO]
TODO有大佬知道的话编程客栈欢迎讨论,我觉得先切片再合并太麻烦。
这种特殊情况一般出现在,视频有彩蛋之类的,在片头之前或片尾之后仍有正片内容。网上搜了但没找到特别好的,找到一个文章但测试后不好用,所以选择了手动切片再合并,
多个视频合并的ffmpeg命令:- 创建文本文件
filelist.txt
,并写入要合并的多个视频片段
file 'input1.mp4' file 'input2.mp4'
- 执行合并命令:
ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp4
由于这种情况比较少,懒得写python代码,自己手动在cmd执行吧
以上就是Python调用ffmpeg截取视频片段并进行批量处理方法的详细内容,更多关于Python ffmpeg截取视频批量处理的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论