73 lines
3.7 KiB
Bash
Executable File
73 lines
3.7 KiB
Bash
Executable File
#!/bin/sh
|
|
# Records the test trailer (tools/trailer/trailer.gd) with Godot's Movie
|
|
# Maker and encodes it, with a generated music pad under the game's sound:
|
|
#
|
|
# tools/trailer/make_trailer.sh # -> build/Cinderwild-trailer.mp4 (+ poster.jpg)
|
|
#
|
|
# Needs ffmpeg. Takes about a minute: the shots are rendered frame by frame
|
|
# at 30 fps whatever the machine's speed, so the result never stutters.
|
|
|
|
cd "$(dirname "$0")/../.." || exit 1
|
|
. tools/godot_env.sh
|
|
command -v ffmpeg >/dev/null || { echo "needs ffmpeg (brew install ffmpeg)"; exit 1; }
|
|
|
|
mkdir -p build/trailer
|
|
RAW=build/trailer/raw.avi
|
|
OUT=build/Cinderwild-trailer.mp4
|
|
rm -f "$RAW"
|
|
"$GODOT" --path . --resolution 1280x720 --fixed-fps 30 --write-movie "$RAW" \
|
|
--script tools/trailer/trailer.gd -- --no-creatures > build/trailer/render.log 2>&1
|
|
if [ ! -f "$RAW" ] || grep -q "SCRIPT ERROR" build/trailer/render.log; then
|
|
echo "recording failed: see build/trailer/render.log"; exit 1
|
|
fi
|
|
|
|
D=$(ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 "$RAW")
|
|
OUT_AT=$(echo "$D - 2.5" | bc)
|
|
# (Movie Maker records MJPEG in full-range yuvj420p: Safari and QuickTime
|
|
# won't play full-range H.264, so it's converted to standard-range yuv420p.)
|
|
# A slow A-minor-ish drone, left and right a little apart, echoed and soft.
|
|
PAD="0.06*(sin(2*PI*110*t)+0.7*sin(2*PI*164.8*t)+0.5*sin(2*PI*220*t)+0.35*sin(2*PI*261.6*t)*(0.5+0.5*sin(2*PI*0.08*t)))*(0.75+0.25*sin(2*PI*0.21*t))"
|
|
PAD_R="0.06*(sin(2*PI*110.4*t)+0.7*sin(2*PI*165.2*t)+0.5*sin(2*PI*219.6*t)+0.35*sin(2*PI*262*t)*(0.5+0.5*cos(2*PI*0.08*t)))*(0.75+0.25*cos(2*PI*0.21*t))"
|
|
# Old Tam narrates (0.6.2: tools/trailer/voice, made by make_trailer_voice.py):
|
|
# each line `at` seconds into its shot, by the shots' start times in the
|
|
# render log. The game's sound and the music dip under him.
|
|
V=tools/trailer/voice
|
|
NARR=$(python3 - "$V/lines.json" build/trailer/render.log <<'EOF'
|
|
import json, sys
|
|
lines = json.load(open(sys.argv[1]))
|
|
starts = {}
|
|
for l in open(sys.argv[2]):
|
|
p = l.split()
|
|
if len(p) == 3 and p[0] == "[shot]":
|
|
starts[int(p[1])] = float(p[2])
|
|
inputs, chains, labels = [], [], []
|
|
for n, x in enumerate(lines):
|
|
if x["shot"] not in starts:
|
|
sys.exit("no start time for shot %d in the render log" % x["shot"])
|
|
ms = int((starts[x["shot"]] + x["at"]) * 1000)
|
|
inputs.append("%s/%s" % (sys.argv[1].rsplit("/", 1)[0], x["file"]))
|
|
chains.append("[%d:a]aresample=48000,adelay=%d|%d,volume=1.7[n%d]" % (n + 2, ms, ms, n))
|
|
labels.append("[n%d]" % n)
|
|
print("\t".join(inputs))
|
|
print(";".join(chains) + ";" + "".join(labels) + "amix=inputs=%d:duration=longest:normalize=0[v]" % len(labels))
|
|
EOF
|
|
) || exit 1
|
|
NARR_IN=$(printf '%s\n' "$NARR" | head -1)
|
|
NARR_FX=$(printf '%s\n' "$NARR" | tail -1)
|
|
set --
|
|
OLDIFS=$IFS; IFS=' '
|
|
for f in $NARR_IN; do set -- "$@" -i "$f"; done
|
|
IFS=$OLDIFS
|
|
ffmpeg -v error -y -i "$RAW" -f lavfi -i "aevalsrc='$PAD|$PAD_R':s=48000:d=$D" "$@" \
|
|
-filter_complex "[1:a]lowpass=f=1800,aecho=0.8:0.7:180|340:0.35|0.25,afade=t=in:d=2.5,afade=t=out:st=$OUT_AT:d=2.5[m];[0:a]volume=0.8[g];\
|
|
$NARR_FX;[v]asplit=2[v1][vk];[g][m]amix=inputs=2:duration=first:normalize=0[bed];\
|
|
[bed][vk]sidechaincompress=threshold=0.02:ratio=6:attack=40:release=500[ducked];[ducked][v1]amix=inputs=2:duration=first:normalize=0[a]" \
|
|
-map 0:v -map "[a]" -vf "scale=in_range=pc:out_range=tv,format=yuv420p" -color_range tv \
|
|
-colorspace bt709 -color_primaries bt709 -color_trc bt709 \
|
|
-c:v libx264 -profile:v high -level 4.0 -preset slow -crf 23 -maxrate 3500k -bufsize 7000k -tag:v avc1 \
|
|
-c:a aac -b:a 160k -movflags +faststart "$OUT" || exit 1
|
|
# The title, for the download page's poster.
|
|
ffmpeg -v error -y -ss 4.5 -i "$OUT" -frames:v 1 -q:v 3 build/trailer/poster.jpg
|
|
rm -f "$RAW"
|
|
echo "built $OUT ($(du -h "$OUT" | cut -f1 | tr -d ' '), ${D%.*} s) and build/trailer/poster.jpg"
|