블로그로 돌아가기
2026년 8월 13일Sergei Solod15 분 읽기

H.264로 실제 운영 영상 하나를 약 280 MB에서 약 50 MB로 줄인 방법

실제 운영 환경의 영상 하나가 H.264 구성을 CRF 28, x264 veryslow, 720p급 해상도 상한, 실제로 필요한 프레임률, 무리하지 않은 디코더 요구 조건을 중심으로 다시 설계한 뒤 약 280 MB에서 약 50 MB로 줄었다. 더 이전 단계에서는 같은 예시가 약 350 MB에서 238 MB로 이미 줄어 있었고, 옛 라이브러리 조사에서는 초당 수 메가비트급 H.264 파일이 흔했다.

H.264FFmpegx264영상 압축웹 성능미디어 최적화

이 최적화를 실제 숫자로 체감하게 만든 사례는 단순했습니다. 제가 추적하던 실제 실운영 영상 하나가 새 H.264 정책 적용 후 약 280 MB에서 50 MB로 줄었습니다. 약 5.6배 작아진 것이고, 대략 230 MB, 원래 크기의 약 82%를 절약한 셈입니다.

AV1, HEVC, VP9로 갈아타서 얻은 결과가 아닙니다. 실운영 출력은 계속 MP4 안의 H.264였습니다. 바꾼 것은 코덱 주변의 정책이었습니다. 불필요한 픽셀을 줄이고, 쓸모없는 시간 샘플을 줄이고, 품질 목표를 훨씬 덜 보수적으로 잡고, 인코딩 때 한 번 더 많은 CPU를 쓰는 대신 디코더 요구사항은 의도적으로 제한했습니다.

대상 사용 조건도 분명했습니다. 짧은 일러스트·애니메이션 소재, 약 80%의 모바일 트래픽, 반복해서 발생하는 비용은 대역폭, 그리고 사전 인코딩 시간은 큰 파일을 계속 전송하는 비용에 비하면 싸다는 조건입니다.

이전 정책과 새 정책은 대략 다음과 같았습니다.

이전 구성
H.264 Main @ Level 4.0
CRF 19
사전 설정 slow
최대 1920x1080 / 1080x1920
30 fps
refs = 3
B 프레임 = 3
GOP ≈ 2초
VBV ≈ 10M / 20M

새 구성
H.264 Main @ Level 3.1
CRF 28
사전 설정 veryslow
720p급을 상한으로 사용, 확대 없음
유효한 CFR, 보통 <= 30 fps
refs = 4
B 프레임 = 5
GOP ≈ 5초
VBV ≈ 4M / 8M
yuv420p / avc1 / faststart

실측 결과: 약 280 MB에서 약 50 MB로

실운영에서 나온 실측값은 여러 개지만, 전부 같은 실험은 아닙니다. 가장 커 보이는 백분율 하나를 고르는 것보다 이 차이를 분리해서 설명하는 것이 더 중요합니다.

측정 항목변경 전변경 후감소량
같은 영상의 직접 비교~280 MB~50 MB약 5.6× 작음 / 약 82% 감소
같은 파일의 이전 단계~350 MB~238 MB약 1.47× 작음 / 32% 감소

첫 번째 행이 제목을 가장 직접적으로 뒷받침합니다. 같은 구체적인 영상이 새 정책 전후로 약 280 MB에서 약 50 MB가 됐습니다. 이 정확한 조합의 비트레이트와 길이 줄은 복구된 옛 기록에서 찾지 못했기 때문에 만들어내지 않습니다. 크기 변화만으로도 충분합니다. 이 특정 파일은 약 5.6배 작아졌습니다.

~350→238 MB 사례는 같은 파일에서 더 이른 시점에 있었던 최적화입니다. 당시 결과는 약 1264×720, 30 fps, 약 500초, 오디오 없음, 약 3.8 Mbps였습니다. 계산도 관측된 크기와 맞습니다. 3.8 Mbps를 약 500초 전송하면 약 238 MB입니다. ~350 MB에서 이미 32% 줄였지만 제 대역폭 목표에는 여전히 너무 컸습니다.

