Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

recap/bin/build-filter.mjs: drop fps filter on concat-demuxer streams

The fps filter on inputs sourced from a concat demuxer with `duration`
directives (slides input #0, pre-encoded subtitle webm input #2)
truncates the output at ~half length. The demuxer emits sparse PTS
matching the duration directives; the fps filter mishandles that and
produces fewer output frames than expected.

Reproduced in isolation: slides+fps→libx264 = 157s output. Same chain
without fps filter = 281.6s. Fix is just to drop the filter — ffmpeg
honors the duration directives directly when the encoder consumes them.

The audio waveform overlay (showwaves rate=30) sets the timeline's
effective frame rate; slides + subs ride along.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+9 -5
+9 -5
recap/bin/build-filter.mjs
··· 24 24 const WAVE_Y = 1752; // y-band for the audio waveform under the subtitle pill 25 25 26 26 const lines = []; 27 - lines.push(`[0:v]format=yuv420p,fps=30,scale=1080:1920,setsar=1[bg]`); 27 + // NOTE: do NOT add an `fps=30` filter to inputs that come from a concat 28 + // demuxer with `duration` directives (slides + subtitle webm here). The 29 + // fps filter mis-handles the sparse PTS that the demuxer emits and 30 + // truncates the output stream at ~half length. ffmpeg honors the 31 + // duration directives natively when the encoder consumes them directly. 32 + lines.push(`[0:v]format=yuv420p,scale=1080:1920,setsar=1[bg]`); 28 33 lines.push(`[1:a]apad=whole_dur=${TOTAL},asplit=2[a1][a2]`); 29 34 lines.push(`[a2]showwaves=s=1080x96:colors=0xff70d0|0x70f0e0:mode=cline:rate=30,format=rgba,colorchannelmixer=aa=0.55[wave]`); 30 35 lines.push(`[bg][wave]overlay=x=0:y=${WAVE_Y}:format=auto[bg2]`); 31 36 lines.push(`[bg2]drawbox=x=0:y=1912:w='iw*t/${TOTAL}':h=8:color=0xff69b4:t=fill[v0]`); 32 - // Single subtitle-track overlay. Input #2 is a pre-encoded webm with 33 - // alpha (vp9 / yuva420p) produced by subtitle-track.mjs — see comments 34 - // there. Format the alpha track to match the slide pipeline and overlay. 35 - lines.push(`[2:v]fps=30,format=rgba,scale=1080:1920[subs]`); 37 + // Single subtitle-track overlay. Input #2 is a pre-encoded 30 fps webm 38 + // with alpha (vp9 / yuva420p) produced by subtitle-track.mjs. 39 + lines.push(`[2:v]scale=1080:1920,format=rgba[subs]`); 36 40 lines.push(`[v0][subs]overlay=x=0:y=0:format=auto:shortest=0[final]`); 37 41 38 42 process.stdout.write(lines.join(";\n") + "\n");