import { buildFfmpegCompositionArgs } from '../src/recording/composer'; import { ServerTrackRecordingFile } from '../src/recording/storage'; function file(filename: string, trackKind: string, participantId: string, role = 'participant'): ServerTrackRecordingFile { return { meetingId: 'room-1', directory: 'recordings/room-1', filename, filePath: `recordings/room-1/${filename}`, metadataPath: `recordings/room-1/${filename}.json`, recordingId: 'recording-1', participantId, trackId: `${participantId}-${trackKind}`, trackKind, uploadedAt: '2026-06-01T00:00:00.000Z', metadata: { role } }; } describe('recording composer', () => { test('builds ffmpeg args for host-led video layout and mixed audio', () => { const args = buildFfmpegCompositionArgs({ videoInputs: [ file('p1-video.webm', 'video', 'p1', 'host'), file('p2-video.webm', 'video', 'p2') ], audioInputs: [ file('p1-audio.webm', 'audio', 'p1'), file('p2-audio.webm', 'audio', 'p2') ], outputPath: 'recordings/room-1/output.webm', format: 'webm' }); expect(args).toContain('-filter_complex'); expect(args.join(' ')).toContain('xstack=inputs=2'); expect(args.join(' ')).toContain('scale=1280:540'); expect(args.join(' ')).toContain('scale=1280:180'); expect(args.join(' ')).toContain('layout=0_0|0_540'); expect(args.join(' ')).toContain('amix=inputs=2'); expect(args).toContain('libvpx-vp9'); expect(args).toContain('libopus'); expect(args[args.length - 1]).toBe('recordings/room-1/output.webm'); }); test('places host in the first row even when host input is not first', () => { const args = buildFfmpegCompositionArgs({ videoInputs: [ file('p1-video.webm', 'video', 'p1'), file('host-video.webm', 'video', 'host', 'host'), file('p2-video.webm', 'video', 'p2') ], audioInputs: [], outputPath: 'recordings/room-1/output.webm', format: 'webm' }); const filter = args[args.indexOf('-filter_complex') + 1]; expect(args.slice(0, 7)).toEqual([ '-y', '-i', 'recordings/room-1/host-video.webm', '-i', 'recordings/room-1/p1-video.webm', '-i', 'recordings/room-1/p2-video.webm' ]); expect(filter).toContain('scale=1280:540'); expect(filter).toContain('scale=640:180'); expect(filter).toContain('layout=0_0|0_540|640_540'); }); test('builds mp4 encoder args', () => { const args = buildFfmpegCompositionArgs({ videoInputs: [file('p1-video.webm', 'video', 'p1')], audioInputs: [], outputPath: 'recordings/room-1/output.mp4', format: 'mp4' }); expect(args).toContain('libx264'); expect(args).toContain('-pix_fmt'); expect(args).not.toContain('libopus'); }); });