핵심 답: 영상의 시작·중간·끝에서 화면과 소리의 같은 사건을 찾고, 소리가 40ms 넘게 빠르거나 60ms 넘게 늦은지 먼저 점검합니다(EBU R37-2007 Table 1을 작업 참고선으로 사용). 입 모양이 늦어 보이면 소리가 앞선 경우일 수 있습니다. 아래 합성 데모는 사건 시점을 프레임에 맞췄지만 실제 발화는 화면·음향 기준점이 불명확할 수 있습니다. 3지점 표본이 참고선 안에 들어도 영상 전체의 립싱크 통과를 뜻하지 않으므로 끝까지 보고 듣습니다.

2026년에도 AI 립싱크가 소리와 어긋날 때 고치는 법을 묻는 영상과 질문이 계속 올라옵니다. 그런데 “조금 늦다”는 느낌만으로는 어디를 얼마나 밀어야 할지 정할 수 없습니다. 이 글은 무료 도구(ffmpeg·ffprobe)로 어긋남을 ms 단위로 재고, 기준 수치와 비교해 고칠지 말지를 정하는 절차를 정리합니다. 아래 숫자는 모두 우리가 직접 만든 합성 데모 클립에서 잰 값입니다.

3지점 측정표 (합성 데모 클립)

아래 표는 우리가 ffmpeg로 만든 합성 데모 클립(12초, 30fps, 흰 화면 1프레임과 1kHz 삑 소리를 세 번 넣음)을 잰 실측값입니다. 실제 AI 립싱크 영상의 측정값이 아닙니다. 시작은 일부러 맞추고, 중간은 소리를 1프레임 늦게, 끝은 소리를 2프레임 빠르게 넣었습니다.

지점 화면 프레임 화면 시각(초) 소리 시작(초) 차이(ms) 방향 판정
시작 30 1.000 1.000 +0.0 일치 통과
중간 180 6.000 6.033 +33.3 소리 늦음 통과
끝 330 11.000 10.933 -66.7 소리 빠름 = 입 모양 늦음 실패

차이 = 소리 시작 − 화면 시각입니다. +는 소리가 늦은 쪽(한계 60ms), −는 소리가 빠른 쪽(한계 40ms)입니다.

합성 데모 클립 10.900~11.067초 6프레임. 11.000초 흰 화면이 화면 기준점이고, 소리는 10.933초에 먼저 시작해 2프레임 빠르다
이미지를 눌러 원본을 새 창에서 확인할 수 있습니다. 끝 지점을 실제로 뽑은 6장입니다. 파일 이름 숫자가 그 장의 화면 시각(ms)이고, 흰 화면은 f_011000.png 한 장뿐입니다. 소리는 10.933초에 시작해 66.7ms 먼저 나옵니다.

데모 클립 직접 만들기

아래 명령은 위 표의 사건 배치를 재현하는 데모입니다. 검은 화면에서 30프레임(1.000초)·180프레임(6.000초)·330프레임(11.000초)만 흰색이고, 1,000Hz 삑 소리를 1.000초·6.033초·10.933초에 넣습니다. 위 표를 잰 원래 파일과 바이트까지 같다는 뜻은 아니며, 실제 AI 발화의 측정값이나 영상 전체의 립싱크 통과를 뜻하지도 않습니다. 새 빈 폴더에서 실행하세요.

ffmpeg -hide_banner -loglevel error -f lavfi -i "color=c=black:s=320x180:r=30:d=12" -f lavfi -i "aevalsrc='0.5*sin(2*PI*1000*t)*(between(t,1,1.1)+between(t,6.0333,6.1333)+between(t,10.9333,11.0333))':s=48000:d=12" -vf "drawbox=x=0:y=0:w=iw:h=ih:color=white:t=fill:enable='eq(n,30)+eq(n,180)+eq(n,330)'" -c:v libx264 -pix_fmt yuv420p -c:a aac -b:a 128k -shortest demo.mp4

화면 쪽 기준점(흰 프레임 시각)은 다음 명령으로 읽습니다.

