diff --git a/.gitignore b/.gitignore
index e43b0f9..b25a863 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
.DS_Store
+.vscode
+.history
diff --git a/README.md b/README.md
index 23ddcab..261538c 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,14 @@
# FFmpegKit Test
-Test applications for [FFmpegKit](https://github.com/tanersener/ffmpeg-kit).
+Test applications for [FFmpegKit](https://github.com/arthenica/ffmpeg-kit).
-- `Android` under the [android](https://github.com/tanersener/ffmpeg-kit-test/tree/main/android) folder
-- `Flutter` under the [flutter](https://github.com/tanersener/ffmpeg-kit-test/tree/main/flutter) folder
-- `iOS` under the [ios](https://github.com/tanersener/ffmpeg-kit-test/tree/main/ios) folder
-- `macOS` under the [macos](https://github.com/tanersener/ffmpeg-kit-test/tree/main/macos) folder
-- `React Native` under the [react-native](https://github.com/tanersener/ffmpeg-kit-test/tree/main/react-native) folder
-- `tvOS` under the [tvos](https://github.com/tanersener/ffmpeg-kit-test/tree/main/tvos) folder
+- `Android` under the [android](https://github.com/arthenica/ffmpeg-kit-test/tree/main/android) folder
+- `Flutter` under the [flutter](https://github.com/arthenica/ffmpeg-kit-test/tree/main/flutter) folder
+- `iOS` under the [ios](https://github.com/arthenica/ffmpeg-kit-test/tree/main/ios) folder
+- `Linux` under the [linux](https://github.com/arthenica/ffmpeg-kit-test/tree/main/linux) folder
+- `macOS` under the [macos](https://github.com/arthenica/ffmpeg-kit-test/tree/main/macos) folder
+- `React Native` under the [react-native](https://github.com/arthenica/ffmpeg-kit-test/tree/main/react-native) folder
+- `tvOS` under the [tvos](https://github.com/arthenica/ffmpeg-kit-test/tree/main/tvos) folder
All applications are identical and supports command execution, video encoding, accessing https urls, encoding audio,
burning subtitles, video stabilisation, pipe operations, concurrent command execution.
@@ -19,41 +20,50 @@ demonstrate how SAF uris can be used with `FFmpegKit`.
Test applications are tagged with `ffmpeg-kit` release they depend on.
-| Platform | FFmpegKit Version | Tag |
-| :----: |:-----------------:|:----------------------------------------------------------------------------------:|
-| React Native | 4.5.2 | [4.5.2](https://github.com/tanersener/ffmpeg-kit-test/tree/react.native.v4.5.2) |
-| Flutter | 4.5.1 | [4.5.1](https://github.com/tanersener/ffmpeg-kit-test/tree/flutter.v4.5.1) |
-| Flutter | 4.5.1-LTS | [4.5.1-LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/flutter.v4.5.1.lts) |
-| React Native | 4.5.1 | [4.5.1](https://github.com/tanersener/ffmpeg-kit-test/tree/react.native.v4.5.1) |
-| Android | 4.5.1 | [4.5.1](https://github.com/tanersener/ffmpeg-kit-test/tree/android.v4.5.1) |
-| Android | 4.5.1.LTS | [4.5.1.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/android.v4.5.1.lts) |
-| iOS | 4.5.1 | [4.5.1](https://github.com/tanersener/ffmpeg-kit-test/tree/ios.v4.5.1) |
-| iOS | 4.5.1.LTS | [4.5.1.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/ios.v4.5.1.lts) |
-| macOS | 4.5.1 | [4.5.1](https://github.com/tanersener/ffmpeg-kit-test/tree/macos.v4.5.1) |
-| macOS | 4.5.1.LTS | [4.5.1.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/macos.v4.5.1.lts) |
-| tvOS | 4.5.1 | [4.5.1](https://github.com/tanersener/ffmpeg-kit-test/tree/tvos.v4.5.1) |
-| tvOS | 4.5.1.LTS | [4.5.1.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/tvos.v4.5.1.lts) |
+| Platform | FFmpegKit Version | Tag |
+| :----: |:-----------------:|:---------------------------------------------------------------------------------:|
+| Android | 5.1 | [5.1](https://github.com/arthenica/ffmpeg-kit-test/tree/android.v5.1) |
+| Android | 5.1.LTS | [5.1.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/android.v5.1.lts) |
+| iOS | 5.1 | [5.1](https://github.com/arthenica/ffmpeg-kit-test/tree/ios.v5.1) |
+| iOS | 5.1.LTS | [5.1.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/ios.v5.1.lts) |
+| macOS | 5.1 | [5.1](https://github.com/arthenica/ffmpeg-kit-test/tree/macos.v5.1) |
+| macOS | 5.1.LTS | [5.1.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/macos.v5.1.lts) |
+| tvOS | 5.1 | [5.1](https://github.com/arthenica/ffmpeg-kit-test/tree/tvos.v5.1) |
+| tvOS | 5.1.LTS | [5.1.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/tvos.v5.1.lts) |
+| - | - | - |
+| React Native | 4.5.2 | [4.5.2](https://github.com/arthenica/ffmpeg-kit-test/tree/react.native.v4.5.2) |
+| Flutter | 4.5.1 | [4.5.1](https://github.com/arthenica/ffmpeg-kit-test/tree/flutter.v4.5.1) |
+| Flutter | 4.5.1-LTS | [4.5.1-LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/flutter.v4.5.1.lts) |
+| React Native | 4.5.1 | [4.5.1](https://github.com/arthenica/ffmpeg-kit-test/tree/react.native.v4.5.1) |
+| Android | 4.5.1 | [4.5.1](https://github.com/arthenica/ffmpeg-kit-test/tree/android.v4.5.1) |
+| Android | 4.5.1.LTS | [4.5.1.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/android.v4.5.1.lts) |
+| iOS | 4.5.1 | [4.5.1](https://github.com/arthenica/ffmpeg-kit-test/tree/ios.v4.5.1) |
+| iOS | 4.5.1.LTS | [4.5.1.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/ios.v4.5.1.lts) |
+| macOS | 4.5.1 | [4.5.1](https://github.com/arthenica/ffmpeg-kit-test/tree/macos.v4.5.1) |
+| macOS | 4.5.1.LTS | [4.5.1.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/macos.v4.5.1.lts) |
+| tvOS | 4.5.1 | [4.5.1](https://github.com/arthenica/ffmpeg-kit-test/tree/tvos.v4.5.1) |
+| tvOS | 4.5.1.LTS | [4.5.1.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/tvos.v4.5.1.lts) |
| - | - | - |
-| Flutter | 4.5.0 | [4.5.0](https://github.com/tanersener/ffmpeg-kit-test/tree/flutter.v4.5.0) |
-| Flutter | 4.5.0-LTS | [4.5.0-LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/flutter.v4.5.0.lts) |
-| React Native | 4.5.0 | [4.5.0](https://github.com/tanersener/ffmpeg-kit-test/tree/react.native.v4.5.0) |
-| Android | 4.5 | [4.5](https://github.com/tanersener/ffmpeg-kit-test/tree/android.v4.5) |
-| Android | 4.5.LTS | [4.5.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/android.v4.5.lts) |
-| iOS | 4.5 | [4.5](https://github.com/tanersener/ffmpeg-kit-test/tree/ios.v4.5) |
-| iOS | 4.5.LTS | [4.5.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/ios.v4.5.lts) |
-| macOS | 4.5 | [4.5](https://github.com/tanersener/ffmpeg-kit-test/tree/macos.v4.5) |
-| macOS | 4.5.LTS | [4.5.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/macos.v4.5.lts) |
-| tvOS | 4.5 | [4.5](https://github.com/tanersener/ffmpeg-kit-test/tree/tvos.v4.5) |
-| tvOS | 4.5.LTS | [4.5.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/tvos.v4.5.lts) |
+| Flutter | 4.5.0 | [4.5.0](https://github.com/arthenica/ffmpeg-kit-test/tree/flutter.v4.5.0) |
+| Flutter | 4.5.0-LTS | [4.5.0-LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/flutter.v4.5.0.lts) |
+| React Native | 4.5.0 | [4.5.0](https://github.com/arthenica/ffmpeg-kit-test/tree/react.native.v4.5.0) |
+| Android | 4.5 | [4.5](https://github.com/arthenica/ffmpeg-kit-test/tree/android.v4.5) |
+| Android | 4.5.LTS | [4.5.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/android.v4.5.lts) |
+| iOS | 4.5 | [4.5](https://github.com/arthenica/ffmpeg-kit-test/tree/ios.v4.5) |
+| iOS | 4.5.LTS | [4.5.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/ios.v4.5.lts) |
+| macOS | 4.5 | [4.5](https://github.com/arthenica/ffmpeg-kit-test/tree/macos.v4.5) |
+| macOS | 4.5.LTS | [4.5.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/macos.v4.5.lts) |
+| tvOS | 4.5 | [4.5](https://github.com/arthenica/ffmpeg-kit-test/tree/tvos.v4.5) |
+| tvOS | 4.5.LTS | [4.5.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/tvos.v4.5.lts) |
| - | - | - |
-| Android | 4.4 | [4.4](https://github.com/tanersener/ffmpeg-kit-test/tree/android.v4.4) |
-| Android | 4.4.LTS | [4.4.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/android.v4.4.lts) |
-| iOS | 4.4 | [4.4](https://github.com/tanersener/ffmpeg-kit-test/tree/ios.v4.4) |
-| iOS | 4.4.LTS | [4.4.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/ios.v4.4.lts) |
-| macOS | 4.4 | [4.4](https://github.com/tanersener/ffmpeg-kit-test/tree/macos.v4.4) |
-| macOS | 4.4.LTS | [4.4.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/macos.v4.4.lts) |
-| tvOS | 4.4 | [4.4](https://github.com/tanersener/ffmpeg-kit-test/tree/tvos.v4.4) |
-| tvOS | 4.4.LTS | [4.4.LTS](https://github.com/tanersener/ffmpeg-kit-test/tree/tvos.v4.4.lts) |
+| Android | 4.4 | [4.4](https://github.com/arthenica/ffmpeg-kit-test/tree/android.v4.4) |
+| Android | 4.4.LTS | [4.4.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/android.v4.4.lts) |
+| iOS | 4.4 | [4.4](https://github.com/arthenica/ffmpeg-kit-test/tree/ios.v4.4) |
+| iOS | 4.4.LTS | [4.4.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/ios.v4.4.lts) |
+| macOS | 4.4 | [4.4](https://github.com/arthenica/ffmpeg-kit-test/tree/macos.v4.4) |
+| macOS | 4.4.LTS | [4.4.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/macos.v4.4.lts) |
+| tvOS | 4.4 | [4.4](https://github.com/arthenica/ffmpeg-kit-test/tree/tvos.v4.4) |
+| tvOS | 4.4.LTS | [4.4.LTS](https://github.com/arthenica/ffmpeg-kit-test/tree/tvos.v4.4.lts) |
### License
diff --git a/android/README.md b/android/README.md
index 7197e3a..c77db2f 100644
--- a/android/README.md
+++ b/android/README.md
@@ -1,3 +1,3 @@
# FFmpegKit Android
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
index 7be6386..e76cba4 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,10 +1,10 @@
buildscript {
repositories {
google()
- jcenter()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.2.2'
+ classpath 'com.android.tools.build:gradle:7.3.0'
}
}
@@ -12,7 +12,6 @@ allprojects {
repositories {
mavenCentral()
google()
- jcenter()
}
}
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 28ff446..ae04661 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/android/test-app-local-dependency/build.gradle b/android/test-app-local-dependency/build.gradle
index 556c649..0f89dab 100644
--- a/android/test-app-local-dependency/build.gradle
+++ b/android/test-app-local-dependency/build.gradle
@@ -9,13 +9,14 @@ android {
keyPassword 'android'
}
}
- compileSdkVersion 30
+ namespace 'com.arthenica.ffmpegkit.test'
+ compileSdk 31
defaultConfig {
applicationId "com.arthenica.ffmpegkit.test"
- minSdkVersion 24
- targetSdkVersion 30
- versionCode 240451
- versionName "4.5.1"
+ minSdk 24
+ targetSdk 31
+ versionCode 240510
+ versionName "5.1"
}
buildTypes {
debug {
@@ -53,7 +54,7 @@ android.applicationVariants.all { variant ->
dependencies {
// implementation files('../../../ffmpeg-kit/prebuilt/bundle-android-aar-lts/ffmpeg-kit/ffmpeg-kit.aar')
implementation files('../../../ffmpeg-kit/prebuilt/bundle-android-aar/ffmpeg-kit/ffmpeg-kit.aar')
- implementation 'com.arthenica:smart-exception-java:0.1.1'
- implementation 'androidx.appcompat:appcompat:1.3.1'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ implementation 'com.arthenica:smart-exception-java:0.2.1'
+ implementation 'androidx.appcompat:appcompat:1.4.2'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}
diff --git a/android/test-app-local-dependency/src/main/AndroidManifest.xml b/android/test-app-local-dependency/src/main/AndroidManifest.xml
index ff03cf3..3d4338d 100644
--- a/android/test-app-local-dependency/src/main/AndroidManifest.xml
+++ b/android/test-app-local-dependency/src/main/AndroidManifest.xml
@@ -1,7 +1,6 @@
+ xmlns:tools="http://schemas.android.com/tools">
@@ -21,7 +20,9 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
-
+
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/AudioTabFragment.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/AudioTabFragment.java
index 2c7c11f..19df119 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/AudioTabFragment.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/AudioTabFragment.java
@@ -148,7 +148,7 @@ public void encodeAudio() {
clearOutput();
- android.util.Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ android.util.Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/CommandTabFragment.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/CommandTabFragment.java
index f10dbf7..a0e1885 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/CommandTabFragment.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/CommandTabFragment.java
@@ -102,7 +102,7 @@ public void runFFmpeg() {
android.util.Log.d(MainActivity.TAG, "Testing FFmpeg COMMAND asynchronously.");
- android.util.Log.d(MainActivity.TAG, String.format("FFmpeg process started with arguments:\n'%s'", ffmpegCommand));
+ android.util.Log.d(MainActivity.TAG, String.format("FFmpeg process started with arguments: '%s'", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -147,9 +147,9 @@ public void runFFprobe() {
android.util.Log.d(MainActivity.TAG, "Testing FFprobe COMMAND asynchronously.");
- android.util.Log.d(MainActivity.TAG, String.format("FFprobe process started with arguments:\n'%s'", ffprobeCommand));
+ android.util.Log.d(MainActivity.TAG, String.format("FFprobe process started with arguments: '%s'", ffprobeCommand));
- FFprobeSession session = new FFprobeSession(FFmpegKitConfig.parseArguments(ffprobeCommand), new FFprobeSessionCompleteCallback() {
+ FFprobeSession session = FFprobeSession.create(FFmpegKitConfig.parseArguments(ffprobeCommand), new FFprobeSessionCompleteCallback() {
@Override
public void apply(final FFprobeSession session) {
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/ConcurrentExecutionTabFragment.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/ConcurrentExecutionTabFragment.java
index 736d4d7..359463a 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/ConcurrentExecutionTabFragment.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/ConcurrentExecutionTabFragment.java
@@ -186,7 +186,7 @@ public void encodeVideo(final int buttonNumber) {
final String ffmpegCommand = Video.generateEncodeVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath(), "mpeg4", "");
- Log.d(TAG, String.format("FFmpeg process starting for button %d with arguments\n'%s'.", buttonNumber, ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process starting for button %d with arguments: '%s'.", buttonNumber, ffmpegCommand));
final FFmpegSession session = FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -218,6 +218,7 @@ public void apply(final FFmpegSession session) {
break;
default: {
sessionId3 = sessionId;
+ FFmpegKitConfig.setSessionHistorySize(3);
}
}
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/HttpsTabFragment.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/HttpsTabFragment.java
index 5854776..7ae706d 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/HttpsTabFragment.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/HttpsTabFragment.java
@@ -54,11 +54,11 @@ public class HttpsTabFragment extends Fragment {
public static final String HTTPS_TEST_FAIL_URL = "https://download2.blender.org/peach/trailer/trailer_1080p.ogg";
- public static final String HTTPS_TEST_RANDOM_URL_1 = "https://file-examples-com.github.io/uploads/2018/04/file_example_MOV_640_800kB.mov";
+ public static final String HTTPS_TEST_RANDOM_URL_1 = "https://filesamples.com/samples/video/mov/sample_640x360.mov";
- public static final String HTTPS_TEST_RANDOM_URL_2 = "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3";
+ public static final String HTTPS_TEST_RANDOM_URL_2 = "https://filesamples.com/samples/audio/mp3/sample3.mp3";
- public static final String HTTPS_TEST_RANDOM_URL_3 = "https://file-examples-com.github.io/uploads/2020/03/file_example_WEBP_50kB.webp";
+ public static final String HTTPS_TEST_RANDOM_URL_3 = "https://filesamples.com/samples/image/webp/sample1.webp";
private static final Random testUrlRandom = new Random();
private static final Object outputLock = new Object();
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/MainActivity.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/MainActivity.java
index 8fec024..a1a63da 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/MainActivity.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/MainActivity.java
@@ -117,7 +117,7 @@ protected void onCreate(final Bundle savedInstanceState) {
}
try {
- registerAppFont();
+ registerApplicationFonts();
Log.d(TAG, "Application fonts registered.");
} catch (final IOException e) {
Log.e(TAG, String.format("Font registration failed.%s.", Exceptions.getStackTraceString(e)));
@@ -166,7 +166,7 @@ public static void addUIAction(final Runnable runnable) {
handler.post(runnable);
}
- protected void registerAppFont() throws IOException {
+ protected void registerApplicationFonts() throws IOException {
final File cacheDirectory = getCacheDir();
final File fontDirectory = new File(cacheDirectory, "fonts");
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/OtherTabFragment.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/OtherTabFragment.java
index d8322dc..b530fd7 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/OtherTabFragment.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/OtherTabFragment.java
@@ -148,7 +148,7 @@ public void apply(FFmpegSession session) {
String chromaprintCommand = String.format("-hide_banner -y -i %s -f chromaprint -fp_format 2 %s", audioSampleFile, getChromaprintOutputFile().getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", chromaprintCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", chromaprintCommand));
FFmpegKit.executeAsync(chromaprintCommand, new FFmpegSessionCompleteCallback() {
@@ -184,7 +184,7 @@ protected void testDav1d() {
final String ffmpegCommand = String.format("-hide_banner -y -i %s %s", DAV1D_TEST_DEFAULT_URL, getDav1dOutputFile().getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -208,7 +208,7 @@ protected void testWebp() {
final String ffmpegCommand = String.format("-hide_banner -y -i %s %s", imageFile.getAbsolutePath(), outputFile.getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -242,7 +242,7 @@ protected void testZscale() {
final String ffmpegCommand = Video.generateZscaleVideoScript(videoFile.getAbsolutePath(), zscaledVideoFile.getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/PipeTabFragment.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/PipeTabFragment.java
index 2fd2bea..3b8fe44 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/PipeTabFragment.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/PipeTabFragment.java
@@ -158,7 +158,7 @@ public void createVideo() {
final String ffmpegCommand = Video.generateCreateVideoWithPipesScript(pipe1, pipe2, pipe3, videoFile.getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -246,20 +246,18 @@ protected void showProgressDialog() {
}
protected void updateProgressDialog() {
- if (statistics == null) {
+ if (statistics == null || statistics.getTime() < 0) {
return;
}
int timeInMilliseconds = this.statistics.getTime();
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
+ String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
- TextView textView = progressDialog.findViewById(R.id.progressDialogText);
- if (textView != null) {
- textView.setText(String.format("Creating video: %% %s.", completePercentage));
- }
+ TextView textView = progressDialog.findViewById(R.id.progressDialogText);
+ if (textView != null) {
+ textView.setText(String.format("Creating video: %% %s.", completePercentage));
}
}
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/Popup.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/Popup.java
index c4fc77b..5dd7491 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/Popup.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/Popup.java
@@ -23,12 +23,12 @@
package com.arthenica.ffmpegkit.test;
import android.content.Context;
-import android.widget.Toast;
public class Popup {
public static void show(final Context context, final String message) {
- Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+ // DISABLED DUE TO THE java.io.IOException: Failed to load asset path ERROR ON API LEVEL 31
+ // Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/SafTabFragment.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/SafTabFragment.java
index e7c9dad..96d0469 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/SafTabFragment.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/SafTabFragment.java
@@ -148,7 +148,7 @@ private void runFFprobe() {
Log.d(TAG, "Testing FFprobe COMMAND synchronously.");
- Log.d(TAG, String.format("FFprobe process started with arguments\n'%s'", ffprobeCommand));
+ Log.d(TAG, String.format("FFprobe process started with arguments: '%s'", ffprobeCommand));
final FFprobeSession session = FFprobeKit.execute(ffprobeCommand);
@@ -178,7 +178,7 @@ private void encodeVideo() {
final String ffmpegCommand = Video.generateEncodeVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoPath, selectedCodec, getCustomOptions(selectedCodec));
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegSession session = FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -332,20 +332,18 @@ private void showProgressDialog() {
}
private void updateProgressDialog() {
- if (statistics == null) {
+ if (statistics == null || statistics.getTime() < 0) {
return;
}
int timeInMilliseconds = this.statistics.getTime();
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
+ String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
- TextView textView = progressDialog.findViewById(R.id.progressDialogText);
- if (textView != null) {
- textView.setText(String.format("Encoding video: %% %s.", completePercentage));
- }
+ TextView textView = progressDialog.findViewById(R.id.progressDialogText);
+ if (textView != null) {
+ textView.setText(String.format("Encoding video: %% %s.", completePercentage));
}
}
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/SubtitleTabFragment.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/SubtitleTabFragment.java
index fec5879..06009d9 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/SubtitleTabFragment.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/SubtitleTabFragment.java
@@ -152,7 +152,7 @@ public void burnSubtitles() {
final String ffmpegCommand = Video.generateEncodeVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath(), "mpeg4", "");
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
state = State.CREATING;
@@ -177,7 +177,7 @@ public void run() {
showBurnProgressDialog();
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", burnSubtitlesCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", burnSubtitlesCommand));
state = State.BURNING;
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/VidStabTabFragment.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/VidStabTabFragment.java
index 21b6e4b..2afe475 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/VidStabTabFragment.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/VidStabTabFragment.java
@@ -101,6 +101,10 @@ public void apply(final com.arthenica.ffmpegkit.Log log) {
});
}
+ public void disableStatisticsCallback() {
+ FFmpegKitConfig.enableStatisticsCallback(null);
+ }
+
public void stabilizeVideo() {
final File image1File = new File(requireContext().getCacheDir(), "machupicchu.jpg");
final File image2File = new File(requireContext().getCacheDir(), "pyramid.jpg");
@@ -135,7 +139,7 @@ public void stabilizeVideo() {
final String ffmpegCommand = Video.generateShakingVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -157,7 +161,7 @@ public void run() {
showStabilizeProgressDialog();
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", analyzeVideoCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", analyzeVideoCommand));
FFmpegKit.executeAsync(analyzeVideoCommand, new FFmpegSessionCompleteCallback() {
@@ -168,7 +172,7 @@ public void apply(final FFmpegSession secondSession) {
if (ReturnCode.isSuccess(secondSession.getReturnCode())) {
final String stabilizeVideoCommand = String.format("-y -i %s -vf vidstabtransform=smoothing=30:input=%s -c:v mpeg4 %s", videoFile.getAbsolutePath(), shakeResultsFile.getAbsolutePath(), stabilizedVideoFile.getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", stabilizeVideoCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", stabilizeVideoCommand));
FFmpegKit.executeAsync(stabilizeVideoCommand, new FFmpegSessionCompleteCallback() {
@@ -280,6 +284,7 @@ public File getStabilizedVideoFile() {
public void setActive() {
Log.i(MainActivity.TAG, "VidStab Tab Activated");
enableLogCallback();
+ disableStatisticsCallback();
Popup.show(requireContext(), getString(R.string.vidstab_test_tooltip_text));
}
diff --git a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/VideoTabFragment.java b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/VideoTabFragment.java
index ab25178..53a0e68 100644
--- a/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/VideoTabFragment.java
+++ b/android/test-app-local-dependency/src/main/java/com/arthenica/ffmpegkit/test/VideoTabFragment.java
@@ -144,7 +144,7 @@ public void encodeVideo() {
final String ffmpegCommand = Video.generateEncodeVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath(), getSelectedVideoCodec(), getPixelFormat(), getCustomOptions());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
final FFmpegSession session = FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -340,20 +340,18 @@ protected void showProgressDialog() {
}
protected void updateProgressDialog() {
- if (statistics == null) {
+ if (statistics == null || statistics.getTime() < 0) {
return;
}
int timeInMilliseconds = this.statistics.getTime();
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
+ String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
- TextView textView = progressDialog.findViewById(R.id.progressDialogText);
- if (textView != null) {
- textView.setText(String.format("Encoding video: %% %s.", completePercentage));
- }
+ TextView textView = progressDialog.findViewById(R.id.progressDialogText);
+ if (textView != null) {
+ textView.setText(String.format("Encoding video: %% %s.", completePercentage));
}
}
diff --git a/android/test-app-local-dependency/src/main/res/values/tooltips.xml b/android/test-app-local-dependency/src/main/res/values/tooltips.xml
index 17b5b1a..f6fd6af 100644
--- a/android/test-app-local-dependency/src/main/res/values/tooltips.xml
+++ b/android/test-app-local-dependency/src/main/res/values/tooltips.xml
@@ -1,12 +1,12 @@
- Enter an FFmpeg command without \'ffmpeg\' at the beginning and click one of the RUN buttons
- Select a video codec and press ENCODE button
+ Enter a command without ffmpeg/ffprobe at the beginning and click one of the RUN buttons
+ Select a video codec and press the ENCODE button
Enter the https url of a media file and click the button
- Select an audio codec and press ENCODE button
+ Select an audio codec and press the ENCODE button
Click the button to burn subtitles. Created video will play inside the frame below
Click the button to stabilize video. Original video will play above and stabilized video will play below
Click the button to create a video using pipe redirection. Created video will play inside the frame below
Use ENCODE and CANCEL buttons to start/stop multiple execution
Use system file picker to test scoped storage extension
- Select a test and press RUN button
+ Select a test and press the RUN button
diff --git a/android/test-app-maven-central/build.gradle b/android/test-app-maven-central/build.gradle
index dc02eb1..9e24103 100644
--- a/android/test-app-maven-central/build.gradle
+++ b/android/test-app-maven-central/build.gradle
@@ -9,13 +9,14 @@ android {
keyPassword 'android'
}
}
- compileSdkVersion 30
+ namespace 'com.arthenica.ffmpegkit.test'
+ compileSdk 31
defaultConfig {
applicationId "com.arthenica.ffmpegkit.test"
- minSdkVersion 24
- targetSdkVersion 30
- versionCode 240451
- versionName "4.5.1"
+ minSdk 24
+ targetSdk 31
+ versionCode 240510
+ versionName "5.1"
}
buildTypes {
debug {
@@ -51,7 +52,7 @@ android.applicationVariants.all { variant ->
}
dependencies {
- implementation 'com.arthenica:ffmpeg-kit-full:4.5.1-1.LTS'
- implementation 'androidx.appcompat:appcompat:1.3.1'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ implementation 'com.arthenica:ffmpeg-kit-full:5.1'
+ implementation 'androidx.appcompat:appcompat:1.4.2'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}
diff --git a/android/test-app-maven-central/src/main/AndroidManifest.xml b/android/test-app-maven-central/src/main/AndroidManifest.xml
index ff03cf3..3d4338d 100644
--- a/android/test-app-maven-central/src/main/AndroidManifest.xml
+++ b/android/test-app-maven-central/src/main/AndroidManifest.xml
@@ -1,7 +1,6 @@
+ xmlns:tools="http://schemas.android.com/tools">
@@ -21,7 +20,9 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
-
+
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/AudioTabFragment.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/AudioTabFragment.java
index 2c7c11f..19df119 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/AudioTabFragment.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/AudioTabFragment.java
@@ -148,7 +148,7 @@ public void encodeAudio() {
clearOutput();
- android.util.Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ android.util.Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/CommandTabFragment.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/CommandTabFragment.java
index f10dbf7..a0e1885 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/CommandTabFragment.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/CommandTabFragment.java
@@ -102,7 +102,7 @@ public void runFFmpeg() {
android.util.Log.d(MainActivity.TAG, "Testing FFmpeg COMMAND asynchronously.");
- android.util.Log.d(MainActivity.TAG, String.format("FFmpeg process started with arguments:\n'%s'", ffmpegCommand));
+ android.util.Log.d(MainActivity.TAG, String.format("FFmpeg process started with arguments: '%s'", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -147,9 +147,9 @@ public void runFFprobe() {
android.util.Log.d(MainActivity.TAG, "Testing FFprobe COMMAND asynchronously.");
- android.util.Log.d(MainActivity.TAG, String.format("FFprobe process started with arguments:\n'%s'", ffprobeCommand));
+ android.util.Log.d(MainActivity.TAG, String.format("FFprobe process started with arguments: '%s'", ffprobeCommand));
- FFprobeSession session = new FFprobeSession(FFmpegKitConfig.parseArguments(ffprobeCommand), new FFprobeSessionCompleteCallback() {
+ FFprobeSession session = FFprobeSession.create(FFmpegKitConfig.parseArguments(ffprobeCommand), new FFprobeSessionCompleteCallback() {
@Override
public void apply(final FFprobeSession session) {
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/ConcurrentExecutionTabFragment.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/ConcurrentExecutionTabFragment.java
index 736d4d7..359463a 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/ConcurrentExecutionTabFragment.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/ConcurrentExecutionTabFragment.java
@@ -186,7 +186,7 @@ public void encodeVideo(final int buttonNumber) {
final String ffmpegCommand = Video.generateEncodeVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath(), "mpeg4", "");
- Log.d(TAG, String.format("FFmpeg process starting for button %d with arguments\n'%s'.", buttonNumber, ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process starting for button %d with arguments: '%s'.", buttonNumber, ffmpegCommand));
final FFmpegSession session = FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -218,6 +218,7 @@ public void apply(final FFmpegSession session) {
break;
default: {
sessionId3 = sessionId;
+ FFmpegKitConfig.setSessionHistorySize(3);
}
}
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/HttpsTabFragment.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/HttpsTabFragment.java
index 5854776..7ae706d 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/HttpsTabFragment.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/HttpsTabFragment.java
@@ -54,11 +54,11 @@ public class HttpsTabFragment extends Fragment {
public static final String HTTPS_TEST_FAIL_URL = "https://download2.blender.org/peach/trailer/trailer_1080p.ogg";
- public static final String HTTPS_TEST_RANDOM_URL_1 = "https://file-examples-com.github.io/uploads/2018/04/file_example_MOV_640_800kB.mov";
+ public static final String HTTPS_TEST_RANDOM_URL_1 = "https://filesamples.com/samples/video/mov/sample_640x360.mov";
- public static final String HTTPS_TEST_RANDOM_URL_2 = "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3";
+ public static final String HTTPS_TEST_RANDOM_URL_2 = "https://filesamples.com/samples/audio/mp3/sample3.mp3";
- public static final String HTTPS_TEST_RANDOM_URL_3 = "https://file-examples-com.github.io/uploads/2020/03/file_example_WEBP_50kB.webp";
+ public static final String HTTPS_TEST_RANDOM_URL_3 = "https://filesamples.com/samples/image/webp/sample1.webp";
private static final Random testUrlRandom = new Random();
private static final Object outputLock = new Object();
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/MainActivity.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/MainActivity.java
index 8fec024..a1a63da 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/MainActivity.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/MainActivity.java
@@ -117,7 +117,7 @@ protected void onCreate(final Bundle savedInstanceState) {
}
try {
- registerAppFont();
+ registerApplicationFonts();
Log.d(TAG, "Application fonts registered.");
} catch (final IOException e) {
Log.e(TAG, String.format("Font registration failed.%s.", Exceptions.getStackTraceString(e)));
@@ -166,7 +166,7 @@ public static void addUIAction(final Runnable runnable) {
handler.post(runnable);
}
- protected void registerAppFont() throws IOException {
+ protected void registerApplicationFonts() throws IOException {
final File cacheDirectory = getCacheDir();
final File fontDirectory = new File(cacheDirectory, "fonts");
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/OtherTabFragment.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/OtherTabFragment.java
index d8322dc..b530fd7 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/OtherTabFragment.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/OtherTabFragment.java
@@ -148,7 +148,7 @@ public void apply(FFmpegSession session) {
String chromaprintCommand = String.format("-hide_banner -y -i %s -f chromaprint -fp_format 2 %s", audioSampleFile, getChromaprintOutputFile().getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", chromaprintCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", chromaprintCommand));
FFmpegKit.executeAsync(chromaprintCommand, new FFmpegSessionCompleteCallback() {
@@ -184,7 +184,7 @@ protected void testDav1d() {
final String ffmpegCommand = String.format("-hide_banner -y -i %s %s", DAV1D_TEST_DEFAULT_URL, getDav1dOutputFile().getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -208,7 +208,7 @@ protected void testWebp() {
final String ffmpegCommand = String.format("-hide_banner -y -i %s %s", imageFile.getAbsolutePath(), outputFile.getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -242,7 +242,7 @@ protected void testZscale() {
final String ffmpegCommand = Video.generateZscaleVideoScript(videoFile.getAbsolutePath(), zscaledVideoFile.getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/PipeTabFragment.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/PipeTabFragment.java
index 2fd2bea..3b8fe44 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/PipeTabFragment.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/PipeTabFragment.java
@@ -158,7 +158,7 @@ public void createVideo() {
final String ffmpegCommand = Video.generateCreateVideoWithPipesScript(pipe1, pipe2, pipe3, videoFile.getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -246,20 +246,18 @@ protected void showProgressDialog() {
}
protected void updateProgressDialog() {
- if (statistics == null) {
+ if (statistics == null || statistics.getTime() < 0) {
return;
}
int timeInMilliseconds = this.statistics.getTime();
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
+ String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
- TextView textView = progressDialog.findViewById(R.id.progressDialogText);
- if (textView != null) {
- textView.setText(String.format("Creating video: %% %s.", completePercentage));
- }
+ TextView textView = progressDialog.findViewById(R.id.progressDialogText);
+ if (textView != null) {
+ textView.setText(String.format("Creating video: %% %s.", completePercentage));
}
}
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/Popup.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/Popup.java
index c4fc77b..5dd7491 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/Popup.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/Popup.java
@@ -23,12 +23,12 @@
package com.arthenica.ffmpegkit.test;
import android.content.Context;
-import android.widget.Toast;
public class Popup {
public static void show(final Context context, final String message) {
- Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+ // DISABLED DUE TO THE java.io.IOException: Failed to load asset path ERROR ON API LEVEL 31
+ // Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/SafTabFragment.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/SafTabFragment.java
index e7c9dad..96d0469 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/SafTabFragment.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/SafTabFragment.java
@@ -148,7 +148,7 @@ private void runFFprobe() {
Log.d(TAG, "Testing FFprobe COMMAND synchronously.");
- Log.d(TAG, String.format("FFprobe process started with arguments\n'%s'", ffprobeCommand));
+ Log.d(TAG, String.format("FFprobe process started with arguments: '%s'", ffprobeCommand));
final FFprobeSession session = FFprobeKit.execute(ffprobeCommand);
@@ -178,7 +178,7 @@ private void encodeVideo() {
final String ffmpegCommand = Video.generateEncodeVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoPath, selectedCodec, getCustomOptions(selectedCodec));
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegSession session = FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -332,20 +332,18 @@ private void showProgressDialog() {
}
private void updateProgressDialog() {
- if (statistics == null) {
+ if (statistics == null || statistics.getTime() < 0) {
return;
}
int timeInMilliseconds = this.statistics.getTime();
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
+ String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
- TextView textView = progressDialog.findViewById(R.id.progressDialogText);
- if (textView != null) {
- textView.setText(String.format("Encoding video: %% %s.", completePercentage));
- }
+ TextView textView = progressDialog.findViewById(R.id.progressDialogText);
+ if (textView != null) {
+ textView.setText(String.format("Encoding video: %% %s.", completePercentage));
}
}
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/SubtitleTabFragment.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/SubtitleTabFragment.java
index fec5879..06009d9 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/SubtitleTabFragment.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/SubtitleTabFragment.java
@@ -152,7 +152,7 @@ public void burnSubtitles() {
final String ffmpegCommand = Video.generateEncodeVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath(), "mpeg4", "");
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
state = State.CREATING;
@@ -177,7 +177,7 @@ public void run() {
showBurnProgressDialog();
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", burnSubtitlesCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", burnSubtitlesCommand));
state = State.BURNING;
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/VidStabTabFragment.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/VidStabTabFragment.java
index 21b6e4b..2afe475 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/VidStabTabFragment.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/VidStabTabFragment.java
@@ -101,6 +101,10 @@ public void apply(final com.arthenica.ffmpegkit.Log log) {
});
}
+ public void disableStatisticsCallback() {
+ FFmpegKitConfig.enableStatisticsCallback(null);
+ }
+
public void stabilizeVideo() {
final File image1File = new File(requireContext().getCacheDir(), "machupicchu.jpg");
final File image2File = new File(requireContext().getCacheDir(), "pyramid.jpg");
@@ -135,7 +139,7 @@ public void stabilizeVideo() {
final String ffmpegCommand = Video.generateShakingVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -157,7 +161,7 @@ public void run() {
showStabilizeProgressDialog();
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", analyzeVideoCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", analyzeVideoCommand));
FFmpegKit.executeAsync(analyzeVideoCommand, new FFmpegSessionCompleteCallback() {
@@ -168,7 +172,7 @@ public void apply(final FFmpegSession secondSession) {
if (ReturnCode.isSuccess(secondSession.getReturnCode())) {
final String stabilizeVideoCommand = String.format("-y -i %s -vf vidstabtransform=smoothing=30:input=%s -c:v mpeg4 %s", videoFile.getAbsolutePath(), shakeResultsFile.getAbsolutePath(), stabilizedVideoFile.getAbsolutePath());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", stabilizeVideoCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", stabilizeVideoCommand));
FFmpegKit.executeAsync(stabilizeVideoCommand, new FFmpegSessionCompleteCallback() {
@@ -280,6 +284,7 @@ public File getStabilizedVideoFile() {
public void setActive() {
Log.i(MainActivity.TAG, "VidStab Tab Activated");
enableLogCallback();
+ disableStatisticsCallback();
Popup.show(requireContext(), getString(R.string.vidstab_test_tooltip_text));
}
diff --git a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/VideoTabFragment.java b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/VideoTabFragment.java
index ab25178..53a0e68 100644
--- a/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/VideoTabFragment.java
+++ b/android/test-app-maven-central/src/main/java/com/arthenica/ffmpegkit/test/VideoTabFragment.java
@@ -144,7 +144,7 @@ public void encodeVideo() {
final String ffmpegCommand = Video.generateEncodeVideoScript(image1File.getAbsolutePath(), image2File.getAbsolutePath(), image3File.getAbsolutePath(), videoFile.getAbsolutePath(), getSelectedVideoCodec(), getPixelFormat(), getCustomOptions());
- Log.d(TAG, String.format("FFmpeg process started with arguments\n'%s'.", ffmpegCommand));
+ Log.d(TAG, String.format("FFmpeg process started with arguments: '%s'.", ffmpegCommand));
final FFmpegSession session = FFmpegKit.executeAsync(ffmpegCommand, new FFmpegSessionCompleteCallback() {
@@ -340,20 +340,18 @@ protected void showProgressDialog() {
}
protected void updateProgressDialog() {
- if (statistics == null) {
+ if (statistics == null || statistics.getTime() < 0) {
return;
}
int timeInMilliseconds = this.statistics.getTime();
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
+ String completePercentage = new BigDecimal(timeInMilliseconds).multiply(new BigDecimal(100)).divide(new BigDecimal(totalVideoDuration), 0, BigDecimal.ROUND_HALF_UP).toString();
- TextView textView = progressDialog.findViewById(R.id.progressDialogText);
- if (textView != null) {
- textView.setText(String.format("Encoding video: %% %s.", completePercentage));
- }
+ TextView textView = progressDialog.findViewById(R.id.progressDialogText);
+ if (textView != null) {
+ textView.setText(String.format("Encoding video: %% %s.", completePercentage));
}
}
diff --git a/android/test-app-maven-central/src/main/res/values/tooltips.xml b/android/test-app-maven-central/src/main/res/values/tooltips.xml
index 17b5b1a..f6fd6af 100644
--- a/android/test-app-maven-central/src/main/res/values/tooltips.xml
+++ b/android/test-app-maven-central/src/main/res/values/tooltips.xml
@@ -1,12 +1,12 @@
- Enter an FFmpeg command without \'ffmpeg\' at the beginning and click one of the RUN buttons
- Select a video codec and press ENCODE button
+ Enter a command without ffmpeg/ffprobe at the beginning and click one of the RUN buttons
+ Select a video codec and press the ENCODE button
Enter the https url of a media file and click the button
- Select an audio codec and press ENCODE button
+ Select an audio codec and press the ENCODE button
Click the button to burn subtitles. Created video will play inside the frame below
Click the button to stabilize video. Original video will play above and stabilized video will play below
Click the button to create a video using pipe redirection. Created video will play inside the frame below
Use ENCODE and CANCEL buttons to start/stop multiple execution
Use system file picker to test scoped storage extension
- Select a test and press RUN button
+ Select a test and press the RUN button
diff --git a/docs/assets/linux.gif b/docs/assets/linux.gif
new file mode 100644
index 0000000..2f43b28
Binary files /dev/null and b/docs/assets/linux.gif differ
diff --git a/flutter/README.md b/flutter/README.md
index c66f7c6..adc8d53 100644
--- a/flutter/README.md
+++ b/flutter/README.md
@@ -1,3 +1,3 @@
# FFmpegKit Flutter
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/flutter/test-app-pub/android/app/build.gradle b/flutter/test-app-pub/android/app/build.gradle
index 323c1d1..ae2a9cf 100644
--- a/flutter/test-app-pub/android/app/build.gradle
+++ b/flutter/test-app-pub/android/app/build.gradle
@@ -25,7 +25,7 @@ apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
- compileSdkVersion 30
+ compileSdkVersion 31
lintOptions {
disable 'InvalidPackage'
@@ -34,7 +34,7 @@ android {
defaultConfig {
applicationId "com.arthenica.ffmpegkit.flutter.android"
minSdkVersion 24
- targetSdkVersion 30
+ targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
diff --git a/flutter/test-app-pub/android/app/src/main/AndroidManifest.xml b/flutter/test-app-pub/android/app/src/main/AndroidManifest.xml
index 7213b83..50634a4 100644
--- a/flutter/test-app-pub/android/app/src/main/AndroidManifest.xml
+++ b/flutter/test-app-pub/android/app/src/main/AndroidManifest.xml
@@ -11,7 +11,8 @@
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
- android:windowSoftInputMode="adjustResize">
+ android:windowSoftInputMode="adjustResize"
+ android:exported="true">
00:00:04,000
+Machu Picchu is a 15th-century Inca citadel, located in the Eastern Cordillera of southern Peru.
+
+2
+00:00:04,001 --> 00:00:06,927
+大金字塔和哈夫拉金字塔是古埃及最大的金字塔。
+
+3
+00:00:06,928 --> 00:00:09,000
+Stonehenge هو نصب ما قبل التاريخ في ويلتشير ، إنجلترا ، على بعد ميلين (3 كم) غرب Amesbury.
diff --git a/linux/test-app-local-dependency/src/Application.cpp b/linux/test-app-local-dependency/src/Application.cpp
new file mode 100644
index 0000000..db4d6d2
--- /dev/null
+++ b/linux/test-app-local-dependency/src/Application.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "Application.h"
+#include
+#include
+#include
+#include
+
+using namespace ffmpegkit;
+
+static bool fs_exists(const std::string &s, const bool isFile, const bool isDirectory) {
+ struct stat dir_info;
+
+ if (stat(s.c_str(), &dir_info) == 0) {
+ if (isFile && S_ISREG(dir_info.st_mode)) {
+ return true;
+ }
+ if (isDirectory && S_ISDIR(dir_info.st_mode)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool fs_create_dir(const std::string& s) {
+ if (!fs_exists(s, false, true)) {
+ if (mkdir(s.c_str(), S_IRWXU | S_IRWXG | S_IROTH) != 0) {
+ std::cout << "Failed to create directory: " << s << ". Operation failed with " << errno << "." << std::endl;
+ return false;
+ }
+ }
+ return true;
+}
+
+std::ostream& operator<<(std::ostream& out, const std::chrono::time_point& o) {
+ char str[100];
+ std::time_t t = std::chrono::system_clock::to_time_t(o);
+ std::strftime(str, sizeof(str), "%c", std::localtime(&t));
+ return out << str;
+}
+
+ffmpegkittest::Application::Application() {
+ set_title("FFmpegKit Linux");
+ set_default_size(800, 600);
+ set_position(Gtk::WIN_POS_CENTER);
+
+ commandTab.setParentWindow(this);
+ videoTab.setParentWindow(this);
+ httpsTab.setParentWindow(this);
+ audioTab.setParentWindow(this);
+ subtitleTab.setParentWindow(this);
+ vidStabTab.setParentWindow(this);
+ pipeTab.setParentWindow(this);
+ concurrentExecutionTab.setParentWindow(this);
+ otherTab.setParentWindow(this);
+ tabs.append_page(commandTab, "Command");
+ tabs.append_page(videoTab, "Video");
+ tabs.append_page(httpsTab, "HTTPS");
+ tabs.append_page(audioTab, "Audio");
+ tabs.append_page(subtitleTab, "Subtitle");
+ tabs.append_page(vidStabTab, "Vid.Stab");
+ tabs.append_page(pipeTab, "Pipe");
+ tabs.append_page(concurrentExecutionTab, "Concurrent Execution");
+ tabs.append_page(otherTab, "Other");
+ tabs.signal_switch_page().connect(sigc::mem_fun(*this, &Application::onTabSelected));
+
+ add(tabs);
+
+ show_all_children();
+
+ initApplicationCacheDirectory();
+
+ registerApplicationFonts();
+ std::cout << "Application fonts registered." << std::endl;
+
+ FFmpegKitConfig::ignoreSignal(SignalXcpu);
+ FFmpegKitConfig::setLogLevel(LevelAVLogInfo);
+}
+
+void ffmpegkittest::Application::initApplicationCacheDirectory() {
+ auto appCacheDir = ffmpegkittest::Application::getApplicationCacheDirectory();
+
+ if (!fs_exists(appCacheDir, false, true)) {
+ if (mkdir(appCacheDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH) != 0) {
+ std::cout << "Failed to create application cache directory: " << appCacheDir << ". Operation failed with " << errno << "." << std::endl;
+ }
+ }
+}
+
+void ffmpegkittest::Application::onTabSelected(const Widget* page, const guint page_number) {
+ switch (page_number) {
+ case 0:
+ commandTab.setActive();
+ break;
+ case 1:
+ videoTab.setActive();
+ break;
+ case 2:
+ httpsTab.setActive();
+ break;
+ case 3:
+ audioTab.setActive();
+ break;
+ case 4:
+ subtitleTab.setActive();
+ break;
+ case 5:
+ vidStabTab.setActive();
+ break;
+ case 6:
+ pipeTab.setActive();
+ break;
+ case 7:
+ concurrentExecutionTab.setActive();
+ break;
+ case 8:
+ otherTab.setActive();
+ break;
+ default:
+ commandTab.setActive();
+ break;
+ }
+}
+
+void ffmpegkittest::Application::listFFmpegSessions() {
+ auto ffmpegSessions = FFmpegKit::listSessions();
+ std::cout << "Listing FFmpeg sessions." << std::endl;
+ int i = 0;
+ std::for_each(ffmpegSessions->begin(), ffmpegSessions->end(), [&](const std::shared_ptr session) {
+ std::cout << "Session " << i++ << " = id:" << session->getSessionId() << ", startTime:" << session->getStartTime() << ", duration:" << session-> getDuration() << ", state:" << FFmpegKitConfig::sessionStateToString(session->getState()) << ", returnCode:" << session->getReturnCode() << "." << std::endl;
+ });
+ std::cout << "Listed FFmpeg sessions." << std::endl;
+}
+
+void ffmpegkittest::Application::listFFprobeSessions() {
+ auto ffprobeSessions = FFprobeKit::listFFprobeSessions();
+ std::cout << "Listing FFprobe sessions." << std::endl;
+ int i = 0;
+ std::for_each(ffprobeSessions->begin(), ffprobeSessions->end(), [&](const std::shared_ptr session) {
+ std::cout << "Session " << i++ << " = id:" << session->getSessionId() << ", startTime:" << session->getStartTime() << ", duration:" << session-> getDuration() << ", state:" << FFmpegKitConfig::sessionStateToString(session->getState()) << ", returnCode:" << session->getReturnCode() << "." << std::endl;
+ });
+ std::cout << "Listed FFprobe sessions." << std::endl;
+}
+
+std::string ffmpegkittest::Application::getApplicationCacheDirectory() {
+ return Glib::get_user_cache_dir() + "/ffmpegkittest";
+}
+
+void ffmpegkittest::Application::registerApplicationFonts() {
+ auto fontDirectory = Application::getApplicationInstallDirectory() + "/share/fonts";
+ auto reportFile = Application::getApplicationCacheDirectory() + "/ffreport.txt";
+
+ FFmpegKitConfig::setFontDirectoryList(std::list{fontDirectory, "/usr/share/fonts"}, std::map{{"MyFontName", "Doppio One"}});
+ FFmpegKitConfig::setEnvironmentVariable("FFREPORT", reportFile.c_str());
+}
diff --git a/linux/test-app-local-dependency/src/Application.h.in b/linux/test-app-local-dependency/src/Application.h.in
new file mode 100644
index 0000000..f8965d2
--- /dev/null
+++ b/linux/test-app-local-dependency/src/Application.h.in
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_APPLICATION_H
+#define FFMPEG_KIT_TEST_APPLICATION_H
+
+#include
+#include "AudioTab.h"
+#include "CommandTab.h"
+#include "ConcurrentExecutionTab.h"
+#include "HttpsTab.h"
+#include "OtherTab.h"
+#include "PipeTab.h"
+#include "SubtitleTab.h"
+#include "VideoTab.h"
+#include "VidStabTab.h"
+
+namespace ffmpegkittest {
+
+ class Application : public Gtk::Window {
+ public:
+ Application();
+ static void listFFmpegSessions();
+ static void listFFprobeSessions();
+ static std::string getApplicationCacheDirectory();
+ static std::string getApplicationInstallDirectory() {
+ return "@CMAKE_INSTALL_PREFIX@";
+ }
+
+ protected:
+ void initApplicationCacheDirectory();
+ void onTabSelected(const Widget* page, const guint page_number);
+
+ private:
+ void registerApplicationFonts();
+
+ Gtk::Notebook tabs;
+ AudioTab audioTab;
+ CommandTab commandTab;
+ ConcurrentExecutionTab concurrentExecutionTab;
+ HttpsTab httpsTab;
+ OtherTab otherTab;
+ PipeTab pipeTab;
+ SubtitleTab subtitleTab;
+ VideoTab videoTab;
+ VidStabTab vidStabTab;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_APPLICATION_H
diff --git a/linux/test-app-local-dependency/src/AudioTab.cpp b/linux/test-app-local-dependency/src/AudioTab.cpp
new file mode 100644
index 0000000..7e59f30
--- /dev/null
+++ b/linux/test-app-local-dependency/src/AudioTab.cpp
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "AudioTab.h"
+#include "Application.h"
+#include "Constants.h"
+#include "Popup.h"
+#include
+#include
+
+using namespace ffmpegkit;
+
+static gboolean showEncodeSuccessPopup(const std::pair* parameters) {
+ Gtk::Window* window = parameters->first;
+ auto messageDetail = parameters->second;
+ ffmpegkittest::Popup::show(window, Gtk::MESSAGE_INFO, messageDetail);
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean showEncodeFailedPopup(const std::pair* parameters) {
+ Gtk::Window* window = parameters->first;
+ auto messageDetail = parameters->second;
+ ffmpegkittest::Popup::show(window, Gtk::MESSAGE_ERROR, messageDetail);
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean appendLog(const std::pair>* parameters) {
+ ffmpegkittest::AudioTab* audioTab = parameters->first;
+ auto log = parameters->second;
+ audioTab->appendOutput(log->getMessage());
+ delete parameters;
+ return FALSE;
+}
+
+ffmpegkittest::AudioTab::AudioTab() : selectedCodec(-1) {
+ audioCodecModel = Gtk::ListStore::create(audioCodecModelColumn);
+ audioCodec.set_model(audioCodecModel);
+ audioCodec.set_size_request(240, 30);
+ audioCodec.signal_changed().connect(sigc::mem_fun(*this, &AudioTab::onAudioCodecChanged));
+ Util::applyComboBoxStyle(audioCodec);
+
+ initAudioCodecData();
+
+ encodeButton.set_label("ENCODE");
+ encodeButton.set_size_request(120, 30);
+ encodeButton.set_tooltip_text(Constants::AudioTestTooltipText);
+ encodeButton.signal_clicked().connect(sigc::mem_fun(*this, &AudioTab::encodeAudio));
+ encodeButton.set_sensitive(false);
+ Util::applyButtonStyle(encodeButton);
+ encodeButtonBox.pack_start(encodeButton, Gtk::PACK_EXPAND_PADDING);
+
+ outputText.set_editable(false);
+ Util::applyOutputTextStyle(outputText);
+ outputTextWindow.add(outputText);
+
+ pack_start(audioCodecBox, Gtk::PACK_SHRINK);
+ pack_start(encodeButtonBox, Gtk::PACK_SHRINK);
+ add(outputTextWindow);
+}
+
+void ffmpegkittest::AudioTab::setActive() {
+ std::cout << "Audio Tab Activated" << std::endl;
+ FFmpegKitConfig::enableLogCallback(nullptr);
+ FFmpegKitConfig::enableStatisticsCallback(nullptr);
+ createAudioSample();
+ FFmpegKitConfig::enableLogCallback([this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ });
+}
+
+void ffmpegkittest::AudioTab::setParentWindow(Gtk::Window* parentWindow) {
+ this->parentWindow = parentWindow;
+}
+
+void ffmpegkittest::AudioTab::appendOutput(const std::string& string) {
+ outputText.get_buffer()->set_text(outputText.get_buffer()->get_text() + string);
+ Glib::RefPtr adj = outputText.get_vadjustment();
+ adj->set_value(adj->get_upper());
+}
+
+void ffmpegkittest::AudioTab::createAudioSample() {
+ std::cout << "Creating AUDIO sample before the test." << std::endl;
+
+ auto audioSampleFile = getAudioSampleFile();
+ std::remove(audioSampleFile.c_str());
+
+ std::string ffmpegCommand = "-hide_banner -y -f lavfi -i sine=frequency=1000:duration=5 -c:a pcm_s16le " + audioSampleFile;
+
+ std::cout << "Creating audio sample with '" << ffmpegCommand << "'." << std::endl;
+
+ auto session = FFmpegKit::execute(ffmpegCommand);
+ if (ReturnCode::isSuccess(session->getReturnCode())) {
+ encodeButton.set_sensitive(true);
+ std::cout << "AUDIO sample created." << std::endl;
+ } else {
+ std::cout << "Creating AUDIO sample failed with state " << FFmpegKitConfig::sessionStateToString(session->getState()) << " and rc " << session->getReturnCode() << "." << session->getFailStackTrace() << std::endl;
+ g_idle_add((GSourceFunc)showEncodeFailedPopup, new std::pair(this->parentWindow, "Creating AUDIO sample failed. Please check logs for the details."));
+ }
+}
+
+void ffmpegkittest::AudioTab::clearOutput() {
+ outputText.get_buffer()->set_text("");
+}
+
+void ffmpegkittest::AudioTab::initAudioCodecData() {
+ auto row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "1";
+ row[audioCodecModelColumn.columnName] = "mp2 (twolame)";
+
+ row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "2";
+ row[audioCodecModelColumn.columnName] = "mp3 (liblame)";
+
+ row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "3";
+ row[audioCodecModelColumn.columnName] = "mp3 (libshine)";
+
+ row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "4";
+ row[audioCodecModelColumn.columnName] = "vorbis";
+
+ row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "5";
+ row[audioCodecModelColumn.columnName] = "opus";
+
+ row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "6";
+ row[audioCodecModelColumn.columnName] = "amr-nb";
+
+ row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "7";
+ row[audioCodecModelColumn.columnName] = "amr-wb";
+
+ row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "8";
+ row[audioCodecModelColumn.columnName] = "ilbc";
+
+ row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "9";
+ row[audioCodecModelColumn.columnName] = "soxr";
+
+ row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "10";
+ row[audioCodecModelColumn.columnName] = "speex";
+
+ row = *(audioCodecModel->append());
+ row[audioCodecModelColumn.columnId] = "11";
+ row[audioCodecModelColumn.columnName] = "wavpack";
+
+ audioCodec.pack_start(audioCodecModelColumn.columnName);
+ audioCodec.set_entry_text_column(audioCodecModelColumn.columnId);
+ audioCodec.set_active(0);
+
+ audioCodecBox.pack_start(audioCodec, Gtk::PACK_EXPAND_PADDING);
+}
+
+void ffmpegkittest::AudioTab::onAudioCodecChanged() {
+ int rowNumber = audioCodec.get_active_row_number();
+ if (rowNumber != -1) {
+ selectedCodec = rowNumber;
+ }
+}
+
+std::string ffmpegkittest::AudioTab::getSelectedAudioCodec() {
+ switch(selectedCodec) {
+ case 0: return "mp2 (twolame)";
+ case 1: return "mp3 (liblame)";
+ case 2: return "mp3 (libshine)";
+ case 3: return "vorbis";
+ case 4: return "opus";
+ case 5: return "amr-nb";
+ case 6: return "amr-wb";
+ case 7: return "ilbc";
+ case 8: return "soxr";
+ case 9: return "speex";
+ case 10: return "wavpack";
+ default: return "";
+ }
+}
+
+void ffmpegkittest::AudioTab::encodeAudio() {
+ auto audioOutputFile = getAudioOutputFile();
+ std::remove(audioOutputFile.c_str());
+
+ auto audioCodec = getSelectedAudioCodec();
+
+ std::cout << "Testing AUDIO encoding with '" << audioCodec << "' codec." << std::endl;
+
+ auto ffmpegCommand = generateAudioEncodeScript();
+
+ showProgressDialog();
+
+ clearOutput();
+
+ std::cout << "FFmpeg process started with arguments: '" << ffmpegCommand << "'." << std::endl;
+
+ auto session = FFmpegKit::executeAsync(ffmpegCommand, [this](auto session) {
+ const auto state = session->getState();
+ auto returnCode = session->getReturnCode();
+
+ this->hideProgressDialog();
+
+ if (ReturnCode::isSuccess(returnCode)) {
+ g_idle_add((GSourceFunc)showEncodeSuccessPopup, new std::pair(this->parentWindow, "Encode completed successfully."));
+ std::cout << "Encode completed successfully." << std::endl;
+ } else {
+ g_idle_add((GSourceFunc)showEncodeFailedPopup, new std::pair(this->parentWindow, "Encode failed. Please check logs for the details."));
+ std::cout << "Encode failed with state " << FFmpegKitConfig::sessionStateToString(state) << " and rc " << returnCode << "." << session->getFailStackTrace() << std::endl;
+ }
+ });
+}
+
+std::string ffmpegkittest::AudioTab::getAudioOutputFile() {
+ std::string audioCodec = getSelectedAudioCodec();
+
+ std::string extension;
+ if (audioCodec.compare("mp2 (twolame)") == 0) {
+ extension = "mpg";
+ } else if (audioCodec.compare("mp3 (liblame)") == 0 || audioCodec.compare("mp3 (libshine)") == 0) {
+ extension = "mp3";
+ } else if (audioCodec.compare("vorbis") == 0) {
+ extension = "ogg";
+ } else if (audioCodec.compare("opus") == 0) {
+ extension = "opus";
+ } else if (audioCodec.compare("amr-nb") == 0 || audioCodec.compare("amr-wb") == 0) {
+ extension = "amr";
+ } else if (audioCodec.compare("ilbc") == 0) {
+ extension = "lbc";
+ } else if (audioCodec.compare("speex") == 0) {
+ extension = "spx";
+ } else if (audioCodec.compare("wavpack") == 0) {
+ extension = "wv";
+ } else {
+
+ // soxr
+ extension = "wav";
+ }
+
+ return Application::getApplicationCacheDirectory() + "/audio." + extension;
+}
+
+std::string ffmpegkittest::AudioTab::getAudioSampleFile() {
+ return Application::getApplicationCacheDirectory() + "/audio-sample.wav";
+}
+
+void ffmpegkittest::AudioTab::showProgressDialog() {
+ // progressDialog.show(this->get_parent_window());
+}
+
+void ffmpegkittest::AudioTab::hideProgressDialog() {
+ // progressDialog.hide();
+}
+
+std::string ffmpegkittest::AudioTab::generateAudioEncodeScript() {
+ auto audioCodec = getSelectedAudioCodec();
+ auto audioSampleFile = getAudioSampleFile();
+ auto audioOutputFile = getAudioOutputFile();
+
+ if (audioCodec.compare("mp2 (twolame)") == 0) {
+ return "-hide_banner -y -i " + audioSampleFile + " -c:a mp2 -b:a 192k " + audioOutputFile;
+ } else if (audioCodec.compare("mp3 (liblame)") == 0) {
+ return "-hide_banner -y -i " + audioSampleFile + " -c:a libmp3lame -qscale:a 2 " + audioOutputFile;
+ } else if (audioCodec.compare("mp3 (libshine)") == 0) {
+ return "-hide_banner -y -i " + audioSampleFile + " -c:a libshine -qscale:a 2 " + audioOutputFile;
+ } else if (audioCodec.compare("vorbis") == 0) {
+ return "-hide_banner -y -i " + audioSampleFile + " -c:a libvorbis -b:a 64k " + audioOutputFile;
+ } else if (audioCodec.compare("opus") == 0) {
+ return "-hide_banner -y -i " + audioSampleFile + " -c:a libopus -b:a 64k -vbr on -compression_level 10 " + audioOutputFile;
+ } else if (audioCodec.compare("amr-nb") == 0) {
+ return "-hide_banner -y -i " + audioSampleFile + " -ar 8000 -ab 12.2k -c:a libopencore_amrnb " + audioOutputFile;
+ } else if (audioCodec.compare("amr-wb") == 0) {
+ return "-hide_banner -y -i " + audioSampleFile + " -ar 8000 -ab 12.2k -c:a libvo_amrwbenc -strict experimental " + audioOutputFile;
+ } else if (audioCodec.compare("ilbc") == 0) {
+ return "-hide_banner -y -i " + audioSampleFile + " -c:a ilbc -ar 8000 -b:a 15200 " + audioOutputFile;
+ } else if (audioCodec.compare("speex") == 0) {
+ return "-hide_banner -y -i " + audioSampleFile + " -c:a libspeex -ar 16000 " + audioOutputFile;
+ } else if (audioCodec.compare("wavpack") == 0) {
+ return "-hide_banner -y -i " + audioSampleFile + " -c:a wavpack -b:a 64k " + audioOutputFile;
+ } else {
+
+ // soxr
+ return "-hide_banner -y -i " + audioSampleFile + " -af aresample=resampler=soxr -ar 44100 " + audioOutputFile;
+ }
+}
diff --git a/linux/test-app-local-dependency/src/AudioTab.h b/linux/test-app-local-dependency/src/AudioTab.h
new file mode 100644
index 0000000..075ab47
--- /dev/null
+++ b/linux/test-app-local-dependency/src/AudioTab.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_AUDIO_TAB_H
+#define FFMPEG_KIT_TEST_AUDIO_TAB_H
+
+#include "ProgressDialog.h"
+#include "Util.h"
+#include
+
+namespace ffmpegkittest {
+
+ class AudioTab: public Gtk::VBox {
+ public:
+ AudioTab();
+ void setActive();
+ void setParentWindow(Gtk::Window* parentWindow);
+ void appendOutput(const std::string& string);
+
+ private:
+ void createAudioSample();
+ void clearOutput();
+ void initAudioCodecData();
+ void onAudioCodecChanged();
+ std::string getSelectedAudioCodec();
+ void encodeAudio();
+ std::string getAudioOutputFile();
+ std::string getAudioSampleFile();
+ void showProgressDialog();
+ void hideProgressDialog();
+ std::string generateAudioEncodeScript();
+
+ Glib::RefPtr audioCodecModel;
+ ComboBoxModelColumn audioCodecModelColumn;
+ Gtk::ComboBox audioCodec;
+ Gtk::HBox audioCodecBox;
+ int selectedCodec;
+ Gtk::Button encodeButton;
+ Gtk::HBox encodeButtonBox;
+ Gtk::TextView outputText;
+ Gtk::ScrolledWindow outputTextWindow;
+ ffmpegkittest::ProgressDialog progressDialog;
+ Gtk::Window* parentWindow;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_AUDIO_TAB_H
diff --git a/linux/test-app-local-dependency/src/CommandTab.cpp b/linux/test-app-local-dependency/src/CommandTab.cpp
new file mode 100644
index 0000000..a413356
--- /dev/null
+++ b/linux/test-app-local-dependency/src/CommandTab.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "CommandTab.h"
+#include "Application.h"
+#include "Constants.h"
+#include "Popup.h"
+#include
+#include
+#include
+#include
+#include
+
+using namespace ffmpegkit;
+
+static gboolean showCommandFailedPopup(Gtk::Window* window) {
+ ffmpegkittest::Popup::show(window, Gtk::MESSAGE_ERROR, "Command failed. Please check output for the details.");
+ return FALSE;
+}
+
+static gboolean appendLog(const std::pair>* parameters) {
+ ffmpegkittest::CommandTab* commandTab = parameters->first;
+ auto log = parameters->second;
+ commandTab->appendOutput(log->getMessage());
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean appendSessionOutput(const std::pair>* parameters) {
+ ffmpegkittest::CommandTab* commandTab = parameters->first;
+ auto session = parameters->second;
+ commandTab->appendOutput(session->getOutput());
+ delete parameters;
+ return FALSE;
+}
+
+ffmpegkittest::CommandTab::CommandTab() : parentWindow(nullptr) {
+ commandText.set_placeholder_text("Enter command");
+ Util::applyEditTextStyle(commandText);
+
+ runFFmpegButton.set_label("RUN FFMPEG");
+ runFFmpegButton.set_size_request(120, 30);
+ runFFmpegButton.set_tooltip_text(Constants::CommandTestFFmpegTooltipText);
+ runFFmpegButton.signal_clicked().connect(sigc::mem_fun(*this, &CommandTab::runFFmpeg));
+ Util::applyButtonStyle(runFFmpegButton);
+ runFFmpegButtonBox.pack_start(runFFmpegButton, Gtk::PACK_EXPAND_PADDING);
+
+ runFFprobeButton.set_label("RUN FFPROBE");
+ runFFprobeButton.set_size_request(120, 30);
+ runFFprobeButton.set_tooltip_text(Constants::CommandTestFFprobeTooltipText);
+ runFFprobeButton.signal_clicked().connect(sigc::mem_fun(*this, &CommandTab::runFFprobe));
+ Util::applyButtonStyle(runFFprobeButton);
+ runFFprobeButtonBox.pack_start(runFFprobeButton, Gtk::PACK_EXPAND_PADDING);
+
+ outputText.set_editable(false);
+ Util::applyOutputTextStyle(outputText);
+ outputTextWindow.add(outputText);
+
+ pack_start(commandText, Gtk::PACK_SHRINK);
+ pack_start(runFFmpegButtonBox, Gtk::PACK_SHRINK);
+ pack_start(runFFprobeButtonBox, Gtk::PACK_SHRINK);
+ add(outputTextWindow);
+}
+
+void ffmpegkittest::CommandTab::setParentWindow(Gtk::Window* parentWindow) {
+ this->parentWindow = parentWindow;
+}
+
+void ffmpegkittest::CommandTab::runFFmpeg() {
+ clearOutput();
+
+ std::string ffmpegCommand(commandText.get_text());
+
+ std::cout << "Current log level is " << FFmpegKitConfig::logLevelToString(FFmpegKitConfig::getLogLevel()) << "." << std::endl;
+
+ std::cout << "Testing FFmpeg COMMAND asynchronously." << std::endl;
+
+ std::cout << "FFmpeg process started with arguments: '" << ffmpegCommand << "'" << std::endl;
+
+ FFmpegKit::executeAsync(ffmpegCommand, [this](auto session) {
+ const auto state = session->getState();
+ auto returnCode = session->getReturnCode();
+
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(state) << " and rc " << returnCode << "." << session->getFailStackTrace() << std::endl;
+
+ if (state == SessionStateFailed || !returnCode->isValueSuccess()) {
+ g_idle_add((GSourceFunc)showCommandFailedPopup, this->parentWindow);
+ }
+ }, [this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ }, nullptr);
+}
+
+void ffmpegkittest::CommandTab::runFFprobe() {
+ clearOutput();
+
+ std::string ffprobeCommand(commandText.get_text());
+
+ std::cout << "Testing FFprobe COMMAND asynchronously." << std::endl;
+
+ std::cout << "FFprobe process started with arguments: '" << ffprobeCommand << "'" << std::endl;
+
+ auto session = FFprobeSession::create(FFmpegKitConfig::parseArguments(ffprobeCommand.c_str()), [this](auto session) {
+ const auto state = session->getState();
+ auto returnCode = session->getReturnCode();
+
+ g_idle_add((GSourceFunc)appendSessionOutput, new std::pair>(this, session));
+
+ std::cout << "FFprobe process exited with state " << FFmpegKitConfig::sessionStateToString(state) << " and rc " << returnCode << "." << session->getFailStackTrace() << std::endl;
+
+ if (state == SessionStateFailed || !returnCode->isValueSuccess()) {
+ g_idle_add((GSourceFunc)showCommandFailedPopup, this->parentWindow);
+ }
+
+ }, nullptr, LogRedirectionStrategyNeverPrintLogs);
+
+ FFmpegKitConfig::asyncFFprobeExecute(session);
+
+ ffmpegkittest::Application::listFFprobeSessions();
+}
+
+void ffmpegkittest::CommandTab::setActive() {
+ std::cout << "Command Tab Activated" << std::endl;
+ FFmpegKitConfig::enableLogCallback(nullptr);
+}
+
+void ffmpegkittest::CommandTab::appendOutput(const std::string& string) {
+ outputText.get_buffer()->set_text(outputText.get_buffer()->get_text() + string);
+ Glib::RefPtr adj = outputText.get_vadjustment();
+ adj->set_value(adj->get_upper());
+}
+
+void ffmpegkittest::CommandTab::clearOutput() {
+ outputText.get_buffer()->set_text("");
+}
diff --git a/linux/test-app-local-dependency/src/CommandTab.h b/linux/test-app-local-dependency/src/CommandTab.h
new file mode 100644
index 0000000..5813b83
--- /dev/null
+++ b/linux/test-app-local-dependency/src/CommandTab.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_COMMAND_TAB_H
+#define FFMPEG_KIT_TEST_COMMAND_TAB_H
+
+#include "Util.h"
+#include
+#include
+#include
+
+namespace ffmpegkittest {
+
+ class CommandTab: public Gtk::VBox {
+ public:
+ CommandTab();
+ void setActive();
+ void setParentWindow(Gtk::Window* parentWindow);
+ void appendOutput(const std::string& string);
+
+ private:
+ void clearOutput();
+ void runFFmpeg();
+ void runFFprobe();
+
+ Gtk::Entry commandText;
+ Gtk::Button runFFmpegButton;
+ Gtk::HBox runFFmpegButtonBox;
+ Gtk::Button runFFprobeButton;
+ Gtk::HBox runFFprobeButtonBox;
+ Gtk::TextView outputText;
+ Gtk::ScrolledWindow outputTextWindow;
+ Gtk::Window* parentWindow;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_COMMAND_TAB_H
diff --git a/linux/test-app-local-dependency/src/ConcurrentExecutionTab.cpp b/linux/test-app-local-dependency/src/ConcurrentExecutionTab.cpp
new file mode 100644
index 0000000..383cb00
--- /dev/null
+++ b/linux/test-app-local-dependency/src/ConcurrentExecutionTab.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ConcurrentExecutionTab.h"
+#include "Application.h"
+#include "Constants.h"
+#include "Log.h"
+#include "Popup.h"
+#include "Video.h"
+#include
+#include
+
+using namespace ffmpegkit;
+
+static long sessionId1 = -1;
+static long sessionId2 = -1;
+static long sessionId3 = -1;
+
+static gboolean appendLog(const std::pair>* parameters) {
+ ffmpegkittest::ConcurrentExecutionTab* concurrentExecutionTab = parameters->first;
+ auto log = parameters->second;
+ concurrentExecutionTab->appendOutput(log->getMessage());
+ delete parameters;
+ return FALSE;
+}
+
+ffmpegkittest::ConcurrentExecutionTab::ConcurrentExecutionTab() {
+ encodeButton1.set_label("ENCODE 1");
+ encodeButton1.set_size_request(120, 30);
+ encodeButton1.set_tooltip_text(Constants::ConcurrentExecutionTestTooltipText);
+ encodeButton1.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ConcurrentExecutionTab::encodeVideo), 1));
+ Util::applyButtonStyle(encodeButton1);
+ encodeButtonBox.pack_start(encodeButton1, Gtk::PACK_EXPAND_PADDING);
+ encodeButton2.set_label("ENCODE 2");
+ encodeButton2.set_size_request(120, 30);
+ encodeButton2.set_tooltip_text(Constants::ConcurrentExecutionTestTooltipText);
+ encodeButton2.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ConcurrentExecutionTab::encodeVideo), 2));
+ Util::applyButtonStyle(encodeButton2);
+ encodeButtonBox.pack_start(encodeButton2, Gtk::PACK_EXPAND_PADDING);
+ encodeButton3.set_label("ENCODE 3");
+ encodeButton3.set_size_request(120, 30);
+ encodeButton3.set_tooltip_text(Constants::ConcurrentExecutionTestTooltipText);
+ encodeButton3.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ConcurrentExecutionTab::encodeVideo), 3));
+ Util::applyButtonStyle(encodeButton3);
+ encodeButtonBox.pack_start(encodeButton3, Gtk::PACK_EXPAND_PADDING);
+
+ cancelButton1.set_label("CANCEL 1");
+ cancelButton1.set_size_request(120, 30);
+ cancelButton1.set_tooltip_text(Constants::ConcurrentExecutionTestTooltipText);
+ cancelButton1.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ConcurrentExecutionTab::cancel), 1));
+ Util::applyButtonStyle(cancelButton1);
+ cancelButtonBox.pack_start(cancelButton1, Gtk::PACK_EXPAND_PADDING);
+ cancelButton2.set_label("CANCEL 2");
+ cancelButton2.set_size_request(120, 30);
+ cancelButton2.set_tooltip_text(Constants::ConcurrentExecutionTestTooltipText);
+ cancelButton2.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ConcurrentExecutionTab::cancel), 2));
+ Util::applyButtonStyle(cancelButton2);
+ cancelButtonBox.pack_start(cancelButton2, Gtk::PACK_EXPAND_PADDING);
+ cancelButton3.set_label("CANCEL 3");
+ cancelButton3.set_size_request(120, 30);
+ cancelButton3.set_tooltip_text(Constants::ConcurrentExecutionTestTooltipText);
+ cancelButton3.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ConcurrentExecutionTab::cancel), 3));
+ Util::applyButtonStyle(cancelButton3);
+ cancelButtonBox.pack_start(cancelButton3, Gtk::PACK_EXPAND_PADDING);
+ cancelButton4.set_label("CANCEL ALL");
+ cancelButton4.set_size_request(120, 30);
+ cancelButton4.set_tooltip_text(Constants::ConcurrentExecutionTestTooltipText);
+ cancelButton4.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ConcurrentExecutionTab::cancel), 0));
+ Util::applyButtonStyle(cancelButton4);
+ cancelButtonBox.pack_start(cancelButton4, Gtk::PACK_EXPAND_PADDING);
+
+ outputText.set_editable(false);
+ Util::applyOutputTextStyle(outputText);
+ outputTextWindow.add(outputText);
+
+ pack_start(encodeButtonBox, Gtk::PACK_SHRINK);
+ pack_start(cancelButtonBox, Gtk::PACK_SHRINK);
+ add(outputTextWindow);
+}
+
+void ffmpegkittest::ConcurrentExecutionTab::setActive() {
+ std::cout << "Concurrent Execution Tab Activated" << std::endl;
+ FFmpegKitConfig::enableLogCallback([this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ });
+}
+
+void ffmpegkittest::ConcurrentExecutionTab::setParentWindow(Gtk::Window* parentWindow) {
+ this->parentWindow = parentWindow;
+}
+
+void ffmpegkittest::ConcurrentExecutionTab::appendOutput(const std::string& string) {
+ outputText.get_buffer()->set_text(outputText.get_buffer()->get_text() + string);
+ Glib::RefPtr adj = outputText.get_vadjustment();
+ adj->set_value(adj->get_upper());
+}
+
+void ffmpegkittest::ConcurrentExecutionTab::clearOutput() {
+ outputText.get_buffer()->set_text("");
+}
+
+void ffmpegkittest::ConcurrentExecutionTab::encodeVideo(const int buttonNumber) {
+ clearOutput();
+
+ std::string image1File = Application::getApplicationInstallDirectory() + "/share/images/machupicchu.jpg";
+ std::string image2File = Application::getApplicationInstallDirectory() + "/share/images/pyramid.jpg";
+ std::string image3File = Application::getApplicationInstallDirectory() + "/share/images/stonehenge.jpg";
+ std::string videoFile = Application::getApplicationCacheDirectory() + "/video" + std::to_string(buttonNumber) + ".mp4";
+
+ std::cout << "Testing CONCURRENT EXECUTION for button " << buttonNumber << "." << std::endl;
+
+ std::string ffmpegCommand = Video::generateEncodeVideoScript(image1File, image2File, image3File, videoFile, "mpeg4", "");
+
+ std::cout << "FFmpeg process starting for button " << buttonNumber << " with arguments: '" << ffmpegCommand << "'." << std::endl;
+
+ auto session = FFmpegKit::executeAsync(ffmpegCommand, [this, buttonNumber](auto session) {
+ const auto state = session->getState();
+ auto returnCode = session->getReturnCode();
+
+ if (ReturnCode::isCancel(returnCode)) {
+ std::cout << "FFmpeg process ended with cancel for button " << buttonNumber << " with sessionId " << session->getSessionId() << "." << std::endl;
+ } else {
+ std::cout << "FFmpeg process ended with state " << FFmpegKitConfig::sessionStateToString(state) << " and rc " << returnCode << " for button " << buttonNumber << " with sessionId " << session->getSessionId() << "." << session->getFailStackTrace() << std::endl;
+ }
+ });
+
+ const long sessionId = session->getSessionId();
+
+ std::cout << "Async FFmpeg process started for button " << buttonNumber << " with sessionId " << session->getSessionId() << "." << std::endl;
+
+ switch (buttonNumber) {
+ case 1: {
+ sessionId1 = sessionId;
+ }
+ break;
+ case 2: {
+ sessionId2 = sessionId;
+ }
+ break;
+ default: {
+ sessionId3 = sessionId;
+ }
+ }
+
+ Application::listFFmpegSessions();
+}
+
+void ffmpegkittest::ConcurrentExecutionTab::cancel(const int buttonNumber) {
+ long sessionId = 0;
+
+ switch (buttonNumber) {
+ case 1: {
+ sessionId = sessionId1;
+ }
+ break;
+ case 2: {
+ sessionId = sessionId2;
+ }
+ break;
+ case 3: {
+ sessionId = sessionId3;
+ }
+ }
+
+ std::cout << "Cancelling FFmpeg process for button " << buttonNumber << " with sessionId " << sessionId << "." << std::endl;
+
+ if (sessionId == 0) {
+ FFmpegKit::cancel();
+ } else {
+ FFmpegKit::cancel(sessionId);
+ }
+}
diff --git a/linux/test-app-local-dependency/src/ConcurrentExecutionTab.h b/linux/test-app-local-dependency/src/ConcurrentExecutionTab.h
new file mode 100644
index 0000000..25a429b
--- /dev/null
+++ b/linux/test-app-local-dependency/src/ConcurrentExecutionTab.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_CONCURRENT_EXECUTION_TAB_H
+#define FFMPEG_KIT_TEST_CONCURRENT_EXECUTION_TAB_H
+
+#include "Util.h"
+#include
+
+namespace ffmpegkittest {
+
+ class ConcurrentExecutionTab: public Gtk::VBox {
+ public:
+ ConcurrentExecutionTab();
+ void setActive();
+ void setParentWindow(Gtk::Window* parentWindow);
+ void appendOutput(const std::string& string);
+
+ private:
+ void clearOutput();
+ void encodeVideo(const int buttonNumber);
+ void cancel(const int buttonNumber);
+
+ Gtk::Button encodeButton1;
+ Gtk::Button encodeButton2;
+ Gtk::Button encodeButton3;
+ Gtk::HBox encodeButtonBox;
+ Gtk::Button cancelButton1;
+ Gtk::Button cancelButton2;
+ Gtk::Button cancelButton3;
+ Gtk::Button cancelButton4;
+ Gtk::HBox cancelButtonBox;
+ Gtk::TextView outputText;
+ Gtk::ScrolledWindow outputTextWindow;
+ Gtk::Window* parentWindow;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_CONCURRENT_EXECUTION_TAB_H
diff --git a/linux/test-app-local-dependency/src/Constants.h b/linux/test-app-local-dependency/src/Constants.h
new file mode 100644
index 0000000..35fc4cd
--- /dev/null
+++ b/linux/test-app-local-dependency/src/Constants.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_CONSTANTS_H
+#define FFMPEG_KIT_TEST_CONSTANTS_H
+
+#include
+#include
+
+namespace ffmpegkittest {
+
+ class Constants {
+ public:
+
+ static constexpr const char* CommandTestFFmpegTooltipText = "Enter an FFmpeg command without 'ffmpeg' at the beginning and click the RUN button";
+
+ static constexpr const char* CommandTestFFprobeTooltipText = "Enter an FFprobe command without 'ffprobe' at the beginning and click the RUN button";
+
+ static constexpr const char* VideoTestTooltipText = "Select a video codec and press the ENCODE button";
+
+ static constexpr const char* HttpsTestTooltipText = "Enter the https url of a media file and click the button";
+
+ static constexpr const char* AudioTestTooltipText = "Select an audio codec and press the ENCODE button";
+
+ static constexpr const char* SubtitleTestEncodeTooltipText = "Click the button to burn subtitles";
+
+ static constexpr const char* SubtitleTestCancelTooltipText = "Click cancel to stop";
+
+ static constexpr const char* VidStabTestTooltipText = "Click the button to stabilize video";
+
+ static constexpr const char* PipeTestTooltipText = "Click the button to create a video using pipe redirection";
+
+ static constexpr const char* ConcurrentExecutionTestTooltipText = "Use ENCODE and CANCEL buttons to start/stop multiple executions";
+
+ static constexpr const char* OtherTestTooltipText = "Select a test and press the RUN button";
+
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_CONSTANTS_H
diff --git a/linux/test-app-local-dependency/src/FFmpegKitTest.cpp b/linux/test-app-local-dependency/src/FFmpegKitTest.cpp
new file mode 100644
index 0000000..1675abb
--- /dev/null
+++ b/linux/test-app-local-dependency/src/FFmpegKitTest.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2019-2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "MediaInformationParserTest.h"
+#include
+#include
+#include
+
+using namespace ffmpegkit;
+
+void testParseSimpleCommand() {
+ auto argumentList = FFmpegKitConfig::parseArguments("-hide_banner -loop 1 -i file.jpg -filter_complex [0:v]setpts=PTS-STARTPTS[video] -map [video] -vsync 2 -async 1 video.mp4");
+
+ assert(14 == argumentList.size());
+
+ auto it = argumentList.begin();
+ assertString("-hide_banner", *it++);
+ assertString("-loop", *it++);
+ assertString("1", *it++);
+ assertString("-i", *it++);
+ assertString("file.jpg", *it++);
+ assertString("-filter_complex", *it++);
+ assertString("[0:v]setpts=PTS-STARTPTS[video]", *it++);
+ assertString("-map", *it++);
+ assertString("[video]", *it++);
+ assertString("-vsync", *it++);
+ assertString("2", *it++);
+ assertString("-async", *it++);
+ assertString("1", *it++);
+ assertString("video.mp4", *it++);
+}
+
+void testParseSingleQuotesInCommand() {
+ auto argumentList = FFmpegKitConfig::parseArguments("-loop 1 'file one.jpg' -filter_complex '[0:v]setpts=PTS-STARTPTS[video]' -map [video] video.mp4 ");
+
+ assert(8 == argumentList.size());
+
+ auto it = argumentList.begin();
+ assertString("-loop", *it++);
+ assertString("1", *it++);
+ assertString("file one.jpg", *it++);
+ assertString("-filter_complex", *it++);
+ assertString("[0:v]setpts=PTS-STARTPTS[video]", *it++);
+ assertString("-map", *it++);
+ assertString("[video]", *it++);
+ assertString("video.mp4", *it++);
+}
+
+void testParseDoubleQuotesInCommand() {
+ auto argumentList = FFmpegKitConfig::parseArguments("-loop 1 \"file one.jpg\" -filter_complex \"[0:v]setpts=PTS-STARTPTS[video]\" -map [video] video.mp4 ");
+
+ assert(8 == argumentList.size());
+
+ auto it = argumentList.begin();
+ assertString("-loop", *it++);
+ assertString("1", *it++);
+ assertString("file one.jpg", *it++);
+ assertString("-filter_complex", *it++);
+ assertString("[0:v]setpts=PTS-STARTPTS[video]", *it++);
+ assertString("-map", *it++);
+ assertString("[video]", *it++);
+ assertString("video.mp4", *it++);
+
+ argumentList = FFmpegKitConfig::parseArguments(" -i file:///tmp/input.mp4 -vcodec libx264 -vf \"scale=1024:1024,pad=width=1024:height=1024:x=0:y=0:color=black\" -acodec copy -q:v 0 -q:a 0 video.mp4");
+
+ assert(13 == argumentList.size());
+
+ it = argumentList.begin();
+ assertString("-i", *it++);
+ assertString("file:///tmp/input.mp4", *it++);
+ assertString("-vcodec", *it++);
+ assertString("libx264", *it++);
+ assertString("-vf", *it++);
+ assertString("scale=1024:1024,pad=width=1024:height=1024:x=0:y=0:color=black", *it++);
+ assertString("-acodec", *it++);
+ assertString("copy", *it++);
+ assertString("-q:v", *it++);
+ assertString("0", *it++);
+ assertString("-q:a", *it++);
+ assertString("0", *it++);
+ assertString("video.mp4", *it++);
+}
+
+void testParseDoubleQuotesAndEscapesInCommand() {
+ auto argumentList = FFmpegKitConfig::parseArguments(" -i file:///tmp/input.mp4 -vf \"subtitles=file:///tmp/subtitles.srt:force_style=\'FontSize=16,PrimaryColour=&HFFFFFF&\'\" -vcodec libx264 -acodec copy -q:v 0 -q:a 0 video.mp4");
+
+ assert(13 == argumentList.size());
+
+ auto it = argumentList.begin();
+ assertString("-i", *it++);
+ assertString("file:///tmp/input.mp4", *it++);
+ assertString("-vf", *it++);
+ assertString("subtitles=file:///tmp/subtitles.srt:force_style='FontSize=16,PrimaryColour=&HFFFFFF&'", *it++);
+ assertString("-vcodec", *it++);
+ assertString("libx264", *it++);
+ assertString("-acodec", *it++);
+ assertString("copy", *it++);
+ assertString("-q:v", *it++);
+ assertString("0", *it++);
+ assertString("-q:a", *it++);
+ assertString("0", *it++);
+ assertString("video.mp4", *it++);
+
+ argumentList = FFmpegKitConfig::parseArguments(" -i file:///tmp/input.mp4 -vf \"subtitles=file:///tmp/subtitles.srt:force_style=\\\"FontSize=16,PrimaryColour=&HFFFFFF&\\\"\" -vcodec libx264 -acodec copy -q:v 0 -q:a 0 video.mp4");
+
+ assert(13 == argumentList.size());
+
+ it = argumentList.begin();
+ assertString("-i", *it++);
+ assertString("file:///tmp/input.mp4", *it++);
+ assertString("-vf", *it++);
+ assertString("subtitles=file:///tmp/subtitles.srt:force_style=\\\"FontSize=16,PrimaryColour=&HFFFFFF&\\\"", *it++);
+ assertString("-vcodec", *it++);
+ assertString("libx264", *it++);
+ assertString("-acodec", *it++);
+ assertString("copy", *it++);
+ assertString("-q:v", *it++);
+ assertString("0", *it++);
+ assertString("-q:a", *it++);
+ assertString("0", *it++);
+ assertString("video.mp4", *it++);
+}
+
+void getSessionIdTest() {
+ const std::list TEST_ARGUMENTS{"argument1", "argument2"};
+
+ auto sessions1 = FFmpegSession::create(TEST_ARGUMENTS);
+ auto sessions2 = FFprobeSession::create(TEST_ARGUMENTS);
+ auto sessions3 = MediaInformationSession::create(TEST_ARGUMENTS);
+
+ assert(sessions3->getSessionId() > sessions2->getSessionId());
+ assert(sessions3->getSessionId() > sessions1->getSessionId());
+ assert(sessions2->getSessionId() > sessions1->getSessionId());
+
+ assert(sessions1->getSessionId() > 0);
+ assert(sessions2->getSessionId() > 0);
+ assert(sessions3->getSessionId() > 0);
+}
+
+void testFFmpegKit(void) {
+ testParseSimpleCommand();
+ testParseSingleQuotesInCommand();
+ testParseDoubleQuotesInCommand();
+ testParseDoubleQuotesAndEscapesInCommand();
+ getSessionIdTest();
+
+ std::cout << "FFmpegKitConfigTest passed." << std::endl;
+}
diff --git a/linux/test-app-local-dependency/src/FFmpegKitTest.h b/linux/test-app-local-dependency/src/FFmpegKitTest.h
new file mode 100644
index 0000000..f286b8a
--- /dev/null
+++ b/linux/test-app-local-dependency/src/FFmpegKitTest.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019-2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+void testFFmpegKit(void);
diff --git a/linux/test-app-local-dependency/src/HttpsTab.cpp b/linux/test-app-local-dependency/src/HttpsTab.cpp
new file mode 100644
index 0000000..76e0a90
--- /dev/null
+++ b/linux/test-app-local-dependency/src/HttpsTab.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "HttpsTab.h"
+#include "Constants.h"
+#include "Popup.h"
+#include
+#include
+#define RAPIDJSON_ASSERT(x)
+#include "rapidjson/writer.h"
+#include "rapidjson/stringbuffer.h"
+
+using namespace ffmpegkit;
+
+static std::recursive_mutex outputMutex;
+
+std::string toString(const rapidjson::Value& value) {
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer writer(buffer);
+ value.Accept(writer);
+ return std::string(buffer.GetString(), buffer.GetSize());
+}
+
+static gboolean appendLog(const std::pair* parameters) {
+ ffmpegkittest::HttpsTab* httpsTab = parameters->first;
+ auto string = parameters->second;
+ httpsTab->appendOutput(string);
+ delete parameters;
+ return FALSE;
+}
+
+void appendLogToMainLoop(const ffmpegkittest::HttpsTab* httpsTab,const std::string string) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair(httpsTab, string));
+}
+
+ffmpegkittest::HttpsTab::HttpsTab() : parentWindow(nullptr) {
+ urlText.set_placeholder_text("Enter https url");
+ Util::applyEditTextStyle(urlText);
+
+ getInfoFromUrlButton.set_label("GET INFO FROM URL");
+ getInfoFromUrlButton.set_size_request(120, 30);
+ getInfoFromUrlButton.set_tooltip_text(Constants::HttpsTestTooltipText);
+ getInfoFromUrlButton.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &HttpsTab::runGetMediaInformation), 1));
+ Util::applyButtonStyle(getInfoFromUrlButton);
+ getInfoFromUrlButtonBox.pack_start(getInfoFromUrlButton, Gtk::PACK_EXPAND_PADDING);
+
+ getRandomInfoButton1.set_label("GET RANDOM INFO");
+ getRandomInfoButton1.set_size_request(120, 30);
+ getRandomInfoButton1.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &HttpsTab::runGetMediaInformation), 2));
+ Util::applyButtonStyle(getRandomInfoButton1);
+ getRandomInfoButton1Box.pack_start(getRandomInfoButton1, Gtk::PACK_EXPAND_PADDING);
+
+ getRandomInfoButton2.set_label("GET RANDOM INFO");
+ getRandomInfoButton2.set_size_request(120, 30);
+ getRandomInfoButton2.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &HttpsTab::runGetMediaInformation), 3));
+ Util::applyButtonStyle(getRandomInfoButton2);
+ getRandomInfoButton2Box.pack_start(getRandomInfoButton2, Gtk::PACK_EXPAND_PADDING);
+
+ getInfoAndFailButton.set_label("GET INFO AND FAIL");
+ getInfoAndFailButton.set_size_request(120, 30);
+
+ getInfoAndFailButton.signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &HttpsTab::runGetMediaInformation), 4));
+ Util::applyButtonStyle(getInfoAndFailButton);
+ getInfoAndFailButtonBox.pack_start(getInfoAndFailButton, Gtk::PACK_EXPAND_PADDING);
+
+ outputText.set_editable(false);
+ Util::applyOutputTextStyle(outputText);
+ outputTextWindow.add(outputText);
+
+ pack_start(urlText, Gtk::PACK_SHRINK);
+ pack_start(getInfoFromUrlButtonBox, Gtk::PACK_SHRINK);
+ pack_start(getRandomInfoButton1Box, Gtk::PACK_SHRINK);
+ pack_start(getRandomInfoButton2Box, Gtk::PACK_SHRINK);
+ pack_start(getInfoAndFailButtonBox, Gtk::PACK_SHRINK);
+ add(outputTextWindow);
+}
+
+void ffmpegkittest::HttpsTab::setActive() {
+ std::cout << "Https Tab Activated" << std::endl;
+ FFmpegKitConfig::enableLogCallback(nullptr);
+ FFmpegKitConfig::enableStatisticsCallback(nullptr);
+}
+
+void ffmpegkittest::HttpsTab::setParentWindow(Gtk::Window* parentWindow) {
+ this->parentWindow = parentWindow;
+}
+
+void ffmpegkittest::HttpsTab::appendOutput(const std::string& string) {
+ outputText.get_buffer()->set_text(outputText.get_buffer()->get_text() + string);
+ Glib::RefPtr adj = outputText.get_vadjustment();
+ adj->set_value(adj->get_upper());
+}
+
+void ffmpegkittest::HttpsTab::clearOutput() {
+ outputText.get_buffer()->set_text("");
+}
+
+void ffmpegkittest::HttpsTab::runGetMediaInformation(const int buttonNumber) {
+
+ // SELECT TEST URL
+ std::string testUrl;
+ switch (buttonNumber) {
+ case 1: {
+ testUrl = urlText.get_text();
+ if (testUrl.empty()) {
+ testUrl = HttpsTestDefaultUrl;
+ urlText.set_text(testUrl);
+ }
+ }
+ break;
+ case 2:
+ case 3: {
+ testUrl = getRandomTestUrl();
+ }
+ break;
+ case 4:
+ default: {
+ testUrl = HttpsTestFailUrl;
+ urlText.set_text(testUrl);
+ }
+ }
+
+ std::cout << "Testing HTTPS with for button " << buttonNumber << " using url " << testUrl << "." << std::endl;
+
+ if (buttonNumber == 4) {
+
+ // ONLY THIS BUTTON CLEARS THE TEXT VIEW
+ clearOutput();
+ }
+
+ // EXECUTE
+ FFprobeKit::getMediaInformationAsync(testUrl, createNewCompleteCallback());
+
+}
+
+std::string ffmpegkittest::HttpsTab::getRandomTestUrl() {
+ switch (std::rand() % 3) {
+ case 0:
+ return HttpsTestRandomUrl1;
+ case 1:
+ return HttpsTestRandomUrl2;
+ default:
+ return HttpsTestRandomUrl3;
+ }
+}
+
+MediaInformationSessionCompleteCallback ffmpegkittest::HttpsTab::createNewCompleteCallback() {
+ return [this](auto session) {
+ std::unique_lock lock(outputMutex);
+ auto information = session->getMediaInformation();
+ if (information == nullptr) {
+ appendLogToMainLoop(this, "Get media information failed\n");
+ appendLogToMainLoop(this, "State: " + FFmpegKitConfig::sessionStateToString(session->getState()) + "\n");
+ appendLogToMainLoop(this, "Duration: " + std::to_string(session->getDuration()) + "\n");
+ if (session->getReturnCode() != nullptr) {
+ appendLogToMainLoop(this, "Return Code: " + std::to_string(session->getReturnCode()->getValue()) + "\n");
+ }
+ appendLogToMainLoop(this, "Fail stack trace: " + session->getFailStackTrace() + "\n");
+ appendLogToMainLoop(this, "Output: " + session->getOutput() + "\n");
+ } else {
+ if (information->getFilename() != nullptr) {
+ appendLogToMainLoop(this, "Media information for " + *information->getFilename() + "\n");
+ }
+ if (information->getFormat() != nullptr) {
+ appendLogToMainLoop(this, "Format: " + *information->getFormat() + "\n");
+ }
+ if (information->getBitrate() != nullptr) {
+ appendLogToMainLoop(this, "Bitrate: " + *information->getBitrate() + "\n");
+ }
+ if (information->getDuration() != nullptr) {
+ appendLogToMainLoop(this, "Duration: " + *information->getDuration() + "\n");
+ }
+ if (information->getStartTime() != nullptr) {
+ appendLogToMainLoop(this, "Start time: " + *information->getStartTime() + "\n");
+ }
+ if (information->getTags() != nullptr) {
+ auto tags = information->getTags();
+ for (auto tagIterator = tags->MemberBegin(); tagIterator != tags->MemberEnd(); ++tagIterator) {
+ const char* tagName = tagIterator->name.GetString();
+ appendLogToMainLoop(this, std::string("Tag: ") + tagName + ":" + toString(tagIterator->value) + "\n");
+ }
+ }
+ if (information->getStreams() != nullptr) {
+ auto streams = information->getStreams();
+ std::for_each(streams->cbegin(), streams->cend(), [this](const std::shared_ptr& stream) {
+ if (stream->getIndex() != nullptr) {
+ appendLogToMainLoop(this, "Stream index: " + std::to_string(*stream->getIndex()) + "\n");
+ }
+ if (stream->getType() != nullptr) {
+ appendLogToMainLoop(this, "Stream type: " + *stream->getType() + "\n");
+ }
+ if (stream->getCodec() != nullptr) {
+ appendLogToMainLoop(this, "Stream codec: " + *stream->getCodec() + "\n");
+ }
+ if (stream->getCodecLong() != nullptr) {
+ appendLogToMainLoop(this, "Stream codec long: " + *stream->getCodecLong() + "\n");
+ }
+ if (stream->getFormat() != nullptr) {
+ appendLogToMainLoop(this, "Stream format: " + *stream->getFormat() + "\n");
+ }
+
+ if (stream->getWidth() != nullptr) {
+ appendLogToMainLoop(this, "Stream width: " + std::to_string(*stream->getWidth()) + "\n");
+ }
+ if (stream->getHeight() != nullptr) {
+ appendLogToMainLoop(this, "Stream height: " + std::to_string(*stream->getHeight()) + "\n");
+ }
+
+ if (stream->getBitrate() != nullptr) {
+ appendLogToMainLoop(this, "Stream bitrate: " + *stream->getBitrate() + "\n");
+ }
+ if (stream->getSampleRate() != nullptr) {
+ appendLogToMainLoop(this, "Stream sample rate: " + *stream->getSampleRate() + "\n");
+ }
+ if (stream->getSampleFormat() != nullptr) {
+ appendLogToMainLoop(this, "Stream sample format: " + *stream->getSampleFormat() + "\n");
+ }
+ if (stream->getChannelLayout() != nullptr) {
+ appendLogToMainLoop(this, "Stream channel layout: " + *stream->getChannelLayout() + "\n");
+ }
+
+ if (stream->getSampleAspectRatio() != nullptr) {
+ appendLogToMainLoop(this, "Stream sample aspect ratio: " + *stream->getSampleAspectRatio() + "\n");
+ }
+ if (stream->getDisplayAspectRatio() != nullptr) {
+ appendLogToMainLoop(this, "Stream display ascpect ratio: " + *stream->getDisplayAspectRatio() + "\n");
+ }
+ if (stream->getAverageFrameRate() != nullptr) {
+ appendLogToMainLoop(this, "Stream average frame rate: " + *stream->getAverageFrameRate() + "\n");
+ }
+ if (stream->getRealFrameRate() != nullptr) {
+ appendLogToMainLoop(this, "Stream real frame rate: " + *stream->getRealFrameRate() + "\n");
+ }
+ if (stream->getTimeBase() != nullptr) {
+ appendLogToMainLoop(this, "Stream time base: " + *stream->getTimeBase() + "\n");
+ }
+ if (stream->getCodecTimeBase() != nullptr) {
+ appendLogToMainLoop(this, "Stream codec time base: " + *stream->getCodecTimeBase() + "\n");
+ }
+
+ if (stream->getTags() != nullptr) {
+ auto tags = stream->getTags();
+ for (auto tagIterator = tags->MemberBegin(); tagIterator != tags->MemberEnd(); ++tagIterator) {
+ const char* tagName = tagIterator->name.GetString();
+ appendLogToMainLoop(this, std::string("Stream tag: ") + tagName + ":" + toString(tagIterator->value) + "\n");
+ }
+ }
+ });
+ }
+
+ if (information->getChapters() != nullptr) {
+ auto chapters = information->getChapters();
+ std::for_each(chapters->cbegin(), chapters->cend(), [this](const std::shared_ptr& chapter) {
+ if (chapter->getId() != nullptr) {
+ appendLogToMainLoop(this, "Chapter id: " + std::to_string(*chapter->getId()) + "\n");
+ }
+ if (chapter->getTimeBase() != nullptr) {
+ appendLogToMainLoop(this, "Chapter time base: " + *chapter->getTimeBase() + "\n");
+ }
+ if (chapter->getStart() != nullptr) {
+ appendLogToMainLoop(this, "Chapter start: " + std::to_string(*chapter->getStart()) + "\n");
+ }
+ if (chapter->getStartTime() != nullptr) {
+ appendLogToMainLoop(this, "Chapter start time: " + *chapter->getStartTime() + "\n");
+ }
+ if (chapter->getEnd() != nullptr) {
+ appendLogToMainLoop(this, "Chapter end: " + std::to_string(*chapter->getEnd()) + "\n");
+ }
+ if (chapter->getEndTime() != nullptr) {
+ appendLogToMainLoop(this, "Chapter end time: " + *chapter->getEndTime() + "\n");
+ }
+ if (chapter->getTags() != nullptr) {
+ auto tags = chapter->getTags();
+ for (auto tagIterator = tags->MemberBegin(); tagIterator != tags->MemberEnd(); ++tagIterator) {
+ const char* tagName = tagIterator->name.GetString();
+ appendLogToMainLoop(this, std::string("Chapter tag: ") + tagName + ":" + toString(tagIterator->value) + "\n");
+ }
+ }
+ });
+ }
+ }
+ };
+}
\ No newline at end of file
diff --git a/linux/test-app-local-dependency/src/HttpsTab.h b/linux/test-app-local-dependency/src/HttpsTab.h
new file mode 100644
index 0000000..1e29dd1
--- /dev/null
+++ b/linux/test-app-local-dependency/src/HttpsTab.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_HTTPS_TAB_H
+#define FFMPEG_KIT_TEST_HTTPS_TAB_H
+
+#include
+#include "Util.h"
+#include
+
+namespace ffmpegkittest {
+
+ class HttpsTab: public Gtk::VBox {
+ public:
+
+ static constexpr const char* HttpsTestDefaultUrl = "https://download.blender.org/peach/trailer/trailer_1080p.ogg";
+
+ static constexpr const char* HttpsTestFailUrl = "https://download2.blender.org/peach/trailer/trailer_1080p.ogg";
+
+ static constexpr const char* HttpsTestRandomUrl1 = "https://filesamples.com/samples/video/mov/sample_640x360.mov";
+
+ static constexpr const char* HttpsTestRandomUrl2 = "https://filesamples.com/samples/audio/mp3/sample3.mp3";
+
+ static constexpr const char* HttpsTestRandomUrl3 = "https://filesamples.com/samples/image/webp/sample1.webp";
+
+ HttpsTab();
+ void setActive();
+ void setParentWindow(Gtk::Window* parentWindow);
+ void appendOutput(const std::string& string);
+
+ private:
+ void clearOutput();
+ void runGetMediaInformation(const int buttonNumber);
+ std::string getRandomTestUrl();
+ ffmpegkit::MediaInformationSessionCompleteCallback createNewCompleteCallback();
+
+ Gtk::Entry urlText;
+ Gtk::Button getInfoFromUrlButton;
+ Gtk::HBox getInfoFromUrlButtonBox;
+ Gtk::Button getRandomInfoButton1;
+ Gtk::HBox getRandomInfoButton1Box;
+ Gtk::Button getRandomInfoButton2;
+ Gtk::HBox getRandomInfoButton2Box;
+ Gtk::Button getInfoAndFailButton;
+ Gtk::HBox getInfoAndFailButtonBox;
+ Gtk::TextView outputText;
+ Gtk::ScrolledWindow outputTextWindow;
+ Gtk::Window* parentWindow;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_HTTPS_TAB_H
diff --git a/linux/test-app-local-dependency/src/MediaInformationParserTest.cpp b/linux/test-app-local-dependency/src/MediaInformationParserTest.cpp
new file mode 100644
index 0000000..ef1cd53
--- /dev/null
+++ b/linux/test-app-local-dependency/src/MediaInformationParserTest.cpp
@@ -0,0 +1,751 @@
+/*
+ * Copyright (c) 2018-2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "MediaInformationParserTest.h"
+#include
+
+using namespace ffmpegkit;
+
+const std::string MEDIA_INFORMATION_MP3 = "{\n"
+ " \"streams\": [\n"
+ " {\n"
+ " \"index\": 0,\n"
+ " \"codec_name\": \"mp3\",\n"
+ " \"codec_long_name\": \"MP3 (MPEG audio layer 3)\",\n"
+ " \"codec_type\": \"audio\",\n"
+ " \"codec_time_base\": \"1/44100\",\n"
+ " \"codec_tag_string\": \"[0][0][0][0]\",\n"
+ " \"codec_tag\": \"0x0000\",\n"
+ " \"sample_fmt\": \"fltp\",\n"
+ " \"sample_rate\": \"44100\",\n"
+ " \"channels\": 2,\n"
+ " \"channel_layout\": \"stereo\",\n"
+ " \"bits_per_sample\": 0,\n"
+ " \"r_frame_rate\": \"0/0\",\n"
+ " \"avg_frame_rate\": \"0/0\",\n"
+ " \"time_base\": \"1/14112000\",\n"
+ " \"start_pts\": 169280,\n"
+ " \"start_time\": \"0.011995\",\n"
+ " \"duration_ts\": 4622376960,\n"
+ " \"duration\": \"327.549388\",\n"
+ " \"bit_rate\": \"320000\",\n"
+ " \"disposition\": {\n"
+ " \"default\": 0,\n"
+ " \"dub\": 0,\n"
+ " \"original\": 0,\n"
+ " \"comment\": 0,\n"
+ " \"lyrics\": 0,\n"
+ " \"karaoke\": 0,\n"
+ " \"forced\": 0,\n"
+ " \"hearing_impaired\": 0,\n"
+ " \"visual_impaired\": 0,\n"
+ " \"clean_effects\": 0,\n"
+ " \"attached_pic\": 0,\n"
+ " \"timed_thumbnails\": 0\n"
+ " },\n"
+ " \"tags\": {\n"
+ " \"encoder\": \"Lavf\"\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"chapters\": [\n"
+ " {\n"
+ " \"id\": 0,\n"
+ " \"time_base\": \"1/22050\",\n"
+ " \"start\": 0,\n"
+ " \"start_time\": \"0.000000\",\n"
+ " \"end\": 11158238,\n"
+ " \"end_time\": \"506.042540\",\n"
+ " \"tags\": {\n"
+ " \"title\": \"1 Laying Plans - 2 Waging War\"\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"id\": 1,\n"
+ " \"time_base\": \"1/22050\",\n"
+ " \"start\": 11158238,\n"
+ " \"start_time\": \"506.042540\",\n"
+ " \"end\": 21433051,\n"
+ " \"end_time\": \"972.020454\",\n"
+ " \"tags\": {\n"
+ " \"title\": \"3 Attack By Stratagem - 4 Tactical Dispositions\"\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"id\": 2,\n"
+ " \"time_base\": \"1/22050\",\n"
+ " \"start\": 21433051,\n"
+ " \"start_time\": \"972.020454\",\n"
+ " \"end\": 35478685,\n"
+ " \"end_time\": \"1609.010658\",\n"
+ " \"tags\": {\n"
+ " \"title\": \"5 Energy - 6 Weak Points and Strong\"\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"id\": 3,\n"
+ " \"time_base\": \"1/22050\",\n"
+ " \"start\": 35478685,\n"
+ " \"start_time\": \"1609.010658\",\n"
+ " \"end\": 47187043,\n"
+ " \"end_time\": \"2140.001950\",\n"
+ " \"tags\": {\n"
+ " \"title\": \"7 Maneuvering - 8 Variation in Tactics\"\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"id\": 4,\n"
+ " \"time_base\": \"1/22050\",\n"
+ " \"start\": 47187043,\n"
+ " \"start_time\": \"2140.001950\",\n"
+ " \"end\": 66635594,\n"
+ " \"end_time\": \"3022.022404\",\n"
+ " \"tags\": {\n"
+ " \"title\": \"9 The Army on the March - 10 Terrain\"\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"id\": 5,\n"
+ " \"time_base\": \"1/22050\",\n"
+ " \"start\": 66635594,\n"
+ " \"start_time\": \"3022.022404\",\n"
+ " \"end\": 83768105,\n"
+ " \"end_time\": \"3799.007029\",\n"
+ " \"tags\": {\n"
+ " \"title\": \"11 The Nine Situations\"\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"id\": 6,\n"
+ " \"time_base\": \"1/22050\",\n"
+ " \"start\": 83768105,\n"
+ " \"start_time\": \"3799.007029\",\n"
+ " \"end\": 95659008,\n"
+ " \"end_time\": \"4338.277007\",\n"
+ " \"tags\": {\n"
+ " \"title\": \"12 The Attack By Fire - 13 The Use of Spies\"\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"format\": {\n"
+ " \"filename\": \"sample.mp3\",\n"
+ " \"nb_streams\": 1,\n"
+ " \"nb_programs\": 0,\n"
+ " \"format_name\": \"mp3\",\n"
+ " \"format_long_name\": \"MP2/3 (MPEG audio layer 2/3)\",\n"
+ " \"start_time\": \"0.011995\",\n"
+ " \"duration\": \"327.549388\",\n"
+ " \"size\": \"13103064\",\n"
+ " \"bit_rate\": \"320026\",\n"
+ " \"probe_score\": 51,\n"
+ " \"tags\": {\n"
+ " \"encoder\": \"Lavf58.20.100\",\n"
+ " \"album\": \"Impact\",\n"
+ " \"artist\": \"Kevin MacLeod\",\n"
+ " \"comment\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit finito.\",\n"
+ " \"genre\": \"Cinematic\",\n"
+ " \"title\": \"Impact Moderato\"\n"
+ " }\n"
+ " }\n"
+ "}";
+
+const std::string MEDIA_INFORMATION_JPG = "{\n"
+ " \"streams\": [\n"
+ " {\n"
+ " \"index\": 0,\n"
+ " \"codec_name\": \"mjpeg\",\n"
+ " \"codec_long_name\": \"Motion JPEG\",\n"
+ " \"profile\": \"Baseline\",\n"
+ " \"codec_type\": \"video\",\n"
+ " \"codec_time_base\": \"0/1\",\n"
+ " \"codec_tag_string\": \"[0][0][0][0]\",\n"
+ " \"codec_tag\": \"0x0000\",\n"
+ " \"width\": 1496,\n"
+ " \"height\": 1729,\n"
+ " \"coded_width\": 1496,\n"
+ " \"coded_height\": 1729,\n"
+ " \"has_b_frames\": 0,\n"
+ " \"sample_aspect_ratio\": \"1:1\",\n"
+ " \"display_aspect_ratio\": \"1496:1729\",\n"
+ " \"pix_fmt\": \"yuvj444p\",\n"
+ " \"level\": -99,\n"
+ " \"color_range\": \"pc\",\n"
+ " \"color_space\": \"bt470bg\",\n"
+ " \"chroma_location\": \"center\",\n"
+ " \"refs\": 1,\n"
+ " \"r_frame_rate\": \"25/1\",\n"
+ " \"avg_frame_rate\": \"0/0\",\n"
+ " \"time_base\": \"1/25\",\n"
+ " \"start_pts\": 0,\n"
+ " \"start_time\": \"0.000000\",\n"
+ " \"duration_ts\": 1,\n"
+ " \"duration\": \"0.040000\",\n"
+ " \"bits_per_raw_sample\": \"8\",\n"
+ " \"disposition\": {\n"
+ " \"default\": 0,\n"
+ " \"dub\": 0,\n"
+ " \"original\": 0,\n"
+ " \"comment\": 0,\n"
+ " \"lyrics\": 0,\n"
+ " \"karaoke\": 0,\n"
+ " \"forced\": 0,\n"
+ " \"hearing_impaired\": 0,\n"
+ " \"visual_impaired\": 0,\n"
+ " \"clean_effects\": 0,\n"
+ " \"attached_pic\": 0,\n"
+ " \"timed_thumbnails\": 0\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"format\": {\n"
+ " \"filename\": \"sample.jpg\",\n"
+ " \"nb_streams\": 1,\n"
+ " \"nb_programs\": 0,\n"
+ " \"format_name\": \"image2\",\n"
+ " \"format_long_name\": \"image2 sequence\",\n"
+ " \"start_time\": \"0.000000\",\n"
+ " \"duration\": \"0.040000\",\n"
+ " \"size\": \"1659050\",\n"
+ " \"bit_rate\": \"331810000\",\n"
+ " \"probe_score\": 50\n"
+ " }\n"
+ "}";
+
+const std::string MEDIA_INFORMATION_GIF = "{\n"
+ " \"streams\": [\n"
+ " {\n"
+ " \"index\": 0,\n"
+ " \"codec_name\": \"gif\",\n"
+ " \"codec_long_name\": \"CompuServe GIF (Graphics Interchange Format)\",\n"
+ " \"codec_type\": \"video\",\n"
+ " \"codec_time_base\": \"12/133\",\n"
+ " \"codec_tag_string\": \"[0][0][0][0]\",\n"
+ " \"codec_tag\": \"0x0000\",\n"
+ " \"width\": 400,\n"
+ " \"height\": 400,\n"
+ " \"coded_width\": 400,\n"
+ " \"coded_height\": 400,\n"
+ " \"has_b_frames\": 0,\n"
+ " \"pix_fmt\": \"bgra\",\n"
+ " \"level\": -99,\n"
+ " \"refs\": 1,\n"
+ " \"r_frame_rate\": \"100/9\",\n"
+ " \"avg_frame_rate\": \"133/12\",\n"
+ " \"time_base\": \"1/100\",\n"
+ " \"start_pts\": 0,\n"
+ " \"start_time\": \"0.000000\",\n"
+ " \"duration_ts\": 396,\n"
+ " \"duration\": \"3.960000\",\n"
+ " \"nb_frames\": \"44\",\n"
+ " \"disposition\": {\n"
+ " \"default\": 0,\n"
+ " \"dub\": 0,\n"
+ " \"original\": 0,\n"
+ " \"comment\": 0,\n"
+ " \"lyrics\": 0,\n"
+ " \"karaoke\": 0,\n"
+ " \"forced\": 0,\n"
+ " \"hearing_impaired\": 0,\n"
+ " \"visual_impaired\": 0,\n"
+ " \"clean_effects\": 0,\n"
+ " \"attached_pic\": 0,\n"
+ " \"timed_thumbnails\": 0\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"format\": {\n"
+ " \"filename\": \"sample.gif\",\n"
+ " \"nb_streams\": 1,\n"
+ " \"nb_programs\": 0,\n"
+ " \"format_name\": \"gif\",\n"
+ " \"format_long_name\": \"CompuServe Graphics Interchange Format (GIF)\",\n"
+ " \"start_time\": \"0.000000\",\n"
+ " \"duration\": \"3.960000\",\n"
+ " \"size\": \"1001718\",\n"
+ " \"bit_rate\": \"2023672\",\n"
+ " \"probe_score\": 100\n"
+ " }\n"
+ "}";
+
+const std::string MEDIA_INFORMATION_MP4 = "{\n"
+ " \"streams\": [\n"
+ " {\n"
+ " \"index\": 0,\n"
+ " \"codec_name\": \"h264\",\n"
+ " \"codec_long_name\": \"H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10\",\n"
+ " \"profile\": \"Main\",\n"
+ " \"codec_type\": \"video\",\n"
+ " \"codec_time_base\": \"1/60\",\n"
+ " \"codec_tag_string\": \"avc1\",\n"
+ " \"codec_tag\": \"0x31637661\",\n"
+ " \"width\": 1280,\n"
+ " \"height\": 720,\n"
+ " \"coded_width\": 1280,\n"
+ " \"coded_height\": 720,\n"
+ " \"has_b_frames\": 0,\n"
+ " \"sample_aspect_ratio\": \"1:1\",\n"
+ " \"display_aspect_ratio\": \"16:9\",\n"
+ " \"pix_fmt\": \"yuv420p\",\n"
+ " \"level\": 42,\n"
+ " \"chroma_location\": \"left\",\n"
+ " \"refs\": 1,\n"
+ " \"is_avc\": \"true\",\n"
+ " \"nal_length_size\": \"4\",\n"
+ " \"r_frame_rate\": \"30/1\",\n"
+ " \"avg_frame_rate\": \"30/1\",\n"
+ " \"time_base\": \"1/15360\",\n"
+ " \"start_pts\": 0,\n"
+ " \"start_time\": \"0.000000\",\n"
+ " \"duration_ts\": 215040,\n"
+ " \"duration\": \"14.000000\",\n"
+ " \"bit_rate\": \"9166570\",\n"
+ " \"bits_per_raw_sample\": \"8\",\n"
+ " \"nb_frames\": \"420\",\n"
+ " \"disposition\": {\n"
+ " \"default\": 1,\n"
+ " \"dub\": 0,\n"
+ " \"original\": 0,\n"
+ " \"comment\": 0,\n"
+ " \"lyrics\": 0,\n"
+ " \"karaoke\": 0,\n"
+ " \"forced\": 0,\n"
+ " \"hearing_impaired\": 0,\n"
+ " \"visual_impaired\": 0,\n"
+ " \"clean_effects\": 0,\n"
+ " \"attached_pic\": 0,\n"
+ " \"timed_thumbnails\": 0\n"
+ " },\n"
+ " \"tags\": {\n"
+ " \"language\": \"und\",\n"
+ " \"handler_name\": \"VideoHandler\"\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"format\": {\n"
+ " \"filename\": \"sample.mp4\",\n"
+ " \"nb_streams\": 1,\n"
+ " \"nb_programs\": 0,\n"
+ " \"format_name\": \"mov,mp4,m4a,3gp,3g2,mj2\",\n"
+ " \"format_long_name\": \"QuickTime / MOV\",\n"
+ " \"start_time\": \"0.000000\",\n"
+ " \"duration\": \"14.000000\",\n"
+ " \"size\": \"16044159\",\n"
+ " \"bit_rate\": \"9168090\",\n"
+ " \"probe_score\": 100,\n"
+ " \"tags\": {\n"
+ " \"major_brand\": \"isom\",\n"
+ " \"minor_version\": \"512\",\n"
+ " \"compatible_brands\": \"isomiso2avc1mp41\",\n"
+ " \"encoder\": \"Lavf58.33.100\"\n"
+ " }\n"
+ " }\n"
+ "}";
+
+const std::string MEDIA_INFORMATION_PNG = "{\n"
+ " \"streams\": [\n"
+ " {\n"
+ " \"index\": 0,\n"
+ " \"codec_name\": \"png\",\n"
+ " \"codec_long_name\": \"PNG (Portable Network Graphics) image\",\n"
+ " \"codec_type\": \"video\",\n"
+ " \"codec_time_base\": \"0/1\",\n"
+ " \"codec_tag_string\": \"[0][0][0][0]\",\n"
+ " \"codec_tag\": \"0x0000\",\n"
+ " \"width\": 1198,\n"
+ " \"height\": 1198,\n"
+ " \"coded_width\": 1198,\n"
+ " \"coded_height\": 1198,\n"
+ " \"has_b_frames\": 0,\n"
+ " \"sample_aspect_ratio\": \"1:1\",\n"
+ " \"display_aspect_ratio\": \"1:1\",\n"
+ " \"pix_fmt\": \"pal8\",\n"
+ " \"level\": -99,\n"
+ " \"color_range\": \"pc\",\n"
+ " \"refs\": 1,\n"
+ " \"r_frame_rate\": \"25/1\",\n"
+ " \"avg_frame_rate\": \"0/0\",\n"
+ " \"time_base\": \"1/25\",\n"
+ " \"disposition\": {\n"
+ " \"default\": 0,\n"
+ " \"dub\": 0,\n"
+ " \"original\": 0,\n"
+ " \"comment\": 0,\n"
+ " \"lyrics\": 0,\n"
+ " \"karaoke\": 0,\n"
+ " \"forced\": 0,\n"
+ " \"hearing_impaired\": 0,\n"
+ " \"visual_impaired\": 0,\n"
+ " \"clean_effects\": 0,\n"
+ " \"attached_pic\": 0,\n"
+ " \"timed_thumbnails\": 0\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"format\": {\n"
+ " \"filename\": \"sample.png\",\n"
+ " \"nb_streams\": 1,\n"
+ " \"nb_programs\": 0,\n"
+ " \"format_name\": \"png_pipe\",\n"
+ " \"format_long_name\": \"piped png sequence\",\n"
+ " \"size\": \"31533\",\n"
+ " \"probe_score\": 99\n"
+ " }\n"
+ "}";
+
+const std::string MEDIA_INFORMATION_OGG = "{\n"
+ " \"streams\": [\n"
+ " {\n"
+ " \"index\": 0,\n"
+ " \"codec_name\": \"theora\",\n"
+ " \"codec_long_name\": \"Theora\",\n"
+ " \"codec_type\": \"video\",\n"
+ " \"codec_time_base\": \"1/25\",\n"
+ " \"codec_tag_string\": \"[0][0][0][0]\",\n"
+ " \"codec_tag\": \"0x0000\",\n"
+ " \"width\": 1920,\n"
+ " \"height\": 1080,\n"
+ " \"coded_width\": 1920,\n"
+ " \"coded_height\": 1088,\n"
+ " \"has_b_frames\": 0,\n"
+ " \"pix_fmt\": \"yuv420p\",\n"
+ " \"level\": -99,\n"
+ " \"color_space\": \"bt470bg\",\n"
+ " \"color_transfer\": \"bt709\",\n"
+ " \"color_primaries\": \"bt470bg\",\n"
+ " \"chroma_location\": \"center\",\n"
+ " \"refs\": 1,\n"
+ " \"r_frame_rate\": \"25/1\",\n"
+ " \"avg_frame_rate\": \"25/1\",\n"
+ " \"time_base\": \"1/25\",\n"
+ " \"start_pts\": 0,\n"
+ " \"start_time\": \"0.000000\",\n"
+ " \"duration_ts\": 813,\n"
+ " \"duration\": \"32.520000\",\n"
+ " \"disposition\": {\n"
+ " \"default\": 0,\n"
+ " \"dub\": 0,\n"
+ " \"original\": 0,\n"
+ " \"comment\": 0,\n"
+ " \"lyrics\": 0,\n"
+ " \"karaoke\": 0,\n"
+ " \"forced\": 0,\n"
+ " \"hearing_impaired\": 0,\n"
+ " \"visual_impaired\": 0,\n"
+ " \"clean_effects\": 0,\n"
+ " \"attached_pic\": 0,\n"
+ " \"timed_thumbnails\": 0\n"
+ " },\n"
+ " \"tags\": {\n"
+ " \"ENCODER\": \"ffmpeg2theora 0.19\"\n"
+ " }\n"
+ " },\n"
+ " {\n"
+ " \"index\": 1,\n"
+ " \"codec_name\": \"vorbis\",\n"
+ " \"codec_long_name\": \"Vorbis\",\n"
+ " \"codec_type\": \"audio\",\n"
+ " \"codec_time_base\": \"1/48000\",\n"
+ " \"codec_tag_string\": \"[0][0][0][0]\",\n"
+ " \"codec_tag\": \"0x0000\",\n"
+ " \"sample_fmt\": \"fltp\",\n"
+ " \"sample_rate\": \"48000\",\n"
+ " \"channels\": 2,\n"
+ " \"channel_layout\": \"stereo\",\n"
+ " \"bits_per_sample\": 0,\n"
+ " \"r_frame_rate\": \"0/0\",\n"
+ " \"avg_frame_rate\": \"0/0\",\n"
+ " \"time_base\": \"1/48000\",\n"
+ " \"start_pts\": 0,\n"
+ " \"start_time\": \"0.000000\",\n"
+ " \"duration_ts\": 1583850,\n"
+ " \"duration\": \"32.996875\",\n"
+ " \"bit_rate\": \"80000\",\n"
+ " \"disposition\": {\n"
+ " \"default\": 0,\n"
+ " \"dub\": 0,\n"
+ " \"original\": 0,\n"
+ " \"comment\": 0,\n"
+ " \"lyrics\": 0,\n"
+ " \"karaoke\": 0,\n"
+ " \"forced\": 0,\n"
+ " \"hearing_impaired\": 0,\n"
+ " \"visual_impaired\": 0,\n"
+ " \"clean_effects\": 0,\n"
+ " \"attached_pic\": 0,\n"
+ " \"timed_thumbnails\": 0\n"
+ " },\n"
+ " \"tags\": {\n"
+ " \"ENCODER\": \"ffmpeg2theora 0.19\"\n"
+ " }\n"
+ " }\n"
+ " ],\n"
+ " \"format\": {\n"
+ " \"filename\": \"sample.ogg\",\n"
+ " \"nb_streams\": 2,\n"
+ " \"nb_programs\": 0,\n"
+ " \"format_name\": \"ogg\",\n"
+ " \"format_long_name\": \"Ogg\",\n"
+ " \"start_time\": \"0.000000\",\n"
+ " \"duration\": \"32.996875\",\n"
+ " \"size\": \"27873937\",\n"
+ " \"bit_rate\": \"6757958\",\n"
+ " \"probe_score\": 100\n"
+ " }\n"
+ "}";
+
+void assertNumber(long expected, std::shared_ptr real) {
+ if (real == nullptr) {
+ assert(expected == -1);
+ } else {
+ assert(expected == *real);
+ }
+}
+
+void assertString(std::string expected, std::shared_ptr real) {
+ if (real == nullptr) {
+ assert(expected == "");
+ } else {
+ assert(expected == *real);
+ }
+}
+
+void assertString(std::string expected, std::string real) {
+ assert(expected == real);
+}
+
+void assertVideoStream(std::shared_ptr stream, long index, std::string codec, std::string fullCodec, std::string format, long width, long height, std::string sampleAspectRatio, std::string displayAspectRatio, std::string bitrate, std::string averageFrameRate, std::string realFrameRate, std::string timeBase, std::string codecTimeBase) {
+ assert(stream != nullptr);
+ assertNumber(index, stream->getIndex());
+ assertString("video", stream->getType());
+
+ assertString(codec, stream->getCodec());
+ assertString(fullCodec, stream->getCodecLong());
+
+ assertString(format, stream->getFormat());
+
+ assertNumber(width, stream->getWidth());
+ assertNumber(height, stream->getHeight());
+ assertString(sampleAspectRatio, stream->getSampleAspectRatio());
+ assertString(displayAspectRatio, stream->getDisplayAspectRatio());
+
+ assertString(bitrate, stream->getBitrate());
+
+ assertString(averageFrameRate, stream->getAverageFrameRate());
+ assertString(realFrameRate, stream->getRealFrameRate());
+ assertString(timeBase, stream->getTimeBase());
+ assertString(codecTimeBase, stream->getCodecTimeBase());
+}
+
+void assertAudioStream(std::shared_ptr stream, long index, std::string codec, std::string fullCodec, std::string sampleRate, std::string channelLayout, std::string sampleFormat, std::string bitrate) {
+ assert(stream != nullptr);
+ assertNumber(index, stream->getIndex());
+ assertString("audio", stream->getType());
+
+ assertString(codec, stream->getCodec());
+ assertString(fullCodec, stream->getCodecLong());
+
+ assertString(sampleRate, stream->getSampleRate());
+ assertString(channelLayout, stream->getChannelLayout());
+ assertString(sampleFormat, stream->getSampleFormat());
+ assertString(bitrate, stream->getBitrate());
+}
+
+void assertChapter(std::shared_ptr chapter, long id, std::string timeBase, long start, std::string startTime, long end, std::string endTime) {
+ assert(chapter != nullptr);
+ assertNumber(id, chapter->getId());
+ assertString(timeBase, chapter->getTimeBase());
+
+ assertNumber(start, chapter->getStart());
+ assertString(startTime, chapter->getStartTime());
+
+ assertNumber(end, chapter->getEnd());
+ assertString(endTime, chapter->getEndTime());
+
+ std::shared_ptr tags = chapter->getTags();
+ assert(tags);
+
+ assert(1 == tags->MemberCount());
+}
+
+void assertMediaInput(std::shared_ptr mediaInformation, std::string expectedFormat, std::string expectedFilename) {
+ std::shared_ptr format = mediaInformation->getFormat();
+ std::shared_ptr filename = mediaInformation->getFilename();
+ if (format == nullptr) {
+ assert(expectedFormat == "");
+ } else {
+ assert(*format == expectedFormat);
+ }
+ if (filename == nullptr) {
+ assert(expectedFilename == "");
+ } else {
+ assert(*filename == expectedFilename);
+ }
+}
+
+void assertMediaDuration(std::shared_ptr mediaInformation, std::string expectedDuration, std::string expectedStartTime, std::string expectedBitrate) {
+ std::shared_ptr duration = mediaInformation->getDuration();
+ std::shared_ptr startTime = mediaInformation->getStartTime();
+ std::shared_ptr bitrate = mediaInformation->getBitrate();
+
+ assertString(expectedDuration, duration);
+ assertString(expectedStartTime, startTime);
+ assertString(expectedBitrate, bitrate);
+}
+
+void assertTag(std::shared_ptr mediaInformation, std::string expectedKey, std::string expectedValue) {
+ std::shared_ptr tags = mediaInformation->getTags();
+ assert(tags);
+
+ auto value = (*tags)[expectedKey.c_str()].GetString();
+ assert(value);
+
+ assert(value == expectedValue);
+}
+
+void assertStreamTag(std::shared_ptr streamInformation, std::string expectedKey, std::string expectedValue) {
+ std::shared_ptr tags = streamInformation->getTags();
+ assert(tags);
+
+ auto value = (*tags)[expectedKey.c_str()].GetString();
+ assert(value);
+
+ assert(value == expectedValue);
+}
+
+void testMediaInformationMp3() {
+ std::shared_ptr mediaInformation = MediaInformationJsonParser::from(MEDIA_INFORMATION_MP3);
+
+ assert(mediaInformation);
+ assertMediaInput(mediaInformation, "mp3", "sample.mp3");
+ assertMediaDuration(mediaInformation, "327.549388", "0.011995", "320026");
+
+ assertTag(mediaInformation, "comment", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit finito.");
+ assertTag(mediaInformation, "album", "Impact");
+ assertTag(mediaInformation, "title", "Impact Moderato");
+ assertTag(mediaInformation, "artist", "Kevin MacLeod");
+
+ std::shared_ptr>> streams = mediaInformation->getStreams();
+ assert(streams);
+ assert(1 == streams->size());
+
+ assertAudioStream((*streams)[0], 0, "mp3", "MP3 (MPEG audio layer 3)", "44100", "stereo", "fltp", "320000");
+
+ std::shared_ptr>> chapters = mediaInformation->getChapters();
+ assert(chapters);
+ assert(7 == chapters->size());
+
+ assertChapter((*chapters)[0], 0, "1/22050", 0, "0.000000", 11158238, "506.042540");
+ assertChapter((*chapters)[1], 1, "1/22050", 11158238, "506.042540", 21433051, "972.020454");
+}
+
+void testMediaInformationJpg() {
+ std::shared_ptr mediaInformation = MediaInformationJsonParser::from(MEDIA_INFORMATION_JPG);
+
+ assert(mediaInformation);
+ assertMediaInput(mediaInformation, "image2", "sample.jpg");
+ assertMediaDuration(mediaInformation, "0.040000", "0.000000", "331810000");
+
+ std::shared_ptr>> streams = mediaInformation->getStreams();
+ assert(streams);
+ assert(1 == streams->size());
+
+ assertVideoStream((*streams)[0], 0, "mjpeg", "Motion JPEG", "yuvj444p", 1496, 1729, "1:1", "1496:1729", "", "0/0", "25/1", "1/25", "0/1");
+}
+
+void testMediaInformationGif() {
+ std::shared_ptr mediaInformation = MediaInformationJsonParser::from(MEDIA_INFORMATION_GIF);
+
+ assert(mediaInformation);
+ assertMediaInput(mediaInformation, "gif", "sample.gif");
+ assertMediaDuration(mediaInformation, "3.960000", "0.000000", "2023672");
+
+ std::shared_ptr>> streams = mediaInformation->getStreams();
+ assert(streams);
+ assert(1 == streams->size());
+
+ assertVideoStream((*streams)[0], 0, "gif", "CompuServe GIF (Graphics Interchange Format)", "bgra", 400, 400, "", "", "", "133/12", "100/9", "1/100", "12/133");
+}
+
+void testMediaInformationMp4() {
+ std::shared_ptr mediaInformation = MediaInformationJsonParser::from(MEDIA_INFORMATION_MP4);
+
+ assert(mediaInformation);
+ assertMediaInput(mediaInformation, "mov,mp4,m4a,3gp,3g2,mj2", "sample.mp4");
+ assertMediaDuration(mediaInformation, "14.000000", "0.000000", "9168090");
+
+ assertTag(mediaInformation, "major_brand", "isom");
+ assertTag(mediaInformation, "minor_version", "512");
+ assertTag(mediaInformation, "compatible_brands", "isomiso2avc1mp41");
+ assertTag(mediaInformation, "encoder", "Lavf58.33.100");
+
+ std::shared_ptr>> streams = mediaInformation->getStreams();
+ assert(streams);
+ assert(1 == streams->size());
+
+ assertVideoStream((*streams)[0], 0, "h264", "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", "yuv420p", 1280, 720, "1:1", "16:9", "9166570", "30/1", "30/1", "1/15360", "1/60");
+
+ assertStreamTag((*streams)[0], "language", "und");
+ assertStreamTag((*streams)[0], "handler_name", "VideoHandler");
+}
+
+void testMediaInformationPng() {
+ std::shared_ptr mediaInformation = MediaInformationJsonParser::from(MEDIA_INFORMATION_PNG);
+
+ assert(mediaInformation);
+ assertMediaInput(mediaInformation, "png_pipe", "sample.png");
+ assertMediaDuration(mediaInformation, "", "", "");
+
+ std::shared_ptr>> streams = mediaInformation->getStreams();
+ assert(streams);
+ assert(1 == streams->size());
+
+ assertVideoStream((*streams)[0], 0, "png", "PNG (Portable Network Graphics) image", "pal8", 1198, 1198, "1:1", "1:1", "", "0/0", "25/1", "1/25", "0/1");
+}
+
+void testMediaInformationOgg() {
+ std::shared_ptr mediaInformation = MediaInformationJsonParser::from(MEDIA_INFORMATION_OGG);
+
+ assert(mediaInformation);
+ assertMediaInput(mediaInformation, "ogg", "sample.ogg");
+ assertMediaDuration(mediaInformation, "32.996875", "0.000000", "6757958");
+
+ std::shared_ptr>> streams = mediaInformation->getStreams();
+ assert(streams);
+ assert(2 == streams->size());
+
+ assertVideoStream((*streams)[0], 0, "theora", "Theora", "yuv420p", 1920, 1080, "", "", "", "25/1", "25/1", "1/25", "1/25");
+ assertAudioStream((*streams)[1], 1, "vorbis", "Vorbis", "48000", "stereo", "fltp", "80000");
+
+ assertStreamTag((*streams)[0], "ENCODER", "ffmpeg2theora 0.19");
+ assertStreamTag((*streams)[1], "ENCODER", "ffmpeg2theora 0.19");
+}
+
+void testMediaInformationJsonParser(void) {
+ testMediaInformationMp3();
+ testMediaInformationJpg();
+ testMediaInformationGif();
+ testMediaInformationMp4();
+ testMediaInformationPng();
+ testMediaInformationOgg();
+
+ std::cout << "MediaInformationJsonParserTest passed." << std::endl;
+}
diff --git a/linux/test-app-local-dependency/src/MediaInformationParserTest.h b/linux/test-app-local-dependency/src/MediaInformationParserTest.h
new file mode 100644
index 0000000..85255f4
--- /dev/null
+++ b/linux/test-app-local-dependency/src/MediaInformationParserTest.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018-2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include
+#include
+#include
+
+void assertNumber(long expected, std::shared_ptr real);
+void assertString(std::string expected, std::shared_ptr real);
+void assertString(std::string expected, std::string real);
+
+/**
+ * All json parser tests are initiated from this method
+ */
+void testMediaInformationJsonParser();
diff --git a/linux/test-app-local-dependency/src/OtherTab.cpp b/linux/test-app-local-dependency/src/OtherTab.cpp
new file mode 100644
index 0000000..d258deb
--- /dev/null
+++ b/linux/test-app-local-dependency/src/OtherTab.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "OtherTab.h"
+#include "Application.h"
+#include "Constants.h"
+#include "Popup.h"
+#include "Video.h"
+#include
+#include
+
+using namespace ffmpegkit;
+
+static gboolean showTestSuccessPopup(const std::pair* parameters) {
+ Gtk::Window* window = parameters->first;
+ auto messageDetail = parameters->second;
+ ffmpegkittest::Popup::show(window, Gtk::MESSAGE_INFO, messageDetail);
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean showTestFailedPopup(const std::pair* parameters) {
+ Gtk::Window* window = parameters->first;
+ auto messageDetail = parameters->second;
+ ffmpegkittest::Popup::show(window, Gtk::MESSAGE_ERROR, messageDetail);
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean appendLog(const std::pair>* parameters) {
+ ffmpegkittest::OtherTab* otherTab = parameters->first;
+ auto log = parameters->second;
+ otherTab->appendOutput(log->getMessage());
+ delete parameters;
+ return FALSE;
+}
+
+ffmpegkittest::OtherTab::OtherTab() : selectedTest(-1) {
+ testModel = Gtk::ListStore::create(testModelColumn);
+ test.set_model(testModel);
+ test.set_size_request(240, 30);
+ test.signal_changed().connect(sigc::mem_fun(*this, &OtherTab::onTestChanged));
+ Util::applyComboBoxStyle(test);
+
+ initTestData();
+
+ runButton.set_label("RUN");
+ runButton.set_size_request(120, 30);
+ runButton.set_tooltip_text(Constants::OtherTestTooltipText);
+ runButton.signal_clicked().connect(sigc::mem_fun(*this, &OtherTab::runTest));
+ Util::applyButtonStyle(runButton);
+ runButtonBox.pack_start(runButton, Gtk::PACK_EXPAND_PADDING);
+
+ outputText.set_editable(false);
+ Util::applyOutputTextStyle(outputText);
+ outputTextWindow.add(outputText);
+
+ pack_start(testBox, Gtk::PACK_SHRINK);
+ pack_start(runButtonBox, Gtk::PACK_SHRINK);
+ add(outputTextWindow);
+}
+
+void ffmpegkittest::OtherTab::setActive() {
+ std::cout << "Other Tab Activated" << std::endl;
+ FFmpegKitConfig::enableLogCallback(nullptr);
+ FFmpegKitConfig::enableStatisticsCallback(nullptr);
+}
+
+void ffmpegkittest::OtherTab::setParentWindow(Gtk::Window* parentWindow) {
+ this->parentWindow = parentWindow;
+}
+
+void ffmpegkittest::OtherTab::appendOutput(const std::string& string) {
+ outputText.get_buffer()->set_text(outputText.get_buffer()->get_text() + string);
+ Glib::RefPtr adj = outputText.get_vadjustment();
+ adj->set_value(adj->get_upper());
+}
+
+void ffmpegkittest::OtherTab::clearOutput() {
+ outputText.get_buffer()->set_text("");
+}
+
+void ffmpegkittest::OtherTab::initTestData() {
+ auto row = *(testModel->append());
+ row[testModelColumn.columnId] = "1";
+ row[testModelColumn.columnName] = "chromaprint";
+
+ row = *(testModel->append());
+ row[testModelColumn.columnId] = "2";
+ row[testModelColumn.columnName] = "dav1d";
+
+ row = *(testModel->append());
+ row[testModelColumn.columnId] = "3";
+ row[testModelColumn.columnName] = "webp";
+
+ row = *(testModel->append());
+ row[testModelColumn.columnId] = "4";
+ row[testModelColumn.columnName] = "zscale";
+
+ test.pack_start(testModelColumn.columnName);
+ test.set_entry_text_column(testModelColumn.columnId);
+ test.set_active(0);
+
+ testBox.pack_start(test, Gtk::PACK_EXPAND_PADDING);
+}
+
+void ffmpegkittest::OtherTab::onTestChanged() {
+ int rowNumber = test.get_active_row_number();
+ if (rowNumber != -1) {
+ selectedTest = rowNumber;
+ }
+}
+
+std::string ffmpegkittest::OtherTab::getSelectedTest() {
+ switch(selectedTest) {
+ case 0: return "chromaprint";
+ case 1: return "dav1d";
+ case 2: return "webp";
+ case 3: return "zscale";
+ default: return "";
+ }
+}
+
+void ffmpegkittest::OtherTab::runTest() {
+ clearOutput();
+
+ std::string selectedTest = this->getSelectedTest();
+ if (selectedTest.compare("chromaprint") == 0) {
+ testChromaprint();
+ } else if (selectedTest.compare("dav1d") == 0) {
+ testDav1d();
+ } else if (selectedTest.compare("webp") == 0) {
+ testWebp();
+ } else if (selectedTest.compare("zscale") == 0) {
+ testZscale();
+ }
+}
+
+void ffmpegkittest::OtherTab::testChromaprint() {
+ std::cout << "Testing 'chromaprint' mutex." << std::endl;
+
+ std::string audioSampleFile = getChromaprintSampleFile();
+ std::remove(audioSampleFile.c_str());
+
+ std::string ffmpegCommand = "-hide_banner -y -f lavfi -i sine=frequency=1000:duration=5 -c:a pcm_s16le " + audioSampleFile;
+
+ std::cout << "Creating audio sample with '" << ffmpegCommand << "'." << std::endl;
+
+ FFmpegKit::executeAsync(ffmpegCommand, [this,audioSampleFile](auto session) {
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(session->getState()) << " and rc " << session->getReturnCode() << "." << session->getFailStackTrace() << std::endl;
+
+ if (ReturnCode::isSuccess(session->getReturnCode())) {
+ std::cout << "AUDIO sample created." << std::endl;
+
+ std::string chromaprintCommand = "-hide_banner -y -i " + audioSampleFile + " -f chromaprint -fp_format 2 " + getChromaprintOutputFile();
+
+ std::cout << "FFmpeg process started with arguments: '" << chromaprintCommand << "'." << std::endl;
+
+ FFmpegKit::executeAsync(chromaprintCommand, [this](auto secondSession) {
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(secondSession->getState()) << " and rc " << secondSession->getReturnCode() << "." << secondSession->getFailStackTrace() << std::endl;
+ if (ReturnCode::isSuccess(secondSession->getReturnCode())) {
+ g_idle_add((GSourceFunc)showTestSuccessPopup, new std::pair(this->parentWindow, "Testing chromaprint completed successfully."));
+ } else {
+ g_idle_add((GSourceFunc)showTestFailedPopup, new std::pair(this->parentWindow, "Testing chromaprint failed. Please check logs for the details."));
+ }
+ }, [this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ }, nullptr);
+
+ } else {
+ g_idle_add((GSourceFunc)showTestFailedPopup, new std::pair(this->parentWindow, "Creating AUDIO sample failed. Please check logs for the details."));
+ }
+ });
+}
+
+void ffmpegkittest::OtherTab::testDav1d() {
+ std::cout << "Testing decoding 'av1' codec." << std::endl;
+
+ std::string ffmpegCommand = std::string("-hide_banner -y -i ") + Dav1dTestDefaultUrl + " " + getDav1dOutputFile();
+
+ std::cout << "FFmpeg process started with arguments: '" << ffmpegCommand << "'." << std::endl;
+
+ FFmpegKit::executeAsync(ffmpegCommand, [this](auto session) {
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(session->getState()) << " and rc " << session->getReturnCode() << "." << session->getFailStackTrace() << std::endl;
+ }, [this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ }, nullptr);
+}
+
+void ffmpegkittest::OtherTab::testWebp() {
+ std::string imageFile = Application::getApplicationInstallDirectory() + "/share/images/machupicchu.jpg";
+ std::string outputFile = Application::getApplicationCacheDirectory() + "/video.webp";
+
+ std::cout << "Testing 'webp' codec." << std::endl;
+
+ std::string ffmpegCommand = "-hide_banner -y -i " + imageFile + " " + outputFile;
+
+ std::cout << "FFmpeg process started with arguments: '" << ffmpegCommand << "'." << std::endl;
+
+ auto session = FFmpegKit::executeAsync(ffmpegCommand, [this](auto session) {
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(session->getState()) << " and rc " << session->getReturnCode() << "." << session->getFailStackTrace() << std::endl;
+
+ if (ReturnCode::isSuccess(session->getReturnCode())) {
+ g_idle_add((GSourceFunc)showTestSuccessPopup, new std::pair(this->parentWindow, "Encode webp completed successfully."));
+ } else {
+ g_idle_add((GSourceFunc)showTestFailedPopup, new std::pair(this->parentWindow, "Encode webp failed. Please check logs for the details."));
+ }
+ }, [this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ }, nullptr);
+}
+
+void ffmpegkittest::OtherTab::testZscale() {
+ std::string videoFile = Application::getApplicationCacheDirectory() + "/video.mp4";
+ std::string zscaledVideoFile = Application::getApplicationCacheDirectory() + "/video-zscaled.mp4";
+
+ std::cout << "Testing 'zscale' filter with video file created on the Video tab." << std::endl;
+
+ std::string ffmpegCommand = Video::generateZscaleVideoScript(videoFile, zscaledVideoFile);
+
+ std::cout << "FFmpeg process started with arguments: '" << ffmpegCommand << "'." << std::endl;
+
+ auto session = FFmpegKit::executeAsync(ffmpegCommand, [this](auto session) {
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(session->getState()) << " and rc " << session->getReturnCode() << "." << session->getFailStackTrace() << std::endl;
+
+ if (ReturnCode::isSuccess(session->getReturnCode())) {
+ g_idle_add((GSourceFunc)showTestSuccessPopup, new std::pair(this->parentWindow, "zscale completed successfully."));
+ } else {
+ g_idle_add((GSourceFunc)showTestFailedPopup, new std::pair(this->parentWindow, "zscale failed. Please check logs for the details."));
+ }
+ }, [this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ }, nullptr);
+}
+
+std::string ffmpegkittest::OtherTab::getChromaprintSampleFile() {
+ return Application::getApplicationCacheDirectory() + "/audio-sample.wav";
+}
+
+std::string ffmpegkittest::OtherTab::getDav1dOutputFile() {
+ return Application::getApplicationCacheDirectory() + "/video.mp4";
+}
+
+std::string ffmpegkittest::OtherTab::getChromaprintOutputFile() {
+ return Application::getApplicationCacheDirectory() + "/chromaprint.txt";
+}
diff --git a/linux/test-app-local-dependency/src/OtherTab.h b/linux/test-app-local-dependency/src/OtherTab.h
new file mode 100644
index 0000000..b8d9b24
--- /dev/null
+++ b/linux/test-app-local-dependency/src/OtherTab.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_OTHER_TAB_H
+#define FFMPEG_KIT_TEST_OTHER_TAB_H
+
+#include "Util.h"
+#include
+
+namespace ffmpegkittest {
+
+ class OtherTab: public Gtk::VBox {
+ public:
+
+ static constexpr const char* Dav1dTestDefaultUrl = "http://download.opencontent.netflix.com.s3.amazonaws.com/AV1/Sparks/Sparks-5994fps-AV1-10bit-960x540-film-grain-synthesis-854kbps.obu";
+
+ OtherTab();
+ void setActive();
+ void setParentWindow(Gtk::Window* parentWindow);
+ void appendOutput(const std::string& string);
+
+ private:
+ void clearOutput();
+ void initTestData();
+ void onTestChanged();
+ std::string getSelectedTest();
+ void runTest();
+ void testChromaprint();
+ void testDav1d();
+ void testWebp();
+ void testZscale();
+ std::string getChromaprintSampleFile();
+ std::string getDav1dOutputFile();
+ std::string getChromaprintOutputFile();
+
+ Glib::RefPtr testModel;
+ ComboBoxModelColumn testModelColumn;
+ Gtk::ComboBox test;
+ Gtk::HBox testBox;
+ int selectedTest;
+ Gtk::Button runButton;
+ Gtk::HBox runButtonBox;
+ Gtk::TextView outputText;
+ Gtk::ScrolledWindow outputTextWindow;
+ Gtk::Window* parentWindow;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_OTHER_TAB_H
diff --git a/linux/test-app-local-dependency/src/PipeTab.cpp b/linux/test-app-local-dependency/src/PipeTab.cpp
new file mode 100644
index 0000000..13d3854
--- /dev/null
+++ b/linux/test-app-local-dependency/src/PipeTab.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "PipeTab.h"
+#include "Application.h"
+#include "Constants.h"
+#include "Log.h"
+#include "Popup.h"
+#include "Statistics.h"
+#include "Video.h"
+#include
+#include
+#include
+
+using namespace ffmpegkit;
+
+static gboolean showCreateFailedPopup(Gtk::Window* window) {
+ ffmpegkittest::Popup::show(window, Gtk::MESSAGE_ERROR, "Create failed. Please check logs for the details.");
+ return FALSE;
+}
+
+static gboolean saveStatistics(const std::pair>* parameters) {
+ ffmpegkittest::PipeTab* videoTab = parameters->first;
+ auto statistics = parameters->second;
+ videoTab->updateProgressDialog(statistics);
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean appendLog(const std::pair>* parameters) {
+ ffmpegkittest::PipeTab* videoTab = parameters->first;
+ auto log = parameters->second;
+ videoTab->appendOutput(log->getMessage());
+ delete parameters;
+ return FALSE;
+}
+
+static void startAsyncCatImageProcess(std::string imagePath, std::shared_ptr namedPipePath) {
+ auto thread = std::thread([imagePath,namedPipePath]() {
+ std::string asyncCommand = "cat " + imagePath + " > " + *namedPipePath;
+
+ std::cout << "Starting async cat image command: " << asyncCommand << std::endl;
+
+ int rc = system(asyncCommand.c_str());
+
+ std::cout << "Async cat image command: " << asyncCommand << " exited with " << rc << "." << std::endl;
+ });
+ thread.detach();
+}
+
+ffmpegkittest::PipeTab::PipeTab() : statistics(nullptr) {
+ createButton.set_label("CREATE");
+ createButton.set_size_request(120, 30);
+ createButton.set_tooltip_text(Constants::PipeTestTooltipText);
+ createButton.signal_clicked().connect(sigc::mem_fun(*this, &PipeTab::createVideo));
+ Util::applyButtonStyle(createButton);
+ createButtonBox.pack_start(createButton, Gtk::PACK_EXPAND_PADDING);
+
+ outputText.set_editable(false);
+ Util::applyOutputTextStyle(outputText);
+ outputTextWindow.add(outputText);
+
+ pack_start(createButtonBox, Gtk::PACK_SHRINK);
+ add(outputTextWindow);
+}
+
+void ffmpegkittest::PipeTab::setActive() {
+ std::cout << "Pipe Tab Activated" << std::endl;
+ FFmpegKitConfig::enableLogCallback([this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ });
+ FFmpegKitConfig::enableStatisticsCallback([this](auto statistics) {
+ g_idle_add((GSourceFunc)saveStatistics, new std::pair>(this, statistics));
+ });
+}
+
+void ffmpegkittest::PipeTab::setParentWindow(Gtk::Window* parentWindow) {
+ this->parentWindow = parentWindow;
+}
+
+void ffmpegkittest::PipeTab::appendOutput(const std::string& string) {
+ outputText.get_buffer()->set_text(outputText.get_buffer()->get_text() + string);
+ Glib::RefPtr adj = outputText.get_vadjustment();
+ adj->set_value(adj->get_upper());
+}
+
+void ffmpegkittest::PipeTab::updateProgressDialog(const std::shared_ptr statistics) {
+ if (statistics == nullptr || statistics->getTime() < 0) {
+ return;
+ }
+
+ this->statistics = statistics;
+ int timeInMilliseconds = this->statistics->getTime();
+ int totalVideoDuration = 9000;
+ double completePercentage = timeInMilliseconds*100/totalVideoDuration;
+ // progressDialog.update(completePercentage);
+ std::cout << "Creating video: " << completePercentage << "%" << std::endl;
+}
+
+void ffmpegkittest::PipeTab::clearOutput() {
+ outputText.get_buffer()->set_text("");
+}
+
+void ffmpegkittest::PipeTab::createVideo() {
+ clearOutput();
+
+ std::string image1File = Application::getApplicationInstallDirectory() + "/share/images/machupicchu.jpg";
+ std::string image2File = Application::getApplicationInstallDirectory() + "/share/images/pyramid.jpg";
+ std::string image3File = Application::getApplicationInstallDirectory() + "/share/images/stonehenge.jpg";
+ std::string videoFile = getVideoFile();
+
+ auto pipe1 = FFmpegKitConfig::registerNewFFmpegPipe();
+ auto pipe2 = FFmpegKitConfig::registerNewFFmpegPipe();
+ auto pipe3 = FFmpegKitConfig::registerNewFFmpegPipe();
+
+ std::remove(videoFile.c_str());
+
+ std::cout << "Testing PIPE with 'mpeg4' codec" << std::endl;
+
+ showProgressDialog();
+
+ std::string ffmpegCommand = Video::generateCreateVideoWithPipesScript(*pipe1, *pipe2, *pipe3, videoFile);
+
+ std::cout << "FFmpeg process started with arguments: '" << ffmpegCommand << "'." << std::endl;
+
+ auto session = FFmpegKit::executeAsync(ffmpegCommand, [this,pipe1,pipe2,pipe3](auto session) {
+ const auto state = session->getState();
+ auto returnCode = session->getReturnCode();
+
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(state) << " and rc " << returnCode << "." << session->getFailStackTrace() << std::endl;
+
+ this->hideProgressDialog();
+
+ // CLOSE PIPES
+ FFmpegKitConfig::closeFFmpegPipe(pipe1->c_str());
+ FFmpegKitConfig::closeFFmpegPipe(pipe2->c_str());
+ FFmpegKitConfig::closeFFmpegPipe(pipe3->c_str());
+
+ if (ReturnCode::isSuccess(returnCode)) {
+ std::cout << "Create completed successfully." << std::endl;
+ } else {
+ g_idle_add((GSourceFunc)showCreateFailedPopup, this->parentWindow);
+ }
+ }, [this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ }, [this](auto statistics) {
+ g_idle_add((GSourceFunc)saveStatistics, new std::pair>(this, statistics));
+ });
+
+ // START ASYNC PROCESSES AFTER INITIATING FFMPEG COMMAND
+ startAsyncCatImageProcess(image1File, pipe1);
+ startAsyncCatImageProcess(image2File, pipe2);
+ startAsyncCatImageProcess(image3File, pipe3);
+}
+
+std::string ffmpegkittest::PipeTab::getVideoFile() {
+ return Application::getApplicationCacheDirectory() + "/video.mp4";
+}
+
+void ffmpegkittest::PipeTab::showProgressDialog() {
+ // progressDialog.show(this->get_parent_window());
+}
+
+void ffmpegkittest::PipeTab::hideProgressDialog() {
+ // progressDialog.hide();
+}
diff --git a/linux/test-app-local-dependency/src/PipeTab.h b/linux/test-app-local-dependency/src/PipeTab.h
new file mode 100644
index 0000000..262a6d8
--- /dev/null
+++ b/linux/test-app-local-dependency/src/PipeTab.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_PIPE_TAB_H
+#define FFMPEG_KIT_TEST_PIPE_TAB_H
+
+#include "ProgressDialog.h"
+#include "Statistics.h"
+#include "Util.h"
+#include
+
+namespace ffmpegkittest {
+
+ class PipeTab: public Gtk::VBox {
+ public:
+ PipeTab();
+ void setActive();
+ void setParentWindow(Gtk::Window* parentWindow);
+ void appendOutput(const std::string& string);
+ void updateProgressDialog(const std::shared_ptr statistics);
+
+ private:
+ void clearOutput();
+ void createVideo();
+ std::string getVideoFile();
+ void showProgressDialog();
+ void hideProgressDialog();
+
+ Gtk::Button createButton;
+ Gtk::HBox createButtonBox;
+ Gtk::TextView outputText;
+ Gtk::ScrolledWindow outputTextWindow;
+ ffmpegkittest::ProgressDialog progressDialog;
+ Gtk::Window* parentWindow;
+ std::shared_ptr statistics;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_PIPE_TAB_H
diff --git a/linux/test-app-local-dependency/src/Popup.cpp b/linux/test-app-local-dependency/src/Popup.cpp
new file mode 100644
index 0000000..d18fa20
--- /dev/null
+++ b/linux/test-app-local-dependency/src/Popup.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "Popup.h"
+
+void ffmpegkittest::Popup::show(Gtk::Window* window, enum Gtk::MessageType messageType, const std::string& detail) {
+ if (window != nullptr) {
+ std::string data;
+ if (messageType == Gtk::MESSAGE_INFO) {
+ data = "Information";
+ } else {
+ data = "Error";
+ }
+ Gtk::MessageDialog dialog(*window, data, false, messageType, Gtk::BUTTONS_OK);
+ dialog.set_secondary_text(detail);
+ dialog.run();
+ }
+}
diff --git a/linux/test-app-local-dependency/src/Popup.h b/linux/test-app-local-dependency/src/Popup.h
new file mode 100644
index 0000000..ff0a184
--- /dev/null
+++ b/linux/test-app-local-dependency/src/Popup.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_POPUP_H
+#define FFMPEG_KIT_TEST_POPUP_H
+
+#include
+#include
+
+namespace ffmpegkittest {
+
+ class Popup {
+ public:
+ static void show(Gtk::Window* window, enum Gtk::MessageType messageType, const std::string& detail);
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_POPUP_H
diff --git a/linux/test-app-local-dependency/src/ProgressDialog.cpp b/linux/test-app-local-dependency/src/ProgressDialog.cpp
new file mode 100644
index 0000000..f991134
--- /dev/null
+++ b/linux/test-app-local-dependency/src/ProgressDialog.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "ProgressDialog.h"
+#include
+
+namespace ffmpegkittest {
+
+ gboolean runDialog(ffmpegkittest::ProgressDialog* progressDialog) {
+ progressDialog->dialog.run();
+ return FALSE;
+ }
+
+}
+
+ffmpegkittest::ProgressDialog::ProgressDialog() : dialog("", Gtk::DIALOG_MODAL), alignment(0.5, 0.5, 0, 0) {
+ progressBar.set_show_text(false);
+ progressBar.set_fraction(0.0);
+ progressBar.set_size_request(100, 20);
+ alignment.add(progressBar);
+
+ dialog.set_default_size(300, 60);
+ dialog.get_content_area()->set_border_width(10);
+ dialog.get_content_area()->pack_start(alignment, Gtk::PACK_EXPAND_WIDGET, 5);
+ dialog.show_all_children(true);
+}
+
+void ffmpegkittest::ProgressDialog::show(const Glib::RefPtr parentWindow) {
+ dialog.set_parent_window(parentWindow);
+ g_idle_add((GSourceFunc)runDialog, this);
+}
+
+void ffmpegkittest::ProgressDialog::update(double fraction) {
+ progressBar.set_fraction(fraction);
+}
+
+void ffmpegkittest::ProgressDialog::hide() {
+ dialog.hide();
+}
diff --git a/linux/test-app-local-dependency/src/ProgressDialog.h b/linux/test-app-local-dependency/src/ProgressDialog.h
new file mode 100644
index 0000000..448cfaf
--- /dev/null
+++ b/linux/test-app-local-dependency/src/ProgressDialog.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_PROGRESS_DIALOG_H
+#define FFMPEG_KIT_TEST_PROGRESS_DIALOG_H
+
+#include
+#include
+
+namespace ffmpegkittest {
+
+ class ProgressDialog {
+ public:
+ ProgressDialog();
+ void show(const Glib::RefPtr parentWindow);
+ void update(double fraction);
+ void hide();
+
+ friend gboolean runDialog(ProgressDialog* progressDialog);
+
+ private:
+ void run();
+
+ Gtk::Dialog dialog;
+ Gtk::Alignment alignment;
+ Gtk::ProgressBar progressBar;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_PROGRESS_DIALOG_H
diff --git a/linux/test-app-local-dependency/src/SubtitleTab.cpp b/linux/test-app-local-dependency/src/SubtitleTab.cpp
new file mode 100644
index 0000000..03b36ae
--- /dev/null
+++ b/linux/test-app-local-dependency/src/SubtitleTab.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "SubtitleTab.h"
+#include "Application.h"
+#include "Constants.h"
+#include "Log.h"
+#include "Popup.h"
+#include "Statistics.h"
+#include "Video.h"
+#include
+#include
+
+using namespace ffmpegkit;
+
+enum State {
+ StateIdle,
+ StateCreating,
+ StateBurning
+};
+
+static State state = StateIdle;
+
+static long sessionId = -1;
+
+static gboolean showBurningCancelledPopup(const std::pair* parameters) {
+ Gtk::Window* window = parameters->first;
+ auto messageDetail = parameters->second;
+ ffmpegkittest::Popup::show(window, Gtk::MESSAGE_INFO, messageDetail);
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean showBurningFailedPopup(const std::pair* parameters) {
+ Gtk::Window* window = parameters->first;
+ auto messageDetail = parameters->second;
+ ffmpegkittest::Popup::show(window, Gtk::MESSAGE_ERROR, messageDetail);
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean saveStatistics(const std::pair>* parameters) {
+ ffmpegkittest::SubtitleTab* subtitleTab = parameters->first;
+ auto statistics = parameters->second;
+ subtitleTab->updateProgressDialog(statistics);
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean appendLog(const std::pair>* parameters) {
+ ffmpegkittest::SubtitleTab* videoTab = parameters->first;
+ auto log = parameters->second;
+ videoTab->appendOutput(log->getMessage());
+ delete parameters;
+ return FALSE;
+}
+
+ffmpegkittest::SubtitleTab::SubtitleTab() : statistics(nullptr) {
+ encodeButton.set_label("BURN SUBTITLES");
+ encodeButton.set_size_request(120, 30);
+ encodeButton.set_tooltip_text(Constants::SubtitleTestEncodeTooltipText);
+ encodeButton.signal_clicked().connect(sigc::mem_fun(*this, &SubtitleTab::burnSubtitles));
+ Util::applyButtonStyle(encodeButton);
+ cancelButton.set_label("CANCEL");
+ cancelButton.set_size_request(120, 30);
+ cancelButton.set_tooltip_text(Constants::SubtitleTestCancelTooltipText);
+ cancelButton.signal_clicked().connect(sigc::mem_fun(*this, &SubtitleTab::cancel));
+ Util::applyButtonStyle(cancelButton);
+ buttonBox.pack_start(encodeButton, Gtk::PACK_EXPAND_PADDING);
+ buttonBox.pack_start(cancelButton, Gtk::PACK_EXPAND_PADDING);
+
+ outputText.set_editable(false);
+ Util::applyOutputTextStyle(outputText);
+ outputTextWindow.add(outputText);
+
+ pack_start(buttonBox, Gtk::PACK_SHRINK);
+ add(outputTextWindow);
+
+ state = StateIdle;
+}
+
+void ffmpegkittest::SubtitleTab::setActive() {
+ std::cout << "Subtitle Tab Activated" << std::endl;
+ FFmpegKitConfig::enableLogCallback([this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ });
+ FFmpegKitConfig::enableStatisticsCallback([this](auto statistics) {
+ g_idle_add((GSourceFunc)saveStatistics, new std::pair>(this, statistics));
+ });
+}
+
+void ffmpegkittest::SubtitleTab::setParentWindow(Gtk::Window* parentWindow) {
+ this->parentWindow = parentWindow;
+}
+
+void ffmpegkittest::SubtitleTab::appendOutput(const std::string& string) {
+ outputText.get_buffer()->set_text(outputText.get_buffer()->get_text() + string);
+ Glib::RefPtr adj = outputText.get_vadjustment();
+ adj->set_value(adj->get_upper());
+}
+
+void ffmpegkittest::SubtitleTab::updateProgressDialog(const std::shared_ptr statistics) {
+ if (statistics == nullptr || statistics->getTime() < 0) {
+ return;
+ }
+
+ this->statistics = statistics;
+ int timeInMilliseconds = this->statistics->getTime();
+ int totalVideoDuration = 9000;
+ double completePercentage = timeInMilliseconds*100/totalVideoDuration;
+ // progressDialog.update(completePercentage);
+ if (state == StateCreating) {
+ std::cout << "Creating video: " << completePercentage << "%" << std::endl;
+ } else if (state == StateBurning) {
+ std::cout << "Burning subtitles: " << completePercentage << "%" << std::endl;
+ }
+}
+
+void ffmpegkittest::SubtitleTab::clearOutput() {
+ outputText.get_buffer()->set_text("");
+}
+
+void ffmpegkittest::SubtitleTab::burnSubtitles() {
+ clearOutput();
+
+ std::string image1File = Application::getApplicationInstallDirectory() + "/share/images/machupicchu.jpg";
+ std::string image2File = Application::getApplicationInstallDirectory() + "/share/images/pyramid.jpg";
+ std::string image3File = Application::getApplicationInstallDirectory() + "/share/images/stonehenge.jpg";
+ std::string videoFile = getVideoFile();
+ std::string videoWithSubtitlesFile = getVideoWithSubtitlesFile();
+
+ std::cout << "Testing SUBTITLE burning." << std::endl;
+
+ showCreateProgressDialog();
+
+ std::string ffmpegCommand = Video::generateEncodeVideoScript(image1File, image2File, image3File, videoFile, "mpeg4", "");
+
+ std::cout << "FFmpeg process started with arguments: '" << ffmpegCommand << "'." << std::endl;
+
+ state = StateCreating;
+
+ sessionId = FFmpegKit::executeAsync(ffmpegCommand, [this, videoFile, videoWithSubtitlesFile](auto session) {
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(session->getState()) << " and rc " << session->getReturnCode() << "." << session->getFailStackTrace() << std::endl;
+
+ this->hideCreateProgressDialog();
+
+ if (ReturnCode::isSuccess(session->getReturnCode())) {
+ std::cout << "Create completed successfully; burning subtitles." << std::endl;
+
+ std::string burnSubtitlesCommand = "-y -i " + videoFile + " -vf subtitles=" + getSubtitleFile() + ":force_style='FontName=MyFontName' -c:v mpeg4 " + videoWithSubtitlesFile;
+
+ this->showBurnProgressDialog();
+
+ std::cout << "FFmpeg process started with arguments: '" << burnSubtitlesCommand << "'." << std::endl;
+
+ state = StateBurning;
+
+ FFmpegKit::executeAsync(burnSubtitlesCommand, [this](auto secondSession) {
+
+ hideBurnProgressDialog();
+
+ if (ReturnCode::isSuccess(secondSession->getReturnCode())) {
+ std::cout << "Burn subtitles completed successfully." << std::endl;
+ } else if (ReturnCode::isCancel(secondSession->getReturnCode())) {
+ g_idle_add((GSourceFunc)showBurningCancelledPopup, new std::pair(this->parentWindow, "Burn subtitles operation cancelled."));
+ std::cout << "Burn subtitles operation cancelled." << std::endl;
+ } else {
+ g_idle_add((GSourceFunc)showBurningFailedPopup, new std::pair(this->parentWindow, "Burn subtitles failed. Please check logs for the details."));
+ std::cout << "Burn subtitles failed with state " << FFmpegKitConfig::sessionStateToString(secondSession->getState()) << " and rc " << secondSession->getReturnCode() << "." << secondSession->getFailStackTrace() << std::endl;
+ }
+ });
+ }
+ })->getSessionId();
+
+ std::cout << "Async FFmpeg process started with sessionId " << sessionId << "." << std::endl;
+}
+
+void ffmpegkittest::SubtitleTab::cancel() {
+ if (sessionId > -1) {
+ std::cout << "Cancelling FFmpeg execution with sessionId " << sessionId << "." << std::endl;
+ FFmpegKit::cancel(sessionId);
+ }
+}
+
+std::string ffmpegkittest::SubtitleTab::getSubtitleFile() {
+ return Application::getApplicationInstallDirectory() + "/share/subtitles/subtitle.srt";
+}
+
+std::string ffmpegkittest::SubtitleTab::getVideoFile() {
+ return Application::getApplicationCacheDirectory() + "/video.mp4";
+}
+
+std::string ffmpegkittest::SubtitleTab::getVideoWithSubtitlesFile() {
+ return Application::getApplicationCacheDirectory() + "/video-with-subtitles.mp4";
+}
+
+void ffmpegkittest::SubtitleTab::showCreateProgressDialog() {
+ // progressDialog.show(this->get_parent_window());
+}
+
+void ffmpegkittest::SubtitleTab::hideCreateProgressDialog() {
+ // progressDialog.hide();
+}
+
+void ffmpegkittest::SubtitleTab::showBurnProgressDialog() {
+ // progressDialog.show(this->get_parent_window());
+}
+
+void ffmpegkittest::SubtitleTab::hideBurnProgressDialog() {
+ // progressDialog.hide();
+}
diff --git a/linux/test-app-local-dependency/src/SubtitleTab.h b/linux/test-app-local-dependency/src/SubtitleTab.h
new file mode 100644
index 0000000..affc553
--- /dev/null
+++ b/linux/test-app-local-dependency/src/SubtitleTab.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_SUBTITLE_TAB_H
+#define FFMPEG_KIT_TEST_SUBTITLE_TAB_H
+
+#include "ProgressDialog.h"
+#include "Statistics.h"
+#include "Util.h"
+#include
+
+namespace ffmpegkittest {
+
+ class SubtitleTab: public Gtk::VBox {
+ public:
+ SubtitleTab();
+ void setActive();
+ void setParentWindow(Gtk::Window* parentWindow);
+ void appendOutput(const std::string& string);
+ void updateProgressDialog(const std::shared_ptr statistics);
+
+ private:
+ void clearOutput();
+ void burnSubtitles();
+ void cancel();
+ std::string getSubtitleFile();
+ std::string getVideoFile();
+ std::string getVideoWithSubtitlesFile();
+ void showCreateProgressDialog();
+ void hideCreateProgressDialog();
+ void showBurnProgressDialog();
+ void hideBurnProgressDialog();
+
+ Gtk::Button encodeButton;
+ Gtk::Button cancelButton;
+ Gtk::HBox buttonBox;
+ Gtk::TextView outputText;
+ Gtk::ScrolledWindow outputTextWindow;
+ ffmpegkittest::ProgressDialog progressDialog;
+ Gtk::Window* parentWindow;
+ std::shared_ptr statistics;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_SUBTITLE_TAB_H
diff --git a/linux/test-app-local-dependency/src/Util.cpp b/linux/test-app-local-dependency/src/Util.cpp
new file mode 100644
index 0000000..4f05891
--- /dev/null
+++ b/linux/test-app-local-dependency/src/Util.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "Util.h"
+#include
+
+void applyCssData(Gtk::Widget& widget, const std::string& data) {
+ Glib::RefPtr cssProvider = Gtk::CssProvider::create();
+ cssProvider->load_from_data(data);
+ widget.get_style_context()->add_provider(cssProvider, GTK_STYLE_PROVIDER_PRIORITY_USER);
+}
+
+void ffmpegkittest::Util::applyEditTextStyle(Gtk::Entry& entry) {
+ entry.override_background_color(Gdk::RGBA("White"));
+ entry.override_color(Gdk::RGBA("Black"));
+ entry.set_margin_start(20);
+ entry.set_margin_end(20);
+ entry.set_margin_top(20);
+ entry.set_margin_bottom(10);
+ applyCssData(entry, "entry {border: 1px solid rgba(52, 152, 219, 1.0);}\
+ entry {border: 1px solid rgba(52, 152, 219, 1.0);}");
+}
+
+void ffmpegkittest::Util::applyButtonStyle(Gtk::Button& button) {
+ button.override_color(Gdk::RGBA("White"));
+ button.set_margin_top(10);
+ button.set_margin_bottom(10);
+ applyCssData(button, "button {background-image: image(rgba(46, 204, 113, 1.0)); border: 1px solid rgba(39, 174, 96, 1.0);}\
+ button:active {background-image: image(rgba(46, 174, 113, 1.0)); border: 1px solid rgba(39, 174, 96, 1.0);}");
+}
+
+void ffmpegkittest::Util::applyOutputTextStyle(Gtk::TextView& textView) {
+ textView.set_margin_start(20);
+ textView.set_margin_end(20);
+ textView.set_margin_top(10);
+ textView.set_margin_bottom(20);
+ textView.override_color(Gdk::RGBA("White"));
+ applyCssData(textView, "textview text {background-image: image(rgba(241, 196, 15, 1.0)); border-radius: 5px; border: 1px solid rgba(243, 156, 18, 1.0);}");
+}
+
+void ffmpegkittest::Util::applyComboBoxStyle(Gtk::ComboBox& comboBox) {
+ comboBox.set_margin_start(20);
+ comboBox.set_margin_end(20);
+ comboBox.set_margin_top(20);
+ comboBox.set_margin_bottom(10);
+ comboBox.override_color(Gdk::RGBA("White"));
+ applyCssData(comboBox, "combobox {background-image: image(rgba(155, 89, 182, 1.0)); border-radius: 5px; border: 1px solid rgba(142, 68, 173, 1.0);}");
+}
+
+void ffmpegkittest::Util::applyVideoPlayerFrameStyle(Gtk::Button& button) {
+ button.set_margin_top(10);
+ button.set_margin_bottom(10);
+ applyCssData(button, "button {background-image: image(rgba(236, 240, 241, 1.0)); border: 1px solid rgba(185, 195, 199, 1.0);}");
+}
diff --git a/linux/test-app-local-dependency/src/Util.h b/linux/test-app-local-dependency/src/Util.h
new file mode 100644
index 0000000..b295820
--- /dev/null
+++ b/linux/test-app-local-dependency/src/Util.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_UTIL_H
+#define FFMPEG_KIT_TEST_UTIL_H
+
+#include
+#include
+
+namespace ffmpegkittest {
+
+ class Util {
+ public:
+ static void applyEditTextStyle(Gtk::Entry& entry);
+ static void applyButtonStyle(Gtk::Button& button);
+ static void applyComboBoxStyle(Gtk::ComboBox& comboBox);
+ static void applyOutputTextStyle(Gtk::TextView& textView);
+ static void applyVideoPlayerFrameStyle(Gtk::Button& button);
+ };
+
+ class ComboBoxModelColumn : public Gtk::TreeModel::ColumnRecord {
+ public:
+ ComboBoxModelColumn() {
+ add(columnId);
+ add(columnName);
+ }
+
+ Gtk::TreeModelColumn columnId;
+ Gtk::TreeModelColumn columnName;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_UTIL_H
diff --git a/linux/test-app-local-dependency/src/VidStabTab.cpp b/linux/test-app-local-dependency/src/VidStabTab.cpp
new file mode 100644
index 0000000..b4a99de
--- /dev/null
+++ b/linux/test-app-local-dependency/src/VidStabTab.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "VidStabTab.h"
+#include "Application.h"
+#include "Constants.h"
+#include "Log.h"
+#include "Popup.h"
+#include "Video.h"
+#include
+#include
+
+using namespace ffmpegkit;
+
+static gboolean showStabilizeFailedPopup(const std::pair* parameters) {
+ Gtk::Window* window = parameters->first;
+ auto messageDetail = parameters->second;
+ ffmpegkittest::Popup::show(window, Gtk::MESSAGE_ERROR, messageDetail);
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean appendLog(const std::pair>* parameters) {
+ ffmpegkittest::VidStabTab* videoTab = parameters->first;
+ auto log = parameters->second;
+ videoTab->appendOutput(log->getMessage());
+ delete parameters;
+ return FALSE;
+}
+
+ffmpegkittest::VidStabTab::VidStabTab() {
+ stabilizeVideoButton.set_label("STABILIZE VIDEO");
+ stabilizeVideoButton.set_size_request(120, 30);
+ stabilizeVideoButton.set_tooltip_text(Constants::VidStabTestTooltipText);
+ stabilizeVideoButton.signal_clicked().connect(sigc::mem_fun(*this, &VidStabTab::stabilizeVideo));
+ Util::applyButtonStyle(stabilizeVideoButton);
+ stabilizeVideoButtonBox.pack_start(stabilizeVideoButton, Gtk::PACK_EXPAND_PADDING);
+
+ outputText.set_editable(false);
+ Util::applyOutputTextStyle(outputText);
+ outputTextWindow.add(outputText);
+
+ pack_start(stabilizeVideoButtonBox, Gtk::PACK_SHRINK);
+ add(outputTextWindow);
+}
+
+void ffmpegkittest::VidStabTab::setActive() {
+ std::cout << "VidStab Tab Activated" << std::endl;
+ FFmpegKitConfig::enableLogCallback([this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ });
+ FFmpegKitConfig::enableStatisticsCallback(nullptr);
+}
+
+void ffmpegkittest::VidStabTab::setParentWindow(Gtk::Window* parentWindow) {
+ this->parentWindow = parentWindow;
+}
+
+void ffmpegkittest::VidStabTab::appendOutput(const std::string& string) {
+ outputText.get_buffer()->set_text(outputText.get_buffer()->get_text() + string);
+ Glib::RefPtr adj = outputText.get_vadjustment();
+ adj->set_value(adj->get_upper());
+}
+
+void ffmpegkittest::VidStabTab::clearOutput() {
+ outputText.get_buffer()->set_text("");
+}
+
+void ffmpegkittest::VidStabTab::stabilizeVideo() {
+ clearOutput();
+
+ std::string image1File = Application::getApplicationInstallDirectory() + "/share/images/machupicchu.jpg";
+ std::string image2File = Application::getApplicationInstallDirectory() + "/share/images/pyramid.jpg";
+ std::string image3File = Application::getApplicationInstallDirectory() + "/share/images/stonehenge.jpg";
+ std::string shakeResultsFile = getShakeResultsFile();
+ std::string videoFile = getVideoFile();
+ std::string stabilizedVideoFile = getStabilizedVideoFile();
+
+ std::remove(shakeResultsFile.c_str());
+ std::remove(videoFile.c_str());
+ std::remove(stabilizedVideoFile.c_str());
+
+ std::cout << "Testing VID.STAB." << std::endl;
+
+ showCreateProgressDialog();
+
+ std::string ffmpegCommand = Video::generateShakingVideoScript(image1File, image2File, image3File, videoFile);
+
+ std::cout << "FFmpeg process started with arguments: '" << ffmpegCommand << "'." << std::endl;
+
+ FFmpegKit::executeAsync(ffmpegCommand, [this, videoFile, shakeResultsFile, stabilizedVideoFile](auto session) {
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(session->getState()) << " and rc " << session->getReturnCode() << "." << session->getFailStackTrace() << std::endl;
+
+ this->hideCreateProgressDialog();
+
+ if (ReturnCode::isSuccess(session->getReturnCode())) {
+ std::cout << "Create completed successfully; stabilizing video." << std::endl;
+
+ std::string analyzeVideoCommand = "-y -i " + videoFile + " -vf vidstabdetect=shakiness=10:accuracy=15:result=" + shakeResultsFile + " -f null -";
+
+ this->showStabilizeProgressDialog();
+
+ std::cout << "FFmpeg process started with arguments: '" << analyzeVideoCommand << "'." << std::endl;
+
+ FFmpegKit::executeAsync(analyzeVideoCommand, [this, videoFile, shakeResultsFile, stabilizedVideoFile](auto secondSession) {
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(secondSession->getState()) << " and rc " << secondSession->getReturnCode() << "." << secondSession->getFailStackTrace() << std::endl;
+
+ if (ReturnCode::isSuccess(secondSession->getReturnCode())) {
+ std::string stabilizeVideoCommand = "-y -i " + videoFile + " -vf vidstabtransform=smoothing=30:input=" + shakeResultsFile + " -c:v mpeg4 " + stabilizedVideoFile;
+
+ std::cout << "FFmpeg process started with arguments: '" << stabilizeVideoCommand << "'." << std::endl;
+
+ FFmpegKit::executeAsync(stabilizeVideoCommand, [this](auto thirdSession) {
+
+ std::cout << "FFmpeg process exited with state " << FFmpegKitConfig::sessionStateToString(thirdSession->getState()) << " and rc " << thirdSession->getReturnCode() << "." << thirdSession->getFailStackTrace() << std::endl;
+
+ this->hideStabilizeProgressDialog();
+
+ if (ReturnCode::isSuccess(thirdSession->getReturnCode())) {
+ std::cout << "Stabilize video completed successfully." << std::endl;
+ } else {
+ g_idle_add((GSourceFunc)showStabilizeFailedPopup, new std::pair(this->parentWindow, "Stabilize video failed. Please check logs for the details."));
+ }
+ });
+
+ } else {
+ this->hideCreateProgressDialog();
+ g_idle_add((GSourceFunc)showStabilizeFailedPopup, new std::pair(this->parentWindow, "Stabilize video failed. Please check logs for the details."));
+ }
+ });
+ } else {
+ g_idle_add((GSourceFunc)showStabilizeFailedPopup, new std::pair(this->parentWindow, "Create video failed. Please check logs for the details."));
+ }
+ });
+}
+
+std::string ffmpegkittest::VidStabTab::getShakeResultsFile() {
+ return Application::getApplicationCacheDirectory() + "/transforms.trf";
+}
+
+std::string ffmpegkittest::VidStabTab::getVideoFile() {
+ return Application::getApplicationCacheDirectory() + "/video.mp4";
+}
+
+std::string ffmpegkittest::VidStabTab::getStabilizedVideoFile() {
+ return Application::getApplicationCacheDirectory() + "/video-stabilized.mp4";
+}
+
+void ffmpegkittest::VidStabTab::showCreateProgressDialog() {
+ // progressDialog.show(this->get_parent_window());
+}
+
+void ffmpegkittest::VidStabTab::hideCreateProgressDialog() {
+ // progressDialog.hide();
+}
+
+void ffmpegkittest::VidStabTab::showStabilizeProgressDialog() {
+ // progressDialog.show(this->get_parent_window());
+}
+
+void ffmpegkittest::VidStabTab::hideStabilizeProgressDialog() {
+ // progressDialog.hide();
+}
diff --git a/linux/test-app-local-dependency/src/VidStabTab.h b/linux/test-app-local-dependency/src/VidStabTab.h
new file mode 100644
index 0000000..2ff5dd1
--- /dev/null
+++ b/linux/test-app-local-dependency/src/VidStabTab.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_VIDSTAB_TAB_H
+#define FFMPEG_KIT_TEST_VIDSTAB_TAB_H
+
+#include "ProgressDialog.h"
+#include "Statistics.h"
+#include "Util.h"
+#include
+
+namespace ffmpegkittest {
+
+ class VidStabTab: public Gtk::VBox {
+ public:
+ VidStabTab();
+ void setActive();
+ void setParentWindow(Gtk::Window* parentWindow);
+ void appendOutput(const std::string& string);
+
+ private:
+ void clearOutput();
+ void stabilizeVideo();
+ std::string getShakeResultsFile();
+ std::string getVideoFile();
+ std::string getStabilizedVideoFile();
+ void showCreateProgressDialog();
+ void hideCreateProgressDialog();
+ void showStabilizeProgressDialog();
+ void hideStabilizeProgressDialog();
+
+ Gtk::Button stabilizeVideoButton;
+ Gtk::HBox stabilizeVideoButtonBox;
+ Gtk::TextView outputText;
+ Gtk::ScrolledWindow outputTextWindow;
+ ffmpegkittest::ProgressDialog progressDialog;
+ Gtk::Window* parentWindow;
+ std::shared_ptr statistics;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_VIDSTAB_TAB_H
diff --git a/linux/test-app-local-dependency/src/Video.cpp b/linux/test-app-local-dependency/src/Video.cpp
new file mode 100644
index 0000000..efb2a34
--- /dev/null
+++ b/linux/test-app-local-dependency/src/Video.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "Video.h"
+
+std::string ffmpegkittest::Video::generateCreateVideoWithPipesScript(std::string image1Pipe, std::string image2Pipe, std::string image3Pipe, std::string videoFilePath) {
+ return
+ "-hide_banner -y -i \"" + image1Pipe + "\" " +
+ "-i '" + image2Pipe + "' " +
+ "-i " + image3Pipe + " " +
+ "-filter_complex \"" +
+ "[0:v]loop=loop=-1:size=1:start=0,setpts=PTS-STARTPTS,scale=w=\'if(gte(iw/ih,640/427),min(iw,640),-1)\':h=\'if(gte(iw/ih,640/427),-1,min(ih,427))\',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1,split=2[stream1out1][stream1out2];" +
+ "[1:v]loop=loop=-1:size=1:start=0,setpts=PTS-STARTPTS,scale=w=\'if(gte(iw/ih,640/427),min(iw,640),-1)\':h=\'if(gte(iw/ih,640/427),-1,min(ih,427))\',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1,split=2[stream2out1][stream2out2];" +
+ "[2:v]loop=loop=-1:size=1:start=0,setpts=PTS-STARTPTS,scale=w=\'if(gte(iw/ih,640/427),min(iw,640),-1)\':h=\'if(gte(iw/ih,640/427),-1,min(ih,427))\',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1,split=2[stream3out1][stream3out2];" +
+ "[stream1out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=3,select=lte(n\\,90)[stream1overlaid];" +
+ "[stream1out2]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=1,select=lte(n\\,30)[stream1ending];" +
+ "[stream2out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=2,select=lte(n\\,60)[stream2overlaid];" +
+ "[stream2out2]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=1,select=lte(n\\,30),split=2[stream2starting][stream2ending];" +
+ "[stream3out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=2,select=lte(n\\,60)[stream3overlaid];" +
+ "[stream3out2]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=1,select=lte(n\\,30)[stream3starting];" +
+ "[stream2starting][stream1ending]blend=all_expr=\'if(gte(X,(W/2)*T/1)*lte(X,W-(W/2)*T/1),B,A)\':shortest=1[stream2blended];" +
+ "[stream3starting][stream2ending]blend=all_expr=\'if(gte(X,(W/2)*T/1)*lte(X,W-(W/2)*T/1),B,A)\':shortest=1[stream3blended];" +
+ "[stream1overlaid][stream2blended][stream2overlaid][stream3blended][stream3overlaid]concat=n=5:v=1:a=0,scale=w=640:h=424,format=yuv420p[video]\"" +
+ " -map [video] -vsync 2 -async 1 -c:v mpeg4 -r 30 " + videoFilePath;
+}
+
+std::string ffmpegkittest::Video::generateEncodeVideoScript(std::string image1Path, std::string image2Path, std::string image3Path, std::string videoFilePath, std::string videoCodec, std::string customOptions) {
+ return ffmpegkittest::Video::generateEncodeVideoScript(image1Path, image2Path, image3Path, videoFilePath, videoCodec, "yuv420p", customOptions);
+}
+
+std::string ffmpegkittest::Video::generateEncodeVideoScript(std::string image1Path, std::string image2Path, std::string image3Path, std::string videoFilePath, std::string videoCodec, std::string pixelFormat, std::string customOptions) {
+ return
+ "-hide_banner -y -loop 1 -i \"" + image1Path + "\" " +
+ "-loop 1 -i '" + image2Path + "' " +
+ "-loop 1 -i \"" + image3Path + "\" " +
+ "-filter_complex \"" +
+ "[0:v]setpts=PTS-STARTPTS,scale=w=\'if(gte(iw/ih,640/427),min(iw,640),-1)\':h=\'if(gte(iw/ih,640/427),-1,min(ih,427))\',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1,split=2[stream1out1][stream1out2];" +
+ "[1:v]setpts=PTS-STARTPTS,scale=w=\'if(gte(iw/ih,640/427),min(iw,640),-1)\':h=\'if(gte(iw/ih,640/427),-1,min(ih,427))\',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1,split=2[stream2out1][stream2out2];" +
+ "[2:v]setpts=PTS-STARTPTS,scale=w=\'if(gte(iw/ih,640/427),min(iw,640),-1)\':h=\'if(gte(iw/ih,640/427),-1,min(ih,427))\',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1,split=2[stream3out1][stream3out2];" +
+ "[stream1out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=3,select=lte(n\\,90)[stream1overlaid];" +
+ "[stream1out2]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=1,select=lte(n\\,30)[stream1ending];" +
+ "[stream2out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=2,select=lte(n\\,60)[stream2overlaid];" +
+ "[stream2out2]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=1,select=lte(n\\,30),split=2[stream2starting][stream2ending];" +
+ "[stream3out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=2,select=lte(n\\,60)[stream3overlaid];" +
+ "[stream3out2]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=1,select=lte(n\\,30)[stream3starting];" +
+ "[stream2starting][stream1ending]blend=all_expr=\'if(gte(X,(W/2)*T/1)*lte(X,W-(W/2)*T/1),B,A)\':shortest=1[stream2blended];" +
+ "[stream3starting][stream2ending]blend=all_expr=\'if(gte(X,(W/2)*T/1)*lte(X,W-(W/2)*T/1),B,A)\':shortest=1[stream3blended];" +
+ "[stream1overlaid][stream2blended][stream2overlaid][stream3blended][stream3overlaid]concat=n=5:v=1:a=0,scale=w=640:h=424,format=" + pixelFormat + "[video]\"" +
+ " -map [video] -vsync 2 -async 1 " + customOptions + "-c:v " + videoCodec + " -r 30 " + videoFilePath;
+}
+
+std::string ffmpegkittest::Video::generateShakingVideoScript(std::string image1Path, std::string image2Path, std::string image3Path, std::string videoFilePath) {
+ return
+ "-hide_banner -y -loop 1 -i \"" + image1Path + "\" " +
+ "-loop 1 -i '" + image2Path + "' " +
+ "-loop 1 -i " + image3Path + " " +
+ "-f lavfi -i color=black:s=640x427 " +
+ "-filter_complex \"" +
+ "[0:v]setpts=PTS-STARTPTS,scale=w=\'if(gte(iw/ih,640/427),min(iw,640),-1)\':h=\'if(gte(iw/ih,640/427),-1,min(ih,427))\',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1[stream1out];" +
+ "[1:v]setpts=PTS-STARTPTS,scale=w=\'if(gte(iw/ih,640/427),min(iw,640),-1)\':h=\'if(gte(iw/ih,640/427),-1,min(ih,427))\',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1[stream2out];" +
+ "[2:v]setpts=PTS-STARTPTS,scale=w=\'if(gte(iw/ih,640/427),min(iw,640),-1)\':h=\'if(gte(iw/ih,640/427),-1,min(ih,427))\',scale=trunc(iw/2)*2:trunc(ih/2)*2,setsar=sar=1/1[stream3out];" +
+ "[stream1out]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=3[stream1overlaid];" +
+ "[stream2out]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=3[stream2overlaid];" +
+ "[stream3out]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=3[stream3overlaid];" +
+ "[3:v][stream1overlaid]overlay=x=\'2*mod(n,4)\':y=\'2*mod(n,2)\',trim=duration=3[stream1shaking];" +
+ "[3:v][stream2overlaid]overlay=x=\'2*mod(n,4)\':y=\'2*mod(n,2)\',trim=duration=3[stream2shaking];" +
+ "[3:v][stream3overlaid]overlay=x=\'2*mod(n,4)\':y=\'2*mod(n,2)\',trim=duration=3[stream3shaking];" +
+ "[stream1shaking][stream2shaking][stream3shaking]concat=n=3:v=1:a=0,scale=w=640:h=424,format=yuv420p[video]\"" +
+ " -map [video] -vsync 2 -async 1 -c:v mpeg4 -r 30 " + videoFilePath;
+}
+
+std::string ffmpegkittest::Video::generateZscaleVideoScript(std::string inputVideoFilePath, std::string outputVideoFilePath) {
+ return "-y -i " +
+ inputVideoFilePath +
+ " -vf zscale=tin=smpte2084:min=bt2020nc:pin=bt2020:rin=tv:t=smpte2084:m=bt2020nc:p=bt2020:r=tv,zscale=t=linear,tonemap=tonemap=clip,zscale=t=bt709,format=yuv420p " +
+ outputVideoFilePath;
+}
diff --git a/linux/test-app-local-dependency/src/Video.h b/linux/test-app-local-dependency/src/Video.h
new file mode 100644
index 0000000..4caf8c0
--- /dev/null
+++ b/linux/test-app-local-dependency/src/Video.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_VIDEO_H
+#define FFMPEG_KIT_TEST_VIDEO_H
+
+#include
+
+namespace ffmpegkittest {
+
+ class Video {
+ public:
+ static std::string generateCreateVideoWithPipesScript(std::string image1Pipe, std::string image2Pipe, std::string image3Pipe, std::string videoFilePath);
+ static std::string generateEncodeVideoScript(std::string image1Path, std::string image2Path, std::string image3Path, std::string videoFilePath, std::string videoCodec, std::string customOptions);
+ static std::string generateEncodeVideoScript(std::string image1Path, std::string image2Path, std::string image3Path, std::string videoFilePath, std::string videoCodec, std::string pixelFormat, std::string customOptions);
+ static std::string generateShakingVideoScript(std::string image1Path, std::string image2Path, std::string image3Path, std::string videoFilePath);
+ static std::string generateZscaleVideoScript(std::string inputVideoFilePath, std::string outputVideoFilePath);
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_VIDEO_H
diff --git a/linux/test-app-local-dependency/src/VideoTab.cpp b/linux/test-app-local-dependency/src/VideoTab.cpp
new file mode 100644
index 0000000..bc238ea
--- /dev/null
+++ b/linux/test-app-local-dependency/src/VideoTab.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "VideoTab.h"
+#include "Application.h"
+#include "Constants.h"
+#include "Log.h"
+#include "Popup.h"
+#include "Statistics.h"
+#include "Video.h"
+#include
+#include
+#include
+
+using namespace ffmpegkit;
+
+static gboolean showEncodeFailedPopup(Gtk::Window* window) {
+ ffmpegkittest::Popup::show(window, Gtk::MESSAGE_ERROR, "Encode failed. Please check logs for the details.");
+ return FALSE;
+}
+
+static gboolean saveStatistics(const std::pair>* parameters) {
+ ffmpegkittest::VideoTab* videoTab = parameters->first;
+ auto statistics = parameters->second;
+ videoTab->updateProgressDialog(statistics);
+ delete parameters;
+ return FALSE;
+}
+
+static gboolean appendLog(const std::pair>* parameters) {
+ ffmpegkittest::VideoTab* videoTab = parameters->first;
+ auto log = parameters->second;
+ videoTab->appendOutput(log->getMessage());
+ delete parameters;
+ return FALSE;
+}
+
+ffmpegkittest::VideoTab::VideoTab() : selectedCodec(-1), statistics(nullptr) {
+ videoCodecModel = Gtk::ListStore::create(videoCodecModelColumn);
+ videoCodec.set_model(videoCodecModel);
+ videoCodec.set_size_request(240, 30);
+ videoCodec.signal_changed().connect(sigc::mem_fun(*this, &VideoTab::onVideoCodecChanged));
+ Util::applyComboBoxStyle(videoCodec);
+
+ initVideoCodecData();
+
+ encodeButton.set_label("ENCODE");
+ encodeButton.set_size_request(120, 30);
+ encodeButton.set_tooltip_text(Constants::VideoTestTooltipText);
+ encodeButton.signal_clicked().connect(sigc::mem_fun(*this, &VideoTab::encodeVideo));
+ Util::applyButtonStyle(encodeButton);
+ encodeButtonBox.pack_start(encodeButton, Gtk::PACK_EXPAND_PADDING);
+
+ outputText.set_editable(false);
+ Util::applyOutputTextStyle(outputText);
+ outputTextWindow.add(outputText);
+
+ pack_start(videoCodecBox, Gtk::PACK_SHRINK);
+ pack_start(encodeButtonBox, Gtk::PACK_SHRINK);
+ add(outputTextWindow);
+}
+
+void ffmpegkittest::VideoTab::setActive() {
+ std::cout << "Video Tab Activated" << std::endl;
+ FFmpegKitConfig::enableLogCallback(nullptr);
+ FFmpegKitConfig::enableStatisticsCallback(nullptr);
+}
+
+void ffmpegkittest::VideoTab::setParentWindow(Gtk::Window* parentWindow) {
+ this->parentWindow = parentWindow;
+}
+
+void ffmpegkittest::VideoTab::appendOutput(const std::string& string) {
+ outputText.get_buffer()->set_text(outputText.get_buffer()->get_text() + string);
+ Glib::RefPtr adj = outputText.get_vadjustment();
+ adj->set_value(adj->get_upper());
+}
+
+void ffmpegkittest::VideoTab::updateProgressDialog(const std::shared_ptr statistics) {
+ if (statistics == nullptr || statistics->getTime() < 0) {
+ return;
+ }
+
+ this->statistics = statistics;
+ int timeInMilliseconds = this->statistics->getTime();
+ int totalVideoDuration = 9000;
+ double completePercentage = timeInMilliseconds*100/totalVideoDuration;
+ // progressDialog.update(completePercentage);
+ std::cout << "Encoding completed " << completePercentage << "%" << std::endl;
+}
+
+void ffmpegkittest::VideoTab::clearOutput() {
+ outputText.get_buffer()->set_text("");
+}
+
+void ffmpegkittest::VideoTab::initVideoCodecData() {
+ auto row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "1";
+ row[videoCodecModelColumn.columnName] = "mpeg4";
+
+ row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "2";
+ row[videoCodecModelColumn.columnName] = "x264";
+
+ row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "3";
+ row[videoCodecModelColumn.columnName] = "openh264";
+
+ row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "4";
+ row[videoCodecModelColumn.columnName] = "x265";
+
+ row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "5";
+ row[videoCodecModelColumn.columnName] = "xvid";
+
+ row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "6";
+ row[videoCodecModelColumn.columnName] = "vp8";
+
+ row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "7";
+ row[videoCodecModelColumn.columnName] = "vp9";
+
+ row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "8";
+ row[videoCodecModelColumn.columnName] = "aom";
+
+ row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "9";
+ row[videoCodecModelColumn.columnName] = "kvazaar";
+
+ row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "10";
+ row[videoCodecModelColumn.columnName] = "theora";
+
+ row = *(videoCodecModel->append());
+ row[videoCodecModelColumn.columnId] = "11";
+ row[videoCodecModelColumn.columnName] = "hap";
+
+ videoCodec.pack_start(videoCodecModelColumn.columnName);
+ videoCodec.set_entry_text_column(videoCodecModelColumn.columnId);
+ videoCodec.set_active(0);
+
+ videoCodecBox.pack_start(videoCodec, Gtk::PACK_EXPAND_PADDING);
+}
+
+void ffmpegkittest::VideoTab::onVideoCodecChanged() {
+ int rowNumber = videoCodec.get_active_row_number();
+ if (rowNumber != -1) {
+ selectedCodec = rowNumber;
+ }
+}
+
+std::string ffmpegkittest::VideoTab::getSelectedVideoCodec() {
+ switch(selectedCodec) {
+ case 0: return "mpeg4";
+ case 1: return "libx264";
+ case 2: return "libopenh264";
+ case 3: return "libx265";
+ case 4: return "libxvid";
+ case 5: return "vp8";
+ case 6: return "vp9";
+ case 7: return "libaom-av1";
+ case 8: return "libkvazaar";
+ case 9: return "theora";
+ case 10: return "hap";
+ default: return "";
+ }
+}
+
+void ffmpegkittest::VideoTab::encodeVideo() {
+ clearOutput();
+
+ std::string image1File = Application::getApplicationInstallDirectory() + "/share/images/machupicchu.jpg";
+ std::string image2File = Application::getApplicationInstallDirectory() + "/share/images/pyramid.jpg";
+ std::string image3File = Application::getApplicationInstallDirectory() + "/share/images/stonehenge.jpg";
+ std::string videoFile = getVideoFile();
+
+ std::remove(videoFile.c_str());
+
+ std::string videoCodec = this->getSelectedVideoCodec();
+
+ std::cout << "Testing VIDEO encoding with '" << videoCodec << "' codec" << std::endl;
+
+ showProgressDialog();
+
+ std::string ffmpegCommand = Video::generateEncodeVideoScript(image1File, image2File, image3File, videoFile, getSelectedVideoCodec(), getPixelFormat(), getCustomOptions());
+
+ std::cout << "FFmpeg process started with arguments: '" << ffmpegCommand << "'." << std::endl;
+
+ auto session = FFmpegKit::executeAsync(ffmpegCommand, [this](auto session) {
+ const auto state = session->getState();
+ auto returnCode = session->getReturnCode();
+
+ this->hideProgressDialog();
+
+ if (ReturnCode::isSuccess(returnCode)) {
+ std::cout << "Encode completed successfully in " << session->getDuration() << " milliseconds." << std::endl;
+ } else {
+ g_idle_add((GSourceFunc)showEncodeFailedPopup, this->parentWindow);
+ std::cout << "Encode failed with state " << FFmpegKitConfig::sessionStateToString(state) << " and rc " << returnCode << "." << session->getFailStackTrace() << std::endl;
+ }
+ }, [this](auto log) {
+ g_idle_add((GSourceFunc)appendLog, new std::pair>(this, log));
+ }, [this](auto statistics) {
+ g_idle_add((GSourceFunc)saveStatistics, new std::pair>(this, statistics));
+ });
+
+ std::cout << "Async FFmpeg process started with sessionId " << session->getSessionId() << "." << std::endl;
+}
+
+std::string ffmpegkittest::VideoTab::getPixelFormat() {
+ std::string videoCodec = this->getSelectedVideoCodec();
+
+ std::string pixelFormat;
+ if (videoCodec.compare("libx265") == 0) {
+ pixelFormat = "yuv420p10le";
+ } else {
+ pixelFormat = "yuv420p";
+ }
+
+ return pixelFormat;
+}
+
+std::string ffmpegkittest::VideoTab::getVideoFile() {
+ std::string videoCodec = this->getSelectedVideoCodec();
+
+ std::string extension;
+ if (videoCodec.compare("vp8") == 0 || videoCodec.compare("vp9") == 0) {
+ extension = "webm";
+ } else if (videoCodec.compare("libaom-av1") == 0) {
+ extension = "mkv";
+ } else if (videoCodec.compare("theora") == 0) {
+ extension = "ogv";
+ } else if (videoCodec.compare("hap") == 0) {
+ extension = "mov";
+ } else {
+
+ // mpeg4, libx264, libx265, libxvid, kvazaar, libopenh264
+ extension = "mp4";
+ }
+
+ return Application::getApplicationCacheDirectory() + "/video." + extension;
+}
+
+std::string ffmpegkittest::VideoTab::getCustomOptions() {
+ std::string videoCodec = this->getSelectedVideoCodec();
+
+ if (videoCodec.compare("libx265") == 0) {
+ return "-crf 28 -preset fast ";
+ } else if (videoCodec.compare("vp8") == 0) {
+ return "-b:v 1M -crf 10 ";
+ } else if (videoCodec.compare("vp9") == 0) {
+ return "-b:v 2M ";
+ } else if (videoCodec.compare("libaom-av1") == 0) {
+ return "-crf 30 -strict experimental ";
+ } else if (videoCodec.compare("theora") == 0) {
+ return "-qscale:v 7 ";
+ } else if (videoCodec.compare("hap") == 0) {
+ return "-format hap_q ";
+ } else {
+
+ // kvazaar, mpeg4, libx264, libxvid, libopenh264
+ return "";
+ }
+}
+
+void ffmpegkittest::VideoTab::showProgressDialog() {
+ // progressDialog.show(this->get_parent_window());
+}
+
+void ffmpegkittest::VideoTab::hideProgressDialog() {
+ // progressDialog.hide();
+}
diff --git a/linux/test-app-local-dependency/src/VideoTab.h b/linux/test-app-local-dependency/src/VideoTab.h
new file mode 100644
index 0000000..4a21c68
--- /dev/null
+++ b/linux/test-app-local-dependency/src/VideoTab.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef FFMPEG_KIT_TEST_VIDEO_TAB_H
+#define FFMPEG_KIT_TEST_VIDEO_TAB_H
+
+#include "ProgressDialog.h"
+#include "Statistics.h"
+#include "Util.h"
+#include
+
+namespace ffmpegkittest {
+
+ class VideoTab: public Gtk::VBox {
+ public:
+ VideoTab();
+ void setActive();
+ void setParentWindow(Gtk::Window* parentWindow);
+ void appendOutput(const std::string& string);
+ void updateProgressDialog(const std::shared_ptr statistics);
+
+ private:
+ void clearOutput();
+ void initVideoCodecData();
+ void onVideoCodecChanged();
+ std::string getSelectedVideoCodec();
+ void encodeVideo();
+ std::string getPixelFormat();
+ std::string getVideoFile();
+ std::string getCustomOptions();
+ void showProgressDialog();
+ void hideProgressDialog();
+
+ Glib::RefPtr videoCodecModel;
+ ComboBoxModelColumn videoCodecModelColumn;
+ Gtk::ComboBox videoCodec;
+ Gtk::HBox videoCodecBox;
+ int selectedCodec;
+ Gtk::Button encodeButton;
+ Gtk::HBox encodeButtonBox;
+ Gtk::TextView outputText;
+ Gtk::ScrolledWindow outputTextWindow;
+ ffmpegkittest::ProgressDialog progressDialog;
+ Gtk::Window* parentWindow;
+ std::shared_ptr statistics;
+ };
+
+}
+
+#endif // FFMPEG_KIT_TEST_VIDEO_TAB_H
diff --git a/linux/test-app-local-dependency/src/main.cpp b/linux/test-app-local-dependency/src/main.cpp
new file mode 100644
index 0000000..a02f5c7
--- /dev/null
+++ b/linux/test-app-local-dependency/src/main.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2022 Taner Sener
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "Application.h"
+#include "MediaInformationParserTest.h"
+#include "FFmpegKitTest.h"
+#include
+
+int main(int argc, char** argv) {
+ auto app = Gtk::Application::create(argc, argv, "com.arthenica.ffmpegkit");
+ ffmpegkittest::Application application;
+ application.set_default_icon_name("ffmpeg-kit-linux-test");
+ application.set_icon_name("ffmpeg-kit-linux-test");
+
+ // RUN UNIT TESTS BEFORE STARTING THE APPLICATION
+ testMediaInformationJsonParser();
+ testFFmpegKit();
+
+ app->run(application);
+ ffmpegkit::FFmpegKitConfig::disableRedirection();
+ app->quit();
+ return 0;
+}
diff --git a/macos/README.md b/macos/README.md
index 172845a..63080fb 100644
--- a/macos/README.md
+++ b/macos/README.md
@@ -1,3 +1,3 @@
# FFmpegKit macOS
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate b/macos/test-app-cocoapods/FFmpegKitMACOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate
index 2ca87f4..fd6397f 100644
Binary files a/macos/test-app-cocoapods/FFmpegKitMACOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate and b/macos/test-app-cocoapods/FFmpegKitMACOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS/AudioViewController.m b/macos/test-app-cocoapods/FFmpegKitMACOS/AudioViewController.m
index 0887342..f477a46 100644
--- a/macos/test-app-cocoapods/FFmpegKitMACOS/AudioViewController.m
+++ b/macos/test-app-cocoapods/FFmpegKitMACOS/AudioViewController.m
@@ -126,7 +126,7 @@ - (IBAction)encodeAudio:(id)sender {
[self clearOutput];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS/CommandViewController.m b/macos/test-app-cocoapods/FFmpegKitMACOS/CommandViewController.m
index 6de1ae6..a83ba76 100644
--- a/macos/test-app-cocoapods/FFmpegKitMACOS/CommandViewController.m
+++ b/macos/test-app-cocoapods/FFmpegKitMACOS/CommandViewController.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2021 Taner Sener
+ * Copyright (c) 2018-2022 Taner Sener
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -64,7 +64,7 @@ - (IBAction)runFFmpeg:(id)sender {
NSLog(@"Testing FFmpeg COMMAND asynchronously.\n");
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -91,9 +91,9 @@ - (IBAction)runFFprobe:(id)sender {
NSLog(@"Testing FFprobe COMMAND asynchronously.\n");
- NSLog(@"FFprobe process started with arguments\n'%@'.\n", ffprobeCommand);
+ NSLog(@"FFprobe process started with arguments '%@'.\n", ffprobeCommand);
- FFprobeSession *session = [[FFprobeSession alloc] init:[FFmpegKitConfig parseArguments:ffprobeCommand] withCompleteCallback:^(FFprobeSession* session) {
+ FFprobeSession *session = [FFprobeSession create:[FFmpegKitConfig parseArguments:ffprobeCommand] withCompleteCallback:^(FFprobeSession* session) {
SessionState state = [session getState];
ReturnCode* returnCode = [session getReturnCode];
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS/ConcurrentExecutionViewController.m b/macos/test-app-cocoapods/FFmpegKitMACOS/ConcurrentExecutionViewController.m
index e758a0c..8a345f6 100644
--- a/macos/test-app-cocoapods/FFmpegKitMACOS/ConcurrentExecutionViewController.m
+++ b/macos/test-app-cocoapods/FFmpegKitMACOS/ConcurrentExecutionViewController.m
@@ -113,7 +113,7 @@ - (void)encodeVideo:(int)buttonNumber {
NSString* ffmpegCommand = [Video generateVideoEncodeScript:image1:image2:image3:videoFile:@"mpeg4":@""];
- NSLog(@"FFmpeg process starting for button %d with arguments\n'%@'.\n", buttonNumber, ffmpegCommand);
+ NSLog(@"FFmpeg process starting for button %d with arguments '%@'.\n", buttonNumber, ffmpegCommand);
FFmpegSession* session = [FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -141,6 +141,7 @@ - (void)encodeVideo:(int)buttonNumber {
break;
default: {
sessionId3 = sessionId;
+ [FFmpegKitConfig setSessionHistorySize:3];
}
}
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS/Constants.m b/macos/test-app-cocoapods/FFmpegKitMACOS/Constants.m
index 97da478..e05f4b9 100644
--- a/macos/test-app-cocoapods/FFmpegKitMACOS/Constants.m
+++ b/macos/test-app-cocoapods/FFmpegKitMACOS/Constants.m
@@ -23,24 +23,24 @@
#include "Constants.h"
// COMMAND TEST
-NSString *const COMMAND_TEST_TOOLTIP_TEXT = @"Enter an FFmpeg command without 'ffmpeg' at the beginning and click one of the RUN buttons";
+NSString *const COMMAND_TEST_TOOLTIP_TEXT = @"Enter a command without ffmpeg/ffprobe at the beginning and click one of the RUN buttons";
NSTimeInterval const COMMAND_TEST_TOOLTIP_DURATION = 4.0;
// VIDEO TEST
-NSString *const VIDEO_TEST_TOOLTIP_TEXT = @"Select a video codec and press ENCODE button";
+NSString *const VIDEO_TEST_TOOLTIP_TEXT = @"Select a video codec and press the ENCODE button";
NSTimeInterval const VIDEO_TEST_TOOLTIP_DURATION = 4.0;
// HTTPS TEST
NSString *const HTTPS_TEST_DEFAULT_URL = @"https://download.blender.org/peach/trailer/trailer_1080p.ogg";
NSString *const HTTPS_TEST_FAIL_URL = @"https://download2.blender.org/peach/trailer/trailer_1080p.ogg";
-NSString *const HTTPS_TEST_RANDOM_URL_1 = @"https://file-examples-com.github.io/uploads/2018/04/file_example_MOV_640_800kB.mov";
-NSString *const HTTPS_TEST_RANDOM_URL_2 = @"https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3";
-NSString *const HTTPS_TEST_RANDOM_URL_3 = @"https://file-examples-com.github.io/uploads/2020/03/file_example_WEBP_50kB.webp";
+NSString *const HTTPS_TEST_RANDOM_URL_1 = @"https://filesamples.com/samples/video/mov/sample_640x360.mov";
+NSString *const HTTPS_TEST_RANDOM_URL_2 = @"https://filesamples.com/samples/audio/mp3/sample3.mp3";
+NSString *const HTTPS_TEST_RANDOM_URL_3 = @"https://filesamples.com/samples/image/webp/sample1.webp";
NSString *const HTTPS_TEST_TOOLTIP_TEXT = @"Enter the https url of a media file and click the button";
NSTimeInterval const HTTPS_TEST_TOOLTIP_DURATION = 4.0;
// AUDIO TEST
-NSString *const AUDIO_TEST_TOOLTIP_TEXT = @"Select an audio codec and press ENCODE button";
+NSString *const AUDIO_TEST_TOOLTIP_TEXT = @"Select an audio codec and press the ENCODE button";
NSTimeInterval const AUDIO_TEST_TOOLTIP_DURATION = 4.0;
// SUBTITLE TEST
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS/FFmpegKitTest.m b/macos/test-app-cocoapods/FFmpegKitMACOS/FFmpegKitTest.m
index 63e61d0..db15dd8 100644
--- a/macos/test-app-cocoapods/FFmpegKitMACOS/FFmpegKitTest.m
+++ b/macos/test-app-cocoapods/FFmpegKitMACOS/FFmpegKitTest.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019-2021 Taner Sener
+ * Copyright (c) 2019-2022 Taner Sener
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -193,9 +193,9 @@ void testParseDoubleQuotesAndEscapesInCommand() {
void getSessionIdTest() {
NSArray *TEST_ARGUMENTS = [[NSArray alloc] initWithObjects:@"argument1", @"argument2", nil];
- FFmpegSession *sessions1 = [[FFmpegSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
- FFprobeSession *sessions2 = [[FFprobeSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
- MediaInformationSession *sessions3 = [[MediaInformationSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
+ FFmpegSession *sessions1 = [FFmpegSession create:TEST_ARGUMENTS];
+ FFprobeSession *sessions2 = [FFprobeSession create:TEST_ARGUMENTS];
+ MediaInformationSession *sessions3 = [MediaInformationSession create:TEST_ARGUMENTS];
assert([sessions3 getSessionId] > [sessions2 getSessionId]);
assert([sessions3 getSessionId] > [sessions1 getSessionId]);
@@ -206,12 +206,31 @@ void getSessionIdTest() {
assert([sessions3 getSessionId] > 0);
}
+void setSessionHistorySizeTest() {
+ NSArray *TEST_ARGUMENTS = [[NSArray alloc] initWithObjects:@"argument1", @"argument2", nil];
+ int newSize = 15;
+ [FFmpegKitConfig setSessionHistorySize:newSize];
+
+ for (int i = 1; i <= (newSize + 5); i++) {
+ [FFmpegSession create:TEST_ARGUMENTS];
+ assert([[FFmpegKitConfig getSessions] count] <= newSize);
+ }
+
+ newSize = 3;
+ [FFmpegKitConfig setSessionHistorySize:newSize];
+ for (int i = 1; i <= (newSize + 5); i++) {
+ [FFmpegSession create:TEST_ARGUMENTS];
+ assert([[FFmpegKitConfig getSessions] count] <= newSize);
+ }
+}
+
void testFFmpegKit(void) {
testParseSimpleCommand();
testParseSingleQuotesInCommand();
testParseDoubleQuotesInCommand();
testParseDoubleQuotesAndEscapesInCommand();
getSessionIdTest();
+ setSessionHistorySizeTest();
- NSLog(@"FFmpegKitConfigTest passed.");
+ NSLog(@"FFmpegKitTest passed.");
}
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS/OtherViewController.m b/macos/test-app-cocoapods/FFmpegKitMACOS/OtherViewController.m
index 1e0a993..5c425a2 100644
--- a/macos/test-app-cocoapods/FFmpegKitMACOS/OtherViewController.m
+++ b/macos/test-app-cocoapods/FFmpegKitMACOS/OtherViewController.m
@@ -128,7 +128,7 @@ -(void)testChromaprint {
NSString *chromaprintCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ -f chromaprint -fp_format 2 %@", audioSampleFile, [self getChromaprintOutputPath]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", chromaprintCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", chromaprintCommand);
[FFmpegKit executeAsync:chromaprintCommand withCompleteCallback:^(FFmpegSession* session) {
@@ -148,7 +148,7 @@ -(void)testDav1d {
NSString *ffmpegCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ %@", DAV1D_TEST_DEFAULT_URL, [self getDav1dOutputPath]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
NSLog(@"FFmpeg process exited with state %@ and rc %@.%@", [FFmpegKitConfig sessionStateToString:[session getState]], [session getReturnCode], notNull([session getFailStackTrace], @"\n"));
@@ -169,7 +169,7 @@ -(void)testWebp {
NSString *ffmpegCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ %@", imageFile, outputFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
@@ -191,7 +191,7 @@ -(void)testZscale {
NSString *ffmpegCommand = [Video generateZscaleVideoScript:videoFile:zscaledVideoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS/PipeViewController.m b/macos/test-app-cocoapods/FFmpegKitMACOS/PipeViewController.m
index 1d213ac..720675e 100644
--- a/macos/test-app-cocoapods/FFmpegKitMACOS/PipeViewController.m
+++ b/macos/test-app-cocoapods/FFmpegKitMACOS/PipeViewController.m
@@ -156,7 +156,7 @@ - (IBAction)createVideo:(id)sender {
NSString* ffmpegCommand = [Video generateCreateVideoWithPipesScript:pipe1:pipe2:pipe3:videoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -229,18 +229,16 @@ - (void)showProgressDialog:(NSString*)dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- int percentage = timeInMilliseconds*100/totalVideoDuration;
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
- [indicator updatePercentage:percentage];
- }
+ [indicator updatePercentage:percentage];
}
- (void)hideProgressDialog {
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS/SubtitleViewController.m b/macos/test-app-cocoapods/FFmpegKitMACOS/SubtitleViewController.m
index d55092c..fef6032 100644
--- a/macos/test-app-cocoapods/FFmpegKitMACOS/SubtitleViewController.m
+++ b/macos/test-app-cocoapods/FFmpegKitMACOS/SubtitleViewController.m
@@ -111,7 +111,7 @@ - (IBAction)burnSubtitles:(id)sender {
NSString* ffmpegCommand = [Video generateVideoEncodeScript:image1:image2:image3:videoFile:@"mpeg4":@""];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
self->state = CreatingState;
@@ -132,7 +132,7 @@ - (IBAction)burnSubtitles:(id)sender {
[self showProgressDialog:@"Burning subtitles\n\n"];
});
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", burnSubtitlesCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", burnSubtitlesCommand);
self->state = BurningState;
@@ -214,21 +214,19 @@ - (void)showProgressDialog:(NSString*)dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- int percentage = timeInMilliseconds*100/totalVideoDuration;
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
- if (state == CreatingState) {
- [indicator updateMessage:@"Creating video" percentage:percentage];
- } else if (state == BurningState) {
- [indicator updateMessage:@"Burning subtitles" percentage:percentage];
- }
+ if (state == CreatingState) {
+ [indicator updateMessage:@"Creating video" percentage:percentage];
+ } else if (state == BurningState) {
+ [indicator updateMessage:@"Burning subtitles" percentage:percentage];
}
}
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS/VidStabViewController.m b/macos/test-app-cocoapods/FFmpegKitMACOS/VidStabViewController.m
index 7573e51..d678ce9 100644
--- a/macos/test-app-cocoapods/FFmpegKitMACOS/VidStabViewController.m
+++ b/macos/test-app-cocoapods/FFmpegKitMACOS/VidStabViewController.m
@@ -95,7 +95,7 @@ - (IBAction)stabilizedVideo:(id)sender {
NSString* ffmpegCommand = [Video generateShakingVideoScript:image1:image2:image3:videoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
NSLog(@"FFmpeg process exited with state %@ and rc %@.%@", [FFmpegKitConfig sessionStateToString:[session getState]], [session getReturnCode], notNull([session getFailStackTrace], @"\n"));
@@ -113,7 +113,7 @@ - (IBAction)stabilizedVideo:(id)sender {
[self showProgressDialog:@"Stabilizing video\n\n"];
});
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", analyzeVideoCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", analyzeVideoCommand);
[FFmpegKit executeAsync:analyzeVideoCommand withCompleteCallback:^(id secondSession) {
@@ -123,7 +123,7 @@ - (IBAction)stabilizedVideo:(id)sender {
NSString *stabilizeVideoCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ -vf vidstabtransform=smoothing=30:input=%@ %@", videoFile, shakeResultsFile, stabilizedVideoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", stabilizeVideoCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", stabilizeVideoCommand);
[FFmpegKit executeAsync:stabilizeVideoCommand withCompleteCallback:^(id thirdSession) {
@@ -200,6 +200,7 @@ - (void)setActive {
[FFmpegKitConfig enableLogCallback:^(Log *log){
NSLog(@"%@", [log getMessage]);
}];
+ [FFmpegKitConfig enableStatisticsCallback:nil];
}
- (void)showProgressDialog:(NSString*)dialogMessage {
diff --git a/macos/test-app-cocoapods/FFmpegKitMACOS/VideoViewController.m b/macos/test-app-cocoapods/FFmpegKitMACOS/VideoViewController.m
index df236fe..d86d51a 100644
--- a/macos/test-app-cocoapods/FFmpegKitMACOS/VideoViewController.m
+++ b/macos/test-app-cocoapods/FFmpegKitMACOS/VideoViewController.m
@@ -128,7 +128,7 @@ - (IBAction)encodeVideo:(id)sender {
NSString* ffmpegCommand = [Video generateVideoEncodeScriptWithCustomPixelFormat:image1:image2:image3:videoFile:[self getSelectedVideoCodec]:[self getPixelFormat]:[self getCustomOptions]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
FFmpegSession* session = [FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session){
SessionState state = [session getState];
@@ -281,18 +281,16 @@ - (void)showProgressDialog:(NSString*)dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- int percentage = timeInMilliseconds*100/totalVideoDuration;
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
- [indicator updatePercentage:percentage];
- }
+ [indicator updatePercentage:percentage];
}
- (void)hideProgressDialog {
diff --git a/macos/test-app-cocoapods/Podfile b/macos/test-app-cocoapods/Podfile
index 663d3c9..726ce29 100644
--- a/macos/test-app-cocoapods/Podfile
+++ b/macos/test-app-cocoapods/Podfile
@@ -3,5 +3,5 @@ platform :osx, '10.15'
use_frameworks!
target "FFmpegKitMACOS" do
- pod 'ffmpeg-kit-macos-full', '4.5.1.LTS'
+ pod 'ffmpeg-kit-macos-full', '5.1'
end
diff --git a/macos/test-app-cocoapods/Podfile.lock b/macos/test-app-cocoapods/Podfile.lock
index e1d0d39..b235716 100644
--- a/macos/test-app-cocoapods/Podfile.lock
+++ b/macos/test-app-cocoapods/Podfile.lock
@@ -1,16 +1,16 @@
PODS:
- - ffmpeg-kit-macos-full (4.5.1.LTS)
+ - ffmpeg-kit-macos-full (5.1)
DEPENDENCIES:
- - ffmpeg-kit-macos-full (= 4.5.1.LTS)
+ - ffmpeg-kit-macos-full (= 5.1)
SPEC REPOS:
trunk:
- ffmpeg-kit-macos-full
SPEC CHECKSUMS:
- ffmpeg-kit-macos-full: 5db1604b88ada6c73c9da072a6bd09341e97009e
+ ffmpeg-kit-macos-full: c292021070be5472e9c855bd59bd44fa75352c84
-PODFILE CHECKSUM: f8fc2f038fe6b00cd764172e3d0fcba19e2dd30b
+PODFILE CHECKSUM: 8e96a72264bd9abe608123afbbde893bc39b2ebc
-COCOAPODS: 1.11.2
+COCOAPODS: 1.11.3
diff --git a/macos/test-app-local-dependency/FFmpegKitMACOS/AudioViewController.m b/macos/test-app-local-dependency/FFmpegKitMACOS/AudioViewController.m
index 0887342..f477a46 100644
--- a/macos/test-app-local-dependency/FFmpegKitMACOS/AudioViewController.m
+++ b/macos/test-app-local-dependency/FFmpegKitMACOS/AudioViewController.m
@@ -126,7 +126,7 @@ - (IBAction)encodeAudio:(id)sender {
[self clearOutput];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
diff --git a/macos/test-app-local-dependency/FFmpegKitMACOS/CommandViewController.m b/macos/test-app-local-dependency/FFmpegKitMACOS/CommandViewController.m
index 6de1ae6..a83ba76 100644
--- a/macos/test-app-local-dependency/FFmpegKitMACOS/CommandViewController.m
+++ b/macos/test-app-local-dependency/FFmpegKitMACOS/CommandViewController.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2021 Taner Sener
+ * Copyright (c) 2018-2022 Taner Sener
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -64,7 +64,7 @@ - (IBAction)runFFmpeg:(id)sender {
NSLog(@"Testing FFmpeg COMMAND asynchronously.\n");
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -91,9 +91,9 @@ - (IBAction)runFFprobe:(id)sender {
NSLog(@"Testing FFprobe COMMAND asynchronously.\n");
- NSLog(@"FFprobe process started with arguments\n'%@'.\n", ffprobeCommand);
+ NSLog(@"FFprobe process started with arguments '%@'.\n", ffprobeCommand);
- FFprobeSession *session = [[FFprobeSession alloc] init:[FFmpegKitConfig parseArguments:ffprobeCommand] withCompleteCallback:^(FFprobeSession* session) {
+ FFprobeSession *session = [FFprobeSession create:[FFmpegKitConfig parseArguments:ffprobeCommand] withCompleteCallback:^(FFprobeSession* session) {
SessionState state = [session getState];
ReturnCode* returnCode = [session getReturnCode];
diff --git a/macos/test-app-local-dependency/FFmpegKitMACOS/ConcurrentExecutionViewController.m b/macos/test-app-local-dependency/FFmpegKitMACOS/ConcurrentExecutionViewController.m
index e758a0c..8a345f6 100644
--- a/macos/test-app-local-dependency/FFmpegKitMACOS/ConcurrentExecutionViewController.m
+++ b/macos/test-app-local-dependency/FFmpegKitMACOS/ConcurrentExecutionViewController.m
@@ -113,7 +113,7 @@ - (void)encodeVideo:(int)buttonNumber {
NSString* ffmpegCommand = [Video generateVideoEncodeScript:image1:image2:image3:videoFile:@"mpeg4":@""];
- NSLog(@"FFmpeg process starting for button %d with arguments\n'%@'.\n", buttonNumber, ffmpegCommand);
+ NSLog(@"FFmpeg process starting for button %d with arguments '%@'.\n", buttonNumber, ffmpegCommand);
FFmpegSession* session = [FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -141,6 +141,7 @@ - (void)encodeVideo:(int)buttonNumber {
break;
default: {
sessionId3 = sessionId;
+ [FFmpegKitConfig setSessionHistorySize:3];
}
}
diff --git a/macos/test-app-local-dependency/FFmpegKitMACOS/Constants.m b/macos/test-app-local-dependency/FFmpegKitMACOS/Constants.m
index 97da478..e05f4b9 100644
--- a/macos/test-app-local-dependency/FFmpegKitMACOS/Constants.m
+++ b/macos/test-app-local-dependency/FFmpegKitMACOS/Constants.m
@@ -23,24 +23,24 @@
#include "Constants.h"
// COMMAND TEST
-NSString *const COMMAND_TEST_TOOLTIP_TEXT = @"Enter an FFmpeg command without 'ffmpeg' at the beginning and click one of the RUN buttons";
+NSString *const COMMAND_TEST_TOOLTIP_TEXT = @"Enter a command without ffmpeg/ffprobe at the beginning and click one of the RUN buttons";
NSTimeInterval const COMMAND_TEST_TOOLTIP_DURATION = 4.0;
// VIDEO TEST
-NSString *const VIDEO_TEST_TOOLTIP_TEXT = @"Select a video codec and press ENCODE button";
+NSString *const VIDEO_TEST_TOOLTIP_TEXT = @"Select a video codec and press the ENCODE button";
NSTimeInterval const VIDEO_TEST_TOOLTIP_DURATION = 4.0;
// HTTPS TEST
NSString *const HTTPS_TEST_DEFAULT_URL = @"https://download.blender.org/peach/trailer/trailer_1080p.ogg";
NSString *const HTTPS_TEST_FAIL_URL = @"https://download2.blender.org/peach/trailer/trailer_1080p.ogg";
-NSString *const HTTPS_TEST_RANDOM_URL_1 = @"https://file-examples-com.github.io/uploads/2018/04/file_example_MOV_640_800kB.mov";
-NSString *const HTTPS_TEST_RANDOM_URL_2 = @"https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3";
-NSString *const HTTPS_TEST_RANDOM_URL_3 = @"https://file-examples-com.github.io/uploads/2020/03/file_example_WEBP_50kB.webp";
+NSString *const HTTPS_TEST_RANDOM_URL_1 = @"https://filesamples.com/samples/video/mov/sample_640x360.mov";
+NSString *const HTTPS_TEST_RANDOM_URL_2 = @"https://filesamples.com/samples/audio/mp3/sample3.mp3";
+NSString *const HTTPS_TEST_RANDOM_URL_3 = @"https://filesamples.com/samples/image/webp/sample1.webp";
NSString *const HTTPS_TEST_TOOLTIP_TEXT = @"Enter the https url of a media file and click the button";
NSTimeInterval const HTTPS_TEST_TOOLTIP_DURATION = 4.0;
// AUDIO TEST
-NSString *const AUDIO_TEST_TOOLTIP_TEXT = @"Select an audio codec and press ENCODE button";
+NSString *const AUDIO_TEST_TOOLTIP_TEXT = @"Select an audio codec and press the ENCODE button";
NSTimeInterval const AUDIO_TEST_TOOLTIP_DURATION = 4.0;
// SUBTITLE TEST
diff --git a/macos/test-app-local-dependency/FFmpegKitMACOS/FFmpegKitTest.m b/macos/test-app-local-dependency/FFmpegKitMACOS/FFmpegKitTest.m
index 63e61d0..db15dd8 100644
--- a/macos/test-app-local-dependency/FFmpegKitMACOS/FFmpegKitTest.m
+++ b/macos/test-app-local-dependency/FFmpegKitMACOS/FFmpegKitTest.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019-2021 Taner Sener
+ * Copyright (c) 2019-2022 Taner Sener
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -193,9 +193,9 @@ void testParseDoubleQuotesAndEscapesInCommand() {
void getSessionIdTest() {
NSArray *TEST_ARGUMENTS = [[NSArray alloc] initWithObjects:@"argument1", @"argument2", nil];
- FFmpegSession *sessions1 = [[FFmpegSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
- FFprobeSession *sessions2 = [[FFprobeSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
- MediaInformationSession *sessions3 = [[MediaInformationSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
+ FFmpegSession *sessions1 = [FFmpegSession create:TEST_ARGUMENTS];
+ FFprobeSession *sessions2 = [FFprobeSession create:TEST_ARGUMENTS];
+ MediaInformationSession *sessions3 = [MediaInformationSession create:TEST_ARGUMENTS];
assert([sessions3 getSessionId] > [sessions2 getSessionId]);
assert([sessions3 getSessionId] > [sessions1 getSessionId]);
@@ -206,12 +206,31 @@ void getSessionIdTest() {
assert([sessions3 getSessionId] > 0);
}
+void setSessionHistorySizeTest() {
+ NSArray *TEST_ARGUMENTS = [[NSArray alloc] initWithObjects:@"argument1", @"argument2", nil];
+ int newSize = 15;
+ [FFmpegKitConfig setSessionHistorySize:newSize];
+
+ for (int i = 1; i <= (newSize + 5); i++) {
+ [FFmpegSession create:TEST_ARGUMENTS];
+ assert([[FFmpegKitConfig getSessions] count] <= newSize);
+ }
+
+ newSize = 3;
+ [FFmpegKitConfig setSessionHistorySize:newSize];
+ for (int i = 1; i <= (newSize + 5); i++) {
+ [FFmpegSession create:TEST_ARGUMENTS];
+ assert([[FFmpegKitConfig getSessions] count] <= newSize);
+ }
+}
+
void testFFmpegKit(void) {
testParseSimpleCommand();
testParseSingleQuotesInCommand();
testParseDoubleQuotesInCommand();
testParseDoubleQuotesAndEscapesInCommand();
getSessionIdTest();
+ setSessionHistorySizeTest();
- NSLog(@"FFmpegKitConfigTest passed.");
+ NSLog(@"FFmpegKitTest passed.");
}
diff --git a/macos/test-app-local-dependency/FFmpegKitMACOS/OtherViewController.m b/macos/test-app-local-dependency/FFmpegKitMACOS/OtherViewController.m
index 1e0a993..5c425a2 100644
--- a/macos/test-app-local-dependency/FFmpegKitMACOS/OtherViewController.m
+++ b/macos/test-app-local-dependency/FFmpegKitMACOS/OtherViewController.m
@@ -128,7 +128,7 @@ -(void)testChromaprint {
NSString *chromaprintCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ -f chromaprint -fp_format 2 %@", audioSampleFile, [self getChromaprintOutputPath]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", chromaprintCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", chromaprintCommand);
[FFmpegKit executeAsync:chromaprintCommand withCompleteCallback:^(FFmpegSession* session) {
@@ -148,7 +148,7 @@ -(void)testDav1d {
NSString *ffmpegCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ %@", DAV1D_TEST_DEFAULT_URL, [self getDav1dOutputPath]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
NSLog(@"FFmpeg process exited with state %@ and rc %@.%@", [FFmpegKitConfig sessionStateToString:[session getState]], [session getReturnCode], notNull([session getFailStackTrace], @"\n"));
@@ -169,7 +169,7 @@ -(void)testWebp {
NSString *ffmpegCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ %@", imageFile, outputFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
@@ -191,7 +191,7 @@ -(void)testZscale {
NSString *ffmpegCommand = [Video generateZscaleVideoScript:videoFile:zscaledVideoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
diff --git a/macos/test-app-local-dependency/FFmpegKitMACOS/PipeViewController.m b/macos/test-app-local-dependency/FFmpegKitMACOS/PipeViewController.m
index 1d213ac..720675e 100644
--- a/macos/test-app-local-dependency/FFmpegKitMACOS/PipeViewController.m
+++ b/macos/test-app-local-dependency/FFmpegKitMACOS/PipeViewController.m
@@ -156,7 +156,7 @@ - (IBAction)createVideo:(id)sender {
NSString* ffmpegCommand = [Video generateCreateVideoWithPipesScript:pipe1:pipe2:pipe3:videoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -229,18 +229,16 @@ - (void)showProgressDialog:(NSString*)dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- int percentage = timeInMilliseconds*100/totalVideoDuration;
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
- [indicator updatePercentage:percentage];
- }
+ [indicator updatePercentage:percentage];
}
- (void)hideProgressDialog {
diff --git a/macos/test-app-local-dependency/FFmpegKitMACOS/SubtitleViewController.m b/macos/test-app-local-dependency/FFmpegKitMACOS/SubtitleViewController.m
index d55092c..fef6032 100644
--- a/macos/test-app-local-dependency/FFmpegKitMACOS/SubtitleViewController.m
+++ b/macos/test-app-local-dependency/FFmpegKitMACOS/SubtitleViewController.m
@@ -111,7 +111,7 @@ - (IBAction)burnSubtitles:(id)sender {
NSString* ffmpegCommand = [Video generateVideoEncodeScript:image1:image2:image3:videoFile:@"mpeg4":@""];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
self->state = CreatingState;
@@ -132,7 +132,7 @@ - (IBAction)burnSubtitles:(id)sender {
[self showProgressDialog:@"Burning subtitles\n\n"];
});
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", burnSubtitlesCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", burnSubtitlesCommand);
self->state = BurningState;
@@ -214,21 +214,19 @@ - (void)showProgressDialog:(NSString*)dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- int percentage = timeInMilliseconds*100/totalVideoDuration;
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
- if (state == CreatingState) {
- [indicator updateMessage:@"Creating video" percentage:percentage];
- } else if (state == BurningState) {
- [indicator updateMessage:@"Burning subtitles" percentage:percentage];
- }
+ if (state == CreatingState) {
+ [indicator updateMessage:@"Creating video" percentage:percentage];
+ } else if (state == BurningState) {
+ [indicator updateMessage:@"Burning subtitles" percentage:percentage];
}
}
diff --git a/macos/test-app-local-dependency/FFmpegKitMACOS/VidStabViewController.m b/macos/test-app-local-dependency/FFmpegKitMACOS/VidStabViewController.m
index 7573e51..d678ce9 100644
--- a/macos/test-app-local-dependency/FFmpegKitMACOS/VidStabViewController.m
+++ b/macos/test-app-local-dependency/FFmpegKitMACOS/VidStabViewController.m
@@ -95,7 +95,7 @@ - (IBAction)stabilizedVideo:(id)sender {
NSString* ffmpegCommand = [Video generateShakingVideoScript:image1:image2:image3:videoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
NSLog(@"FFmpeg process exited with state %@ and rc %@.%@", [FFmpegKitConfig sessionStateToString:[session getState]], [session getReturnCode], notNull([session getFailStackTrace], @"\n"));
@@ -113,7 +113,7 @@ - (IBAction)stabilizedVideo:(id)sender {
[self showProgressDialog:@"Stabilizing video\n\n"];
});
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", analyzeVideoCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", analyzeVideoCommand);
[FFmpegKit executeAsync:analyzeVideoCommand withCompleteCallback:^(id secondSession) {
@@ -123,7 +123,7 @@ - (IBAction)stabilizedVideo:(id)sender {
NSString *stabilizeVideoCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ -vf vidstabtransform=smoothing=30:input=%@ %@", videoFile, shakeResultsFile, stabilizedVideoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", stabilizeVideoCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", stabilizeVideoCommand);
[FFmpegKit executeAsync:stabilizeVideoCommand withCompleteCallback:^(id thirdSession) {
@@ -200,6 +200,7 @@ - (void)setActive {
[FFmpegKitConfig enableLogCallback:^(Log *log){
NSLog(@"%@", [log getMessage]);
}];
+ [FFmpegKitConfig enableStatisticsCallback:nil];
}
- (void)showProgressDialog:(NSString*)dialogMessage {
diff --git a/macos/test-app-local-dependency/FFmpegKitMACOS/VideoViewController.m b/macos/test-app-local-dependency/FFmpegKitMACOS/VideoViewController.m
index df236fe..d86d51a 100644
--- a/macos/test-app-local-dependency/FFmpegKitMACOS/VideoViewController.m
+++ b/macos/test-app-local-dependency/FFmpegKitMACOS/VideoViewController.m
@@ -128,7 +128,7 @@ - (IBAction)encodeVideo:(id)sender {
NSString* ffmpegCommand = [Video generateVideoEncodeScriptWithCustomPixelFormat:image1:image2:image3:videoFile:[self getSelectedVideoCodec]:[self getPixelFormat]:[self getCustomOptions]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
FFmpegSession* session = [FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session){
SessionState state = [session getState];
@@ -281,18 +281,16 @@ - (void)showProgressDialog:(NSString*)dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- int percentage = timeInMilliseconds*100/totalVideoDuration;
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
- [indicator updatePercentage:percentage];
- }
+ [indicator updatePercentage:percentage];
}
- (void)hideProgressDialog {
diff --git a/react-native/README.md b/react-native/README.md
index eb2877c..6a38677 100644
--- a/react-native/README.md
+++ b/react-native/README.md
@@ -1,3 +1,3 @@
# FFmpegKit ReactNative
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/scripts/clean.sh b/scripts/clean.sh
index b113d31..aebc66a 100755
--- a/scripts/clean.sh
+++ b/scripts/clean.sh
@@ -49,3 +49,5 @@ rm -rf ../macos/test-app-local-dependency/*.xcframework
rm -rf ../tvos/test-app-cocoapods/Pods
rm -rf ../tvos/test-app-local-dependency/*.framework
rm -rf ../tvos/test-app-local-dependency/*.xcframework
+
+rm -rf ../linux/test-app-local-dependency/build
diff --git a/tvos/README.md b/tvos/README.md
index 63cbc89..c862128 100644
--- a/tvos/README.md
+++ b/tvos/README.md
@@ -1,3 +1,3 @@
# FFmpegKit tvOS
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS.xcodeproj/project.pbxproj b/tvos/test-app-cocoapods/FFmpegKitTVOS.xcodeproj/project.pbxproj
index c7dc0a1..2302675 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS.xcodeproj/project.pbxproj
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS.xcodeproj/project.pbxproj
@@ -302,14 +302,14 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-FFmpegKitTVOS/Pods-FFmpegKitTVOS-frameworks.sh",
- "${PODS_ROOT}/ffmpeg-kit-tvos-full/ffmpegkit.framework",
- "${PODS_ROOT}/ffmpeg-kit-tvos-full/libavcodec.framework",
- "${PODS_ROOT}/ffmpeg-kit-tvos-full/libavdevice.framework",
- "${PODS_ROOT}/ffmpeg-kit-tvos-full/libavfilter.framework",
- "${PODS_ROOT}/ffmpeg-kit-tvos-full/libavformat.framework",
- "${PODS_ROOT}/ffmpeg-kit-tvos-full/libavutil.framework",
- "${PODS_ROOT}/ffmpeg-kit-tvos-full/libswresample.framework",
- "${PODS_ROOT}/ffmpeg-kit-tvos-full/libswscale.framework",
+ "${PODS_XCFRAMEWORKS_BUILD_DIR}/ffmpeg-kit-tvos-full/ffmpegkit.framework/ffmpegkit",
+ "${PODS_XCFRAMEWORKS_BUILD_DIR}/ffmpeg-kit-tvos-full/libavcodec.framework/libavcodec",
+ "${PODS_XCFRAMEWORKS_BUILD_DIR}/ffmpeg-kit-tvos-full/libavdevice.framework/libavdevice",
+ "${PODS_XCFRAMEWORKS_BUILD_DIR}/ffmpeg-kit-tvos-full/libavfilter.framework/libavfilter",
+ "${PODS_XCFRAMEWORKS_BUILD_DIR}/ffmpeg-kit-tvos-full/libavformat.framework/libavformat",
+ "${PODS_XCFRAMEWORKS_BUILD_DIR}/ffmpeg-kit-tvos-full/libavutil.framework/libavutil",
+ "${PODS_XCFRAMEWORKS_BUILD_DIR}/ffmpeg-kit-tvos-full/libswresample.framework/libswresample",
+ "${PODS_XCFRAMEWORKS_BUILD_DIR}/ffmpeg-kit-tvos-full/libswscale.framework/libswscale",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate b/tvos/test-app-cocoapods/FFmpegKitTVOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate
index 5442a71..62b8d50 100644
Binary files a/tvos/test-app-cocoapods/FFmpegKitTVOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate and b/tvos/test-app-cocoapods/FFmpegKitTVOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS/AudioViewController.m b/tvos/test-app-cocoapods/FFmpegKitTVOS/AudioViewController.m
index 4d1960d..f881d5b 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS/AudioViewController.m
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS/AudioViewController.m
@@ -96,7 +96,7 @@ - (IBAction)encodeAudio:(id)sender {
[self clearOutput];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS/CommandViewController.m b/tvos/test-app-cocoapods/FFmpegKitTVOS/CommandViewController.m
index 17bb8aa..ae17326 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS/CommandViewController.m
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS/CommandViewController.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2021 Taner Sener
+ * Copyright (c) 2018-2022 Taner Sener
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -69,7 +69,7 @@ - (IBAction)runFFmpeg:(id)sender {
NSLog(@"Testing FFmpeg COMMAND asynchronously.\n");
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -98,9 +98,9 @@ - (IBAction)runFFprobe:(id)sender {
NSLog(@"Testing FFprobe COMMAND asynchronously.\n");
- NSLog(@"FFprobe process started with arguments\n'%@'.\n", ffprobeCommand);
+ NSLog(@"FFprobe process started with arguments '%@'.\n", ffprobeCommand);
- FFprobeSession *session = [[FFprobeSession alloc] init:[FFmpegKitConfig parseArguments:ffprobeCommand] withCompleteCallback:^(FFprobeSession* session) {
+ FFprobeSession *session = [FFprobeSession create:[FFmpegKitConfig parseArguments:ffprobeCommand] withCompleteCallback:^(FFprobeSession* session) {
SessionState state = [session getState];
ReturnCode* returnCode = [session getReturnCode];
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS/ConcurrentExecutionViewController.m b/tvos/test-app-cocoapods/FFmpegKitTVOS/ConcurrentExecutionViewController.m
index c3f8d80..611c722 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS/ConcurrentExecutionViewController.m
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS/ConcurrentExecutionViewController.m
@@ -119,7 +119,7 @@ - (void)encodeVideo:(int)buttonNumber {
NSString* ffmpegCommand = [Video generateVideoEncodeScript:image1:image2:image3:videoFile:@"mpeg4":@""];
- NSLog(@"FFmpeg process starting for button %d with arguments\n'%@'.\n", buttonNumber, ffmpegCommand);
+ NSLog(@"FFmpeg process starting for button %d with arguments '%@'.\n", buttonNumber, ffmpegCommand);
FFmpegSession* session = [FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -147,6 +147,7 @@ - (void)encodeVideo:(int)buttonNumber {
break;
default: {
sessionId3 = sessionId;
+ [FFmpegKitConfig setSessionHistorySize:3];
}
}
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS/Constants.m b/tvos/test-app-cocoapods/FFmpegKitTVOS/Constants.m
index 97da478..e05f4b9 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS/Constants.m
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS/Constants.m
@@ -23,24 +23,24 @@
#include "Constants.h"
// COMMAND TEST
-NSString *const COMMAND_TEST_TOOLTIP_TEXT = @"Enter an FFmpeg command without 'ffmpeg' at the beginning and click one of the RUN buttons";
+NSString *const COMMAND_TEST_TOOLTIP_TEXT = @"Enter a command without ffmpeg/ffprobe at the beginning and click one of the RUN buttons";
NSTimeInterval const COMMAND_TEST_TOOLTIP_DURATION = 4.0;
// VIDEO TEST
-NSString *const VIDEO_TEST_TOOLTIP_TEXT = @"Select a video codec and press ENCODE button";
+NSString *const VIDEO_TEST_TOOLTIP_TEXT = @"Select a video codec and press the ENCODE button";
NSTimeInterval const VIDEO_TEST_TOOLTIP_DURATION = 4.0;
// HTTPS TEST
NSString *const HTTPS_TEST_DEFAULT_URL = @"https://download.blender.org/peach/trailer/trailer_1080p.ogg";
NSString *const HTTPS_TEST_FAIL_URL = @"https://download2.blender.org/peach/trailer/trailer_1080p.ogg";
-NSString *const HTTPS_TEST_RANDOM_URL_1 = @"https://file-examples-com.github.io/uploads/2018/04/file_example_MOV_640_800kB.mov";
-NSString *const HTTPS_TEST_RANDOM_URL_2 = @"https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3";
-NSString *const HTTPS_TEST_RANDOM_URL_3 = @"https://file-examples-com.github.io/uploads/2020/03/file_example_WEBP_50kB.webp";
+NSString *const HTTPS_TEST_RANDOM_URL_1 = @"https://filesamples.com/samples/video/mov/sample_640x360.mov";
+NSString *const HTTPS_TEST_RANDOM_URL_2 = @"https://filesamples.com/samples/audio/mp3/sample3.mp3";
+NSString *const HTTPS_TEST_RANDOM_URL_3 = @"https://filesamples.com/samples/image/webp/sample1.webp";
NSString *const HTTPS_TEST_TOOLTIP_TEXT = @"Enter the https url of a media file and click the button";
NSTimeInterval const HTTPS_TEST_TOOLTIP_DURATION = 4.0;
// AUDIO TEST
-NSString *const AUDIO_TEST_TOOLTIP_TEXT = @"Select an audio codec and press ENCODE button";
+NSString *const AUDIO_TEST_TOOLTIP_TEXT = @"Select an audio codec and press the ENCODE button";
NSTimeInterval const AUDIO_TEST_TOOLTIP_DURATION = 4.0;
// SUBTITLE TEST
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS/FFmpegKitTest.m b/tvos/test-app-cocoapods/FFmpegKitTVOS/FFmpegKitTest.m
index 81b1bd7..af68d67 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS/FFmpegKitTest.m
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS/FFmpegKitTest.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019-2021 Taner Sener
+ * Copyright (c) 2019-2022 Taner Sener
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -192,9 +192,9 @@ void testParseDoubleQuotesAndEscapesInCommand() {
void getSessionIdTest() {
NSArray *TEST_ARGUMENTS = [[NSArray alloc] initWithObjects:@"argument1", @"argument2", nil];
- FFmpegSession *sessions1 = [[FFmpegSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
- FFprobeSession *sessions2 = [[FFprobeSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
- MediaInformationSession *sessions3 = [[MediaInformationSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
+ FFmpegSession *sessions1 = [FFmpegSession create:TEST_ARGUMENTS];
+ FFprobeSession *sessions2 = [FFprobeSession create:TEST_ARGUMENTS];
+ MediaInformationSession *sessions3 = [MediaInformationSession create:TEST_ARGUMENTS];
assert([sessions3 getSessionId] > [sessions2 getSessionId]);
assert([sessions3 getSessionId] > [sessions1 getSessionId]);
@@ -205,12 +205,31 @@ void getSessionIdTest() {
assert([sessions3 getSessionId] > 0);
}
+void setSessionHistorySizeTest() {
+ NSArray *TEST_ARGUMENTS = [[NSArray alloc] initWithObjects:@"argument1", @"argument2", nil];
+ int newSize = 15;
+ [FFmpegKitConfig setSessionHistorySize:newSize];
+
+ for (int i = 1; i <= (newSize + 5); i++) {
+ [FFmpegSession create:TEST_ARGUMENTS];
+ assert([[FFmpegKitConfig getSessions] count] <= newSize);
+ }
+
+ newSize = 3;
+ [FFmpegKitConfig setSessionHistorySize:newSize];
+ for (int i = 1; i <= (newSize + 5); i++) {
+ [FFmpegSession create:TEST_ARGUMENTS];
+ assert([[FFmpegKitConfig getSessions] count] <= newSize);
+ }
+}
+
void testFFmpegKit(void) {
testParseSimpleCommand();
testParseSingleQuotesInCommand();
testParseDoubleQuotesInCommand();
testParseDoubleQuotesAndEscapesInCommand();
getSessionIdTest();
+ setSessionHistorySizeTest();
- NSLog(@"FFmpegKitConfigTest passed.");
+ NSLog(@"FFmpegKitTest passed.");
}
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS/OtherViewController.m b/tvos/test-app-cocoapods/FFmpegKitTVOS/OtherViewController.m
index 99feea4..32a68ee 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS/OtherViewController.m
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS/OtherViewController.m
@@ -90,7 +90,7 @@ -(void)testChromaprint {
NSString *chromaprintCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ -f chromaprint -fp_format 2 %@", audioSampleFile, [self getChromaprintOutputPath]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", chromaprintCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", chromaprintCommand);
[FFmpegKit executeAsync:chromaprintCommand withCompleteCallback:^(FFmpegSession* session) {
@@ -110,7 +110,7 @@ -(void)testDav1d {
NSString *ffmpegCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ %@", DAV1D_TEST_DEFAULT_URL, [self getDav1dOutputPath]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
NSLog(@"FFmpeg process exited with state %@ and rc %@.%@", [FFmpegKitConfig sessionStateToString:[session getState]], [session getReturnCode], notNull([session getFailStackTrace], @"\n"));
@@ -131,7 +131,7 @@ -(void)testWebp {
NSString *ffmpegCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ %@", imageFile, outputFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
@@ -153,7 +153,7 @@ -(void)testZscale {
NSString *ffmpegCommand = [Video generateZscaleVideoScript:videoFile:zscaledVideoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS/PipeViewController.m b/tvos/test-app-cocoapods/FFmpegKitTVOS/PipeViewController.m
index ad98527..3862b12 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS/PipeViewController.m
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS/PipeViewController.m
@@ -173,7 +173,7 @@ - (IBAction)createVideo:(id)sender {
NSString* ffmpegCommand = [Video generateCreateVideoWithPipesScript:pipe1:pipe2:pipe3:videoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -260,19 +260,17 @@ - (void)showProgressDialog:(NSString*) dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
if (alertController != nil) {
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- int percentage = timeInMilliseconds*100/totalVideoDuration;
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
- [alertController setMessage:[NSString stringWithFormat:@"Creating video %% %d \n\n", percentage]];
- }
+ [alertController setMessage:[NSString stringWithFormat:@"Creating video %% %d \n\n", percentage]];
}
}
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS/SubtitleViewController.m b/tvos/test-app-cocoapods/FFmpegKitTVOS/SubtitleViewController.m
index 1cc3aa1..a757a14 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS/SubtitleViewController.m
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS/SubtitleViewController.m
@@ -131,7 +131,7 @@ - (IBAction)burnSubtitles:(id)sender {
NSString* ffmpegCommand = [Video generateVideoEncodeScript:image1:image2:image3:videoFile:@"mpeg4":@""];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
self->state = CreatingState;
@@ -152,7 +152,7 @@ - (IBAction)burnSubtitles:(id)sender {
[self showProgressDialog:@"Burning subtitles\n\n"];
});
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", burnSubtitlesCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", burnSubtitlesCommand);
self->state = BurningState;
@@ -254,22 +254,20 @@ - (void)showProgressDialog:(NSString*) dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
if (alertController != nil) {
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
-
- int percentage = timeInMilliseconds*100/totalVideoDuration;
-
- if (state == CreatingState) {
- [alertController setMessage:[NSString stringWithFormat:@"Creating video %% %d \n\n", percentage]];
- } else if (state == BurningState) {
- [alertController setMessage:[NSString stringWithFormat:@"Burning subtitles %% %d \n\n", percentage]];
- }
+ int totalVideoDuration = 9000;
+
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
+
+ if (state == CreatingState) {
+ [alertController setMessage:[NSString stringWithFormat:@"Creating video %% %d \n\n", percentage]];
+ } else if (state == BurningState) {
+ [alertController setMessage:[NSString stringWithFormat:@"Burning subtitles %% %d \n\n", percentage]];
}
}
}
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS/VidStabViewController.m b/tvos/test-app-cocoapods/FFmpegKitTVOS/VidStabViewController.m
index 66a6ec3..13e79e0 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS/VidStabViewController.m
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS/VidStabViewController.m
@@ -123,7 +123,7 @@ - (IBAction)stabilizedVideo:(id)sender {
NSString* ffmpegCommand = [Video generateShakingVideoScript:image1:image2:image3:videoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
NSLog(@"FFmpeg process exited with state %@ and rc %@.%@", [FFmpegKitConfig sessionStateToString:[session getState]], [session getReturnCode], notNull([session getFailStackTrace], @"\n"));
@@ -141,7 +141,7 @@ - (IBAction)stabilizedVideo:(id)sender {
[self showProgressDialog:@"Stabilizing video\n\n"];
});
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", analyzeVideoCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", analyzeVideoCommand);
[FFmpegKit executeAsync:analyzeVideoCommand withCompleteCallback:^(id secondSession) {
@@ -151,7 +151,7 @@ - (IBAction)stabilizedVideo:(id)sender {
NSString *stabilizeVideoCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ -vf vidstabtransform=smoothing=30:input=%@ %@", videoFile, shakeResultsFile, stabilizedVideoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", stabilizeVideoCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", stabilizeVideoCommand);
[FFmpegKit executeAsync:stabilizeVideoCommand withCompleteCallback:^(id thirdSession) {
@@ -228,6 +228,7 @@ - (void)setActive {
[FFmpegKitConfig enableLogCallback:^(Log *log){
NSLog(@"%@", [log getMessage]);
}];
+ [FFmpegKitConfig enableStatisticsCallback:nil];
}
- (void)showProgressDialog:(NSString*) dialogMessage {
diff --git a/tvos/test-app-cocoapods/FFmpegKitTVOS/VideoViewController.m b/tvos/test-app-cocoapods/FFmpegKitTVOS/VideoViewController.m
index 31f43fb..9ba95aa 100644
--- a/tvos/test-app-cocoapods/FFmpegKitTVOS/VideoViewController.m
+++ b/tvos/test-app-cocoapods/FFmpegKitTVOS/VideoViewController.m
@@ -111,7 +111,7 @@ - (IBAction)encodeVideo:(id)sender {
NSString* ffmpegCommand = [Video generateVideoEncodeScriptWithCustomPixelFormat:image1:image2:image3:videoFile:[self getSelectedVideoCodec]:[self getPixelFormat]:[self getCustomOptions]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
FFmpegSession* session = [FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session){
SessionState state = [session getState];
@@ -278,19 +278,17 @@ - (void)showProgressDialog:(NSString*) dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
if (alertController != nil) {
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- int percentage = timeInMilliseconds*100/totalVideoDuration;
-
- [alertController setMessage:[NSString stringWithFormat:@"Encoding video %% %d \n\n", percentage]];
- }
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
+
+ [alertController setMessage:[NSString stringWithFormat:@"Encoding video %% %d \n\n", percentage]];
}
}
diff --git a/tvos/test-app-cocoapods/Podfile b/tvos/test-app-cocoapods/Podfile
index 3584caa..f02871c 100644
--- a/tvos/test-app-cocoapods/Podfile
+++ b/tvos/test-app-cocoapods/Podfile
@@ -1,7 +1,7 @@
-platform :tvos, '10.2'
+platform :tvos, '11.0'
use_frameworks!
target "FFmpegKitTVOS" do
- pod 'ffmpeg-kit-tvos-full', '4.5.1.LTS'
+ pod 'ffmpeg-kit-tvos-full', '5.1'
end
diff --git a/tvos/test-app-cocoapods/Podfile.lock b/tvos/test-app-cocoapods/Podfile.lock
index 3f516c9..373776b 100644
--- a/tvos/test-app-cocoapods/Podfile.lock
+++ b/tvos/test-app-cocoapods/Podfile.lock
@@ -1,16 +1,16 @@
PODS:
- - ffmpeg-kit-tvos-full (4.5.1.LTS)
+ - ffmpeg-kit-tvos-full (5.1)
DEPENDENCIES:
- - ffmpeg-kit-tvos-full (= 4.5.1.LTS)
+ - ffmpeg-kit-tvos-full (= 5.1)
SPEC REPOS:
trunk:
- ffmpeg-kit-tvos-full
SPEC CHECKSUMS:
- ffmpeg-kit-tvos-full: 97d802696cbcc29a08c3d6b0dc461eb6eb2fb9e6
+ ffmpeg-kit-tvos-full: f98fd71a863bd7278bdbd57a8215fa2871c53c82
-PODFILE CHECKSUM: a6b09ab19b0863fa198b8dc4104ec338c649dd3b
+PODFILE CHECKSUM: f6debb332e46b18eac87c8576514513247ab7f80
-COCOAPODS: 1.11.2
+COCOAPODS: 1.11.3
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS.xcodeproj/project.pbxproj b/tvos/test-app-local-dependency/FFmpegKitTVOS.xcodeproj/project.pbxproj
index a9bec3f..6cb5a90 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS.xcodeproj/project.pbxproj
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS.xcodeproj/project.pbxproj
@@ -282,6 +282,7 @@
34695E8322AD292000889D20 /* Frameworks */,
34695E8422AD292000889D20 /* Resources */,
34416D5D2734B4C800F225A0 /* Embed Frameworks */,
+ 3417556128E75135001438A9 /* ShellScript */,
);
buildRules = (
);
@@ -344,6 +345,26 @@
};
/* End PBXResourcesBuildPhase section */
+/* Begin PBXShellScriptBuildPhase section */
+ 3417556128E75135001438A9 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/ffmpegkit.framework/strip-frameworks.sh\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
/* Begin PBXSourcesBuildPhase section */
34695E8222AD292000889D20 /* Sources */ = {
isa = PBXSourcesBuildPhase;
@@ -391,7 +412,7 @@
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 12;
+ CURRENT_PROJECT_VERSION = 15;
DEVELOPMENT_TEAM = 98GD5J4999;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -420,7 +441,7 @@
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 12;
+ CURRENT_PROJECT_VERSION = 15;
DEVELOPMENT_TEAM = 98GD5J4999;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate b/tvos/test-app-local-dependency/FFmpegKitTVOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate
index 40fa5f8..d516b7c 100644
Binary files a/tvos/test-app-local-dependency/FFmpegKitTVOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate and b/tvos/test-app-local-dependency/FFmpegKitTVOS.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS/AudioViewController.m b/tvos/test-app-local-dependency/FFmpegKitTVOS/AudioViewController.m
index 4d1960d..f881d5b 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS/AudioViewController.m
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS/AudioViewController.m
@@ -96,7 +96,7 @@ - (IBAction)encodeAudio:(id)sender {
[self clearOutput];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS/CommandViewController.m b/tvos/test-app-local-dependency/FFmpegKitTVOS/CommandViewController.m
index 17bb8aa..ae17326 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS/CommandViewController.m
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS/CommandViewController.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2021 Taner Sener
+ * Copyright (c) 2018-2022 Taner Sener
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -69,7 +69,7 @@ - (IBAction)runFFmpeg:(id)sender {
NSLog(@"Testing FFmpeg COMMAND asynchronously.\n");
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -98,9 +98,9 @@ - (IBAction)runFFprobe:(id)sender {
NSLog(@"Testing FFprobe COMMAND asynchronously.\n");
- NSLog(@"FFprobe process started with arguments\n'%@'.\n", ffprobeCommand);
+ NSLog(@"FFprobe process started with arguments '%@'.\n", ffprobeCommand);
- FFprobeSession *session = [[FFprobeSession alloc] init:[FFmpegKitConfig parseArguments:ffprobeCommand] withCompleteCallback:^(FFprobeSession* session) {
+ FFprobeSession *session = [FFprobeSession create:[FFmpegKitConfig parseArguments:ffprobeCommand] withCompleteCallback:^(FFprobeSession* session) {
SessionState state = [session getState];
ReturnCode* returnCode = [session getReturnCode];
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS/ConcurrentExecutionViewController.m b/tvos/test-app-local-dependency/FFmpegKitTVOS/ConcurrentExecutionViewController.m
index c3f8d80..611c722 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS/ConcurrentExecutionViewController.m
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS/ConcurrentExecutionViewController.m
@@ -119,7 +119,7 @@ - (void)encodeVideo:(int)buttonNumber {
NSString* ffmpegCommand = [Video generateVideoEncodeScript:image1:image2:image3:videoFile:@"mpeg4":@""];
- NSLog(@"FFmpeg process starting for button %d with arguments\n'%@'.\n", buttonNumber, ffmpegCommand);
+ NSLog(@"FFmpeg process starting for button %d with arguments '%@'.\n", buttonNumber, ffmpegCommand);
FFmpegSession* session = [FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -147,6 +147,7 @@ - (void)encodeVideo:(int)buttonNumber {
break;
default: {
sessionId3 = sessionId;
+ [FFmpegKitConfig setSessionHistorySize:3];
}
}
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS/Constants.m b/tvos/test-app-local-dependency/FFmpegKitTVOS/Constants.m
index 97da478..e05f4b9 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS/Constants.m
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS/Constants.m
@@ -23,24 +23,24 @@
#include "Constants.h"
// COMMAND TEST
-NSString *const COMMAND_TEST_TOOLTIP_TEXT = @"Enter an FFmpeg command without 'ffmpeg' at the beginning and click one of the RUN buttons";
+NSString *const COMMAND_TEST_TOOLTIP_TEXT = @"Enter a command without ffmpeg/ffprobe at the beginning and click one of the RUN buttons";
NSTimeInterval const COMMAND_TEST_TOOLTIP_DURATION = 4.0;
// VIDEO TEST
-NSString *const VIDEO_TEST_TOOLTIP_TEXT = @"Select a video codec and press ENCODE button";
+NSString *const VIDEO_TEST_TOOLTIP_TEXT = @"Select a video codec and press the ENCODE button";
NSTimeInterval const VIDEO_TEST_TOOLTIP_DURATION = 4.0;
// HTTPS TEST
NSString *const HTTPS_TEST_DEFAULT_URL = @"https://download.blender.org/peach/trailer/trailer_1080p.ogg";
NSString *const HTTPS_TEST_FAIL_URL = @"https://download2.blender.org/peach/trailer/trailer_1080p.ogg";
-NSString *const HTTPS_TEST_RANDOM_URL_1 = @"https://file-examples-com.github.io/uploads/2018/04/file_example_MOV_640_800kB.mov";
-NSString *const HTTPS_TEST_RANDOM_URL_2 = @"https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3";
-NSString *const HTTPS_TEST_RANDOM_URL_3 = @"https://file-examples-com.github.io/uploads/2020/03/file_example_WEBP_50kB.webp";
+NSString *const HTTPS_TEST_RANDOM_URL_1 = @"https://filesamples.com/samples/video/mov/sample_640x360.mov";
+NSString *const HTTPS_TEST_RANDOM_URL_2 = @"https://filesamples.com/samples/audio/mp3/sample3.mp3";
+NSString *const HTTPS_TEST_RANDOM_URL_3 = @"https://filesamples.com/samples/image/webp/sample1.webp";
NSString *const HTTPS_TEST_TOOLTIP_TEXT = @"Enter the https url of a media file and click the button";
NSTimeInterval const HTTPS_TEST_TOOLTIP_DURATION = 4.0;
// AUDIO TEST
-NSString *const AUDIO_TEST_TOOLTIP_TEXT = @"Select an audio codec and press ENCODE button";
+NSString *const AUDIO_TEST_TOOLTIP_TEXT = @"Select an audio codec and press the ENCODE button";
NSTimeInterval const AUDIO_TEST_TOOLTIP_DURATION = 4.0;
// SUBTITLE TEST
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS/FFmpegKitTest.m b/tvos/test-app-local-dependency/FFmpegKitTVOS/FFmpegKitTest.m
index 81b1bd7..af68d67 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS/FFmpegKitTest.m
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS/FFmpegKitTest.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019-2021 Taner Sener
+ * Copyright (c) 2019-2022 Taner Sener
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -192,9 +192,9 @@ void testParseDoubleQuotesAndEscapesInCommand() {
void getSessionIdTest() {
NSArray *TEST_ARGUMENTS = [[NSArray alloc] initWithObjects:@"argument1", @"argument2", nil];
- FFmpegSession *sessions1 = [[FFmpegSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
- FFprobeSession *sessions2 = [[FFprobeSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
- MediaInformationSession *sessions3 = [[MediaInformationSession alloc] init:TEST_ARGUMENTS withCompleteCallback:nil];
+ FFmpegSession *sessions1 = [FFmpegSession create:TEST_ARGUMENTS];
+ FFprobeSession *sessions2 = [FFprobeSession create:TEST_ARGUMENTS];
+ MediaInformationSession *sessions3 = [MediaInformationSession create:TEST_ARGUMENTS];
assert([sessions3 getSessionId] > [sessions2 getSessionId]);
assert([sessions3 getSessionId] > [sessions1 getSessionId]);
@@ -205,12 +205,31 @@ void getSessionIdTest() {
assert([sessions3 getSessionId] > 0);
}
+void setSessionHistorySizeTest() {
+ NSArray *TEST_ARGUMENTS = [[NSArray alloc] initWithObjects:@"argument1", @"argument2", nil];
+ int newSize = 15;
+ [FFmpegKitConfig setSessionHistorySize:newSize];
+
+ for (int i = 1; i <= (newSize + 5); i++) {
+ [FFmpegSession create:TEST_ARGUMENTS];
+ assert([[FFmpegKitConfig getSessions] count] <= newSize);
+ }
+
+ newSize = 3;
+ [FFmpegKitConfig setSessionHistorySize:newSize];
+ for (int i = 1; i <= (newSize + 5); i++) {
+ [FFmpegSession create:TEST_ARGUMENTS];
+ assert([[FFmpegKitConfig getSessions] count] <= newSize);
+ }
+}
+
void testFFmpegKit(void) {
testParseSimpleCommand();
testParseSingleQuotesInCommand();
testParseDoubleQuotesInCommand();
testParseDoubleQuotesAndEscapesInCommand();
getSessionIdTest();
+ setSessionHistorySizeTest();
- NSLog(@"FFmpegKitConfigTest passed.");
+ NSLog(@"FFmpegKitTest passed.");
}
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS/OtherViewController.m b/tvos/test-app-local-dependency/FFmpegKitTVOS/OtherViewController.m
index 99feea4..32a68ee 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS/OtherViewController.m
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS/OtherViewController.m
@@ -90,7 +90,7 @@ -(void)testChromaprint {
NSString *chromaprintCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ -f chromaprint -fp_format 2 %@", audioSampleFile, [self getChromaprintOutputPath]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", chromaprintCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", chromaprintCommand);
[FFmpegKit executeAsync:chromaprintCommand withCompleteCallback:^(FFmpegSession* session) {
@@ -110,7 +110,7 @@ -(void)testDav1d {
NSString *ffmpegCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ %@", DAV1D_TEST_DEFAULT_URL, [self getDav1dOutputPath]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
NSLog(@"FFmpeg process exited with state %@ and rc %@.%@", [FFmpegKitConfig sessionStateToString:[session getState]], [session getReturnCode], notNull([session getFailStackTrace], @"\n"));
@@ -131,7 +131,7 @@ -(void)testWebp {
NSString *ffmpegCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ %@", imageFile, outputFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
@@ -153,7 +153,7 @@ -(void)testZscale {
NSString *ffmpegCommand = [Video generateZscaleVideoScript:videoFile:zscaledVideoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS/PipeViewController.m b/tvos/test-app-local-dependency/FFmpegKitTVOS/PipeViewController.m
index ad98527..3862b12 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS/PipeViewController.m
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS/PipeViewController.m
@@ -173,7 +173,7 @@ - (IBAction)createVideo:(id)sender {
NSString* ffmpegCommand = [Video generateCreateVideoWithPipesScript:pipe1:pipe2:pipe3:videoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
SessionState state = [session getState];
@@ -260,19 +260,17 @@ - (void)showProgressDialog:(NSString*) dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
if (alertController != nil) {
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- int percentage = timeInMilliseconds*100/totalVideoDuration;
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
- [alertController setMessage:[NSString stringWithFormat:@"Creating video %% %d \n\n", percentage]];
- }
+ [alertController setMessage:[NSString stringWithFormat:@"Creating video %% %d \n\n", percentage]];
}
}
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS/SubtitleViewController.m b/tvos/test-app-local-dependency/FFmpegKitTVOS/SubtitleViewController.m
index 1cc3aa1..a757a14 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS/SubtitleViewController.m
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS/SubtitleViewController.m
@@ -131,7 +131,7 @@ - (IBAction)burnSubtitles:(id)sender {
NSString* ffmpegCommand = [Video generateVideoEncodeScript:image1:image2:image3:videoFile:@"mpeg4":@""];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
self->state = CreatingState;
@@ -152,7 +152,7 @@ - (IBAction)burnSubtitles:(id)sender {
[self showProgressDialog:@"Burning subtitles\n\n"];
});
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", burnSubtitlesCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", burnSubtitlesCommand);
self->state = BurningState;
@@ -254,22 +254,20 @@ - (void)showProgressDialog:(NSString*) dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
if (alertController != nil) {
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
-
- int percentage = timeInMilliseconds*100/totalVideoDuration;
-
- if (state == CreatingState) {
- [alertController setMessage:[NSString stringWithFormat:@"Creating video %% %d \n\n", percentage]];
- } else if (state == BurningState) {
- [alertController setMessage:[NSString stringWithFormat:@"Burning subtitles %% %d \n\n", percentage]];
- }
+ int totalVideoDuration = 9000;
+
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
+
+ if (state == CreatingState) {
+ [alertController setMessage:[NSString stringWithFormat:@"Creating video %% %d \n\n", percentage]];
+ } else if (state == BurningState) {
+ [alertController setMessage:[NSString stringWithFormat:@"Burning subtitles %% %d \n\n", percentage]];
}
}
}
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS/VidStabViewController.m b/tvos/test-app-local-dependency/FFmpegKitTVOS/VidStabViewController.m
index 66a6ec3..13e79e0 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS/VidStabViewController.m
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS/VidStabViewController.m
@@ -123,7 +123,7 @@ - (IBAction)stabilizedVideo:(id)sender {
NSString* ffmpegCommand = [Video generateShakingVideoScript:image1:image2:image3:videoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
[FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session) {
NSLog(@"FFmpeg process exited with state %@ and rc %@.%@", [FFmpegKitConfig sessionStateToString:[session getState]], [session getReturnCode], notNull([session getFailStackTrace], @"\n"));
@@ -141,7 +141,7 @@ - (IBAction)stabilizedVideo:(id)sender {
[self showProgressDialog:@"Stabilizing video\n\n"];
});
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", analyzeVideoCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", analyzeVideoCommand);
[FFmpegKit executeAsync:analyzeVideoCommand withCompleteCallback:^(id secondSession) {
@@ -151,7 +151,7 @@ - (IBAction)stabilizedVideo:(id)sender {
NSString *stabilizeVideoCommand = [NSString stringWithFormat:@"-hide_banner -y -i %@ -vf vidstabtransform=smoothing=30:input=%@ %@", videoFile, shakeResultsFile, stabilizedVideoFile];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", stabilizeVideoCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", stabilizeVideoCommand);
[FFmpegKit executeAsync:stabilizeVideoCommand withCompleteCallback:^(id thirdSession) {
@@ -228,6 +228,7 @@ - (void)setActive {
[FFmpegKitConfig enableLogCallback:^(Log *log){
NSLog(@"%@", [log getMessage]);
}];
+ [FFmpegKitConfig enableStatisticsCallback:nil];
}
- (void)showProgressDialog:(NSString*) dialogMessage {
diff --git a/tvos/test-app-local-dependency/FFmpegKitTVOS/VideoViewController.m b/tvos/test-app-local-dependency/FFmpegKitTVOS/VideoViewController.m
index 31f43fb..9ba95aa 100644
--- a/tvos/test-app-local-dependency/FFmpegKitTVOS/VideoViewController.m
+++ b/tvos/test-app-local-dependency/FFmpegKitTVOS/VideoViewController.m
@@ -111,7 +111,7 @@ - (IBAction)encodeVideo:(id)sender {
NSString* ffmpegCommand = [Video generateVideoEncodeScriptWithCustomPixelFormat:image1:image2:image3:videoFile:[self getSelectedVideoCodec]:[self getPixelFormat]:[self getCustomOptions]];
- NSLog(@"FFmpeg process started with arguments\n'%@'.\n", ffmpegCommand);
+ NSLog(@"FFmpeg process started with arguments '%@'.\n", ffmpegCommand);
FFmpegSession* session = [FFmpegKit executeAsync:ffmpegCommand withCompleteCallback:^(FFmpegSession* session){
SessionState state = [session getState];
@@ -278,19 +278,17 @@ - (void)showProgressDialog:(NSString*) dialogMessage {
}
- (void)updateProgressDialog {
- if (statistics == nil) {
+ if (statistics == nil || [statistics getTime] < 0) {
return;
}
if (alertController != nil) {
int timeInMilliseconds = [statistics getTime];
- if (timeInMilliseconds > 0) {
- int totalVideoDuration = 9000;
+ int totalVideoDuration = 9000;
- int percentage = timeInMilliseconds*100/totalVideoDuration;
-
- [alertController setMessage:[NSString stringWithFormat:@"Encoding video %% %d \n\n", percentage]];
- }
+ int percentage = timeInMilliseconds*100/totalVideoDuration;
+
+ [alertController setMessage:[NSString stringWithFormat:@"Encoding video %% %d \n\n", percentage]];
}
}