음악 영상 하나를 만들려면 편집 프로그램 설치부터 시작해야 했습니다. "브라우저를 열면 바로 되는 영상 스튜디오"를 목표로, 오디오 트랙 편집부터 MP4 렌더링까지 서버 없이 구현한 과정입니다. 핵심은 FFmpeg를 WASM으로 브라우저에 올리는 것이었습니다.
저처럼 영상을 자주 만들지만 무거운 편집 프로그램은 부담스러운 사람들에게 "웹 브라우저가 곧 편집실"이 되면 좋겠다고 생각했습니다. 특히 음악 영상의 경우 오디오 트랙 + 배경 + 로고 + 자막 정도면 충분한데, 이 조합을 브라우저에서 빠르게 처리할 수 있다면 설득력 있는 도구가 될 것 같았습니다.
영상 렌더링의 표준은 FFmpeg입니다. 서버에서 돌리면 가장 쉽지만, "서버 전송 없이"라는 목표와 맞지 않았습니다. 다행히 @ffmpeg/ffmpeg 프로젝트가 FFmpeg를 WebAssembly로 컴파일해 브라우저에서 실행할 수 있게 해줍니다.
핵심 흐름은 단순합니다. 사용자가 업로드한 미디어(오디오·이미지·비디오)를 브라우저 메모리로 읽어 들이고, ffmpeg.exec()로 필터 그래프를 실행한 뒤, 결과 MP4를 다운로드 링크로 만들어 주는 것입니다.
await ffmpeg.writeFile('audio.mp3', audioData)
await ffmpeg.writeFile('bg.jpg', bgData)
await ffmpeg.exec([
'-i', 'bg.jpg', '-i', 'audio.mp3',
'-vf', 'scale=1920:1080,format=yuv420p',
'-t', '10', '-r', '30',
'-c:v', 'libx264', '-c:a', 'aac',
'output.mp4'
])
const out = await ffmpeg.readFile('output.mp4')
오디오 트랙을 업로드하면 파형을 그려줍니다. Web Audio API의 decodeAudioData()로 오디오를 디코딩한 뒤, 채널 데이터를 축소해서 Canvas에 그리는 방식입니다. 렌더링 성능을 위해 1초당 수천 개에 달하는 샘플을 화면 폭에 맞게 버킷으로 묶어 처리했습니다.
기능이 많아질수록 UI가 복잡해지기 마련입니다. 그래서 흐름을 세 단계로 강제했습니다.
| 단계 | 이름 | 할 일 |
|---|---|---|
| 1 | 미디어 준비 | 오디오·배경 업로드 |
| 2 | 비주얼 편집 | 로고·스티커·GIF 배치 |
| 3 | 영상 출력 | 프리뷰 확인 후 MP4 렌더링 |
사용자는 한 번에 한 단계만 신경 쓰면 되고, 진행률도 단계별로 보여줘서 "지금 뭘 하고 있는지"가 항상 명확했습니다. 스티커/GIF는 최대 5개까지 올릴 수 있고, 다중 트랙(최대 50개) 구조로 설계해 확장성을 열어뒀습니다.
가장 어려웠던 건 역시 성능이었습니다. WASM이라 해도 브라우저에서 1080p 영상을 인코딩하는 건 서버보다 몇 배 느립니다. 해결책은 세 가지였습니다.
첫째, 프리뷰는 저해상도로 돌리고 최종 출력만 1920×1080으로 렌더링했습니다. 둘째, 렌더링 중에는 Web Worker에서 돌려 UI가 멈추지 않게 했습니다. 셋째, 긴 영상은 사용자가 직접 -t(길이)를 지정해 필요 부분만 렌더링하도록 유도했습니다.
무거운 편집 프로그램을 대체하기에는 아직 한참 부족하지만, "브라우저에서 1분 안에 영상 하나 뽑아내기"라는 목표에서는 충분히 설득력 있는 결과물이 나왔습니다.