들어가며: 수십억 건의 FFmpeg 실행이 만들어낸 독특한 도전

메타(페이스북)는 하루에 수백억 번의 FFmpeg와 ffprobe 실행을 처리합니다. 사용자가 업로드하는 동영상 하나하나를 다양한 해상도, 코덱, 비트레이트로 인코딩해야 하기 때문입니다. 이런 규모에서 FFmpeg는 단순한 툴이 아니라 핵심 인프라 자체입니다.

문제는 FFmpeg가 원래 이런 규모를 염두에 두고 설계된 게 아니라는 점입니다. 개별 파일의 트랜스코딩은 훌륭하지만, 수억 개의 업로드를 효율적으로 처리하려면 추가 기능이 필요했습니다. 그래서 메타는 수년간 자체 FFmpeg 포크를 유지하며 멀티 스레드 멀티 레인 인코딩, 실시간 화질 측정 등의 기능을 내부적으로 개발해 왔습니다.

하지만 시간이 지나면서 내부 포크는 upstream과 점점 멀어졌고, 새로운 코덱 지원이나 안정성 개선 같은 upstream의 혜택을 받기 어려워졌습니다. 이는 기술 부채의 전형적인 사례죠. 결국 메타는 포크를 폐기하고 upstream에 모든 기능을 기여하기로 결정합니다.

이 글에서는 메타가 어떤 과정을 통해 이 전환을 성공시켰는지, 그리고 그 과정에서 얻은 기술적 인사이트를 공유합니다.

참고 자료: 이 내용은 메타 엔지니어링 블로그에 게시된 FFmpeg at Meta: Media Processing at Scale을 기반으로 재구성했습니다.

Meta engineers optimizing FFmpeg for multi-lane transcoding on server infrastructure System Abstract Visual

핵심 개선 1: 멀티 레인 트랜스코딩의 효율화

문제 상황

사용자가 동영상을 업로드하면 메타는 DASH(Dynamic Adaptive Streaming over HTTP) 재생을 위해 여러 개의 인코딩을 생성합니다. 각 인코딩은 해상도, 코덱, 프레임레이트, 화질 수준이 다르지만, 모두 같은 원본에서 파생됩니다.

가장 단순한 방식은 각 인코딩을 순차적으로 실행하는 것입니다. 병렬로 실행해도 되지만, 각 프로세스가 동일한 디코딩 작업을 중복 수행하므로 비효율적입니다.

해결책: 단일 FFmpeg 명령어로 다중 출력 생성

메타의 내부 포크는 비디오 디코딩을 한 번만 수행하고, 디코딩된 프레임을 각 출력의 인코더 인스턴스로 보내는 방식을 사용했습니다. 이를 통해 프로세스 시작 오버헤드와 디코딩 중복을 크게 줄였습니다.

# 단일 명령어로 여러 해상도의 인코딩 생성 (단순화된 예시)
ffmpeg -i input.mp4 \
  -filter_complex "[0:v]split=3[v1][v2][v3]" \
  -map "[v1]" -c:v libx264 -b:v 500k -s 640x360 output_360p.mp4 \
  -map "[v2]" -c:v libx264 -b:v 1500k -s 1280x720 output_720p.mp4 \
  -map "[v3]" -c:v libx264 -b:v 4000k -s 1920x1080 output_1080p.mp4

추가 최적화: 병렬 인코딩

이전 FFmpeg 버전에서는 여러 인코더를 사용할 때 각 인코더가 프레임 단위로 직렬 실행되었습니다. 메타의 포크는 모든 인코더 인스턴스를 병렬로 실행하여 전체 처리량을 높였습니다.

upstream 기여와 현재

FFlabs와 VideoLAN의 FFmpeg 개발자들이 이 설계를 참고하여 FFmpeg 6.0부터 더 효율적인 스레딩을 구현했고, 8.0에서 완성했습니다. 이는 수십 년 만에 FFmpeg에서 가장 복잡한 리팩토링 중 하나였으며, 이제 모든 FFmpeg 사용자가 혜택을 볼 수 있게 되었습니다.

