Dup Ver Goto 📝

Make A Static Music Video

PT2/media/ffmpeg ffmpeg video does not exist
To
101 lines, 375 words, 2644 chars Page 'MakeStaticMusicVideo' does not exist.

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