시리즈: 실시간 통화, 어떻게 녹화하는가5/6
- 1.실시간 통화, 왜 녹화해야 하는가
- 2.내 서버에서 직접 녹화한다면
- 3.녹화 모드 해부
- 4.M3U8과 TS 파일의 모든 것
- 5.FFmpeg, 미디어의 스위스 아미 나이프 ← 현재 글
- 6.FFmpeg 실전 파이프라인
FFmpeg, 미디어의 스위스 아미 나이프 — 컨테이너, 코덱, 스트림
Container와 Codec의 차이, Stream 개념, -c copy vs Transcoding, FFmpeg 내부 5단계 파이프라인, SRS 리패키징 서버 실전 예시(RTMP→디먹서→HLS/WebRTC 분기 먹서)까지 해부합니다.
FFmpeg 없이 미디어를 다루겠다는 건, Git 없이 코드를 관리하겠다는 것과 같다. 물론 가능하긴 하다. 하지만 그 고통을 굳이 감수할 이유가 없다.
녹화 파일이 생성된 순간부터 사용자에게 실제로 전달되기까지, 그 사이에는 수많은 후처리 과정이 존재한다. TS 파일을 MP4로 변환하고, 개별 트랙을 합치고, 구간을 자르고, 썸네일을 뽑아내고. 이 모든 작업의 중심에 FFmpeg이 있다.
이 글에서는 FFmpeg을 제대로 이해하기 위한 세 가지 핵심 개념 — Container, Codec, Stream — 과 함께, 실무에서 반드시 알아야 할 명령어 패턴을 정리한다.
핵심 개념 1: Container vs Codec
가장 많이 혼동하는 개념부터 시작한다.
택배 상자(MP4)와 포장 방식(H.264)은 별개다. 같은 물건을 다른 상자에 옮겨 담을 수 있고, 같은 상자에 다른 방식으로 포장된 물건을 넣을 수도 있다.
컨테이너별 특성:
| 컨테이너 | 특징 | 주요 용도 |
|---|---|---|
| MP4 | 가장 범용, 브라우저 호환 최고 | VOD, 다운로드 |
| TS | 자기완결적 패킷, 장애 내성 | HLS 스트리밍, 실시간 녹화 |
| MKV | 거의 모든 코덱 지원, 다중 트랙 | 아카이브, 자막 포함 영상 |
| WebM | VP8/VP9 + Opus 전용 | 웹 최적화 |
| FLV | 레거시 Flash 포맷 | RTMP 스트리밍 (레거시) |
TS가 "자기완결적 패킷"이라는 표현에 주목할 필요가 있다. HLS 세그먼트로 TS를 쓰는 이유가 바로 이것이다. 패킷 단위로 독립적으로 파싱 가능하기 때문에, 네트워크 오류나 파일 손상이 발생해도 이후 패킷부터 정상 재생이 가능하다.
핵심 개념 2: Stream (스트림)
컨테이너 내부에는 하나 이상의 스트림(트랙)이 존재한다.
각 스트림은 독립적으로 처리할 수 있다. 비디오 스트림만 추출하거나, 오디오 스트림만 교체하거나, 자막 스트림을 추가하는 것이 모두 가능하다.
ffprobe로 파일의 스트림 구성을 확인할 수 있다:
출력 결과에서 codec_type, codec_name, width, height, sample_rate 등을 확인할 수 있다. 후처리 파이프라인을 작성하기 전에 항상 먼저 실행해보는 습관을 들이면 좋다. "왜 오디오가 없지?" 같은 문제를 미리 발견할 수 있다.
핵심 개념 3: Muxing / Demuxing
스트림을 컨테이너에 넣고 빼는 작업에도 이름이 있다.
- Demuxing: 컨테이너에서 스트림을 꺼내는 작업
- Muxing: 스트림들을 컨테이너에 담는 작업
- Remuxing: 스트림 변경 없이 컨테이너만 바꾸는 것 (TS → MP4)
Remuxing이 중요한 이유는 비디오/오디오 데이터를 디코딩/인코딩하지 않기 때문이다. 원본 데이터를 그대로 다른 상자에 옮겨 담는 것이라 속도가 극도로 빠르고 품질 손실이 전혀 없다.
실전 예시 — 리패키징 서버 (SRS 등)
디먹서/먹서 개념이 실제로 어떻게 조합되는지 가장 흔한 사례가 리패키징 서버입니다. OBS 같은 송출 도구가 RTMP로 푸시하면, 서버 한 곳에서 받아서 여러 프로토콜로 동시에 재배포하는 구조.
대표 오픈소스: SRS (Simple Realtime Server).
플로우:
- RTMP 수신 — OBS가 푸시한 RTMP 스트림을 TCP 1935에서 수신
- 디먹서 — RTMP 컨테이너에서 H.264(비디오) + AAC(오디오) 코덱 데이터만 추출. 코덱 페이로드는 그대로 유지 (재인코딩 아님)
- 분기 먹서
- HLS 먹서: 추출한 H.264/AAC를 MPEG-TS 세그먼트로 포장 →
.m3u8플레이리스트 갱신 - WebRTC 먹서: 같은 H.264/AAC를 RTP 패킷으로 포장 → 저지연 피어 전송
- HLS 먹서: 추출한 H.264/AAC를 MPEG-TS 세그먼트로 포장 →
- 재배포 — 한 입력이 두 가지 프로토콜로 동시 제공
왜 이 구조가 효율적인가
| 측면 | 설명 |
|---|---|
| 재인코딩 없음 | 디먹서→먹서만 수행. CPU 부하 최소. 화질 손실 없음 |
| 프로토콜 다양성 | 한 입력으로 HLS(대중 호환) + WebRTC(저지연) 동시 |
| 확장성 | 디먹서 출력을 캐시해서 N개 먹서에 공급 가능 |
주의: 코덱 호환성
디먹서는 컨테이너만 벗기므로 코덱 자체가 목적지와 호환돼야 합니다. 예를 들어 송출단이 H.264 Main Profile + B-frame을 쓰면, HLS 쪽은 OK지만 WebRTC 쪽에서 디코딩 실패할 수 있습니다 (B-frame + RTP 단일 타임스탬프 모델의 구조적 충돌).
이 때문에 실전에서 RTMP→WebRTC 변환 경로는 송출단 설정을 Baseline / Main+bframes=0으로 유도하거나, 엣지에서 **트랜스코딩 (재인코딩)**으로 전환해야 합니다. 사례 분석: RTMP 라이브 송출의 B-frame이 만든 PTS rollback.
FFmpeg 명령어 구조
FFmpeg 명령어는 처음 보면 복잡해 보이지만, 구조를 이해하면 패턴이 보인다.
주요 옵션 정리:
| 옵션 | 의미 | 예시 |
|---|---|---|
-i | 입력 파일 지정 | -i input.mp4 |
-c:v | 비디오 코덱 지정 | -c:v libx264 |
-c:a | 오디오 코덱 지정 | -c:a aac |
-c copy | 모든 스트림 복사 (remux) | -c copy |
-ss | 시작 시점 | -ss 00:01:00 |
-t | 출력 길이 | -t 30 |
-to | 종료 시점 | -to 00:02:00 |
-vf | 비디오 필터 | -vf scale=1280:720 |
-an | 오디오 스트림 제거 | -an |
-vn | 비디오 스트림 제거 | -vn |
-y | 출력 파일 덮어쓰기 | -y |
-c copy vs Transcoding
FFmpeg을 쓸 때 가장 중요한 판단 중 하나다.
| -c copy (Remuxing) | Transcoding | |
|---|---|---|
| 속도 | 매우 빠름 (I/O bound) | 느림 (CPU bound) |
| 품질 | 무손실 (원본 그대로) | CRF/비트레이트로 제어 |
| CPU | 거의 안 씀 | 집약적 |
| 용도 | 컨테이너 변환, 스트림 추출 | 해상도 변경, 레이아웃 합성, 코덱 변환 |
| 필터 사용 | 불가 | 가능 |
결론: 코덱을 바꾸거나 필터를 적용할 필요가 없다면 항상 -c copy를 써라. 녹화 후처리의 대부분은 -c copy로 충분하다.
-map: 스트림 선택
입력이 여러 개일 때, 또는 특정 스트림만 선택해야 할 때 -map을 사용한다.
Individual 모드로 녹화한 파일을 합칠 때 이 패턴을 자주 쓰게 된다. 비디오 트랙과 오디오 트랙이 별도 파일로 저장되어 있을 경우, -map으로 각각 지정해서 하나의 파일로 mux한다.
FFmpeg 내부 5단계 파이프라인
FFmpeg이 내부적으로 어떻게 동작하는지 이해하면, 에러 메시지를 해석하고 옵션을 조합하는 데 도움이 된다.
트랜스코딩이 느린 이유가 여기 있다. Decode → Filter → Encode 세 단계를 거치는 동안 Raw 데이터(압축 해제된 원시 영상/음성)를 처리해야 하기 때문이다. 4K 영상 1시간을 트랜스코딩하면 수십 분이 걸리는 이유다.
기본 명령어 10가지
명령어 8번에서 filelist.txt 형식은 다음과 같다:
HLS 녹화 후 여러 TS 세그먼트를 하나의 MP4로 합칠 때 이 패턴을 쓴다.
"언제 -c copy, 언제 트랜스코딩?" 판단 플로우차트
실무 경험상 녹화 후처리의 약 80%는 -c copy로 해결된다. TS → MP4 변환, M3U8 → MP4 변환, 스트림 추출, 파일 연결. 트랜스코딩이 필요한 경우는 해상도 변경이나 Individual 모드 트랙을 특정 레이아웃으로 합성할 때 정도다.
핵심 요약
- Container는 포맷(상자), Codec은 압축 방식(포장법): 같은 코덱이 여러 컨테이너에 담길 수 있다
- Stream은 컨테이너 내부의 개별 트랙: 하나의 파일에 비디오/오디오/자막 스트림이 공존한다
- Remuxing (-c copy)은 무손실, 초고속: 컨테이너만 바꿀 때는 반드시
-c copy를 사용하라 - Transcoding은 필터가 필요할 때만: 디코딩 → 처리 → 인코딩을 거치므로 CPU 집약적이다
- -map으로 스트림을 정밀 선택: 멀티 입력이나 특정 트랙 추출 시 필수 옵션이다
- ffprobe를 습관처럼 먼저 실행하라: 파일 구조를 모르고 명령어를 작성하면 예상치 못한 결과가 나온다
시리즈 네비게이션
실시간 통화, 어떻게 녹화하는가 시리즈