微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 Python FFMPEG 在模板/布局中合并多个视频?

如何解决使用 Python FFMPEG 在模板/布局中合并多个视频?

我目前正在尝试使用 Python library of FFMPEG 编辑视频。我正在处理多种文件格式,准确地说是 .mp4.png 和文本输入 (.txt)。目标是在“布局”中嵌入不同的视频文件 - 出于演示目的,我尝试设计一个示例图片

Example

输出应该是具有以下元素的 1920x1080 .mp4 文件

  • 元素 3 是视频本身(由于它是手机屏幕录制,所以大约是那里显示的大小)
  • Element 12 是“边框”,即静态图片 (?)
  • Element 4 代表定期变化的文本 - 通过 python 脚本输入(可能从 .txt 文件中读取)
  • Element 5 描绘了 .png.svg 或类似的;通常是广义上的“图片”。

我想要实现的是创建一种模板文件,我“只”需要在其中输入不同的 .mp4.png 文件,以及文本和最后我会收到一个 .mp4 文件,而我的 Python 脚本作为导航器将数据包发送到 FFMPEG 以处理视频本身。

我深入研究了 FFMPEG 库以及特定于 Python 的存储库,但找不到这样的选项。有很多文章解释了“频道布局”的用法(尽管这些似乎不适合我的需要)。

如果有人想尝试相同的版本:

  • python --versionPython 3.7.3
  • pip show ffmpeg版本:1.4这是最新的;在题外话:使用 FFMPEG 不是必须的,不过我更喜欢使用这个库如果它不提供我正在寻找的功能,如果有人提出其他建议,我将不胜感激)

解决方法

enter image description here

ffmpeg -i left.jpg -i video.mp4 -i right.png -i logo.png -filter_complex "[0]scale=(1920-1080*($width/$height))/2:1080:force_original_aspect_ratio=increase,crop=(1920-1080*($width/$height))/2:1080[left];[1]scale=-2:1080[main];[2]scale=(1920-1080*($width/$height))/2:1080:force_original_aspect_ratio=increase,crop=(1920-1080*($width/$height))/2:1080[right];[left][main][right]hstack=inputs=3[bg];[bg][3]overlay=5:main_h-overlay_h-5:format=auto,drawtext=textfile=text.txt:reload=1:x=w-tw-10:y=h-th-10,format=yuv420p" -movflags +faststart output.mp4

这样做是缩放 video.mp4 使其高 1080 像素并自动缩放宽度。 left.jpgright.png 被缩放以占据余数,因此结果为 1920x1080。

这是一个简单的示例,不适用于所有输入,例如 video.mp4 自动缩放宽度大于 1920,但您可以使用 arithmetic expressions 来处理。

$width$height 指的是 video.mp4 的大小。有关使用 JSON 或 XML 的 Python 友好方法,请参阅 Getting video dimension / resolution / width x height from ffmpeg

请参阅有关 scalecrophstackdrawtextoverlayformat 过滤器的文档。

一个更简单的方法是添加彩色条而不是任意图像。见Resizing videos with ffmpeg to fit a specific size

通过原子更新 text.txt 动态更改文本。或者,如果您希望文本在特定时间戳(您没有指定所需内容)发生更改,则使用 subtitles 过滤器而不是 drawtext。

相关答案:

,

我已经做到了。该代码可用作命令行程序或模块。要了解有关命令行用法的更多信息,请使用 --help 选项调用它。对于模块使用,在您的代码中导入 make_video 函数(或复制粘贴它),并将适当的参数传递给它。我已经包含了我的脚本使用一些示例材料生成的屏幕截图,当然还有代码。 代码:

#!/usr/bin/python3
#-*-coding: utf-8-*-

import sys,argparse,ffmpeg,os