이전 라이브러리 조사에서도 지나치게 큰 H.264가 한두 개의 특이 사례가 아니라는 점이 드러났습니다. 한 조사에는 실운영 영상 238개, 총 6.37 GB가 있었고 117개가 H.264, 121개가 AV1이었습니다. 101개는 20 MB 이상, 34개는 50 MB 이상이었습니다. 큰 H.264 일부는 다음과 같았습니다.

크기길이평균 비트레이트
121.0 MB4:253.83 Mbps
101.2 MB5:192.659 Mbps
92.78 MB4:442.735 Mbps
90.10 MB3:553.206 Mbps
89.74 MB4:412.676 Mbps

서로 다른 자료이므로 이 표는 직접적인 전후 비교 시험이 아니라 배경 자료입니다. 그래도 예전 3.8 Mbps 사례가 우연한 한 건이 아니었다는 점은 보여 줍니다. 옛 라이브러리의 여러 대용량 H.264 파일이 실제로 약 2.6–3.8 Mbps 범위에 있었습니다.

가장 중요한 최적화는 FFmpeg 옵션 하나가 아니었다

가장 크게 바뀐 것은 비용을 보는 방식이었습니다.

인코딩은 한 번 합니다. 전송은 누군가 파일을 요청할 때마다 반복됩니다.

실시간 영상에서는 약간의 비트레이트를 줄이기 위해 CPU를 훨씬 더 쓰는 것이 나쁜 선택일 수 있습니다. 제 파일은 오프라인에서 한 번 인코딩한 뒤 계속해서 전송됩니다. 이런 구조에서는 빠른 인코딩 때문에 이후 모든 요청에서 더 큰 파일을 보내야 한다면, 인코딩 시간을 10분 줄이는 것이 경제적으로 거의 의미가 없을 수 있습니다.

그래서 저에게는 -preset veryslow가 맞습니다. x264가 더 효율적인 표현을 찾는 데 CPU를 더 쓸 수 있다면 그 비용은 한 번만 지불하면 됩니다. 브라우저는 인코더의 탐색 과정을 반복하지 않고 완성된 비트스트림만 디코딩합니다.

기준은 단순해졌습니다. 한 번만 실행하는 단계에는 계산량을 쓰고, 반복되는 단계에서는 바이트를 아낀다.

최신 코덱을 쫓지 않고 H.264를 유지한 이유

H.264가 현재 가장 압축 효율이 높은 코덱이라고 주장하는 것은 아닙니다. 그렇지 않습니다. 전송 시스템이 여러 인코딩 버전을 보관하고 클라이언트마다 가장 적절한 버전을 선택할 수 있다면 최신 코덱은 매우 매력적입니다.

제 조건은 달랐습니다. URL 하나, 파일 하나, 코덱 하나, 그리고 모바일 사용자가 많은 환경에서 실제 재생 문제를 최대한 줄이는 것이 목표였습니다.

이 목적이라면 MP4 안의 H.264는 여전히 매우 안전한 기준입니다. Apple은 현재 Safari에서 정적 영상 파일을 제공할 때 웹 개발자에게 H.264로 인코딩한 MP4 파일을 사용하라고 안내합니다. Android의 현재 문서도 MP4의 H.264를 지원 형식으로 명시하고 Android 6.0 이상에서 Main 프로필 디코더를 필수로 요구합니다. H.264 재생 권장값에는 HD용 1280×720, 30 fps도 포함되지만, HD가 모든 기기에서 가능한 것은 아니라는 점도 함께 적혀 있습니다. 자세한 내용은 Android 지원 미디어 형식을 참고하면 됩니다.

그렇다고 최신 기기가 Main 프로필이나 레벨 3.1까지만 처리할 수 있다는 뜻은 아닙니다. 예를 들어 Apple의 HLS 가이드는 일반적으로 Main이나 Baseline 프로필보다 High 프로필을 선호합니다. 제가 Main@3.1을 고른 이유는 단일 정적 MP4에서 의도적으로 부담이 낮은 디코딩 요구사항을 원했기 때문이지, Apple이 이를 강제하기 때문이 아닙니다.

필요 없는 픽셀을 인코딩하지 않았다

해상도는 가장 큰 조절 요소 중 하나였습니다. 상한은 가로 영상에서 약 1280×720, 세로 영상에서 720×1280, 정사각형이나 방향이 섞인 소재에서는 약 960×960로 잡았습니다.

