python调用ffmpeg对截取视频片段,可批量处理

本文完全免费,非VIP文章,如果您发现需VIP可看全文,请邮箱联系我:openwebsite@foxmail.com

文章目录

python调用ffmpeg对截取视频片段,可批量处理

背景:我本地下载了一些番剧,但是片头片尾无用还占空间,因此决定使用ffmpeg对视频切割,只保留中间的正片内容。

用到的ffmpeg命令

ffmpeg中文文档:https://ffmpeg.github.net.cn/ffmpeg.html

由于csdn点击链接会跳转到安全确认页面,因此选择直接给出链接文本,在edge浏览器中,可以选中链接文本并拖动到标签页区域,直接打开文档,更方便。

先说用到的ffmpeg命令,你可以自行在cmd窗口中执行以处理单个视频。

  1. 获取视频时长:ffprobe -show_entries format=duration -v error -select_streams v:0 <视频路径>
    示例输出:
bash 复制代码
[FORMAT]
duration=1200.981333
[/FORMAT]
  1. 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包,详细信息可以自己查,简单用法如下:

python 复制代码
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即可

python 复制代码
"""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批量处理

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

c 复制代码
序号,片头时间点,片尾时间点,总时长,片尾长度,有用视频长度
120,2:16,18:28,20:44,,
122,2:16,17:25,19:41,,
123,2:16,19:11,21:27,,
127,2:16,19:13,20:49,,

序号片头时间点片尾时间点总时长是必填项,剩余两项可以空着,但是必须填写英文分号。

其实总时长可以通过ffmpeg命令获取,但是既然是特殊情况,手动打开视频了,填一下总时长也不麻烦

可选操作:填补csv数据

有时候需要填写几个视频信息,来判断这两个序号之间的视频是不是片头片尾时长一样,如果一样就可以通过python批量处理的代码来操作,因此写了下面的代码,可以自动计算csv表格中的最后两列数据,观察片头时间点片尾长度是否一直可以粗略判断

python 复制代码
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数据裁剪视频
python 复制代码
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命令:

  1. 创建文本文件filelist.txt,并写入要合并的多个视频片段
bash 复制代码
file 'input1.mp4' 
file 'input2.mp4'
  1. 执行合并命令:ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp4
    由于这种情况比较少,懒得写python代码,自己手动在cmd执行吧
相关推荐
金銀銅鐵2 小时前
[Python] 基于欧几里得算法,实现分数约分计算器
python·数学
Lyn_Li4 小时前
Kaggle Top 5 | 198只股票、200条数据的金融预测——BattleFin高分方案从零复现
python·kaggle·比赛复盘·金融预测
小九九的爸爸8 小时前
前端想要入门Agent开发,要具备哪些Python基础?
python·agent·ai编程
阿耶同学9 小时前
手把手教你用 LangGraph 搭建三层嵌套 Agent 架构
python·程序员
blanks202010 小时前
ffmpeg 学习笔记 通过命令行采集音频
ffmpeg
RTC实战笔记13 小时前
实时互动数字人怎么做,才不是一个只会说话的视频?
音视频·数字人·rtc·数字人接入
花酒锄作田1 天前
Pydantic校验配置文件
python
hboot1 天前
AI工程师第四课 - 深度学习入门
pytorch·python·神经网络
ZhengEnCi2 天前
P2M-Matplotlib折线图完全指南-从数据可视化到趋势分析的Python绘图利器
python·matlab·数据可视化
ZhengEnCi2 天前
P2L-Matplotlib饼图完全指南-从数据可视化到图表定制的Python绘图利器
python·matlab