느린 Preset이 항상 이기지는 않는다
개요
FFmpeg으로 libx264/libx265 인코딩을 걸 때 -preset 옵션에는 ultrafast부터 veryslow까지 여러 단계가 있습니다. 흔히 "느릴수록 화질이 좋다"고 알려져 있지만, 정확히는 preset이 화질 자체를 바꾸는 옵션이 아닙니다. 인코더가 각 프레임을 인코딩할 때 얼마나 많은 조합을 탐색하느냐(움직임 추정 범위, 모드 결정 후보 수, RDO 정밀도 등)를 조절하는 옵션입니다.
그래서 preset이 실제로 무엇을 바꾸는지는 인코딩 모드에 따라 다르게 나타납니다. CRF(품질 기반)로 인코딩할 때는 preset이 "같은 화질을 내는 데 필요한 비트 수"에 영향을 주고, 목표 비트레이트를 고정했을 때는 "같은 비트 예산 안에서 뽑아낼 수 있는 화질"에 영향을 줍니다. 이 둘은 서로 다른 이야기인데 종종 뭉뚱그려집니다.
이 글에서는 720p 소스 하나를 놓고 libx264 preset 5단계(ultrafast, fast, medium, slow, veryslow)를 동일 CRF 23으로 각각 인코딩해 소요 시간과 출력 비트레이트, VMAF를 실측하고, 같은 5단계를 이번엔 동일 목표 비트레이트(2Mbps)로 다시 인코딩해 화질이 어떻게 갈리는지 비교합니다.
libx265로도 일부 preset을 같은 방식으로 실측하고, 마지막으로 Tencent Cloud MPS에는 이런 preset 손잡이가 어떤 형태로 존재하는지 API로 직접 확인합니다.
실무 시나리오
트래픽이 몰리는 시간대에 VOD 플랫폼의 인코딩 큐가 밀렸다고 가정하겠습니다. 대기 중인 작업이 쌓이자 담당자는 preset을 slow에서 fast로, 혹은 fast에서 ultrafast로 낮춰서 처리량을 늘리기로 했습니다. 화질 설정(CRF 값)은 손대지 않았으니 "화질은 그대로에 속도만 빨라진다"고 판단한 겁니다.
며칠 뒤 스토리지와 CDN 트래픽 비용 리포트를 보니 같은 콘텐츠 볼륨인데도 저장 용량과 송출 비트레이트가 눈에 띄게 늘어나 있었습니다. 화질 저하 민원은 없었는데 비용만 올라간 셈입니다.
반대로 만약 이 파이프라인이 CRF가 아니라 고정 비트레이트(ABR 상한)로 돌아가는 구조였다면, 이번엔 파일 크기와 트래픽 비용은 그대로인데 화질 민원이 슬금슬금 들어왔을 가능성이 큽니다.
두 시나리오 모두 "preset만 낮췄다"는 같은 조치에서 출발했지만, 결과는 인코딩 파이프라인이 CRF 기반인지 비트레이트 기반인지에 따라 완전히 다른 방향으로 나타납니다. 어느 쪽이든 감으로 판단하지 않으려면 preset이 각 모드에서 정확히 무엇을 바꾸는지 실측해둘 필요가 있습니다.
테스트 환경과 소스
- FFmpeg 8.0 (Homebrew 빌드, libx264/libx265/libvmaf 포함), macOS, Apple Silicon
- 소스: 1280x720, 24fps, H.264, 오디오 트랙 없음, 5.04초(121프레임), 원본 비트레이트 약 15.9Mbps의 모션이 많은 합성 영상
$ ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate,bit_rate \
-show_entries format=duration -of default=noprint_wrappers=0 src.mp4
codec_name=h264
width=1280
height=720
r_frame_rate=24/1
bit_rate=15914415
duration=5.0416671. 같은 CRF, 다른 Preset: 화질은 거의 그대로, 파일 크기는 크게 벌어집니다
먼저 CRF 23을 고정하고 preset만 바꿔가며 인코딩했습니다.
$ time ffmpeg -y -i src.mp4 -an -c:v libx264 -preset ultrafast -crf 23 -pix_fmt yuv420p out.mp4
$ time ffmpeg -y -i src.mp4 -an -c:v libx264 -preset fast -crf 23 -pix_fmt yuv420p out.mp4
$ time ffmpeg -y -i src.mp4 -an -c:v libx264 -preset medium -crf 23 -pix_fmt yuv420p out.mp4
$ time ffmpeg -y -i src.mp4 -an -c:v libx264 -preset slow -crf 23 -pix_fmt yuv420p out.mp4
$ time ffmpeg -y -i src.mp4 -an -c:v libx264 -preset veryslow -crf 23 -pix_fmt yuv420p out.mp4각 결과물은 ffprobe로 크기/비트레이트를, libvmaf 필터로 원본 대비 VMAF를 실측했습니다.
$ ffmpeg -y -i out.mp4 -i src.mp4 -lavfi \
"[0:v]setpts=PTS-STARTPTS[dist];[1:v]setpts=PTS-STARTPTS[ref];[dist][ref]libvmaf" -f null -| Preset | 인코딩 시간(실측) | 초당 처리 프레임 | 출력 비트레이트 | 파일 크기 | VMAF |
|---|---|---|---|---|---|
| ultrafast | 0.38s | 약 319fps | 7,005.7kbps | 4.31MB | 97.77 |
| fast | 1.11s | 약 110fps | 4,394.2kbps | 2.71MB | 96.93 |
| medium | 1.53s | 약 79fps | 4,302.9kbps | 2.65MB | 97.38 |
| slow | 2.02s | 약 60fps | 4,288.2kbps | 2.64MB | 97.64 |
| veryslow | 9.17s | 약 13fps | 4,016.6kbps | 2.48MB | 97.85 |
veryslow는 ultrafast보다 약 24배 더 오래 걸렸습니다(9.17초 대 0.38초). 반대로 파일 크기는 ultrafast가 veryslow보다 약 1.74배 더 컸습니다(4.31MB 대 2.48MB). 여기까지는 "느린 preset이 같은 화질을 더 적은 비트로 담아낸다"는 통념과 정확히 일치합니다.
그런데 VMAF 열을 보면 이야기가 좀 더 미묘합니다. veryslow(97.85)가 ultrafast(97.77)보다 근소하게 높긴 하지만, 그 차이는 0.08점뿐이고 오히려 fast(96.93)가 이 다섯 중 가장 낮은 점수를 받았습니다. veryslow가 가장 느리다고 해서 VMAF가 가장 높게 나온 것도 아니고, ultrafast가 가장 빠르다고 해서 화질이 가장 나쁜 것도 아니었습니다.
이건 이 소스에서 CRF 23이 이미 VMAF 97점대라는 사실상 원본과 구분하기 어려운 구간(VMAF 93점 이상은 통상 일반 시청자가 원본과 구분하지 못하는 수준으로 봅니다)에 다섯 preset 모두를 올려놓았기 때문입니다. 화질이 이미 포화된 상태에서는 preset 차이가 VMAF 소수점 단위의 잡음으로만 드러나고, 실제로 의미 있게 벌어지는 값은 화질이 아니라 그 화질을 담는 데 든 비트 수, 즉 파일 크기입니다.
"같은 CRF"는 "같은 파일 크기"를 보장하지 않을 뿐 아니라, 화질이 이미 포화 구간에 들어가 있다면 "같은 CRF"의 실질적인 차이는 화질표가 아니라 크기표에서 읽어야 한다는 뜻입니다.
2. 같은 목표 비트레이트, 다른 Preset: 크기는 같지만 화질이 벌어집니다
이번에는 반대로 목표 비트레이트를 2Mbps로 고정하고 preset만 바꿨습니다.
$ ffmpeg -y -i src.mp4 -an -c:v libx264 -preset ultrafast -b:v 2M -maxrate 2M -bufsize 4M -pix_fmt yuv420p out.mp4
$ ffmpeg -y -i src.mp4 -an -c:v libx264 -preset fast -b:v 2M -maxrate 2M -bufsize 4M -pix_fmt yuv420p out.mp4
$ ffmpeg -y -i src.mp4 -an -c:v libx264 -preset medium -b:v 2M -maxrate 2M -bufsize 4M -pix_fmt yuv420p out.mp4
$ ffmpeg -y -i src.mp4 -an -c:v libx264 -preset slow -b:v 2M -maxrate 2M -bufsize 4M -pix_fmt yuv420p out.mp4
$ ffmpeg -y -i src.mp4 -an -c:v libx264 -preset veryslow -b:v 2M -maxrate 2M -bufsize 4M -pix_fmt yuv420p out.mp4| Preset | 인코딩 시간(실측) | 파일 크기 | 출력 비트레이트 | VMAF |
|---|---|---|---|---|
| ultrafast | 0.32s | 1.42MB | 2,303.3kbps | 73.18 |
| fast | 0.88s | 1.40MB | 2,269.4kbps | 81.36 |
| medium | 1.02s | 1.37MB | 2,222.1kbps | 82.33 |
| slow | 1.27s | 1.34MB | 2,175.1kbps | 82.30 |
| veryslow | 6.16s | 1.32MB | 2,144.0kbps | 84.21 |
VMAF 점수 차이가 화면에서도 그대로 보입니다. 같은 2Mbps 상한, 같은 소스입니다.
ultrafast (2Mbps 고정, VMAF 73.18)
veryslow (2Mbps 고정, VMAF 84.21)
ultrafast 쪽은 움직임이 많은 구간에서 블록킹과 뭉개짐이 뚜렷합니다. 파일 크기가 다섯 preset 모두 1.3~1.4MB 대로 거의 같습니다(비트레이트 상한을 걸었으니 당연한 결과입니다). 대신 VMAF가 크게 벌어집니다. ultrafast는 73.18점으로 이 다섯 중 가장 낮았고, veryslow는 84.21점으로 11점 넘게 높았습니다.
ultrafast에서 fast로만 preset을 한 단계 올려도 8점 넘게 뛰어오르고(73.18 → 81.36), 그 이후 medium, slow, veryslow 구간에서는 상승 폭이 완만해집니다. 즉 "preset을 낮춰서 속도를 벌 때 가장 비싼 대가를 치르는 구간은 ultrafast 근처"라는 뜻입니다.
이 결과를 1번 테스트와 나란히 놓으면 이 글의 핵심을 정확히 설명할 수 있습니다. 같은 CRF에서는 preset을 낮춰도 화질(VMAF)은 거의 그대로고 대신 파일이 커집니다. 같은 비트레이트에서는 파일 크기는 그대로고 대신 화질이 눈에 띄게 낮아집니다.
"preset을 낮추면 화질이 떨어진다"는 말은 비트레이트가 고정된 파이프라인에서만 정확하고, CRF 기반 파이프라인에서는 오히려 "화질은 그대로인데 비용(파일 크기)이 올라간다"로 읽어야 합니다. 앞서 실무 시나리오에서 두 갈래로 나눠 설명한 이유가 여기에 있습니다.
3. libx265 preset도 같은 패턴일까
libx265로도 몇 개 preset을 CRF 23으로 인코딩해봤습니다.
$ time ffmpeg -y -i src.mp4 -an -c:v libx265 -preset fast -crf 23 -pix_fmt yuv420p out.mp4
$ time ffmpeg -y -i src.mp4 -an -c:v libx265 -preset medium -crf 23 -pix_fmt yuv420p out.mp4
$ time ffmpeg -y -i src.mp4 -an -c:v libx265 -preset veryslow -crf 23 -pix_fmt yuv420p out.mp4| Preset | 인코딩 시간(실측) | 인코더 자체 보고 fps | 파일 크기 | 출력 비트레이트 | VMAF |
|---|---|---|---|---|---|
| fast | 3.46s | 36.12fps | 1.99MB | 3,238.1kbps | 95.68 |
| medium | 4.93s | 25.13fps | 1.96MB | 3,194.1kbps | 97.22 |
| veryslow | 80.10s | 1.51fps | 2.10MB | 3,412.6kbps | 99.58 |
x264와 다른 점이 두 가지 보입니다. 첫째, 같은 CRF 23에서 x265 파일 크기(1.96~2.10MB)가 x264(2.48~4.31MB)보다 전반적으로 작습니다. 코덱 자체의 압축 효율 차이입니다.
둘째, x265에서는 veryslow가 fast/medium보다 VMAF가 뚜렷하게 높게 나왔습니다(99.58 대 95.68~97.22). x264 CRF 23 테스트에서 preset 간 VMAF 차이가 1점 이내로 거의 없었던 것과 대조적입니다. 다만 x265 veryslow가 파일 크기는 오히려 fast/medium보다 약간 더 큽니다(2.10MB 대 1.96~1.99MB). 즉 x265 veryslow는 "더 적은 비트로 같은 화질"이 아니라 "비슷하거나 조금 더 많은 비트로 훨씬 높은 화질"을 택한 결과로 보입니다.
가장 눈에 띄는 숫자는 속도입니다. x265 veryslow는 80.10초가 걸렸는데, 이건 x264 veryslow(9.17초)보다 8.7배, x264 ultrafast(0.38초)보다는 무려 211배 느립니다. "preset을 veryslow로 올린다"는 조치와 "코덱을 x265로 바꾼다"는 조치가 겹치면 처리 시간에 미치는 영향이 단순히 더해지는 게 아니라 곱해진다는 뜻입니다.
배치 인코딩 작업의 소요 시간을 예상할 때 이 조합은 preset 표 하나만 보고 추정하면 실제 처리 시간을 크게 과소평가하기 쉬운 지점입니다.
4. Tencent Cloud MPS의 x264 preset 부재
로컬에서 확인한 것과 같은 종류의 손잡이가 Tencent Cloud MPS에도 있는지 DescribeTranscodeTemplates로 먼저 확인했습니다.
$ python3 -c "
from mps_pipeline import call_tc3
status, body = call_tc3('mps', 'DescribeTranscodeTemplates', '2019-06-12', {'Limit': 5})
print(status, body)
"응답에 담긴 VideoTemplate 필드 목록에는 Codec, Fps, Bitrate, Gop, VideoProfile, VideoLevel 같은 익숙한 항목들과 함께 Subjective, ScenarioBased, SceneType, CompressType 같은 낯선 필드가 있었습니다.
VideoProfile, VideoLevel과 나란히 있지만 이름만으로는 역할이 짐작되지 않아서, 로컬에 설치된 Tencent Cloud Python SDK(tencentcloud-sdk-python-mps)의 VideoTemplateInfo 모델 주석을 확인했습니다.
CompressType은 문서 주석에 이렇게 정의돼 있었습니다.
ultra_compress: 극치압축, 표준압축 대비 화질을 일정 수준 보장하면서 코드율을 최대한 압축
standard_compress: 종합최적, 압축률과 화질의 균형(기본값)
high_compress: 코드율 우선, 파일 용량 감소를 우선하며 화질 손실 가능
low_compress: 화질 우선, 압축 파일 용량이 상대적으로 클 수 있음이건 x264의 ultrafast~veryslow처럼 "인코더가 얼마나 오래 탐색하는가"를 직접 노출하는 필드는 아니지만, "속도/용량과 화질 사이 어디에 무게를 둘 것인가"를 고르는 선택지라는 점에서 이 글의 주제와 가장 가까운 MPS 옵션입니다.
다만 주석에 ScenarioBased가 1일 때만 SceneType과 CompressType 값이 적용된다는 조건이 달려 있었습니다.
첫 시도: 요청 거부
RawParameter로 즉석 인코딩을 걸면서 ScenarioBased: 1과 CompressType을 지정했더니, 태스크가 정확한 이유를 대며 실패했습니다.
"ErrCode": 70000, "ErrCodeExt": "InvalidParameterValue.ScenarioBased", "Message": "Only supports TEHD"ScenarioBased(그리고 이에 딸린 SceneType/CompressType)는 일반 RawParameter 인코딩 어디서나 쓸 수 있는 필드가 아니라, MPS의 극속고화질(TEHD, Tencent Extreme High Definition) 기능이 켜져 있을 때만 동작하는 옵션이었습니다.
TEHDConfig를 VideoTemplate과 같은 층위에 추가해서 다시 시도했습니다.
task = {
"OutputStorage": output_storage,
"OutputObjectPath": out_prefix + "{Definition}.{format}",
"Definition": 0,
"RawParameter": {
"Container": "mp4",
"RemoveAudio": 1,
"TEHDConfig": {"Type": "TEHD-100"},
"VideoTemplate": {
"Codec": "libx264",
"Fps": 24,
"Mode": "VCRF",
"Vcrf": 23,
"Bitrate": 8000,
"ResolutionAdaptive": "close",
"Width": 1280,
"Height": 720,
"ScenarioBased": 1,
"SceneType": "normal",
"CompressType": "ultra_compress", # 또는 standard_compress, high_compress, low_compress
},
},
}참고로 Mode: "VCRF"(CRF와 동등한 고정 품질 모드)로 걸었는데도 Bitrate 필드를 비워두면 InvalidParameterValue.VideoBitrate is null로 거부당했습니다.
VCRF 모드에서도 Bitrate는 상한값으로 필수 입력이라, 넉넉하게 8,000(kbps)을 넣어 사실상 상한 역할만 하게 두고 실제 품질은 Vcrf: 23이 결정하도록 했습니다.
CompressType 4종 실측(Vcrf 23 고정)
TEHD-100을 켠 채로 CompressType 네 가지를 전부 돌렸습니다. 네 태스크 모두 SUCCESS로 끝났고, DescribeTaskDetail의 BeginProcessTime/FinishTime으로 처리 시간을, 다운로드한 결과물을 ffprobe와 libvmaf로 화질을 각각 확인했습니다.
| CompressType | 처리 시간 | 파일 크기 | 출력 비트레이트 | VMAF |
|---|---|---|---|---|
| ultra_compress | 11s | 1.67MB | 2,713.7kbps | 97.66 |
| standard_compress | 6s | 1.59MB | 2,578.4kbps | 93.74 |
| high_compress | 5s | 1.43MB | 2,327.1kbps | 91.27 |
| low_compress | 5s | 1.59MB | 2,576.1kbps | 93.94 |
전체적인 경향은 x264 preset 실험과 같은 축 위에 있습니다. 처리 시간이 가장 긴 ultra_compress(11초)가 VMAF도 가장 높았고(97.66), 처리 시간이 가장 짧은 high_compress(5초)가 VMAF도 가장 낮았습니다(91.27). "더 오래 걸릴수록 화질이 좋다"는 큰 방향은 x264 veryslow/ultrafast와 다르지 않습니다.
다만 파일 크기는 예상과 다르게 나왔습니다. 문서 주석만 보면 ultra_compress는 "코드율을 최대한 압축"하는 옵션이라 가장 작은 파일을 기대하기 쉬운데, 실제로는 네 가지 중 파일이 가장 컸습니다(1.67MB). 반대로 파일이 가장 작았던 건 "코드율 우선"인 high_compress(1.43MB)였는데, 이건 이름 그대로의 동작이라 자연스럽습니다.
이번 테스트는 Mode: VCRF로 품질(Vcrf 23)을 고정한 조건이었기 때문에, ultra_compress가 같은 Vcrf 값 아래에서 화질을 더 적극적으로 끌어올리는 쪽으로 비트를 더 쓴 것으로 보입니다. 문서의 "코드율 최대 압축"이라는 설명은 아마 비트레이트를 직접 고정하는 ABR/CBR 모드를 염두에 둔 것일 가능성이 있는데, 이 부분은 5초짜리 합성 클립 하나로 단정할 수 있는 내용이 아니라서 결론으로 못 박지는 않겠습니다.
확실한 사실만 정리하면, 같은 Vcrf 값 아래에서 ultra_compress는 가장 느리고 가장 큰 파일에 가장 높은 VMAF를, high_compress는 가장 빠르고 가장 작은 파일에 가장 낮은 VMAF를 냈습니다.
x264 preset 직접 지정 옵션 부재
정리하면 MPS의 RawParameter 경로에는 -preset ultrafast처럼 x264/x265 인코더의 탐색 강도를 직접 지정하는 필드가 없습니다. 가장 가까운 대체재는 CompressType인데, 이건 일반 트랜스코딩이 아니라 TEHD(극속고화질) 기능 아래에서만 동작하고, 값도 x264의 5단계 척도가 아니라 "속도/용량 우선"과 "화질 우선" 사이의 4단계 전략으로 추상화돼 있습니다.
이건 현재 기본 기능으로는 지원하지 않는 옵션입니다. FFmpeg 파이프라인을 그대로 옮겨와 특정 preset 값을 고정하려는 요구사항이 있다면, CompressType 4종 중 목표(속도/용량/화질)에 가장 가까운 값으로 매핑하거나, 더 세밀한 제어가 꼭 필요하면 담당 계정팀이나 기술지원팀에 문의해 커스텀 지원 여부를 논의하는 걸 권합니다.
테스트에 쓴 COS 오브젝트(preset-test/ 아래 소스 파일과 트랜스코딩 결과물)는 검증이 끝난 뒤 전부 삭제했습니다.
정리
- 같은 CRF에서는 preset을 낮춰도(빠르게 해도) 화질(VMAF)이 거의 변하지 않습니다. 이번 테스트에서는 다섯 preset의 VMAF가 96.93~97.85 사이, 0.9점 이내에 몰려 있었습니다. 대신 파일 크기가 크게 벌어집니다. ultrafast는 veryslow보다 파일이 약 1.74배 컸고(4.31MB 대 2.48MB), 인코딩 시간은 veryslow가 ultrafast보다 약 24배 더 걸렸습니다(9.17초 대 0.38초).
- 같은 목표 비트레이트에서는 반대로 파일 크기가 거의 고정되고(1.3~1.4MB대) 화질이 크게 벌어집니다. ultrafast의 VMAF는 73.18점, veryslow는 84.21점으로 11점 넘게 차이 났고, 특히 ultrafast에서 fast로 한 단계만 올려도 8점 넘게 뛰었습니다.
- "preset을 낮추면 화질이 떨어진다"는 통념은 비트레이트 고정 파이프라인에서만 정확합니다. CRF 기반 파이프라인에서 preset을 낮추면 화질 저하보다 파일 크기(=스토리지/CDN 비용) 증가가 먼저 나타납니다.
- libx265는 같은 CRF에서 x264보다 대체로 더 작은 파일을 냈고, veryslow에서는 preset 간 VMAF 차이도 x264보다 뚜렷했습니다(95.68~99.58). 다만 x265 veryslow는 x264 veryslow보다 8.7배, x264 ultrafast보다는 211배 느렸습니다(80.10초). 코덱과 preset을 동시에 올리면 처리 시간이 곱으로 늘어날 수 있다는 뜻입니다.
- Tencent Cloud MPS의
RawParameter에는 x264 preset과 동일한 필드는 없습니다. 가장 가까운 옵션인CompressType(ultra/standard/high/low_compress)은 극속고화질(TEHD) 기능이 켜져 있을 때만 동작하며,ScenarioBased: 1을 이 옵션 없이 지정하면InvalidParameterValue.ScenarioBased(Only supports TEHD)로 명확히 거부됩니다. 같은 Vcrf 23 조건에서 처리 시간과 VMAF는 ultra_compress(11초, VMAF 97.66)와 high_compress(5초, VMAF 91.27) 사이에 정렬됐지만, 파일 크기는 문서의 "극치압축" 설명과 달리 ultra_compress가 가장 컸습니다. 이 지점은 5초짜리 클립 하나로 일반화하기보다는 실제 콘텐츠로 재검증하거나 계정팀에 확인하는 걸 권합니다. - Preset을 조정하는 판단은 먼저 파이프라인이 CRF 기반인지 비트레이트 기반인지부터 확인하고 시작해야 합니다. 같은 "preset 낮추기"라도 어느 쪽이냐에 따라 비용이 늘어나는 방향과 화질이 떨어지는 방향 중 어느 쪽으로 대가를 치르는지가 완전히 갈립니다.