한국 개발 생태계에서의 적용: 국내 OTT 서비스나 라이브 스트리밍 플랫폼에서도 유사한 멀티 레인 트랜스코딩이 필요합니다. 특히 VOD 업로드가 많은 서비스라면 단일 명령어로 다중 출력을 생성하는 방식을 도입해 서버 리소스를 절약할 수 있습니다. 다만, filter_complex 구문이 복잡할 수 있으니 초기에는 간단한 split부터 시작하는 것을 추천합니다.

Real-time quality metrics computation VMAF SSIM during video encoding pipeline Programming Illustration

핵심 개선 2: 실시간 화질 측정 (In-Loop Quality Metrics)

문제 상황

VOD(주문형 비디오)의 경우 인코딩이 완료된 후 별도의 명령어로 PSNR, SSIM, VMAF 같은 화질 지표를 계산할 수 있습니다. 하지만 라이브 스트리밍에서는 실시간으로 화질을 모니터링해야 합니다.

해결책: In-Loop 디코딩

메타의 내부 포크는 각 출력 레인의 인코더 뒤에 비디오 디코더를 추가로 삽입했습니다. 압축이 적용된 후의 프레임을 다시 비트맵으로 복원하여, 압축 전 프레임과 비교하는 방식입니다.

# in-loop quality metrics 계산 (개념적 예시)
ffmpeg -i input.mp4 \
  -filter_complex "[0:v]split=2[ref][enc]; \
                   [enc]libx264[compressed]; \
                   [compressed]decode[dec]; \
                   [ref][dec]libvmaf=model_path=vmaf_v0.6.1.json" \
  -f null -

upstream 기여

FFlabs와 VideoLAN 개발자들이 FFmpeg 7.0부터 in-loop 디코딩을 가능하게 했고, 메타는 이 기능을 통해 내부 포크를 완전히 대체할 수 있었습니다.

주의사항

in-loop 디코딩은 추가적인 CPU/GPU 리소스를 소모합니다. 모든 인코딩에 적용하기보다는 샘플링 기반으로 운영하거나, 중요한 라이브 스트림에만 적용하는 전략이 필요합니다. 메타의 경우 수십억 건의 업로드 중 일부에만 적용해도 전체 파이프라인의 품질을 모니터링할 수 있었습니다.

다음 단계 학습 방향: FFmpeg의 필터 그래프(filtergraph)에 대한 깊은 이해가 필요하다면 FFmpeg 필터 문서를 추천합니다. 또한 VMAF 모델을 직접 학습시켜 서비스에 특화된 화질 지표를 만드는 방법도 고려해볼 만합니다.

FFmpeg upstream collaboration between Meta FFlabs and VideoLAN for threaded encoding IT Technology Image

결론: 포크 지옥에서 탈출한 교훈

메타의 FFmpeg 포크 폐기 사례는 규모가 큰 조직이 오픈소스 프로젝트와 관계를 맺는 방식에 대한 좋은 교훈을 줍니다.

  1. 포크는 마지막 수단이다: 초기에는 필요해 보였지만, 시간이 지날수록 기술 부채가 쌓이고 유지보수 비용이 증가합니다.
  2. upstream 퍼스트 전략: 필요한 기능이 있다면 가능하면 upstream에 기여하는 것이 장기적으로 유리합니다. 메타는 FFlabs, VideoLAN과 협력해 FFmpeg 6.0, 7.0, 8.0에 걸쳐 필요한 기능을 추가했습니다.
  3. 모든 기능을 upstream할 필요는 없다: MSVP(Meta Scalable Video Processor) 같은 내부 전용 하드웨어 지원 패치는 오히려 upstream에 혼란을 줄 수 있으므로 내부에서 유지하는 것이 합리적입니다.

메타는 이제 모든 VOD와 라이브 스트리밍 파이프라인에서 내부 포크 대신 upstream FFmpeg를 사용합니다. 25년 이상 활발히 개발된 FFmpeg의 안정성과 새로운 기능을 활용하면서, 동시에 자체적인 최적화도 유지할 수 있게 된 것입니다.

함께 보면 좋은 글:

본 콘텐츠는 신뢰할 수 있는 출처를 바탕으로 AI 도구를 활용하여 초안이 작성되었으며, 편집자의 검토를 거쳐 발행되었습니다. 전문가의 조언을 대체하지 않습니다.