AI & 자동화

"재생해봤는데 괜찮던데요" — 영상 검수를 코드로 하는 법

"재생해봤는데 괜찮던데요" — 영상 검수를 코드로 하는 법

완성본을 재생해서 판단하면 안 된다. 앞부분 유실도, 자막 텍스트 변형도 눈으로는 안 잡힌다.

오디오 상관분석으로 잡아낸 편집 사고

완성본은 재생해서 판단하면 안 됩니다. 앞부분이 통째로 잘려도, 자막 텍스트가 대본과 달라져도 눈과 귀로는 잡히지 않습니다. 한 편을 만들며 내보내기를 네 번 했고, 네 번 모두 코드로 찾았습니다.

내보내기 네 번

회차문제발견 수단
1자막 하단 여백 8% — 쇼츠 UI가 덮음격자 크롭
2타임라인 앞 2.4초 유실오디오 상관분석
3자막 3건이 대본과 다름컨택트 시트
4출처 자막 상단 11% — 위쪽 UI에 걸림격자 크롭

사고 ① 자막 위치

쇼츠는 하단 15~20%를 UI(채널명·제목·음원)가, 상단 12%를 검색·카메라가 덮습니다. 캡컷 기본 자막 위치는 아래 여백 8% 근처라 그대로 두면 아랫줄이 가려집니다. 격자를 겹쳐 실측합니다 — 한 칸이 화면의 5%입니다.

ffmpeg -ss <t> -i final.mp4 -frames:v 1 \
  -vf "crop=1080:600:0:1320,drawgrid=w=1080:h=96:t=2:c=red" /tmp/bot.png

목표는 아래 여백 20% 이상, 1920px 기준 약 250px 위입니다. 출처 자막을 올릴 때 반대쪽으로 넘어가지 않게 합니다 — 4회차에서 상단 11%까지 올라가 이번엔 위쪽 UI에 걸렸습니다.

사고 ② 앞 2.4초가 사라졌다 — 이 글의 핵심

자막 위치를 고쳐 재출력했더니 길이가 70.96초 → 68.58초로 줄었습니다. 나레이션은 70.50초입니다. 재생해보면 멀쩡해 보입니다. 훅이 사라진 걸 몰랐습니다.

나레이션 원본과 완성본의 음량 포락선을 상관분석해 시간 어긋남(lag)을 잽니다.

구간별 lag → 0s:+2.40  8s:+2.35  16s:+2.40  24s:+2.35
             32s:+2.35  40s:+2.35  48s:+2.35  56s:+2.40

전 구간이 일정하게 +2.4초 → 앞이 잘린 것입니다. 중간에서 값이 튀면 그 지점에서 구간이 삭제·삽입된 것입니다. 정상본은 이렇게 나옵니다.

구간별 lag → 0s:+0.00  8s:-0.15  16s:-0.10  24s:-0.10
             32s:-0.10  40s:-0.15  48s:-0.15  56s:-0.10

배속 변경 가설도 검증했다

68.58 / 70.50 = 2.8% 차이라 "속도가 바뀐 것 아닌가" 의심할 수 있습니다. 재봤습니다.

가설최적 lag상관
속도 동일2.35초0.673
2.8% 빨라짐1.55초0.192

배속 아닙니다. 가설을 세우고 수치로 죽이는 과정을 그대로 남깁니다.

알고리즘(numpy 없이)

  1. ffmpeg로 두 오디오를 4000Hz 모노 s16le로 디코드
  2. 50ms 창의 RMS로 포락선 계산
  3. 정규화(평균 0, 표준편차 1)
  4. 8초 창을 밀어가며 −2초~+4초 범위에서 내적 최대인 lag 탐색
def envelope(raw, hop):
    a = array.array('h'); a.frombytes(raw)
    return [math.sqrt(sum(v*v for v in a[i:i+hop])/hop)
            for i in range(0, len(a)-hop, hop)]

사고 ③ 자막 텍스트가 대본과 달랐다

자막 55블록의 중간 시점 프레임에서 자막 띠만 잘라 세로로 이어 붙였습니다(14개씩 4장). 대본과 한 줄씩 대조하니 3건이 나왔습니다.

대본화면심각도
동물들이 똑같이 속는다면, 건물은 원인이동물들이 똑같이 속는다면 원인이논지 붕괴
비판의 대표 증거비판의 대표적인 증거경미
1960년대에 연구자들이1960년대 연구자들이무시 가능

건물은이 빠지면 무엇이 원인이 아닌지가 사라집니다. 이 문장은 각진 세계 가설을 무너뜨리는 자리인데 "원인이 될 수 없다"가 허공에 뜹니다. 게다가 소리로는 들리니 자막이 틀린 게 드러납니다. 고쳤더니 건물은이 옆 블록으로 들어가 어순이 뒤집혔습니다 — 한 번 더 왕복했습니다.

