영상 인코딩 도구 고르기: ffmpeg preset과 faststart 실행 결과, GStreamer와의 역할 차이
이 글의 핵심
8초 720p 합성 영상을 libx264 crf 23으로 인코딩했을 때 preset ultrafast는 0.24초에 7.5MB, medium은 0.72초에 3.1MB, slow는 1.27초에 3.0MB였습니다. faststart를 켜면 MP4의 moov 박스가 mdat 앞으로 이동했습니다. 이 결과와 ffmpeg·GStreamer의 역할 차이를 정리했습니다.
영상 파일을 다룰 때 처음 만나는 도구는 ffmpeg와 GStreamer다. 둘 다 인코딩과 변환을 하지만 쓰임새가 다르다. 이 글은 ffmpeg에서 가장 많이 만지는 preset과 faststart를 실제로 실행해 보고, 두 도구의 역할 차이를 정리한다. 환경은 ffmpeg 9.0.2(Homebrew), macOS arm64이고, 입력은 testsrc2 합성 영상(1280x720, 30fps, 8초)이다. 합성 영상은 움직임과 질감이 실제 촬영 영상과 다르므로 숫자는 경향을 보는 용도다.
실행 결과
ffmpeg -f lavfi -i testsrc2=size=1280x720:rate=30 -t 8 \
-c:v libx264 -preset <이름> -crf 23 -pix_fmt yuv420p out.mp4
| preset | 인코딩 시간 | 파일 크기 |
|---|---|---|
| ultrafast | 0.24초 | 7,499,196바이트 |
| medium | 0.72초 | 3,137,487바이트 |
| slow | 1.27초 | 3,027,167바이트 |
- ultrafast는 빠르지만 같은
crf값에서 파일이 medium의 약 2.4배였다. - medium에서 slow로 가면 시간이 약 1.8배 걸리고 크기는 약 3.5% 줄었다. 이 이득이 시간을 들일 만한지는 용도에 따른다.
- 각 preset을 한 번씩만 실행한 값이라 오차가 있다.
화질(VMAF)까지 포함한 비교, crf 값별 변화, 실제 영상 기준의 폭은 ffmpeg libx264 실험: preset과 crf에서 다룬다.
faststart가 바꾸는 것
MP4 파일은 moov(재생 정보)와 mdat(실제 데이터) 박스로 이뤄지는데, 기본 인코딩 결과는 moov가 파일 끝에 있다. 웹에서 재생하려면 moov를 먼저 받아야 하므로 앞으로 옮기는 옵션이 -movflags +faststart다. 같은 파일에 ffmpeg -i in.mp4 -c copy -movflags +faststart out.mp4를 적용하자 박스 순서가 다음처럼 바뀌었다.
| 파일 | 앞쪽 박스 순서 |
|---|---|
| 기본 인코딩 | ftyp, free, mdat, moov |
| faststart 적용 | ftyp, moov, free, mdat |
-c copy는 다시 인코딩하지 않고 컨테이너만 다시 쓰므로 빠르다. 이 박스 순서 확인은 ffmpeg를 쓰지 않고 브라우저에서 영상을 처리하는 도구를 만들 때도 같은 이유로 테스트했다.
crf를 바꾸면 크기와 화질이 얼마나 변하나
같은 합성 영상(testsrc2, 720p, 30fps, 8초)을 libx264 -preset medium으로 crf만 바꿔 인코딩하고, 원본과 비교한 VMAF 점수(ffmpeg의 libvmaf 필터)도 같이 구했다.
| crf | 인코딩 시간 | 파일 크기 | VMAF |
|---|---|---|---|
| 18 | 0.76초 | 4,834,394바이트 | 98.67 |
| 23 | 0.66초 | 3,137,487바이트 | 97.16 |
| 28 | 0.61초 | 1,531,244바이트 | 93.72 |
| 35 | 0.52초 | 712,101바이트 | 84.69 |
crf를 5 올릴 때마다 크기가 대략 절반에 가깝게 줄었다(23→28에서 약 51%, 28→35에서는 약 53%). 이 합성 영상에서의 경향이다.- VMAF는 crf 18에서 23으로 가면 1.5점, 28에서 35로 가면 9점이 떨어졌다. 화질이 급격히 나빠지는 구간은 crf가 큰 쪽에 있었다.
- 합성 영상은 반복되는 무늬와 단순한 움직임이라 실제 촬영 영상보다 압축이 잘 된다. 같은
crf라도 실제 영상에서는 크기가 훨씬 크고 VMAF 변화 폭도 다를 수 있으므로, 이 표는 “crf가 이런 방향으로 작용한다”는 확인으로만 쓴다.
하드웨어 인코더와 소프트웨어 인코더
이 맥의 ffmpeg는 h264_videotoolbox(애플의 하드웨어 H.264 인코더)를 지원했다. 같은 입력을 -b:v 3M으로 인코딩했다.
| 인코더 | 설정 | 시간 | 크기 |
|---|---|---|---|
libx264 | crf 23, medium | 0.66초 | 3,137,487바이트 |
h264_videotoolbox | 비트레이트 3Mbps | 0.79초 | 3,050,422바이트 |
이 8초짜리 합성 영상에서는 하드웨어 인코더가 더 빠르지 않았다. 입력이 짧고 단순해서 시작 비용이 지배적일 수 있다. 긴 영상과 실제 촬영 영상에서의 속도와 화질은 확인하지 않았다. 하드웨어 인코더는 보통 같은 비트레이트에서 소프트웨어 인코더(libx264의 느린 preset)보다 압축 효율이 낮다고 알려져 있으나 이 글에서 비교하지 않았다.
컨테이너만 바꿀 때와 다시 인코딩할 때
| 작업 | 시간 | 결과 |
|---|---|---|
-c copy로 컨테이너만 다시 쓰기 | 0.05초 | 같은 영상·음성 데이터를 그대로 복사 |
720p → 360p로 줄이면서 재인코딩(-vf scale=640:360, crf 23) | 0.29초 | 397,378바이트 |
형식만 바꾸거나 faststart 위치만 옮길 때는 -c copy가 훨씬 빠르고 화질 손실이 없다. 해상도를 바꾸려면 재인코딩이 필요하다. 이 글의 8초 영상에서 해상도를 절반으로 줄이자 크기가 약 8분의 1이 됐다(3.1MB → 0.4MB).
결과를 확인하는 방법: ffprobe
인코딩 결과가 의도대로인지는 ffprobe로 읽는다.
ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,width,height,r_frame_rate,bit_rate \
-show_entries format=duration,size -of default=nw=1 out.mp4
위 파일에서는 codec_name=h264, width=1280, height=720, r_frame_rate=30/1, bit_rate=3133996, duration=8.000000, size=3137487이 나왔다. 비트레이트와 크기, 길이가 서로 맞는지(3,137,487바이트×8÷8초 ≈ 3.14Mbps) 계산으로 확인하면 파일이 잘못 만들어졌는지 알 수 있다.
ffmpeg와 GStreamer: 역할 차이
| 구분 | ffmpeg | GStreamer |
|---|---|---|
| 중심 | 파일 변환·인코딩 명령 한 줄 | 소스, 필터, 싱크를 이어 붙이는 파이프라인 |
| 잘 맞는 일 | 포맷 변환, 인코딩 옵션 실험, 일괄 처리 | 카메라·네트워크 스트림을 실시간으로 받아 처리 |
| 배우는 방식 | 옵션과 필터 | 요소(element)와 패드 연결 |
이 표는 일반적으로 알려진 역할 구분이고, 이 글에서 GStreamer는 실행하지 않았다(gst-launch를 설치하지 않았다). GStreamer의 요소 조립과 C·파이썬 연동은 GStreamer 실전 가이드에 정리했다. 코덱과 컨테이너의 배경 지식은 H.264 가이드와 MP4 컨테이너 가이드를 참고한다.
고르는 기준
- 파일을 바꾸고 옵션을 비교한다: ffmpeg.
- 카메라나 네트워크 스트림을 실시간으로 이어 붙인다: GStreamer 쪽을 먼저 검토한다.
- 웹 재생용 MP4를 만든다:
-movflags +faststart를 확인한다.
확인하지 못한 것
- 실제 촬영 영상과 다른 해상도에서의 preset 효과.
- 하드웨어 인코더(VideoToolbox, NVENC)와의 비교.
- GStreamer 파이프라인의 실행 결과.