use library redirects for ffmpeg output
This commit is contained in:
parent
e6ccb9b66c
commit
a003f84e70
|
@ -1,67 +1,43 @@
|
||||||
package ctbrec.recorder;
|
package ctbrec.recorder;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.lang.ProcessBuilder.Redirect;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import ctbrec.io.DevNull;
|
|
||||||
import ctbrec.io.ProcessStreamRedirector;
|
|
||||||
import ctbrec.recorder.download.ProcessExitedUncleanException;
|
import ctbrec.recorder.download.ProcessExitedUncleanException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class FFmpeg {
|
public class FFmpeg {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FFmpeg.class);
|
|
||||||
|
|
||||||
private static ScheduledExecutorService processOutputReader = Executors.newScheduledThreadPool(2, createThreadFactory("FFmpeg output stream reader"));
|
|
||||||
|
|
||||||
private Process process;
|
private Process process;
|
||||||
private boolean logOutput = false;
|
private boolean logOutput = false;
|
||||||
private Consumer<Process> startCallback;
|
private Consumer<Process> startCallback;
|
||||||
private Consumer<Integer> exitCallback;
|
private Consumer<Integer> exitCallback;
|
||||||
private File ffmpegLog = null;
|
private File ffmpegLog = null;
|
||||||
private OutputStream ffmpegLogStream;
|
|
||||||
private ProcessStreamRedirector stdoutRedirector;
|
|
||||||
private ProcessStreamRedirector stderrRedirector;
|
|
||||||
|
|
||||||
private FFmpeg() {}
|
private FFmpeg() {}
|
||||||
|
|
||||||
private static ThreadFactory createThreadFactory(String name) {
|
|
||||||
return r -> {
|
|
||||||
Thread t = new Thread(r);
|
|
||||||
t.setName(name);
|
|
||||||
t.setDaemon(true);
|
|
||||||
t.setPriority(Thread.MIN_PRIORITY);
|
|
||||||
return t;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void exec(String[] cmdline, String[] env, File executionDir) throws IOException {
|
public void exec(String[] cmdline, String[] env, File executionDir) throws IOException {
|
||||||
LOG.trace("FFmpeg command line: {}", Arrays.toString(cmdline));
|
log.trace("FFmpeg command line: {}", Arrays.toString(cmdline));
|
||||||
process = Runtime.getRuntime().exec(cmdline, env, executionDir);
|
// process = Runtime.getRuntime().exec(cmdline, env, executionDir);
|
||||||
afterStart();
|
|
||||||
|
var builder = new ProcessBuilder(cmdline);
|
||||||
|
// builder.environment().();
|
||||||
|
builder.directory(executionDir);
|
||||||
|
setupLogging(builder);
|
||||||
|
process = builder.start();
|
||||||
|
|
||||||
|
notifyStartCallback(process);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void afterStart() throws IOException {
|
|
||||||
notifyStartCallback(process);
|
|
||||||
setupLogging();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdown(int exitCode) throws IOException {
|
public void shutdown(int exitCode) throws IOException {
|
||||||
LOG.trace("FFmpeg exit code was {}", exitCode);
|
process.destroy();
|
||||||
ffmpegLogStream.flush();
|
log.trace("FFmpeg exit code was {}", exitCode);
|
||||||
ffmpegLogStream.close();
|
|
||||||
stdoutRedirector.setKeepGoing(false);
|
|
||||||
stderrRedirector.setKeepGoing(false);
|
|
||||||
notifyExitCallback(exitCode);
|
notifyExitCallback(exitCode);
|
||||||
if (exitCode != 1) {
|
if (exitCode != 1) {
|
||||||
if (ffmpegLog != null && ffmpegLog.exists()) {
|
if (ffmpegLog != null && ffmpegLog.exists()) {
|
||||||
|
@ -72,28 +48,27 @@ public class FFmpeg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupLogging() throws IOException {
|
private void setupLogging(ProcessBuilder builder) throws IOException {
|
||||||
if (logOutput) {
|
if (logOutput) {
|
||||||
if (ffmpegLog == null) {
|
if (ffmpegLog == null) {
|
||||||
ffmpegLog = File.createTempFile("ffmpeg_", ".log");
|
ffmpegLog = File.createTempFile("ffmpeg_", ".log");
|
||||||
}
|
}
|
||||||
LOG.trace("Logging FFmpeg output to {}", ffmpegLog);
|
log.trace("Logging FFmpeg output to {}", ffmpegLog);
|
||||||
ffmpegLog.deleteOnExit();
|
ffmpegLog.deleteOnExit();
|
||||||
ffmpegLogStream = new FileOutputStream(ffmpegLog);
|
|
||||||
|
builder.redirectOutput(Redirect.to(ffmpegLog));
|
||||||
|
builder.redirectErrorStream(true);
|
||||||
} else {
|
} else {
|
||||||
ffmpegLogStream = new DevNull();
|
builder.redirectOutput(Redirect.DISCARD);
|
||||||
|
builder.redirectError(Redirect.DISCARD);
|
||||||
}
|
}
|
||||||
stdoutRedirector = new ProcessStreamRedirector(processOutputReader, process.getInputStream(), ffmpegLogStream);
|
|
||||||
stderrRedirector = new ProcessStreamRedirector(processOutputReader, process.getErrorStream(), ffmpegLogStream);
|
|
||||||
processOutputReader.submit(stdoutRedirector);
|
|
||||||
processOutputReader.submit(stderrRedirector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyStartCallback(Process process) {
|
private void notifyStartCallback(Process process) {
|
||||||
try {
|
try {
|
||||||
startCallback.accept(process);
|
startCallback.accept(process);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
LOG.error("Exception in onStart callback", e);
|
log.error("Exception in onStart callback", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +76,7 @@ public class FFmpeg {
|
||||||
try {
|
try {
|
||||||
exitCallback.accept(exitCode);
|
exitCallback.accept(exitCode);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
LOG.error("Exception in onExit callback", e);
|
log.error("Exception in onExit callback", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +85,7 @@ public class FFmpeg {
|
||||||
try {
|
try {
|
||||||
shutdown(exitCode);
|
shutdown(exitCode);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Error while shutting down FFmpeg process", e);
|
log.error("Error while shutting down FFmpeg process", e);
|
||||||
}
|
}
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue