olrlobt

[Spring boot] FFmpeg로 영상 배속 설정하기 본문

Spring/Project

[Spring boot] FFmpeg로 영상 배속 설정하기

olrlobt 2023. 3. 31. 04:46

FFmpeg

FFmpeg는 비디오, 오디오를 처리하는 오픈소스 라이브러리이다. 대표적으로 지원하는 동작으로는 파일변환, 편집, 스트리밍, 인코딩과 디코딩 등이 있고, 비디오에서 썸네일을 자동으로 뽑아내는 곳에 자주 이용된다.

특이한 점으로는, 주로 커멘드 입력으로 동작한다.

 

또한, FFmpeg는 다양한 운영체제에서, 다양한 프로그래밍 언어로 사용할 수 있는 라이브러리로 제공되어, 수많은 개발자와 사용자 모두에게 유용한 기능을 많이 제공해 준다.

 

오늘은 FFmpeg를 이용하여 Spring boot 프로젝트에서,

클라이언트가 요청한 동영상 파일을, 2 배속하여 다시 사용자에게 보여줄 것이다.

 

공식 Github : 

https://github.com/FFmpeg/FFmpeg

 


FFmpeg 설치

먼저, 스프링 부트에서 FFmpeg를 사용하기 위해서는 서버에 설치하는 방법과 API를 이용하는 방법이 있다.

당연히 API를 이용하여 간단하게 해결하면 좋겠지만, FFmpeg는 C언어로 구현이 되어 있기 때문에 JAVA에서 사용하기 위해서는 JNI(Java Native Interface)를 이용하여 JAVA에서 C언어의 함수를 호출할 수 있도록 해야 한다.

 

FFmpeg 구성언어 비율

JNI를 이용하는 방법은 꽤나 복잡하고 어렵기 때문에, 나는 비교적 간단한 서버 설치 방법을 이용하기로 하였다.

 

 

 

다운로드

FFmpeg 공식 다운로드 사이트 :

https://ffmpeg.org/download.html

 

Download FFmpeg

If you find FFmpeg useful, you are welcome to contribute by donating. More downloading options Git Repositories Since FFmpeg is developed with Git, multiple repositories from developers and groups of developers are available. Release Verification All FFmpe

ffmpeg.org

 

 

위 링크로 접속하여 다운로드를 진행한다.

FFmpeg 다운로드 페이지

각자의 운영 체제에 맞게 선택하면 되고, 나는 Window를 사용 중이기 때문에 윈도 버전을 다운로드하였다.

빨갛게 하이라이트 친 부분을 눌러 페이지를 이동한다.

 

 

가장 마지막 빌드 버전에서, 본인이 원하는 버전을 눌러 다운로드한다.

full 버전의 경우, 다양한 기능들이 무수히 많이 들어 가 있지만 무거우며,

essentials 버전의 경우, 중요한 기능들 위주로 들어 가 있지만 가벼운 편이다.

 

본인이 구현하고자 하는 기능이 essentials로 구현이 가능하다면, essentials로 받는 것을 추천한다.

 

 

 

 

로컬 디스크(C 또는 D)에 압축을 풀어준다.

 

 

 

 

환경변수

다운로드를 완료했으면 프로젝트에서 사용하기 편하도록 환경변수를 등록해 주어야 한다.

환경변수를 등록하지 않더라도, 사용은 가능하지만 매 번 경로를 입력해 주어야 하므로 상당히 번거로울 것이다.

 

 

 

윈도에 "시스템 환경 변수 편집" 검색

 

 

 

"고급"에서 "환경 변수"를 눌러 "Path"를 찾는다.

 

 

"편집"을 눌러 FFmpeg가 설치된 경로를 bin까지 등록한다.

 

 

이렇게 하면 FFmpeg 설치가 완료되었다.

이제, 커멘드로 FFmpeg 사용이 가능하다.

 

 

Cmd를 열어 "ffmpeg"를 눌러 등록이 잘 되었는지 확인한다.

 

위와 같이 나온다면, 정상적으로 등록이 된 것이다.

 


동영상 배속하기

Spring boot에서 FFmpeg를 사용하기 위해서는 먼저, application.properties에 경로를 등록해 주어야 한다.

 

 

application.properties

## ffmpeg 설정

ffmpeg.location=C:/ffmpeg/bin/ffmpeg.exe
ffprobe.location=C:/ffmpeg/bin/ffprobe.exe

 

본인 경로에 맞게 등록을 해주었으면 Class를 작성한다.

 

 

FFmpeg 경로 가져오기

@Value("${ffmpeg.location}")
private String ffmpegPath;
@Value("${ffprobe.location}")
private String ffprobePath;

먼저, application.properties에 등록한 경로를 받아오기 위해 @Value 어노테이션을 사용한다.

 

 

 

파일 경로 처리

// MultipartFile file

String fileName = file.getOriginalFilename();
String filePath = "src/main/webapp/resources/upload/" + UUID.randomUUID().toString() + fileName;
String outPath = "src/main/webapp/resources/upload/" + UUID.randomUUID().toString() +fileName;

File temp = new File(filePath).getCanonicalFile(); //getCanonicalFile로 정적인 경로를 생성
if(!temp.exists()) {
    temp.createNewFile();
}
file.transferTo(temp); // 파일 변경

 

filePath는 클라이언트에서 올린 경로,

outPath는 2배속 영상이 저장될 경로이며,

클라이언트에서 올린 경로 filePath를 따로 설정해 주는 이유는 클라이언트에서 올린 영상이 blob으로 로딩처리가 되기 때문이다.

 

blob으로 로딩된 영상 경로

