From b7dbdae4d5a4f7e0af7581c6c1f3b3377fd86b07 Mon Sep 17 00:00:00 2001 From: Speiger Date: Sun, 3 Nov 2024 13:13:54 +0100 Subject: [PATCH] Added Support for Duration Objects. These are simply cleaner to work with. Yes you can convert them to millseconds but honestly if there is already a timeunit option a duration option should exist too. --- .../kokorin/jaffree/ffmpeg/BaseInOut.java | 54 ++++++++++++++++++- .../kokorin/jaffree/ffmpeg/FFmpegTest.java | 38 +++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/kokorin/jaffree/ffmpeg/BaseInOut.java b/src/main/java/com/github/kokorin/jaffree/ffmpeg/BaseInOut.java index 5415953e..1272ee98 100644 --- a/src/main/java/com/github/kokorin/jaffree/ffmpeg/BaseInOut.java +++ b/src/main/java/com/github/kokorin/jaffree/ffmpeg/BaseInOut.java @@ -20,6 +20,7 @@ import com.github.kokorin.jaffree.StreamType; import com.github.kokorin.jaffree.process.ProcessHelper; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -63,7 +64,21 @@ public T setFormat(final String format) { this.format = format; return thisAsT(); } - + + /** + * When used as an input option, limit the duration of data read from the + * input file. + *

+ * When used as an output option, stop writing the output after its + * duration reaches duration. + * + * @param duration duration as Duration instance + * @return this + */ + public T setDuration(Duration duration) { + return setDuration(duration.toMillis()); + } + /** * When used as an input option, limit the duration of data read from the * input file. @@ -96,7 +111,30 @@ public T setDuration(final Number duration, final TimeUnit timeUnit) { long millis = (long) (duration.doubleValue() * timeUnit.toMillis(1)); return setDuration(millis); } - + + + /** + * When used as an input option, seeks in this input file to position. + *

+ * Note that in most formats it is not possible to seek exactly, so ffmpeg + * will seek to the closest seek point before position. + * When transcoding and -accurate_seek is enabled (the default), this extra + * segment between the seek point and position will be decoded and + * discarded. + *

+ * When doing stream copy or when -noaccurate_seek is used, + * it will be preserved. + *

+ * When used as an output option (before an output url), decodes but + * discards input until the timestamps reach position. + * + * @param duration position as a Duration instance. + * @return this + */ + public T setPosition(Duration duration) { + return setPosition(duration.toMillis()); + } + /** * When used as an input option, seeks in this input file to position. *

@@ -145,6 +183,18 @@ public T setPosition(final Number position, final TimeUnit unit) { long millis = (long) (position.doubleValue() * unit.toMillis(1)); return setPosition(millis); } + + /** + * Like the {@link #setPosition(long)} (-ss) option but relative to + * the "end of file". + * That is negative values are earlier in the file, 0 is at EOF. + * + * @param duration position as a Duration instance, relative to the EOF + * @return this + */ + public T setPositionEof(Duration duration) { + return setPositionEof(duration.toMillis()); + } /** * Like the {@link #setPosition(long)} (-ss) option but relative to diff --git a/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java b/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java index c1c99d44..b0525652 100644 --- a/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java +++ b/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java @@ -29,6 +29,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; +import java.time.Duration; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -211,6 +212,43 @@ public void testDuration() throws Exception { outputDuration = getDuration(outputPath); assertEquals(10.0, outputDuration, 0.1); } + + @Test + public void testDurationInstance() throws Exception { + Path tempDir = Files.createTempDirectory("jaffree"); + Path outputPath = tempDir.resolve(Artifacts.VIDEO_MP4.getFileName()); + + FFmpegResult result = FFmpeg.atPath(Config.FFMPEG_BIN) + .addInput(UrlInput + .fromPath(Artifacts.VIDEO_MP4) + .setDuration(Duration.ofSeconds(10)) + ) + .addOutput(UrlOutput + .toPath(outputPath) + .copyAllCodecs()) + .execute(); + + Assert.assertNotNull(result); + + double outputDuration = getDuration(outputPath); + assertEquals(10.0, outputDuration, 0.1); + + result = FFmpeg.atPath(Config.FFMPEG_BIN) + .addInput(UrlInput + .fromPath(Artifacts.VIDEO_MP4) + .setDuration(Duration.ofMinutes(1).dividedBy(6)) + ) + .setOverwriteOutput(true) + .addOutput(UrlOutput + .toPath(outputPath) + .copyAllCodecs()) + .execute(); + + Assert.assertNotNull(result); + + outputDuration = getDuration(outputPath); + assertEquals(10.0, outputDuration, 0.1); + } @Test public void testForceStopWithProgressListenerException() throws Exception {