-
Notifications
You must be signed in to change notification settings - Fork 0
/
ugoira-to-mp4
executable file
·64 lines (56 loc) · 2.61 KB
/
ugoira-to-mp4
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
59
60
61
62
63
64
#!/bin/bash
set -eo pipefail
DEFAULT_CRF=0 # Lossless
if [[ $# == 0 || $1 =~ ^(--help|-h)$ || ! -f "$1/animation.json" ]]; then
cat >&2 <<EOF
Usage: ugoira-to-mp4 <ugoira directory>
Outputs an mp4 in the current directory with the name of the ugoira.
CRF environment variable controls crf, defaults to $DEFAULT_CRF.
EOF
exit 1
fi
out_dir=$PWD
cd "$1"
output="$out_dir/$(basename "$PWD").mp4"
# Read animation.json and convert to concat demuxer file, converting any images
# with non-sRGB color profiles to sRGB to avoid colors getting screwed up
animation=$(jq -r '.[]|"\(.file)\t\(.delay)"' animation.json)
last_file=
while IFS=$'\t' read -r file duration; do
if convert-to-srgb -x "$file" | sed 's/^/\c[[2mconvert-to-srgb:\c[[m /' >&2; then
file="${file%.*}.srgb.png"
fi
printf "file '%s'\nduration %sms\n" "$file" "$duration"
last_file=$file
done <<<"$animation" > animation.txt
# For whatever reason, the last frame must be specified twice (bug in concat
# demuxer?). Can confirm correct (or close enough) frame times using:
# ffprobe *.mp4 -show_frames | grep pkt_duration_time=
printf "file '%s'\n" "$last_file" >> animation.txt
# VFR causes glitchiness in XnView, but when using CFR ffmpeg doesn't seem to be
# able to figure out the input framerate on its own and just defaults to 25 fps.
# This finds the greatest common divisor of the frame delays, which represents
# the largest constant interval on which each frame can be repeated without
# changing the timings, and converts it to frames per second, capped at 60 fps.
frame_delays=$(jq -c '[.[].delay]|unique' animation.json)
fps=$(python -c "import math; print(min(60, 1000 / math.gcd(*${frame_delays})))")
# Switched from AV1 to H.264 here for better image viewer support; XnView MP
# still doesn't support animated AVIFs, and while it does support video files,
# AV1 in an mkv/mp4/webm container typically causes it to freeze. APNG using
# apngasm is decent in terms of file size but loads slow. Chroma subsampling
# needs to be 4:2:0; 4:4:4 causes subtle color shifting in Chrome. The scale
# filter below seems to be the final secret sauce to preserving colors (along
# with making sure the inputs' color profiles aren't weird). -colorspace and
# -color_trc didn't seem to do anything, but I'm leaving them in just in case.
ffmpeg -hide_banner -y \
-colorspace bt709 -color_trc iec61966-2-1 \
-safe 0 -f concat -i animation.txt \
-vf 'scale=in_color_matrix=bt709:out_color_matrix=bt709,
crop=trunc(iw/2)*2:trunc(ih/2)*2' \
-c:v libx264 \
-pix_fmt yuv420p \
-crf "${CRF:-$DEFAULT_CRF}" \
-preset slow \
-r "$fps" \
-movflags +faststart \
"$output"