ffmpeg -hide_banner -nostats -i demo.mp4 -an -vf "signalstats,metadata=select:key=lavfi.signalstats.YAVG:value=200:function=greater,metadata=print:key=lavfi.signalstats.YAVG:file=-" -f null - 2>&1 | grep pts_time

소리 쪽은 아래 재는 법 4단계 가운데 3단계의 silencedetect 명령에서 clip.mp4 대신 demo.mp4를 넣어 읽습니다.

이 명령으로 새로 만든 demo.mp4를 2026년 10월 13일 FFmpeg 9.0.1(macOS)에서 잰 결과, 흰 프레임의 pts_time은 1·6·11초, silence_end는 1.000021·6.033313·10.933312초였습니다(맨 끝에 나오는 12초 값은 파일 끝입니다). 소리 시작 − 화면 시각은 +0.0·+33.3·-66.7ms로, 위 표와 같은 값입니다. 표를 잰 원래 파일을 다시 잰 값이 아니라 새로 만든 파일의 값입니다. AAC 인코딩을 거친 파일이라 소수 여섯째 자리 값은 환경에 따라 다를 수 있습니다.

재는 법 4단계

  1. 프레임레이트를 확인합니다. ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate -of csv=p=0 clip.mp4 결과가 30/1이면 1프레임은 33.3ms, 24/1이면 41.7ms입니다.
  2. 시작·중간·끝에서 쉼 바로 뒤의 ㅃ·ㅂ·ㅍ처럼 입술이 닫혔다가 터지는 소리를 하나씩 고릅니다. 그 소리 조금 앞(예: 5.90초)부터 6장을 뽑습니다. ffmpeg -v error -copyts -ss 5.90 -i clip.mp4 -frames:v 6 -fps_mode passthrough -enc_time_base 1/1000 -frame_pts 1 f_%06d.png 뽑힌 장 가운데 입술이 처음 떨어지는 장을 찾습니다. 파일 이름 숫자가 그 장의 화면 시각(ms)이라 프레임레이트와 상관없이 그대로 씁니다(데모 중간 지점은 f_006000.png).
  3. 무음 뒤 소리가 나타나는 후보 시각을 찾습니다. ffmpeg -hide_banner -nostats -copyts -i clip.mp4 -af silencedetect=noise=-30dB:d=0.02 -f null - 2>&1 | grep silence_end 출력에서 그 지점과 가장 가까운 silence_end 값(초)은 설정한 무음 구간이 끝나는 후보 시점입니다. 무음 임계값을 넘었다고 해당 자음이 시작했다고 확정할 수는 없습니다. 합성 데모의 독립된 삑 소리에는 사용할 수 있지만, 실제 말소리는 해당 입술 움직임과 대응하는 소리인지 파형과 재생으로 확인하세요. 배경음·잡음·연속 발화로 기준점이 분명하지 않으면 이 방법만으로 ms 값을 확정하거나 통과로 판정하지 않습니다. 2단계와 똑같이 -copyts를 넣어야 두 시각의 기준이 같아집니다.
  4. silence_end × 1000 − 파일 이름 숫자가 −40 ~ +60 안이면 통과입니다(데모 중간: 6033.3 − 6000 = +33.3). 벗어나면 그 구간 오디오를 프레임 단위로 밀거나 당긴 뒤 같은 지점을 다시 잽니다.

재고 나서 편집자나 외주에 수정 요청을 보낼 때는 지점별 시간코드와 잰 값, 완료 기준(예: 끝 지점 −40ms 안쪽)을 같이 적으면 되돌아오는 일이 줄어듭니다. 요청서 양식은 영상 피드백을 수정 요청으로 바꾸는 법: 시간코드·관찰·완료 기준 검수표에 정리해 두었습니다.

