영상 끝만 자르면 중간 페이지의 지연은 남는다

강의 영상은 페이지별 영상 조각을 만든 뒤 이어 붙인다. AI Commons의 연결 단계에는 페이지마다 달라지는 음량을 맞추기 위한 loudnorm 필터가 있었다. 이 경로에서 뒤 페이지로 갈수록 음성이 화면보다 늦어지는 문제가 확인됐다. 40페이지 비교 기록의 마지막 페이지 음성 시작 오차는 433밀리초였다.

MP4의 선언 길이에 AAC 인코더 프라이밍이 포함돼 다음 페이지 경계가 밀리는 것을 확인하고, concat 목록에 프레임에 맞춘 duration을 명시했다. 아래의 loudnorm 앞 aresample 적용과 40페이지 비교는 다른 잔여 원인을 다룬 후속 작업이다.

단순히 최종 음성 길이를 영상 길이에 맞춰 자르는 방법은 전체 길이 차를 없앨 수 있다. 하지만 20번째 페이지에서 이미 늦어진 음성을 제자리로 돌리지는 못한다. 사용자가 보는 증상이 페이지별 시작 위치의 차이였으므로, 길이와 시작 시각을 따로 살펴야 했다.

마지막 AAC 프레임이 다음 조각과 조금 겹쳤다

AAC는 음성을 일정 샘플 수의 프레임으로 인코딩한다. 조각의 선언 길이가 그 프레임 경계와 정확히 맞지 않으면 마지막 프레임이 조금 더 길 수 있다. 코드의 예시는 3.584초에 시작한 1,024샘플 프레임이 3.6053초에 끝나지만 조각의 선언 길이는 3.6초인 경우다. 다음 조각은 3.6초부터 시작하므로 작은 겹침이 생긴다.

각 샘플에는 언제 재생해야 하는지 나타내는 타임스탬프가 있다. 원래는 이 시간표로 겹침을 정리할 수 있지만, loudnorm의 동적 모드는 입력 타임스탬프를 버리고 샘플을 연속으로 다시 매겼다. 페이지 끝의 겹친 샘플이 그대로 뒤에 붙으며 오차가 누적됐다. 한 조각의 작은 차이가 긴 강의 뒤쪽에서 눈에 띄는 지연이 된 것이다.

이 입력의 마지막 겹침은 조각 생성기의 apad·atrim·길이 제한으로 만든 무음 패딩이라는 전제가 있다. 임의 오디오의 겹친 부분을 무조건 잘라도 되는 규칙은 아니다. 조각 생성 필터가 달라지면 이 전제도 다시 확인해야 한다.

시간표를 맞춘 다음 음량을 정규화했다

변경은 연결 필터의 맨 앞에 aresample을 넣었다. 타임스탬프에 맞춰 겹침을 제거하고 필요한 빈 구간을 채운 뒤 음량 정규화를 수행한다. 핵심은 필터를 추가한 사실보다 적용 순서였다.

aresample=async=1:min_hard_comp=0.001:first_pts=0,
loudnorm=I=-20:LRA=1:TP=-1.5:dual_mono=true,
acompressor=threshold=0.3:ratio=6:attack=5:release=150:knee=1

min_hard_comp=0.001은 1밀리초 이상 어긋나면 즉시 보정하도록 임계를 낮춘 설정이다. 기본 0.1초 임계에서는 몇 밀리초의 겹침을 점진적으로 보정해 중간 페이지에 오차가 남았다. first_pts=0은 첫 샘플의 시간 기준을 0으로 고정한다. 정규화 이후에는 이미 입력 시간표가 사라지므로 같은 역할을 하기 어렵다.

마지막 페이지뿐 아니라 중간의 오차도 비교했다

아래는 보관된 코드 주석의 같은 실행 비교다. ffmpeg 3.4, 40조각·242.8초 영상, 44.1kHz·64k·모노 조건이다. 이번 원고 작성에서 새로 측정한 결과는 아니다.

연결 필터음성·영상 길이 차2번째 음성 시작 오차20번째40번째
정규화 + 압축452ms10ms224ms433ms
정렬 · 기본 임계 + 기존 필터53ms33ms38ms38ms
정렬 · 1ms 임계 + 기존 필터30ms10ms15ms15ms

마지막 시작 오차는 433ms에서 15ms로 줄었고, 채택 설정에서 2번째와 40번째의 차이는 5ms였다. 기본 임계도 전체 길이는 줄였지만 33~38ms의 시작 오차가 남아 있었다. “파일 길이가 맞는다” 하나만으로 수정안을 선택하지 않은 이유다.

별도의 실제 ffmpeg 회귀 테스트는 24개 조각을 사용한다. AAC 프레임 경계에 맞지 않는 길이와 앞뒤 무음을 만들고, 연결 후 음성·영상 길이 차가 60ms 미만인지 검사한다. PCM 샘플에서 음성 시작을 찾아 2번째와 마지막 오차 차이가 20ms 이하인지도 확인한다. 이는 합성 톤을 사용한 테스트이며 위 40페이지 측정과 동일한 입력이나 수치가 아니다.

정지 화면의 늦은 등장과는 원인이 달랐다

별도로 수정한 아바타 정지 화면 문제도 시간 기준과 관련 있지만 누적 AAC 지연과 다르다. 컨테이너 전체 시작은 0인데 비디오 스트림만 늦게 시작하고 앞 무음이 0인 입력에서는, freeze 경로의 시작 시각 정규화 누락으로 인물이 늦게 나타났다. 이 경로는 setpts=PTS-STARTPTS를 정지 프레임 패딩 전에 넣었다.

같은 “음성과 화면이 안 맞는다”는 증상이라도 입력의 시작 오프셋과 페이지 경계마다 쌓이는 오차는 구분해야 한다. 이 비교만으로 전체 고객 환경의 동기 품질을 입증할 수는 없다. AI Commons의 이 사례에서는 전체 길이보다 각 페이지의 시간 기준을 따라간 것이 원인을 좁히는 데 중요했다.