| 43 | * initialized by invoking `start`. |
| 44 | */ |
| 45 | export class FFmpegExporterClient implements Exporter { |
| 46 | public static readonly id = '@revideo/core/ffmpeg'; |
| 47 | public static readonly displayName = 'Video (FFmpeg)'; |
| 48 | |
| 49 | private readonly settings: RendererSettings; |
| 50 | private readonly exporterOptions: FfmpegExporterOptions; |
| 51 | |
| 52 | public static async create(_: Project, settings: RendererSettings) { |
| 53 | return new FFmpegExporterClient(settings); |
| 54 | } |
| 55 | |
| 56 | private static readonly response = new EventDispatcher<ServerResponse>(); |
| 57 | |
| 58 | static { |
| 59 | if (import.meta.hot) { |
| 60 | import.meta.hot.on( |
| 61 | `revideo:ffmpeg-exporter-ack`, |
| 62 | (response: ServerResponse) => this.response.dispatch(response), |
| 63 | ); |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | public constructor(settings: RendererSettings) { |
| 68 | if (settings.exporter.name !== FFmpegExporterClient.id) { |
| 69 | throw new Error('Invalid exporter'); |
| 70 | } |
| 71 | this.settings = settings; |
| 72 | this.exporterOptions = settings.exporter.options; |
| 73 | } |
| 74 | |
| 75 | public async start(): Promise<void> { |
| 76 | await this.invoke('start', this.settings); |
| 77 | } |
| 78 | |
| 79 | public async handleFrame(canvas: HTMLCanvasElement): Promise<void> { |
| 80 | const blob = await new Promise<Blob | null>(resolve => |
| 81 | canvas.toBlob(resolve, 'image/png'), |
| 82 | ); |
| 83 | |
| 84 | if (!blob) { |
| 85 | throw Error('Failed to convert canvas to Blob.'); |
| 86 | } |
| 87 | |
| 88 | const dataUrl = await this.blobToDataUrl(blob); |
| 89 | await this.invoke('handleFrame', { |
| 90 | data: dataUrl, |
| 91 | }); |
| 92 | } |
| 93 | |
| 94 | private async blobToDataUrl(blob: Blob): Promise<string> { |
| 95 | return new Promise((resolve, reject) => { |
| 96 | const reader = new FileReader(); |
| 97 | reader.onloadend = () => resolve(reader.result as string); |
| 98 | reader.onerror = reject; |
| 99 | reader.readAsDataURL(blob); |
| 100 | }); |
| 101 | } |
| 102 | |