FFmpeg 실전 가이드 | 동영상/오디오 처리

FFmpeg 실전 가이드 | 동영상/오디오 처리

🎯 이 글을 읽으면 (읽는 시간: 30분)

TL;DR: FFmpeg로 동영상/오디오를 자유자재로 다루는 방법을 배웁니다. 설치부터 변환, 인코딩, 스트리밍까지 실무에서 바로 쓸 수 있는 모든 것을 마스터합니다.

이 글을 읽으면:

  • ✅ FFmpeg 설치부터 기본 사용법까지 완벽 이해
  • ✅ 동영상 변환, 인코딩, 최적화 기법 실전 적용
  • ✅ HLS 스트리밍과 라이브 방송 구축 능력 습득
  • ✅ Python/Node.js에서 FFmpeg 프로그래밍 방식 활용

실무 활용:

  • 🔥 동영상 플랫폼 (YouTube, Netflix 스타일)
  • 🔥 라이브 스트리밍 서비스
  • 🔥 썸네일 자동 생성 시스템
  • 🔥 동영상 인코딩 파이프라인

난이도: 중급 | 실습 예제: 30개 | 프로덕션 레벨


이 글의 핵심

FFmpeg는 동영상과 오디오를 다루는 가장 강력한 오픈소스 도구입니다. 이 가이드는 설치부터 실전 활용까지, 실무에서 바로 써먹을 수 있는 패턴과 노하우를 담았습니다. 단순한 명령어 나열이 아닌, 왜 이렇게 쓰는지, 어떤 상황에서 쓰는지를 중심으로 정리했습니다.


목차

  1. FFmpeg이란?
  2. 설치 및 기본 사용법
  3. 동영상 변환 및 인코딩
  4. 오디오 처리
  5. 썸네일 및 이미지 추출
  6. 스트리밍 및 HLS
  7. 필터 및 효과
  8. FFmpeg 라이브러리 (libav)
  9. 실전 프로젝트
  10. 성능 최적화

사전 지식 (초보자를 위한 기초)

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      # 두 번째 오디오 스트림 선택

실전 팁:

  1. 옵션 순서가 중요합니다

    • 입력 옵션은 -i 앞에
    • 출력 옵션은 -i 뒤, 출력 파일 앞에
  2. -c:v와 -vcodec은 같습니다

    • -c:v libx264 = -vcodec libx264
    • -c:a aac = -acodec aac
  3. copy는 재인코딩을 건너뜁니다

    • 매우 빠르지만 품질 조정 불가
    • 컨테이너만 바꿀 때 유용
  4. 여러 입력 파일 사용 가능

    ffmpeg -i video.mp4 -i audio.mp3 -c copy output.mp4
  5. 진행 상황 확인

    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/Vimeo18-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, veryfastCPU 부하 최소화
빠른 프리뷰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 성능에 따라 다릅니다.

실전 팁:

  1. 개발/테스트 단계: veryfast 사용

    • 빠른 피드백으로 효율적인 작업
  2. 최종 배포: slow 사용

    • 시간을 들여도 최적 결과 확보
  3. 대량 처리: medium 사용

    • 시간과 품질의 균형
  4. 프리셋과 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 최적화 팁:

  1. 첫 번째 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. 로그 파일 정리:

    # 2-Pass는 ffmpeg2pass-0.log 파일을 생성합니다
    # 작업 후 삭제:
    rm ffmpeg2pass-0.log
  3. 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

주의사항:

  1. 업스케일링은 피하세요

    • 720p → 1080p로 키워도 화질은 개선되지 않습니다
    • 오히려 파일 크기만 커집니다
  2. 홀수 픽셀 문제

    • H.264 등은 짝수 픽셀을 요구합니다
    • -2 사용으로 자동 조정
  3. 비율 왜곡 주의

    • scale=1280:720는 강제로 늘리거나 줄입니다
    • 비율 유지는 -1 또는 -2 사용
  4. 성능 고려

    • 다운스케일은 빠름
    • 업스케일은 느리고 의미 없음

프레임레이트 변경

아래 코드는 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.26540-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 veryfast3-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

품질 우선순위:

  1. 재인코딩 피하기 (-c copy)
  2. 낮은 CRF 값 (17-20)
  3. 느린 프리셋 (slow, slower)
  4. 적절한 해상도 유지
  5. 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시각적 무손실
YouTube18-20플랫폼 재인코딩 대비
일반 공유21-23균형잡힌 품질
웹 스트리밍23-26빠른 로딩
모바일26-28데이터 절약

프리셋 선택 가이드:

상황프리셋이유
테스트/프리뷰veryfast빠른 확인
일반 작업medium기본 균형
최종 배포slow최적 품질
아카이브veryslow최소 크기
실시간 스트리밍ultrafastCPU 부하 최소

학습 로드맵

1주차: 기초 다지기

  • FFmpeg 설치 및 버전 확인
  • 기본 명령어 구조 이해
  • 간단한 포맷 변환 (-c copy)
  • CRF와 프리셋 개념 파악
  • 해상도 변경 실습

2주차: 실전 활용

  • H.264 인코딩 마스터
  • 오디오 처리 (추출, 변환, 편집)
  • 썸네일 및 GIF 생성
  • 영상 자르기 및 병합
  • 필터 적용 (워터마크, 자막)

3주차: 고급 기능

  • HLS 스트리밍 구축
  • 2-Pass 인코딩
  • GPU 가속 활용
  • Python/Node.js 연동
  • 배치 처리 스크립트

4주차: 최적화 및 트러블슈팅

  • 성능 최적화 기법
  • 일반적인 에러 해결
  • 프로덕션 파이프라인 구축
  • 모니터링 및 로깅
  • 실전 프로젝트 적용

실무 적용 팁:

  1. 작게 시작하세요

    • 짧은 테스트 영상으로 실험
    • 다양한 설정 비교
    • 결과를 눈으로 확인
  2. 자주 쓰는 명령어는 스크립트로

    # 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"
  3. GPU 가속 활용

    • NVIDIA: h264_nvenc
    • Intel: h264_qsv
    • AMD: h264_amf
  4. 병렬 처리로 시간 절약

    parallel -j 4 ffmpeg -i {} -crf 23 {.}_output.mp4 ::: *.mp4
  5. 품질 vs 속도 균형

    • 개발: veryfast + crf 23
    • 배포: slow + crf 20
    • 테스트: ultrafast + crf 28

다음 단계

관련 심화 주제:

실전 프로젝트:

  • 동영상 스트리밍 서버 구축
  • 자동 인코딩 파이프라인 개발
  • 영상 편집 자동화 시스템
  • 라이브 방송 플랫폼 구축

추가 학습 자료:

마치며

FFmpeg는 처음에는 복잡해 보이지만, 기본 패턴만 익히면 매우 강력한 도구입니다. 이 가이드의 예제들을 직접 실행해보면서 익숙해지세요.

기억해야 할 핵심:

  1. 대부분의 경우 -c:v libx264 -crf 23이면 충분합니다
  2. 재인코딩이 필요 없으면 -c copy를 사용하세요
  3. 웹용은 -movflags +faststart를 잊지 마세요
  4. 테스트는 짧은 영상으로, 최종본은 느린 프리셋으로
  5. 문제가 생기면 ffprobe로 먼저 확인하세요

질문이나 피드백은 댓글로 남겨주세요!


키워드: FFmpeg, Video, Audio, 동영상, 오디오, 인코딩, 스트리밍, HLS, RTMP, libav, Multimedia, 변환, H.264, H.265, VP9, AV1, AAC, MP3, 썸네일, GIF, 필터, 워터마크

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3