def make_video(video,left_boarder,right_boarder,picture_file,picture_pos,text_file,text_pos,output):
    videoprobe = ffmpeg.probe(video)
    videowidth,videoheight = videoprobe["streams"][0]["width"],videoprobe["streams"][0]["height"] # get width of main video
    scale = (1080 / videoheight)
    videowidth *= scale
    videoheight *= scale
    videostr = ffmpeg.input(video) # open main video
    audiostr = videostr.audio
    videostr = ffmpeg.filter(videostr,"scale","%dx%d" %(videowidth,videoheight))
    videostr = ffmpeg.filter(videostr,"pad",1920,1080,"(ow-iw)/2","(oh-ih)/2")
    videostr = videostr.split()
    boarderwidth = (1920 - videowidth) / 2 # calculate width of boarders
    left_boarderstr = ffmpeg.input(left_boarder) # open left boarder and scale it
    left_boarderstr = ffmpeg.filter(left_boarderstr,"%dx%d" % (boarderwidth,1080))
    right_boarderstr = ffmpeg.input(right_boarder) # open right boarder
    right_boarderstr = ffmpeg.filter(right_boarderstr,1080))
    picturewidth = boarderwidth - 100 # calculate width of picture
    pictureheight = (1080 / 3) - 100 # calculate height of picture
    picturestr = ffmpeg.input(picture_file) # open picture and scale it (there will be a padding of 100 px around it)
    picturestr = ffmpeg.filter(picturestr,"%dx%d" % (picturewidth,pictureheight))
    videostr = ffmpeg.overlay(videostr[0],left_boarderstr,x=0,y=0) # add left boarder
    videostr = ffmpeg.overlay(videostr,right_boarderstr,x=boarderwidth + videowidth,y=0) #add right boarder
    picture_y = (((1080 / 3) * 2) + 50) # calculate picture y position for bottom alignment
    if picture_pos == "top":
        picture_y = 50
    elif picture_pos == "center":
        picture_y = (1080 / 3) + 50
    videostr = ffmpeg.overlay(videostr,picturestr,x=50,y=picture_y)
    text_x = (1920 - boarderwidth) + 50
    text_y = ((1080 / 3) * 2) + 50
    if text_pos == "center":
        text_y = (1080 / 3) + 50
    elif text_pos == "top":
        text_y = 50
    videostr = ffmpeg.drawtext(videostr,textfile=text_file,reload=1,x=text_x,y=text_y,fontsize=50)
    videostr = ffmpeg.output(videostr,audiostr,output)
    ffmpeg.run(videostr)

def main():
    #create ArgumentParser and add options to it
    argp = argparse.ArgumentParser(prog="ffmpeg-template")
    argp.add_argument("--videos",help="paths to main videos (default: video.mp4)",nargs="*",default="video.mp4")
    argp.add_argument("--left-boarders",help="paths to images for left boarders (default: left_boarder.png)",default="left_boarder.png")
    argp.add_argument("--right-boarders",help="paths to images for right boarders (default: right_boarder.png)",default="right_boarder.png")
    argp.add_argument("--picture-files",help="paths to pictures (default: picture.png)",default="picture.png")
    argp.add_argument("--picture-pos",help="where to put the pictures (default: bottom)",choices=["top","center","bottom"],default="bottom")
    argp.add_argument("--text-files",help="paths to files with text (default: text.txt)",default="text.txt")
    argp.add_argument("--text-pos",help="where to put the texts (default: bottom)",default="bottom")
    argp.add_argument("--outputs",help="paths to outputfiles (default: out.mp4)",default="out.mp4")
    args = argp.parse_args()
    # if only one file was provided,put it into a list (else,later,every letter of the filename will be treated as a filename)
    if type(args.videos) == str:
        args.videos = [args.videos]
    if type(args.left_boarders) == str:
        args.left_boarders = [args.left_boarders]
    if type(args.right_boarders) == str:
        args.right_boarders = [args.right_boarders]
    if type(args.picture_files) == str:
        args.picture_files = [args.picture_files]
    if type(args.text_files) == str:
        args.text_files = [args.text_files]
    if type(args.outputs) == str:
        args.outputs = [args.outputs]

    for i in (range(0,min(len(args.videos),len(args.left_boarders),len(args.right_boarders),len(args.picture_files),len(args.text_files),len(args.outputs))) or [0]):
        print("Info : merging video %s,boarders %s %s,picture %s and textfile %s into %s" % (args.videos[i],args.left_boarders[i],args.right_boarders[i],args.picture_files[i],args.text_files[i],args.outputs[i]))
        # check if all files provided with the options exist
        if not os.path.isfile(args.videos[i]):
            print("Error : video %s was not found" % args.videos[i])
            continue
        if not os.path.isfile(args.left_boarders[i]):
            print("Error : left boarder %s was not found" % args.left_boarders[i])
            continue
        if not os.path.isfile(args.right_boarders[i]):
            print("Error : rightt boarder %s was not found" % args.right_boarders[i])
            continue
        if not os.path.isfile(args.picture_files[i]):
            print("Error : picture %s was not found" % args.picture_files[i])
            continue
        if not os.path.isfile(args.text_files[i]):
            print("Error : textfile %s was not found" % args.text_files[i])
            continue
        try:
            make_video(args.videos[i],args.picture_pos,args.text_pos,args.outputs[i])
        except Exception as e:
            print(e)

if __name__ == "__main__":
    main()

直接用作脚本的示例:

$ ./ffmpeg-template --videos input1.mp4 inout2.mp4 --left-boarders left_boarder1.png left_boarder2.png --right-boarders right_boarder1.png right_boarder2.png --picture-files picture1.png picture2.png --text-files text1.txt text2.png --outputs out1.mp4 out2.mp4 --picture-pos bottom --text-pos bottom

作为选项的值,我采用了默认值。如果省略这些选项,将使用这些默认值,如果找不到其中一个文件,则会显示错误消息。

图片:enter image description here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。