[TIL] FFmpeg & Node JS로 영상 썸네일 생성하기

June hyoung Park·2020년 9월 21일
1

NodeJs

목록 보기
7/9
post-thumbnail


요즘 John Ahn님의 Node js 튜토리얼을 보며, 정말 많은 것들을 배우고있다. 특히 강좌에서 다룬 파일업로드 부분은 많은 곳에서 쓸일이 많을거같아서 튜토리얼 내용 외에도 구글링을 해가며 공부하고 있는데, 오늘은 Node js에서 FFmpeg를 이용하여, 영상 업로드 시 영상의 길이와, 썸네일 이미지를 추출하는 과정을 정리해 보려한다.

FFmpeg란?

FFmpeg은 디지털 음성 스트림과 영상 스트림에 대해서 다양한 종류의 형태로 기록하고 변환하는 컴퓨터 프로그램이다. FFmpeg은 명령어를 직접 입력하는 방식으로 동작하며 여러가지 자유 소프트웨어와 오픈 소스 라이브러리로 구성되어 있다. - 위키백과

FFmpeg에 대해 알아보니, 정말 많은 기능들이 있고, 대부분 동영상, 음악, 사진 포맷들의 디코딩과 인코딩에 많이 쓰이는것 같다. 하지만 지금 당장은 영상의 길이와, 썸네일 추출 기능만 사용하면 되므로, 필요한 기능 외 다른 기능들은 깊게 살펴보진 않았다.

설치

npm

npm install fluent-ffmpeg

'fluent-ffmpeg' 라이브러리는 ffmpeg를 자바 스크립트로 포팅한게 아니라 ffmpeg 실행 커맨드라인 래퍼(wrapper) 정도의 모듈이기에 사용하기 위해선 시스템에 'ffmpeg'가 설치되어 있어야 한다.

사전 준비

fluent-ffmpeg를 이용한 썸네일과 영상정보를 추출은 영상 업로드 후 이뤄지기 때문에 'react-dropZone'을 이용해서 업로드 기능을 구현 했다. drop zone은 간단히 말해서 multipart/form-data 속성의 form으로 파일 업로드 시 파일을 끌어서 간편하게 업로드 할 수 있게 끔 도와주는 라이브러리이다.

영상 파일을 드래그하여 업로드 시 실행시킬 콜백 함수를 drop-zone의 속성 중 하나인 onDrop에 지정해 줄 수 있다. 콜백 함수 이름은 속성 이름과 마찬가지로 onDrop으로 해줬으며, FormData 객체를 만든뒤 업로드할 파일을 'file'이란 key의 value로 넣어서 formData로 실어준다.

그 후 Axios를 통해 node서버 측에 만들어둔 api로 post요청을 보내게되며, 업로드 성공 시 파일 이름과 경로를 받게된다.

정상적으로 업로드가 되었고, 프로젝트 root폴더에 static폴더로 지정해 둔 uploads폴더에 영상이 저장되어있는것을 알 수 있다. (파일 업로드는 multer 라이브러리를 이용했으며, 이 글에선 다루지 않을 예정이다. multer 관련 포스트)

const onDrop = (files) => {
    console.log(files);
    let formData = new FormData();
    const config = {
      header: { "content-type": "multipart/form-data" },
    };
    formData.append("file", files[0]);

    Axios.post("/api/video/uploadfiles", formData, config).then((response) => {
      if (response.data.success) {
        console.log(response.data);

        let variable = {
          url: response.data.url,
          fileName: response.data.filename,
        };

        Axios.post("/api/video/thumbnail", variable).then((response) => {
          if (response.data.success) {
            console.log(response.data);
          } else {
            alert("썸네일 에러 발생");
          }
        });
      } else {
        alert("upload failed");
      }
    });
  };

위에서 잠시 다뤘던 onDrop 함수의 풀버전이다. 이제, Axios를 이용해서 url과 fileName를 받아왔으니, 위 코드에서 let variable부분 부터 살펴보자. 먼저 받아온 정보들을 variable이란 객체에 다시 넣어준뒤 Axios 두번째 인자에 넣어서 "/api/video/thumbnail"로 Post 요청을 보낸다.

/thumbnail (API)

router.post("/thumbnail", (req, res) => {
  let thumbsFilePath = "";
  let fileDuration = "";

   // 비디오 전체 정보 추출
  ffmpeg.ffprobe(req.body.url, function (err, metadata) {
    console.dir(metadata);
    console.log(metadata.format.duration);

    fileDuration = metadata.format.duration;
  });

  //썸네일 생성, 비디오 길이 추출
  ffmpeg(req.body.url)
    .on("filenames", function (filenames) {
      console.log("Will generate " + filenames.join(", "));
      thumbsFilePath = "uploads/thumbnails/" + filenames[0];
    })
    .on("end", function () {
      console.log("Screenshots taken");
      return res.json({
        success: true,
        thumbsFilePath: thumbsFilePath,
        fileDuration: fileDuration,
      });
    })
    .on("error", function (err) {
      console.error(err);
      return res.json({ success: false, err });
    })
    .screenshots({
      // Will take screens at 20%, 40%, 60% and 80% of the video
      count: 1,
      folder: "uploads/thumbnails",
      size: "320x200",
      // %b input basename ( filename w/o extension )
      filename: "thumbnail-%b.png",
    });
});

먼저 ffprobe란 것부터 알아볼 필요가 있다. 구글링을 해본 결과, 멀티미디어 스트림에서 정보를 수집하여 사람과 기계가 읽을 수있는 방식으로 바꿔 해준다고 한다. 그렇기에 ffprobe를 통해 영상의 길이를 가져올 수 있다. 또한 ffmpeg 설치 시 함께 설치되니, 따로 설치해줄 필요는 없다.

  let thumbsFilePath = "";
  let fileDuration = "";

   // 비디오 전체 정보 추출
  ffmpeg.ffprobe(req.body.url, function (err, metadata) {
    console.dir(metadata);
    console.log(metadata.format.duration);

    fileDuration = metadata.format.duration;
  });

우선 아까 클라이언트 측에서 Axios로 보낸 업로드된 영상의 url을 첫번째 인자로 넣고, 두번째 인자로 콜백이 들어가는데, 콜백의 두번째 매개변수인 metadata를 통해 영상의 정보를 가져올 수 있다.

console.dir()로 metadata를 출력해보면 해당 영상의 정보들이 담겨져 있는것을 알 수 있다.

일단 이렇게 영상의 길이는 알아냈으니, 위에 선언했던 변수 중 하나인 fileDuration에 할당해준다.
이제, 썸네일 추출 부분으로 넘어가자.

출처 - fluent-ffmpeg 공식 깃허브

fluent-ffmpeg 공식 깃허브 페이지에 들어가면, 예제와 함께 친절하게 설명되어있다. (문서가 너무 길기때문에, screenshot으로 검색하면 빠르게 찾을 수 있다.)

먼저 .on 으로 되어있는 부분은 파일 이름과 모든 작업이 종료 되었을 시, 에러 발생 시 처리할 로직들에 대한 부분인것 같다. 일단 .screenshots부분을 보면 count, folder, size 등 여러 옵션을 지정해 줄 수 있고, 처리가 끝나면 .on("end", ... 부분으로 이동하여, 클라이언트 측으로 썸네일 이미지가 저장된 경로와 영상의 길이를 보내주는 작업을 처리하게된다.

이제 영상 업로드 시 영상의 길이와 썸네일 파일 경로, 그리고 서버의 static폴더인 uploads의 thumbnails 폴더에 이미지가 저장되게된다.

profile
Take me home~~~~

0개의 댓글