Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for recent FFmpeg versions #394

Merged
merged 10 commits into from
Aug 25, 2024
22 changes: 14 additions & 8 deletions src/main/java/com/github/kokorin/jaffree/util/ParseUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
*/
@SuppressWarnings("checkstyle:MagicNumber")
public final class ParseUtil {

private static final String KBYTES_SUFFIX = "kB";
private static final String[] KBYTES_SUFFIXES = {"kB", "KiB"};
private static final String KBITS_PER_SECOND_SUFFIX = "kbits/s";
private static final String SPEED_SUFFIX = "x";
private static final String PERCENT_SUFFIX = "%";
Expand Down Expand Up @@ -78,33 +77,40 @@ public static Double parseDouble(final String value) {
}

/**
* Parses size in kilobytes without exception.
* Parses size in kibibytes without exception.
*
* @param value string to parse
* @return parsed long or null if value can't be parsed
*/
public static Long parseSizeInBytes(final String value) {
Long result = parseSizeInKiloBytes(value);
Long result = parseSizeInKibiBytes(value);

if (result == null) {
return null;
}

return result * 1000;
return result * 1024;
}

/**
* Parses size in kilobytes without exception.
* Parses size in kibibytes without exception.
*
* @param value string to parse
* @return parsed long or null if value can't be parsed
*/
public static Long parseSizeInKiloBytes(final String value) {
public static Long parseSizeInKibiBytes(final String value) {
if (value == null || value.isEmpty()) {
return null;
}

return parseLongWithSuffix(value.trim(), KBYTES_SUFFIX);
final String trimmedValue = value.trim();
Long result = null;

for (int i = 0; i < KBYTES_SUFFIXES.length && result == null; i++) {
result = parseLongWithSuffix(trimmedValue, KBYTES_SUFFIXES[i]);
}

return result;
}

/**
Expand Down
52 changes: 48 additions & 4 deletions src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
Expand Down Expand Up @@ -240,6 +241,43 @@ public void onProgress(FFmpegProgress progress) {
.execute();
}

@Test
public void testFrameCountingWithStreamCopyAndProgressListener() throws Exception {
final AtomicBoolean ffmpegHasStreamCopyBug = new AtomicBoolean(false);

final OutputListener outputListener = message -> {
// Don't check the frame count in at FFmpeg 6.1.x and 7.0.x due to a stream copy bug,
// which is addressed on the master branch, see:
// https://github.com/FFmpeg/FFmpeg/commit/598f541ba49cb682dcd74e86858c9a4985149e1f
if (message.contains("ffmpeg version 6.1") || message.contains("ffmpeg version 7.0")) {
ffmpegHasStreamCopyBug.set(true);
}
};

final AtomicReference<Long> frameRef = new AtomicReference<>();

final ProgressListener progressListener = new ProgressListener() {
@Override
public void onProgress(FFmpegProgress progress) {
System.out.println(progress);
frameRef.set(progress.getFrame());
}
};

final FFmpegResult result = FFmpeg.atPath(Config.FFMPEG_BIN)
.addInput(UrlInput.fromPath(Artifacts.VIDEO_NUT))
.addOutput(new NullOutput())
.setOutputListener(outputListener)
.setProgressListener(progressListener)
.execute();

if (ffmpegHasStreamCopyBug.get()) {
LOGGER.warn("Detected buggy FFmpeg version, frame count not checked");
} else {
assertNotNull(frameRef.get());
}
}

@Test
public void testForceStopWithThreadInterruption() throws Exception {
Path tempDir = Files.createTempDirectory("jaffree");
Expand Down Expand Up @@ -519,14 +557,20 @@ public void testExceptionIsThrownIfFfmpegExitsWithError() {
.addOutput(new NullOutput())
.execute();
} catch (JaffreeAbnormalExitException e) {
assertEquals("Process execution has ended with non-zero status: 1. Check logs for detailed error message.", e.getMessage());
assertEquals(1, e.getProcessErrorLogMessages().size());
assertEquals("[error] non_existent.mp4: No such file or directory", e.getProcessErrorLogMessages().get(0).message);
if ("Process execution has ended with non-zero status: 254. Check logs for detailed error message.".equals(e.getMessage())) {
// FFmpeg 6+
assertEquals(3, e.getProcessErrorLogMessages().size());
assertEquals("[error] Error opening input file non_existent.mp4.", e.getProcessErrorLogMessages().get(1).message);
} else if ("Process execution has ended with non-zero status: 1. Check logs for detailed error message.".equals(e.getMessage())) {
assertEquals(1, e.getProcessErrorLogMessages().size());
assertEquals("[error] non_existent.mp4: No such file or directory", e.getProcessErrorLogMessages().get(0).message);
} else {
fail("Unknown FFmpeg output format (update test code!)");
}
return;
}

fail("JaffreeAbnormalExitException should have been thrown!");

}

@Test
Expand Down
19 changes: 15 additions & 4 deletions src/test/java/com/github/kokorin/jaffree/util/ParseUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,25 @@ public void parseLogLevel() {
}

@Test
public void parsResult() throws Exception {
public void parseKibiByteFormats() {
final Long oldFormat = ParseUtil.parseSizeInKibiBytes("2904kB");
Assert.assertEquals(2904L, oldFormat.longValue());

final Long newFormat = ParseUtil.parseSizeInKibiBytes("2904KiB");
Assert.assertEquals(2904L, newFormat.longValue());

final Long unknownFormat = ParseUtil.parseSizeInKibiBytes("2904KB");
Assert.assertNull(unknownFormat);
}

@Test
public void parseResult() throws Exception {
String value = "video:1417kB audio:113kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown";
FFmpegResult result = ParseUtil.parseResult(value);

Assert.assertNotNull(result);
Assert.assertEquals((Long) 1_417_000L, result.getVideoSize());
Assert.assertEquals((Long) 113_000L, result.getAudioSize());
Assert.assertEquals((Long) 1_451_008L, result.getVideoSize());
Assert.assertEquals((Long) 115_712L, result.getAudioSize());
Assert.assertEquals((Long) 0L, result.getSubtitleSize());
Assert.assertEquals((Long) 0L, result.getOtherStreamsSize());
Assert.assertEquals((Long) 0L, result.getGlobalHeadersSize());
Expand Down Expand Up @@ -98,5 +110,4 @@ public void parseResultWhichDoesntContainResult() throws Exception {

Assert.assertNull(result);
}

}
Loading