Often I'll want to make a video with a static image so as to upload some music to youtube.
These scripts take an image, resize and crop to full hd, make a 15 second loop of it as
an .mp4, then concatenate as many copies as necessary to make it long enough, then
add the audio. By concatenating with -c copy, we save a lot of video encoding time:
we only have to encode 15s of video rather than having to encode the entire length of the
audio.
If we have a short .mp4 loop, the second script loop_music_vid is all we need.
img2loop
#!/bin/bash
T="${T-15}"
I="$1"
O="$2"
C="${C-libx264}"
if [ ! -f "$I" ]; then
echo "$I does not exist"
exit 1
fi
if [ -z "$O" ]; then
O="${I%.*}.mp4"
fi
ffmpeg -loop 1 -i "$I" -c:v "$C" -r 24 -t "$T" -pix_fmt yuv420p "$O"
echo "$O"
loop music vid
Usage:
loop_music_vid <loop> [<audio> ...]
#!/usr/bin/env python3
import argparse
import subprocess
import re
import os
def hms(x):
mat = re.match(r"(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s?)?$",x)
if not mat:
return None
d, h, m, s = ( ( int(x) if x is not None else 0 ) for x in mat.groups() )
return s + 60 * ( m + 60 * ( h + 24 * d ) )
def parse_res(x):
d = {
"1080p": "1920:1080",
"720p": "1280:720",
"540p": "960:540"
}
if m := re.match(r"(\d+)[x:](\d+)$",x):
w,h = ( int(y) for y in m.groups() )
return f"{w}:{h}"
elif x in d:
return d[x]
else:
return None
def main():
parser = argparse.ArgumentParser(prog="make_video_loop",
description="Take an image and make an mp4 loop of the specified length and resolution")
parser.add_argument("-g","--geometry",help="width:height or 720p or 1080p",default="1280:720")
parser.add_argument("-t","--duration",default="15")
parser.add_argument("files",nargs="+")
ns = parser.parse_args()
ifns = ns.files
dur = hms(ns.duration)
if dur is None:
print(f"Invalid hms",ns.duration)
exit(1)
res = parse_res(ns.geometry)
if res is None:
print("Unrecognised res:",ns.geometry)
exit(1)
for ifn in ifns:
xs = ifn.split(".")
ext = xs[-1]
stem = ifn[:-(len(ext)+1)]
if not ext in [ "png", "jpg", "jpeg" ]:
print(f"Image {ifn} must be png, jpg, or jpeg")
continue
ofn = f"{stem}_{dur}s.mp4"
cmd = ["ffmpeg","-loop","1","-i",ifn,"-c:v","libx264","-t",str(dur),"-pix_fmt","yuv420p","-vf",f"scale={res}",ofn]
if os.path.exists(ofn):
print(f"Already exists",ofn)
continue
subprocess.run(cmd)
if __name__ == "__main__":
exit(main())
Combine
ffmpeg -i video.mp4 -i audio.wav -c:v copy -map 0:0 -map 1:0 out.mp4