더 중요한 규칙은 상한에 맞추기 위해 억지로 확대하지 않는 것입니다.

원본이 900×600이라면 1280×720으로 키워도 디테일이 되살아나지 않습니다. 인코더가 표현해야 할 샘플만 늘어납니다. 1920×1080 원본은 720p급으로 줄일 수 있지만, 900×600 원본은 900×600 근처에 그대로 두면 됩니다. 상한은 최대값이지 목표값이 아닙니다.

단순한 이야기지만 불필요한 픽셀을 없애는 것이 복잡한 인코더 미세 조정보다 더 큰 차이를 만들 수 있습니다.

이 상한은 숫자가 보기 좋다는 이유로 정한 것이 아닙니다. 1920×1080 한 프레임은 2,073,600픽셀이고 1280×720은 921,600픽셀입니다. 따라서 1080p에서 720p로 내리는 것만으로도 인코더가 압축 결정을 시작하기 전에 공간 샘플의 약 55.6%가 줄어듭니다.

540p를 전체 기본값으로 쓰는 방안도 검토했습니다. 하지만 960×540은 518,400픽셀뿐이라 1280×720보다 43.75% 적고, 720p 샘플의 56.25%만 남습니다. 그림 중심 자료에서는 그 샘플이 가는 선, 눈, 머리카락, 손가락, 얼굴, 날카로운 윤곽을 표현합니다. 용량을 더 줄여야 한다면 공간 정보를 무조건 43.75% 더 버리기 전에 CRF를 조금 높인 결과부터 측정하겠습니다. 양자화는 다음 인코딩에서 바꿀 수 있지만, 축소로 사라진 세부 정보는 이미 되돌릴 수 없습니다.

그래서 720p급은 내가 쓰는 신중한 공통 상한이지, 540p가 나쁘다는 뜻이 아닙니다. 특정 파일에서는 측정 결과 540p가 더 나을 수 있습니다. 다만 근거 없이 그 되돌릴 수 없는 공간 손실을 전체 라이브러리 규칙으로 만들지 않습니다.

원본에 실제로 없는 프레임에 대역폭을 쓰지 않았다

프레임률도 곱셈처럼 영향을 줍니다. 애니메이션이 실제로 초당 약 16개의 유효한 시각 상태만 갖고 있다면 30 fps나 60 fps로 저장한다고 움직임이 자동으로 좋아지지는 않습니다. 반복되거나 합성된 시간 샘플이 늘어날 뿐이고, 그 샘플도 결국 파일 안에 표현해야 합니다.

제 정책은 원본의 유효한 리듬을 유지하면서 보통 30 fps 이하에 머무르는 것입니다. 이런 소재에서는 실제 원본을 제대로 설명한다면 12, 15, 16, 18, 20, 24, 25, 30 fps 모두 합리적일 수 있습니다.

생성된 출력에서는 깔끔한 고정 프레임률도 선호합니다. VFR 자체가 문제가 있는 것은 아닙니다. 다만 CFR을 쓰면 제 파이프라인에서 타임스탬프, 프레임 수, 길이 검사, 탐색, 이후 검증이 더 단순해집니다.

특정 FPS 숫자보다 중요한 원칙은 이것입니다. 원본에 없는 시간 정보 때문에 대역폭을 쓰지 않는다.

CRF 28은 이 작업을 위한 선택이지 마법의 숫자가 아니다

모든 클립을 같은 목표 비트레이트로 몰아가고 싶지 않았습니다. 거의 정적인 일러스트와 복잡한 움직임이 있는 장면은 보기 좋게 만들기 위해 같은 양의 비트가 필요하지 않습니다.

그래서 x264의 CRF 모드를 사용하고, 대역폭을 우선하는 이 일러스트 계열 소재에서는 대략 -crf 28에 정착했습니다. FFmpeg는 libx264의 CRF를 일정 품질 모드의 레이트 제어로 설명합니다. 자세한 내용은 FFmpeg 코덱 문서에서 확인할 수 있습니다.

CRF 28은 의도적으로 공격적인 값입니다. 필름 그레인, 노이즈가 많은 카메라 영상, 아주 작은 화면 글자, 또는 대역폭보다 원본 충실도가 더 중요한 작업에는 그대로 적용하지 않을 것입니다.

