Python 调用 FFMPEG

news/2024/10/18 18:13:25/文章来源:https://www.cnblogs.com/shouwangrenjian/p/18470342

Python 调用 FFMPEG

从 Python 中调用 FFMPEG 不是很方便,只有通过 subprocess 或 os.system 等执行 FFMPEG 命令。也有 ffmpeg-python 这种已经封装好的三方库,但本着学习的目的,还是自己写了一个简单的 Demo 来实现获取视频信息和视频转码。

Demo

需要先下载 FFMPEG 的可执行文件放入同级的 ffmpeg 目录下。

"""
视频操作ffmpeg 命令
查看 FFMPEG 支持的编解码器:ffmpeg -codecs
查看 FFMPEG 支持的封装格式:ffmpeg -formats
"""
import dataclasses
import os
import platform
import queue
import re
import subprocess
import threading
from pathlib import Path
from typing import Union, Optional# FFMPEG 路径
FFMPEG_DIR = os.path.join(os.path.dirname(__file__), 'ffmpeg')
if platform.system() == 'Linux':FFMPEG_EXE = os.path.join(FFMPEG_DIR, 'ffmpeg')ENCODING = 'UTF-8'
else:FFMPEG_EXE = os.path.join(FFMPEG_DIR, 'ffmpeg.exe')ENCODING = 'GBK'@dataclasses.dataclass
class VideoInfo:"""视频信息"""video_name: strduration_str: str  # ffmpeg 输出的原始时间duration_seconds: float  # 转换为秒数bitrate: strencoding: strwidth: strheight: strfps: intdef __str__(self) -> str:return (f'时长:{self.duration_str} 'f'编码:{self.encoding} 'f'分辨率:{self.width}x{self.height} 'f'帧率:{self.fps}')def parse_ffmpeg_progress(line: str):"""解析 ffmpeg 输出中的进度信息,并转换为秒数"""match = re.match(r'frame.*time=(\d+:\d+:\d+\.\d+)',line, flags=re.DOTALL)if match:# 将 "HH:MM:SS.ms" 格式转换为秒数time_str = match.group(1)hours, minutes, seconds = map(float, time_str.split(':'))return hours * 3600 + minutes * 60 + secondsreturn Nonedef stream_reader(popen: subprocess.Popen, total_duration: int, progress_q: queue.Queue):"""读取 stderr 输出并计算进度百分比:param popen: 输入流对象:param total_duration: 总时长(秒):param progress_q: 进度队列"""buffer = ''while True:chunk = popen.stderr.read(256)if not chunk:breakbuffer += chunk.decode()# 检查是否有错误输出,停止子进程if 'Error' in buffer:print(buffer)if popen.poll() is None:popen.kill()progress_q.put(-1)# 查找 '\r' 代表的一行结束elif '\r' in buffer:# 按 '\r' 分割并获取最新的进度行lines = buffer.split('\r')buffer = lines[-1]  # 保留缓冲区中最后一部分(不完整的行)progress_output = lines[-2]  # 获取最后完整的一行# 解析进度并计算百分比current_time = parse_ffmpeg_progress(progress_output)if current_time:percent = (current_time / total_duration) * 100# print(f'Progress: {percent:.2f}%')progress_q.put(percent)progress_q.put(100)class VideoOperator:"""视频转换器:param video_path: 视频路径"""# 解析 FFMPEG 输出的视频信息正则表达式VideoInfoReStr = (r'.+Duration: (?P<duration_str>\d+:\d+:\d+.\d+), start.+'r'bitrate: (?P<bitrate>\d+) kb/s.+'r'Video: (?P<encoding>.+?),.+, (?P<width>\d+)x(?P<height>\d+)'r'.+, (?P<fps>\d+.?\d*) fps,.+')def __init__(self, video_path: Union[str, Path]):if not os.path.exists(FFMPEG_EXE):raise FileNotFoundError(f"FFmpeg executable not found at {FFMPEG_EXE}")if not os.path.exists(video_path):raise FileNotFoundError(f"Source video not found: {video_path}")self.source_video_path = video_pathself.video_info = self.get_video_info()self.progress_q = queue.Queue()  # 创建一个队列接收进度信息def get_video_info(self) -> Optional[VideoInfo]:"""获取视频信息"""cmd = [FFMPEG_EXE, '-i', self.source_video_path, '-hide_banner']p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)_, stderr_data = p.communicate()  # FFMPEG 的所有输出信息都在 err 中video_info_str = stderr_data.decode()# print(video_info_str)match_res = re.match(self.VideoInfoReStr,video_info_str, flags=re.DOTALL)if match_res:# 计算视频时长hours, minutes, seconds = map(float, match_res.groupdict()['duration_str'].split(':'))duration_time = hours * 3600 + minutes * 60 + secondsvideo_info = VideoInfo(video_name=os.path.basename(self.source_video_path),duration_seconds=duration_time,**match_res.groupdict())return video_inforeturn Nonedef convert_video(self, out_video_path: Union[str, Path],video_decoder: str = None,out_video_encoder: str = None,out_video_format: str = None,out_video_bitrate: int = None,out_video_fps: str = None,out_video_res: str = None):"""视频转换:param out_video_path: 输出视频路径:param video_decoder: 输入视频解码器:param out_video_encoder: 输出视频编码器:param out_video_format: 输出视频格式:param out_video_bitrate: 输出视频码率:param out_video_fps: 输出视频帧率:param out_video_res: 输出视频分辨率:return: """cmd = [FFMPEG_EXE]if video_decoder:cmd.extend(['-c:v', video_decoder])  # h264_qsvcmd.extend(['-i', self.source_video_path, '-hide_banner', '-y'])if out_video_encoder:cmd.extend(['-c:v', out_video_encoder])if out_video_format:cmd.extend(['-f', out_video_format])if out_video_bitrate:cmd.extend(['-b:v', f'{out_video_bitrate}k'])if out_video_fps:cmd.extend(['-r', out_video_fps])if out_video_res:cmd.extend(['-s', out_video_res])cmd.append(out_video_path)# print(cmd)# print(' '.join(cmd))p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)stderr_thread = threading.Thread(target=stream_reader,args=(p, self.video_info.duration_seconds, self.progress_q))stderr_thread.start()if __name__ == '__main__':video_path = ‘'  # 视频路径vo = VideoOperator(video_path)print(vo.video_info)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/816992.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Fluid Flux2.0海浪原理拆解

