Merge video clips: silent-clip fix and real-footage test log, 2026-10-25 What changed in the tool: a clip with no audio stream now gets generated silence of its own length (anullsrc, 48 kHz stereo), so the other clips' sound is kept. If NO clip has sound, the result has none. If a silent clip's length cannot be read, the tool still joins pictures only. The first clip's frame rate is now snapped to the nearest standard rate when within 2 % (phone variable-frame-rate averages such as 29.74 or 59.87 fps become 29.97 or 59.94 fps instead of an odd constant rate); other rates are kept as reported. Source footage: "Big Buck Bunny" (c) Blender Foundation, CC BY 3.0, https://commons.wikimedia.org/wiki/File:Big_Buck_Bunny_4K.webm (licence read from the Commons API 2026-10-25). We downloaded the first 80 MB of the 480p VP9 transcode and cut/re-encoded the clips below with ffmpeg 6.1.1. It is animation with a 5.1 Opus soundtrack, not camera footage: it exercises sound/no-sound and derived VFR, rotation flag, HDR tags, size; it is NOT a phone recording. Derived clips (all 854x480 unless noted; ffmpeg commands in scripts/merge-silent-check.sh): r1_sound 8 s H.264 30 fps AAC stereo | r2_silent 6 s same picture type, no audio stream (-an) | r3_sound 7 s | r4_vfr 8 s VFR (30 % of frames dropped at random, avg 40.6 fps) | r4b_vfr_light 8 s VFR (1 % dropped, avg 59.87 fps) | r5_rotated 6 s with display-matrix rotation 90 (stream copy) | r6_hdrtag 5 s HEVC 10-bit with BT.2020 + PQ tags (tags only; the pixels are SDR, so this is NOT true HDR) | r7_51 5 s AAC 5.1 (6 channels) | r8_big 30 s 1920x1080 H.264 26 Mb/s, 103,895,258 bytes (99.1 MiB; the tool limit is 100 MiB per file) Method: next dev --webpack on the hubos/ia/20261019 worktree (local dev build, not production), Playwright headless Chromium (bypassCSP for dev), new context per case, files chosen with the file input, Merge clicked, download saved, ffprobe and ffmpeg volumedetect on the download. Seconds = click to download link (dev build on a shared 11 GB VPS, includes first engine load, so slower than production). Horizontal output (1920x1080) except rotated cases (vertical 1080x1920). Cases 13-14 at 390 px width also check document scrollWidth (390 = no horizontal overflow). ## Results (ffprobe: codec|w|h|r_frame_rate|duration per stream, then container duration) 01-silent-then-sound | r2_silent + r1_sound | OK 52.9s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=14.133333; 02-sound-then-silent | r1_sound + r2_silent | OK 52.4s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=14.136000; 03-sound-silent-sound | r1_sound + r2_silent + r3_sound | OK 78.6s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=21.200000; 04-all-silent | r2_silent + r2_silent | OK 53.4s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;duration=12.133333; 05-control-two-sound | r1_sound + r3_sound | OK 51.9s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=15.133333; 06-vfr-then-silent | r4_vfr + r2_silent | OK 64.0s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=2031/50;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=14.096000; 07-silent-then-vfr | r2_silent + r4_vfr | OK 54.3s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=14.070000; 08-rotated-flag-then-silent | r5_rotated + r2_silent (vertical) | OK 41.3s | codec_name=h264|codec_type=video|width=1080|height=1920|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=12.136000; 09-silent-then-rotated-flag | r2_silent + r5_rotated (vertical) | OK 41.4s | codec_name=h264|codec_type=video|width=1080|height=1920|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=12.133333; 10-hdr-tagged-hevc-then-silent | r6_hdrtag + r2_silent | OK 57.5s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=11.136000; 11-surround51-then-silent | r7_51 + r2_silent | OK 43.4s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=11.136000; 12-near-100MB-then-silent | r8_big + r2_silent | OK 148.3s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=36.136000; 13-silent-then-sound-390px | r2_silent + r1_sound, 390 px wide, scrollWidth 390 | OK 52.4s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=14.133333; 14-sound-silent-sound-390px | r1_sound + r2_silent + r3_sound, 390 px wide, scrollWidth 390 | OK 78.6s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=30/1;codec_name=aac|codec_type=audio|sample_rate=48000|channels=2|r_frame_rate=0/0;duration=21.200000; 15-vfr-light-then-silent | r4b_vfr_light + r2_silent | OK 84.3s | codec_name=h264|codec_type=video|width=1920|height=1080|r_frame_rate=2997/50 (59.94 fps) ; aac 48000 stereo ; duration=14.097431 ## Sound check (ffmpeg volumedetect mean_volume of a time window of the DOWNLOAD; the sound clips measure -22 to -37 dB, generated silence measures -91 dB) 01 silent(0-6) then sound(6-14): first1-5 mean_volume: -91.0 dB | last 7-13 mean_volume: -33.9 dB 02 sound(0-8) then silent(8-14): 1-7 mean_volume: -33.7 dB | 9-13 mean_volume: -91.0 dB 03 sound 0-8, silent 8-14, sound 14-21: 1-7 mean_volume: -33.7 dB | 9-13 mean_volume: -91.0 dB | 15-20 mean_volume: -22.5 dB 06 vfr(0-8) sound, silent 8-14: 1-7 mean_volume: -27.9 dB | 9-13 mean_volume: -91.0 dB 08 rotated sound 0-6, silent: 1-5 mean_volume: -26.1 dB | 7-11 mean_volume: -91.0 dB 10 hdr sound 0-5: 1-4 mean_volume: -37.5 dB | 6-10 mean_volume: -91.0 dB 11 5.1 0-5: 1-4 mean_volume: -27.1 dB | 6-10 mean_volume: -91.0 dB 12 big 0-30 sound, silent 30-36: 5-25 mean_volume: -32.8 dB | 31-35 mean_volume: -91.0 dB 15 (snap check) not volume-measured; sound present as audio stream ## Reading the results - 15 of 15 runs finished, none stalled, no page errors. The 90 s no-progress stop did not trigger. - Silent first or last, or in the middle (cases 01, 02, 03, 06-11, 13-15): the sound of the other clips is in the result at the right position and the silent clip's span is silence. Before this change the same pairs gave a file with no audio stream (test-results.txt Part 7a cases 03-04). - All clips silent (04): output has no audio stream, as intended. - Audio duration vs picture duration differs by 0.001 to 0.08 s (for example 14.070 s audio vs 14.133 s picture in case 01), same order as the 0.016 s known from earlier runs; not audible in a listen-free check, but we did not listen to the files. - Rotation flag + silent (08, 09): portrait 1080x1920 result, sound kept. - 5.1 source (11): becomes AAC 48 kHz stereo; sound kept (downmixed by ffmpeg defaults, we did not compare loudness against the source). - HDR-tagged HEVC (10): the result is 8-bit SDR H.264; the tool does not tone-map, and this test file has SDR pixels, so real HDR phone video (HLG / Dolby Vision) remains untested. - Near 100 MB (12): 99.1 MiB file plus a short silent clip finished in 148 s (dev build) with sound and silence correct. - VFR (06, 07, 15): 06 average 40.6 fps is not near a standard rate so the output carries 40.62 fps (r_frame_rate 2031/50); 15 (59.87 avg) gave 59.94 fps. Both finished with sound. These VFR clips are made by dropping frames from a 60 fps source, not recorded by a phone. - NOT tested: real iPhone / Android recordings (VFR from a camera, HDR, HEVC Dolby Vision), Safari, Firefox, phones, files above 100 MB, more than 3 clips, lip-sync of long clips.