This commit is contained in:
Manav Rathi
2025-05-07 16:43:45 +05:30
parent 2f670e316b
commit c2c7ac8b23
4 changed files with 22 additions and 14 deletions

View File

@@ -23,7 +23,7 @@ const outputPathPlaceholder = "OUTPUT";
* The interface of the object exposed by `ffmpeg-worker.ts` on the message port
* pair that the main process creates to communicate with it.
*
* @see {@link ffmpegUtilityProcessPort}.
* @see {@link ffmpegUtilityProcessEndpoint}.
*/
export interface FFmpegUtilityProcess {
ffmpegExec: (

View File

@@ -22,6 +22,11 @@ export const ffmpegUtilityProcess = () =>
wrap<FFmpegUtilityProcess>(port),
);
/**
* Implement the IPC "ffmpegExec" contract, writing the input and output to
* temporary files as needed, and then forward to the {@link ffmpegExec} running
* in the utility process.
*/
export const ffmpegExec = async (
command: FFmpegCommand,
dataOrPathOrZipItem: Uint8Array | string | ZipItem,

View File

@@ -31,7 +31,8 @@ let _utilityProcessFFmpegEndpoint: Promise<Endpoint> | undefined;
* Currently the only type is "ml". The following note explains the reasoning
* why utility processes were used for the first workload (ML) that was handled
* this way. Similar reasoning applies to subsequent workloads (ffmpeg) that
* have been offloaded to utility processes to avoid stutter in the UI.
* have been offloaded to utility processes in a slightly different manner to
* avoid stutter in the UI.
*
* [Note: ML IPC]
*
@@ -167,22 +168,28 @@ const handleMessagesFromMLUtilityProcess = (child: UtilityProcess) => {
*
* @returns an endpoint that can be used to communicate with the utility
* process. The utility process is expected to expose an object that conforms to
* the {@link ElectronFFmpegWorkerNode} interface on this port.
* the {@link ElectronFFmpegWorkerNode} interface on this endpoint.
*/
export const ffmpegUtilityProcessEndpoint = () => {
if (_utilityProcessFFmpegEndpoint) return _utilityProcessFFmpegEndpoint;
export const ffmpegUtilityProcessEndpoint = () =>
(_utilityProcessFFmpegEndpoint ??= createFFmpegUtilityProcessEndpoint());
const createFFmpegUtilityProcessEndpoint = () => {
// Promise.withResolvers is currently in the node available to us.
let resolve: ((endpoint: Endpoint) => void) | undefined;
const promise = new Promise<Endpoint>((r) => (resolve = r));
const { port1, port2 } = new MessageChannelMain();
const child = utilityProcess.fork(path.join(__dirname, "ffmpeg-worker.js"));
// Send a handle to the port (one end of the message channel).
// Send a handle to the port (one end of the message channel) to the utility
// process. The utility process will reply with an "ack" when it get it.
child.postMessage({}, [port1]);
child.on("message", (m: unknown) => {
if (m && typeof m == "object" && "method" in m) {
switch (m.method) {
case "ack":
resolveEndpoint!(messagePortMainEndpoint(port2));
resolve!(messagePortMainEndpoint(port2));
return;
}
}
@@ -194,9 +201,7 @@ export const ffmpegUtilityProcessEndpoint = () => {
log.info("Ignoring unknown message from ffmpeg utility process", m);
});
let resolveEndpoint: ((port: Endpoint) => void) | undefined;
_utilityProcessFFmpegEndpoint = new Promise((r) => (resolveEndpoint = r));
// Resolve with the other end of the message channel.
return _utilityProcessFFmpegEndpoint;
// Resolve with the other end of the message channel (once we get an "ack"
// from the utility process).
return promise;
};

View File

@@ -334,8 +334,6 @@ export interface Electron {
maxSize: number,
) => Promise<Uint8Array>;
// - FFmpeg
/**
* Execute a FFmpeg {@link command} on the given
* {@link dataOrPathOrZipItem}.