aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/build_blender.py
blob: 1a80d976fc8d87088b493a01d36eb9df83dc5b08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# -*- coding: utf-8 -*-
"""Builds the final video of the scenes made in Blender.

Composites in a single ffmpeg pass:
    dotted background  +  PNG sequence with alpha (3D)  +  Manim overlay  +  audio
The overlay carries the chip, progress bar and subtitles, so the style comes
from the same code as the Manim videos.
"""
import glob, os, subprocess, sys

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(ROOT, "scripts"))
import build_video

FPS = 30


def compose(name, add_music=True):
    background = os.path.join(ROOT, "media", "images", "overlay", "fondo.png")
    folder = os.path.join(ROOT, "render", name)
    seq = os.path.join(folder, "f%04d.png")
    overlay = os.path.join(ROOT, "media", "videos", "overlay", "1920p30", f"{name}.mov")
    for p in (background, overlay):
        if not os.path.exists(p):
            raise SystemExit(f"missing {p}")
    n = len(glob.glob(os.path.join(folder, "f*.png")))
    if n < 30:
        raise SystemExit(f"[{name}] only {n} frames rendered")

    wav = build_video.build_track(name, add_music)
    out = os.path.join(ROOT, "out", f"{name}.mp4")
    cmd = [
        "ffmpeg", "-v", "error", "-y",
        "-loop", "1", "-framerate", str(FPS), "-i", background,
        "-framerate", str(FPS), "-start_number", "1", "-i", seq,
        "-i", overlay,
        "-i", wav,
        "-filter_complex",
        "[0:v][1:v]overlay=format=auto[a];[a][2:v]overlay=format=auto,format=yuv420p[v]",
        "-map", "[v]", "-map", "3:a",
        "-filter:a", "loudnorm=I=-14:TP=-1.5:LRA=11,aresample=48000",
        "-frames:v", str(n),
        "-c:v", "libx264", "-profile:v", "high", "-pix_fmt", "yuv420p",
        "-preset", "slow", "-crf", "20", "-r", str(FPS),
        "-c:a", "aac", "-b:a", "192k", "-ac", "2",
        "-movflags", "+faststart", "-shortest", out,
    ]
    subprocess.run(cmd, check=True)
    d = subprocess.run(["ffprobe", "-v", "error", "-show_entries", "format=duration",
                        "-of", "csv=p=0", out], capture_output=True, text=True).stdout.strip()
    print(f"[{name}] {out}  {float(d):.1f}s  {os.path.getsize(out)/1e6:.1f} MB  "
          f"({n} frames 3D)", flush=True)
    return out


if __name__ == "__main__":
    for n in (sys.argv[1:] or ["bolapeluda", "caos", "diferencial"]):
        compose(n)