본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


Node.js

FFMPEG를 활용한 간단한 웹 비디오 에디터(web video editor) - 1

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2021. 9. 1.

ffmpeg를 활용하여 제작하여본 웹 비디오 에디터(영상편집기) 입니다.
 * 웹(web) 이라는 단어가 있으므로 브라우저에서 동작 합니다.

영상 편집기는 이미 많은 수의 성능 좋은 응용프로그램들이 존재 합니다.
많은 수의 영상편집 프로그램들의 일반적인 UI를 구경하여 보면, 영상을 선택 한 뒤에 시간(초)단위로 화면이 나타나게 됩니다.
사진들이 일정한 타임라인이 생성된 곳 에서 구간이 잘리고, 사진이 추가되고 아니면 새로운 영상이 삽입되게 됩니다.

이러한 기능을 구현하기 위해서 ffmpeg를 활용하여 보았습니다.
ffmpeg에서는 내가 원하는 시간(초)별로 영상에서 사진을 추출 할 수 있습니다. (타임라인을 만들 수 있습니다)
또한 영상을 자르고 붙이고 사진을 넣는 등의 기능도 제공 됩니다.

* 참조 : https://lts0606.tistory.com/510

 

이미 좋은 기능을 제공하는 ffmpeg를 응용하면 이러한 기능들을 비슷하게 구현 할 수 있습니다.
ffmpeg의 기능이 정말 훌륭한 것 같습니다.

 

이제 간단하게 영상 편집기를 구현하기 위해 먼저 구현할 기능을 정리하여 보았습니다.
 * 1번 : 영상을 업로드
 * 2번 : 영상을 시간별로 분할(1초단위)
 * 3번 : 원하는 영상의 특정 시간을 잘라내기, 잘라낸 작업 취소
 * 4번 : 잘라낸 구간의 영상을 다른 영상으로 생성

 

1번기능을 구현하기 위해서는 웹서버가 필요 합니다.
2번같은 경우에는 파일이 업로드가 완료 되면 ffmpeg를 활용하여 시간을 분할해야하는 작업이 들어가야 됩니다.
이를 위해 node.js를 활용하여 간단하게 웹서버를 구축하였습니다.
사용된 라이브러리(프레임워크) 목록 입니다. 

 1) ejs
 2) express
 3) express-fileupload
 4) ffmpeg
 5) fluent-ffmpeg

 

#1. 영상 업로드

영상을 업로드 하는 기능은 간단 합니다.

express-fileupload 라이브러리에서 제공되는 함수를 express에서 사용한다고 선언하여 주면 끝 입니다.

* 업로드하는 코드 샘플

const express = require('express')
const fileUpload = require('express-fileupload')
const app = express()
app.use(fileUpload())

app.post('/reFile', (req, res, next) => {  //업로드할 주소 

})

 

#2. 영상분할

다음으로 영상을 분할하는 기술 입니다.

영상을 분할하려면 ffmpeg라이브러리에서 fnExtractFrameToJPG 함수를 사용하여 주면 됩니다.

파일이 업로드가 완료되면 fnExtractFrameToJPG 함수를 통해서 영상을 초단위로 분리합니다.

초단위로 분리하게 되면 지정된 디렉토리에 이미지 파일이 생성됩니다.

* 영상을 분할하는 코드 샘플

const ffmpeg = require('ffmpeg')

const targetMP4File = 'sample.mp4'  //영상 파일(업로드된 파일)
const to_img_file = '저장할디렉토리'
new ffmpeg( targetMP4File,  (err, video)=>{ 
    if (!err) {
        let img_option = {
            start_time : 0,
            frame_rate : 1,
            number : video.metadata.duration.seconds,
            file_name : 'file_%t_%s'
        }
        //동영상에서 이미지를 추출하기 (비동기 방식)
        video.fnExtractFrameToJPG(to_img_file, img_option, (error, files)=>{
            if(!error) {
                //완료 행위
            }
        })   
    }
})

 

여기서 고려해야되는 점이, 사용자의 파일이 업로드가 끝난 뒤의 행위 입니다.

상용화된 영상 편집기를 살펴 보면 동영상을 업로드하면 하단부에 타임라인이 그려지면서 타임라인 위에는 해당 시간의 사진이 나타나게 됩니다.

