简体   繁体   中英

Kill child process only after readFile() Node js Electron

I'm using an imported ffmpeg binary ffmpeg-static-electron in an Electron-React app (on Windows Pro 10) and want to delete a saved cropped video and then kill the child process afterwards from my Main process .

The overall aim is to crop a video of the whole screen following capture and then send the cropped video to a renderer.

const fsPromises = require('fs').promises
const ffmpeg = require('ffmpeg-static-electron')
const { execFile } = require("child_process")


ipcMain.on("windoze_capture_screen:video_buffer", async (event, buffer) => {
    const temp_directory =  await fsPromises.mkdtemp(await fsPromises.realpath(os.tmpdir()) + path.sep)
    const capture_screen_video_path = path.join(temp_directory, "screen_capture_video.mp4")

    child_object = execFile(`${ffmpeg.path}`, 

            ['-i', `${capture_screen_video_path}`, '-vf', `crop=${width}:${height}:${x_pos}:${y_pos}`, `${path.join(temp_directory,'cropped_video.mp4')}`])
            
    child_object.on("exit", async () => {

        // child_object.kill()
        console.log("?Killed -1", child_object.killed)
        
        try { 
            databasePayload.video_buffer = await fsPromises.readFile(path.join(temp_directory, "cropped_video.mp4"), {encoding: 'base64'})
            mainWindow.webContents.send("main_process:video_buffer", databasePayload.video_buffer)
        } catch (error) {
            console.log(error)
        } finally {

            // child_object.kill()
            console.log("?Killed - 2", child_object.killed)
            
            // noASAR required to be set to 'true' in order to remove temp directory in build
            process.noAsar = true 
            fs.rmdir(temp_directory, {recursive: true}, (error) => {if (error) {log(error)}})
            process.noASAR = false
            }

            // 3rd scenario
            // child_object.kill()
            console.log("?Killed -3", child_object.killed)   
        })

        // 4th scenario
        console.log("?Killed-4", child_object.killed)   
    })

When running each of these scenarios, I get the following outputs.

Scenario's 1, 2 and 3 - Successfully sends cropped video to renderer but doesn't kill process.

Output:

?Killed-4 false
?Killed -1 false
[ffmpeg version 3.0.1 Copyright (c) 2000-2016 the FFmpeg developers built with gcc 5.3.0 (GCC).... etc. ]
?Killed-2 false
?Killed -3 false

Scenario 4 - Doesn't crop video

?Killed-4 true
?Killed -1 true
Command failed: C:\Users\XXX\Desktop\windows-electron-forge\node_modules\ffmpeg-static-electron\bin\win\x64\ffmpeg.exe -i C:\Users\XXX\AppData\Local\Temp\4WgnUw\screen_capture_video.mp4 -vf crop=796:763:462:509 C:\Users\XXX\AppData\Local\Temp\4WgnUw\cropped_video.mp4

[Error: ENOENT: no such file or directory, open 'C:\Users\XXX\AppData\Local\Temp\4WgnUw\cropped_video.mp4'] {
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'C:\\Users\\XXX\\AppData\\Local\\Temp\\4WgnUw\\cropped_video.mp4'
}
?Killed - 2 true
?Killed -3 true

The last scenario has been tried out of desperation.

Question: Where am I going wrong with this? I suspect it's something to do with ipc between child and parent process but not sure where to place the code.

Any help will be much appreciated !!!

Once you've received the exit event in your .on ("exit", () => {... });function, there is no need to kill the child process -- it has already exited on its own, meaning it has finished running and will "die" because there is nothing left to do. Killing processes is only needed when they should be forced to abort what they are currently doing or if they misbehave and stick around without actually doing anything (which can be observed in the Task Manager of your OS).

As the Node.js documentation on exit states, this event will only trigger when the process terminates. Thus, you no longer can kill it in the handler function and actually don't need to, because it won't stick around as a so-called "zombie" (see above) as it has already finished execution.

Thus, the following function should be sufficient:

child_object.on("exit", async () => {
    try {
        databasePayload.video_buffer = await fsPromises.readFile(path.join(temp_directory, "cropped_video.mp4"), {encoding: 'base64'})
        mainWindow.webContents.send("main_process:video_buffer", databasePayload.video_buffer)
    } catch (error) {
        console.log(error)
    }
    
    // ... your file removal code ...
});

There are three more things I would like to address:

  • When requiring promises to actually be resolved before continuing with the code following them, it's better not to use await but rather use the function's synchronous version if available. Your fs function calls would then become:
const fs = require ("fs");
const temp_directory = fs.mkdtempSync (fs.realpathSync (os.tmpdir()) + path.sep)
// ...
databasePayload.video_buffer = fs.readFileSync (path.join (temp_directory, "cropped_video.mp4"), { encoding: 'base64' })
// ...
try { fs.rmdirSync (temp_directory, { recursive: true }) } catch (e) { console.log (e); }
  • process.noAsar = true should not be needed unless your temporary directory was actually created inside your application's ASAR archive which is unlikely unless Electron Forge does some magic I'm not aware of. Consider investigating why setting this flag solves your problem (probably by dumping the temporary directory's path).
  • A temporary directory should not be needed for a single file. Unless you plan to use this directory as a storage space for more files, consider rewriting the function to create a temporary file instead and then delete this single file (this will also be easier on the disk hardware, as only one deletion needs to be performed instead of two, but that is actually negligible unless you do this hundreds of times).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM