비디오 제작을 위한 FFmpeg
FFmpeg는 비디오/오디오 처리에 필수적인 도구입니다. 이 스킬은 Remotion 비디오 프로젝트를 위한 일반적인 작업들을 다룹니다.
빠른 참조
GIF → MP4 (Remotion 호환)
ffmpeg -i input.gif -movflags faststart -pix_fmt yuv420p \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" output.mp4
이 플래그들을 사용하는 이유:
-movflags faststart- 웹 스트리밍을 위해 메타데이터를 앞쪽으로 이동-pix_fmt yuv420p- 대부분의 플레이어와의 호환성 보장scale=trunc(...)- 짝수 크기 강제 (대부분의 코덱에서 요구)
비디오 크기 조정
# 1920x1080으로 (종횡비 유지, 검은색 막대 추가)
ffmpeg -i input.mp4 -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" output.mp4
# 1920x1080으로 (늘려서 채움)
ffmpeg -i input.mp4 -vf "scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080" output.mp4
# 너비 기준, 높이 자동
ffmpeg -i input.mp4 -vf "scale=1280:-2" output.mp4
비디오 압축
# 좋은 품질, 작은 파일 (CRF 23이 기본, 낮을수록 좋은 품질)
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4
# 웹 미리보기용 강력 압축
ffmpeg -i input.mp4 -c:v libx264 -crf 28 -preset fast -c:a aac -b:a 96k output.mp4
# 목표 파일 크기 (예: 60초 비디오 ~10MB = ~1.3Mbps)
ffmpeg -i input.mp4 -c:v libx264 -b:v 1300k -c:a aac -b:a 128k output.mp4
오디오 추출
# MP3로 추출
ffmpeg -i input.mp4 -vn -acodec libmp3lame -q:a 2 output.mp3
# AAC로 추출
ffmpeg -i input.mp4 -vn -acodec aac -b:a 192k output.m4a
# WAV로 추출 (무압축)
ffmpeg -i input.mp4 -vn output.wav
오디오 포맷 변환
# M4A → MP3 (ElevenLabs 음성 샘플용)
ffmpeg -i input.m4a -codec:a libmp3lame -qscale:a 2 output.mp3
# WAV → MP3
ffmpeg -i input.wav -codec:a libmp3lame -b:a 192k output.mp3
# 볼륨 조절
ffmpeg -i input.mp3 -filter:a "volume=1.5" output.mp3
비디오 자르기/트리밍
# 특정 시점부터 일정 시간만큼 (권장 - 안정적)
ffmpeg -i input.mp4 -ss 00:00:30 -t 00:00:15 -c:v libx264 -c:a aac output.mp4
# 특정 시점부터 특정 시점까지
ffmpeg -i input.mp4 -ss 00:00:30 -to 00:00:45 -c:v libx264 -c:a aac output.mp4
# 스트림 복사 (빠르지만 자른 지점에서 프레임 손실 가능)
# 소스에 키프레임이 자주 있을 때만 사용
ffmpeg -i input.mp4 -ss 00:00:30 -t 00:00:15 -c copy output.mp4
참고: 자르기에는 재인코딩을 권장합니다. 스트림 복사(-c copy)는 탐색 지점이 키프레임과 일치하지 않으면 비디오를 조용히 드롭할 수 있습니다.
속도 변경 (빠르게/느리게)
# 2배속 (영상 및 음성)
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
# 0.5배속 (슬로우 모션)
ffmpeg -i input.mp4 -filter_complex "[0:v]setpts=2.0*PTS[v];[0:a]atempo=0.5[a]" -map "[v]" -map "[a]" output.mp4
# 비디오만 (오디오 없음)
ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" -an output.mp4
비디오 연결
# 파일 목록 생성
echo "file 'clip1.mp4'" > list.txt
echo "file 'clip2.mp4'" >> list.txt
echo "file 'clip3.mp4'" >> list.txt
# 연결 (동일 코덱/해상도)
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4
# 재인코딩으로 연결 (다른 소스)
ffmpeg -f concat -safe 0 -i list.txt -c:v libx264 -c:a aac output.mp4
페이드 인/아웃 추가
# 처음 1초 페이드 인, 마지막 1초 페이드 아웃 (30fps 비디오)
ffmpeg -i input.mp4 -vf "fade=t=in:st=0:d=1,fade=t=out:st=9:d=1" -c:a copy output.mp4
# 오디오 페이드
ffmpeg -i input.mp4 -af "afade=t=in:st=0:d=1,afade=t=out:st=9:d=1" -c:v copy output.mp4
비디오 정보 얻기
# 길이, 해상도, 코덱 정보
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4
# 전체 정보
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4
Remotion 관련 패턴
Remotion용 비디오 속도 조정
FFmpeg vs Remotion playbackRate 사용 시기:
| 시나리오 | FFmpeg 사용 | Remotion 사용 |
|---|---|---|
| 일정한 속도 (1.5배, 2배) | 둘 다 가능 | ✅ 더 간단 |
| 극단적인 속도 (4배 이상 또는 0.25배 미만) | ✅ 더 안정적 | 문제 발생 가능 |
| 가변 속도 (시간에 따라 가속) | ✅ 전처리 필수 | 복잡한 우회 방법 필요 |
| 완벽한 오디오 동기화 필요 | ✅ 보장됨 | 일반적으로 괜찮음 |
| 음성 해설 타이밍 맞춤 데모 | ✅ 사전 계산 | 런타임 조정 |
Remotion 제한 사항: playbackRate는 상수여야 합니다. playbackRate={interpolate(frame, [0, 100], [1, 5])}와 같은 동적 보간은 Remotion이 프레임을 독립적으로 평가하기 때문에 올바르게 작동하지 않습니다.
# 한 장면에 맞게 데모 속도 조정 (예: 60초 데모를 20초에 맞추기 = 3배속)
ffmpeg -i demo-raw.mp4 \
-filter_complex "[0:v]setpts=0.333*PTS[v];[0:a]atempo=3.0[a]" \
-map "[v]" -map "[a]" \
public/demos/demo-fast.mp4
# 강조를 위한 슬로우 모션 (0.5배속)
ffmpeg -i action.mp4 \
-filter_complex "[0:v]setpts=2.0*PTS[v];[0:a]atempo=0.5[a]" \
-map "[v]" -map "[a]" \
public/demos/action-slow.mp4
# 오디오 없이 속도만 변경 (화면 녹화에 흔함)
ffmpeg -i demo.mp4 -filter:v "setpts=0.5*PTS" -an public/demos/demo-2x.mp4
# 타임랩스 효과 (10배속, 오디오 제거)
ffmpeg -i long-demo.mp4 -filter:v "setpts=0.1*PTS" -an public/demos/timelapse.mp4
속도 계수 계산:
- X초 비디오를 Y초 장면에 맞추기:
속도 = X / Y - setpts 승수 =
1 / 속도(예: 3배속 = setpts=0.333*PTS) - atempo 값 =
속도(예: 3배속 = atempo=3.0)
극단적인 속도 (2배속 이상 오디오): atempo 필터 체인 (각각 0.5~2.0 범위로 제한):
# 4배속 오디오
-filter_complex "[0:a]atempo=2.0,atempo=2.0[a]"
# 8배속 오디오
-filter_complex "[0:a]atempo=2.0,atempo=2.0,atempo=2.0[a]"
Remotion용 데모 녹화 준비
# 표준 1080p, 30fps, Remotion 준비 완료
ffmpeg -i raw-recording.mp4 \
-vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,fps=30" \
-c:v libx264 -crf 18 -preset slow \
-c:a aac -b:a 192k \
-movflags faststart \
public/demos/demo.mp4
화면 녹화 → Remotion 에셋
# iPhone/iPad 녹화 (보통 60fps, 가변 해상도)
ffmpeg -i iphone-recording.mov \
-vf "scale=1920:-2,fps=30" \
-c:v libx264 -crf 20 \
-an \
public/demos/mobile-demo.mp4
GIF 일괄 변환
for f in assets/*.gif; do
ffmpeg -i "$f" -movflags faststart -pix_fmt yuv420p \
-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
"public/demos/$(basename "$f" .gif).mp4"
done
일반적인 문제
"높이가 2로 나누어지지 않음"
스케일 필터 추가: -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2"
브라우저에서 재생되지 않음
사용: -movflags faststart -pix_fmt yuv420p -c:v libx264
속도 변경 후 오디오 동기화 깨짐
atempo와 함께 filter_complex 사용: -filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]"
파일이 너무 큼
CRF 증가 (23→28) 또는 해상도 축소
품질 가이드라인
| 사용 사례 | CRF | 프리셋 | 비고 |
|---|---|---|---|
| 아카이브/마스터 | 18 | slow | 최고 품질, 큰 파일 |
| 프로덕션 | 20–22 | medium | 좋은 균형 |
| 웹/미리보기 | 23–25 | fast | 작은 파일 |
| 초안/빠른 작업 | 28+ | veryfast | 빠른 인코딩 |
플랫폼별 출력 최적화
Remotion이 비디오를 렌더링한 후(일반적으로 out/video.mp4), FFmpeg를 사용하여 각 배포 플랫폼에 맞게 최적화하세요.
워크플로우 통합
Remotion 렌더링 (마스터) FFmpeg 최적화 플랫폼 업로드
↓ ↓ ↓
out/video.mp4 ────────→ out/video-youtube.mp4 ───→ YouTube
────────→ out/video-twitter.mp4 ───→ Twitter/X
────────→ out/video-linkedin.mp4 ───→ LinkedIn
────────→ out/video-web.mp4 ───→ 웹사이트 임베드
YouTube (권장 설정)
YouTube는 모든 것을 재인코딩하므로 고품질로 업로드:
# YouTube 최적화 (1080p)
ffmpeg -i out/video.mp4 \
-c:v libx264 -preset slow -crf 18 \
-profile:v high -level 4.0 \
-bf 2 -g 30 \
-c:a aac -b:a 192k -ar 48000 \
-movflags +faststart \
out/video-youtube.mp4
# YouTube Shorts (세로 1080x1920)
ffmpeg -i out/video.mp4 \
-vf "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2" \
-c:v libx264 -crf 18 -c:a aac -b:a 192k \
out/video-shorts.mp4
Twitter/X
Twitter는 엄격한 제한: 최대 140초, 512MB, 1920x1200:
# Twitter 최적화 (빠른 업로드 목표 15MB 미만)
ffmpeg -i out/video.mp4 \
-c:v libx264 -preset medium -crf 24 \
-profile:v main -level 3.1 \
-vf "scale='min(1280,iw)':'min(720,ih)':force_original_aspect_ratio=decrease" \
-c:a aac -b:a 128k -ar 44100 \
-movflags +faststart \
-fs 15M \
out/video-twitter.mp4
# 파일 크기 및 길이 확인
ffprobe -v error -show_entries format=duration,size -of csv=p=0 out/video-twitter.mp4
LinkedIn은 AAC 오디오가 포함된 MP4를 선호하며, 최대 10분:
# LinkedIn 최적화
ffmpeg -i out/video.mp4 \
-c:v libx264 -preset medium -crf 22 \
-profile:v main \
-vf "scale='min(1920,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease" \
-c:a aac -b:a 192k -ar 48000 \
-movflags +faststart \
out/video-linkedin.mp4
웹사이트/임베드 (빠른 로딩에 최적화)
# 웹 최적화 MP4 (작은 파일, 프로그레시브 로딩)
ffmpeg -i out/video.mp4 \
-c:v libx264 -preset medium -crf 26 \
-profile:v baseline -level 3.0 \
-vf "scale=1280:720" \
-c:a aac -b:a 128k \
-movflags +faststart \
out/video-web.mp4
# WebM 대안 (더 나은 압축, 더 넓은 브라우저 지원)
ffmpeg -i out/video.mp4 \
-c:v libvpx-vp9 -crf 30 -b:v 0 \
-vf "scale=1280:720" \
-c:a libopus -b:a 128k \
-deadline good \
out/video-web.webm
GIF (미리보기/썸네일용)
# 고품질 GIF (처음 5초)
ffmpeg -i out/video.mp4 -t 5 \
-vf "fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
out/preview.gif
# 더 작은 파일 GIF
ffmpeg -i out/video.mp4 -t 3 \
-vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
out/preview-small.gif
플랫폼 요구 사항 빠른 참조
| 플랫폼 | 최대 해상도 | 최대 크기 | 최대 길이 | 오디오 |
|---|---|---|---|---|
| YouTube | 8K | 256GB | 12시간 | AAC 48kHz |
| Twitter/X | 1920x1200 | 512MB | 140초 | AAC 44.1kHz |
| 4096x2304 | 5GB | 10분 | AAC 48kHz | |
| Instagram 피드 | 1080x1350 | 4GB | 60초 | AAC 48kHz |
| Instagram Reels | 1080x1920 | 4GB | 90초 | AAC 48kHz |
| TikTok | 1080x1920 | 287MB | 10분 | AAC |
모든 플랫폼용 일괄 내보내기
#!/bin/bash
# 저장: export-all-platforms.sh
INPUT="out/video.mp4"
# YouTube (고품질)
ffmpeg -i "$INPUT" -c:v libx264 -preset slow -crf 18 \
-c:a aac -b:a 192k -movflags +faststart \
out/video-youtube.mp4
# Twitter (압축)
ffmpeg -i "$INPUT" -c:v libx264 -crf 24 \
-vf "scale='min(1280,iw)':'-2'" \
-c:a aac -b:a 128k -movflags +faststart \
out/video-twitter.mp4
# LinkedIn
ffmpeg -i "$INPUT" -c:v libx264 -crf 22 \
-c:a aac -b:a 192k -movflags +faststart \
out/video-linkedin.mp4
# 웹 임베드 (작게)
ffmpeg -i "$INPUT" -c:v libx264 -crf 26 \
-vf "scale=1280:720" \
-c:a aac -b:a 128k -movflags +faststart \
out/video-web.mp4
echo "내보냄:"
ls -lh out/video-*.mp4
오류 처리
비디오 처리 중 일반적인 오류 및 해결 방법:
# FFmpeg 성공 여부 확인
ffmpeg -i input.mp4 -c:v libx264 output.mp4 && echo "성공" || echo "실패: 입력 파일 확인"
# 출력 파일 재생 가능 확인
ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of csv=p=0 output.mp4
# 상세 오류 정보 얻기
ffmpeg -v error -i input.mp4 -f null - 2>&1 | head -20
일반적인 실패 처리
| 오류 | 원인 | 해결책 |
|---|---|---|
| "No such file" | 입력 경로 잘못됨 | 경로 확인, 공백이면 따옴표 사용 |
| "Invalid data" | 손상된 입력 | 소스 다시 다운로드 또는 다시 녹화 |
| "height not divisible by 2" | 홀수 크기 | trunc로 스케일 필터 추가 |
| "encoder not found" | 코덱 누락 | 전체 코덱이 포함된 FFmpeg 설치 |
| 출력 0 바이트 | 무음 실패 | 오류에 대한 전체 ffmpeg 출력 확인 |
피드백 및 기여
이 스킬에 누락된 정보가 있거나 개선할 점이 있다면:
- 명령어가 누락되었나요? 필요한 작업을 설명해 주세요.
- 오류를 발견하셨나요? 무엇이 잘못되었는지 알려 주세요.
- 기여하고 싶으신가요? 다음을 도와드릴 수 있습니다:
- 이 스킬을 개선 사항으로 업데이트
- github.com/digitalsamba/claude-code-video-toolkit에 PR 생성
"이 스킬 개선"이라고 말씀해 주시면 .claude/skills/ffmpeg/SKILL.md 업데이트를 안내해 드리겠습니다.