그러므로 동영상을 업로드 하는 요청이 끝나면,  영상의 시간값 + 사진의 경로값을 리턴하여 브라우저에서 그려주도록 해야 합니다.

* 비동기 방식으로 파일을 업로드 하는 코드 샘플

let param = new FormData()
param.append('file', $('#file')[0].files[0])

$.ajax({
    url : '업로드주소',
    type:'post',
    contentType : 'application/x-www-form-urlencoded; charset=UTF-8',
    data:param,  //동영상 파일
    contentType : false, 
    processData: false,
    success : (res)=>{
        console.log(res)  //영상의 시간값, 사진의 경로값들
    }, 
    error : (error)=>{
        console.log(error)
    }
})

 

그리고 나서 영상의 시간값을 받아서 타임라인을 그려주고, 시간에 따라 받은 사진의 경로값을 타임라인에 추가하여 줍니다.

Javascript를 활용하여 화면 그리는 기능은 Class형태와 Jquery를 사용하였습니다. ^-^

여기까지 내용을 구현하는 것은 어렵지가 않습니다.

단순하게 그리는 것은 매우 쉽습니다.

 

화면을 전체적으로 그려주는 것 까지 완료하였다면 2번작업까지는 무탈하게 끝난 것 입니다.

아래 사진은 타임라인과 사진을 그릴 때 html 노드의 모습입니다.

backgound속성에 이미지 값을 주었습니다.

 

이렇게 해놓고나서 다음으로 고려한 것은 사진의 갯수 입니다.

사용자가 동영상을 업로드 할 때 1분짜리의 동영상이라면 사진이 60개 만들어 졌을 것 이며, 1시간 짜리의 동영상이라면 사진이 60 * 60개 생성되었을 것 입니다.

 

이미지를 표현하는 테그에 background 속성에 이미지 를 부여할 때 3,600개의 이미지를 전부 대입하면 시간도 오래 걸리고 속도 또한 매우 더디게 됩니다.

더욱이 1시간짜리 동영상이 아니라 2시간, 3시간이거나 동영상을 1초단위가 아니라 그 이하로 분할한 경우라면 사진의 갯수는 더욱 많을 것 입니다.

 

그러므로 한번에 사진을 다 가져오는 것이 아니라, 페이징 기법처럼 처음 n개, 초단위로 n개씩 계속해서 가져오도록 수정 해 주어야 합니다.

그리고 영상의 길이에 따라 생긴 스크롤바에도 스크롤이 움직이고 난 뒤의 해당 구간에 이미지가 존재하지 않다면 이미지를 받아오도록 기능을 붙여 주어야 합니다.

* 스크롤바가 움직일 때 이미지가 없다면 이미지를 가져오도록 하는 코드 샘플

let isScroll = false
let scrollLeft = 0

$(`스크롤Element`).scroll(function (event) {  //스크롤 이벤트가 발생하면,
    isScroll = true  //스크롤 이벤트가 발생되었음을 알려주고
    scrollLeft = $(this).scrollLeft()  //왼쪽으로 이동한 값을 대입 합니다
})

$(window).mouseup(()=>{  //스크롤이 끝났음은 마우스가 올라갔을 때 입니다
    let len = Number($(`.타임라인`).css('width').replace('px',''))  //타임라인 그려진 크기를 가져와서
    if(isScroll){
        let startImageNumber = parseInt(scrollLeft / len)  //이동된 스크롤 값으로 나누면 해당 인덱스 번호와 얼추 맞습니다
        downloadImg(startImageNumber)  //해당 구간의 사진을 가져 옵니다
    }
    isScroll = false  //스크롤 이벤트가 끝났음을 대입 합니다
})

 

여기까지의 내용을 적용한 모습 입니다.

스크롤바가 움직이면 없는 구간의 사진을 가져와서 보여주도록 하였습니다.

구간을 선택하면 사각형 박스에 이미지를 나타내면 그럴싸한 모습이 되겠네요.

구현된 모습을 재생하여 보세요.

 

이제 1번 2번에 대한 기능은 얼추 끝이 났습니다.

다음 포스팅에서는 3번, 4번에 대해서 설명 한 뒤에 전체 소스코드를 살펴보겠습니다.

* 다음글 : FFMPEG를 활용한 간단한 웹 비디오 에디터(web video editor) - 2

 

댓글 또는 메일로 언제든 연락주세요👻

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글