Skip to content

Commit 079ae08

Browse files
committed
Full sponsor and title cards, better yt endboard
1 parent 925fd62 commit 079ae08

2 files changed

Lines changed: 88 additions & 81 deletions

File tree

formvideo.py

Lines changed: 86 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import os.path
88
import shutil
99

10-
logger = logging.getLogger("__name__")
10+
logger = logging.getLogger(__name__)
1111

1212
logger.setLevel(logging.DEBUG)
1313

@@ -48,19 +48,49 @@ def timecode_to_timestamp(timecode, framerate = FRAMERATE):
4848

4949
def form_video(video, talk, start_tc, end_tc, framerate = FRAMERATE, out_dir = OUT_DIR, temp_dir = TEMP_DIR):
5050

51-
end_dur = 10
52-
end_fade = 2
53-
51+
# Timing information
52+
end_dur = 10 # How long to hold the endslate for
53+
end_fade_in = 2
54+
end_fade = 2 # Fade out time on endslate
55+
afade_in = 3 # Talk audio fade in duration
56+
afade_out = 3 # Talk audio fade out duration
57+
spn_dur = 5 # Sponsor slide hold duration
58+
spn_fade_in = 0.4 # Sponsor slide fade-in duration
59+
spn_fade_out = 0.4 # Sponsor slide cross-fade out duration
60+
title_dur = 5 # How long to hold the title card
61+
title_fade_out = 0.4 # Title card into program duration
62+
main_fade_out = 0.4 # Main program into endboard duration
63+
64+
# Colour and design information
65+
col_talk = "#f9e200"
66+
col_pres = "#2eadd9"
67+
col_bkg = "#00000000" #"#21301850"
68+
69+
# Resource files
70+
bkgd_file = "resources/BG_V3.mp4"
71+
transp_file = "resources/transparent.png"
72+
logo_file = "resources/logo.svg"
73+
spons_file = "resources/sponsor_slide_rounded.png"
74+
75+
# Generated files paths
76+
copr_file = "temp/copyright.png"
77+
spres_file = "temp/start_pres.png"
78+
stalk_file = "temp/start_title.png"
79+
80+
# Convert start and end to some other forms
5481
start_s = timecode_to_seconds(start_tc)
5582
end_s = timecode_to_seconds(end_tc)
56-
5783
start_ts = timecode_to_timestamp(start_tc)
5884
end_ts = timecode_to_timestamp(end_tc)
5985

60-
fade_offset = end_s - start_s - 1
86+
# Calculate some other reused variables
87+
fade_offset = end_s - start_s - (end_fade_in/2.)
88+
afade_offset = fade_offset - (afade_out/2.) # When to fade out the main talk audio
89+
eb_end = fade_offset + end_dur #
90+
end_tdur = end_dur + end_fade # Total duration of the endslate, including fade time
91+
title_end = spn_dur + title_dur
6192

62-
start_png = Path.joinpath(Path(temp_dir), Path("start.png"))
63-
end_png = Path.joinpath(Path(temp_dir), Path("end.png"))
93+
lic_text = "This work is licensed under CC BY-SA 4.0. To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/4.0/"
6494

6595
start_timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
6696

