-
Notifications
You must be signed in to change notification settings - Fork 27
기술 스택
WAVE의 UI는 react
로 구성하였으며, 상태 관리에는 redux
와 공식 바인딩 라이브러리인 react-redux
를 사용하였습니다. UI의 상태와 캔버스의 편집 상태를 동기화하기, 서버와 통신하기 등의 side-effect를 관리하기 위해 redux-saga
도 도입하여 Component와 Reducer의 코드를 순수하게 유지하였습니다. 또한, atomic design
을 벤치마킹하여 Button과 Modal 등 공통적인 요소가 뚜렷한 경우 컴포넌트를 재사용하였습니다.
편집 중인 영상은 HTML5 의 WebGL context
를 통해 보여집니다. 직각삼각형 2개로 구성된 직사각형 modelViewMatrix
에 각 영상 효과를 행렬 연산 또는 shader
로 적용하고, 여기에 원본 영상을 texture
로 그리는 방식입니다. 서명이 있는 경우 사용자의 마우스 이벤트에서 위치를 받아 비슷한 방식으로 적용합니다.
Web Codecs
는 Chrome 브라우저에서 실험 중인 기능으로, 코덱이 이미 브라우저에 내장되어 있음에도 이를 JavaScript로 사용할 수 없어 미디어 인코딩/디코딩을 위해 Web Assembly 등의 다른 API를 도입해야 하는 필요성을 없애는 것이 목적입니다. 이는 MediaStreamTrack
을 디코딩하여 ImageBitmap
으로 전환이 가능한 VideoFrame
을 제공하는 VideoTrackReader
와, VideoFrame
을 인코딩하여 EncodedVideoChunk
를 제공하는 VideoEncoder
로 구성되어 있습니다.
VideoTrackReader
로 재생 중인 영상으로부터 VideoFrame
을 받아 이를 ImageBitmap
으로 전환 후 WebGL로 편집하고자 했지만, 영상의 화질이 좋거나 사용자의 브라우저 상황/컴퓨터 성능이 좋지 못한 경우 심각한 framerate 드랍이 일어나서 사용을 포기하고 HTMLVideoElement.currentTime
을 직접 조작, onseeked
콜백에서 인코딩을 진행하였습니다.
VideoEncoder
로 인코딩을 해서 EncodedVideoChunk
를 얻었지만, 이를 mp4 등의 컨테이너 포맷에 담는 과정을 직접 구현하거나 이를 수행하는 라이브러리를 찾을 수 없어 VideoEncoder
사용도 포기하였습니다. 결과적으로, Web Codecs
를 프로젝트에서 사용하지 않고, Web Assembly를 사용하는 라이브러리(mp4-h264
, ffmpeg.wasm
)를 통해 인코딩과 오디오 추출, mux를 하게 되었습니다.
영상 추출 과정을 요약하면, HTMLVideoElement.currentTime
을 1/30초(30fps)씩 이동하며 onseeked
콜백에서 createImageBitmap()
을 통해 각 프레임을 지정한 해상도의 ImageBitmap
으로 받고, 이를 WebGL로 편집한 후, mp4-h264
라이브러리를 통해 인코딩하여 비디오 트랙만 있는 mp4 파일을 생성합니다. ffmpeg.wasm
라이브러리로는 원본 영상에서 해당 구간의 오디오 트랙을 추출하고, 위 mp4와 합쳐서 오디오 트랙과 비디오 트랙이 모두 있는 mp4 파일을 생성합니다.
nodejs
로 express
서버를 구성하였으며, 영상 목록 관리를 위해 MySQL
데이터베이스를 사용하였습니다.
Webpack
을 통해 bundling, Babel
을 통해 polyfill/transpiling을 적용하였고, prettier
와 ESlint
를 사용하여 코드 스타일을 깔끔하게 관리하였습니다. TypeScript
사용을 통해 컴파일 단계에서 타입 오류를 잡아낼 수 있었습니다.
Web Codecs
를 사용하기 위해서는 secure domain
을 포함하여 사용 신청을 해야 하는 관계로, https
배포를 하고자 Naver Cloud Platform
에서 서버와 공인 IP, freenom
에서 도메인, sslforfree
에서 SSL 발급을 받았습니다.
Backend의 API 서버로는 express
를 사용하였고, pm2
를 통해 항상 동작하도록 했습니다. Frontend의 웹 서버로는 nginx
를 사용하였고, API를 하나의 도메인에서 이용하기 위해 프록시 설정을 추가하였습니다.
Jenkins
를 활용하여 master
브랜치로 merge할 때마다 서버에서도 자동으로 업데이트가 되도록 했습니다.