동영상 파일 썸네일 추출하기 (Java, ffmpeg 사용)
동영상 파일 썸네일 추출하기 (Java, ffmpeg 사용)
이번 포스팅은 ffmpeg를 이용하혀 Thumbnail을 추출하려고 합니다.
ffmpeg 파일은 https://ffmpeg.zeranoe.com/builds/ 에서 받을 수 있습니다.
1. 동영상 썸네일 추출하기
ffmpeg -i [비디오파일경로] -ss [추출시간] -vcodec [썸네일확장자] -vframes [추출 썸네일 갯수] [썸네일파일명]
영상 10초 부분의 화면 썸네일 1개 생성 예 )
ffmpeg -i input.mp4 -ss 00:00:10 -vcodec png
-vframes 1 thumbnail_%d.png
2. 동영상 워터마크 이미지 오버레이
ffmpeg -i [비디오파일경로] -i [워터마크파일경로] -filter_complex "overlay=[워터마크 박을 위치값]" -codec:a copy [결과파일경로]
영상 가운데 워터마크 오버레이 예 )
ffmpeg -i input.mp4 -i watermark.png
-filter_complex "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2"
-codec:a copy output.mp4
3. 사운드파일에 워터마크 사운드 오버레이 (사운드파일에 워터마크 사운드 삽입하기)
ffmpeg -i [사운드파일경로] -f lavfi -i amovie=[워터마크사운드파일명]:loop=[워터마크사운드반복횟수]
-filter_complex "[0:a][1:a]amerge, pan=stereo|c0=0.5*c0+0.5*c2|c1=0.5*c1+0.5*c3" [결과파일경로]
사운드에 1초짜리 워터마크 사운드 무한반복 오버레이 예)
ffmpeg -i input.mp3 -f lavfi -i amovie=watermark.mp3:loop=99999
-filter_complex "[0:a][1:a]amerge, pan=stereo|c0=0.5*c0+0.5*c2|c1=0.5*c1+0.5*c3" output.mp3
※ 워터마크사운드파일명(watermark.mp3)란에 경로까지 넣으면(ex: c:/test/watermark.mp3)
No such file or directory 오류가 발생되는데 아직 이유를 모르겠다.
4. 사운드파일에 앨범이미지 삽입하기
ffmpeg -i input.mp3 -i album.jpg -map 0:0 -map 1:0 -c copy -id3v2_version 3
-metadata:s:v title="Album cover" -metadata:s:v comment="Cover (Front)" output.mp3
5. 사운드파일에서 앨범이미지 추출하기 (만약 앨범 이미지 없을시 오류)
ffmpeg -i input.mp3 -an -vcodec copy album.jpg
6. 사운드에 앨범 이미지 삽입해서 영상파일 만들기
ffmpeg -loop 1 -i album.jpg -i input.mp3 -c:v libx264 -c:a aac -b:a 192k -shortest output.mp4
※ JAVA 구현 소스
/**
* 동영상 파일 인코딩 작업
* @param fileName
* @param basePath
* @return
*/
public String convertImage(String fileName, String basePath) {
File fOriginal = new File(basePath + System.getProperty("file.separator") + fileName);
String outputName = "img" + fileName.substring(0, fileName.indexOf("."))+ ".jpg";
File fResult = new File(basePath + System.getProperty("file.separator") + outputName);
String ffmpegPath = PropertyInfo.videoConvertDir; // ffmpeg 경로
// cmd 창에 날릴 명령어 만들기~
String[] cmdLine = new String[]{ffmpegPath,
"-i",
fOriginal.getPath(),
"-an",
"-ss",
"00:00:01",
"-r",
"1",
"-vframes",
"1",
"-y",
fResult.getPath()};
// ffmpeg -i "ok.flv" -an -ss 00:00:01 -r 1 -vframes 1 -y "%d.jpg"
// 만약 flv 파일이라면 변환과정 없이 스킵~
if(fOriginal.getPath().endsWith(".jpg")) {
return outputName;
}
// 프로세스 속성을 관리하는 ProcessBuilder 생성.
ProcessBuilder pb = new ProcessBuilder(cmdLine);
pb.redirectErrorStream(true);
Process p = null;
try {
// 프로세스 작업을 실행함.
p = pb.start();
} catch (Exception e) {
e.printStackTrace();
p.destroy();
return null;
}
// 자식 프로세스에서 발생되는 인풋스트림 소비시킴
exhaustInputStream(p.getInputStream());
try {
// p의 자식 프로세스의 작업이 완료될 동안 p를 대기시킴
p.waitFor();
} catch (InterruptedException e) {
p.destroy();
}
// 정상 종료가 되지 않았을 경우
if (p.exitValue() != 0) {
System.out.println("변환 중 에러 발생");
return null;
}
// 변환을 하는 중 에러가 발생하여 파일의 크기가 0일 경우
if (fResult.length() == 0) {
System.out.println("변환된 파일의 사이즈가 0임");
return null;
}
System.out.println("변환 성공 ^^");
// fOriginal.delete(); // 원본 파일 삭제
return outputName;
}
/**
* 동영상파일 인코딩 작업
* @param is
*/
private void exhaustInputStream(final InputStream is) {
// InputStream.read() 에서 블럭상태에 빠지기 때문에 따로 쓰레드를 구현하여 스트림을
// 소비한다.
new Thread() {
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String cmd;
while((cmd = br.readLine()) != null) { // 읽어들일 라인이 없을때까지 계속 반복
System.out.println(cmd);
}
} catch(IOException e) {
e.printStackTrace();
}
}
}.start();
}