-
Notifications
You must be signed in to change notification settings - Fork 135
/
checkerboard.sh
executable file
·122 lines (100 loc) · 5.56 KB
/
checkerboard.sh
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/bin/bash
#
# ffmpeg video slideshow script with checkerboard transition v4 (25.05.2019)
#
# Copyright (c) 2017-2019, Taner Sener (https://github.com/tanersener)
#
# This work is licensed under the terms of the MIT license. For a copy, see <https://opensource.org/licenses/MIT>.
#
# SCRIPT OPTIONS - CAN BE MODIFIED
WIDTH=1280
HEIGHT=720
FPS=30
TRANSITION_DURATION=1
IMAGE_DURATION=2
SCREEN_MODE=2 # 1=CENTER, 2=CROP, 3=SCALE, 4=BLUR
CELL_SIZE=64
BACKGROUND_COLOR="#00000000"
IFS=$'\t\n' # REQUIRED TO SUPPORT SPACES IN FILE NAMES
# FILE OPTIONS
# FILES=`find ../media/*.jpg | sort -r` # USE ALL IMAGES UNDER THE media FOLDER SORTED
# FILES=('../media/1.jpg' '../media/2.jpg') # USE ONLY THESE IMAGE FILES
FILES=`find ../media/*.jpg` # USE ALL IMAGES UNDER THE media FOLDER
############################
# DO NO MODIFY LINES BELOW
############################
# CALCULATE LENGTH MANUALLY
let IMAGE_COUNT=0
for IMAGE in ${FILES[@]}; do (( IMAGE_COUNT+=1 )); done
if [[ ${IMAGE_COUNT} -lt 2 ]]; then
echo "Error: media folder should contain at least two images"
exit 1;
fi
# INTERNAL VARIABLES
TRANSITION_FRAME_COUNT=$(( TRANSITION_DURATION*FPS ))
IMAGE_FRAME_COUNT=$(( IMAGE_DURATION*FPS ))
TOTAL_DURATION=$(( (IMAGE_DURATION+TRANSITION_DURATION)*IMAGE_COUNT - TRANSITION_DURATION ))
TOTAL_FRAME_COUNT=$(( TOTAL_DURATION*FPS ))
echo -e "\nVideo Slideshow Info\n------------------------\nImage count: ${IMAGE_COUNT}\nDimension: ${WIDTH}x${HEIGHT}\nFPS: ${FPS}\nImage duration: ${IMAGE_DURATION} s\n\
Transition duration: ${TRANSITION_DURATION} s\nTotal duration: ${TOTAL_DURATION} s\n"
START_TIME=$SECONDS
# 1. START COMMAND
FULL_SCRIPT="ffmpeg -y "
# 2. ADD INPUTS
for IMAGE in ${FILES[@]}; do
FULL_SCRIPT+="-loop 1 -i '${IMAGE}' "
done
# 3. START FILTER COMPLEX
FULL_SCRIPT+="-filter_complex \""
# 4. PREPARE INPUTS
for (( c=0; c<${IMAGE_COUNT}; c++ ))
do
case ${SCREEN_MODE} in
1)
FULL_SCRIPT+="[${c}:v]setpts=PTS-STARTPTS,scale=w='if(gte(iw/ih,${WIDTH}/${HEIGHT}),min(iw,${WIDTH}),-1)':h='if(gte(iw/ih,${WIDTH}/${HEIGHT}),-1,min(ih,${HEIGHT}))',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1,fps=${FPS},format=rgba,split=2[stream$((c+1))out1][stream$((c+1))out2];"
;;
2)
FULL_SCRIPT+="[${c}:v]setpts=PTS-STARTPTS,scale=w='if(gte(iw/ih,${WIDTH}/${HEIGHT}),-1,${WIDTH})':h='if(gte(iw/ih,${WIDTH}/${HEIGHT}),${HEIGHT},-1)',crop=${WIDTH}:${HEIGHT},setsar=sar=1/1,fps=${FPS},format=rgba,split=2[stream$((c+1))out1][stream$((c+1))out2];"
;;
3)
FULL_SCRIPT+="[${c}:v]setpts=PTS-STARTPTS,scale=${WIDTH}:${HEIGHT},setsar=sar=1/1,fps=${FPS},format=rgba,split=2[stream$((c+1))out1][stream$((c+1))out2];"
;;
4)
FULL_SCRIPT+="[${c}:v]scale=${WIDTH}x${HEIGHT},setsar=sar=1/1,fps=${FPS},format=rgba,boxblur=100,setsar=sar=1/1[stream${c}blurred];"
FULL_SCRIPT+="[${c}:v]scale=w='if(gte(iw/ih,${WIDTH}/${HEIGHT}),min(iw,${WIDTH}),-1)':h='if(gte(iw/ih,${WIDTH}/${HEIGHT}),-1,min(ih,${HEIGHT}))',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1,fps=${FPS},format=rgba[stream${c}raw];"
FULL_SCRIPT+="[stream${c}blurred][stream${c}raw]overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2:format=rgb,setpts=PTS-STARTPTS,split=2[stream$((c+1))out1][stream$((c+1))out2];"
;;
esac
done
# 5. APPLY PADDING
for (( c=1; c<=${IMAGE_COUNT}; c++ ))
do
FULL_SCRIPT+="[stream${c}out1]pad=width=${WIDTH}:height=${HEIGHT}:x=(${WIDTH}-iw)/2:y=(${HEIGHT}-ih)/2:color=${BACKGROUND_COLOR},trim=duration=${IMAGE_DURATION},select=lte(n\,${IMAGE_FRAME_COUNT})[stream${c}overlaid];"
if [[ ${c} -eq 1 ]]; then
if [[ ${IMAGE_COUNT} -gt 1 ]]; then
FULL_SCRIPT+="[stream${c}out2]pad=width=${WIDTH}:height=${HEIGHT}:x=(${WIDTH}-iw)/2:y=(${HEIGHT}-ih)/2:color=${BACKGROUND_COLOR},trim=duration=${TRANSITION_DURATION},select=lte(n\,${TRANSITION_FRAME_COUNT})[stream${c}ending];"
fi
elif [[ ${c} -lt ${IMAGE_COUNT} ]]; then
FULL_SCRIPT+="[stream${c}out2]pad=width=${WIDTH}:height=${HEIGHT}:x=(${WIDTH}-iw)/2:y=(${HEIGHT}-ih)/2:color=${BACKGROUND_COLOR},trim=duration=${TRANSITION_DURATION},select=lte(n\,${TRANSITION_FRAME_COUNT}),split=2[stream${c}starting][stream${c}ending];"
elif [[ ${c} -eq ${IMAGE_COUNT} ]]; then
FULL_SCRIPT+="[stream${c}out2]pad=width=${WIDTH}:height=${HEIGHT}:x=(${WIDTH}-iw)/2:y=(${HEIGHT}-ih)/2:color=${BACKGROUND_COLOR},trim=duration=${TRANSITION_DURATION},select=lte(n\,${TRANSITION_FRAME_COUNT})[stream${c}starting];"
fi
done
# 6. CREATE TRANSITION FRAMES
for (( c=1; c<${IMAGE_COUNT}; c++ ))
do
FULL_SCRIPT+="[stream$((c+1))starting][stream${c}ending]blend=all_expr='if((lte(mod(X,${CELL_SIZE}),${CELL_SIZE}/2-(${CELL_SIZE}/2)*T/${TRANSITION_DURATION})+lte(mod(Y,${CELL_SIZE}),${CELL_SIZE}/2-(${CELL_SIZE}/2)*T/${TRANSITION_DURATION}))+(gte(mod(X,${CELL_SIZE}),(${CELL_SIZE}/2)+(${CELL_SIZE}/2)*T/${TRANSITION_DURATION})+gte(mod(Y,${CELL_SIZE}),(${CELL_SIZE}/2)+(${CELL_SIZE}/2)*T/${TRANSITION_DURATION})),B,A)':shortest=1[stream$((c+1))blended];"
done
# 7. BEGIN CONCAT
for (( c=1; c<${IMAGE_COUNT}; c++ ))
do
FULL_SCRIPT+="[stream${c}overlaid][stream$((c+1))blended]"
done
# 8. END CONCAT
FULL_SCRIPT+="[stream${IMAGE_COUNT}overlaid]concat=n=$((2*IMAGE_COUNT-1)):v=1:a=0,format=yuv420p[video]\""
# 9. END
FULL_SCRIPT+=" -map [video] -vsync 2 -async 1 -rc-lookahead 0 -g 0 -profile:v main -level 42 -c:v libx264 -r ${FPS} ../transition_checkerboard.mp4"
eval ${FULL_SCRIPT}
ELAPSED_TIME=$(($SECONDS - $START_TIME))
echo -e '\nSlideshow created in '$ELAPSED_TIME' seconds\n'
unset $IFS