【USparkle专栏】如果你深怀绝技,爱“搞点研究”,乐于分享也博采众长,我们期待你的加入,让智慧的火花碰撞交织,让知识的传递生生不息!大概一年前,在油管上看到这个视频: Fluid Flux 2.0 - Coastline [Unreal Engine 5]除了效果很好,更重要的是看到简介中写着:“并非实…

PHP将整形数字转为Excel下标

1、背景 这两天在接到一个需求,需要导出一个班级所有学员的所有成绩,在最后excel表处理的时候发现导出的列超过了26列,后面会出现AA之类的下标,所以写了一个函数把数字整型转为Excel对应的下标。 2、转换函数/*** @Notes:将整数转为excel对应的列标* @Function int_to_chr*…

18. 模块

一、什么是模块模块化 指将一个完成的程序分解为一个一个小的模块。通过将模块组合,来搭建一个完整的程序。如果不采用模块化,那么所有的代码将统一保存到一个文件中。采用模块化后,将程序分别编写到多个文件中。使用模块化后,我们可以把代码进行复用,这方面后序的开发和维…

csp-s模拟12

csp-s模拟12\(T1\) T2918. 小 h 的几何 \(100pts\)对于任意三角形,均有其三条边的中点、三条高的垂足、三个顶点与垂心连线的中点,这九个点在一个圆上。观察样例可知,对于单位圆上 \(\triangle ABC\) 的三个顶点 \(A(x_{a},y_{a}),B(x_{b},y_{b}),C(x_{c},y_{c})\) ,其九点…

Android8,reactnative中webView传的token,vue中获取不到是怎么回事?

rn代码是这样的,要在vue页面获取到tokenvue中是这样写的,安卓9以上都能得到,就是8获取不到有没有大佬帮忙看看,跪谢!!!!

实时同步服务

1 实时同步应用场景通过rsync+定时任务实现定时备份/同步 对于NFS我们需要进行实时同步2 实时同步工具选型实时同步工具 特点 选型inotify工具+脚本inotify监控指定的目录,监控 目录下是否有变化,显示变化了的文件. 通过rsync服务端与客户端传送,书写脚本.不推荐,有性能问题.se…

五款免费报表工具推荐:山海鲸报表、Tableau 等优劣对比

在当今数据驱动的时代,报表工具已经成为各类企业进行决策和管理的重要工具。无论是大中型企业还是小微企业,能够快速、高效地生成可视化报表,洞察业务运营情况,已经成为提升竞争力的关键。今天为大家挑选了5款非常优秀的报表软件,并且详细分析了它们的优缺点,希望能够帮助…

DevExpress隐藏列表中显示的加号+

GridView1.OptionsDetail.EnableMasterViewMode = False

人大金仓kingbase部署

环境准备: 系统版本:CentOS Linux release 7.9.2009 (Core) 硬件配置:4C4G200G 安装包准备:KingbaseES_V008R006C008B0020_Lin64_install.iso https://www.kingbase.com.cn/xzzx/index.htmlicense准备:license_企业版.zip https://www.kingbase.com.cn/xzzx/index.htm计划…

从深海探测到海洋强国:数字孪生助力海洋装备跨越式发展

浮力调节系统作为涉及潜水的海洋设备的关键部件,能够为潜水器提供稳定悬浮的深度控制,并能根据工作深度的不同通过改变浮力来带动潜水器上浮或下潜。海洋广袤无垠,蕴藏着丰富的资源。近现代以来,人类使用各种手段探索海洋探索,广袤无垠的海洋与人类的生活越来越紧密,至少…

python - 分享绕过验证码登录的方法

一、通过webdriver启动浏览器:二、添加cookie:三、切换到目标地址: # This is a sample Python script. from selenium import webdriver import time # Press Shift+F10 to execute it or replace it with your code. # Press Double Shift to search everywhere for clas…