CRF 28이 언제나 지각적으로 투명하다는 것을 증명하는 보편적인 점수도 없습니다. 제 작업에 대해 말할 수 있는 범위는 더 좁습니다. 파일은 크게 작아졌고 일반 재생에서는 제 눈에 여전히 자연스럽게 보였습니다. 이것은 실무 관찰이지 CRF 28이 시각적으로 무손실이라는 주장이 아닙니다.

veryslow가 비싼 것은 인코더 쪽이지 디코더 쪽이라고 단정할 수 없다

제가 사용하는 프리셋은 -preset veryslow입니다. 느린 프리셋은 x264가 더 효율적인 예측과 코딩 결정을 찾을 시간을 늘려 줍니다. 대가는 인코딩 CPU와 시간입니다.

중요한 차이는 인코더의 탐색 비용과 디코더 복잡도가 같은 것이 아니라는 점입니다.

x264에는 매우 무거운 탐색을 시키면서도 완성되는 스트림에는 별도의 제약을 걸 수 있습니다. 제가 사용하는 보수적인 출력 조건은 다음과 같습니다.

H.264 Main 프로필
Level 3.1
8비트 yuv420p
avc1
refs = 4
B 프레임 = 5
B-pyramid = normal
오픈 GOP = 비활성화

FFmpeg는 CRF, 프리셋, 튜닝, 프로필 제한, 참조 프레임, B 프레임을 각각 따로 설정할 수 있게 해 줍니다. 저도 똑같이 생각합니다. 인코더는 충분히 깊게 탐색하게 두되, 재생 쪽은 평범하고 예측 가능하게 유지합니다.

GOP와 VBV는 품질 조절의 핵심이 아니라 안전장치다

이 짧은 점진적 재생용 클립에서는 최대 GOP를 약 5초로 둡니다. 30 fps에서는 약 -g 150, 24 fps에서는 -g 120, 16 fps에서는 -g 80입니다.

이건 제 작업에 맞춘 선택이지 보편 규칙이 아닙니다. 적응형 스트리밍은 제약이 다릅니다. 예를 들어 Apple의 HLS 제작 가이드는 2초마다 IDR을 두도록 권장합니다. 저는 그 HLS 규칙을 짧은 정적 점진적 MP4에 그대로 복사하지 않습니다.

추가로 대략 다음 값을 사용합니다.

-maxrate:v 4M
-bufsize:v 8M

이 값들은 비정상적인 비트레이트 급등을 막기 위한 상한입니다. “모든 영상을 4 Mbps로 인코딩한다”는 뜻이 아닙니다. 일반적인 비트 배분은 여전히 CRF가 담당하므로 단순한 클립은 매우 작아질 수 있습니다.

MP4 컨테이너도 최대한 평범하게 만들었다

avc1을 명시적으로 사용합니다. Apple의 현재 HLS 문서는 avc3보다 avc1 같은 샘플 형식을 권장합니다. 이 선택이 파일 크기를 줄인 것은 아니지만, 일반적인 MP4의 H.264를 만들겠다는 목표에는 잘 맞습니다.

-movflags +faststart도 사용합니다. FFmpeg의 포맷 문서에 따르면 faststart는 MP4의 moov 인덱스를 파일 앞쪽으로 옮깁니다. Android의 HTTP 스트리밍 요구사항도 MPEG-4에서 moovftyp 뒤, mdat 앞에 있어야 한다고 명시합니다.

ftyp
moov
mdat

faststart는 압축률을 높이지 않습니다. 점진적 HTTP 재생을 더 수월하게 만들 뿐입니다.

일반 SDR 출력에는 8비트 yuv420p를 사용하고 BT.709와 제한된 비디오 범위를 표시합니다. 클립에 오디오가 없으면 오디오 트랙을 억지로 만들지 않습니다. 이 일러스트 계열 콘텐츠에서는 -tune animation도 사용하지만, 이것은 콘텐츠 종류에 맞춘 선택이지 보편적인 호환성 조건의 일부는 아닙니다.

핵심 FFmpeg 설정

30 fps 일러스트 원본이라면 명령의 중심 부분은 대략 다음과 같습니다.