간단히 말해, Blob URL은 브라우저에서 영상을 로딩했기 때문에 생기는 경로로, 로컬에 파일을 저장하지 않고, 브라우저에 저장해서 사용하기 위해 생긴 경로라고 이해하면 된다.

 

이 말은 즉, 브라우저에서 생성된 경로이기 때문에, 서버에서 접근이 불가능한 경로라는 말이다.

 

따라서, filePath를 새롭게 생성해 주고 transferTo()를 이용하여 파일을 복사해 준 것이다.

 

 

영상 배속

FFprobe ffprobe = new FFprobe(ffprobePath); // window에 설치된 ffprobe.exe 경로

FFmpegBuilder builder = new FFmpegBuilder().setInput(filePath) // 입력 파일 경로
        .addOutput(outPath) // 출력 파일 경로
        .setFormat("mp4") // 출력 파일 포맷
        .disableSubtitle() // 자막 끄기
        .setVideoCodec("libx264") // 비디오 코덱 설정
        .setAudioCodec("aac") // 오디오 코덱 설정
        .setVideoFrameRate(30) // 비디오 프레임 레이트 설정
        .setVideoFilter("setpts=" + (1.0 / speed) + "*PTS") // 비디오 속도 조절
        .done();

// FFmpeg 실행
FFmpegExecutor executor = new FFmpegExecutor(new FFmpeg(ffmpegPath), new FFprobe(ffprobePath));
executor.createJob(builder).run();

FFmpeg는 커멘드 명령어로 실행이 되고,

FFmpegBuilder는 이 명령어를 생성하기 위한 빌더 클래스이다.

 

. addOutput() 경로에 출력 파일의 경로가 들어가며,

. setVideoFilter()에서 speed 값에 의해 비디오의 속도가 조절된다. 예를 들어 speed가 2라면, 2배속인 것이다.

 

명령어 생성이 끝났다면, 명령어를 실행하기 위하여 FFmpegExecutor를 생성해 주고, run()으로 실행해 준다.

 

 

결과

1배속 영상(좌) / 2배속 영상(우)

 

2배속 영상이 잘 나오는 것을 확인할 수 있다.

하지만, 배속만 해 주었고, 영상 길이는 그대로이기 때문에 영상이 중간부터 영상이 끝나지 않고 마지막 장면에서 계속 진행되는 것을 확인할 수 있었다.

 

 

영상 길이 줄이기

FFprobe ffprobe = new FFprobe(ffprobePath);

FFmpegProbeResult probeResult = ffprobe.probe(filePath);  // FFmpegProbeResult 객체 생성
double second = probeResult.getFormat().duration;  // 영상의 총 시간

FFmpegBuilder builder = new FFmpegBuilder().setInput(filePath) 
        .addOutput(outPath) 
        .setFormat("mp4")
        .disableSubtitle()
        .setVideoCodec("libx264") 
        .setAudioCodec("aac")
        .setVideoFrameRate(30) 
        .setVideoFilter("setpts=" + (1.0 / speed) + "*PTS") 
        .setDuration((long)(second*1000/speed), TimeUnit.MILLISECONDS) // 영상 길이 설정
        .done();

FFmpegExecutor executor = new FFmpegExecutor(new FFmpeg(ffmpegPath), ffprobe);
executor.createJob(builder).run();

FFmpeg에는 동영상의 재생시간을 출력하는 기능도 있다.

나는 그 기능을 사용하여 Duration을 새로 설정해 주는 방식으로 문제를 해결하였다.

 

 

전체코드

@Slf4j
@Component
public class VideoFileUtils {

	@Value("${ffmpeg.location}")
	private String ffmpegPath;
	@Value("${ffprobe.location}")
	private String ffprobePath;

	/**
	 * 비디오 파일을 배속해서 저장하는 함수
	 * @param file : 파일
	 * @param speed : 배속
	 * @return 
	 */
	public String changePlaybackRate(MultipartFile file, double speed) throws IOException, InterruptedException {
		String fileName = file.getOriginalFilename();
		
		String filePath = "src/main/webapp/resources/upload/" + UUID.randomUUID().toString() + fileName;
		String outPath = "src/main/webapp/resources/upload/" + UUID.randomUUID().toString() +fileName;
		
		File temp = new File(filePath).getCanonicalFile();
		if(!temp.exists()) {
			temp.createNewFile();
		}
		file.transferTo(temp);

		FFprobe ffprobe = new FFprobe(ffprobePath); // window에 설치된 ffprobe.exe 경로
		FFmpegProbeResult probeResult = ffprobe.probe(filePath); // 동영상 경로
		double second = probeResult.getFormat().duration;
		
		FFmpegBuilder builder = new FFmpegBuilder().setInput(filePath) // 입력 파일 경로
				.addOutput(outPath) // 출력 파일 경로
				.setFormat("mp4") // 출력 파일 포맷
				.disableSubtitle() // 자막 끄기
				.setVideoCodec("libx264") // 비디오 코덱 설정
				.setAudioCodec("aac") // 오디오 코덱 설정
				.setVideoFrameRate(30) // 비디오 프레임 레이트 설정
				.setVideoFilter("setpts=" + (1.0 / speed) + "*PTS") // 비디오 속도 조절
				.setDuration((long)(second*1000/speed), TimeUnit.MILLISECONDS)
				.done();

		// FFmpeg 실행
		FFmpegExecutor executor = new FFmpegExecutor(new FFmpeg(ffmpegPath), new FFprobe(ffprobePath));
		executor.createJob(builder).run();

		new File(filePath).delete();
		return outPath;
	}

}

 

 

 

결과

1배속 영상(좌) / 2배속 영상(우)

시간에 맞게 잘 종료되는 것을 확인할 수 있다.

 

 

Comments