package com.github.kokorin.jaffree.ffmpeg; import com.github.kokorin.jaffree.Artifacts; import com.github.kokorin.jaffree.LogLevel; import com.github.kokorin.jaffree.SizeUnit; import com.github.kokorin.jaffree.StackTraceMatcher; import com.github.kokorin.jaffree.StreamType; import com.github.kokorin.jaffree.ffprobe.FFprobe; import com.github.kokorin.jaffree.ffprobe.FFprobeResult; import com.github.kokorin.jaffree.ffprobe.Stream; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.READ; import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static java.nio.file.StandardOpenOption.WRITE; import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; public class FFmpegTest { public static Path BIN; public static Path VIDEO_MP4 = Artifacts.getFFmpegSample("MPEG-4/video.mp4"); public static Path SMALL_FLV = Artifacts.getFFmpegSample("FLV/zelda.flv"); public static Path SMALL_MP4 = Artifacts.getFFmpegSample("MPEG-4/turn-on-off.mp4"); public static Path ERROR_MP4 = Paths.get("non_existent.mp4"); private static final Logger LOGGER = LoggerFactory.getLogger(FFmpegTest.class); @Rule public ExpectedException expectedException = ExpectedException.none(); @BeforeClass public static void setUp() throws Exception { String ffmpegHome = System.getProperty("FFMPEG_BIN"); if (ffmpegHome == null) { ffmpegHome = System.getenv("FFMPEG_BIN"); } Assert.assertNotNull("Nor command line property, neither system variable FFMPEG_BIN is set up", ffmpegHome); BIN = Paths.get(ffmpegHome); Assert.assertTrue("Sample videos weren't found: " + VIDEO_MP4.toAbsolutePath(), Files.exists(VIDEO_MP4)); Assert.assertTrue("Sample videos weren't found: " + SMALL_FLV.toAbsolutePath(), Files.exists(SMALL_FLV)); Assert.assertTrue("Sample videos weren't found: " + SMALL_MP4.toAbsolutePath(), Files.exists(SMALL_MP4)); } @Test public void testSimpleCopy() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(VIDEO_MP4)) .addOutput(UrlOutput .toPath(outputPath) .copyAllCodecs()) .execute(); Assert.assertNotNull(result); } // For this test to pass ffmpeg must be added to Operation System PATH environment variable @Test public void testEnvPath() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result = FFmpeg.atPath() .addInput(UrlInput.fromPath(VIDEO_MP4)) .addOutput(UrlOutput .toPath(outputPath) .copyAllCodecs()) .execute(); Assert.assertNotNull(result); } @Test public void testOutputAdditionalOption() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve("test.mp3"); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(VIDEO_MP4)) .addOutput(UrlOutput .toPath(outputPath) .setCodec(StreamType.AUDIO, "mp3") .disableStream(StreamType.VIDEO) .addArguments("-id3v2_version", "3") ) .execute(); Assert.assertNotNull(result); FFprobeResult probe = FFprobe.atPath(BIN) .setInput(outputPath) .setShowStreams(true) .execute(); Assert.assertNotNull(probe); Assert.assertEquals(1, probe.getStreams().size()); Assert.assertEquals(StreamType.AUDIO, probe.getStreams().get(0).getCodecType()); } @Test public void testProgress() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve("test.mkv"); final AtomicLong counter = new AtomicLong(); ProgressListener listener = new ProgressListener() { @Override public void onProgress(FFmpegProgress progress) { counter.incrementAndGet(); } }; FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(SMALL_FLV)) .addOutput(UrlOutput.toPath(outputPath)) .setProgressListener(listener) .execute(); Assert.assertNotNull(result); Assert.assertTrue(counter.get() > 0); } @Test public void testProgress2() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve("test.flv"); final AtomicLong counter = new AtomicLong(); ProgressListener listener = new ProgressListener() { @Override public void onProgress(FFmpegProgress progress) { counter.incrementAndGet(); } }; FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(SMALL_MP4)) .addOutput(UrlOutput.toPath(outputPath)) .setProgressListener(listener) .execute(); Assert.assertNotNull(result); Assert.assertTrue(counter.get() > 0); } @Test public void testDuration() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput .fromPath(VIDEO_MP4) .setDuration(10, TimeUnit.SECONDS) ) .addOutput(UrlOutput .toPath(outputPath) .copyAllCodecs()) .execute(); Assert.assertNotNull(result); double outputDuration = getDuration(outputPath); Assert.assertEquals(10.0, outputDuration, 0.1); result = FFmpeg.atPath(BIN) .addInput(UrlInput .fromPath(VIDEO_MP4) .setDuration(1. / 6., TimeUnit.MINUTES) ) .setOverwriteOutput(true) .addOutput(UrlOutput .toPath(outputPath) .copyAllCodecs()) .execute(); Assert.assertNotNull(result); outputDuration = getDuration(outputPath); Assert.assertEquals(10.0, outputDuration, 0.1); } @Test public void testForceStopWithProgressListenerException() throws Exception { expectedException.expect(new StackTraceMatcher("Stop ffmpeg with ProgressListener Exception")); Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); final long startedAtMillis = System.currentTimeMillis(); final ProgressListener progressListener = new ProgressListener() { @Override public void onProgress(FFmpegProgress progress) { System.out.println(progress); if (System.currentTimeMillis() - startedAtMillis > 5_000) { throw new RuntimeException("Stop ffmpeg with ProgressListener Exception"); } } }; final FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput .fromPath(VIDEO_MP4) .setReadAtFrameRate(true) ) .setProgressListener(progressListener) .addOutput(UrlOutput.toPath(outputPath)) .execute(); } @Test public void testForceStopWithThreadInterruption() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); final AtomicReference<FFmpegResult> result = new AtomicReference<>(); final FFmpeg ffmpeg = FFmpeg.atPath(BIN) .addInput(UrlInput .fromPath(VIDEO_MP4) .setReadAtFrameRate(true) ) .addOutput(UrlOutput.toPath(outputPath)); final AtomicReference<Exception> executeException = new AtomicReference<>(); Thread thread = new Thread() { @Override public void run() { try { FFmpegResult r = ffmpeg.execute(); result.set(r); } catch (Exception e) { executeException.set(e); } } }; thread.start(); Thread.sleep(5_000); thread.interrupt(); Thread.sleep(1_000); Assert.assertNull(result.get()); Assert.assertTrue(Files.exists(outputPath)); Assert.assertTrue(executeException.get() instanceof RuntimeException); Assert.assertEquals("Failed to execute, was interrupted", executeException.get().getMessage()); } @Test public void testForceAsyncStop() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpeg ffmpeg = FFmpeg.atPath(BIN) .addInput(UrlInput .fromPath(VIDEO_MP4) .setReadAtFrameRate(true) ) .addOutput(UrlOutput.toPath(outputPath)); FFmpegResultFuture futureResult = ffmpeg.executeAsync(); Thread.sleep(5_000); futureResult.forceStop(); Thread.sleep(1_000); Assert.assertTrue(Files.exists(outputPath)); } @Test public void testGraceAsyncStop() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); final AtomicReference<FFmpegResultFuture> futureRef = new AtomicReference<>(); final ProgressListener progressListener = new ProgressListener() { @Override public void onProgress(FFmpegProgress progress) { System.out.println(progress); if (progress.getTime(TimeUnit.SECONDS) >= 15) { futureRef.get().graceStop(); } } }; FFmpeg ffmpeg = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(VIDEO_MP4)) .setProgressListener(progressListener) .addOutput(UrlOutput.toPath(outputPath)); FFmpegResultFuture futureResult = ffmpeg.executeAsync(); futureRef.set(futureResult); FFmpegResult encodingResult = futureResult.get(12, TimeUnit.SECONDS); Assert.assertNotNull(encodingResult); FFprobeResult probeResult = FFprobe.atPath(BIN) .setShowStreams(true) .setInput(outputPath) .execute(); Assert.assertEquals(2, probeResult.getStreams().size()); final AtomicReference<Long> durationRef = new AtomicReference<>(); final ProgressListener progressDurationListener = new ProgressListener() { @Override public void onProgress(FFmpegProgress progress) { System.out.println(progress); durationRef.set(progress.getTime(TimeUnit.SECONDS)); } }; FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(outputPath)) .setProgressListener(progressDurationListener) .addOutput(new NullOutput()) .execute(); Assert.assertNotNull(result); Assert.assertTrue(durationRef.get() >= 15); } @Test public void testOutputPosition() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(VIDEO_MP4)) .addOutput(UrlOutput .toPath(outputPath) .copyAllCodecs() .setOutputPosition(15, TimeUnit.SECONDS) ) .execute(); Assert.assertNotNull(result); double outputDuration = getDuration(outputPath); Assert.assertEquals(15.0, outputDuration, 0.1); } @Test public void testSizeLimit() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(VIDEO_MP4)) .addOutput(UrlOutput .toPath(outputPath) .copyAllCodecs() .setSizeLimit(1, SizeUnit.MB) ) .execute(); Assert.assertNotNull(result); long outputSize = Files.size(outputPath); Assert.assertTrue(outputSize > 900_000); Assert.assertTrue(outputSize < 1_100_000); } @Test public void testPosition() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput .fromPath(VIDEO_MP4) .setPosition(10, TimeUnit.SECONDS) ) .addOutput(UrlOutput .toPath(outputPath) .copyAllCodecs()) .execute(); Assert.assertNotNull(result); double inputDuration = getDuration(VIDEO_MP4); double outputDuration = getDuration(outputPath); Assert.assertEquals(inputDuration - 10, outputDuration, 0.5); } @Test public void testPositionNegative() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput .fromPath(VIDEO_MP4) .setPositionEof(-7, TimeUnit.SECONDS) ) .addOutput(UrlOutput .toPath(outputPath) .copyAllCodecs()) .execute(); Assert.assertNotNull(result); double outputDuration = getDuration(outputPath); Assert.assertEquals(7.0, outputDuration, 0.5); } @Test public void testNullOutput() throws Exception { final AtomicLong time = new AtomicLong(); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput .fromPath(VIDEO_MP4) ) .addOutput( new NullOutput() ) .setOverwriteOutput(true) .setProgressListener(new ProgressListener() { @Override public void onProgress(FFmpegProgress progress) { time.set(progress.getTimeMillis()); } }) .execute(); Assert.assertNotNull(result); Assert.assertTrue(time.get() > 165_000); } @Test public void testMap() throws Exception { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(VIDEO_MP4)) .addOutput(UrlOutput .toPath(outputPath) .copyAllCodecs() .addMap(0, StreamType.AUDIO) .addMap(0, StreamType.AUDIO) .addMap(0, StreamType.VIDEO) ) .execute(); Assert.assertNotNull(result); FFprobeResult probe = FFprobe.atPath(BIN) .setShowStreams(true) .setInput(outputPath) .execute(); Assert.assertNull(probe.getError()); List<Stream> streamTypes = probe.getStreams(); Assert.assertEquals(3, streamTypes.size()); Assert.assertEquals(StreamType.AUDIO, streamTypes.get(0).getCodecType()); Assert.assertEquals(StreamType.AUDIO, streamTypes.get(1).getCodecType()); Assert.assertEquals(StreamType.VIDEO, streamTypes.get(2).getCodecType()); } @Test @Ignore("This test requires manual verification of result frames") public void testAlpha() throws Exception { // https://www.videezy.com/elements-and-effects/7213-animated-character-girl-biking-alpha-transparent Path videoWithAlpha = Artifacts.getSample(URI.create("https://static.videezy.com/system/protected/files/000/007/213/Biking_Girl_Alpha.mov?md5=zJB3WS6tzcdWmKjzHnSTLA&expires=1553233302")); FrameConsumer frameConsumer = new FrameConsumer() { @Override public void consumeStreams(List<com.github.kokorin.jaffree.ffmpeg.Stream> streams) { LOGGER.debug(streams + ""); } @Override public void consume(Frame frame) { LOGGER.debug(frame + ""); } }; FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput .fromPath(videoWithAlpha) .setDuration(1_000) ) .addOutput(FrameOutput .withConsumerAlpha(frameConsumer) .disableStream(StreamType.AUDIO) ) .execute(); Assert.assertNotNull(result); } @Test public void testExceptionIsThrownIfFfmpegExitsWithError() { expectedException.expect(new StackTraceMatcher("No such file or directory")); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(ERROR_MP4)) .addOutput(new NullOutput()) .execute(); } @Test public void testCustomOutputParsing() { final AtomicBoolean loudnormReportFound = new AtomicBoolean(); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(VIDEO_MP4)) .addArguments("-af", "loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json") .addOutput(new NullOutput(false)) .setOutputListener(new OutputListener() { @Override public boolean onOutput(String line) { if (line.contains("loudnorm")) { loudnormReportFound.set(true); } return loudnormReportFound.get(); } }) .execute(); Assert.assertNotNull(result); Assert.assertTrue(loudnormReportFound.get()); } @Test public void testPipeInput() throws IOException { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result; try (InputStream inputStream = Files.newInputStream(VIDEO_MP4)) { result = FFmpeg.atPath(BIN) .addInput(PipeInput.pumpFrom(inputStream)) .addOutput(UrlOutput.toPath(outputPath)) .execute(); } Assert.assertNotNull(result); Assert.assertNotNull(result.getVideoSize()); Assert.assertTrue(getDuration(outputPath) > 10.); } @Test public void testPipeInputPartialRead() throws IOException { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result; try (InputStream inputStream = Files.newInputStream(VIDEO_MP4)) { result = FFmpeg.atPath(BIN) .addInput( PipeInput .pumpFrom(inputStream) .setDuration(15, TimeUnit.SECONDS) ) .addOutput(UrlOutput.toPath(outputPath)) .execute(); } Assert.assertNotNull(result); Assert.assertNotNull(result.getVideoSize()); Assert.assertTrue(getDuration(outputPath) > 10.); } @Test public void testPipeInputAsync() throws IOException { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(PipeInput.withSupplier(new TcpInput.Supplier() { @Override public void supplyAndClose(final OutputStream out) { Runnable runnable = new Runnable() { @Override public void run() { try (InputStream inputStream = Files.newInputStream(VIDEO_MP4); Closeable toClose = out) { IOUtils.copyLarge(inputStream, out); } catch (IOException e) { throw new RuntimeException(e); } } }; Thread thread = new Thread(runnable, "Supplier"); thread.start(); } })) .addOutput(UrlOutput.toPath(outputPath)) .execute(); Assert.assertNotNull(result); Assert.assertNotNull(result.getVideoSize()); Assert.assertTrue(getDuration(outputPath) > 10.); } @Test public void testPipeOutput() throws IOException { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result; try (OutputStream outputStream = Files.newOutputStream(outputPath, CREATE)) { result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(VIDEO_MP4)) .addOutput(PipeOutput.pumpTo(outputStream).setFormat("flv")) .setOverwriteOutput(true) .execute(); } Assert.assertNotNull(result); Assert.assertNotNull(result.getVideoSize()); Assert.assertTrue(getExactDuration(outputPath) > 10.); } @Test public void testPipeOutputAsync() throws IOException { Path tempDir = Files.createTempDirectory("jaffree"); final Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(VIDEO_MP4)) .addOutput(PipeOutput.withConsumer( new TcpOutput.Consumer() { @Override public void consumeAndClose(final InputStream in) { Runnable runnable = new Runnable() { @Override public void run() { try (OutputStream outputStream = Files.newOutputStream(outputPath, CREATE); Closeable toClose = in) { IOUtils.copyLarge(in, outputStream); } catch (IOException e) { throw new RuntimeException(e); } } }; Thread thread = new Thread(runnable, "Consumer"); thread.start(); } } ).setFormat("flv")) .setOverwriteOutput(true) .execute(); Assert.assertNotNull(result); Assert.assertNotNull(result.getVideoSize()); Assert.assertTrue(getExactDuration(outputPath) > 10.); } @Test public void testChannelInput() throws IOException { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve("channel.mp4"); try (SeekableByteChannel channel = Files.newByteChannel(VIDEO_MP4, READ)) { FFmpegResult result = FFmpeg.atPath(BIN) .addInput( new ChannelInput("testChannelInput.mp4", channel) ) .addOutput( UrlOutput.toPath(outputPath) ) .setLogLevel(LogLevel.DEBUG) .execute(); Assert.assertNotNull(result); Assert.assertNotNull(result.getVideoSize()); } Assert.assertTrue(Files.exists(outputPath)); Assert.assertTrue(Files.size(outputPath) > 1000); } @Test public void testChannelInputPartialRead() throws IOException { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve("channel.mp4"); try (SeekableByteChannel channel = Files.newByteChannel(VIDEO_MP4, READ)) { FFmpegResult result = FFmpeg.atPath(BIN) .addInput( new ChannelInput("testChannelInputPartialRead.mp4", channel) .setDuration(10, TimeUnit.SECONDS) ) .addOutput( UrlOutput.toPath(outputPath) ) .setLogLevel(LogLevel.INFO) .execute(); Assert.assertNotNull(result); Assert.assertNotNull(result.getVideoSize()); } Assert.assertTrue(Files.exists(outputPath)); Assert.assertTrue(Files.size(outputPath) > 1000); } @Test public void testChannelInputSeek() throws IOException { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve("frame.jpg"); try (SeekableByteChannel channel = Files.newByteChannel(VIDEO_MP4, READ)) { FFmpegResult result = FFmpeg.atPath(BIN) .addInput( new ChannelInput("testChannelInputSeek.mp4", channel) .setPosition(1, TimeUnit.MINUTES) ) .addOutput( UrlOutput.toPath(outputPath) .setFrameCount(StreamType.VIDEO, 1L) ) .setLogLevel(LogLevel.INFO) .execute(); Assert.assertNotNull(result); Assert.assertNotNull(result.getVideoSize()); } Assert.assertTrue(Files.exists(outputPath)); Assert.assertTrue(Files.size(outputPath) > 1000); } @Test public void testChannelOutput() throws IOException { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve("channel.mp4"); LOGGER.debug("Will write to " + outputPath); try (SeekableByteChannel channel = Files.newByteChannel(outputPath, CREATE, WRITE, READ, TRUNCATE_EXISTING)) { FFmpegResult result = FFmpeg.atPath(BIN) .addInput( UrlInput.fromPath(VIDEO_MP4) ) .addOutput( new ChannelOutput("channel.mp4", channel) ) .setOverwriteOutput(true) .setLogLevel(LogLevel.INFO) .execute(); Assert.assertNotNull(result); Assert.assertNotNull(result.getVideoSize()); } Assert.assertTrue(Files.exists(outputPath)); Assert.assertTrue(Files.size(outputPath) > 1000); } @Test public void testStreamFilters() throws IOException { Path tempDir = Files.createTempDirectory("jaffree"); Path outputPath = tempDir.resolve(VIDEO_MP4.getFileName()); LOGGER.debug("Will write to " + outputPath); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(VIDEO_MP4)) .setFilter(StreamType.VIDEO, "crop=64:48:32:32") .setFilter(StreamType.AUDIO, "aecho=0.8:0.88:6:0.4") .addOutput(UrlOutput .toPath(outputPath) ) .execute(); Assert.assertNotNull(result); Assert.assertNotNull(result.getVideoSize()); Assert.assertTrue(Files.exists(outputPath)); Assert.assertTrue(Files.size(outputPath) > 1000); } private static double getDuration(Path path) { FFprobeResult probe = FFprobe.atPath(BIN) .setShowStreams(true) .setInput(path) .execute(); Assert.assertNull(probe.getError()); double result = 0.0; for (Stream stream : probe.getStreams()) { result = Math.max(result, stream.getDuration()); } return result; } private static double getExactDuration(Path path) { final AtomicReference<FFmpegProgress> progressRef = new AtomicReference<>(); FFmpegResult result = FFmpeg.atPath(BIN) .addInput(UrlInput.fromPath(path)) .addOutput(new NullOutput()) .setProgressListener(new ProgressListener() { @Override public void onProgress(FFmpegProgress progress) { progressRef.set(progress); } }) .execute(); return progressRef.get().getTime(TimeUnit.SECONDS); } }