동영상 파일 썸네일 추출하기 (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 구현 소스


  1.         /**
  2. * 동영상 파일 인코딩 작업
  3. * @param fileName
  4. * @param basePath
  5. * @return
  6. */
  7. public String convertImage(String fileName, String basePath) {
  8. File fOriginal = new File(basePath + System.getProperty("file.separator") + fileName);
  9. String outputName = "img" + fileName.substring(0, fileName.indexOf("."))+ ".jpg";
  10. File fResult = new File(basePath + System.getProperty("file.separator") + outputName);
  11. String ffmpegPath = PropertyInfo.videoConvertDir; // ffmpeg 경로
  12. // cmd 창에 날릴 명령어 만들기~
  13. String[] cmdLine = new String[]{ffmpegPath,
  14. "-i",
  15. fOriginal.getPath(),
  16. "-an",
  17. "-ss",
  18. "00:00:01",
  19. "-r",
  20. "1",
  21. "-vframes",
  22. "1",
  23. "-y",
  24. fResult.getPath()};
  25. // ffmpeg -i "ok.flv" -an -ss 00:00:01 -r 1 -vframes 1 -y "%d.jpg"
  26. // 만약 flv 파일이라면 변환과정 없이 스킵~
  27. if(fOriginal.getPath().endsWith(".jpg")) {
  28. return outputName;
  29. }
  30. // 프로세스 속성을 관리하는 ProcessBuilder 생성.
  31. ProcessBuilder pb = new ProcessBuilder(cmdLine);
  32. pb.redirectErrorStream(true);
  33. Process p = null;
  34. try {
  35. // 프로세스 작업을 실행함.
  36. p = pb.start();
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. p.destroy();
  40. return null;
  41. }
  42. // 자식 프로세스에서 발생되는 인풋스트림 소비시킴
  43. exhaustInputStream(p.getInputStream());
  44. try {
  45. // p의 자식 프로세스의 작업이 완료될 동안 p를 대기시킴
  46. p.waitFor();
  47. } catch (InterruptedException e) {
  48. p.destroy();
  49. }
  50. // 정상 종료가 되지 않았을 경우
  51. if (p.exitValue() != 0) {
  52. System.out.println("변환 중 에러 발생");
  53. return null;
  54. }
  55. // 변환을 하는 중 에러가 발생하여 파일의 크기가 0일 경우
  56. if (fResult.length() == 0) {
  57. System.out.println("변환된 파일의 사이즈가 0임");
  58. return null;
  59. }
  60. System.out.println("변환 성공 ^^");
  61. // fOriginal.delete(); // 원본 파일 삭제
  62. return outputName;
  63. }
  64. /**
  65. * 동영상파일 인코딩 작업
  66. * @param is
  67. */
  68. private void exhaustInputStream(final InputStream is) {
  69. // InputStream.read() 에서 블럭상태에 빠지기 때문에 따로 쓰레드를 구현하여 스트림을
  70. // 소비한다.
  71. new Thread() {
  72. public void run() {
  73. try {
  74. BufferedReader br = new BufferedReader(new InputStreamReader(is));
  75. String cmd;
  76. while((cmd = br.readLine()) != null) { // 읽어들일 라인이 없을때까지 계속 반복
  77. System.out.println(cmd);
  78. }
  79. } catch(IOException e) {
  80. e.printStackTrace();
  81. }
  82. }
  83. }.start();
  84. }


작성자 소개
초이 프로필
WrapUp 블로거

초이

반려견을 좋아하고, 차를 좋아하고, 여행을 좋아하고, 맛집을 찾아 즐기는 웹 개발자 입니다^^