Paint Frame Reel Renderer은(는) 무엇을 하나요?
헤드리스 Chrome에서 픽셀 완벽한 1080x1920 세로 비디오 프레임을 렌더링하고, 마이크로 타이밍 FFmpeg concat demuxer 스크립트를 빌드한 후, 최종 H.264 MP4를 320kbps AAC 오디오로 인코딩합니다.
flowchart TD
A[Web Canvas UI 1080x1920] --> B[Puppeteer Headless Capture]
B --> C[Visual Phase Screenshots]
C --> D[Concat Demuxer Script]
D --> E[FFmpeg Encoding libx264 + AAC + faststart]
E --> F[Finished Reel MP4]
Puppeteer + Concat Demuxer를 사용하는 이유
- 드롭된 프레임 제로: 화면 녹화 시 발생하는 드롭된 프레임과 끊김 현상을 제거합니다.
- 마이크로초 정밀도: 각 단어 또는 프레임 상태에 초 단위의 정확한 부동 소수점 지속 시간을 할당합니다.
- 난류 잉크 위글: SVG
feTurbulence와feDisplacementMap을 회전 시드와 함께 사용하여 정적 텍스트에 손으로 그린 마커 생동감을 부여합니다. - 속도: 1배속 화면 녹화 대비 3~6배 빠른 실시간 인코딩 속도.
핵심 렌더 스크립트 구조
const puppeteer = require('puppeteer-core');
const { spawnSync } = require('child_process');
// 1. Puppeteer를 1080x1920으로 실행
const browser = await puppeteer.launch({
executablePath: CHROME_PATH,
args: ['--no-sandbox', '--disable-gpu']
});
const page = await browser.newPage();
await page.setViewport({ width: 1080, height: 1920, deviceScaleFactor: 1 });
await page.goto('file:///' + HTML_PATH);
// 2. 단어/라인 상태 캡처
const concatLines = [];
for (const line of linesData) {
// 스크린샷 캡처...
concatLines.push(`file '${framePath}'`);
concatLines.push(`duration ${duration.toFixed(3)}`);
}
// 3. Concat Demuxer 요구사항 (마지막 프레임 반복)
concatLines.push(concatLines[concatLines.length - 2]);
fs.writeFileSync('concat.txt', concatLines.join('\n'));
// 4. FFmpeg로 인코딩
spawnSync(FFMPEG_PATH, [
'-f', 'concat', '-safe', '0', '-i', 'concat.txt',
'-ss', String(audioStartOffset),
'-i', songAudioPath,
'-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-r', '30',
'-c:a', 'aac', '-b:a', '320k',
'-shortest', '-movflags', '+faststart',
'-y', outputMp4Path
]);
주요 최적화 규칙
- 스냅 리빌: 빠른 노래 구간에서는 리빌 애니메이션이 0.24초를 초과하지 않아야 합니다.
- 장치 스케일 팩터:
deviceScaleFactor: 1로 설정하여 메모리 부담 없이 정확한 1080x1920 해상도를 보장합니다. - +faststart: 항상
-movflags +faststart를 포함하여 릴이 Instagram 및 Telegram에서 즉시 버퍼링되도록 합니다.