FFmpeg 실전 가이드 | 동영상/오디오 처리
🎯 이 글을 읽으면 (읽는 시간: 30분)
TL;DR: FFmpeg로 동영상/오디오를 자유자재로 다루는 방법을 배웁니다. 설치부터 변환, 인코딩, 스트리밍까지 실무에서 바로 쓸 수 있는 모든 것을 마스터합니다.
이 글을 읽으면:
- ✅ FFmpeg 설치부터 기본 사용법까지 완벽 이해
- ✅ 동영상 변환, 인코딩, 최적화 기법 실전 적용
- ✅ HLS 스트리밍과 라이브 방송 구축 능력 습득
- ✅ Python/Node.js에서 FFmpeg 프로그래밍 방식 활용
실무 활용:
- 🔥 동영상 플랫폼 (YouTube, Netflix 스타일)
- 🔥 라이브 스트리밍 서비스
- 🔥 썸네일 자동 생성 시스템
- 🔥 동영상 인코딩 파이프라인
난이도: 중급 | 실습 예제: 30개 | 프로덕션 레벨
이 글의 핵심
FFmpeg는 동영상과 오디오를 다루는 가장 강력한 오픈소스 도구입니다. 이 가이드는 설치부터 실전 활용까지, 실무에서 바로 써먹을 수 있는 패턴과 노하우를 담았습니다. 단순한 명령어 나열이 아닌, 왜 이렇게 쓰는지, 어떤 상황에서 쓰는지를 중심으로 정리했습니다.
목차
- FFmpeg이란?
- 설치 및 기본 사용법
- 동영상 변환 및 인코딩
- 오디오 처리
- 썸네일 및 이미지 추출
- 스트리밍 및 HLS
- 필터 및 효과
- FFmpeg 라이브러리 (libav)
- 실전 프로젝트
- 성능 최적화
사전 지식 (초보자를 위한 기초)
1. 동영상 파일 구조
동영상 파일을 이해하려면 컨테이너와 코덱의 차이를 알아야 합니다. 많은 사람들이 이 둘을 혼동하는데, 실제로는 완전히 다른 개념입니다.
동영상 파일 구조:
┌─────────────────────────────────┐
│ 컨테이너 (Container) │
│ 예: MP4, MKV, AVI, MOV │
│ │
│ ┌──────────────────────────┐ │
│ │ 비디오 스트림 │ │
│ │ 코덱: H.264, H.265 │ │
│ └──────────────────────────┘ │
│ │
│ ┌──────────────────────────┐ │
│ │ 오디오 스트림 │ │
│ │ 코덱: AAC, MP3 │ │
│ └──────────────────────────┘ │
│ │
│ ┌──────────────────────────┐ │
│ │ 자막 스트림 (선택) │ │
│ │ SRT, ASS │ │
│ └──────────────────────────┘ │
└─────────────────────────────────┘
컨테이너 (Container):
- 비디오, 오디오, 자막을 담는 “상자” 역할
- 예: MP4, MKV, AVI, MOV, WebM
- 같은 코덱이라도 컨테이너에 따라 호환성이 달라집니다
- MP4는 가장 범용적이고, MKV는 기능이 많지만 일부 기기에서 재생 안 될 수 있습니다
코덱 (Codec):
- 데이터를 압축/해제하는 “알고리즘”
- 비디오: H.264 (가장 보편적), H.265 (고효율), VP9 (웹용), AV1 (차세대)
- 오디오: AAC (범용), MP3 (레거시), Opus (고효율), FLAC (무손실)
- 같은 화질이라도 코덱에 따라 파일 크기가 2배 이상 차이날 수 있습니다
비유로 이해하기:
- 컨테이너 = 택배 상자
- 코덱 = 물건을 압축하는 방법
- 같은 물건(영상)이라도 어떻게 압축하고(코덱) 어떤 상자에 담느냐(컨테이너)에 따라 결과가 달라집니다
2. 비트레이트와 해상도
비트레이트 (Bitrate): 초당 전송되는 데이터 양입니다. 단위는 kbps(킬로비트), Mbps(메가비트)를 사용합니다.
- 높은 비트레이트: 화질은 좋지만 파일 크기가 크고 스트리밍 시 버퍼링 발생 가능
- 낮은 비트레이트: 파일은 작지만 화질 저하, 특히 빠른 움직임에서 블록 노이즈 발생
- 적정 비트레이트: 해상도와 프레임레이트, 콘텐츠 특성에 따라 결정됩니다
비디오 비트레이트 가이드:
4K (3840×2160): 20-50 Mbps
1080p (1920×1080): 5-10 Mbps
720p (1280×720): 2.5-5 Mbps
480p (854×480): 1-2 Mbps
360p (640×360): 0.5-1 Mbps
오디오 비트레이트:
320 kbps: 고음질 (음악)
192 kbps: 표준 (음악)
128 kbps: 일반 (팟캐스트)
64 kbps: 저음질 (통화)
해상도 (Resolution): 가로×세로 픽셀 수입니다.
- 4K (3840×2160): 영화관 수준, 파일 크기 매우 큼
- 1080p (1920×1080): Full HD, 가장 보편적인 고화질
- 720p (1280×720): HD, 모바일이나 웹 스트리밍에 적합
- 480p (854×480): SD, 저사양 기기나 느린 네트워크용
실전 팁: 같은 비트레이트라도 해상도가 높으면 화질이 떨어집니다. 4K를 5Mbps로 인코딩하는 것보다 1080p를 5Mbps로 인코딩하는 게 훨씬 깨끗합니다.
3. 프레임레이트
프레임레이트 (Frame Rate, FPS): 초당 몇 장의 화면을 보여주는지를 나타냅니다. 단위는 fps(frames per second)입니다.
프레임레이트가 높을수록 움직임이 부드럽지만, 파일 크기도 비례해서 커집니다. 콘텐츠 특성에 맞는 선택이 중요합니다.
일반적인 프레임레이트:
24 fps: 영화 (시네마틱 느낌, 영화관 표준)
30 fps: TV, 유튜브 (일반 영상, 가장 보편적)
60 fps: 게임, 스포츠 (부드러운 움직임, 액션 장면)
120 fps: 슬로우 모션 촬영용 (편집 시 24fps나 30fps로 느리게 재생)
실전 선택 가이드:
- 브이로그, 강의: 30fps (파일 크기와 품질 균형)
- 게임 플레이, 스포츠: 60fps (빠른 움직임 표현)
- 영화 느낌: 24fps (시네마틱 무드)
- 슬로우 모션 필요: 120fps 이상 촬영 후 편집
4. 코덱 선택 가이드
코덱 선택은 호환성, 파일 크기, 인코딩 속도, 화질의 균형을 맞추는 작업입니다. 상황별로 최적의 선택이 다릅니다.
비디오 코덱 비교:
H.264 (AVC) - 가장 안전한 선택
✅ 장점: 거의 모든 기기 지원, 빠른 인코딩, 하드웨어 가속 지원
❌ 단점: H.265 대비 파일 크기 큼
📌 추천: 일반 용도, 최대 호환성 필요, 빠른 처리 필요
H.265 (HEVC) - 용량 최적화
✅ 장점: H.264 대비 40-50% 작은 파일, 4K에 적합
❌ 단점: 느린 인코딩, 구형 기기 미지원, 라이선스 비용
📌 추천: 4K 영상, 저장 공간 부족, 최신 기기 타겟
VP9 - 웹 스트리밍용
✅ 장점: 로열티 프리, YouTube 지원, 웹 브라우저 호환
❌ 단점: 매우 느린 인코딩, 하드웨어 지원 제한적
📌 추천: YouTube 업로드, 웹 전용 콘텐츠
AV1 - 미래 대비
✅ 장점: 최고의 압축 효율, 로열티 프리
❌ 단점: 극도로 느린 인코딩, 제한적 재생 지원
📌 추천: 장기 보관용, 실험적 프로젝트
오디오 코덱 비교:
AAC - 범용 표준
✅ 장점: 우수한 음질/용량 비율, 광범위한 지원
❌ 단점: MP3보다 약간 느린 인코딩
📌 추천: 대부분의 경우 (기본 선택)
MP3 - 레거시 호환
✅ 장점: 100% 호환성, 빠른 인코딩
❌ 단점: AAC 대비 큰 파일 또는 낮은 음질
📌 추천: 구형 기기 지원 필수, 팟캐스트
Opus - 고효율
✅ 장점: 최고의 음질/용량 비율, 로열티 프리
❌ 단점: 제한적 기기 지원
📌 추천: 웹 스트리밍, 음성 통화, Discord
FLAC - 무손실
✅ 장점: 완벽한 음질 보존, 압축 지원
❌ 단점: 큰 파일 크기
📌 추천: 음악 아카이빙, 마스터링
실전 의사결정 트리:
1. 최대 호환성 필요? → H.264 + AAC
2. 파일 크기 최소화? → H.265 + AAC (또는 Opus)
3. YouTube 업로드? → VP9 + Opus (또는 H.264 + AAC)
4. 4K 영상? → H.265 + AAC
5. 웹 전용? → VP9 + Opus
6. 음악/오디오 보관? → FLAC
1. FFmpeg이란?
FFmpeg는 동영상과 오디오를 다루는 가장 강력한 오픈소스 멀티미디어 프레임워크입니다. 1999년부터 개발되어 현재까지 업계 표준으로 자리잡았으며, YouTube, Netflix, VLC 등 수많은 서비스와 프로그램이 FFmpeg를 기반으로 동작합니다.
주요 기능
FFmpeg로 할 수 있는 작업은 거의 무한합니다:
기본 기능:
- 동영상/오디오 포맷 변환 (MP4 ↔ AVI ↔ MKV 등)
- 인코딩/디코딩 (압축 및 압축 해제)
- 해상도 변경 및 품질 조정
- 썸네일 및 스크린샷 추출
고급 기능:
- 실시간 스트리밍 (RTMP, HLS, DASH)
- 필터 적용 (자르기, 회전, 워터마크, 색보정)
- 오디오 처리 (노이즈 제거, 볼륨 조절, 믹싱)
- 화면 녹화 및 웹캠 캡처
- 자막 추가 및 변환
- 여러 파일 병합 및 분할
실무 활용 예시:
- 유튜브 업로드용 영상 최적화
- 웹사이트용 다중 해상도 영상 생성
- 라이브 스트리밍 서버 구축
- 동영상 자동 처리 파이프라인 구축
- 모바일 앱용 영상 변환
FFmpeg 구성 요소
FFmpeg는 여러 도구와 라이브러리로 구성되어 있습니다. 각각의 역할을 이해하면 더 효과적으로 활용할 수 있습니다.
명령줄 도구:
ffmpeg
- 가장 많이 쓰는 핵심 도구
- 파일 변환, 인코딩, 필터 적용 등 모든 작업 수행
- 예: ffmpeg -i input.mp4 output.avi
ffprobe
- 미디어 파일 정보 분석 도구
- 코덱, 해상도, 비트레이트, 길이 등 메타데이터 추출
- 스크립트에서 파일 정보 확인 시 필수
- 예: ffprobe -show_format input.mp4
ffplay
- 간단한 미디어 재생기
- 인코딩 결과 빠르게 확인할 때 유용
- 테스트 및 디버깅용
- 예: ffplay output.mp4
라이브러리 (프로그래밍 시 사용):
libavcodec
- 코덱 인코딩/디코딩 라이브러리
- H.264, H.265, AAC, MP3 등 수백 개 코덱 지원
- 직접 코딩할 때 가장 핵심적인 라이브러리
libavformat
- 컨테이너 포맷 처리 라이브러리
- MP4, MKV, AVI 등 파일 읽기/쓰기
- 스트리밍 프로토콜 지원 (RTMP, HLS 등)
libavfilter
- 필터 그래프 처리 라이브러리
- 영상/오디오 효과 적용
- 크기 조정, 자르기, 워터마크, 색보정 등
libavutil
- 공통 유틸리티 라이브러리
- 수학 함수, 메모리 관리, 로깅 등
- 다른 라이브러리들의 기반
libswscale
- 이미지 스케일링 및 색공간 변환
- 해상도 변경, RGB ↔ YUV 변환
- 고품질 리사이징 알고리즘 제공
libswresample
- 오디오 리샘플링 라이브러리
- 샘플레이트 변환, 채널 믹싱
- 스테레오 ↔ 모노 변환 등
실전 팁:
- 일반 사용자: ffmpeg 명령어만 알면 충분
- 개발자: Python/Node.js 바인딩 사용 (ffmpeg-python, fluent-ffmpeg)
- 고급 개발자: libav 라이브러리 직접 사용 (C/C++)
2. 설치 및 기본 사용법
설치
macOS:
brew install ffmpeg
# 모든 옵션 포함
brew install ffmpeg --with-fdk-aac --with-libvpx --with-libvorbis
Ubuntu/Debian:
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
sudo apt update
sudo apt install ffmpeg
# 최신 버전 (PPA)
sudo add-apt-repository ppa:savoury1/ffmpeg4
sudo apt update
sudo apt install ffmpeg
Windows:
아래 코드는 powershell를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# Chocolatey
choco install ffmpeg
# 또는 수동 설치
# 1. https://ffmpeg.org/download.html
# 2. 압축 해제
# 3. 환경 변수 PATH에 bin 폴더 추가
Docker:
docker pull linuxserver/ffmpeg
docker run --rm -v $(pwd):/data linuxserver/ffmpeg \
-i /data/input.mp4 -c:v libx264 /data/output.mp4
버전 확인
ffmpeg -version
# 출력:
# ffmpeg version 6.1.1
# configuration: --enable-gpl --enable-libx264 --enable-libx265 ...
# libavcodec 60.31.102
# libavformat 60.16.100
# ...
기본 명령어 구조
FFmpeg 명령어는 일정한 패턴을 따릅니다. 이 구조를 이해하면 복잡한 명령어도 쉽게 만들 수 있습니다.
ffmpeg [전역 옵션] [입력 옵션] -i [입력 파일] [출력 옵션] [출력 파일]
# 기본 예시
ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4
# ↑ ↑ ↑ ↑
# 입력 비디오 코덱 품질 출력
# 더 복잡한 예시
ffmpeg -y -i input.mp4 -i audio.mp3 -c:v libx264 -crf 20 -c:a aac -b:a 192k -shortest output.mp4
# ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
# 덮어쓰기 비디오입력 오디오입력 비디오코덱 비디오품질 오디오코덱 오디오비트 짧은쪽맞춤 출력
주요 옵션 설명:
# 전역 옵션 (입력 파일 앞에 위치)
-y # 출력 파일 덮어쓰기 (확인 없이)
-n # 출력 파일 덮어쓰기 금지
-v quiet # 로그 출력 최소화
-stats # 진행 상황 표시
# 입력 옵션 (-i 앞에 위치)
-ss 00:01:30 # 시작 시간 (1분 30초부터)
-t 10 # 지속 시간 (10초만)
-to 00:02:00 # 종료 시간 (2분까지)
# 비디오 출력 옵션
-c:v libx264 # 비디오 코덱 (H.264)
-c:v copy # 비디오 재인코딩 없이 복사
-crf 23 # 품질 (0=최고, 51=최저, 23=권장)
-preset slow # 인코딩 속도 (slow=고품질)
-b:v 5M # 비디오 비트레이트 (5Mbps)
-r 30 # 프레임레이트 (30fps)
-vf scale=1280:720 # 비디오 필터 (해상도 변경)
# 오디오 출력 옵션
-c:a aac # 오디오 코덱 (AAC)
-c:a copy # 오디오 재인코딩 없이 복사
-b:a 192k # 오디오 비트레이트 (192kbps)
-ar 48000 # 샘플레이트 (48kHz)
-ac 2 # 채널 수 (2=스테레오)
# 기타 유용한 옵션
-an # 오디오 제거
-vn # 비디오 제거
-shortest # 가장 짧은 스트림에 맞춤
-map 0:v:0 # 첫 번째 비디오 스트림 선택
-map 0:a:1 # 두 번째 오디오 스트림 선택
실전 팁:
-
옵션 순서가 중요합니다
- 입력 옵션은
-i앞에 - 출력 옵션은
-i뒤, 출력 파일 앞에
- 입력 옵션은
-
-c:v와 -vcodec은 같습니다
-c:v libx264=-vcodec libx264-c:a aac=-acodec aac
-
copy는 재인코딩을 건너뜁니다
- 매우 빠르지만 품질 조정 불가
- 컨테이너만 바꿀 때 유용
-
여러 입력 파일 사용 가능
ffmpeg -i video.mp4 -i audio.mp3 -c copy output.mp4 -
진행 상황 확인
ffmpeg -i input.mp4 -c:v libx264 output.mp4 # 실시간으로 frame=, fps=, time= 등이 표시됩니다
3. 동영상 변환 및 인코딩
동영상 변환은 FFmpeg의 가장 기본적이면서도 강력한 기능입니다. 단순 포맷 변환부터 고급 인코딩까지 다룹니다.
기본 변환
가장 간단한 형태의 변환입니다. FFmpeg가 자동으로 적절한 코덱을 선택합니다.
# MP4 → AVI (자동 코덱 선택)
ffmpeg -i input.mp4 output.avi
# MOV → MP4 (자동 코덱 선택)
ffmpeg -i input.mov output.mp4
# MKV → MP4 (재인코딩 없이 복사 - 가장 빠름)
ffmpeg -i input.mkv -c copy output.mp4
# -c copy: 비디오와 오디오를 재인코딩 없이 그대로 복사
# 장점: 매우 빠름 (1분 영상을 1초 만에 변환), 품질 손실 없음
# 단점: 해상도/품질 조정 불가, 코덱이 컨테이너와 호환되어야 함
# 비디오만 복사, 오디오는 재인코딩
ffmpeg -i input.mkv -c:v copy -c:a aac output.mp4
# MKV의 비디오는 그대로 두고 오디오만 AAC로 변환
# 실전 예시: iPhone 촬영 영상(MOV)을 웹용 MP4로
ffmpeg -i iphone_video.mov -c:v libx264 -crf 23 -c:a aac -b:a 128k web_video.mp4
언제 -c copy를 쓸까?
- 컨테이너만 바꾸고 싶을 때 (MKV → MP4)
- 영상 자르기만 할 때 (품질 유지)
- 빠른 처리가 필요할 때
언제 재인코딩이 필요할까?
- 해상도를 바꿀 때
- 파일 크기를 줄일 때
- 호환성 문제가 있을 때 (예: HEVC → H.264)
- 필터를 적용할 때 (워터마크, 색보정 등)
H.264 인코딩
H.264는 가장 보편적인 비디오 코덱입니다. 거의 모든 기기에서 재생되며, 품질과 파일 크기의 균형이 우수합니다.
CRF (Constant Rate Factor) 방식 - 권장
CRF는 일정한 품질을 유지하면서 인코딩하는 방식입니다. 파일 크기는 영상의 복잡도에 따라 자동으로 조절됩니다.
# 기본 CRF 인코딩
ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4
# CRF 값 가이드:
# 0 = 무손실 (파일 크기 매우 큼, 거의 사용 안 함)
# 17-18 = 시각적으로 무손실 (전문가용, 편집 소스)
# 19-23 = 고품질 (일반 용도, YouTube 권장)
# 24-28 = 중간 품질 (웹 스트리밍, 모바일)
# 29-35 = 낮은 품질 (저사양 기기, 프리뷰용)
# 36-51 = 매우 낮은 품질 (거의 사용 안 함)
# 실전 예시들:
# YouTube 업로드용 (고품질)
ffmpeg -i input.mp4 -c:v libx264 -crf 18 -preset slow -c:a aac -b:a 192k youtube.mp4
# 웹사이트 배경 영상 (중간 품질, 작은 파일)
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset fast -c:a aac -b:a 128k web_bg.mp4
# 모바일 앱용 (균형잡힌 품질)
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -vf scale=1280:720 -c:a aac -b:a 128k mobile.mp4
# 아카이브용 (최고 품질)
ffmpeg -i input.mp4 -c:v libx264 -crf 17 -preset veryslow -c:a aac -b:a 320k archive.mp4
CRF 선택 가이드:
| 용도 | CRF 값 | 설명 |
|---|---|---|
| 편집 소스 | 17-18 | 재편집 시 품질 손실 최소화 |
| YouTube/Vimeo | 18-20 | 플랫폼 재인코딩 대비 |
| 일반 공유 | 21-23 | 대부분의 경우 적합 |
| 웹 스트리밍 | 24-26 | 빠른 로딩과 품질 균형 |
| 모바일 | 26-28 | 작은 화면, 데이터 절약 |
| 프리뷰/테스트 | 28-30 | 빠른 확인용 |
주의사항:
- CRF 값이 낮을수록 품질은 좋지만 파일 크기가 커집니다
- 같은 CRF라도 영상의 복잡도에 따라 파일 크기가 다릅니다
- 정적인 장면: 작은 파일
- 빠른 움직임, 복잡한 장면: 큰 파일
프리셋 (Preset) - 속도 vs 압축률
프리셋은 인코딩 속도와 압축 효율의 균형을 조절합니다. 느린 프리셋일수록 같은 품질에서 파일 크기가 작아지지만, 인코딩 시간이 오래 걸립니다.
# 프리셋 기본 사용법
ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 23 output.mp4
# 프리셋 종류 (빠름 → 느림):
ultrafast # 인코딩: 초고속 (실시간 가능), 파일: 매우 큼
superfast # 인코딩: 매우 빠름, 파일: 큼
veryfast # 인코딩: 빠름, 파일: 약간 큼
faster # 인코딩: 빠른 편, 파일: 보통
fast # 인코딩: 보통, 파일: 보통
medium # 인코딩: 보통 (기본값), 파일: 보통
slow # 인코딩: 느림, 파일: 작음 (★ 권장)
slower # 인코딩: 매우 느림, 파일: 매우 작음
veryslow # 인코딩: 극도로 느림, 파일: 최소
# 실전 예시:
# 빠른 테스트용 (품질 확인)
ffmpeg -i input.mp4 -c:v libx264 -preset ultrafast -crf 23 test.mp4
# 1080p 1분 영상: 약 5초 소요
# 일반 용도 (균형)
ffmpeg -i input.mp4 -c:v libx264 -preset medium -crf 23 output.mp4
# 1080p 1분 영상: 약 30초 소요
# 최종 배포용 (최적 품질)
ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 20 final.mp4
# 1080p 1분 영상: 약 1-2분 소요
# 아카이브용 (최대 압축)
ffmpeg -i input.mp4 -c:v libx264 -preset veryslow -crf 18 archive.mp4
# 1080p 1분 영상: 약 5-10분 소요
프리셋 선택 가이드:
| 상황 | 추천 프리셋 | 이유 |
|---|---|---|
| 실시간 스트리밍 | ultrafast, veryfast | CPU 부하 최소화 |
| 빠른 프리뷰 | veryfast, fast | 빠른 확인 |
| 일반 작업 | medium | 기본 균형 |
| 최종 배포 | slow | 최적 품질/크기 비율 |
| 아카이브 | slower, veryslow | 장기 보관용 최소 크기 |
| 배치 처리 | fast, medium | 대량 파일 처리 |
성능 비교 (1080p 1분 영상 기준):
프리셋 인코딩 시간 파일 크기 품질 (CRF 23)
ultrafast 5초 50MB 보통
veryfast 10초 40MB 좋음
medium 30초 30MB 매우 좋음
slow 1-2분 25MB 최고
veryslow 5-10분 23MB 최고+
실제 시간은 CPU 성능에 따라 다릅니다.
실전 팁:
-
개발/테스트 단계:
veryfast사용- 빠른 피드백으로 효율적인 작업
-
최종 배포:
slow사용- 시간을 들여도 최적 결과 확보
-
대량 처리:
medium사용- 시간과 품질의 균형
-
프리셋과 CRF 조합:
# 빠르게 + 높은 품질 = 큰 파일 ffmpeg -i input.mp4 -preset veryfast -crf 18 output.mp4 # 느리게 + 낮은 품질 = 작은 파일 ffmpeg -i input.mp4 -preset slow -crf 28 output.mp4 # 권장 조합 (일반 용도) ffmpeg -i input.mp4 -preset slow -crf 22 output.mp4
2-Pass 인코딩 - 목표 비트레이트 정확도
2-Pass 인코딩은 영상을 두 번 처리합니다. 첫 번째는 분석, 두 번째는 실제 인코딩입니다. 목표 비트레이트를 정확히 맞춰야 할 때 사용합니다.
# 1단계: 분석 (영상 전체를 스캔하여 통계 수집)
ffmpeg -i input.mp4 -c:v libx264 -b:v 5M -pass 1 -f null /dev/null
# Windows에서는: -f null NUL
# 2단계: 실제 인코딩 (1단계 통계를 바탕으로 최적화)
ffmpeg -i input.mp4 -c:v libx264 -b:v 5M -pass 2 output.mp4
# 완전한 예시 (오디오 포함)
# Pass 1
ffmpeg -i input.mp4 -c:v libx264 -b:v 5M -pass 1 -an -f null /dev/null
# Pass 2
ffmpeg -i input.mp4 -c:v libx264 -b:v 5M -pass 2 -c:a aac -b:a 192k output.mp4
2-Pass vs 1-Pass (CRF) 비교:
| 특성 | 1-Pass (CRF) | 2-Pass (Bitrate) |
|---|---|---|
| 인코딩 시간 | 1배 | 2배 |
| 품질 일관성 | 일정한 품질 | 일정한 비트레이트 |
| 파일 크기 | 가변적 | 정확히 목표치 |
| 사용 시기 | 일반 용도 | 스트리밍, 방송 |
| 복잡한 장면 | 비트레이트 증가 | 품질 약간 저하 |
| 단순한 장면 | 비트레이트 감소 | 품질 약간 향상 |
언제 2-Pass를 사용할까?
✅ 사용해야 할 때:
- 정확한 파일 크기가 필요할 때 (예: DVD, Blu-ray)
- 스트리밍 비트레이트 상한이 정해져 있을 때
- 방송 송출 규격을 맞춰야 할 때
- 여러 영상의 비트레이트를 동일하게 맞춰야 할 때
❌ 사용하지 않아도 될 때:
- 일반 파일 변환 (CRF가 더 간단하고 효율적)
- 빠른 처리가 필요할 때
- 파일 크기가 유동적이어도 될 때
- YouTube 등 업로드 (플랫폼이 다시 인코딩함)
실전 예시:
# 웹 스트리밍용 (정확한 비트레이트 필요)
# Pass 1
ffmpeg -i input.mp4 -c:v libx264 -b:v 2M -maxrate 2M -bufsize 4M \
-pass 1 -an -f null /dev/null
# Pass 2
ffmpeg -i input.mp4 -c:v libx264 -b:v 2M -maxrate 2M -bufsize 4M \
-pass 2 -c:a aac -b:a 128k streaming.mp4
# DVD 제작용 (MPEG-2, 정확한 크기)
# Pass 1
ffmpeg -i input.mp4 -c:v mpeg2video -b:v 8M -pass 1 -an -f null /dev/null
# Pass 2
ffmpeg -i input.mp4 -c:v mpeg2video -b:v 8M -pass 2 -c:a mp2 -b:a 192k dvd.mpg
# 모바일 스트리밍 (낮은 비트레이트, 정확한 제어)
# Pass 1
ffmpeg -i input.mp4 -c:v libx264 -b:v 500k -maxrate 500k -bufsize 1M \
-pass 1 -an -f null /dev/null
# Pass 2
ffmpeg -i input.mp4 -c:v libx264 -b:v 500k -maxrate 500k -bufsize 1M \
-pass 2 -c:a aac -b:a 64k mobile.mp4
2-Pass 최적화 팁:
-
첫 번째 Pass는 빠르게:
# preset을 빠르게 설정 (분석만 하므로) ffmpeg -i input.mp4 -c:v libx264 -b:v 5M -preset veryfast -pass 1 -f null /dev/null ffmpeg -i input.mp4 -c:v libx264 -b:v 5M -preset slow -pass 2 output.mp4 -
로그 파일 정리:
# 2-Pass는 ffmpeg2pass-0.log 파일을 생성합니다 # 작업 후 삭제: rm ffmpeg2pass-0.log -
maxrate와 bufsize 함께 사용:
# 비트레이트 스파이크 방지 -b:v 5M -maxrate 5M -bufsize 10M
성능 비교:
1080p 10분 영상 기준:
1-Pass CRF:
- 시간: 5분
- 크기: 500MB (가변)
- 품질: 일정
2-Pass 5Mbps:
- 시간: 10분 (2배)
- 크기: 375MB (정확)
- 품질: 약간 가변
결론: 일반 용도는 1-Pass CRF 추천
H.265 (HEVC) 인코딩
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# H.265 기본
ffmpeg -i input.mp4 -c:v libx265 -crf 28 output.mp4
# H.265는 H.264보다 CRF 값을 4-6 높여도 비슷한 품질
# H.264 CRF 23 ≈ H.265 CRF 28
# 프리셋 포함
ffmpeg -i input.mp4 -c:v libx265 -preset medium -crf 28 output.mp4
# 태그 추가 (호환성)
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -tag:v hvc1 output.mp4
해상도 변경
해상도 변경은 모바일 최적화, 웹 스트리밍, 파일 크기 절감 등 다양한 목적으로 사용됩니다.
# 기본 해상도 변경 (1080p → 720p)
ffmpeg -i input.mp4 -vf scale=1280:720 output.mp4
# 비율 유지 (너비 고정, 높이 자동 계산)
ffmpeg -i input.mp4 -vf scale=1280:-1 output.mp4
# -1: 원본 비율 유지하며 자동 계산
# 비율 유지 (높이 고정, 너비 자동 계산)
ffmpeg -i input.mp4 -vf scale=-1:720 output.mp4
# 짝수 픽셀 보장 (H.264 등 일부 코덱 요구사항)
ffmpeg -i input.mp4 -vf scale=1280:-2 output.mp4
# -2: 2의 배수로 자동 조정 (홀수 픽셀 방지)
# 4K → 1080p (고품질 다운스케일)
ffmpeg -i input_4k.mp4 -vf scale=1920:1080 -c:v libx264 -preset slow -crf 18 output_1080p.mp4
실전 예시:
# 모바일 최적화 (720p, 작은 파일)
ffmpeg -i input.mp4 -vf scale=1280:-2 -c:v libx264 -crf 26 -preset fast -c:a aac -b:a 96k mobile.mp4
# 웹 스트리밍용 다중 해상도 생성
# 1080p
ffmpeg -i input.mp4 -vf scale=1920:-2 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 192k output_1080p.mp4
# 720p
ffmpeg -i input.mp4 -vf scale=1280:-2 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output_720p.mp4
# 480p
ffmpeg -i input.mp4 -vf scale=854:-2 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 96k output_480p.mp4
# Instagram 정사각형 (1:1 비율, 크롭)
ffmpeg -i input.mp4 -vf "scale=1080:1080:force_original_aspect_ratio=increase,crop=1080:1080" \
-c:v libx264 -crf 23 instagram.mp4
# YouTube Shorts / TikTok (9:16 세로 비율)
ffmpeg -i input.mp4 -vf "scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920" \
-c:v libx264 -crf 23 shorts.mp4
# 4K 다운스케일 (고품질 유지)
ffmpeg -i input_4k.mp4 -vf scale=1920:-2 -c:v libx264 -preset slow -crf 18 \
-c:a copy output_1080p.mp4
고급 스케일링 옵션:
# 고품질 스케일링 알고리즘 (lanczos)
ffmpeg -i input.mp4 -vf scale=1280:-2:flags=lanczos output.mp4
# 스케일링 알고리즘 비교:
# bilinear: 빠름, 품질 보통
# bicubic: 중간 (기본값)
# lanczos: 느림, 품질 최고
# spline: 부드러움
# 예시: 각 알고리즘 비교
ffmpeg -i input.mp4 -vf scale=1280:-2:flags=bilinear bilinear.mp4
ffmpeg -i input.mp4 -vf scale=1280:-2:flags=bicubic bicubic.mp4
ffmpeg -i input.mp4 -vf scale=1280:-2:flags=lanczos lanczos.mp4
# 업스케일링 (권장하지 않음, 품질 향상 없음)
ffmpeg -i 720p.mp4 -vf scale=1920:-2:flags=lanczos 1080p.mp4
# 주의: 해상도만 커지고 실제 화질은 개선되지 않음
비율 맞추기:
# 16:9 비율 강제 (패딩 추가)
ffmpeg -i input.mp4 -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" output.mp4
# 4:3 → 16:9 (좌우 검은 바 추가)
ffmpeg -i input_4_3.mp4 -vf "scale=1920:-2,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:black" output_16_9.mp4
# 16:9 → 4:3 (상하 검은 바 추가)
ffmpeg -i input_16_9.mp4 -vf "scale=-2:1080,pad=1440:1080:(ow-iw)/2:(oh-ih)/2:black" output_4_3.mp4
일괄 변환 스크립트:
# 폴더 내 모든 영상을 720p로 변환
for file in *.mp4; do
ffmpeg -i "$file" -vf scale=1280:-2 -c:v libx264 -crf 23 "720p_$file"
done
# 다중 해상도 자동 생성
for file in *.mp4; do
base="${file%.mp4}"
ffmpeg -i "$file" -vf scale=1920:-2 -c:v libx264 -crf 23 "${base}_1080p.mp4"
ffmpeg -i "$file" -vf scale=1280:-2 -c:v libx264 -crf 23 "${base}_720p.mp4"
ffmpeg -i "$file" -vf scale=854:-2 -c:v libx264 -crf 23 "${base}_480p.mp4"
done
주의사항:
-
업스케일링은 피하세요
- 720p → 1080p로 키워도 화질은 개선되지 않습니다
- 오히려 파일 크기만 커집니다
-
홀수 픽셀 문제
- H.264 등은 짝수 픽셀을 요구합니다
-2사용으로 자동 조정
-
비율 왜곡 주의
scale=1280:720는 강제로 늘리거나 줄입니다- 비율 유지는
-1또는-2사용
-
성능 고려
- 다운스케일은 빠름
- 업스케일은 느리고 의미 없음
프레임레이트 변경
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 60fps → 30fps
ffmpeg -i input.mp4 -r 30 output.mp4
# 24fps → 60fps (보간)
ffmpeg -i input.mp4 -filter:v "minterpolate=fps=60" output.mp4
비트레이트 설정
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 비디오 비트레이트
ffmpeg -i input.mp4 -b:v 5M output.mp4
# 오디오 비트레이트
ffmpeg -i input.mp4 -b:a 192k output.mp4
# 둘 다
ffmpeg -i input.mp4 -b:v 5M -b:a 192k output.mp4
# 최대 비트레이트 제한
ffmpeg -i input.mp4 -b:v 5M -maxrate 5M -bufsize 10M output.mp4
4. 오디오 처리
오디오 추출
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 동영상에서 오디오만 추출
ffmpeg -i video.mp4 -vn -c:a copy audio.aac
# -vn: 비디오 제외
# MP3로 변환
ffmpeg -i video.mp4 -vn -c:a libmp3lame -b:a 192k audio.mp3
# WAV로 변환 (무손실)
ffmpeg -i video.mp4 -vn audio.wav
오디오 변환
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# MP3 → AAC
ffmpeg -i input.mp3 -c:a aac -b:a 192k output.aac
# WAV → MP3
ffmpeg -i input.wav -c:a libmp3lame -b:a 320k output.mp3
# FLAC → MP3 (무손실 → 손실)
ffmpeg -i input.flac -c:a libmp3lame -q:a 0 output.mp3
# -q:a 0: 최고 품질 (VBR)
오디오 편집
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 볼륨 조절 (2배)
ffmpeg -i input.mp3 -af "volume=2.0" output.mp3
# 볼륨 조절 (dB)
ffmpeg -i input.mp3 -af "volume=10dB" output.mp3
# 페이드 인/아웃
ffmpeg -i input.mp3 -af "afade=t=in:st=0:d=3,afade=t=out:st=57:d=3" output.mp3
# t=in: 페이드 인, t=out: 페이드 아웃
# st: 시작 시간(초), d: 지속 시간(초)
# 노이즈 제거
ffmpeg -i input.mp3 -af "highpass=f=200,lowpass=f=3000" output.mp3
# 스테레오 → 모노
ffmpeg -i input.mp3 -ac 1 output.mp3
# 샘플레이트 변경
ffmpeg -i input.mp3 -ar 44100 output.mp3
5. 썸네일 및 이미지 추출
썸네일 추출
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 첫 프레임 추출
ffmpeg -i video.mp4 -vframes 1 thumbnail.jpg
# 특정 시간 (5초)
ffmpeg -i video.mp4 -ss 00:00:05 -vframes 1 thumbnail.jpg
# 고품질
ffmpeg -i video.mp4 -ss 00:00:05 -vframes 1 -q:v 2 thumbnail.jpg
# -q:v 2: 품질 (1=최고, 31=최저)
# 특정 크기
ffmpeg -i video.mp4 -ss 00:00:05 -vf scale=1280:720 -vframes 1 thumbnail.jpg
여러 썸네일 추출
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 1초마다 추출
ffmpeg -i video.mp4 -vf fps=1 thumb_%04d.jpg
# 10초마다 추출
ffmpeg -i video.mp4 -vf fps=1/10 thumb_%04d.jpg
# 특정 구간 (10초~20초, 1초마다)
ffmpeg -i video.mp4 -ss 00:00:10 -to 00:00:20 -vf fps=1 thumb_%04d.jpg
GIF 생성
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 동영상 → GIF
ffmpeg -i video.mp4 -vf "fps=10,scale=480:-1:flags=lanczos" output.gif
# 고품질 GIF (팔레트 생성)
# 1단계: 팔레트 생성
ffmpeg -i video.mp4 -vf "fps=10,scale=480:-1:flags=lanczos,palettegen" palette.png
# 2단계: GIF 생성
ffmpeg -i video.mp4 -i palette.png -filter_complex "fps=10,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse" output.gif
# 특정 구간 (0~5초)
ffmpeg -ss 00:00:00 -t 00:00:05 -i video.mp4 -vf "fps=10,scale=480:-1:flags=lanczos,palettegen" palette.png
ffmpeg -ss 00:00:00 -t 00:00:05 -i video.mp4 -i palette.png -filter_complex "fps=10,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse" output.gif
6. 동영상 편집
자르기 (Trim)
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 시간 기준 자르기
ffmpeg -i input.mp4 -ss 00:00:10 -to 00:00:30 output.mp4
# -ss: 시작 시간
# -to: 종료 시간
# 또는
ffmpeg -i input.mp4 -ss 00:00:10 -t 00:00:20 output.mp4
# -t: 지속 시간 (20초)
# 재인코딩 없이 (빠름, 정확도 낮음)
ffmpeg -ss 00:00:10 -i input.mp4 -to 00:00:30 -c copy output.mp4
# -ss를 -i 앞에 두면 더 빠름 (키프레임 탐색)
크롭 (Crop)
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 중앙 1280×720 크롭
ffmpeg -i input.mp4 -vf "crop=1280:720" output.mp4
# 특정 위치 크롭
ffmpeg -i input.mp4 -vf "crop=1280:720:0:0" output.mp4
# crop=width:height:x:y
# 16:9 → 1:1 (정사각형, 중앙)
ffmpeg -i input.mp4 -vf "crop=ih:ih" output.mp4
# ih: 입력 높이
회전
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 90도 시계방향
ffmpeg -i input.mp4 -vf "transpose=1" output.mp4
# 90도 반시계방향
ffmpeg -i input.mp4 -vf "transpose=2" output.mp4
# 180도
ffmpeg -i input.mp4 -vf "transpose=1,transpose=1" output.mp4
# 메타데이터로 회전 (재인코딩 없음)
ffmpeg -i input.mp4 -c copy -metadata:s:v:0 rotate=90 output.mp4
병합
동영상 이어붙이기:
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 1. 파일 목록 생성
cat > filelist.txt << EOF
file 'video1.mp4'
file 'video2.mp4'
file 'video3.mp4'
EOF
# 2. 병합
ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp4
# 재인코딩 포함
ffmpeg -f concat -safe 0 -i filelist.txt -c:v libx264 -crf 23 output.mp4
오디오 추가:
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 동영상에 오디오 추가
ffmpeg -i video.mp4 -i audio.mp3 -c:v copy -c:a aac output.mp4
# 기존 오디오 교체
ffmpeg -i video.mp4 -i audio.mp3 -c:v copy -c:a aac -map 0:v:0 -map 1:a:0 output.mp4
7. 스트리밍 및 HLS
HLS 생성
HLS (HTTP Live Streaming)는 Apple이 개발한 스트리밍 프로토콜이다.
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 기본 HLS
ffmpeg -i input.mp4 \
-c:v libx264 -c:a aac \
-hls_time 10 \
-hls_playlist_type vod \
-hls_segment_filename "segment_%03d.ts" \
playlist.m3u8
# 옵션:
# -hls_time 10: 세그먼트 길이 (10초)
# -hls_playlist_type vod: VOD (주문형)
# -hls_segment_filename: 세그먼트 파일명
다중 해상도 (Adaptive Bitrate):
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 1080p
ffmpeg -i input.mp4 \
-vf scale=1920:1080 -c:v libx264 -b:v 5M -c:a aac -b:a 192k \
-hls_time 10 -hls_playlist_type vod \
-hls_segment_filename "1080p_%03d.ts" \
1080p.m3u8
# 720p
ffmpeg -i input.mp4 \
-vf scale=1280:720 -c:v libx264 -b:v 3M -c:a aac -b:a 128k \
-hls_time 10 -hls_playlist_type vod \
-hls_segment_filename "720p_%03d.ts" \
720p.m3u8
# 480p
ffmpeg -i input.mp4 \
-vf scale=854:480 -c:v libx264 -b:v 1.5M -c:a aac -b:a 128k \
-hls_time 10 -hls_playlist_type vod \
-hls_segment_filename "480p_%03d.ts" \
480p.m3u8
# Master Playlist 생성
cat > master.m3u8 << EOF
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080
1080p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3000000,RESOLUTION=1280x720
720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1500000,RESOLUTION=854x480
480p.m3u8
EOF
RTMP 스트리밍
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# YouTube 라이브
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset veryfast -b:v 3M -maxrate 3M -bufsize 6M \
-pix_fmt yuv420p -g 60 -c:a aac -b:a 128k -ar 44100 \
-f flv rtmp://a.rtmp.youtube.com/live2/YOUR_STREAM_KEY
# Twitch 라이브
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset veryfast -b:v 3M \
-c:a aac -b:a 128k \
-f flv rtmp://live.twitch.tv/app/YOUR_STREAM_KEY
# -re: 실시간 속도로 읽기
# -g 60: GOP 크기 (2초, 30fps 기준)
웹캠 스트리밍
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# macOS (웹캠)
ffmpeg -f avfoundation -i "0:0" \
-c:v libx264 -preset veryfast -b:v 2M \
-c:a aac -b:a 128k \
-f flv rtmp://localhost/live/stream
# Linux (웹캠)
ffmpeg -f v4l2 -i /dev/video0 \
-c:v libx264 -preset veryfast -b:v 2M \
-f flv rtmp://localhost/live/stream
# Windows (웹캠)
ffmpeg -f dshow -i video="USB Video Device" \
-c:v libx264 -preset veryfast -b:v 2M \
-f flv rtmp://localhost/live/stream
8. 필터 및 효과
워터마크
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 이미지 워터마크 (우측 상단)
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "overlay=W-w-10:10" \
output.mp4
# 좌측 하단
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "overlay=10:H-h-10" \
output.mp4
# 중앙
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "overlay=(W-w)/2:(H-h)/2" \
output.mp4
# 투명도 조절
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "[1:v]format=rgba,colorchannelmixer=aa=0.5[logo];[0:v][logo]overlay=W-w-10:10" \
output.mp4
텍스트 추가
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 간단한 텍스트
ffmpeg -i input.mp4 \
-vf "drawtext=text='Hello World':fontsize=48:fontcolor=white:x=10:y=10" \
output.mp4
# 시간 표시
ffmpeg -i input.mp4 \
-vf "drawtext=text='%{pts\:hms}':fontsize=32:fontcolor=white:x=10:y=10" \
output.mp4
# 한글 텍스트 (폰트 지정 필수)
ffmpeg -i input.mp4 \
-vf "drawtext=text='안녕하세요':fontfile=/System/Library/Fonts/AppleSDGothicNeo.ttc:fontsize=48:fontcolor=white:x=10:y=10" \
output.mp4
# 배경 추가
ffmpeg -i input.mp4 \
-vf "drawtext=text='Hello':fontsize=48:fontcolor=white:x=10:y=10:box=1:[email protected]:boxborderw=5" \
output.mp4
색상 조정
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 밝기 조절
ffmpeg -i input.mp4 -vf "eq=brightness=0.1" output.mp4
# 대비 조절
ffmpeg -i input.mp4 -vf "eq=contrast=1.5" output.mp4
# 채도 조절
ffmpeg -i input.mp4 -vf "eq=saturation=1.5" output.mp4
# 흑백
ffmpeg -i input.mp4 -vf "hue=s=0" output.mp4
# 세피아
ffmpeg -i input.mp4 -vf "colorchannelmixer=.393:.769:.189:0:.349:.686:.168:0:.272:.534:.131" output.mp4
블러 및 샤프닝
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 블러
ffmpeg -i input.mp4 -vf "boxblur=5:1" output.mp4
# 샤프닝
ffmpeg -i input.mp4 -vf "unsharp=5:5:1.0:5:5:0.0" output.mp4
# 특정 영역만 블러 (얼굴 가리기)
ffmpeg -i input.mp4 \
-vf "boxblur=10:enable='between(t,5,10)'" \
output.mp4
9. 미디어 정보 확인
ffprobe 사용
아래 코드는 bash를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 기본 정보
ffprobe input.mp4
# JSON 형식
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
# 비디오 정보만
ffprobe -v error -select_streams v:0 -show_entries stream=width,height,r_frame_rate,bit_rate -of default=noprint_wrappers=1 input.mp4
# 오디오 정보만
ffprobe -v error -select_streams a:0 -show_entries stream=codec_name,sample_rate,channels,bit_rate -of default=noprint_wrappers=1 input.mp4
# 길이 확인
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4
실용적인 정보 추출
# 해상도
ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 input.mp4
# 출력: 1920x1080
# 프레임레이트
ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate -of default=noprint_wrappers=1:nokey=1 input.mp4
# 출력: 30/1
# 비트레이트
ffprobe -v error -show_entries format=bit_rate -of default=noprint_wrappers=1:nokey=1 input.mp4
# 출력: 5000000 (5 Mbps)
# 코덱
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 input.mp4
# 출력: h264
10. FFmpeg 라이브러리 (libav)
Python (ffmpeg-python)
설치:
pip install ffmpeg-python
기본 사용:
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import ffmpeg
# 동영상 변환
(
ffmpeg
.input('input.mp4')
.output('output.mp4', vcodec='libx264', crf=23)
.run()
)
# 해상도 변경
(
ffmpeg
.input('input.mp4')
.filter('scale', 1280, 720)
.output('output.mp4')
.run()
)
# 썸네일 추출
(
ffmpeg
.input('input.mp4', ss=5)
.output('thumbnail.jpg', vframes=1)
.run()
)
# 오디오 추출
(
ffmpeg
.input('input.mp4')
.output('audio.mp3', vn=None, acodec='libmp3lame', audio_bitrate='192k')
.run()
)
고급 예제:
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import ffmpeg
import sys
def convert_video(input_file, output_file, resolution='1280:720', crf=23):
"""동영상 변환"""
try:
(
ffmpeg
.input(input_file)
.filter('scale', *resolution.split(':'))
.output(
output_file,
vcodec='libx264',
preset='slow',
crf=crf,
acodec='aac',
audio_bitrate='192k'
)
.global_args('-loglevel', 'error')
.run(capture_stdout=True, capture_stderr=True)
)
print(f"✅ Conversion complete: {output_file}")
except ffmpeg.Error as e:
print(f"❌ Error: {e.stderr.decode()}", file=sys.stderr)
raise
def get_video_info(input_file):
"""동영상 정보 추출"""
try:
probe = ffmpeg.probe(input_file)
video_stream = next((s for s in probe['streams'] if s['codec_type'] == 'video'), None)
audio_stream = next((s for s in probe['streams'] if s['codec_type'] == 'audio'), None)
return {
'duration': float(probe['format']['duration']),
'size': int(probe['format']['size']),
'bitrate': int(probe['format']['bit_rate']),
'video': {
'codec': video_stream['codec_name'],
'width': video_stream['width'],
'height': video_stream['height'],
'fps': eval(video_stream['r_frame_rate'])
} if video_stream else None,
'audio': {
'codec': audio_stream['codec_name'],
'sample_rate': audio_stream['sample_rate'],
'channels': audio_stream['channels']
} if audio_stream else None
}
except ffmpeg.Error as e:
print(f"❌ Error: {e.stderr.decode()}", file=sys.stderr)
raise
# 사용
info = get_video_info('input.mp4')
print(f"Duration: {info['duration']}s")
print(f"Resolution: {info['video']['width']}x{info['video']['height']}")
print(f"FPS: {info['video']['fps']}")
진행률 표시:
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import ffmpeg
import sys
def convert_with_progress(input_file, output_file):
"""진행률 표시"""
# 동영상 길이 확인
probe = ffmpeg.probe(input_file)
duration = float(probe['format']['duration'])
# 변환 시작
process = (
ffmpeg
.input(input_file)
.output(output_file, vcodec='libx264', crf=23)
.global_args('-progress', 'pipe:1')
.overwrite_output()
.run_async(pipe_stdout=True, pipe_stderr=True)
)
# 진행률 파싱
for line in process.stdout:
line = line.decode()
if line.startswith('out_time_ms='):
time_ms = int(line.split('=')[1])
time_s = time_ms / 1000000.0
progress = (time_s / duration) * 100
sys.stdout.write(f"\rProgress: {progress:.1f}%")
sys.stdout.flush()
process.wait()
print("\n✅ Complete!")
convert_with_progress('input.mp4', 'output.mp4')
Node.js (fluent-ffmpeg)
설치:
npm install fluent-ffmpeg
기본 사용:
다음은 javascript를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
const ffmpeg = require('fluent-ffmpeg');
// 동영상 변환
ffmpeg('input.mp4')
.output('output.mp4')
.videoCodec('libx264')
.audioCodec('aac')
.on('end', () => console.log('✅ Complete!'))
.on('error', (err) => console.error('❌ Error:', err))
.run();
// 해상도 변경
ffmpeg('input.mp4')
.size('1280x720')
.output('output.mp4')
.run();
// 썸네일 추출
ffmpeg('input.mp4')
.screenshots({
timestamps: ['00:00:05'],
filename: 'thumbnail.jpg',
folder: './thumbnails'
});
고급 예제:
다음은 javascript를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
const ffmpeg = require('fluent-ffmpeg');
const path = require('path');
// 동영상 정보 추출
function getVideoInfo(inputPath) {
return new Promise((resolve, reject) => {
ffmpeg.ffprobe(inputPath, (err, metadata) => {
if (err) return reject(err);
const videoStream = metadata.streams.find(s => s.codec_type === 'video');
const audioStream = metadata.streams.find(s => s.codec_type === 'audio');
resolve({
duration: metadata.format.duration,
size: metadata.format.size,
bitrate: metadata.format.bit_rate,
video: videoStream ? {
codec: videoStream.codec_name,
width: videoStream.width,
height: videoStream.height,
fps: eval(videoStream.r_frame_rate)
} : null,
audio: audioStream ? {
codec: audioStream.codec_name,
sampleRate: audioStream.sample_rate,
channels: audioStream.channels
} : null
});
});
});
}
// 진행률 표시
function convertWithProgress(inputPath, outputPath) {
return new Promise((resolve, reject) => {
ffmpeg(inputPath)
.output(outputPath)
.videoCodec('libx264')
.videoBitrate('5M')
.audioCodec('aac')
.audioBitrate('192k')
.on('start', (cmd) => {
console.log('Starting:', cmd);
})
.on('progress', (progress) => {
console.log(`Progress: ${progress.percent?.toFixed(1)}%`);
})
.on('end', () => {
console.log('✅ Complete!');
resolve();
})
.on('error', (err) => {
console.error('❌ Error:', err);
reject(err);
})
.run();
});
}
// HLS 생성
function createHLS(inputPath, outputDir) {
return new Promise((resolve, reject) => {
ffmpeg(inputPath)
.outputOptions([
'-c:v libx264',
'-c:a aac',
'-hls_time 10',
'-hls_playlist_type vod',
'-hls_segment_filename', path.join(outputDir, 'segment_%03d.ts')
])
.output(path.join(outputDir, 'playlist.m3u8'))
.on('end', resolve)
.on('error', reject)
.run();
});
}
// 사용
(async () => {
const info = await getVideoInfo('input.mp4');
console.log('Video Info:', info);
await convertWithProgress('input.mp4', 'output.mp4');
await createHLS('input.mp4', './hls');
})();
Go (ffmpeg-go)
설치:
go get github.com/u2takey/ffmpeg-go
기본 사용:
다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
package main
import (
"fmt"
ffmpeg "github.com/u2takey/ffmpeg-go"
)
func main() {
// 동영상 변환
err := ffmpeg.Input("input.mp4").
Output("output.mp4", ffmpeg.KwArgs{
"c:v": "libx264",
"crf": 23,
}).
Run()
if err != nil {
panic(err)
}
fmt.Println("✅ Complete!")
}
// 해상도 변경
func resizeVideo(input, output string) error {
return ffmpeg.Input(input).
Filter("scale", ffmpeg.Args{"1280:720"}).
Output(output).
Run()
}
// 썸네일 추출
func extractThumbnail(input, output string, timestamp float64) error {
return ffmpeg.Input(input, ffmpeg.KwArgs{"ss": timestamp}).
Output(output, ffmpeg.KwArgs{"vframes": 1}).
Run()
}
// 오디오 추출
func extractAudio(input, output string) error {
return ffmpeg.Input(input).
Output(output, ffmpeg.KwArgs{
"vn": "",
"c:a": "libmp3lame",
"b:a": "192k",
}).
Run()
}
11. 실전 프로젝트
프로젝트 1: 동영상 업로드 서버
Node.js + Express:
다음은 javascript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
const express = require('express');
const multer = require('multer');
const ffmpeg = require('fluent-ffmpeg');
const path = require('path');
const fs = require('fs').promises;
const app = express();
const upload = multer({ dest: 'uploads/' });
// 동영상 업로드 및 처리
app.post('/upload', upload.single('video'), async (req, res) => {
const inputPath = req.file.path;
const outputDir = path.join('processed', req.file.filename);
try {
await fs.mkdir(outputDir, { recursive: true });
// 1. 썸네일 생성
await new Promise((resolve, reject) => {
ffmpeg(inputPath)
.screenshots({
timestamps: ['00:00:01'],
filename: 'thumbnail.jpg',
folder: outputDir
})
.on('end', resolve)
.on('error', reject);
});
// 2. 다중 해상도 변환
const resolutions = [
{ name: '1080p', size: '1920x1080', bitrate: '5M' },
{ name: '720p', size: '1280x720', bitrate: '3M' },
{ name: '480p', size: '854x480', bitrate: '1.5M' }
];
await Promise.all(resolutions.map(res => {
return new Promise((resolve, reject) => {
ffmpeg(inputPath)
.size(res.size)
.videoBitrate(res.bitrate)
.output(path.join(outputDir, `${res.name}.mp4`))
.on('end', resolve)
.on('error', reject)
.run();
});
}));
// 3. HLS 생성
await new Promise((resolve, reject) => {
ffmpeg(inputPath)
.outputOptions([
'-c:v libx264',
'-c:a aac',
'-hls_time 10',
'-hls_playlist_type vod',
'-hls_segment_filename', path.join(outputDir, 'segment_%03d.ts')
])
.output(path.join(outputDir, 'playlist.m3u8'))
.on('end', resolve)
.on('error', reject)
.run();
});
// 4. 원본 파일 삭제
await fs.unlink(inputPath);
res.json({
success: true,
thumbnail: `/processed/${req.file.filename}/thumbnail.jpg`,
videos: resolutions.map(r => ({
quality: r.name,
url: `/processed/${req.file.filename}/${r.name}.mp4`
})),
hls: `/processed/${req.file.filename}/playlist.m3u8`
});
} catch (error) {
console.error('Processing error:', error);
res.status(500).json({ error: 'Processing failed' });
}
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
프로젝트 2: 동영상 스트리밍 서버
Python + Flask:
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from flask import Flask, request, send_file, jsonify
import ffmpeg
import os
import uuid
app = Flask(__name__)
UPLOAD_FOLDER = 'uploads'
PROCESSED_FOLDER = 'processed'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(PROCESSED_FOLDER, exist_ok=True)
@app.route('/upload', methods=['POST'])
def upload_video():
if 'video' not in request.files:
return jsonify({'error': 'No video file'}), 400
file = request.files['video']
video_id = str(uuid.uuid4())
input_path = os.path.join(UPLOAD_FOLDER, f"{video_id}.mp4")
output_dir = os.path.join(PROCESSED_FOLDER, video_id)
os.makedirs(output_dir, exist_ok=True)
file.save(input_path)
try:
# 1. 동영상 정보 추출
probe = ffmpeg.probe(input_path)
duration = float(probe['format']['duration'])
# 2. 썸네일 생성 (3개)
timestamps = [duration * 0.25, duration * 0.5, duration * 0.75]
for i, ts in enumerate(timestamps):
(
ffmpeg
.input(input_path, ss=ts)
.output(os.path.join(output_dir, f'thumb_{i}.jpg'), vframes=1)
.overwrite_output()
.run(capture_stdout=True, capture_stderr=True)
)
# 3. HLS 생성 (다중 해상도)
resolutions = [
{'name': '1080p', 'width': 1920, 'height': 1080, 'bitrate': '5M'},
{'name': '720p', 'width': 1280, 'height': 720, 'bitrate': '3M'},
{'name': '480p', 'width': 854, 'height': 480, 'bitrate': '1.5M'}
]
for res in resolutions:
res_dir = os.path.join(output_dir, res['name'])
os.makedirs(res_dir, exist_ok=True)
(
ffmpeg
.input(input_path)
.output(
os.path.join(res_dir, 'playlist.m3u8'),
**{
'c:v': 'libx264',
'b:v': res['bitrate'],
'c:a': 'aac',
'b:a': '128k',
'vf': f"scale={res['width']}:{res['height']}",
'hls_time': 10,
'hls_playlist_type': 'vod',
'hls_segment_filename': os.path.join(res_dir, 'segment_%03d.ts')
}
)
.overwrite_output()
.run(capture_stdout=True, capture_stderr=True)
)
# 4. Master Playlist 생성
master_playlist = "#EXTM3U\n"
for res in resolutions:
bandwidth = int(res['bitrate'].replace('M', '')) * 1000000
master_playlist += f"#EXT-X-STREAM-INF:BANDWIDTH={bandwidth},RESOLUTION={res['width']}x{res['height']}\n"
master_playlist += f"{res['name']}/playlist.m3u8\n"
with open(os.path.join(output_dir, 'master.m3u8'), 'w') as f:
f.write(master_playlist)
# 5. 원본 삭제
os.remove(input_path)
return jsonify({
'video_id': video_id,
'duration': duration,
'thumbnails': [f'/processed/{video_id}/thumb_{i}.jpg' for i in range(3)],
'hls': f'/processed/{video_id}/master.m3u8'
})
except ffmpeg.Error as e:
return jsonify({'error': e.stderr.decode()}), 500
@app.route('/processed/<path:filename>')
def serve_processed(filename):
return send_file(os.path.join(PROCESSED_FOLDER, filename))
if __name__ == '__main__':
app.run(debug=True, port=5000)
프로젝트 3: 일괄 변환 도구
Python CLI:
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#!/usr/bin/env python3
import ffmpeg
import argparse
import os
import sys
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
def convert_video(input_path, output_dir, resolution, crf, preset):
"""단일 동영상 변환"""
input_name = Path(input_path).stem
output_path = os.path.join(output_dir, f"{input_name}_{resolution}.mp4")
try:
width, height = resolution.split('x')
(
ffmpeg
.input(input_path)
.filter('scale', width, height)
.output(
output_path,
vcodec='libx264',
preset=preset,
crf=crf,
acodec='aac',
audio_bitrate='192k'
)
.global_args('-loglevel', 'error')
.overwrite_output()
.run(capture_stdout=True, capture_stderr=True)
)
return f"✅ {input_name} → {output_path}"
except ffmpeg.Error as e:
return f"❌ {input_name}: {e.stderr.decode()}"
def batch_convert(input_dir, output_dir, resolution='1280x720', crf=23, preset='medium', workers=4):
"""일괄 변환"""
os.makedirs(output_dir, exist_ok=True)
# 동영상 파일 찾기
video_extensions = ['.mp4', '.mov', '.avi', '.mkv', '.flv', '.wmv']
video_files = []
for ext in video_extensions:
video_files.extend(Path(input_dir).glob(f'*{ext}'))
video_files.extend(Path(input_dir).glob(f'*{ext.upper()}'))
if not video_files:
print(f"No video files found in {input_dir}")
return
print(f"Found {len(video_files)} videos")
print(f"Resolution: {resolution}, CRF: {crf}, Preset: {preset}")
print(f"Workers: {workers}\n")
# 병렬 처리
with ThreadPoolExecutor(max_workers=workers) as executor:
futures = {
executor.submit(convert_video, str(f), output_dir, resolution, crf, preset): f
for f in video_files
}
for future in as_completed(futures):
result = future.result()
print(result)
print(f"\n✅ All conversions complete!")
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Batch video converter')
parser.add_argument('input_dir', help='Input directory')
parser.add_argument('output_dir', help='Output directory')
parser.add_argument('--resolution', default='1280x720', help='Output resolution (default: 1280x720)')
parser.add_argument('--crf', type=int, default=23, help='CRF value (default: 23)')
parser.add_argument('--preset', default='medium', help='Encoding preset (default: medium)')
parser.add_argument('--workers', type=int, default=4, help='Number of parallel workers (default: 4)')
args = parser.parse_args()
batch_convert(
args.input_dir,
args.output_dir,
resolution=args.resolution,
crf=args.crf,
preset=args.preset,
workers=args.workers
)
사용:
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 기본
python batch_convert.py ./videos ./output
# 720p, CRF 20, slow 프리셋
python batch_convert.py ./videos ./output --resolution 1280x720 --crf 20 --preset slow
# 8개 병렬 처리
python batch_convert.py ./videos ./output --workers 8
12. 고급 필터
복잡한 필터 체인
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 여러 필터 적용
ffmpeg -i input.mp4 \
-vf "scale=1280:720,eq=brightness=0.1:contrast=1.2,unsharp=5:5:1.0" \
output.mp4
# 필터 그래프
ffmpeg -i input.mp4 -i logo.png \
-filter_complex "\
[0:v]scale=1280:720[scaled];\
[scaled][1:v]overlay=W-w-10:10[watermarked];\
[watermarked]drawtext=text='Hello':fontsize=48:x=10:y=10\
" \
output.mp4
화면 분할
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 2개 동영상 나란히 (Side by Side)
ffmpeg -i left.mp4 -i right.mp4 \
-filter_complex "\
[0:v]scale=960:1080[left];\
[1:v]scale=960:1080[right];\
[left][right]hstack\
" \
output.mp4
# 4개 동영상 (2×2 그리드)
ffmpeg -i top_left.mp4 -i top_right.mp4 -i bottom_left.mp4 -i bottom_right.mp4 \
-filter_complex "\
[0:v]scale=960:540[tl];\
[1:v]scale=960:540[tr];\
[2:v]scale=960:540[bl];\
[3:v]scale=960:540[br];\
[tl][tr]hstack[top];\
[bl][br]hstack[bottom];\
[top][bottom]vstack\
" \
output.mp4
슬로우 모션 / 타임랩스
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 슬로우 모션 (0.5배 속도)
ffmpeg -i input.mp4 -filter:v "setpts=2.0*PTS" output.mp4
# 타임랩스 (2배 속도)
ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" output.mp4
# 오디오 포함 (속도 동기화)
ffmpeg -i input.mp4 \
-filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]" \
-map "[v]" -map "[a]" \
output.mp4
트랜지션 효과
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 페이드 인/아웃
ffmpeg -i input.mp4 \
-vf "fade=t=in:st=0:d=3,fade=t=out:st=57:d=3" \
output.mp4
# 크로스페이드 (2개 동영상 전환)
ffmpeg -i video1.mp4 -i video2.mp4 \
-filter_complex "\
[0:v]fade=t=out:st=7:d=1[v0];\
[1:v]fade=t=in:st=0:d=1[v1];\
[v0][v1]concat=n=2:v=1:a=0\
" \
output.mp4
13. 성능 최적화
하드웨어 가속
NVIDIA GPU (NVENC):
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# H.264 GPU 인코딩
ffmpeg -hwaccel cuda -i input.mp4 \
-c:v h264_nvenc -preset fast -b:v 5M \
output.mp4
# H.265 GPU 인코딩
ffmpeg -hwaccel cuda -i input.mp4 \
-c:v hevc_nvenc -preset fast -b:v 5M \
output.mp4
# 품질 설정
ffmpeg -hwaccel cuda -i input.mp4 \
-c:v h264_nvenc -preset slow -rc vbr -cq 23 \
output.mp4
Intel Quick Sync (QSV):
다음은 간단한 bash 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# H.264 QSV 인코딩
ffmpeg -hwaccel qsv -c:v h264_qsv -i input.mp4 \
-c:v h264_qsv -preset medium -global_quality 23 \
output.mp4
Apple VideoToolbox (macOS):
다음은 간단한 bash 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# H.264 VideoToolbox
ffmpeg -i input.mp4 \
-c:v h264_videotoolbox -b:v 5M \
output.mp4
멀티스레딩
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 스레드 수 지정
ffmpeg -i input.mp4 -threads 8 -c:v libx264 -crf 23 output.mp4
# 자동 (CPU 코어 수)
ffmpeg -i input.mp4 -threads 0 -c:v libx264 -crf 23 output.mp4
# 타일 인코딩 (AV1)
ffmpeg -i input.mp4 -c:v libaom-av1 -tiles 4x4 -threads 16 output.mp4
메모리 최적화
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 버퍼 크기 조절
ffmpeg -i input.mp4 \
-bufsize 10M -maxrate 5M \
-c:v libx264 -crf 23 \
output.mp4
# 스트리밍 최적화 (faststart)
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -movflags +faststart output.mp4
# moov atom을 파일 앞으로 이동 (웹 스트리밍 필수)
14. 실전 명령어 모음
유튜브 최적화
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 유튜브 권장 설정
ffmpeg -i input.mp4 \
-c:v libx264 -preset slow -crf 18 \
-c:a aac -b:a 192k -ar 48000 \
-pix_fmt yuv420p \
-movflags +faststart \
youtube.mp4
# 4K 유튜브
ffmpeg -i input.mp4 \
-c:v libx264 -preset slow -crf 18 \
-vf scale=3840:2160 \
-c:a aac -b:a 192k \
-pix_fmt yuv420p \
-movflags +faststart \
youtube_4k.mp4
Instagram 최적화
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# Instagram 피드 (1:1)
ffmpeg -i input.mp4 \
-vf "scale=1080:1080:force_original_aspect_ratio=decrease,pad=1080:1080:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k \
-t 60 \
instagram_feed.mp4
# Instagram 스토리 (9:16)
ffmpeg -i input.mp4 \
-vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k \
-t 15 \
instagram_story.mp4
웹 최적화
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 웹용 MP4 (빠른 시작)
ffmpeg -i input.mp4 \
-c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k \
-movflags +faststart \
web.mp4
# WebM (VP9)
ffmpeg -i input.mp4 \
-c:v libvpx-vp9 -b:v 2M \
-c:a libopus -b:a 128k \
web.webm
# 다중 포맷 (호환성)
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -movflags +faststart web.mp4
ffmpeg -i input.mp4 -c:v libvpx-vp9 -b:v 2M web.webm
15. 라이브 스트리밍
RTMP 서버 설정
Nginx-RTMP:
다음은 nginx를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# nginx.conf
rtmp {
server {
listen 1935;
application live {
live on;
record off;
# HLS 생성
hls on;
hls_path /tmp/hls;
hls_fragment 3;
hls_playlist_length 60;
}
}
}
http {
server {
listen 8080;
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /tmp;
add_header Cache-Control no-cache;
add_header Access-Control-Allow-Origin *;
}
}
}
스트리밍 시작:
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 파일 스트리밍
ffmpeg -re -i video.mp4 \
-c:v libx264 -preset veryfast -b:v 3M -maxrate 3M -bufsize 6M \
-pix_fmt yuv420p -g 60 \
-c:a aac -b:a 128k -ar 44100 \
-f flv rtmp://localhost/live/stream
# 웹캠 스트리밍
ffmpeg -f avfoundation -i "0:0" \
-c:v libx264 -preset veryfast -b:v 2M \
-c:a aac -b:a 128k \
-f flv rtmp://localhost/live/webcam
# 화면 녹화 + 스트리밍 (macOS)
ffmpeg -f avfoundation -capture_cursor 1 -i "1:0" \
-c:v libx264 -preset veryfast -b:v 3M \
-c:a aac -b:a 128k \
-f flv rtmp://localhost/live/screen
재스트리밍 (Restreaming)
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# RTMP → HLS
ffmpeg -i rtmp://source/live/stream \
-c:v copy -c:a copy \
-hls_time 3 -hls_list_size 10 \
-hls_flags delete_segments \
output.m3u8
# 다중 플랫폼 동시 스트리밍
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset veryfast -b:v 3M -c:a aac -b:a 128k \
-f flv rtmp://a.rtmp.youtube.com/live2/YOUTUBE_KEY \
-c:v libx264 -preset veryfast -b:v 3M -c:a aac -b:a 128k \
-f flv rtmp://live.twitch.tv/app/TWITCH_KEY
16. 트러블슈팅
일반적인 문제
1) “Unknown encoder ‘libx264’”
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 원인: FFmpeg이 libx264 없이 컴파일됨
# 해결: 전체 버전 재설치
# macOS
brew reinstall ffmpeg
# Ubuntu (full 버전)
sudo apt install ffmpeg
2) “Conversion failed”
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 상세 로그 확인
ffmpeg -i input.mp4 -loglevel debug output.mp4
# 코덱 지원 확인
ffmpeg -codecs | grep h264
ffmpeg -encoders | grep h264
3) “Invalid data found when processing input”
# 원인: 손상된 파일
# 해결: 에러 무시 옵션
ffmpeg -err_detect ignore_err -i input.mp4 -c copy output.mp4
4) 오디오/비디오 동기화 문제
아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
# 오디오 지연 (0.5초)
ffmpeg -i input.mp4 -itsoffset 0.5 -i input.mp4 -map 0:v -map 1:a -c copy output.mp4
# 오디오 앞당기기
ffmpeg -i input.mp4 -itsoffset -0.5 -i input.mp4 -map 0:v -map 1:a -c copy output.mp4
성능 문제
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 1. 하드웨어 가속 사용
ffmpeg -hwaccel cuda -i input.mp4 -c:v h264_nvenc output.mp4
# 2. 빠른 프리셋
ffmpeg -i input.mp4 -c:v libx264 -preset ultrafast output.mp4
# 3. 재인코딩 없이 복사
ffmpeg -i input.mp4 -c copy output.mp4
# 4. 낮은 해상도
ffmpeg -i input.mp4 -vf scale=640:360 output.mp4
17. 실전 스크립트
자동 인코딩 워커
Python (Celery):
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
from celery import Celery
import ffmpeg
import os
app = Celery('video_worker', broker='redis://localhost:6379/0')
@app.task
def encode_video(input_path, output_path, resolution='1280x720', crf=23):
"""비디오 인코딩 작업"""
try:
width, height = resolution.split('x')
(
ffmpeg
.input(input_path)
.filter('scale', width, height)
.output(
output_path,
vcodec='libx264',
preset='slow',
crf=crf,
acodec='aac',
audio_bitrate='192k',
movflags='+faststart'
)
.overwrite_output()
.run(capture_stdout=True, capture_stderr=True)
)
return {'status': 'success', 'output': output_path}
except ffmpeg.Error as e:
return {'status': 'error', 'message': e.stderr.decode()}
@app.task
def create_thumbnails(input_path, output_dir, count=3):
"""썸네일 생성 작업"""
try:
probe = ffmpeg.probe(input_path)
duration = float(probe['format']['duration'])
os.makedirs(output_dir, exist_ok=True)
thumbnails = []
for i in range(count):
timestamp = duration * (i + 1) / (count + 1)
output_path = os.path.join(output_dir, f'thumb_{i}.jpg')
(
ffmpeg
.input(input_path, ss=timestamp)
.output(output_path, vframes=1, q=2)
.overwrite_output()
.run(capture_stdout=True, capture_stderr=True)
)
thumbnails.append(output_path)
return {'status': 'success', 'thumbnails': thumbnails}
except ffmpeg.Error as e:
return {'status': 'error', 'message': e.stderr.decode()}
# 사용
if __name__ == '__main__':
# 작업 큐에 추가
encode_video.delay('input.mp4', 'output.mp4', resolution='1920x1080', crf=20)
create_thumbnails.delay('input.mp4', './thumbnails', count=5)
진행률 웹소켓
Node.js + Socket.io:
다음은 javascript를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const ffmpeg = require('fluent-ffmpeg');
const multer = require('multer');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
const upload = multer({ dest: 'uploads/' });
app.post('/convert', upload.single('video'), (req, res) => {
const inputPath = req.file.path;
const outputPath = `processed/${req.file.filename}.mp4`;
const socketId = req.body.socketId;
ffmpeg(inputPath)
.output(outputPath)
.videoCodec('libx264')
.audioCodec('aac')
.on('start', (cmd) => {
console.log('Started:', cmd);
})
.on('progress', (progress) => {
// 클라이언트에게 진행률 전송
io.to(socketId).emit('progress', {
percent: progress.percent,
currentTime: progress.timemark,
fps: progress.currentFps
});
})
.on('end', () => {
io.to(socketId).emit('complete', {
url: `/processed/${req.file.filename}.mp4`
});
})
.on('error', (err) => {
io.to(socketId).emit('error', {
message: err.message
});
})
.run();
res.json({ message: 'Processing started' });
});
io.on('connection', (socket) => {
console.log('Client connected:', socket.id);
socket.on('disconnect', () => {
console.log('Client disconnected:', socket.id);
});
});
server.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
클라이언트 (HTML):
다음은 html를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
<!DOCTYPE html>
<html>
<head>
<title>Video Converter</title>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<h1>Video Converter</h1>
<input type="file" id="videoFile" accept="video/*">
<button onclick="uploadVideo()">Upload & Convert</button>
<div id="progress" style="display: none;">
<p>Progress: <span id="percent">0</span>%</p>
<progress id="progressBar" value="0" max="100"></progress>
</div>
<div id="result" style="display: none;">
<p>✅ Conversion complete!</p>
<video id="outputVideo" controls width="640"></video>
</div>
<script>
const socket = io();
function uploadVideo() {
const fileInput = document.getElementById('videoFile');
const file = fileInput.files[0];
if (!file) {
alert('Please select a video file');
return;
}
const formData = new FormData();
formData.append('video', file);
formData.append('socketId', socket.id);
document.getElementById('progress').style.display = 'block';
fetch('/convert', {
method: 'POST',
body: formData
});
}
socket.on('progress', (data) => {
document.getElementById('percent').textContent = data.percent.toFixed(1);
document.getElementById('progressBar').value = data.percent;
});
socket.on('complete', (data) => {
document.getElementById('progress').style.display = 'none';
document.getElementById('result').style.display = 'block';
document.getElementById('outputVideo').src = data.url;
});
socket.on('error', (data) => {
alert('Error: ' + data.message);
});
</script>
</body>
</html>
18. 고급 활용
자막 처리
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 자막 추가 (하드서브)
ffmpeg -i video.mp4 -vf subtitles=subtitle.srt output.mp4
# 자막 추가 (소프트서브)
ffmpeg -i video.mp4 -i subtitle.srt -c copy -c:s mov_text output.mp4
# 자막 추출
ffmpeg -i video.mp4 -map 0:s:0 subtitle.srt
# 자막 스타일 변경
ffmpeg -i video.mp4 \
-vf "subtitles=subtitle.srt:force_style='FontName=Arial,FontSize=24,PrimaryColour=&H00FFFF'" \
output.mp4
메타데이터 편집
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 메타데이터 확인
ffprobe -show_format input.mp4
# 메타데이터 추가
ffmpeg -i input.mp4 -c copy \
-metadata title="My Video" \
-metadata author="Alice" \
-metadata comment="Created with FFmpeg" \
output.mp4
# 메타데이터 제거
ffmpeg -i input.mp4 -c copy -map_metadata -1 output.mp4
화면 녹화
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# macOS (화면 + 오디오)
ffmpeg -f avfoundation -capture_cursor 1 -i "1:0" \
-c:v libx264 -preset ultrafast -crf 23 \
-c:a aac -b:a 128k \
screen_recording.mp4
# Linux (X11)
ffmpeg -f x11grab -s 1920x1080 -i :0.0 \
-c:v libx264 -preset ultrafast -crf 23 \
screen_recording.mp4
# Windows (GDI)
ffmpeg -f gdigrab -i desktop \
-c:v libx264 -preset ultrafast -crf 23 \
screen_recording.mp4
19. 프로덕션 팁
에러 처리
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import ffmpeg
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def safe_convert(input_path, output_path, max_retries=3):
"""재시도 로직 포함 변환"""
for attempt in range(max_retries):
try:
logger.info(f"Attempt {attempt + 1}/{max_retries}")
(
ffmpeg
.input(input_path)
.output(output_path, vcodec='libx264', crf=23)
.global_args('-loglevel', 'error')
.overwrite_output()
.run(capture_stdout=True, capture_stderr=True)
)
logger.info("✅ Success!")
return True
except ffmpeg.Error as e:
logger.error(f"❌ Attempt {attempt + 1} failed: {e.stderr.decode()}")
if attempt == max_retries - 1:
logger.error("All attempts failed")
raise
return False
큐 시스템
다음은 python를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
import ffmpeg
import redis
import json
import time
from threading import Thread
class VideoQueue:
def __init__(self):
self.redis = redis.Redis(host='localhost', port=6379, db=0)
self.queue_key = 'video:queue'
self.processing_key = 'video:processing'
def add_job(self, input_path, output_path, options=None):
"""작업 추가"""
job = {
'input': input_path,
'output': output_path,
'options': options or {},
'status': 'queued',
'created_at': time.time()
}
job_id = f"job:{time.time()}"
self.redis.lpush(self.queue_key, json.dumps(job))
return job_id
def process_job(self, job_data):
"""작업 처리"""
job = json.loads(job_data)
try:
# FFmpeg 실행
stream = ffmpeg.input(job['input'])
# 옵션 적용
if 'scale' in job['options']:
stream = stream.filter('scale', *job['options']['scale'].split('x'))
stream.output(
job['output'],
vcodec='libx264',
crf=job['options'].get('crf', 23),
preset=job['options'].get('preset', 'medium')
).overwrite_output().run()
print(f"✅ Job complete: {job['output']}")
except ffmpeg.Error as e:
print(f"❌ Job failed: {e.stderr.decode()}")
def worker(self):
"""워커 스레드"""
print("Worker started")
while True:
# 큐에서 작업 가져오기
job_data = self.redis.brpop(self.queue_key, timeout=5)
if job_data:
_, job_json = job_data
self.redis.lpush(self.processing_key, job_json)
self.process_job(job_json)
self.redis.lrem(self.processing_key, 1, job_json)
def start_workers(self, num_workers=4):
"""워커 시작"""
threads = []
for i in range(num_workers):
t = Thread(target=self.worker, daemon=True)
t.start()
threads.append(t)
return threads
# 사용
queue = VideoQueue()
# 작업 추가
queue.add_job('input1.mp4', 'output1.mp4', {'scale': '1280x720', 'crf': 20})
queue.add_job('input2.mp4', 'output2.mp4', {'scale': '1920x1080', 'crf': 18})
# 워커 시작 (4개)
workers = queue.start_workers(num_workers=4)
# 메인 스레드 유지
for worker in workers:
worker.join()
20. 성능 벤치마크
인코딩 속도 비교
다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 테스트 스크립트
#!/bin/bash
INPUT="test_4k.mp4"
echo "=== FFmpeg Encoding Benchmark ==="
echo
# H.264 프리셋 비교
for preset in ultrafast superfast veryfast faster fast medium slow slower veryslow; do
echo "Testing preset: $preset"
start=$(date +%s)
ffmpeg -i "$INPUT" -c:v libx264 -preset "$preset" -crf 23 -an -f null - 2>&1 | grep "frame="
end=$(date +%s)
duration=$((end - start))
echo "Time: ${duration}s"
echo
done
# 코덱 비교
for codec in libx264 libx265 libvpx-vp9; do
echo "Testing codec: $codec"
start=$(date +%s)
ffmpeg -i "$INPUT" -c:v "$codec" -b:v 5M -an -f null - 2>&1 | grep "frame="
end=$(date +%s)
duration=$((end - start))
echo "Time: ${duration}s"
echo
done
결과 예시 (4K 1분 영상):
아래 코드는 text를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
프리셋 비교 (H.264):
ultrafast: 15초 (파일: 50MB)
veryfast: 25초 (파일: 40MB)
medium: 45초 (파일: 30MB)
slow: 90초 (파일: 25MB)
veryslow: 180초 (파일: 23MB)
코덱 비교 (medium 프리셋):
H.264: 45초 (파일: 30MB)
H.265: 120초 (파일: 15MB)
VP9: 180초 (파일: 18MB)
21. 유용한 원라이너
다음은 bash를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 모든 MP4를 720p로 일괄 변환
for f in *.mp4; do ffmpeg -i "$f" -vf scale=1280:720 -c:v libx264 -crf 23 "720p_$f"; done
# 모든 동영상에서 오디오 추출
for f in *.mp4; do ffmpeg -i "$f" -vn -c:a libmp3lame -b:a 192k "${f%.mp4}.mp3"; done
# 모든 동영상의 첫 프레임 추출
for f in *.mp4; do ffmpeg -i "$f" -vframes 1 "${f%.mp4}.jpg"; done
# 모든 이미지를 동영상으로 (슬라이드쇼)
ffmpeg -framerate 1 -pattern_type glob -i '*.jpg' -c:v libx264 -pix_fmt yuv420p slideshow.mp4
# 오디오 파일들을 하나로 병합
ffmpeg -i "concat:audio1.mp3|audio2.mp3|audio3.mp3" -c copy output.mp3
# 동영상 정보 CSV로 저장
for f in *.mp4; do echo "$f,$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$f")"; done > videos.csv
22. Docker로 FFmpeg 사용
Dockerfile
아래 코드는 dockerfile를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
FROM ubuntu:22.04
# FFmpeg 설치
RUN apt-get update && \
apt-get install -y ffmpeg && \
rm -rf /var/lib/apt/lists/*
WORKDIR /data
ENTRYPOINT [ffmpeg]
빌드 및 사용:
아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# 빌드
docker build -t my-ffmpeg .
# 사용
docker run --rm -v $(pwd):/data my-ffmpeg \
-i /data/input.mp4 \
-c:v libx264 -crf 23 \
/data/output.mp4
# 별칭 생성
alias dffmpeg='docker run --rm -v $(pwd):/data my-ffmpeg'
# 사용
dffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4
트러블슈팅
실무에서 자주 마주치는 문제들과 해결 방법입니다.
문제 1: “height not divisible by 2” 에러
증상:
[libx264 @ 0x...] height not divisible by 2 (1281x721)
원인: H.264 코덱은 짝수 픽셀을 요구합니다.
해결:
# ❌ 잘못된 예
ffmpeg -i input.mp4 -vf scale=1281:721 output.mp4
# ✅ 올바른 예
ffmpeg -i input.mp4 -vf scale=1280:-2 output.mp4
# -2는 2의 배수로 자동 조정
문제 2: 오디오/비디오 싱크 안 맞음
증상: 영상과 음성이 어긋남
원인: 프레임레이트 변환, 잘못된 타임스탬프
해결:
# 방법 1: 오디오 타임스탬프 재계산
ffmpeg -i input.mp4 -c:v copy -c:a copy -async 1 output.mp4
# 방법 2: 오디오/비디오 재동기화
ffmpeg -i input.mp4 -c:v libx264 -c:a aac -vsync 1 -async 1 output.mp4
# 방법 3: 오디오 지연 수정 (0.5초 지연)
ffmpeg -i input.mp4 -itsoffset 0.5 -i input.mp4 -map 0:v -map 1:a -c copy output.mp4
문제 3: “moov atom not found” 에러
증상: 웹 브라우저에서 재생 안 됨, 다운로드 완료 후에만 재생
원인: MP4의 메타데이터(moov atom)가 파일 끝에 위치
해결:
# 인코딩 시 faststart 옵션 추가
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -movflags +faststart output.mp4
# 이미 인코딩된 파일 수정 (빠름)
ffmpeg -i input.mp4 -c copy -movflags +faststart output.mp4
문제 4: 인코딩이 너무 느림
해결 방법:
# 1. 프리셋을 빠르게
ffmpeg -i input.mp4 -c:v libx264 -preset veryfast -crf 23 output.mp4
# 2. 해상도 낮추기
ffmpeg -i input.mp4 -vf scale=1280:-2 -c:v libx264 -crf 23 output.mp4
# 3. GPU 인코더 사용 (NVIDIA)
ffmpeg -i input.mp4 -c:v h264_nvenc -preset fast -crf 23 output.mp4
# 4. GPU 인코더 사용 (Intel QSV)
ffmpeg -i input.mp4 -c:v h264_qsv -preset fast -global_quality 23 output.mp4
# 5. GPU 인코더 사용 (AMD)
ffmpeg -i input.mp4 -c:v h264_amf -quality balanced -rc cqp -qp 23 output.mp4
# 6. 병렬 처리 (여러 파일)
parallel -j 4 ffmpeg -i {} -c:v libx264 -crf 23 {.}_output.mp4 ::: *.mp4
문제 5: 파일 크기가 너무 큼
해결:
# 1. CRF 값 높이기 (품질 낮추기)
ffmpeg -i input.mp4 -c:v libx264 -crf 28 output.mp4
# 2. 해상도 낮추기
ffmpeg -i input.mp4 -vf scale=1280:-2 -c:v libx264 -crf 23 output.mp4
# 3. 프레임레이트 낮추기
ffmpeg -i input.mp4 -r 24 -c:v libx264 -crf 23 output.mp4
# 4. H.265 사용 (더 작은 파일)
ffmpeg -i input.mp4 -c:v libx265 -crf 28 output.mp4
# 5. 2-Pass 인코딩으로 목표 크기 맞추기
# 목표: 100MB, 10분 영상 = 100MB / 600초 = 1.33Mbps
ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 1 -f null /dev/null
ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 2 -c:a aac -b:a 128k output.mp4
문제 6: “Conversion failed” 또는 “Invalid data found”
원인: 손상된 파일, 지원하지 않는 코덱
해결:
# 1. 파일 정보 확인
ffprobe input.mp4
# 2. 에러 무시하고 강제 변환
ffmpeg -err_detect ignore_err -i input.mp4 -c:v libx264 -crf 23 output.mp4
# 3. 손상된 부분 건너뛰기
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -max_muxing_queue_size 9999 output.mp4
# 4. 재인코딩 없이 복사 시도
ffmpeg -i input.mp4 -c copy output.mp4
문제 7: 메모리 부족 에러
해결:
# 1. 버퍼 크기 조정
ffmpeg -i input.mp4 -max_muxing_queue_size 9999 -c:v libx264 output.mp4
# 2. 파일을 나눠서 처리
ffmpeg -i input.mp4 -ss 0 -t 600 -c:v libx264 part1.mp4
ffmpeg -i input.mp4 -ss 600 -t 600 -c:v libx264 part2.mp4
# 나중에 병합
# 3. 스레드 수 제한
ffmpeg -i input.mp4 -threads 2 -c:v libx264 output.mp4
문제 8: 색상이 이상함 (회색빛, 채도 낮음)
원인: 색공간 변환 문제
해결:
# YUV 색공간 명시
ffmpeg -i input.mp4 -vf scale=1280:-2 -pix_fmt yuv420p -c:v libx264 output.mp4
# 색공간 정보 보존
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -color_primaries bt709 \
-color_trc bt709 -colorspace bt709 output.mp4
문제 9: 자막이 안 나옴
해결:
# 1. 자막 스트림 확인
ffprobe -show_streams input.mkv
# 2. 자막 포함하여 변환
ffmpeg -i input.mkv -c:v libx264 -c:a aac -c:s mov_text output.mp4
# 3. 자막 하드코딩 (영상에 구워넣기)
ffmpeg -i video.mp4 -vf subtitles=subtitle.srt output.mp4
# 4. 외부 자막 파일 추가
ffmpeg -i video.mp4 -i subtitle.srt -c:v copy -c:a copy -c:s mov_text output.mp4
문제 10: FFmpeg 설치 확인
# 버전 확인
ffmpeg -version
# 지원 코덱 확인
ffmpeg -codecs | grep h264
ffmpeg -codecs | grep hevc
# 지원 포맷 확인
ffmpeg -formats | grep mp4
# 하드웨어 인코더 확인
ffmpeg -encoders | grep nvenc # NVIDIA
ffmpeg -encoders | grep qsv # Intel
ffmpeg -encoders | grep amf # AMD
FAQ
Q1. FFmpeg와 HandBrake는 어떻게 나누면 되나?
A:
- FFmpeg: 명령줄 기반, 자동화·스크립트·서버에 적합, 세밀한 제어 가능
- HandBrake: GUI 기반, 직관적, 빠른 설정, 개인 사용에 편리
선택 가이드:
- 한두 개 파일 변환: HandBrake
- 배치 처리, 자동화: FFmpeg
- 서버/CI/CD 파이프라인: FFmpeg
- 복잡한 필터, 스트리밍: FFmpeg
- 처음 시작하는 초보자: HandBrake
Q2. 코덱은 무엇을 쓰면 되나?
A: 상황에 따라 다릅니다:
| 상황 | 추천 코덱 | 이유 |
|---|---|---|
| 최대 호환성 | H.264 | 모든 기기 지원 |
| 파일 크기 최소화 | H.265 | 40-50% 작음 |
| 웹 스트리밍 | H.264 또는 VP9 | 브라우저 지원 |
| YouTube 업로드 | H.264 | 플랫폼 권장 |
| 4K 영상 | H.265 | 효율적 압축 |
| 미래 대비 | AV1 | 차세대 표준 |
실전 팁:
- 일반 용도: H.264 (libx264)
- 저장 공간 부족: H.265 (libx265)
- 웹 전용: VP9 (libvpx-vp9)
Q3. CRF와 비트레이트 중 무엇을 쓰나?
A: 대부분의 경우 CRF를 권장합니다.
CRF 사용 (권장):
- ✅ 일정한 품질 유지
- ✅ 간단한 사용법
- ✅ 대부분의 용도에 적합
- ❌ 파일 크기 예측 어려움
ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4
비트레이트 사용:
- ✅ 정확한 파일 크기
- ✅ 스트리밍 대역폭 제어
- ❌ 복잡한 장면에서 품질 저하
- ❌ 2-Pass 필요 시 시간 2배
ffmpeg -i input.mp4 -c:v libx264 -b:v 5M output.mp4
선택 가이드:
- 일반 파일 변환: CRF 23
- YouTube 업로드: CRF 18-20
- 웹 스트리밍: 비트레이트 2-5M
- 방송 송출: 비트레이트 (2-Pass)
- 모바일: CRF 26-28
Q4. 인코딩이 너무 느리다
A: 여러 최적화 방법이 있습니다:
1. 프리셋 조정 (가장 쉬움)
# 느림 (현재)
ffmpeg -i input.mp4 -preset slow -crf 23 output.mp4
# 빠름 (권장)
ffmpeg -i input.mp4 -preset veryfast -crf 23 output.mp4
# 속도: 3-5배 향상, 파일 크기: 10-20% 증가
2. GPU 인코더 사용 (가장 효과적)
# NVIDIA GPU
ffmpeg -i input.mp4 -c:v h264_nvenc -preset fast -crf 23 output.mp4
# 속도: 5-10배 향상
# Intel GPU
ffmpeg -i input.mp4 -c:v h264_qsv -preset fast output.mp4
# AMD GPU
ffmpeg -i input.mp4 -c:v h264_amf -quality balanced output.mp4
3. 해상도 낮추기
ffmpeg -i input_4k.mp4 -vf scale=1920:-2 -crf 23 output_1080p.mp4
# 4K → 1080p: 4배 빠름
4. 병렬 처리 (여러 파일)
# GNU Parallel 사용
parallel -j 4 ffmpeg -i {} -crf 23 {.}_output.mp4 ::: *.mp4
# 4개 파일 동시 처리
5. 프레임레이트 낮추기
ffmpeg -i input.mp4 -r 24 -crf 23 output.mp4
# 60fps → 24fps: 2.5배 빠름
속도 vs 품질 비교:
| 방법 | 속도 향상 | 품질/크기 영향 |
|---|---|---|
| preset veryfast | 3-5배 | 크기 +10-20% |
| GPU 인코더 | 5-10배 | 크기 +20-30% |
| 해상도 감소 | 4배 (4K→1080p) | 해상도 감소 |
| 프레임레이트 감소 | 2배 (60→30fps) | 부드러움 감소 |
Q5. 화질 손실을 최소화하려면?
A: 상황별 최적 전략:
1. 재인코딩 없이 변환 (무손실)
# 컨테이너만 변경
ffmpeg -i input.mkv -c copy output.mp4
# 장점: 초고속, 무손실
# 단점: 해상도/품질 조정 불가
2. 고품질 재인코딩 (거의 무손실)
# CRF 17-18 사용
ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 17 output.mp4
# 시각적으로 무손실, 파일 크기 큼
3. 편집용 중간 코덱
# ProRes (macOS)
ffmpeg -i input.mp4 -c:v prores_ks -profile:v 3 output.mov
# DNxHD (크로스 플랫폼)
ffmpeg -i input.mp4 -c:v dnxhd -b:v 185M output.mov
# 무손실 H.264
ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 0 output.mp4
4. 다단계 인코딩 최소화
# ❌ 나쁜 예: 3번 인코딩
원본 → 편집 → 효과 → 최종
(품질 손실 누적)
# ✅ 좋은 예: 1번 인코딩
원본 → [편집+효과 동시] → 최종
5. 적절한 코덱 선택
# 보관용: 무손실 또는 고품질
ffmpeg -i input.mp4 -c:v libx264 -crf 17 -preset slow archive.mp4
# 배포용: 균형잡힌 품질
ffmpeg -i input.mp4 -c:v libx264 -crf 20 -preset slow final.mp4
# 웹용: 적정 품질
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium web.mp4
품질 우선순위:
- 재인코딩 피하기 (
-c copy) - 낮은 CRF 값 (17-20)
- 느린 프리셋 (slow, slower)
- 적절한 해상도 유지
- 2-Pass 인코딩 (필요시)
Q6. 웹 스트리밍용 최적 설정은?
A:
# 웹 최적화 (progressive download)
ffmpeg -i input.mp4 \
-c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k \
-movflags +faststart \
-vf scale=1920:-2 \
web_optimized.mp4
# 핵심 옵션:
# -movflags +faststart: 메타데이터를 앞으로 (즉시 재생)
# -crf 23: 웹용 적정 품질
# -b:a 128k: 오디오 대역폭 절약
Q7. 여러 파일을 하나로 합치려면?
A:
# 방법 1: concat 프로토콜 (같은 코덱)
ffmpeg -i "concat:file1.mp4|file2.mp4|file3.mp4" -c copy output.mp4
# 방법 2: concat demuxer (권장)
# 1. 파일 목록 생성
echo "file 'file1.mp4'" > list.txt
echo "file 'file2.mp4'" >> list.txt
echo "file 'file3.mp4'" >> list.txt
# 2. 병합
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
# 방법 3: filter_complex (다른 코덱/해상도)
ffmpeg -i file1.mp4 -i file2.mp4 -i file3.mp4 \
-filter_complex "[0:v][0:a][1:v][1:a][2:v][2:a]concat=n=3:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" output.mp4
Q8. 특정 구간만 자르려면?
A:
# 빠른 방법 (재인코딩 없음, 키프레임에서만 정확)
ffmpeg -i input.mp4 -ss 00:01:30 -to 00:02:45 -c copy output.mp4
# 정확한 방법 (재인코딩, 느림)
ffmpeg -i input.mp4 -ss 00:01:30 -to 00:02:45 -c:v libx264 -crf 23 output.mp4
# 시작 시간과 길이로 지정
ffmpeg -i input.mp4 -ss 00:01:30 -t 00:01:15 -c copy output.mp4
# -ss: 시작 시간
# -t: 길이
# -to: 종료 시간
요약
핵심 정리
FFmpeg란?
- 가장 강력한 오픈소스 멀티미디어 프레임워크
- 명령줄 도구 (
ffmpeg,ffprobe,ffplay) - 프로그래밍 라이브러리 (libav 계열)
- 거의 모든 비디오/오디오 포맷 지원
- YouTube, Netflix 등 산업 표준
핵심 명령어 치트시트:
# 기본 변환 (자동 코덱)
ffmpeg -i input.mp4 output.avi
# 고품질 H.264 인코딩 (권장)
ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 20 -c:a aac -b:a 192k output.mp4
# 빠른 변환 (재인코딩 없음)
ffmpeg -i input.mkv -c copy output.mp4
# 해상도 변경
ffmpeg -i input.mp4 -vf scale=1280:-2 output.mp4
# 특정 구간 자르기
ffmpeg -i input.mp4 -ss 00:01:30 -to 00:02:45 -c copy output.mp4
# 썸네일 추출
ffmpeg -i input.mp4 -ss 00:00:05 -vframes 1 -q:v 2 thumbnail.jpg
# 오디오 추출
ffmpeg -i input.mp4 -vn -c:a libmp3lame -b:a 192k audio.mp3
# HLS 스트리밍 생성
ffmpeg -i input.mp4 -hls_time 10 -hls_playlist_type vod playlist.m3u8
# 파일 정보 확인
ffprobe -show_format -show_streams input.mp4
# 여러 파일 병합
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
상황별 최적 설정:
# YouTube 업로드 (고품질)
ffmpeg -i input.mp4 \
-c:v libx264 -preset slow -crf 18 \
-c:a aac -b:a 192k \
-pix_fmt yuv420p \
youtube.mp4
# 웹 스트리밍 (즉시 재생)
ffmpeg -i input.mp4 \
-c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k \
-movflags +faststart \
-vf scale=1920:-2 \
web.mp4
# 모바일 최적화 (작은 파일)
ffmpeg -i input.mp4 \
-c:v libx264 -preset fast -crf 26 \
-c:a aac -b:a 96k \
-vf scale=1280:-2 \
mobile.mp4
# 빠른 변환 (테스트용)
ffmpeg -i input.mp4 \
-c:v libx264 -preset veryfast -crf 23 \
-c:a aac -b:a 128k \
quick.mp4
# 최소 파일 크기 (보관용)
ffmpeg -i input.mp4 \
-c:v libx265 -preset slow -crf 28 \
-c:a aac -b:a 128k \
compressed.mp4
# 최고 품질 (아카이브)
ffmpeg -i input.mp4 \
-c:v libx264 -preset veryslow -crf 17 \
-c:a aac -b:a 320k \
archive.mp4
CRF 값 선택 가이드:
| 용도 | CRF | 설명 |
|---|---|---|
| 편집 소스 | 17-18 | 시각적 무손실 |
| YouTube | 18-20 | 플랫폼 재인코딩 대비 |
| 일반 공유 | 21-23 | 균형잡힌 품질 |
| 웹 스트리밍 | 23-26 | 빠른 로딩 |
| 모바일 | 26-28 | 데이터 절약 |
프리셋 선택 가이드:
| 상황 | 프리셋 | 이유 |
|---|---|---|
| 테스트/프리뷰 | veryfast | 빠른 확인 |
| 일반 작업 | medium | 기본 균형 |
| 최종 배포 | slow | 최적 품질 |
| 아카이브 | veryslow | 최소 크기 |
| 실시간 스트리밍 | ultrafast | CPU 부하 최소 |
학습 로드맵
1주차: 기초 다지기
- FFmpeg 설치 및 버전 확인
- 기본 명령어 구조 이해
- 간단한 포맷 변환 (
-c copy) - CRF와 프리셋 개념 파악
- 해상도 변경 실습
2주차: 실전 활용
- H.264 인코딩 마스터
- 오디오 처리 (추출, 변환, 편집)
- 썸네일 및 GIF 생성
- 영상 자르기 및 병합
- 필터 적용 (워터마크, 자막)
3주차: 고급 기능
- HLS 스트리밍 구축
- 2-Pass 인코딩
- GPU 가속 활용
- Python/Node.js 연동
- 배치 처리 스크립트
4주차: 최적화 및 트러블슈팅
- 성능 최적화 기법
- 일반적인 에러 해결
- 프로덕션 파이프라인 구축
- 모니터링 및 로깅
- 실전 프로젝트 적용
실무 적용 팁:
-
작게 시작하세요
- 짧은 테스트 영상으로 실험
- 다양한 설정 비교
- 결과를 눈으로 확인
-
자주 쓰는 명령어는 스크립트로
# convert_to_web.sh #!/bin/bash ffmpeg -i "$1" -c:v libx264 -preset medium -crf 23 \ -c:a aac -b:a 128k -movflags +faststart "web_$1" -
GPU 가속 활용
- NVIDIA: h264_nvenc
- Intel: h264_qsv
- AMD: h264_amf
-
병렬 처리로 시간 절약
parallel -j 4 ffmpeg -i {} -crf 23 {.}_output.mp4 ::: *.mp4 -
품질 vs 속도 균형
- 개발: veryfast + crf 23
- 배포: slow + crf 20
- 테스트: ultrafast + crf 28
다음 단계
관련 심화 주제:
- H.264 코덱 완벽 가이드 - 코덱 내부 동작 원리
- AAC 오디오 코덱 가이드 - 오디오 인코딩 최적화
- MKV 컨테이너 포맷 가이드 - 컨테이너 구조 이해
실전 프로젝트:
- 동영상 스트리밍 서버 구축
- 자동 인코딩 파이프라인 개발
- 영상 편집 자동화 시스템
- 라이브 방송 플랫폼 구축
추가 학습 자료:
마치며
FFmpeg는 처음에는 복잡해 보이지만, 기본 패턴만 익히면 매우 강력한 도구입니다. 이 가이드의 예제들을 직접 실행해보면서 익숙해지세요.
기억해야 할 핵심:
- 대부분의 경우
-c:v libx264 -crf 23이면 충분합니다 - 재인코딩이 필요 없으면
-c copy를 사용하세요 - 웹용은
-movflags +faststart를 잊지 마세요 - 테스트는 짧은 영상으로, 최종본은 느린 프리셋으로
- 문제가 생기면
ffprobe로 먼저 확인하세요
질문이나 피드백은 댓글로 남겨주세요!
키워드: FFmpeg, Video, Audio, 동영상, 오디오, 인코딩, 스트리밍, HLS, RTMP, libav, Multimedia, 변환, H.264, H.265, VP9, AV1, AAC, MP3, 썸네일, GIF, 필터, 워터마크