회사에서 영상을 처리하는 로직을 만들면서 FFmpeg를 자주 사용하고 있다. FFmpeg는 사용해본 지 꽤 되었지만, 이번에 처음으로 블로그에 관련 글을 작성해보려고 한다. 최근에 영상에 SRT 파일(자막)을 입혀 인코딩 후 다운로드하는 로직을 작성했는데, 일부 영상에서 자막이 깨지거나 나오지 않는 문제가 발생했다. 이에 대해 FFmpeg가 무엇인지 간단히 정리하고, 최근에 겪었던 문제에 대해 설명해보려고한다
1. FFmpeg란?
1) FFmpeg
멀티미디어 데이터를 다루기 위해 주로 사용되는 오픈 소스 소프트웨어. 비디오, 오디오, 및 기타 멀티미디어 파일과 스트림을 녹화, 변환, 스트리밍할 수 있는 다양한 기능을 제공. FFmpeg는 리눅스(Linux), 윈도우(Windows), 맥OS(macOS)를 포함한 대부분의 주요 운영 체제에서 사용할 수 있으며, 다양한 CPU 아키텍처(x86, x86-64, ARM, PowerPC 등)를 지원. 주로 명령줄 인터페이스를 통해 조작.
2) 라이브러리 및 도구
- 라이브러리: FFmpeg는 여러 개의 라이브러리로 구성
- libavcodec: 다양한 코덱을 포함한 코덱 라이브러리
- libavformat: 컨테이너 포맷을 처리하는 라이브러리
- ibavfilter: 오디오/비디오 필터를 적용하는 라이브러리
- libavdevice: 캡처 및 재생 장치를 처리하는 라이브러리
- libswscale: 이미지 크기 조정 및 색상 공간 변환 라이브러리
- libswresample: 오디오 샘플링 라이브러리
- 도구 : FFmpeg는 여러 명령줄 도구를 제공
- ffmpeg: 멀티미디어 파일을 변환하는 핵심 도구. 다양한 형식 간의 변환, 파일 병합, 필터 적용 등을 수행
- ffplay: 간단한 미디어 플레이어로, FFmpeg 라이브러리를 사용하여 미디어 파일을 재생
- ffprobe: 미디어 파일의 정보(메타데이터)를 분석하고 출력하는 도구. 파일 형식, 코덱, 비트레이트, 해상도 등의 정보를 확인할 수 있음
3) 주요 기능
- 변환 (Transcoding):
- 비디오와 오디오 파일을 다양한 형식으로 변환 가능. 예를 들어, MP4 파일을 AVI 파일로 변환하거나, WAV 파일을 MP3 파일로 변환.
- 필터링 (Filtering):
- 다양한 필터를 사용하여 비디오와 오디오에 효과를 적용할 수 있음. 비디오 필터로는 크기 조정, 자르기, 회전, 자막 추가 등이 있으며, 오디오 필터로는 볼륨 조정, 노이즈 제거 등이 있음
- 스트리밍 (Streaming):
- 네트워크를 통해 실시간 스트리밍을 지원. 라이브 방송을 인코딩하여 스트리밍 서버로 전송하거나, 스트리밍된 데이터를 수신하여 재생할 수 있음
- 병합 및 분할 (Merging and Splitting):
- 여러 미디어 파일을 하나로 병합하거나, 하나의 파일을 여러 부분으로 분할할 수 있음
4) FFmpeg의 장점
- 광범위한 형식 지원: 거의 모든 비디오, 오디오 형식을 지원
- 강력한 기능: 고급 사용자와 개발자를 위한 다양한 기능과 옵션을 제공
- 오픈 소스: 누구나 무료로 사용할 수 있으며, 소스 코드를 수정하여 커스터마이징할 수 있음
- 활발한 커뮤니티: 전 세계의 많은 개발자들이 참여하고 있어, 다양한 자료와 도움을 받을 수 있음
5) FFmpeg를 사용한 서비스
- YouTube: Google의 대형 비디오 플랫폼인 YouTube는 비디오 업로드와 스트리밍을 위해 FFmpeg를 활용. 사용자가 업로드한 다양한 형식의 비디오 파일을 YouTube의 표준 형식으로 변환하는 데 사용
- Netflix: 넷플릭스는 다양한 장치와 네트워크 환경에 맞추어 비디오 콘텐츠를 변환하고 최적화하는 데 FFmpeg를 사용. 이를 통해 높은 품질의 스트리밍 경험을 제공
- Facebook: 페이스북은 사용자들이 업로드하는 비디오 콘텐츠를 처리하고 변환하는 데 FFmpeg를 사용. 이를 통해 다양한 형식의 비디오를 플랫폼에 맞게 변환하고 최적화
2. Troubleshooting
문제의 메소드
public void cutAddSubtitlesAndEncode(File videoFile, Highlight highlight, File outputFile, File subtitleFile) {
try {
// 시작 시간과 끝 시간을 초 단위로 계산
int startTime = (int) highlight.getStartTime();
int endTime = (int) highlight.getEndTime();
int duration = endTime - startTime;
FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe);
// 자막 스타일을 지정.
String subtitleFilter = String.format("subtitles=%s:force_style='Alignment=2,Fontsize=24,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BackColour=&H00000000,BorderStyle=3'",
subtitleFile.getAbsolutePath());
FFmpegBuilder builder = new FFmpegBuilder()
.setInput(videoFile.getAbsolutePath()) // 입력 파일 설정
.addExtraArgs("-ss", String.valueOf(startTime)) // 시작 시간 설정
.addExtraArgs("-t", String.valueOf(duration)) // 지속 시간 설정
.addOutput(outputFile.getAbsolutePath()) // 출력 파일명 설정
.setFormat("mp4") // 출력 파일 포맷 설정
.setVideoCodec("libx264") // 비디오 코덱 설정 (자막 추가 시 재인코딩 필요)
.setAudioCodec("aac") // 오디오 코덱 설정 (재인코딩 필요)
.addExtraArgs("-vf", subtitleFilter) // 자막 필터 적용
.done();
FFmpegJob job = executor.createJob(builder);
job.run();
} catch (Exception e) {
throw new ApiException(ErrorCode.VIDEO_ENCODING_ERROR);
}
}
1. 일부 영상에서 자막이 깨짐
문제: 영상에서 글자가 흰색박스로 나오는 경우 발생
원인: 폰트가 지정되어 있지 않았음
해결: 서버에 폰트를 다운받고 ffmpeg 자막 포맷에 폰트 지정
- Dockerfile에서 컨테이너 올릴 때 폰트를 다운 받음(나눔고딕)
// 변경 전
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates fuse wget tzdata vim ffmpeg
// 변경 후
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates fuse wget tzdata vim ffmpeg && \
apt-get install -y fonts-nanum
- 다운받은 폰트로 ffmpeg 자막 포맷에 폰트 지정
// 변경 전
String subtitleFilter = String.format("subtitles=%s:force_style='Alignment=2,Fontsize=24,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BackColour=&H00000000,BorderStyle=3'",
subtitleFile.getAbsolutePath());
// 변경 후
String subtitleFilter = String.format("subtitles=%s:force_style='Alignment=2,Fontname=NanumGothic,Fontsize=24,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BackColour=&H00000000,BorderStyle=3'",
subtitleFile.getAbsolutePath());
- 자막 적용 완료!
2. 일부 영상에서 자막이 안나옴
문제: 영상에서 자막 적용이 안된상태로 인코딩 됨. 자막이 없음
원인: FFmpegBuilder를 통한 인코딩 시 순서
해결: 메소드 면경, 옵선 순서 변경
- 변경 전 코드에서는 addExtraArgs를 사용하여 -vf 옵션을 추가 했지만 필터가 적용되지 않을 가능성 있음. FFmpegBuilder를 사용할 때는 라이브러리가 제공하는 특정 메소드(setVideoFilter)를 사용하는 것이 더 명확함
- 필터를 적용한 후에 비디오 코덱을 설정. 필터가 적용된 비디오 스트림을 코덱을 통해 인코딩하기 때문에 필터를 먼저 적용해야 함.
// 변경 전
FFmpegBuilder builder = new FFmpegBuilder()
.setInput(videoFile.getAbsolutePath()) // 입력 파일 설정
.addExtraArgs("-ss", String.valueOf(startTime)) // 시작 시간 설정
.addExtraArgs("-t", String.valueOf(duration)) // 지속 시간 설정
.addOutput(outputFile.getAbsolutePath()) // 출력 파일명 설정
.setFormat("mp4") // 출력 파일 포맷 설정
.setVideoCodec("libx264") // 비디오 코덱 설정 (자막 추가 시 재인코딩 필요)
.setAudioCodec("aac") // 오디오 코덱 설정 (재인코딩 필요)
.addExtraArgs("-vf", subtitleFilter) // 자막 필터 적용
.done();
// 변경 후
FFmpegBuilder builder = new FFmpegBuilder()
.setInput(videoFile.getAbsolutePath()) // 입력 파일 설정
.addExtraArgs("-ss", String.valueOf(startTime)) // 시작 시간 설정
.addExtraArgs("-t", String.valueOf(duration)) // 지속 시간 설정
.addOutput(outputFile.getAbsolutePath()) // 출력 파일명 설정
.setFormat("mp4") // 출력 파일 포맷 설정
.setVideoFilter(subtitleFilter) // 비디오 필터 설정: 자막 파일 적용
.setVideoCodec("libx264") // 비디오 코덱 설정 (자막 추가 시 재인코딩 필요)
.setAudioCodec("aac") // 오디오 코덱 설정 (재인코딩 필요)
.done();
- 자막 적용 완료!
1번, 2번 둘다 문제가 무엇인지 몰라서 서버에서 ffmpeg를 돌려보아도 오류메세지가 나오지 않았다. 구글링에 의존했는데 1번 자막이 깨졌던 문제는 구글링을 통해 금방 문제를 해결할 수 있었지만 두번 째 문제는 개선할 수 있는 방법이 잘 나오지 않았다. 고민하다가 전에도 builder를 사용할 때 순서관련한 이슈가 있었어서 혹시나 하고 순서관련해서 gpt에게 물어봐서 해결했다. 단순 영상 자르기는 많이 해봤지만 이번처럼 srt파일을 입혀 자막을 넣어 인코딩한 것은 처음이라 필터처리에 익숙하지 않았던것 같다. 이번 경험을 통해서 FFmpeg는 강력하지만, 그만큼 사용자가 깊이 이해하고 다룰 수 있어야 한다는것을 느꼈다!
'개발 하나둘셋 > Java & Spring' 카테고리의 다른 글
서버-클라이언트 연결로 실시간 상태 전달하는 SSE 특징 및 적용기 (0) | 2024.11.17 |
---|---|
Java와 Spring에서의 비동기 처리 @Async와 CompletableFuture (2) | 2024.11.08 |
Spring Boot 3.x 주요 변경 사항과 마이그레이션 방법 (0) | 2024.02.04 |
Redis 서버 재시작 시 데이터 초기화 문제와 해결 방법: RDB와 AOF (1) | 2024.01.21 |
Redis를 활용한 효율적인 조회수 관리 방법 (2) | 2024.01.04 |