fix(render): preserve source fps/sample-rate + accurate frame seeking to prevent A/V drift#31
Open
daniellegurgel wants to merge 1 commit into
Open
fix(render): preserve source fps/sample-rate + accurate frame seeking to prevent A/V drift#31daniellegurgel wants to merge 1 commit into
daniellegurgel wants to merge 1 commit into
Conversation
Two related fixes to prevent A/V drift when concatenating many segments
(observed with 525-segment edits where lip sync went out of sync over time):
1. Stop forcing -r 24 and -ar 48000 in extract_segment()
Original source fps (e.g. 30fps) was being downsampled to 24fps,
dropping ~20% of frames. Audio was being upsampled from 44.1kHz to
48kHz. Both transformations introduced cumulative timing errors over
hundreds of concatenated segments. By removing the hardcoded values,
ffmpeg passes through the source rates intact.
Same fix also applied to apply_loudnorm_two_pass() (-ar 48000 removed).
2. Hybrid seek (-ss before AND after -i) in extract_segment()
Previous: -ss seg_start -i source -t duration
Fast seek but inexact (jumps to nearest keyframe before seg_start).
With 525 cuts, sub-frame errors accumulate.
New: -ss (seg_start - 1.0) -i source -ss 1.0 -t duration
Fast seek to 1s before, then accurate (frame-by-frame) seek
for the last 1s. Frame-exact start for every segment.
Per-segment overhead: ~1s of decode. For 525 cuts that's ~9 min
extra processing — acceptable trade for zero drift.
Validated on a 3h37min source with 525 cuts. Lip sync now stable
throughout the full output.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue found across 1 file
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="helpers/render.py">
<violation number="1" location="helpers/render.py:189">
P1: Removing fixed output FPS/sample-rate from per-segment encodes makes the concat inputs non-uniform, which can break the concat demuxer’s `-c copy` requirements on mixed-source EDLs.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| seek_pre = max(0.0, seg_start - 1.0) | ||
| seek_fine = seg_start - seek_pre # geralmente 1.0 (menor se seg_start < 1) | ||
|
|
||
| cmd = [ |
There was a problem hiding this comment.
P1: Removing fixed output FPS/sample-rate from per-segment encodes makes the concat inputs non-uniform, which can break the concat demuxer’s -c copy requirements on mixed-source EDLs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At helpers/render.py, line 189:
<comment>Removing fixed output FPS/sample-rate from per-segment encodes makes the concat inputs non-uniform, which can break the concat demuxer’s `-c copy` requirements on mixed-source EDLs.</comment>
<file context>
@@ -178,16 +178,25 @@ def extract_segment(
+ seek_pre = max(0.0, seg_start - 1.0)
+ seek_fine = seg_start - seek_pre # geralmente 1.0 (menor se seg_start < 1)
+
cmd = [
"ffmpeg", "-y",
- "-ss", f"{seg_start:.3f}",
</file context>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related fixes to prevent A/V drift when concatenating many segments. Discovered while editing a 3h37min source with 525 cuts — lip sync went out of sync progressively over the timeline.
1. Stop forcing
-r 24and-ar 48000inextract_segment()The hardcoded
-r 24was downsampling source FPS (e.g. 30fps → 24fps), dropping ~20% of frames per segment. The hardcoded-ar 48000was upsampling audio (44.1kHz → 48kHz). Both transformations introduced cumulative timing errors over hundreds of concatenated segments.By removing the hardcoded values, ffmpeg passes through the source rates intact — no resampling or framerate conversion. Same fix applied to both
apply_loudnorm_two_pass()calls (-ar 48000removed there as well).2. Hybrid seek (-ss before AND after -i) in
extract_segment()Before:
Fast seek but inexact (jumps to nearest keyframe before
seg_start). With many cuts, sub-frame errors accumulate.After:
Fast seek to 1s before, then accurate (frame-by-frame) seek for the last 1s. Frame-exact start for every segment.
Per-segment overhead: ~1s of decode. For 525 cuts, ~9 min of extra processing — acceptable trade for zero drift.
Test plan
Notes
This was found while building a content production pipeline using
video-use. The drift was very subtle on small edits but devastating on long-form (>1h) edits with many cuts.🤖 Generated with Claude Code
Summary by cubic
Prevents A/V drift by preserving source frame rate and sample rate, and by using accurate frame seeking for segment extraction. This keeps lip sync stable on long edits with many cuts, with a small per-segment decode cost.
-r 24and-ar 48000inextract_segment(); removed-ar 48000inapply_loudnorm_two_pass().ffmpegnow passes through source FPS/sample-rate unchanged.extract_segment():-sstomax(0, start-1s)before-i, then-ssfor fine seek. Produces frame-exact starts and avoids cumulative timing errors (~1s extra decode per segment).Written for commit 9ed65c8. Summary will update on new commits.