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

本文完全免费,非VIP文章,如果您发现需VIP可看全文,请邮箱联系我:[email protected]

文章目录

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执行吧
相关推荐
z_mazin30 分钟前
用户行为检测技术解析:从请求头到流量模式的对抗与防御
python·网络爬虫
狐凄1 小时前
Python实例题:使用Pvthon3编写系列实用脚本
java·网络·python
fish_study_csdn7 小时前
pytest 技术总结
开发语言·python·pytest
咖啡调调。7 小时前
使用Django框架表单
后端·python·django
亦双城的双子娴7 小时前
通过音频的pcm数据格式利用canvas绘制音频波形图
音视频·pcm·canva可画
就叫飞六吧7 小时前
如何判断你的PyTorch是GPU版还是CPU版?
人工智能·pytorch·python
pyengine9 小时前
基于pandoc的MarkDown格式与word相互转换小工具开发(pyqt5)
开发语言·python·qt·word
YuSun_WK9 小时前
配置MambaIRv2: Attentive State Space Restoration的环境
开发语言·python
Nick_zcy9 小时前
开发基于python的商品推荐系统,前端框架和后端框架的选择比较
开发语言·python·前端框架·flask·fastapi