()
| 247 | } |
| 248 | |
| 249 | export function registerExportHandlers() { |
| 250 | ipcMain.handle( |
| 251 | "native-video-export-start", |
| 252 | async ( |
| 253 | event, |
| 254 | options: { |
| 255 | width: number; |
| 256 | height: number; |
| 257 | frameRate: number; |
| 258 | bitrate: number; |
| 259 | encodingMode: NativeExportEncodingMode; |
| 260 | inputMode?: "rawvideo" | "h264-stream"; |
| 261 | }, |
| 262 | ) => { |
| 263 | try { |
| 264 | if (options.width % 2 !== 0 || options.height % 2 !== 0) { |
| 265 | throw new Error("Native export requires even output dimensions"); |
| 266 | } |
| 267 | |
| 268 | const ffmpegPath = getFfmpegBinaryPath(); |
| 269 | const inputMode = options.inputMode ?? "rawvideo"; |
| 270 | const sessionId = `recordly-export-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; |
| 271 | const outputPath = path.join(app.getPath("temp"), `${sessionId}.mp4`); |
| 272 | |
| 273 | let encoderName: string; |
| 274 | let ffmpegArgs: string[]; |
| 275 | |
| 276 | if (inputMode === "h264-stream") { |
| 277 | // Pre-encoded H.264 Annex B from browser VideoEncoder — just stream-copy into MP4 |
| 278 | encoderName = "h264-stream-copy"; |
| 279 | ffmpegArgs = buildNativeH264StreamExportArgs({ |
| 280 | frameRate: options.frameRate, |
| 281 | outputPath, |
| 282 | }); |
| 283 | } else { |
| 284 | encoderName = await resolveNativeVideoEncoder(ffmpegPath, options.encodingMode); |
| 285 | ffmpegArgs = buildNativeVideoExportArgs(encoderName, options, outputPath); |
| 286 | } |
| 287 | |
| 288 | const ffmpegProcess = spawn(ffmpegPath, ffmpegArgs, { |
| 289 | stdio: ["pipe", "ignore", "pipe"], |
| 290 | }) as ChildProcessByStdio<Writable, null, Readable>; |
| 291 | // For rawvideo, frames are a fixed RGBA size. For h264-stream, chunks are variable. |
| 292 | const inputByteSize = |
| 293 | inputMode === "rawvideo" |
| 294 | ? getNativeVideoInputByteSize(options.width, options.height) |
| 295 | : 0; |
| 296 | |
| 297 | const session: NativeVideoExportSession = { |
| 298 | ffmpegProcess, |
| 299 | outputPath, |
| 300 | inputByteSize, |
| 301 | inputMode, |
| 302 | maxQueuedWriteBytes: |
| 303 | inputMode === "h264-stream" |
| 304 | ? 32 * 1024 * 1024 |
| 305 | : getNativeVideoExportMaxQueuedWriteBytes(inputByteSize), |
| 306 | stderrOutput: "", |
no test coverage detected