Skip to content

Unire più video in un modello/layout con Python FFMPEG?

Il nostro team di scrittura ha passato ore a cercare la risposta alla tua domanda, ti offriamo la risposta quindi speriamo di esserti di grande supporto.

Soluzione:

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

Ciò che fa è scalare video.mp4 in modo che sia alto 1080 pixel e autoscala la larghezza. left.jpg e right.png vengono scalati per occupare il resto, quindi il risultato è 1920x1080.

Questo è un semplice esempio e non funzionerà per tutti gli input, ad esempio se l'opzione video.mp4 è più grande di 1920, ma è possibile risolvere il problema utilizzando le espressioni aritmetiche.

$width e $height si riferiscono alla dimensione di video.mp4. Vedere Ottenere dimensioni video / risoluzione / larghezza x altezza da ffmpeg per un metodo Python friendly che utilizza JSON o XML.

Vedere la documentazione per i filtri scale, crop, hstack, drawtext, overlay e format.

Un metodo molto più semplice consiste nell'aggiungere barre colorate al posto di immagini arbitrarie. Vedere Ridimensionamento dei video con ffmpeg per adattarli a una dimensione specifica.

Cambiare il testo al volo aggiornando atomicamente text.txt. Oppure usare il filtro sottotitoli invece di drawtext se si vuole che il testo cambi a determinati orari (non si è specificato cosa serve).

Risposte correlate:

  • Come posizionare overlay/watermark/logo con ffmpeg?
  • Come posizionare il testo di drawtext?
  • Impilare verticalmente o orizzontalmente (mosaico) diversi video usando ffmpeg?
  • Ottenere dimensioni video / risoluzione / larghezza x altezza da ffmpeg

L'ho fatto. Il codice può essere usato come programma a riga di comando o come modulo. Per saperne di più sull'uso a riga di comando, richiamatelo con l'opzione --help. Per l'uso del modulo, importare il file make_video nel codice (o copiarlo e incollarlo) e passargli gli argomenti appropriati. Ho incluso una schermata di ciò che il mio script ha prodotto con del materiale di esempio e, naturalmente, il codice.
Codice:

#!/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, "scale", "%dx%d" % (boarderwidth, 1080))
    right_boarderstr = ffmpeg.input(right_boarder) # open right boarder
    right_boarderstr = ffmpeg.filter(right_boarderstr, "scale", "%dx%d" % (boarderwidth, 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, "scale", "%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)", nargs="*", default="left_boarder.png")
    argp.add_argument("--right-boarders", help="paths to images for right boarders (default: right_boarder.png)", nargs="*", default="right_boarder.png")
    argp.add_argument("--picture-files", nargs="*", 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", nargs="*", 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)", choices=["top", "center", "bottom"], default="bottom")
    argp.add_argument("--outputs", nargs="*", 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.left_boarders[i], args.right_boarders[i], args.picture_files[i], args.picture_pos, args.text_files[i], args.text_pos, args.outputs[i])
        except Exception as e:
            print(e)

if __name__ == "__main__":
    main()

Esempio di utilizzo diretto come script:

$ ./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

Come valori per le opzioni ho preso i valori predefiniti. Se si omettono le opzioni, verranno utilizzati i valori predefiniti e se uno dei file non viene trovato, verrà visualizzato un messaggio di errore.

Immagine: enter image description here

Recensioni e valutazioni

Vi invitiamo a confermare la nostra funzione lasciando un commento o lasciando un punteggio vi diamo il benvenuto.



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.