From 8890104886e34b606c8e75d5de65f12db52ea23b Mon Sep 17 00:00:00 2001 From: Euklios Date: Mon, 11 Mar 2024 17:30:53 +0100 Subject: [PATCH] Feature/pr214 Appendable to output and error streams - changes (#304) * FEAT Define Appendable to read input and error streams of ffmpeg/ffprobe process Co-authored-by: Mickael GREGORI --- src/main/java/net/bramp/ffmpeg/FFcommon.java | 31 +++++++++++++++++-- .../java/net/bramp/ffmpeg/FFmpegTest.java | 19 ++++++++++++ .../bramp/ffmpeg/lang/NewProcessAnswer.java | 11 ++++++- .../bramp/ffmpeg/fixtures/ffmpeg-no-such-file | 1 + 4 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-no-such-file diff --git a/src/main/java/net/bramp/ffmpeg/FFcommon.java b/src/main/java/net/bramp/ffmpeg/FFcommon.java index b5746102..e766a1ca 100644 --- a/src/main/java/net/bramp/ffmpeg/FFcommon.java +++ b/src/main/java/net/bramp/ffmpeg/FFcommon.java @@ -8,6 +8,7 @@ import com.google.common.io.CharStreams; import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.List; @@ -28,6 +29,12 @@ abstract class FFcommon { /** Version string */ String version = null; + /** Process input stream */ + Appendable processOutputStream = System.out; + + /** Process error stream */ + Appendable processErrorStream = System.err; + public FFcommon(@Nonnull String path) { this(path, new RunProcessFunction()); } @@ -38,8 +45,26 @@ protected FFcommon(@Nonnull String path, @Nonnull ProcessFunction runFunction) { this.path = path; } + public void setProcessOutputStream(@Nonnull Appendable processOutputStream) { + Preconditions.checkNotNull(processOutputStream); + this.processOutputStream = processOutputStream; + } + + public void setProcessErrorStream(@Nonnull Appendable processErrorStream) { + Preconditions.checkNotNull(processErrorStream); + this.processErrorStream = processErrorStream; + } + + private BufferedReader _wrapInReader(final InputStream inputStream) { + return new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + } + protected BufferedReader wrapInReader(Process p) { - return new BufferedReader(new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8)); + return _wrapInReader(p.getInputStream()); + } + + protected BufferedReader wrapErrorInReader(Process p) { + return _wrapInReader(p.getErrorStream()); } protected void throwOnError(Process p) throws IOException { @@ -107,8 +132,8 @@ public void run(List args) throws IOException { // TODO Move the copy onto a thread, so that FFmpegProgressListener can be on this thread. // Now block reading ffmpeg's stdout. We are effectively throwing away the output. - CharStreams.copy(wrapInReader(p), System.out); // TODO Should I be outputting to stdout? - + CharStreams.copy(wrapInReader(p), processOutputStream); + CharStreams.copy(wrapErrorInReader(p), processErrorStream); throwOnError(p); } finally { diff --git a/src/test/java/net/bramp/ffmpeg/FFmpegTest.java b/src/test/java/net/bramp/ffmpeg/FFmpegTest.java index 7a4902fe..91e31f6f 100644 --- a/src/test/java/net/bramp/ffmpeg/FFmpegTest.java +++ b/src/test/java/net/bramp/ffmpeg/FFmpegTest.java @@ -7,6 +7,8 @@ import java.io.IOException; import java.util.List; + +import com.google.common.collect.Lists; import net.bramp.ffmpeg.fixtures.Codecs; import net.bramp.ffmpeg.fixtures.Filters; import net.bramp.ffmpeg.fixtures.Formats; @@ -35,6 +37,8 @@ public void before() throws IOException { when(runFunc.run(argThatHasItem("-codecs"))).thenAnswer(new NewProcessAnswer("ffmpeg-codecs")); when(runFunc.run(argThatHasItem("-pix_fmts"))) .thenAnswer(new NewProcessAnswer("ffmpeg-pix_fmts")); + when(runFunc.run(argThatHasItem("toto.mp4"))) + .thenAnswer(new NewProcessAnswer("ffmpeg-version", "ffmpeg-no-such-file")); when(runFunc.run(argThatHasItem("-filters"))) .thenAnswer(new NewProcessAnswer("ffmpeg-filters")); @@ -72,6 +76,21 @@ public void testFormats() throws IOException { verify(runFunc, times(1)).run(argThatHasItem("-formats")); } + @Test + public void testReadProcessStreams() throws IOException { + // process input stream + Appendable processInputStream = mock(Appendable.class); + ffmpeg.setProcessOutputStream(processInputStream); + // process error stream + Appendable processErrStream = mock(Appendable.class); + ffmpeg.setProcessErrorStream(processErrStream); + // run ffmpeg with non existing file + ffmpeg.run(Lists.newArrayList("-i", "toto.mp4")); + // check calls to Appendables + verify(processInputStream, times(1)).append(any(CharSequence.class)); + verify(processErrStream, times(1)).append(any(CharSequence.class)); + } + @Test public void testPixelFormat() throws IOException { // Run twice, the second should be cached diff --git a/src/test/java/net/bramp/ffmpeg/lang/NewProcessAnswer.java b/src/test/java/net/bramp/ffmpeg/lang/NewProcessAnswer.java index 5895537b..25154120 100644 --- a/src/test/java/net/bramp/ffmpeg/lang/NewProcessAnswer.java +++ b/src/test/java/net/bramp/ffmpeg/lang/NewProcessAnswer.java @@ -7,12 +7,21 @@ public class NewProcessAnswer implements Answer { final String resource; + final String errResource; + public NewProcessAnswer(String resource) { + this(resource, null); + } + + public NewProcessAnswer(String resource, String errResource) { this.resource = resource; + this.errResource = errResource; } @Override public Process answer(InvocationOnMock invocationOnMock) throws Throwable { - return new MockProcess(Helper.loadResource(resource)); + return errResource == null + ? new MockProcess(Helper.loadResource(resource)) + : new MockProcess(null, Helper.loadResource(resource), Helper.loadResource(errResource)); } } diff --git a/src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-no-such-file b/src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-no-such-file new file mode 100644 index 00000000..070b5f6d --- /dev/null +++ b/src/test/resources/net/bramp/ffmpeg/fixtures/ffmpeg-no-such-file @@ -0,0 +1 @@ +toto.mp4: No such file or directory \ No newline at end of file