그래서 자동 캡션을 쓰지 않는다

자동 캡션을 피하는 이유는 정확도가 아니라 출처입니다. 음성을 다시 인식하므로 화면 문장이 대본과 달라질 수 있습니다. 음소거 시청자에게는 자막이 곧 원문입니다. 대본에서 기계적으로 SRT를 만들면 어긋날 수가 없습니다. 무손실 왕복을 검증합니다.

자막 글자수 586 / 대본 글자수 586
완전 일치: True

문단 경계를 TTS 자막에 고정한다

글자 수 비례로만 배분하면 뒤로 갈수록 자막이 앞서 나갑니다. 문단마다 낭독 속도가 다르기 때문입니다.

타입캐스트가 주는 SRT는 문단 단위(한 블록이 12~13초)라 자막으로는 못 씁니다. 그런데 문단이 언제 시작하고 끝나는지는 정확합니다. 그걸 못으로 박고 문단 안에서만 나눕니다.

문단글자 수 비례문단 고정
4−0.77초+0.01초
5−1.63초+0.00초
6−0.87초+0.12초

최대 어긋남 1.63초 → 0.12초. 13배 좋아졌습니다. TTS 도구가 자막 파일을 주면 버리지 말고 받아 둡니다. 자막으로 쓰려는 게 아니라 기준점으로 쓰려는 것입니다.

부수 효과 — 줄바꿈도 여기서 잡힌다

캡컷은 자막 한 줄에 12~14자가 들어가고, 넘치면 어절 중간에서 자릅니다(아니었습니 / 다, 반대로 가장 심하게 속 / 은). 생성기에서 블록 24자 상한 + 어절 경계에서만 분할해 원천 차단합니다.

SRT 만들 때 낸 버그

f"{start} --> {end}\n{text}".replace('.', ',')   # ← 이러면 자막 본문의 마침표까지 쉼표가 된다

타임스탬프 변환은 전용 함수로 분리합니다. 그리고 HH:를 빠뜨리면 어떤 플레이어는 조용히 무시합니다.

왕복을 없앤 방법 — 조각을 미리 굽는다

1편에서 내보내기를 네 번 했습니다. 2·3편에서는 캡컷에 트리밍을 시키지 않았습니다.

컷 표대로 ffmpeg로 조각을 미리 구워 cuts/01.mp4 … 17.mp4로 넘깁니다. 캡컷은 전부 골라 순서대로 놓기만 합니다. 자를 것도 없고 키프레임을 찍을 것도 없습니다.

001002003
방식캡컷에서 트리밍cuts/ 재단cuts/ 재단 + 이미지 굽기
오디오 정렬0.15초0.05초0.05초(한 번은 0.00초)
내보내기4회1회3회(전부 자막 높이)

프레임 단위로 자릅니다. 초 단위로 자르면 24fps 경계에서 반올림이 누적돼 마지막에 어긋납니다. 시작 시각을 프레임으로 환산하고 길이를 역산하면 합계가 나레이션과 정확히 맞습니다.

17구간 · 1740프레임 · 72.500초
나레이션            72.500초

3편은 이미지 8장도 여기서 같이 구웠습니다 — zoompan으로 줌·팬을 입혀 영상으로 만들었습니다(5편). 움직임을 바꾸려면 컷 표의 값만 고치고 그 번호만 다시 굽습니다.

캡컷에 트리밍을 시키지 않으면 오차가 낄 자리가 없다.

캡컷 실무 요약

  1. 오디오를 먼저 깐다. 나레이션이 기준선이고 영상이 거기 맞춰진다
  2. 컷은 말이 끝나는 지점에서 전환한다
  3. 출처·수치는 자막 트랙과 다른 트랙에 올린다(같은 트랙이면 자막이 밀린다)
  4. 자막을 옮길 때 모서리 핸들을 잡으면 크기가 바뀐다 — 가운데를 잡는다
  5. 오디오 내보내기는 별도 음원 파일 옵션이지 영상 소리와 무관하다

전 구간이 일정하게 +2.4초면 앞이 잘린 것이고, 중간에서 튀면 그 지점이 삭제된 것이다.

음소거로 보는 시청자에게는 자막이 곧 원문이다.


이 시리즈는 제가 실제로 만든 유튜브 쇼츠 한 편(완성본 보기)의 제작 기록입니다. 문화인류학·심리학 소재로 상식을 뒤집는 지식 쇼츠를 만듭니다.

발행 2026-08-19 · 최종 확인 2026-08-20(3편 반영)