diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index ab9e636d..2e75bf85 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -42,14 +42,14 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- - uses: actions/setup-java@v3
+ - uses: actions/setup-java@v4
with:
- distribution: 'adopt'
- java-version: 17
+ distribution: 'temurin'
+ java-version: 21
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -57,7 +57,7 @@ jobs:
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- - uses: actions/cache@v3
+ - uses: actions/cache@v4
with:
# be careful not to include ~/.m2/settings.xml which contains credentials
path: |
@@ -70,4 +70,4 @@ jobs:
run: bash mvnw clean install -B -DskipTests
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@v3
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b049672e..6f35ebba 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -19,12 +19,12 @@ jobs:
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-java@v3
+ - uses: actions/setup-java@v4
with:
- distribution: 'adopt'
- java-version: 11
+ distribution: 'temurin'
+ java-version: 21
- - uses: actions/cache@v3
+ - uses: actions/cache@v4
with:
# be careful not to include ~/.m2/settings.xml which contains credentials
path: |
diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml
index 972982e2..ff4d47aa 100644
--- a/.github/workflows/sonar-analysis.yml
+++ b/.github/workflows/sonar-analysis.yml
@@ -31,22 +31,22 @@ jobs:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
- distribution: 'adopt'
- java-version: 17
+ distribution: 'temurin'
+ java-version: 21
- name: Install ffmpeg on Ubuntu
run: sudo apt-get update && sudo apt-get install -y ffmpeg && ffmpeg -version
- name: Cache SonarCloud packages
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- - uses: actions/cache@v3
+ - uses: actions/cache@v4
with:
# be careful not to include ~/.m2/settings.xml which contains credentials
path: |
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index fda833c4..cf2618ad 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -25,21 +25,23 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ ubuntu-latest, macos-latest, windows-latest ]
- java-version: [ 8, 11, 17 ]
+ os:
+ - ubuntu-latest
+ - macos-latest
+ - windows-latest
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Set up JDK
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
- distribution: 'adopt'
- java-version: ${{ matrix.java-version }}
+ distribution: 'temurin'
+ java-version: 21
- name: Cache Maven Packages
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
# be careful not to include ~/.m2/settings.xml which contains credentials
path: |
@@ -49,35 +51,28 @@ jobs:
restore-keys: ${{ runner.os }}-m2
- name: Cache Test Artifacts
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: |
.artifacts
key: ${{ runner.os }}-artifacts-${{ hashFiles('**/Artifacts.java') }}
- name: Install ffmpeg on Ubuntu
- run: sudo apt-get update && sudo apt-get install -y ffmpeg && ffmpeg -version
+ run: sudo apt-get update && sudo apt-get install -y ffmpeg
if: startsWith(matrix.os, 'ubuntu')
-
+
- name: Install ffmpeg on MacOS
- run: brew install ffmpeg && ls -lhtr /usr/local/opt/ffmpeg/ && ffmpeg -version
+ run: brew install ffmpeg
if: startsWith(matrix.os, 'macos')
- name: Install ffmpeg on Windows
- run: choco install ffmpeg && ffmpeg -version
+ run: choco install ffmpeg
if: startsWith(matrix.os, 'windows')
- - name: Build on Ubuntu
- run: bash mvnw clean package -B
- if: startsWith(matrix.os, 'ubuntu')
+ - run: ffmpeg -version
- - name: Build on MacOS
- run: bash mvnw clean package -B
- if: startsWith(matrix.os, 'macos')
-
- - name: Build on Windows
+ - name: Build and test
run: ./mvnw clean package -B
- if: startsWith(matrix.os, 'windows')
test-release:
runs-on: ubuntu-latest
@@ -85,15 +80,15 @@ jobs:
- uses: actions/checkout@v4
- name: Set up JDK
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
- distribution: 'adopt'
- java-version: 11
+ distribution: 'temurin'
+ java-version: 21
- name: Set up RANDOM GPG key
run: gpg --quick-generate-key --batch --passphrase '' test42
- - uses: actions/cache@v3
+ - uses: actions/cache@v4
with:
# be careful not to include ~/.m2/settings.xml which contains credentials
path: |
diff --git a/pom.xml b/pom.xml
index 02eb3274..86bd55e9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,13 +70,13 @@
com.grack
nanojson
- 1.7
+ 1.9
ch.qos.logback
logback-classic
- 1.2.10
+ 1.5.7
test
@@ -150,7 +150,7 @@
org.apache.maven.plugins
maven-source-plugin
- 3.2.1
+ 3.3.1
attach-sources
@@ -180,7 +180,7 @@
org.apache.maven.plugins
maven-gpg-plugin
- 3.0.1
+ 3.2.5
sign-artifacts
@@ -241,7 +241,7 @@
org.apache.maven.plugins
maven-checkstyle-plugin
- 3.1.2
+ 3.5.0
4
checkstyle.xml
diff --git a/src/main/java/com/github/kokorin/jaffree/util/ParseUtil.java b/src/main/java/com/github/kokorin/jaffree/util/ParseUtil.java
index 604cb23f..88cb3e37 100644
--- a/src/main/java/com/github/kokorin/jaffree/util/ParseUtil.java
+++ b/src/main/java/com/github/kokorin/jaffree/util/ParseUtil.java
@@ -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 = "%";
@@ -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;
}
/**
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 fdc4e57f..c1c99d44 100644
--- a/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java
+++ b/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java
@@ -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;
@@ -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 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");
@@ -519,14 +557,24 @@ 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: -2. Check logs for detailed error message.".equals(e.getMessage())) {
+ // FFmpeg 7
+ 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!): " + e.getMessage());
+ }
return;
}
fail("JaffreeAbnormalExitException should have been thrown!");
-
}
@Test
diff --git a/src/test/java/com/github/kokorin/jaffree/util/ParseUtilTest.java b/src/test/java/com/github/kokorin/jaffree/util/ParseUtilTest.java
index e932cf7c..111a89a1 100644
--- a/src/test/java/com/github/kokorin/jaffree/util/ParseUtilTest.java
+++ b/src/test/java/com/github/kokorin/jaffree/util/ParseUtilTest.java
@@ -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());
@@ -98,5 +110,4 @@ public void parseResultWhichDoesntContainResult() throws Exception {
Assert.assertNull(result);
}
-
}
\ No newline at end of file