官方api
官方github
参数说明
import ffmpeg
from pathlib import Path
class VideoEd:
image_extend = [".jpg", ".jpeg", ".png", ".tif", ".tiff"]
def extract_video(self, input_file, output_dir, output_ext='png', fps=0):
'''
视频每帧截图
:param input_file: 视频文件
:param output_dir: 输出目录
:param output_ext: 截取的图片后缀名称 :'png' or 'jpg'
:param fps: 每帧的fps
:return: True
'''
input_file_path = Path(input_file)
output_path = Path(output_dir)
if not input_file_path.exists():
raise Exception('input file not find')
if not output_path.exists():
output_path.mkdir(exist_ok=True)
job = ffmpeg.input(str(input_file_path))
kwargs = {'pix_fmt': 'rgb24'}
if fps != 0:
kwargs.update({'r': str(fps)})
if output_ext == 'jpg':
kwargs.update({'q:v': '2'}) # highest quality for jpg
job = job.output(str(output_path / ('%5d.' + output_ext)), **kwargs)
try:
job = job.run()
except:
raise Exception("ffmpeg fail, job commandline:" + str(job.compile()))
def cut_video(self, input_file, from_time, to_time, output_file=None, audio_track_id=None, kwargs={}):
'''
剪切视频
:param input_file: 视频文件路径
:param from_time: 开始时间 格式 00:00:00.000
:param to_time: 结束时间 格式 00:02:00.000
:param output_file:保存文件路径
:param audio_track_id: ?
:param kwargs: 其他参数
:return: None
'''
input_file = Path(input_file)
if input_file is None:
raise Exception("input_file not found.")
if output_file is None:
output_file = input_file.parent / (input_file.stem + "_cut" + input_file.suffix)
if audio_track_id is None:
audio_track_id = 0
job = ffmpeg.input(str(input_file), ss=from_time, to=to_time)
job_v = job['v:0']
job_a = job['a:' + str(audio_track_id) + '?']
job = ffmpeg.output(job_v, job_a, str(output_file), **kwargs).overwrite_output()
try:
job = job.run()
except:
raise Exception("ffmpeg fail, job commandline:" + str(job.compile()))
def video_from_sequence(self, input_dir, output_file, fps=None, bitrate=None, logo=None, position=(0, 0), kwargs={}):
'''
根据图片生成视频文件
:param input_dir: 图片所在目录
:param output_file: 生成的视频路径
:param fps: 每秒帧数
:param bitrate: 比特率
:param logo: 是否添加水印
:param position: 水印位置(x, y)
x > 0: 距离左边缘x像素 x < 0: 距离右边缘x像素
y > 0: 距离上边缘y像素 y < 0: 距离下边缘y像素
例如:
(10,10): 左上各10
(10, -10): 左下各10
(-10, 10): 右上各10
(-10, -10): 右下各10
:return: None
'''
input_path = Path(input_dir)
output_file_path = Path(output_file)
if not input_path.exists():
raise Exception("input path not found.")
if not output_file_path.parent.exists():
output_file_path.parent.mkdir(parents=True, exist_ok=True)
input_image_paths = self._all_input_image_paths(input_path)
fps = (fps, 25)[fps is None]
i_in = ffmpeg.input('pipe:', format='image2pipe', r=fps)
output_args = [i_in]
output_args += [str(output_file_path)]
output_kwargs = {}
output_kwargs.update(self._format_logo_command_param(logo, position)) # 添加水印
output_kwargs.update({
"b:v": "%dM" % (max(1, bitrate or 16)), # 比特率
"pix_fmt": "yuv420p", # ?
"r": fps, # 每秒帧数 默认25
})
output_kwargs.update(kwargs)
job = (ffmpeg.output(*output_args, **output_kwargs).overwrite_output())
try:
job_run = job.run_async(pipe_stdin=True)
for image_path in input_image_paths:
with open(image_path, "rb") as f:
image_bytes = f.read()
job_run.stdin.write(image_bytes)
job_run.stdin.close()
job_run.wait()
except:
raise Exception("ffmpeg fail, job commandline:" + str(job.compile()))
def _all_input_image_paths(self, input_path):
'''
生成最终视频的每一帧图片(有序)
:param input_path: 文件路径
:return: None
'''
return sorted([str(i) for i in input_path.iterdir() if i.is_file() and i.suffix in self.image_extend])
def _format_logo_command_param(self, logopath, position):
'''
生成ffmpeg水印命令行参数
:param logopath: logo路径
:param position: 水印的相对位置
:return: None
'''
if type(position) != tuple or position.__len__() != 2 or type(position[0]) != int or type(position[1]) != int:
raise Exception('position error, for example: (0, 0)')
x = (position[0], 'main_w-overlay_w-{}'.format(abs(position[0])))[position[0] < 0]
y = (position[1], 'main_h-overlay_h-{}'.format(abs(position[1])))[position[1] < 0]
return {'vf': 'movie={} [wm];[in][wm]overlay={}:{}[out]'.format(logopath, x, y)}
if __name__ == '__main__':
# demo1
# input_path = '/Users/jemes/workspace/simage/huge1.mp4'
# output_path = '/Users/jemes/workspace/simage/shortcut'
# VideoEd().extract_video(input_path, output_path)
# demo2
# input_path = '/Users/jemes/workspace/simage/huge1.mp4'
# output_file = '/Users/jemes/workspace/simage/huge_cut.mp4'
# VideoEd().cut_video(input_path, from_time="00:00:00.000", to_time="00:00:10.000", output_file=output_file)
# demo3
# input_path = '/Users/jemes/workspace/simage/shortcut'
# output_path = '/Users/jemes/workspace/simage/huge2_res.mp4'
# logo = '/Users/jemes/workspace/simage/logo.png'
# VideoEd().video_from_sequence(input_path, output_path, logo=logo, position=(-20, -20))
pass