티스토리 뷰
[서론]
이번에는 두 개 영상의 시간 정보를 가지고 두 영상을 하나의 영상으로 합쳐주는 프로그램을 만들어보고 싶었다. 하나의 단일 프로그램이 아니라 서버에서 클라이언트로 부터 전달 받은 영상의 시간 정보를 가지고 새로운 영상을 생성하고 사용자는 그것을 클라이언트가 다운로드 받는 형태로 만들어보고자 했다. 영상을 직접 자르고 붙여서 하나의 파일로 만들어주는 것을 직접 구현하기 전에 사용할 수 있는 라이브러리 및 프로그램을 찾아 보았다. 그렇게 해서 찾은 프로그램이 FFMPEG이라는 영상 관련 프로그램이였는데 FFMPEG은 상당히 유명한 프로그램 이였다. FFMPEG에 대한 설명은 링크를 타고 가서 볼 수 있다. FFMPEG은 서버에 별도로 설치를 하고 터미널을 통해서 명령어를 받아서 동작을 하는데 서버에서 콘솔 명령어를 보낼 수 있어야 한다. 이것을 쉽게 해주는 라이브러리가 FFMPEG JAVA라는 라이브러리이다. 해당 라이브러리를 사용하면 프로그래밍을 통해 손쉽게 ffmpeg 명령어을 실행 할 수 있다. 이번 포스팅에서는 해당 라이브러리를 사용하는 법에 대해서 설명한다.
[FFMPEG 기본 명령]
- 영상 구간 자르는 명령어
ffmpeg -i '영상경로.mp4' -ss 5000 -t 10 '출력물경로'
영상의 오디오를 제거 하고 싶다면
ffmpeg -i '영상경로.mp4' -ss 5000 -t 10 -an '출력물경로'
과 같이 출력물경로 바로 앞에 -an 옵션을 적용하면 된다. - 여러개의 영상 합치는 명령어
mp4를 사용하는 경우는 concat명령어가 작동하지 않는다. 그래서 text파일에 여러 영상들의 경로를 적어 놓고 해당 text파일을 읽어서 영상을 합쳐야 한다.
여기서는 mp4를 기준으로 설명을 할 것이므로 text파일을 이용해서 영상을 합치는 명령어를 설명한다.
ffmpeg -f concat -safe 0 -i `text file input path` `out path`
-safe 옵션은 출력파일명에 대한 안정성을 보장 하기 위한 옵션이다. - 두개의 영상을 Side by Side로 위치 시키기
ffmpeg -i `input file path 1` -i `input file path 2` -preset ultrafast -filter_complex "[0:v]setpts=PTS-STARTPTS, pad=iw*2:ih[bg]; [1:v]setpts=PTS-STARTPTS[fg]; [bg][fg]overlay=w" `out put file path'
위 명령어를 보게 되면 두개의 입력 영상 경로를 받아서 0번째 2번째 파일로 지정한뒤 이것을 하나로 합치는 명령을 볼 수 있다. pad라는 것에 iw는 입력받은 영상의 크기를 말하며 여기서 두개의 영상을 하나로 합칠 것이기 때문에 iw*2라는 것을 준것을 알 수 있다.
package ffmpeg.timecut; import java.io.IOException; import net.bramp.ffmpeg.FFmpeg; import net.bramp.ffmpeg.FFmpegExecutor; import net.bramp.ffmpeg.FFprobe; import net.bramp.ffmpeg.builder.FFmpegBuilder; public class VideoTimeCut { /** * 입력 영상의 구간을 잘라서 새로운 영상을 만드는 FFMPEG 명령어를 자바로 * 구현 한 소스 * @param args */ private static final String inputPath = "/Users/birdhead/Desktop/videoTest/pitching.mp4"; private static final String outputPath = "/Users/birdhead/Desktop/videoTest/"; public static void main(String[] args) throws IOException { FFmpeg ffmpeg = new FFmpeg("/usr/local/bin/ffmpeg"); FFprobe ffprobe = new FFprobe("/usr/local/bin/ffprobe"); //다음번 영상 merge를 위해서 3개의 영상 생성 for(int i=0; i<3; i++) { FFmpegBuilder builder = new FFmpegBuilder() .overrideOutputFiles(true) .addInput(inputPath) //입력 영상 경로의 .addExtraArgs("-ss", String.valueOf(i)) //영상의 i초 위치 부 .addExtraArgs("-t", "3") //3초 동안 재생한 영상 .addOutput(outputPath + "pitching_out" + i + ".mp4") //outputpath 위치에 .addExtraArgs("-an") //영상의 소리를 제거하고 .done(); //저장 FFmpegExecutor excutor = new FFmpegExecutor(ffmpeg, ffprobe); excutor.createJob(builder).run(); } } }
다음으로는 곧바로 연이어 영상을 합치는 부분과 Side by Side하는 소스를 연속해서 보자.
* Video Merge
package ffmpeg.merge; import java.io.IOException; import net.bramp.ffmpeg.FFmpeg; import net.bramp.ffmpeg.FFmpegExecutor; import net.bramp.ffmpeg.FFprobe; import net.bramp.ffmpeg.builder.FFmpegBuilder; public class VideoMerge { private static final String inputPath = "/Users/birdhead/Desktop/videoTest/mergeInfo.txt"; private static final String outputPath = "/Users/birdhead/Desktop/videoTest/"; public static void main(String[] args) throws IOException { FFmpeg ffmpeg = new FFmpeg("/usr/local/bin/ffmpeg"); FFprobe ffprobe = new FFprobe("/usr/local/bin/ffprobe"); FFmpegBuilder builder = new FFmpegBuilder() .overrideOutputFiles(true) .addInput(inputPath) .addExtraArgs("-f", "concat") .addExtraArgs("-safe", "0") .addOutput(outputPath + "mergeVideo.mp4") .done(); FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe); executor.createJob(builder).run(); } }
* Video Side By Side
package ffmpeg.sidebyside; import java.io.IOException; import net.bramp.ffmpeg.FFmpeg; import net.bramp.ffmpeg.FFmpegExecutor; import net.bramp.ffmpeg.FFprobe; import net.bramp.ffmpeg.builder.FFmpegBuilder; public class VideoSideBySide { private static final String inputPath = "/Users/birdhead/Desktop/videoTest/mergeVideo.mp4"; private static final String outputPath = "/Users/birdhead/Desktop/videoTest/sideBySide.mp4";; public static void main(String[] args) throws IOException { FFmpeg ffmpeg = new FFmpeg("/usr/local/bin/ffmpeg"); FFprobe ffprobe = new FFprobe("/usr/local/bin/ffprobe"); FFmpegBuilder builder = new FFmpegBuilder() .overrideOutputFiles(true) .addInput(inputPath) .addInput(inputPath) .addOutput(outputPath) .addExtraArgs("-preset", "ultrafast") .addExtraArgs("-filter_complex", "[0:v]setpts=PTS-STARTPTS, pad=iw*2+5:ih[bg]; [1:v]setpts=PTS-STARTPTS[fg]; [bg][fg]overlay=w+10") .done(); FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe); executor.createJob(builder).run(); } }
그럼 여기서 하나 추가적인 의문이 들 수 있다. 콘솔 명령일 경우 main 메소드에서 완료 되었다는 결과 를 받을 수가 없다. 그 이유는 별도의 프로그램이므로 하나의 스레드에서 동작 하는 것이 아니기 때문인데 이 경우 파일이 생성 되었다는 것을 어떻게 알 수 있을까?
그것 또한 아주 손쉽게 처리 할 수 있도록 지원 해주고 있다. ProgressListener를 사용하여 동작에 대한 완료 상태를 체크 할 수 있다. 확인 하기 위해서 VideoTimeCut 클래스에 리스너를 등록 해 보도록 한다.
package ffmpeg.timecut; import java.io.IOException; import net.bramp.ffmpeg.FFmpeg; import net.bramp.ffmpeg.FFmpegExecutor; import net.bramp.ffmpeg.FFprobe; import net.bramp.ffmpeg.builder.FFmpegBuilder; public class VideoTimeCutWithListener { private static final String inputPath = "/Users/birdhead/Desktop/videoTest/pitching.mp4"; private static final String outputPath = "/Users/birdhead/Desktop/videoTest/"; public static void main(String[] args) throws IOException { FFmpeg ffmpeg = new FFmpeg("/usr/local/bin/ffmpeg"); FFprobe ffprobe = new FFprobe("/usr/local/bin/ffprobe"); //다음번 영상 merge를 위해서 3개의 영상 생성 for(int i=0; i<3; i++) { final String fileName = "pitching_out" + i + ".mp4"; FFmpegBuilder builder = new FFmpegBuilder() .overrideOutputFiles(true) .addInput(inputPath) //입력 영상 경로의 .addExtraArgs("-ss", String.valueOf(i)) //영상의 i초 위치 부 .addExtraArgs("-t", "3") //3초 동안 재생한 영상 .addOutput(outputPath + "pitching_out" + i + ".mp4") //outputpath 위치에 .addExtraArgs("-an") //영상의 소리를 제거하고 .done(); //저장 FFmpegExecutor excutor = new FFmpegExecutor(ffmpeg, ffprobe); excutor.createJob(builder, p -> { if(p.isEnd()) { System.out.println(fileName + "make success"); } }).run(); } } }
'Java' 카테고리의 다른 글
MapStruct란? (0) | 2021.08.01 |
---|---|
HttpServletRequest의 getInputStream 사용시 주의 사항 (0) | 2019.03.28 |
구글 주스와 MyBatis의 연동 (0) | 2017.09.23 |
구글 주스 사용기[1] (0) | 2017.08.27 |