ffmpeg -i input \
  -c:v libx264 \
  -preset veryslow \
  -tune animation \
  -crf 28 \
  -profile:v main \
  -level:v 3.1 \
  -pix_fmt yuv420p \
  -tag:v avc1 \
  -refs 4 \
  -bf 5 \
  -g 150 \
  -maxrate:v 4M \
  -bufsize:v 8M \
  -x264-params "open-gop=0:b-pyramid=normal:nal-hrd=none" \
  -color_range tv \
  -color_primaries bt709 \
  -color_trc bt709 \
  -colorspace bt709 \
  -an \
  -movflags +faststart \
  output.mp4

스케일링과 프레임률 단계는 여기서 의도적으로 고정하지 않았습니다. 상한이 1280×720이라는 이유만으로 900×600 원본을 확대할 필요는 없고, 예시가 -g 150을 쓴다는 이유만으로 원래 프레임률이 낮은 애니메이션을 30 fps로 강제할 필요도 없습니다.

명령은 정책을 구현한 것이지 정책 그 자체는 아닙니다.

파일이 몇 배 작아진 이유

마법 같은 옵션 하나가 있었던 것은 아닙니다.

크기 감소는 서로 다른 낭비를 없애는 여러 결정을 겹친 결과입니다. 불필요한 픽셀과 프레임, 지나치게 보수적인 품질 목표, 효율보다 속도를 우선한 인코더 설정, 너무 잦은 키프레임, 필요 없는 데이터 스트림을 각각 줄였습니다.

그래서 “이 파일은 H.264다”라는 정보만으로는 크기를 거의 알 수 없습니다. 같은 원본을 H.264로 인코딩해도 해상도, 프레임률, 레이트 제어, 프리셋, GOP 구조, 프로필, 원본 전처리가 다르면 파일 크기는 크게 달라질 수 있습니다.

제 경우에는 코덱을 바꾸는 것보다 코덱 주변의 이런 결정을 바꾸는 것이 더 중요했습니다.

이 결과가 증명하지 않는 것

각 설정을 통제된 실험에서 하나씩 분리해 시험하지 않았기 때문에 veryslow, CRF 28, 해상도 축소, 프레임률 축소가 절감량의 몇 퍼센트를 각각 차지했는지 정확히 말할 수 없습니다.

모든 CRF 28 출력이 지각적으로 투명하다고 주장할 수도 없습니다. “눈에 띄는 화질 저하가 없었다”는 것은 보통 크기로 이 일러스트 계열 소재를 본 제 관찰일 뿐, 임의의 모든 영상에 대한 과학적 보장이 아닙니다.

단일 H.264 파일이 모든 사이트에 맞는 구조라고 말하는 것도 아닙니다. 여러 인코딩 버전, 적응형 스트리밍, HDR, 4K, 코덱 협상은 이 판단을 바꿉니다.

제가 실제로 말할 수 있는 것은 더 좁습니다. 대역폭을 우선하고 모바일 이용자가 많으며 인코딩 시간은 저렴하고 예측 가능한 재생이 중요한 짧은 일러스트·애니메이션 라이브러리에서는 이 설정으로 파일을 몇 배 줄이면서 일반 재생 시 자연스러운 화면을 유지할 수 있었습니다.

이 결과가 내 최적화 방식을 어떻게 바꿨는가

예전에는 영상 최적화를 주로 인코더 설정 문제라고 생각했습니다. 지금은 파일의 전체 수명 동안 드는 비용 문제라고 봅니다.

인코더는 한 번만 실행될 수 있습니다. 바이트는 네트워크를 수천 번, 수백만 번 지나갈 수 있습니다.

그러면 “비싸다”는 말의 의미가 달라집니다.

CPU를 한 번 더 쓰는 것은 괜찮습니다. 그보다 확대 과정에서 만들어진 픽셀, 유효한 움직임을 더하지 않는 프레임, 콘텐츠에 필요하지 않은 비트레이트를 이후의 모든 요청에서 계속 보내는 것이 더 아깝습니다.

코덱은 평범한 그대로였습니다. MP4 안의 H.264. 최적화는 그 주변에서 이루어졌습니다.

이 작업에서는 어떤 FFmpeg 옵션 하나보다 이 원칙이 더 중요합니다. 한 번만 지불하는 비용이 아니라 반복해서 지불하는 비용을 최적화한다.