bramp / ffmpeg-cli-wrapper

Java wrapper around the FFmpeg command line tool
BSD 2-Clause "Simplified" License
1.68k stars 412 forks source link

Kill ffmpeg process? #172

Open nolawnchairs opened 6 years ago

nolawnchairs commented 6 years ago

Question,

I include this library in a small FX application, and I would like to know how to go about killing (SIGINT) the ffmpeg process in the event of the user closing the application.

unix-junkie commented 5 years ago

Additionally, when grabbing a screen:

        final FFmpegBuilder builder = new FFmpegBuilder()
                .setVerbosity(Verbosity.ERROR)
                .setFormat("x11grab") // Format is not always a media format name
                .addInput("+1920,540") // Input is not always a filename
                .overrideOutputFiles(true)
                .addOutput("/tmp/output.mp4")
                .setVideoFrameRate(25, 1)
                .setVideoResolution(640, 480)
                .addExtraArgs("-threads", "0",
                        "-hide_banner")
                .done();

ffmpeg never terminates by itself, so it can only be terminated by external means.

It would be a good idea to also add a JVM shutdown hook to terminate any child processes remaining alive, just in case.

unix-junkie commented 5 years ago

The problem can be worked around by using a custom ProcessFunction:

        final FFmpeg ffmpeg = new FFmpeg("/usr/bin/ffmpeg", new RunProcessFunction() {
            @Override
            public Process run(final List<String> args) throws IOException {
                final Process process = super.run(args);

                if (!(args.size() == 2 && args.get(1).equals("-version"))) {
                    /*
                     * Remember all child processes except "ffmpeg -version"
                     */
                    processes.add(process);
                }

                Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                    terminateFfmpeg(process);
                }, "FFmpeg process destroyer"));
                return process;
            }
        });

where terminateFfmpeg(Process) can be implemented like this:

    /**
     * @return the exit code of the terminated process as an {@code unsigned
     *         byte} (0..255 range), or -1 if the current thread has been
     *         interrupted.
     */
    int terminateFfmpeg(final Process process) {
        if (!process.isAlive()) {
            /*
             * ffmpeg -version, do nothing
             */
            return process.exitValue();
        }

        /*
         * ffmpeg -f x11grab
         */
        System.out.println("About to destroy the child process...");
        try (final OutputStreamWriter out = new OutputStreamWriter(process.getOutputStream(), UTF_8)) {
            out.write('q');
        } catch (final IOException ioe) {
            ioe.printStackTrace();
        }
        try {
            if (!process.waitFor(5L, TimeUnit.SECONDS)) {
                process.destroy();
                process.waitFor();
            }
            return process.exitValue();
        } catch (final InterruptedException ie) {
            System.out.println("Interrupted");
            ie.printStackTrace();
            Thread.currentThread().interrupt();
            return -1;
        }
    }