@@ -74,72 +104,47 @@ def form_video(video, talk, start_tc, end_tc, framerate = FRAMERATE, out_dir = O
74104
output_path = Path.joinpath(Path(out_dir), output_file)
75105
log_path = Path.joinpath(Path(LOG_DIR), log_file)
76106

77-
start_lt_args = [
78-
IMAGEMAGICK_BIN,
79-
"-size", "1920x1080", "xc:none",
80-
"-fill", "blue", "-pointsize", "72", "-annotate", "+70+70", talk["title"],
81-
"-fill", "green", "-pointsize", "60", "-annotate", "+70+200", talk["presenter"],
82-
start_png
83-
]
84-
85-
start_lt_title_args = [
107+
start_title_args = [
86108
IMAGEMAGICK_BIN,
87-
"-size", "1860x160", "-background", "#00000000",
88-
"-fill", "#f9e200", "-gravity", "southwest", "caption:{}".format(talk["title"]),
89-
"(", "+clone", "-shadow", "500x2+0+0", ")", "+swap", "-background", "#21301850", "-layers", "merge", "+repage",
90-
"temp/start_title.png"
109+
"-size", "1800x300", "-background", "#00000000",
110+
"-fill", col_talk, "-gravity", "center", "caption:{}".format(talk["title"]),
111+
"(", "+clone", "-shadow", "500x2+0+0", ")", "+swap", "-background", col_bkg, "-layers", "merge", "+repage",
112+
stalk_file
91113
]
92114

93-
start_lt_pres_arg = [
115+
start_pres_arg = [
94116
IMAGEMAGICK_BIN,
95-
"-size", "1860x80", "-background", "#00000000",
96-
"-fill", "#2eadd9", "-gravity", "southwest", "caption:{}".format(talk["presenter"]),
97-
"(", "+clone", "-shadow", "500x2+0+0", ")", "+swap", "-background", "#21301850", "-layers", "merge", "+repage",
98-
"temp/start_pres.png"
99-
]
100-
101-
end_slide_args = [
102-
IMAGEMAGICK_BIN,
103-
"-size", "1920x1080", "xc:orange",
104-
"-fill", "blue", "-gravity", "center", "-pointsize", "72", "-annotate", "+0-70", talk["title"],
105-
"-fill", "green", "-gravity", "center", "-pointsize", "60", "-annotate", "+0+70", talk["presenter"],
106-
end_png
107-
]
108-
109-
end_slide_title_args = [
110-
IMAGEMAGICK_BIN,
111-
"-size", "1320x350", "-background", "#00000000",
112-
"-fill", "#f9e200", "-gravity", "Center", "caption:{}".format(talk["title"]),
113-
"(", "+clone", "-shadow", "500x2+0+0", ")", "+swap", "-background", "#21301850", "-layers", "merge", "+repage",
114-
"temp/end_title.png"
117+
"-size", "1800x256", "-background", "#00000000",
118+
"-fill", col_pres, "-gravity", "center", "caption:{}".format(talk["presenter"]),
119+
"(", "+clone", "-shadow", "500x2+0+0", ")", "+swap", "-background", col_bkg, "-layers", "merge", "+repage",
120+
spres_file
115121
]
116122

117-
end_slide_pres_arg = [
123+
copyright_args = [
118124
IMAGEMAGICK_BIN,
119-
"-size", "700x200", "-background", "#00000000",
120-
"-fill", "#2eadd9", "-gravity", "Center", "caption:{}".format(talk["presenter"]),
121-
"(", "+clone", "-shadow", "500x2+0+0", ")", "+swap", "-background", "#21301850", "-layers", "merge", "+repage",
122-
"temp/end_pres.png"
125+
"-size", "1000x256", "-background", "#00000000",
126+
"-fill", "#2eadd9", "-gravity", "east", "caption:{}".format(lic_text),
127+
"(", "+clone", "-shadow", "500x2+0+0", ")", "+swap", "-background", col_bkg, "-layers", "merge", "+repage",
128+
copr_file
123129
]
124130

125131
ffmpeg_loudness_args = [
126132
FFMPEG_BIN,
127133
"-ss", start_ts, "-to", end_ts, "-i", video,
128-
"-filter_complex", "[0:a]afade=in:d=5,afade=out:st={fade_offset3:.2f}:d=5,adelay=8000:all=1,loudnorm=print_format=json".format(fade_offset3 = fade_offset - 3),
134+
"-filter_complex", "[0:a]afade=in:d={in_:.2f},afade=out:st={out_st:.2f}:d={out:.2f},loudnorm=print_format=json".format(in_ = afade_in, out = afade_out, out_st = afade_offset),
129135
"-f", "null", "-"
130136
]
131137

132138
with open(log_path, "a") as error_log:
133139

134140
# Build all the text assets
135-
subprocess.run(start_lt_args, stderr=error_log)
136-
subprocess.run(start_lt_title_args, stderr=error_log)
137-
subprocess.run(start_lt_pres_arg, stderr=error_log)
138-
subprocess.run(end_slide_args, stderr=error_log)
139-
subprocess.run(end_slide_title_args, stderr=error_log)
140-
subprocess.run(end_slide_pres_arg, stderr=error_log)
141+
logger.info("Building text assets")
142+
subprocess.run(start_title_args)
143+
subprocess.run(start_pres_arg)
144+
subprocess.run(copyright_args)
141145

142146
# First FFmpeg pass for getting loudness stats
147+
logger.info("Detecting loudness information")
143148
logger.debug(ffmpeg_loudness_args)
144149
analysis = subprocess.check_output(ffmpeg_loudness_args, stderr=subprocess.STDOUT).decode("utf-8").split("\n")
145150

@@ -156,42 +161,43 @@ def form_video(video, talk, start_tc, end_tc, framerate = FRAMERATE, out_dir = O
156161
# Run the final build FFmpeg
157162
ffmpeg_args = [
158163
FFMPEG_BIN,
159-
"-ss", start_ts, "-to", end_ts, "-i", video,
160-
"-loop", "1", "-framerate", str(framerate), "-i", start_png,
161-
"-stream_loop", "-1", "-r", str(framerate), "-i", "temp/BG_V3.mp4",
162-
"-loop", "1", "-framerate", str(framerate), "-i", "temp/end_pres.png",
163-
"-loop", "1", "-framerate", str(framerate), "-i", "temp/end_title.png",
164-
"-loop", "1", "-framerate", str(framerate), "-i", "temp/sponsor_slide.png",
165-
"-loop", "1", "-framerate", str(framerate), "-i", "temp/start_pres.png",
166-
"-loop", "1", "-framerate", str(framerate), "-i", "temp/start_title.png",
167-
"-filter_complex", ("[0:a]afade=in:d=5,afade=out:st={fade_offset3:.2f}:d=5,adelay=8000:all=1,".format(fade_offset3 = fade_offset - 3) +
168-
"loudnorm=I={target:.2f}:TP=-1.5:measured_I={mI}:measured_tp={mTP}:measured_LRA={mLRA}:measured_thresh={mTH}:offset={off}:linear=true:print_format=json[a0];".format(
164+
"-ss", start_ts, "-to", end_ts, "-i", video, #0
165+
"-stream_loop", "-1", "-r", str(framerate), "-i", bkgd_file, #1
166+
"-loop", "1", "-framerate", str(framerate), "-i", transp_file, #2
167+
"-loop", "1", "-framerate", str(framerate), "-i", spres_file, #3
168+
"-loop", "1", "-framerate", str(framerate), "-i", stalk_file, #4
169+
"-loop", "1", "-framerate", str(framerate), "-i", logo_file, #5
170+
"-loop", "1", "-framerate", str(framerate), "-i", spons_file, #6
171+
"-loop", "1", "-framerate", str(framerate), "-i", copr_file, #7
172+
"-filter_complex", ("[0:a]afade=in:d={in_:.2f},afade=out:st={out_st:.2f}:d={out:.2f},adelay={title_end:.2f}:all=1,".format(in_ = afade_in, out = afade_out, out_st = afade_offset, spn_dur = spn_dur, title_end = title_end * 1000) +
173+
"loudnorm=I={target:.2f}:TP=-1.5:measured_I={mI}:measured_tp={mTP}:measured_LRA={mLRA}:measured_thresh={mTH}:offset={off}:linear=true:print_format=json[a1];".format(
169174
target = LOUD_LEVEL,
170175
mI = loud_vals["input_i"],
171176
mTP = loud_vals["input_tp"],
172177
mLRA = loud_vals["input_lra"],
173178
mTH = loud_vals["input_thresh"],
174179
off = loud_vals["target_offset"]) +
175-
"[a0]asplit[a1][a2];" +
176-
"[a2]ebur128=peak=true;" +
177-
"[2:v]crop=w=1920:h=290,fade=in:st=3:d=0.4:alpha=1,fade=out:st=13:d=0.4:alpha=1[ltb];" +
178-
"[0:v]fade=in:st=0:d=1[v1];" +
179-
"[6:v]fade=in:st=3:d=0.4:alpha=1,fade=out:st=13:d=0.4:alpha=1[s2];" +
180-
"[7:v]fade=in:st=3:d=0.4:alpha=1,fade=out:st=13:d=0.4:alpha=1[s3];" +
181-
"[v1][ltb]overlay=y=790:x=0:shortest=1[lt1];" +
182-
"[lt1][s2]overlay=y=970:x=30:shortest=1,settb=1/{framerate:.2f}[v3];".format(framerate = framerate) +
183-
"[v3][s3]overlay=y=800:x=30:shortest=1,settb=1/{framerate:.2f}[v4];".format(framerate = framerate) +
184-
"[2:v]trim=start=0:end={main_end:.2f},settb=1/{framerate:.2f}[e1];".format(framerate = framerate, main_end = end_dur + end_fade) +
185-
"[e1][3:v]overlay=shortest=1:x=610:y=560[e2];" +
186-
"[e2][4:v]overlay=shortest=1:x=300:y=150[e3];" +
187-
"[v4][e3]xfade=offset={eb_start:.2f}:duration=1,fade=out:st={eb_end:.2f}:d={end_fade:.2f}[m1];".format(eb_start = fade_offset, eb_end = fade_offset + end_dur, end_fade = end_fade) +
188-
"[5:v][m1]xfade=offset=8:duration=1,fade=in:d=3[p1]"
180+
"[5:v]split[l1][l2];" +
181+
"[1:v]settb=1/{framerate:.2f},split[bg1][bg2];".format(framerate = framerate) +
182+
"[0:v]settb=1/{framerate:.2f}[m1];".format(framerate = framerate) +
183+
"[2:v][3:v]overlay=x=60:y=640:shortest=1[s2];" +
184+
"[s2][4:v]overlay=x=60:y=320:shortest=1[s3];" +
185+
"[s3][l1]overlay=shortest=1[s4];" +
186+
"[6:v][s4]xfade=offset={spn_dur:.2f}:duration={spn_fade_out:.2f}[s5];".format(spn_dur = spn_dur, spn_fade_out = spn_fade_out) +
187+
"[bg1]trim=start=0:end={title_end:.2f}[bg3];".format(title_end = title_end) +
188+
"[bg3][s5]overlay[s6];" +
189+
"[bg2][l2]overlay[e1];" +
190+
"[e1][7:v]overlay=x=870:y=70:shortest=1,trim=start=0:end={end_tdur:.2f}[e2];".format(end_tdur = end_tdur) +
191+
"[m1][e2]xfade=offset={eb_start:.2f}:duration=1,fade=out:st={eb_end:.2f}:d={end_fade:.2f}[m2];".format(eb_start = fade_offset, eb_end = eb_end, end_fade = end_fade) +
192+
"[s6][m2]xfade=offset={title_end:.2f}:duration={title_fade_out:.2f},fade=in:d={spn_fade_in:.2f}[p1]".format(title_fade_out = title_fade_out, title_end = title_end, spn_fade_in = spn_fade_in)
193+
189194
),
190195
"-map", "[p1]", "-map", "[a1]", "-map_metadata", "-1",
191196
"-c:v", "h264", "-crf", "16", "-g", str(math.floor(framerate/2)), "-flags", "+cgop",
192197
"-c:a", "aac", "-ar", "48000", "-b:a", "128k",
193198
"-r", str(framerate), "-pix_fmt", "yuv420p", "-movflags", "+faststart", output_path, "-y"
194199
]
200+
logger.info("Running main build")
195201
logger.debug(ffmpeg_args)
196202
subprocess.run(ffmpeg_args, stderr=error_log)
197203

@@ -206,7 +212,7 @@ def ingest_video(input_path, output_dir, framerate = FRAMERATE):
206212
ffmpeg_args = [
207213
FFMPEG_BIN,
208214
"-i", input_path,
209-
"-c:v", "h264", "-crf", "12", "-g", str(math.floor(framerate/2)), "-flags", "+cgop",
215+
"-c:v", "h264", "-crf", "12", "-g", str(math.floor(framerate/2)), "-flags", "+cgop", "-s", "1920x1080",
210216
#"-c:v", "h264_nvenc", "-b:v", "12M",
211217
"-c:a", "aac", "-ar", "48000", "-b:a", "128k",
212218
"-r", str(framerate), "-pix_fmt", "yuv420p", "-movflags", "+faststart", output_path, "-y"
@@ -240,4 +246,4 @@ def ingest_video(input_path, output_dir, framerate = FRAMERATE):
240246
"presenter": "Kim M"
241247
}
242248

243-
form_video("static/video/stage_a/bbb_50.mp4", talk_data, "00:05:00:00", "00:06:00:00")
249+
form_video("static/video/stage_a/bbb_50.mp4", talk_data, "00:05:05:00", "00:05:20:00")

static/js/videolog.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ function checkKey(e) {
118118
const nopropElements = [
119119
document.getElementById("presenter"),
120120
document.getElementById("title"),
121-
document.getElementById("video1")
121+
document.getElementById("video1"),
122+
document.getElementById("talkid")
122123
]
123124
if (nopropElements.indexOf(document.activeElement) != -1){
124125
return

0 commit comments

Comments
 (0)