기준 수치: EBU와 ITU

  • EBU R37-2007 Table 1: 소리가 화면보다 빠른 쪽 40ms, 늦은 쪽 60ms까지. 이 글의 통과 기준입니다.
  • ITU-R BT.1359-1(1998): 시청자 평가 실험 평균으로, 어긋남을 알아채기 시작하는 값이 소리 빠름 약 45ms·소리 늦음 약 125ms, 받아들일 수 있는 한계가 소리 빠름 약 90ms·소리 늦음 약 185ms입니다.

부호에 주의해야 합니다. ITU 문서는 +를 ‘소리가 앞섬’으로 적어 +45 / −125ms, +90 / −185ms로 표기합니다. 이 글의 표는 +가 ‘소리 늦음’이라 부호를 뒤집어 옮겼습니다. 우리 표 기준으로는 감지 한계가 −45 / +125ms, 수용 한계가 −90 / +185ms입니다.

두 값 모두 방송 신호를 위한 기준입니다. YouTube 같은 플랫폼의 업로드 조건이나 의무 기준이 아니며, 여기서는 고칠지 말지 정하는 참고선으로만 씁니다.

프레임으로 재는 방법의 한계

입술이 실제로 떨어진 순간은 두 프레임 사이일 수 있습니다. 그래서 잰 값이 실제보다 최대 1프레임(30fps 33.3ms)까지 − 쪽으로 치우쳐 나옵니다. 합성 클립으로 확인해 보니, 설계상 −30.0ms(통과)였던 지점이 −57.0ms(실패)로, 설계상 +75.0ms(실패)였던 지점이 +52.0ms(통과)로 재졌습니다. 위 측정표의 클립은 기준점을 프레임에 딱 맞춰 만들어 이 오차가 없습니다.

같은 EBU 문서에 적힌 단계별 정확도(5ms 빠름 ~ 15ms 늦음)는 30fps 1프레임보다 좁아서, 프레임을 넘겨 보는 이 방법으로는 확인할 수 없습니다.

자주 묻는 질문

24fps 영상에서는 기준이 어떻게 달라지나요?

24fps는 1프레임이 41.7ms입니다. 소리가 1프레임만 빨라도 40ms 한계를 넘어 실패입니다. 소리가 늦은 쪽은 1프레임(41.7ms)까지 통과입니다. 같은 영상이라도 프레임레이트부터 확인해야 하는 이유입니다.

왜 ㅁ 소리는 빼고 재나요?

ㅁ은 입을 닫은 채로 콧소리가 먼저 나서, 소리 시작과 입술이 떨어지는 순간이 원래 일치하지 않습니다. ㅃ·ㅂ·ㅍ처럼 입술이 터지면서 소리가 나는 자음을 고르면 두 시각을 같은 순간으로 비교할 수 있습니다.

EBU와 ITU 숫자가 다른 이유는?

두 문서가 말하는 대상이 다릅니다. ITU-R BT.1359-1의 값은 시청자가 어긋남을 알아채거나 받아들이는 평균 한계이고, EBU R37-2007의 값은 방송 신호에서 지키도록 권고한 범위입니다. EBU 범위(−40 / +60ms)는 ITU 감지 한계(−45 / +125ms) 안쪽에 있어서, 이 글은 더 좁은 EBU 값을 기준선으로 씁니다.

한계 근처 값이 나오면 어떻게 하나요?

−40ms를 1프레임 안쪽으로 넘긴 실패(−73 ~ −40ms)와 +60ms에 1프레임 안쪽으로 붙은 통과(+27 ~ +60ms)는 앞에서 말한 1프레임 치우침 때문에 판정이 뒤집힐 수 있습니다. 이 범위가 나오면 같은 지점 근처의 다른 단어로 한 번 더 잽니다.

참고자료

측정 파일: 이 글을 위해 ffmpeg로 직접 만든 30fps 합성 데모 클립과 그 측정 기록입니다. 상품·제휴 링크는 없습니다.

함께 보면 좋은 글: 내보낸 파일의 소리 크기(LUFS)와 트루 피크를 ffmpeg로 재는 법


이 글을 쓴 사람

작성·검토 방식 · 오류 제보·문의