├── .gitattributes
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ └── issue-template.md
└── workflows
│ └── stale.yml
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── LICENSE.GPLv3
├── README.md
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── arthenica
│ └── flutter
│ └── ffmpeg
│ ├── FlutterFFmpegExecuteFFmpegAsyncArgumentsTask.java
│ ├── FlutterFFmpegExecuteFFprobeAsyncArgumentsTask.java
│ ├── FlutterFFmpegGetMediaInformationAsyncTask.java
│ ├── FlutterFFmpegPlugin.java
│ └── FlutterFFmpegResultHandler.java
├── doc
└── assets
│ ├── flutter-ffmpeg-logo-v2-cropped.png
│ ├── flutter-ffmpeg-logo-v2.png
│ ├── flutter_test_app.gif
│ ├── tip_png_files.png
│ ├── tip_runner_deployment_target.png
│ └── tip_use_frameworks.png
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── app
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── arthenica
│ │ │ │ └── flutter
│ │ │ │ └── ffmpeg
│ │ │ │ └── flutterffmpegexample
│ │ │ │ └── MainActivity.java
│ │ │ └── res
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ └── values
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── assets
│ ├── colosseum.jpg
│ ├── doppioone_regular.ttf
│ ├── pyramid.jpg
│ ├── sil_open_font_license.txt
│ ├── subtitle.srt
│ ├── tajmahal.jpg
│ └── truenorg.otf
├── clean.sh
├── ios
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── Runner
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── main.m
├── lib
│ ├── abstract.dart
│ ├── audio_tab.dart
│ ├── command_tab.dart
│ ├── concurrent_execution_tab.dart
│ ├── decoration.dart
│ ├── flutter_ffmpeg_api_wrapper.dart
│ ├── https_tab.dart
│ ├── main.dart
│ ├── pipe_tab.dart
│ ├── player.dart
│ ├── popup.dart
│ ├── progress_modal.dart
│ ├── subtitle_tab.dart
│ ├── test_api.dart
│ ├── tooltip.dart
│ ├── util.dart
│ ├── vid_stab_tab.dart
│ ├── video_tab.dart
│ └── video_util.dart
├── pubspec.lock
└── pubspec.yaml
├── flutter_ffmpeg.iml
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── EmptyLogDelegate.h
│ ├── EmptyLogDelegate.m
│ ├── FlutterExecuteDelegate.h
│ ├── FlutterExecuteDelegate.m
│ ├── FlutterFFmpegPlugin.h
│ └── FlutterFFmpegPlugin.m
└── flutter_ffmpeg.podspec
├── lib
├── completed_ffmpeg_execution.dart
├── ffmpeg_execution.dart
├── flutter_ffmpeg.dart
├── log.dart
├── log_level.dart
├── media_information.dart
├── statistics.dart
└── stream_information.dart
└── pubspec.yaml
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
2 | android/src/main/java/* linguist-vendored
3 | example/android/app/src/main/java/* linguist-vendored
4 | ios/* linguist-vendored
5 | example/ios/* linguist-vendored
6 | packages/* linguist-vendored
7 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | patreon: tanersener
2 | custom: ['https://buymeacoff.ee/tanersener']
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue-template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Issue Template
3 | about: Create an issue to help us improve
4 |
5 | ---
6 |
7 | **Description**
8 | Description of what the issue is about.
9 |
10 | **Expected behavior**
11 | What you expected to happen.
12 |
13 | **Current behavior**
14 | What happened.
15 |
16 | **Screenshots**
17 | If applicable, add screenshots to help explain your problem.
18 |
19 | **Logs**
20 | Post logs here or paste them to [Ghostbin](https://ghostbin.co) and insert the link here.
21 |
22 | **Environment**
23 | Flutter doctor output
24 |
25 | **Other**
26 | Add any other context about the problem here.
27 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: Mark stale issues and pull requests
2 |
3 | on:
4 | schedule:
5 | - cron: "30 1 * * *"
6 |
7 | jobs:
8 | stale:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/stale@v1
14 | with:
15 | repo-token: ${{ secrets.GITHUB_TOKEN }}
16 | stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
17 | stale-pr-message: 'This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
18 | stale-issue-label: 'no-issue-activity'
19 | stale-pr-label: 'no-pr-activity'
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .DS_Store
3 | .dart_tool/
4 | .pub/
5 | build/
6 | /pubspec.lock
7 | /.packages
8 | /.gradle/
9 |
10 | .settings/
11 | .project
12 | .classpath
13 |
14 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.4.2
2 | - Support Android v1 and v2 embeddings
3 |
4 | ## 0.4.1
5 | - Migrated the plugin to Android v2 embedding
6 |
7 | ## 0.4.0
8 | - Migrated to nullsafety
9 | - Fixes issue #246 and #266
10 | - Adds closeFFmpegPipe method to close pipes
11 | - Updated example application
12 |
13 | ## 0.3.1
14 | - Adds mavenCentral() repository for Android
15 | - Minor updates in the test application
16 |
17 | ## 0.3.0
18 | - Uses thread pool executor to process Android executions
19 | - Adds listExecutions API method
20 | - Implements async FFmpeg execution methods
21 | - LogCallback and StatisticsCallback functions updated with executionId
22 | - Updates getMediaInformation implementation
23 | - Adds setEnvironmentVariable API method
24 | - Depends on mobile-ffmpeg v4.4
25 | - Allows modifying mobile-ffmpeg version for android
26 | - Includes an updated example application
27 | - Fixes issue #115, #120, #157, #159, #170, #178 and #202
28 |
29 | ## 0.2.10
30 | - Fixes issue #94
31 |
32 | ## 0.2.9
33 | - Implements FFprobe
34 | - Adds concurrent execution support
35 | - Re-organises plugin classes
36 | - iOS releases depend on iconv system library instead of iconv external library
37 |
38 | ## 0.2.8
39 | - Uses ffmpeg v4.3
40 | - Implements registerNewFFmpegPipe API method
41 |
42 | ## 0.2.7
43 | - Uses new package selection mechanism
44 | - Fixes issue #52
45 |
46 | ## 0.2.6
47 | - Adds support for single quotes and double quotes in command strings
48 |
49 | ## 0.2.5
50 | - Implements side data information parsing
51 |
52 | ## 0.2.4
53 | - Adds support for Android devices with API Level 16+
54 | - Fixes issues #21 and #36
55 | - Removes conflicting attributes from AndroidManifest.xml
56 | - Includes ProGuard configuration file inside
57 |
58 | ## 0.2.3
59 | - Fixed flutter v1.6 compatibility errors on packages
60 |
61 | ## 0.2.2
62 | - Fixed flutter v1.6 compatibility errors
63 |
64 | ## 0.2.1
65 | - Fixed documentation errors
66 | - Updated package description
67 |
68 | ## 0.2.0
69 | - Added AndroidX support
70 | - Removed app icons for Android
71 | - Fixes issues #13 and #14
72 |
73 | ## 0.1.1
74 | - LTS release instructions added
75 | - Documentation updated
76 |
77 | ## 0.1.0
78 | - First release
79 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | static String safePackageName(String prop) {
2 | prop.replace("-lts", "")
3 | }
4 |
5 | static String safePackageVersion(String prop, String version, String ltsVersion) {
6 | prop.contains("-lts") ? ltsVersion + ".LTS" : version
7 | }
8 |
9 | String safeExtGet(String prop, String fallback) {
10 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
11 | }
12 |
13 | group 'com.arthenica.flutter.ffmpeg'
14 | version '0.4.2'
15 |
16 | buildscript {
17 | repositories {
18 | google()
19 | jcenter()
20 | }
21 |
22 | dependencies {
23 | classpath 'com.android.tools.build:gradle:4.1.3'
24 | }
25 | }
26 |
27 | rootProject.allprojects {
28 | repositories {
29 | mavenCentral()
30 | google()
31 | jcenter()
32 | }
33 | }
34 |
35 | apply plugin: 'com.android.library'
36 |
37 | android {
38 | compileSdkVersion 29
39 |
40 | defaultConfig {
41 | minSdkVersion safeExtGet('flutterFFmpegPackage', 'https').contains("-lts") ? 16 : 24
42 | }
43 | lintOptions {
44 | disable 'InvalidPackage'
45 | }
46 | }
47 |
48 | dependencies {
49 | implementation 'com.arthenica:mobile-ffmpeg-' + safePackageName(safeExtGet('flutterFFmpegPackage', 'https')) + ':' + safePackageVersion(safeExtGet('flutterFFmpegPackage', 'https'), safeExtGet('mobileFFmpegVersion', '4.4'), safeExtGet('mobileFFmpegLTSVersion', '4.4'))
50 | }
51 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
3 | org.gradle.jvmargs=-Xmx1536M
4 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'flutter_ffmpeg'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteFFmpegAsyncArgumentsTask.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019-2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | package com.arthenica.flutter.ffmpeg;
21 |
22 | import android.os.AsyncTask;
23 | import android.util.Log;
24 |
25 | import com.arthenica.mobileffmpeg.FFmpeg;
26 |
27 | import java.util.Arrays;
28 | import java.util.List;
29 |
30 | import io.flutter.plugin.common.MethodChannel;
31 |
32 | /**
33 | * Asynchronous task which performs {@link FFmpeg#execute(String[])} method invocations.
34 | *
35 | * @author Taner Sener
36 | * @since 0.1.0
37 | */
38 | public class FlutterFFmpegExecuteFFmpegAsyncArgumentsTask extends AsyncTask {
39 |
40 | private final MethodChannel.Result result;
41 | private final List arguments;
42 | private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
43 |
44 | FlutterFFmpegExecuteFFmpegAsyncArgumentsTask(final List arguments, final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final MethodChannel.Result result) {
45 | this.arguments = arguments;
46 | this.result = result;
47 | this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
48 | }
49 |
50 | @Override
51 | protected Integer doInBackground(final String... unusedArgs) {
52 | final String[] argumentsArray = arguments.toArray(new String[0]);
53 |
54 | Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("Running FFmpeg with arguments: %s.", Arrays.toString(argumentsArray)));
55 |
56 | int rc = FFmpeg.execute(argumentsArray);
57 |
58 | Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("FFmpeg exited with rc: %d", rc));
59 |
60 | return rc;
61 | }
62 |
63 | @Override
64 | protected void onPostExecute(final Integer rc) {
65 | flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteFFprobeAsyncArgumentsTask.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019-2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | package com.arthenica.flutter.ffmpeg;
21 |
22 | import android.os.AsyncTask;
23 | import android.util.Log;
24 |
25 | import com.arthenica.mobileffmpeg.FFprobe;
26 |
27 | import java.util.Arrays;
28 | import java.util.List;
29 |
30 | import io.flutter.plugin.common.MethodChannel;
31 |
32 | /**
33 | * Asynchronous task which performs {@link FFprobe#execute(String[])} method invocations.
34 | *
35 | * @author Taner Sener
36 | * @since 0.2.9
37 | */
38 | public class FlutterFFmpegExecuteFFprobeAsyncArgumentsTask extends AsyncTask {
39 |
40 | private final MethodChannel.Result result;
41 | private final List arguments;
42 | private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
43 |
44 | FlutterFFmpegExecuteFFprobeAsyncArgumentsTask(final List arguments, final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final MethodChannel.Result result) {
45 | this.arguments = arguments;
46 | this.result = result;
47 | this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
48 | }
49 |
50 | @Override
51 | protected Integer doInBackground(final String... unusedArgs) {
52 | final String[] argumentsArray = arguments.toArray(new String[0]);
53 |
54 | Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("Running FFprobe with arguments: %s.", Arrays.toString(argumentsArray)));
55 |
56 | int rc = FFprobe.execute(argumentsArray);
57 |
58 | Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("FFprobe exited with rc: %d", rc));
59 |
60 | return rc;
61 | }
62 |
63 | @Override
64 | protected void onPostExecute(final Integer rc) {
65 | flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019-2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | package com.arthenica.flutter.ffmpeg;
21 |
22 | import android.os.AsyncTask;
23 | import android.util.Log;
24 |
25 | import com.arthenica.mobileffmpeg.FFprobe;
26 | import com.arthenica.mobileffmpeg.MediaInformation;
27 |
28 | import io.flutter.plugin.common.MethodChannel;
29 |
30 | /**
31 | * Asynchronous task which performs {@link FFprobe#getMediaInformation(String, Long)} method invocations.
32 | *
33 | * @author Taner Sener
34 | * @since 0.1.0
35 | */
36 | public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask {
37 |
38 | private final String path;
39 | private final MethodChannel.Result result;
40 | private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
41 |
42 | FlutterFFmpegGetMediaInformationAsyncTask(final String path, final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final MethodChannel.Result result) {
43 | this.path = path;
44 | this.result = result;
45 | this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
46 | }
47 |
48 | @Override
49 | protected MediaInformation doInBackground(final String... unusedArgs) {
50 | Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("Getting media information for %s.", path));
51 | return FFprobe.getMediaInformation(path);
52 | }
53 |
54 | @Override
55 | protected void onPostExecute(final MediaInformation mediaInformation) {
56 | flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019-2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | package com.arthenica.flutter.ffmpeg;
21 |
22 | import android.os.Handler;
23 | import android.os.Looper;
24 |
25 | import io.flutter.plugin.common.EventChannel;
26 | import io.flutter.plugin.common.MethodChannel;
27 |
28 | /**
29 | * Flutter FFmpeg Result Handler
30 | *
31 | * @author Taner Sener
32 | * @since 0.2.2
33 | */
34 | class FlutterFFmpegResultHandler {
35 | private final Handler handler;
36 |
37 | FlutterFFmpegResultHandler() {
38 | handler = new Handler(Looper.getMainLooper());
39 | }
40 |
41 | void notImplemented(final MethodChannel.Result result) {
42 | handler.post(new Runnable() {
43 |
44 | @Override
45 | public void run() {
46 | if (result != null) {
47 | result.notImplemented();
48 | }
49 | }
50 | });
51 | }
52 |
53 | void success(final MethodChannel.Result result, final Object object) {
54 | handler.post(new Runnable() {
55 |
56 | @Override
57 | public void run() {
58 | if (result != null) {
59 | result.success(object);
60 | }
61 | }
62 | });
63 | }
64 |
65 | void success(final EventChannel.EventSink eventSink, final Object object) {
66 | handler.post(new Runnable() {
67 |
68 | @Override
69 | public void run() {
70 | if (eventSink != null) {
71 | eventSink.success(object);
72 | }
73 | }
74 | });
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/doc/assets/flutter-ffmpeg-logo-v2-cropped.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/doc/assets/flutter-ffmpeg-logo-v2-cropped.png
--------------------------------------------------------------------------------
/doc/assets/flutter-ffmpeg-logo-v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/doc/assets/flutter-ffmpeg-logo-v2.png
--------------------------------------------------------------------------------
/doc/assets/flutter_test_app.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/doc/assets/flutter_test_app.gif
--------------------------------------------------------------------------------
/doc/assets/tip_png_files.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/doc/assets/tip_png_files.png
--------------------------------------------------------------------------------
/doc/assets/tip_runner_deployment_target.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/doc/assets/tip_runner_deployment_target.png
--------------------------------------------------------------------------------
/doc/assets/tip_use_frameworks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/doc/assets/tip_use_frameworks.png
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # Visual Studio Code related
19 | .vscode/
20 |
21 | # Flutter/Dart/Pub related
22 | **/doc/api/
23 | .dart_tool/
24 | .flutter-plugins
25 | .packages
26 | .pub-cache/
27 | .pub/
28 | build/
29 | flutter_*.png
30 | linked_*.ds
31 | unlinked.ds
32 | unlinked_spec.ds
33 |
34 | # Android related
35 | **/android/**/gradle-wrapper.jar
36 | **/android/.gradle
37 | **/android/captures/
38 | **/android/gradlew
39 | **/android/gradlew.bat
40 | **/android/local.properties
41 | **/android/**/GeneratedPluginRegistrant.java
42 |
43 | # iOS/XCode related
44 | **/ios/**/*.mode1v3
45 | **/ios/**/*.mode2v3
46 | **/ios/**/*.moved-aside
47 | **/ios/**/*.pbxuser
48 | **/ios/**/*.perspectivev3
49 | **/ios/**/*sync/
50 | **/ios/**/.sconsign.dblite
51 | **/ios/**/.tags*
52 | **/ios/**/.vagrant/
53 | **/ios/**/DerivedData/
54 | **/ios/**/Icon?
55 | **/ios/**/Pods/
56 | **/ios/**/.symlinks/
57 | **/ios/**/profile
58 | **/ios/**/xcuserdata
59 | **/ios/.generated/
60 | **/ios/Flutter/App.framework
61 | **/ios/Flutter/Flutter.framework
62 | **/ios/Flutter/Flutter.podspec
63 | **/ios/Flutter/Generated.xcconfig
64 | **/ios/Flutter/app.flx
65 | **/ios/Flutter/app.zip
66 | **/ios/Flutter/flutter_assets/
67 | **/ios/Flutter/flutter_export_environment.sh
68 | **/ios/ServiceDefinitions.json
69 | **/ios/Runner/GeneratedPluginRegistrant.*
70 | **/ios/Flutter/.last_build_id
71 |
72 | # Exceptions to above rules.
73 | !**/ios/**/default.mode1v3
74 | !**/ios/**/default.mode2v3
75 | !**/ios/**/default.pbxuser
76 | !**/ios/**/default.perspectivev3
77 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
78 | /.flutter-plugins-dependencies
79 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flutter_ffmpeg_example
2 |
3 | Demonstrates how to use the flutter_ffmpeg plugin.
4 |
5 | ## Getting Started
6 |
7 | 1. Execute synchronous FFmpeg commands.
8 |
9 | - Use execute() method with a single command line
10 | ```
11 | import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
12 |
13 | final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg();
14 |
15 | _flutterFFmpeg.execute("-i file1.mp4 -c:v mpeg4 file2.mp4").then((rc) => print("FFmpeg process exited with rc $rc"));
16 | ```
17 |
18 | - Use executeWithArguments() method with an array of arguments
19 |
20 | ```
21 | import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
22 |
23 | final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg();
24 |
25 | var arguments = ["-i", "file1.mp4", "-c:v", "mpeg4", "file2.mp4"];
26 | _flutterFFmpeg.executeWithArguments(arguments).then((rc) => print("FFmpeg process exited with rc $rc"));
27 | ```
28 | 2. Execute asynchronous FFmpeg commands.
29 |
30 | ```
31 | _flutterFFmpeg.executeAsync(ffmpegCommand, (int executionId, int returnCode) {
32 | print("FFmpeg process for executionId $executionId exited with rc $returnCode");
33 | }).then((executionId) => print("Async FFmpeg process started with executionId $executionId."));
34 | ```
35 |
36 | 3. Execute FFprobe commands.
37 |
38 | - Use execute() method with a single command line
39 | ```
40 | import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
41 |
42 | final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
43 |
44 | _flutterFFprobe.execute("-i file1.mp4").then((rc) => print("FFprobe process exited with rc $rc"));
45 | ```
46 |
47 | - Use executeWithArguments() method with an array of arguments
48 |
49 | ```
50 | import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
51 |
52 | final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
53 |
54 | var arguments = ["-i", "file1.mp4"];
55 | _flutterFFprobe.executeWithArguments(arguments).then((rc) => print("FFprobe process exited with rc $rc"));
56 | ```
57 |
58 | 4. Check execution output. Zero represents successful execution, 255 means user cancel and non-zero values represent failure.
59 | ```
60 |
61 | final FlutterFFmpegConfig _flutterFFmpegConfig = new FlutterFFmpegConfig();
62 |
63 | _flutterFFmpegConfig.getLastReturnCode().then((rc) => print("Last rc: $rc"));
64 |
65 | _flutterFFmpegConfig.getLastCommandOutput().then((output) => print("Last command output: $output"));
66 | ```
67 |
68 | 5. Stop ongoing FFmpeg operations. Note that these two functions do not wait for termination to complete and return immediately.
69 | - Stop all executions
70 | ```
71 | _flutterFFmpeg.cancel();
72 | ```
73 | - Stop a specific execution
74 | ```
75 | _flutterFFmpeg.cancelExecution(executionId);
76 | ```
77 |
78 | 6. Get media information for a file.
79 | - Print all fields
80 | ```
81 | final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
82 |
83 | _flutterFFprobe.getMediaInformation("").then((info) => print(info));
84 | ```
85 | - Print selected fields
86 | ```
87 | final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
88 |
89 | _flutterFFprobe.getMediaInformation("").then((info) {
90 | print("Media Information");
91 |
92 | print("Path: ${info.getMediaProperties()['filename']}");
93 | print("Format: ${info.getMediaProperties()['format_name']}");
94 | print("Duration: ${info.getMediaProperties()['duration']}");
95 | print("Start time: ${info.getMediaProperties()['start_time']}");
96 | print("Bitrate: ${info.getMediaProperties()['bit_rate']}");
97 | Map tags = info.getMediaProperties()['tags'];
98 | if (tags != null) {
99 | tags.forEach((key, value) {
100 | print("Tag: " + key + ":" + value + "\n");
101 | });
102 | }
103 |
104 | if (info.getStreams() != null) {
105 | List streams = info.getStreams();
106 |
107 | if (streams.length > 0) {
108 | for (var stream in streams) {
109 | print("Stream id: ${stream.getAllProperties()['index']}");
110 | print("Stream type: ${stream.getAllProperties()['codec_type']}");
111 | print("Stream codec: ${stream.getAllProperties()['codec_name']}");
112 | print("Stream full codec: ${stream.getAllProperties()['codec_long_name']}");
113 | print("Stream format: ${stream.getAllProperties()['pix_fmt']}");
114 | print("Stream width: ${stream.getAllProperties()['width']}");
115 | print("Stream height: ${stream.getAllProperties()['height']}");
116 | print("Stream bitrate: ${stream.getAllProperties()['bit_rate']}");
117 | print("Stream sample rate: ${stream.getAllProperties()['sample_rate']}");
118 | print("Stream sample format: ${stream.getAllProperties()['sample_fmt']}");
119 | print("Stream channel layout: ${stream.getAllProperties()['channel_layout']}");
120 | print("Stream sar: ${stream.getAllProperties()['sample_aspect_ratio']}");
121 | print("Stream dar: ${stream.getAllProperties()['display_aspect_ratio']}");
122 | print("Stream average frame rate: ${stream.getAllProperties()['avg_frame_rate']}");
123 | print("Stream real frame rate: ${stream.getAllProperties()['r_frame_rate']}");
124 | print("Stream time base: ${stream.getAllProperties()['time_base']}");
125 | print("Stream codec time base: ${stream.getAllProperties()['codec_time_base']}");
126 |
127 | Map tags = stream.getAllProperties()['tags'];
128 | if (tags != null) {
129 | tags.forEach((key, value) {
130 | print("Stream tag: " + key + ":" + value + "\n");
131 | });
132 | }
133 | }
134 | }
135 | }
136 | });
137 | ```
138 |
139 | 7. Enable log callback and redirect all `FFmpeg`/`FFprobe` logs to a console/file/widget.
140 | ```
141 | void logCallback(Log log) {
142 | print("${log.executionId}:${log.message}");
143 | }
144 | ...
145 | _flutterFFmpegConfig.enableLogCallback(this.logCallback);
146 | ```
147 |
148 | 8. Enable statistics callback and follow the progress of an ongoing `FFmpeg` operation.
149 | ```
150 | void statisticsCallback(Statistics statistics) {
151 | print("Statistics: executionId: ${statistics.executionId}, time: ${statistics.time}, size: ${statistics.size}, bitrate: ${statistics.bitrate}, speed: ${statistics.speed}, videoFrameNumber: ${statistics.videoFrameNumber}, videoQuality: ${statistics.videoQuality}, videoFps: ${statistics.videoFps}");
152 | }
153 | ...
154 | _flutterFFmpegConfig.enableStatisticsCallback(this.statisticsCallback);
155 | ```
156 |
157 | 9. Poll statistics without implementing statistics callback.
158 | ```
159 | _flutterFFmpegConfig.getLastReceivedStatistics().then((stats) => print(stats));
160 | ```
161 |
162 | 10. List ongoing executions.
163 | ```
164 | _flutterFFmpeg.listExecutions().then((ffmpegExecutions) {
165 | ffmpegExecutions.forEach((execution) {
166 | ffprint(
167 | "Execution id:${execution.executionId}, startTime:${execution.command}, command:${execution.startTime}.");
168 | });
169 | });
170 | ```
171 |
172 | 11. Set log level.
173 | ```
174 | _flutterFFmpegConfig.setLogLevel(LogLevel.AV_LOG_WARNING);
175 | ```
176 |
177 | 12. Register your own fonts by specifying a custom fonts directory, so they are available to use in `FFmpeg` filters. Please note that this function can not work on relative paths, you need to provide full file system path.
178 | ```
179 | _flutterFFmpegConfig.setFontDirectory("");
180 | ```
181 |
182 | 13. Use your own `fontconfig` configuration.
183 | ```
184 | _flutterFFmpegConfig.setFontconfigConfigurationPath("");
185 | ```
186 |
187 | 14. Disable log functionality of the library. Logs will not be printed to console and log callback will be disabled.
188 | ```
189 | _flutterFFmpegConfig.disableLogs();
190 | ```
191 |
192 | 15. Disable statistics functionality of the library. Statistics callback will be disabled but the last received statistics data will be still available.
193 | ```
194 | _flutterFFmpegConfig.disableStatistics();
195 | ```
196 |
197 | 16. Create new `FFmpeg` pipe.
198 | ```
199 | _flutterFFmpegConfig.registerNewFFmpegPipe().then((path) {
200 | then((stats) => print("New ffmpeg pipe at $path"));
201 | });
202 | ```
203 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion 29
29 | ndkVersion "21.4.7075529"
30 |
31 | lintOptions {
32 | disable 'InvalidPackage'
33 | }
34 |
35 | defaultConfig {
36 | applicationId "com.arthenica.flutter.ffmpeg.FlutterFFmpegExample"
37 | minSdkVersion 24
38 | targetSdkVersion 29
39 | versionCode flutterVersionCode.toInteger()
40 | versionName flutterVersionName
41 | }
42 |
43 | buildTypes {
44 | debug {
45 | minifyEnabled false
46 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
47 | }
48 | release {
49 | minifyEnabled true
50 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
51 | signingConfig signingConfigs.debug
52 | }
53 | }
54 | }
55 |
56 | flutter {
57 | source '../..'
58 | }
59 |
60 | dependencies {
61 | testImplementation 'junit:junit:4.12'
62 | }
63 |
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | #Flutter Wrapper
13 | -keep class io.flutter.app.** { *; }
14 | -keep class io.flutter.plugin.** { *; }
15 | -keep class io.flutter.util.** { *; }
16 | -keep class io.flutter.view.** { *; }
17 | -keep class io.flutter.** { *; }
18 | -keep class io.flutter.plugins.** { *; }
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
10 |
11 |
16 |
19 |
20 |
27 |
28 |
29 |
32 |
33 |
34 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/arthenica/flutter/ffmpeg/flutterffmpegexample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.arthenica.flutter.ffmpeg.flutterffmpegexample;
2 |
3 | import io.flutter.embedding.android.FlutterActivity;
4 |
5 | public class MainActivity extends FlutterActivity {
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:4.0.1'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | mavenCentral()
15 | google()
16 | jcenter()
17 | }
18 | }
19 |
20 | rootProject.buildDir = '../build'
21 | subprojects {
22 | project.buildDir = "${rootProject.buildDir}/${project.name}"
23 | }
24 | subprojects {
25 | project.evaluationDependsOn(':app')
26 | }
27 |
28 | ext.flutterFFmpegPackage = 'full'
29 |
30 | task clean(type: Delete) {
31 | delete rootProject.buildDir
32 | }
33 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableJetifier=true
3 | android.useAndroidX=true
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/example/assets/colosseum.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/assets/colosseum.jpg
--------------------------------------------------------------------------------
/example/assets/doppioone_regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/assets/doppioone_regular.ttf
--------------------------------------------------------------------------------
/example/assets/pyramid.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/assets/pyramid.jpg
--------------------------------------------------------------------------------
/example/assets/sil_open_font_license.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011-2012, Julieta Ulanovsky (julieta.ulanovsky@gmail.com), with Reserved Font Names 'Montserrat'
2 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
3 | This license is copied below, and is also available with a FAQ at:
4 | http://scripts.sil.org/OFL
5 |
6 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
7 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
8 |
9 | -----------------------------------------------------------
10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
11 | -----------------------------------------------------------
12 |
13 | PREAMBLE
14 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
15 |
16 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
17 |
18 | DEFINITIONS
19 | "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
20 |
21 | "Reserved Font Name" refers to any names specified as such after the copyright statement(s).
22 |
23 | "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
24 |
25 | "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
26 |
27 | "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
28 |
29 | PERMISSION & CONDITIONS
30 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
31 |
32 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
33 |
34 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
35 |
36 | 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
37 |
38 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
39 |
40 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
41 |
42 | TERMINATION
43 | This license becomes null and void if any of the above conditions are not met.
44 |
45 | DISCLAIMER
46 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
--------------------------------------------------------------------------------
/example/assets/subtitle.srt:
--------------------------------------------------------------------------------
1 | 1
2 | 00:00:00,021 --> 00:00:04,000
3 | Colosseum or Coliseum is an oval amphitheatre in the centre of the city of Rome, Italy.
4 |
5 | 2
6 | 00:00:04,001 --> 00:00:06,927
7 | The Great Pyramid of Giza is the oldest and largest of the three pyramids in the Giza pyramid complex bordering what is now El Giza, Egypt.
8 |
9 | 3
10 | 00:00:06,928 --> 00:00:09,000
11 | The Taj Mahal is an ivory-white marble mausoleum on the south bank of the Yamuna river in the Indian city of Agra.
12 |
--------------------------------------------------------------------------------
/example/assets/tajmahal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/assets/tajmahal.jpg
--------------------------------------------------------------------------------
/example/assets/truenorg.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/assets/truenorg.otf
--------------------------------------------------------------------------------
/example/clean.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | rm -rf build
4 | rm -rf ios/Pods
5 | rm -rf ios/Flutter/App.framework
6 | rm -rf ios/Flutter/Flutter.framework
7 | rm -rf ios/Flutter/flutter_assets
8 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | platform :ios, '9.3'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | def flutter_install_plugin_pods(application_path = nil, relative_symlink_dir, platform)
31 | # defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
32 | application_path ||= File.dirname(defined_in_file.realpath) if self.respond_to?(:defined_in_file)
33 | raise 'Could not find application path' unless application_path
34 |
35 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
36 | # referring to absolute paths on developers' machines.
37 |
38 | symlink_dir = File.expand_path(relative_symlink_dir, application_path)
39 | system('rm', '-rf', symlink_dir) # Avoid the complication of dependencies like FileUtils.
40 |
41 | symlink_plugins_dir = File.expand_path('plugins', symlink_dir)
42 | system('mkdir', '-p', symlink_plugins_dir)
43 |
44 | plugins_file = File.join(application_path, '..', '.flutter-plugins-dependencies')
45 | plugin_pods = flutter_parse_plugins_file(plugins_file, platform)
46 | plugin_pods.each do |plugin_hash|
47 | plugin_name = plugin_hash['name']
48 | plugin_path = plugin_hash['path']
49 | if (plugin_name && plugin_path)
50 | symlink = File.join(symlink_plugins_dir, plugin_name)
51 | File.symlink(plugin_path, symlink)
52 |
53 | if plugin_name == 'flutter_ffmpeg'
54 | pod 'flutter_ffmpeg/audio-lts', :path => File.join(relative_symlink_dir, 'plugins', plugin_name, platform)
55 | else
56 | pod plugin_name, :path => File.join(relative_symlink_dir, 'plugins', plugin_name, platform)
57 | end
58 | end
59 | end
60 | end
61 |
62 | target 'Runner' do
63 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
64 | end
65 |
66 | post_install do |installer|
67 | installer.pods_project.targets.each do |target|
68 | flutter_additional_ios_build_settings(target)
69 | end
70 | end
71 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - flutter_ffmpeg/audio-lts (0.3.1):
4 | - Flutter
5 | - mobile-ffmpeg-audio (= 4.4.LTS)
6 | - fluttertoast (0.0.2):
7 | - Flutter
8 | - Toast
9 | - mobile-ffmpeg-audio (4.4.LTS)
10 | - path_provider (0.0.1):
11 | - Flutter
12 | - Toast (4.0.0)
13 | - video_player (0.0.1):
14 | - Flutter
15 |
16 | DEPENDENCIES:
17 | - Flutter (from `Flutter`)
18 | - flutter_ffmpeg/audio-lts (from `.symlinks/plugins/flutter_ffmpeg/ios`)
19 | - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
20 | - path_provider (from `.symlinks/plugins/path_provider/ios`)
21 | - video_player (from `.symlinks/plugins/video_player/ios`)
22 |
23 | SPEC REPOS:
24 | trunk:
25 | - mobile-ffmpeg-audio
26 | - Toast
27 |
28 | EXTERNAL SOURCES:
29 | Flutter:
30 | :path: Flutter
31 | flutter_ffmpeg:
32 | :path: ".symlinks/plugins/flutter_ffmpeg/ios"
33 | fluttertoast:
34 | :path: ".symlinks/plugins/fluttertoast/ios"
35 | path_provider:
36 | :path: ".symlinks/plugins/path_provider/ios"
37 | video_player:
38 | :path: ".symlinks/plugins/video_player/ios"
39 |
40 | SPEC CHECKSUMS:
41 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
42 | flutter_ffmpeg: 582ed04161233e3955540e32e79fd30bcbec272d
43 | fluttertoast: 6122fa75143e992b1d3470f61000f591a798cc58
44 | mobile-ffmpeg-audio: 1e0a053f8a6de57114e50ff48b3a85ff1c60f902
45 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
46 | Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
47 | video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
48 |
49 | PODFILE CHECKSUM: b4aecb86ca5c092d82af4346073e49c3e007970b
50 |
51 | COCOAPODS: 1.10.1
52 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application
7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
8 | [GeneratedPluginRegistrant registerWithRegistry:self];
9 | // Override point for customization after application launch.
10 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
11 | }
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flutter_ffmpeg_example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | NSAppTransportSecurity
45 |
46 | NSAllowsArbitraryLoads
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/example/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char* argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/lib/abstract.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | class Refreshable {
21 | void refresh() {}
22 | }
23 |
24 | class DialogFactory {
25 | void dialogShow(String message) {}
26 |
27 | void dialogShowCancellable(String message, Function cancelFunction) {}
28 |
29 | void dialogUpdate(String message) {}
30 |
31 | void dialogHide() {}
32 | }
33 |
34 | class RefreshablePlayerDialogFactory implements Refreshable, DialogFactory {
35 | @override
36 | void dialogHide() {}
37 |
38 | @override
39 | void dialogUpdate(String message) {}
40 |
41 | @override
42 | void refresh() {}
43 |
44 | @override
45 | void dialogShow(String message) {}
46 |
47 | @override
48 | void dialogShowCancellable(String message, Function cancelFunction) {}
49 | }
50 |
--------------------------------------------------------------------------------
/example/lib/audio_tab.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'dart:async';
21 | import 'dart:io';
22 |
23 | import 'package:flutter/material.dart';
24 | import 'package:flutter_ffmpeg/completed_ffmpeg_execution.dart';
25 | import 'package:flutter_ffmpeg/log.dart';
26 | import 'package:flutter_ffmpeg_example/abstract.dart';
27 | import 'package:flutter_ffmpeg_example/flutter_ffmpeg_api_wrapper.dart';
28 | import 'package:flutter_ffmpeg_example/popup.dart';
29 | import 'package:flutter_ffmpeg_example/test_api.dart';
30 | import 'package:flutter_ffmpeg_example/tooltip.dart';
31 | import 'package:flutter_ffmpeg_example/video_util.dart';
32 |
33 | import 'util.dart';
34 |
35 | class AudioTab {
36 | late RefreshablePlayerDialogFactory _refreshablePlayerDialogFactory;
37 | late String _selectedCodec;
38 | String _outputText = "";
39 |
40 | void init(RefreshablePlayerDialogFactory refreshablePlayerDialogFactory) {
41 | _refreshablePlayerDialogFactory = refreshablePlayerDialogFactory;
42 | List> videoCodecList = getAudioCodecList();
43 | _selectedCodec = videoCodecList[0].value!;
44 | clearLog();
45 | }
46 |
47 | void setActive() {
48 | print("Audio Tab Activated");
49 | enableLogCallback(null);
50 | _createAudioSample();
51 | enableStatisticsCallback(null);
52 | showPopup(AUDIO_TEST_TOOLTIP_TEXT);
53 | }
54 |
55 | void logCallback(Log log) {
56 | appendLog(log.message);
57 | _refreshablePlayerDialogFactory.refresh();
58 | }
59 |
60 | void appendLog(String logMessage) {
61 | _outputText += logMessage;
62 | }
63 |
64 | void clearLog() {
65 | _outputText = "";
66 | }
67 |
68 | void changedAudioCodec(String? selectedCodec) {
69 | _selectedCodec = selectedCodec!;
70 | _refreshablePlayerDialogFactory.refresh();
71 | }
72 |
73 | void encodeAudio() {
74 | getAudioOutputFile().then((audioOutputFile) {
75 | try {
76 | audioOutputFile.delete().catchError((_) {});
77 | } on Exception catch (_) {}
78 |
79 | final String audioCodec = _selectedCodec;
80 |
81 | ffprint("Testing AUDIO encoding with '$audioCodec' codec");
82 |
83 | generateAudioEncodeScript().then((ffmpegCommand) {
84 | showProgressDialog();
85 |
86 | clearLog();
87 |
88 | executeAsyncFFmpeg(ffmpegCommand, (CompletedFFmpegExecution execution) {
89 | hideProgressDialog();
90 |
91 | if (execution.returnCode == 0) {
92 | showPopup("Encode completed successfully.");
93 | ffprint("Encode completed successfully.");
94 | } else {
95 | showPopup("Encode failed. Please check log for the details.");
96 | ffprint("Encode failed with rc=${execution.returnCode}.");
97 | }
98 |
99 | ffprint("Testing post execution commands.");
100 | Test.testPostExecutionCommands();
101 | }).then((executionId) {
102 | ffprint(
103 | "Async FFmpeg process started with arguments '$ffmpegCommand' and executionId $executionId.");
104 | });
105 | });
106 | });
107 | }
108 |
109 | void _createAudioSample() {
110 | getAudioSampleFile().then((audioSampleFile) {
111 | ffprint("Creating AUDIO sample before the test.");
112 |
113 | try {
114 | audioSampleFile.delete().catchError((_) {});
115 | } on Exception catch (_) {}
116 |
117 | String ffmpegCommand =
118 | "-hide_banner -y -f lavfi -i sine=frequency=1000:duration=5 -c:a pcm_s16le ${audioSampleFile.path}";
119 |
120 | ffprint("Creating audio sample with '$ffmpegCommand'.");
121 |
122 | executeFFmpeg(ffmpegCommand).then((result) {
123 | if (result == 0) {
124 | ffprint("AUDIO sample created");
125 | } else {
126 | ffprint("Creating AUDIO sample failed with rc=$result.");
127 | showPopup(
128 | "Creating AUDIO sample failed. Please check log for the details.");
129 | }
130 | enableLogCallback(logCallback);
131 | });
132 | });
133 | }
134 |
135 | Future getAudioOutputFile() async {
136 | String audioCodec = _selectedCodec;
137 |
138 | String extension;
139 | switch (audioCodec) {
140 | case "mp2 (twolame)":
141 | extension = "mpg";
142 | break;
143 | case "mp3 (liblame)":
144 | case "mp3 (libshine)":
145 | extension = "mp3";
146 | break;
147 | case "vorbis":
148 | extension = "ogg";
149 | break;
150 | case "opus":
151 | extension = "opus";
152 | break;
153 | case "amr-nb":
154 | extension = "amr";
155 | break;
156 | case "amr-wb":
157 | extension = "amr";
158 | break;
159 | case "ilbc":
160 | extension = "lbc";
161 | break;
162 | case "speex":
163 | extension = "spx";
164 | break;
165 | case "wavpack":
166 | extension = "wv";
167 | break;
168 | default:
169 | // soxr
170 | extension = "wav";
171 | break;
172 | }
173 |
174 | final String audio = "audio." + extension;
175 | Directory documentsDirectory = await VideoUtil.documentsDirectory;
176 | return new File("${documentsDirectory.path}/$audio");
177 | }
178 |
179 | Future getAudioSampleFile() async {
180 | Directory documentsDirectory = await VideoUtil.documentsDirectory;
181 | return new File("${documentsDirectory.path}/audio-sample.wav");
182 | }
183 |
184 | void showProgressDialog() {
185 | _refreshablePlayerDialogFactory.dialogShow("Encoding audio");
186 | }
187 |
188 | void hideProgressDialog() {
189 | _refreshablePlayerDialogFactory.dialogHide();
190 | }
191 |
192 | Future generateAudioEncodeScript() async {
193 | String audioCodec = _selectedCodec;
194 | String audioSampleFile = (await getAudioSampleFile()).path;
195 | String audioOutputFile = (await getAudioOutputFile()).path;
196 |
197 | switch (audioCodec) {
198 | case "mp2 (twolame)":
199 | return "-hide_banner -y -i $audioSampleFile -c:a mp2 -b:a 192k $audioOutputFile";
200 | case "mp3 (liblame)":
201 | return "-hide_banner -y -i $audioSampleFile -c:a libmp3lame -qscale:a 2 $audioOutputFile";
202 | case "mp3 (libshine)":
203 | return "-hide_banner -y -i $audioSampleFile -c:a libshine -qscale:a 2 $audioOutputFile";
204 | case "vorbis":
205 | return "-hide_banner -y -i $audioSampleFile -c:a libvorbis -b:a 64k $audioOutputFile";
206 | case "opus":
207 | return "-hide_banner -y -i $audioSampleFile -c:a libopus -b:a 64k -vbr on -compression_level 10 $audioOutputFile";
208 | case "amr-nb":
209 | return "-hide_banner -y -i $audioSampleFile -ar 8000 -ab 12.2k -c:a libopencore_amrnb $audioOutputFile";
210 | case "amr-wb":
211 | return "-hide_banner -y -i $audioSampleFile -ar 8000 -ab 12.2k -c:a libvo_amrwbenc -strict experimental $audioOutputFile";
212 | case "ilbc":
213 | return "-hide_banner -y -i $audioSampleFile -c:a ilbc -ar 8000 -b:a 15200 $audioOutputFile";
214 | case "speex":
215 | return "-hide_banner -y -i $audioSampleFile -c:a libspeex -ar 16000 $audioOutputFile";
216 | case "wavpack":
217 | return "-hide_banner -y -i $audioSampleFile -c:a wavpack -b:a 64k $audioOutputFile";
218 | default:
219 | // soxr
220 | return "-hide_banner -y -i $audioSampleFile -af aresample=resampler=soxr -ar 44100 $audioOutputFile";
221 | }
222 | }
223 |
224 | List> getAudioCodecList() {
225 | List> list = List.empty(growable: true);
226 |
227 | list.add(new DropdownMenuItem(
228 | value: "mp2 (twolame)",
229 | child: SizedBox(
230 | width: 100, child: Center(child: new Text("mp2 (twolame)")))));
231 | list.add(new DropdownMenuItem(
232 | value: "mp3 (liblame)",
233 | child: SizedBox(
234 | width: 100, child: Center(child: new Text("mp3 (liblame)")))));
235 | list.add(new DropdownMenuItem(
236 | value: "mp3 (libshine)",
237 | child: SizedBox(
238 | width: 100, child: Center(child: new Text("mp3 (libshine)")))));
239 | list.add(new DropdownMenuItem(
240 | value: "vorbis",
241 | child: SizedBox(width: 100, child: Center(child: new Text("vorbis")))));
242 | list.add(new DropdownMenuItem(
243 | value: "opus",
244 | child: SizedBox(width: 100, child: Center(child: new Text("opus")))));
245 | list.add(new DropdownMenuItem(
246 | value: "amr-nb",
247 | child: SizedBox(width: 100, child: Center(child: new Text("amr-nb")))));
248 | list.add(new DropdownMenuItem(
249 | value: "amr-wb",
250 | child: SizedBox(width: 100, child: Center(child: new Text("amr-wb")))));
251 | list.add(new DropdownMenuItem(
252 | value: "ilbc",
253 | child: SizedBox(width: 100, child: Center(child: new Text("ilbc")))));
254 | list.add(new DropdownMenuItem(
255 | value: "soxr",
256 | child: SizedBox(width: 100, child: Center(child: new Text("soxr")))));
257 | list.add(new DropdownMenuItem(
258 | value: "speex",
259 | child: SizedBox(width: 100, child: Center(child: new Text("speex")))));
260 | list.add(new DropdownMenuItem(
261 | value: "wavpack",
262 | child:
263 | SizedBox(width: 100, child: Center(child: new Text("wavpack")))));
264 |
265 | return list;
266 | }
267 |
268 | String getOutputText() => _outputText;
269 |
270 | String getSelectedCodec() => _selectedCodec;
271 | }
272 |
--------------------------------------------------------------------------------
/example/lib/command_tab.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'package:flutter/material.dart';
21 | import 'package:flutter_ffmpeg/log.dart';
22 | import 'package:flutter_ffmpeg_example/abstract.dart';
23 | import 'package:flutter_ffmpeg_example/popup.dart';
24 | import 'package:flutter_ffmpeg_example/tooltip.dart';
25 |
26 | import 'flutter_ffmpeg_api_wrapper.dart';
27 | import 'util.dart';
28 |
29 | class CommandTab {
30 | late Refreshable _refreshable;
31 | late TextEditingController _commandText;
32 | String _outputText = "";
33 |
34 | void init(Refreshable refreshable) {
35 | _refreshable = refreshable;
36 | _commandText = TextEditingController();
37 | clearLog();
38 |
39 | // COMMAND TAB IS SELECTED BY DEFAULT
40 | setActive();
41 | }
42 |
43 | void setActive() {
44 | print("Command Tab Activated");
45 | enableLogCallback(logCallback);
46 | enableStatisticsCallback(null);
47 | showPopup(COMMAND_TEST_TOOLTIP_TEXT);
48 | }
49 |
50 | void logCallback(Log log) {
51 | appendLog(log.message);
52 | _refreshable.refresh();
53 | }
54 |
55 | void appendLog(String logMessage) {
56 | _outputText += logMessage;
57 | }
58 |
59 | void clearLog() {
60 | _outputText = "";
61 | }
62 |
63 | void runFFmpeg() {
64 | clearLog();
65 |
66 | final String ffmpegCommand = _commandText.text;
67 |
68 | getLogLevel()
69 | .then((logLevel) => ffprint("Current log level is $logLevel."));
70 |
71 | ffprint("Testing FFmpeg COMMAND synchronously.");
72 |
73 | ffprint("FFmpeg process started with arguments\n\'$ffmpegCommand\'");
74 |
75 | executeFFmpeg(ffmpegCommand).then((result) {
76 | ffprint("FFmpeg process exited with rc $result.");
77 | if (result != 0) {
78 | showPopup("Command failed. Please check output for the details.");
79 | }
80 | });
81 | }
82 |
83 | void runFFprobe() {
84 | clearLog();
85 |
86 | final String ffprobeCommand = _commandText.text;
87 |
88 | ffprint("Testing FFprobe COMMAND synchronously.");
89 |
90 | ffprint("FFprobe process started with arguments\n\'$ffprobeCommand\'");
91 |
92 | executeFFprobe(ffprobeCommand).then((result) {
93 | ffprint("FFprobe process exited with rc $result.");
94 | if (result != 0) {
95 | showPopup("Command failed. Please check output for the details.");
96 | }
97 | });
98 | }
99 |
100 | String getOutputText() => _outputText;
101 |
102 | TextEditingController getCommandText() => _commandText;
103 |
104 | void dispose() {
105 | _commandText.dispose();
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/example/lib/concurrent_execution_tab.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'dart:io';
21 |
22 | import 'package:flutter_ffmpeg/completed_ffmpeg_execution.dart';
23 | import 'package:flutter_ffmpeg/log.dart';
24 | import 'package:flutter_ffmpeg_example/abstract.dart';
25 | import 'package:flutter_ffmpeg_example/flutter_ffmpeg_api_wrapper.dart';
26 | import 'package:flutter_ffmpeg_example/popup.dart';
27 | import 'package:flutter_ffmpeg_example/tooltip.dart';
28 | import 'package:flutter_ffmpeg_example/video_util.dart';
29 |
30 | import 'util.dart';
31 |
32 | class ConcurrentExecutionTab {
33 | late Refreshable _refreshable;
34 | String _outputText = "";
35 | late int _executionId1;
36 | late int _executionId2;
37 | late int _executionId3;
38 |
39 | void init(Refreshable refreshable) {
40 | _refreshable = refreshable;
41 | clearLog();
42 |
43 | _executionId1 = 0;
44 | _executionId2 = 0;
45 | _executionId3 = 0;
46 | }
47 |
48 | void setActive() {
49 | print("Concurrent Execution Tab Activated");
50 | enableLogCallback(logCallback);
51 | enableStatisticsCallback(null);
52 | showPopup(CONCURRENT_EXECUTION_TEST_TOOLTIP_TEXT);
53 | }
54 |
55 | void logCallback(Log log) {
56 | appendLog("${log.executionId}:${log.message}");
57 | _refreshable.refresh();
58 | }
59 |
60 | void appendLog(String logMessage) {
61 | _outputText += logMessage;
62 | }
63 |
64 | void clearLog() {
65 | _outputText = "";
66 | }
67 |
68 | void encodeVideo(int buttonNumber) {
69 | VideoUtil.assetPath(VideoUtil.ASSET_1).then((image1Path) {
70 | VideoUtil.assetPath(VideoUtil.ASSET_2).then((image2Path) {
71 | VideoUtil.assetPath(VideoUtil.ASSET_3).then((image3Path) {
72 | getVideoFile(buttonNumber).then((videoFile) {
73 | ffprint("Testing CONCURRENT EXECUTION for button $buttonNumber.");
74 |
75 | final ffmpegCommand = VideoUtil.generateEncodeVideoScript(
76 | image1Path,
77 | image2Path,
78 | image3Path,
79 | videoFile.path,
80 | "mpeg4",
81 | "");
82 |
83 | executeAsyncFFmpeg(ffmpegCommand,
84 | (CompletedFFmpegExecution execution) {
85 | if (execution.returnCode == 255) {
86 | ffprint(
87 | "FFmpeg process ended with cancel for button $buttonNumber with executionId ${execution.executionId}.");
88 | } else {
89 | ffprint(
90 | "FFmpeg process ended with rc ${execution.returnCode} for button $buttonNumber with executionId ${execution.executionId}.");
91 | }
92 | }).then((executionId) {
93 | ffprint(
94 | "Async FFmpeg process started for button $buttonNumber with arguments '$ffmpegCommand' and executionId $executionId.");
95 |
96 | switch (buttonNumber) {
97 | case 1:
98 | {
99 | _executionId1 = executionId;
100 | }
101 | break;
102 | case 2:
103 | {
104 | _executionId2 = executionId;
105 | }
106 | break;
107 | default:
108 | {
109 | _executionId3 = executionId;
110 | }
111 | }
112 |
113 | runListFFmpegExecutions();
114 | });
115 | });
116 | });
117 | });
118 | });
119 | }
120 |
121 | Future getVideoFile(int buttonNumber) async {
122 | final String video = "video$buttonNumber.mp4";
123 | Directory documentsDirectory = await VideoUtil.documentsDirectory;
124 | return new File("${documentsDirectory.path}/$video");
125 | }
126 |
127 | void runListFFmpegExecutions() {
128 | listFFmpegExecutions().then((ffmpegExecutions) {
129 | ffprint("Listing ongoing FFmpeg executions.");
130 | int i = 0;
131 | ffmpegExecutions.forEach((execution) {
132 | i++;
133 | ffprint(
134 | "Execution $i = id:${execution.executionId}, startTime:${execution.command}, command:${execution.startTime}.");
135 | });
136 | ffprint("Listed ongoing FFmpeg executions.");
137 | });
138 | }
139 |
140 | void runCancel(final int buttonNumber) {
141 | int executionId = 0;
142 |
143 | switch (buttonNumber) {
144 | case 1:
145 | {
146 | executionId = _executionId1;
147 | }
148 | break;
149 | case 2:
150 | {
151 | executionId = _executionId2;
152 | }
153 | break;
154 | case 3:
155 | {
156 | executionId = _executionId3;
157 | }
158 | }
159 |
160 | ffprint(
161 | "Cancelling FFmpeg process for button $buttonNumber with executionId $executionId.");
162 |
163 | if (executionId == 0) {
164 | cancel();
165 | } else {
166 | cancelExecution(executionId);
167 | }
168 | }
169 |
170 | String getOutputText() => _outputText;
171 | }
172 |
--------------------------------------------------------------------------------
/example/lib/decoration.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'package:flutter/material.dart';
21 |
22 | final appThemeData = ThemeData(
23 | primaryColor: Color(0xFFF46842),
24 | );
25 |
26 | final buttonDecoration = new BoxDecoration(
27 | color: Color.fromRGBO(46, 204, 113, 1.0),
28 | borderRadius: new BorderRadius.circular(5),
29 | border: Border.all(color: Color.fromRGBO(39, 174, 96, 1.0)));
30 |
31 | final videoPlayerFrameDecoration = BoxDecoration(
32 | color: Color.fromRGBO(236, 240, 241, 1.0),
33 | border: Border.all(color: Color.fromRGBO(185, 195, 199, 1.0), width: 1.0),
34 | );
35 |
36 | final dropdownButtonDecoration = BoxDecoration(
37 | color: Color.fromRGBO(155, 89, 182, 1.0),
38 | borderRadius: BorderRadius.circular(5),
39 | border: Border.all(color: Color.fromRGBO(142, 68, 173, 1.0)));
40 |
41 | final outputDecoration = new BoxDecoration(
42 | borderRadius: BorderRadius.all(new Radius.circular(5)),
43 | color: Color.fromRGBO(241, 196, 15, 1.0),
44 | border: Border.all(color: Color.fromRGBO(243, 156, 18, 1.0)));
45 |
46 | final buttonTextStyle = new TextStyle(
47 | fontSize: 14.0, fontWeight: FontWeight.bold, color: Colors.white);
48 |
49 | final buttonSmallTextStyle = new TextStyle(
50 | fontSize: 10.0, fontWeight: FontWeight.bold, color: Colors.white);
51 |
52 | final hintTextStyle = new TextStyle(fontSize: 14, color: Colors.grey[400]);
53 |
54 | final selectedTabColor = Color(0xFF1e90ff);
55 | final unSelectedTabColor = Color(0xFF808080);
56 |
57 | final tabBarDecoration = BoxDecoration(
58 | border: Border(
59 | top: BorderSide(
60 | color: unSelectedTabColor,
61 | width: 1.0,
62 | ),
63 | bottom: BorderSide(
64 | width: 0.0,
65 | ),
66 | ),
67 | );
68 |
69 | final textFieldStyle = new TextStyle(fontSize: 14, color: Colors.black);
70 |
71 | final dropdownButtonTextStyle = new TextStyle(
72 | fontSize: 14,
73 | color: Colors.black,
74 | );
75 |
76 | InputDecoration inputDecoration(String hintText) {
77 | return InputDecoration(
78 | border: const OutlineInputBorder(
79 | borderSide: const BorderSide(color: Color.fromRGBO(52, 152, 219, 1.0)),
80 | borderRadius: const BorderRadius.all(
81 | const Radius.circular(5),
82 | ),
83 | ),
84 | focusedBorder: const OutlineInputBorder(
85 | borderSide: const BorderSide(color: Color.fromRGBO(52, 152, 219, 1.0)),
86 | borderRadius: const BorderRadius.all(
87 | const Radius.circular(5),
88 | ),
89 | ),
90 | enabledBorder: const OutlineInputBorder(
91 | borderSide: const BorderSide(color: Color.fromRGBO(52, 152, 219, 1.0)),
92 | borderRadius: const BorderRadius.all(
93 | const Radius.circular(5),
94 | ),
95 | ),
96 | contentPadding: EdgeInsets.fromLTRB(8, 12, 8, 12),
97 | hintStyle: hintTextStyle,
98 | hintText: hintText);
99 | }
100 |
--------------------------------------------------------------------------------
/example/lib/flutter_ffmpeg_api_wrapper.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'dart:async';
21 |
22 | import 'package:flutter_ffmpeg/ffmpeg_execution.dart';
23 | import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
24 | import 'package:flutter_ffmpeg/media_information.dart';
25 | import 'package:flutter_ffmpeg/statistics.dart';
26 |
27 | final FlutterFFmpegConfig _flutterFFmpegConfig = new FlutterFFmpegConfig();
28 | final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg();
29 | final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
30 |
31 | void enableLogCallback(LogCallback? callback) {
32 | _flutterFFmpegConfig.enableLogCallback(callback);
33 | }
34 |
35 | void enableStatisticsCallback(StatisticsCallback? callback) {
36 | _flutterFFmpegConfig.enableStatisticsCallback(callback);
37 | }
38 |
39 | Future getFFmpegVersion() async {
40 | return await _flutterFFmpegConfig.getFFmpegVersion();
41 | }
42 |
43 | Future getPlatform() async {
44 | return await _flutterFFmpegConfig.getPlatform();
45 | }
46 |
47 | Future executeFFmpegWithArguments(List arguments) async {
48 | return await _flutterFFmpeg.executeWithArguments(arguments);
49 | }
50 |
51 | Future executeFFmpeg(String command) async {
52 | return await _flutterFFmpeg.execute(command);
53 | }
54 |
55 | Future executeAsyncFFmpeg(
56 | String command, ExecuteCallback executeCallback) async {
57 | return await _flutterFFmpeg.executeAsync(command, executeCallback);
58 | }
59 |
60 | Future executeFFprobeWithArguments(List arguments) async {
61 | return await _flutterFFprobe.executeWithArguments(arguments);
62 | }
63 |
64 | Future executeFFprobe(String command) async {
65 | return await _flutterFFprobe.execute(command);
66 | }
67 |
68 | Future cancel() async {
69 | return await _flutterFFmpeg.cancel();
70 | }
71 |
72 | Future cancelExecution(int executionId) async {
73 | return await _flutterFFmpeg.cancelExecution(executionId);
74 | }
75 |
76 | Future disableRedirection() async {
77 | return await _flutterFFmpegConfig.disableRedirection();
78 | }
79 |
80 | Future getLogLevel() async {
81 | return await _flutterFFmpegConfig.getLogLevel();
82 | }
83 |
84 | Future setLogLevel(int logLevel) async {
85 | return await _flutterFFmpegConfig.setLogLevel(logLevel);
86 | }
87 |
88 | Future enableLogs() async {
89 | return await _flutterFFmpegConfig.enableLogs();
90 | }
91 |
92 | Future disableLogs() async {
93 | return await _flutterFFmpegConfig.disableLogs();
94 | }
95 |
96 | Future enableStatistics() async {
97 | return await _flutterFFmpegConfig.enableStatistics();
98 | }
99 |
100 | Future disableStatistics() async {
101 | return await _flutterFFmpegConfig.disableStatistics();
102 | }
103 |
104 | Future getLastReceivedStatistics() async {
105 | return await _flutterFFmpegConfig.getLastReceivedStatistics();
106 | }
107 |
108 | Future resetStatistics() async {
109 | return await _flutterFFmpegConfig.resetStatistics();
110 | }
111 |
112 | Future setFontconfigConfigurationPath(String path) async {
113 | return await _flutterFFmpegConfig.setFontconfigConfigurationPath(path);
114 | }
115 |
116 | Future setFontDirectory(
117 | String fontDirectory, Map fontNameMap) async {
118 | return await _flutterFFmpegConfig.setFontDirectory(
119 | fontDirectory, fontNameMap);
120 | }
121 |
122 | Future getPackageName() async {
123 | return await _flutterFFmpegConfig.getPackageName();
124 | }
125 |
126 | Future> getExternalLibraries() async {
127 | return await _flutterFFmpegConfig.getExternalLibraries();
128 | }
129 |
130 | Future getLastReturnCode() async {
131 | return await _flutterFFmpegConfig.getLastReturnCode();
132 | }
133 |
134 | Future getLastCommandOutput() async {
135 | return await _flutterFFmpegConfig.getLastCommandOutput();
136 | }
137 |
138 | Future getMediaInformation(String path) async {
139 | return await _flutterFFprobe.getMediaInformation(path);
140 | }
141 |
142 | Future registerNewFFmpegPipe() async {
143 | return await _flutterFFmpegConfig.registerNewFFmpegPipe();
144 | }
145 |
146 | Future closeFFmpegPipe(String ffmpegPipePath) async {
147 | return await _flutterFFmpegConfig.closeFFmpegPipe(ffmpegPipePath);
148 | }
149 |
150 | Future setEnvironmentVariable(
151 | String variableName, String variableValue) async {
152 | return await _flutterFFmpegConfig.setEnvironmentVariable(
153 | variableName, variableValue);
154 | }
155 |
156 | Future> listFFmpegExecutions() async {
157 | return await _flutterFFmpeg.listExecutions();
158 | }
159 |
160 | List? parseArguments(command) {
161 | return FlutterFFmpeg.parseArguments(command);
162 | }
163 |
--------------------------------------------------------------------------------
/example/lib/https_tab.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'package:flutter/material.dart';
21 | import 'package:flutter_ffmpeg/log.dart';
22 | import 'package:flutter_ffmpeg/stream_information.dart';
23 | import 'package:flutter_ffmpeg_example/abstract.dart';
24 | import 'package:flutter_ffmpeg_example/popup.dart';
25 | import 'package:flutter_ffmpeg_example/tooltip.dart';
26 |
27 | import 'flutter_ffmpeg_api_wrapper.dart';
28 | import 'util.dart';
29 |
30 | class HttpsTab {
31 | static const String HTTPS_TEST_DEFAULT_URL =
32 | "https://download.blender.org/peach/trailer/trailer_1080p.ogg";
33 |
34 | late Refreshable _refreshable;
35 | late TextEditingController _urlText;
36 | String _outputText = "";
37 |
38 | void init(Refreshable refreshable) {
39 | _refreshable = refreshable;
40 | _urlText = TextEditingController();
41 | clearLog();
42 | }
43 |
44 | void setActive() {
45 | print("Https Tab Activated");
46 | enableLogCallback(logCallback);
47 | enableStatisticsCallback(null);
48 | showPopup(HTTPS_TEST_TOOLTIP_TEXT);
49 | }
50 |
51 | void logCallback(Log log) {
52 | appendLog(log.message);
53 | _refreshable.refresh();
54 | }
55 |
56 | void appendLog(String logMessage) {
57 | _outputText += logMessage;
58 | }
59 |
60 | void clearLog() {
61 | _outputText = "";
62 | }
63 |
64 | void runGetMediaInformation() {
65 | clearLog();
66 |
67 | String testUrl = _urlText.text;
68 | if (testUrl.isEmpty) {
69 | testUrl = HTTPS_TEST_DEFAULT_URL;
70 | _urlText.text = testUrl;
71 | ffprint(
72 | "Testing HTTPS with default url '$testUrl'.",
73 | );
74 | } else {
75 | ffprint("Testing HTTPS with url '$testUrl'.");
76 | }
77 |
78 | // HTTPS COMMAND ARGUMENTS
79 | getMediaInformation(testUrl).then((information) {
80 | if (information.getMediaProperties() != null) {
81 | ffprint("---");
82 | Map mediaProperties =
83 | information.getMediaProperties()!;
84 | if (mediaProperties.containsKey('filename')) {
85 | ffprint('Path: ${mediaProperties['filename']}');
86 | }
87 | if (mediaProperties.containsKey('format_name')) {
88 | ffprint("Format: " + mediaProperties['format_name']);
89 | }
90 | if (mediaProperties.containsKey('bit_rate')) {
91 | ffprint("Bitrate: " + mediaProperties['bit_rate']);
92 | }
93 | if (mediaProperties.containsKey('duration')) {
94 | ffprint("Duration: " + mediaProperties['duration']);
95 | }
96 | if (mediaProperties.containsKey('start_time')) {
97 | ffprint("Start time: " + mediaProperties['start_time']);
98 | }
99 | if (mediaProperties.containsKey('nb_streams')) {
100 | ffprint(
101 | "Number of streams: " + mediaProperties['nb_streams'].toString());
102 | }
103 | Map? tags = mediaProperties['tags'];
104 | if (tags != null) {
105 | tags.forEach((key, value) {
106 | ffprint("Tag: " + key + ":" + value);
107 | });
108 | }
109 |
110 | List? streams = information.getStreams();
111 | if (streams != null) {
112 | for (var i = 0; i < streams.length; ++i) {
113 | StreamInformation stream = streams[i];
114 | ffprint("---");
115 | Map streamProperties = stream.getAllProperties();
116 | if (streamProperties.containsKey('index')) {
117 | ffprint("Stream index: " + streamProperties['index'].toString());
118 | }
119 | if (streamProperties.containsKey('codec_type')) {
120 | ffprint("Stream type: " + streamProperties['codec_type']);
121 | }
122 | if (streamProperties.containsKey('codec_name')) {
123 | ffprint("Stream codec: " + streamProperties['codec_name']);
124 | }
125 | if (streamProperties.containsKey('codec_long_name')) {
126 | ffprint(
127 | "Stream full codec: " + streamProperties['codec_long_name']);
128 | }
129 | if (streamProperties.containsKey('pix_fmt')) {
130 | ffprint("Stream format: " + streamProperties['pix_fmt']);
131 | }
132 | if (streamProperties.containsKey('width')) {
133 | ffprint("Stream width: " + streamProperties['width'].toString());
134 | }
135 | if (streamProperties.containsKey('height')) {
136 | ffprint(
137 | "Stream height: " + streamProperties['height'].toString());
138 | }
139 | if (streamProperties.containsKey('bit_rate')) {
140 | ffprint("Stream bitrate: " + streamProperties['bit_rate']);
141 | }
142 | if (streamProperties.containsKey('sample_rate')) {
143 | ffprint("Stream sample rate: " + streamProperties['sample_rate']);
144 | }
145 | if (streamProperties.containsKey('sample_fmt')) {
146 | ffprint(
147 | "Stream sample format: " + streamProperties['sample_fmt']);
148 | }
149 | if (streamProperties.containsKey('channel_layout')) {
150 | ffprint("Stream channel layout: " +
151 | streamProperties['channel_layout']);
152 | }
153 | if (streamProperties.containsKey('sample_aspect_ratio')) {
154 | ffprint("Stream sample aspect ratio: " +
155 | streamProperties['sample_aspect_ratio']);
156 | }
157 | if (streamProperties.containsKey('display_aspect_ratio')) {
158 | ffprint("Stream display aspect ratio: " +
159 | streamProperties['display_aspect_ratio']);
160 | }
161 | if (streamProperties.containsKey('avg_frame_rate')) {
162 | ffprint("Stream average frame rate: " +
163 | streamProperties['avg_frame_rate']);
164 | }
165 | if (streamProperties.containsKey('r_frame_rate')) {
166 | ffprint("Stream real frame rate: " +
167 | streamProperties['r_frame_rate']);
168 | }
169 | if (streamProperties.containsKey('time_base')) {
170 | ffprint("Stream time base: " + streamProperties['time_base']);
171 | }
172 | if (streamProperties.containsKey('codec_time_base')) {
173 | ffprint("Stream codec time base: " +
174 | streamProperties['codec_time_base']);
175 | }
176 |
177 | Map? tags = streamProperties['tags'];
178 | if (tags != null) {
179 | tags.forEach((key, value) {
180 | ffprint("Stream tag: " + key + ":" + value);
181 | });
182 | }
183 | }
184 | }
185 | }
186 | });
187 | _refreshable.refresh();
188 | }
189 |
190 | String getOutputText() => _outputText;
191 |
192 | TextEditingController getUrlText() => _urlText;
193 |
194 | void dispose() {
195 | _urlText.dispose();
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/example/lib/pipe_tab.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'dart:io';
21 |
22 | import 'package:flutter_ffmpeg/completed_ffmpeg_execution.dart';
23 | import 'package:flutter_ffmpeg/log.dart';
24 | import 'package:flutter_ffmpeg/statistics.dart';
25 | import 'package:flutter_ffmpeg_example/abstract.dart';
26 | import 'package:flutter_ffmpeg_example/flutter_ffmpeg_api_wrapper.dart';
27 | import 'package:flutter_ffmpeg_example/player.dart';
28 | import 'package:flutter_ffmpeg_example/popup.dart';
29 | import 'package:flutter_ffmpeg_example/tooltip.dart';
30 | import 'package:flutter_ffmpeg_example/video_util.dart';
31 | import 'package:video_player/video_player.dart';
32 |
33 | import 'util.dart';
34 |
35 | class PipeTab implements PlayerTab {
36 | VideoPlayerController? _videoPlayerController;
37 | late RefreshablePlayerDialogFactory _refreshablePlayerDialogFactory;
38 | Statistics? _statistics;
39 |
40 | void init(RefreshablePlayerDialogFactory refreshablePlayerDialogFactory) {
41 | _refreshablePlayerDialogFactory = refreshablePlayerDialogFactory;
42 | _statistics = null;
43 | }
44 |
45 | void setActive() {
46 | print("Pipe Tab Activated");
47 | enableLogCallback(logCallback);
48 | enableStatisticsCallback(statisticsCallback);
49 | showPopup(PIPE_TEST_TOOLTIP_TEXT);
50 | }
51 |
52 | void logCallback(Log log) {
53 | ffprint(log.message);
54 | _refreshablePlayerDialogFactory.refresh();
55 | }
56 |
57 | void statisticsCallback(Statistics statistics) {
58 | this._statistics = statistics;
59 | updateProgressDialog();
60 | }
61 |
62 | void asyncAssetWriteToPipe(String assetName, String pipePath) {
63 | VideoUtil.writeAssetToPipeAndClose(assetName, pipePath);
64 | }
65 |
66 | void createVideo() {
67 | getVideoFile().then((videoFile) {
68 | registerNewFFmpegPipe().then((pipe1) {
69 | registerNewFFmpegPipe().then((pipe2) {
70 | registerNewFFmpegPipe().then((pipe3) {
71 | // IF VIDEO IS PLAYING STOP PLAYBACK
72 | pause();
73 |
74 | try {
75 | videoFile.delete().catchError((_) {});
76 | } on Exception catch (_) {}
77 |
78 | ffprint("Testing PIPE with 'mpeg4' codec");
79 |
80 | showProgressDialog();
81 |
82 | final ffmpegCommand = VideoUtil.generateCreateVideoWithPipesScript(
83 | pipe1, pipe2, pipe3, videoFile.path);
84 |
85 | executeAsyncFFmpeg(ffmpegCommand,
86 | (CompletedFFmpegExecution execution) {
87 | ffprint("FFmpeg process exited with rc ${execution.returnCode}.");
88 |
89 | hideProgressDialog();
90 |
91 | if (execution.returnCode == 0) {
92 | ffprint("Create completed successfully; playing video.");
93 | playVideo();
94 | } else {
95 | showPopup("Create failed. Please check log for the details.");
96 | ffprint("Create failed with rc=${execution.returnCode}.");
97 | }
98 | }).then((executionId) {
99 | ffprint(
100 | "Async FFmpeg process started with arguments '$ffmpegCommand' and executionId $executionId.");
101 | });
102 |
103 | asyncAssetWriteToPipe("pyramid.jpg", pipe1);
104 | asyncAssetWriteToPipe("colosseum.jpg", pipe2);
105 | asyncAssetWriteToPipe("tajmahal.jpg", pipe3);
106 | });
107 | });
108 | });
109 | });
110 | }
111 |
112 | Future playVideo() async {
113 | if (_videoPlayerController != null) {
114 | await _videoPlayerController!.initialize();
115 | await _videoPlayerController!.play();
116 | }
117 | _refreshablePlayerDialogFactory.refresh();
118 | }
119 |
120 | Future pause() async {
121 | if (_videoPlayerController != null) {
122 | await _videoPlayerController!.pause();
123 | }
124 | _refreshablePlayerDialogFactory.refresh();
125 | }
126 |
127 | Future getVideoFile() async {
128 | final String video = "video.mp4";
129 | Directory documentsDirectory = await VideoUtil.documentsDirectory;
130 | return new File("${documentsDirectory.path}/$video");
131 | }
132 |
133 | void showProgressDialog() {
134 | // CLEAN STATISTICS
135 | _statistics = null;
136 | resetStatistics();
137 | _refreshablePlayerDialogFactory.dialogShow("Creating video");
138 | }
139 |
140 | void updateProgressDialog() {
141 | if (_statistics == null) {
142 | return;
143 | }
144 |
145 | int timeInMilliseconds = this._statistics!.time;
146 | if (timeInMilliseconds > 0) {
147 | int totalVideoDuration = 9000;
148 |
149 | int completePercentage = (timeInMilliseconds * 100) ~/ totalVideoDuration;
150 |
151 | _refreshablePlayerDialogFactory
152 | .dialogUpdate("Creating video % $completePercentage");
153 | _refreshablePlayerDialogFactory.refresh();
154 | }
155 | }
156 |
157 | void hideProgressDialog() {
158 | _refreshablePlayerDialogFactory.dialogHide();
159 | }
160 |
161 | @override
162 | void setController(VideoPlayerController controller) {
163 | _videoPlayerController = controller;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/example/lib/player.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'dart:async';
21 | import 'dart:io';
22 |
23 | import 'package:flutter/cupertino.dart';
24 | import 'package:flutter/material.dart';
25 | import 'package:flutter_ffmpeg_example/decoration.dart';
26 | import 'package:video_player/video_player.dart';
27 |
28 | class PlayerTab {
29 | void setController(VideoPlayerController controller) {}
30 | }
31 |
32 | class EmbeddedPlayer extends StatefulWidget {
33 | final String _filePath;
34 | final PlayerTab _playerTab;
35 |
36 | EmbeddedPlayer(this._filePath, this._playerTab);
37 |
38 | @override
39 | _EmbeddedPlayerState createState() =>
40 | _EmbeddedPlayerState(new File(_filePath), _playerTab);
41 | }
42 |
43 | class _EmbeddedPlayerState extends State {
44 | final PlayerTab _playerTab;
45 | final File _file;
46 | VideoPlayerController? _videoPlayerController;
47 | bool startedPlaying = false;
48 |
49 | _EmbeddedPlayerState(this._file, this._playerTab);
50 |
51 | @override
52 | void initState() {
53 | super.initState();
54 |
55 | _videoPlayerController = VideoPlayerController.file(_file);
56 | _playerTab.setController(_videoPlayerController!);
57 | }
58 |
59 | @override
60 | void dispose() {
61 | _videoPlayerController!.dispose();
62 | super.dispose();
63 | }
64 |
65 | @override
66 | Widget build(BuildContext context) {
67 | return Material(
68 | elevation: 0,
69 | child: Center(
70 | child: FutureBuilder(
71 | future: Future.value(_videoPlayerController!.value.isInitialized),
72 | builder: (BuildContext context, AsyncSnapshot snapshot) {
73 | if (snapshot.data == true) {
74 | return Container(
75 | alignment: Alignment(0.0, 0.0),
76 | child: VideoPlayer(_videoPlayerController!),
77 | );
78 | } else {
79 | return Container(
80 | alignment: Alignment(0.0, 0.0),
81 | decoration: videoPlayerFrameDecoration,
82 | );
83 | }
84 | },
85 | ),
86 | ));
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/example/lib/popup.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'package:flutter/material.dart';
21 | import 'package:fluttertoast/fluttertoast.dart';
22 |
23 | void showPopup(String text) {
24 | Fluttertoast.showToast(
25 | msg: text,
26 | toastLength: Toast.LENGTH_SHORT,
27 | gravity: ToastGravity.CENTER,
28 | timeInSecForIosWeb: 2,
29 | backgroundColor: Colors.white70,
30 | textColor: Colors.black87,
31 | fontSize: 16.0);
32 | }
33 |
--------------------------------------------------------------------------------
/example/lib/progress_modal.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'package:flutter/material.dart';
21 |
22 | class ProgressModal {
23 | _Progress? _progress;
24 | late BuildContext _context;
25 | BuildContext? _cancelContext;
26 | late bool displayed;
27 |
28 | ProgressModal(BuildContext context) {
29 | _context = context;
30 | displayed = false;
31 | }
32 |
33 | void show(String message, {Function? cancelFunction}) {
34 | if (displayed) {
35 | return;
36 | }
37 | if (cancelFunction == null) {
38 | _progress = new _Progress(message);
39 | } else {
40 | _progress = new _Progress(message, cancelFunction: () {
41 | cancelFunction();
42 | hide();
43 | });
44 | }
45 |
46 | showDialog(
47 | context: _context,
48 | barrierDismissible: cancelFunction != null,
49 | builder: (BuildContext context) {
50 | _cancelContext = context;
51 | return new WillPopScope(
52 | onWillPop: () async => cancelFunction != null,
53 | child: Dialog(
54 | backgroundColor: Colors.white,
55 | insetAnimationCurve: Curves.easeInOut,
56 | insetAnimationDuration: Duration(milliseconds: 100),
57 | elevation: 10.0,
58 | shape: RoundedRectangleBorder(
59 | borderRadius: BorderRadius.all(Radius.circular(10.0))),
60 | child: _progress));
61 | });
62 |
63 | displayed = true;
64 | }
65 |
66 | void update({String? message}) {
67 | if (displayed) {
68 | _progress!.update(message: message);
69 | }
70 | }
71 |
72 | void hide() {
73 | if (displayed) {
74 | Navigator.of(_cancelContext!).pop();
75 | displayed = false;
76 | }
77 | }
78 | }
79 |
80 | class _Progress extends StatefulWidget {
81 | final _ProgressState _progressState;
82 |
83 | _Progress(String message, {Function? cancelFunction})
84 | : _progressState =
85 | _ProgressState(message, cancelFunction: cancelFunction);
86 |
87 | update({String? message}) {
88 | _progressState.update(message: message);
89 | }
90 |
91 | @override
92 | State createState() {
93 | return _progressState;
94 | }
95 | }
96 |
97 | class _ProgressState extends State<_Progress> {
98 | String _message;
99 | Function? _cancelFunction;
100 |
101 | _ProgressState(this._message, {Function? cancelFunction})
102 | : _cancelFunction = cancelFunction;
103 |
104 | update({String? message}) {
105 | if (message != null) {
106 | _message = message;
107 | }
108 | setState(() {});
109 | }
110 |
111 | @override
112 | void dispose() {
113 | super.dispose();
114 | }
115 |
116 | @override
117 | Widget build(BuildContext context) {
118 | final text = Expanded(
119 | child: Padding(
120 | padding: const EdgeInsets.fromLTRB(8.0, 8.0, 0, 8.0),
121 | child: Column(
122 | mainAxisSize: MainAxisSize.min,
123 | children: [
124 | Row(
125 | children: [
126 | Expanded(
127 | child: Text(
128 | _message,
129 | style: TextStyle(
130 | color: Colors.black,
131 | fontSize: 16.0,
132 | fontWeight: FontWeight.w600),
133 | textDirection: TextDirection.ltr,
134 | )),
135 | ],
136 | ),
137 | ],
138 | ),
139 | ),
140 | );
141 |
142 | var cancelRow = Row(mainAxisSize: MainAxisSize.min, children: [
143 | Container(
144 | padding: const EdgeInsets.only(top: 10),
145 | child: new InkWell(
146 | onTap: () => _cancelFunction!(),
147 | child: new Container(
148 | width: 100,
149 | height: 38,
150 | decoration: new BoxDecoration(
151 | color: Colors.grey[400],
152 | borderRadius: new BorderRadius.circular(5),
153 | ),
154 | child: new Center(
155 | child: new Text(
156 | 'CANCEL',
157 | style: new TextStyle(
158 | fontSize: 14.0,
159 | fontWeight: FontWeight.bold,
160 | color: Colors.white),
161 | ),
162 | ),
163 | ),
164 | ),
165 | )
166 | ]);
167 |
168 | var widgets = _cancelFunction == null
169 | ? [
170 | Row(
171 | mainAxisSize: MainAxisSize.min,
172 | children: [
173 | const SizedBox(width: 10.0),
174 | SizedBox(
175 | width: 40.0,
176 | height: 40.0,
177 | child: CircularProgressIndicator(),
178 | ),
179 | const SizedBox(width: 20.0),
180 | text,
181 | const SizedBox(width: 20.0)
182 | ],
183 | )
184 | ]
185 | : [
186 | Row(
187 | mainAxisSize: MainAxisSize.min,
188 | children: [
189 | const SizedBox(width: 10.0),
190 | SizedBox(
191 | width: 40.0,
192 | height: 40.0,
193 | child: CircularProgressIndicator(),
194 | ),
195 | const SizedBox(width: 20.0),
196 | text,
197 | const SizedBox(width: 20.0)
198 | ],
199 | ),
200 | cancelRow
201 | ];
202 |
203 | return Container(
204 | padding: const EdgeInsets.all(20.0),
205 | child: Column(
206 | mainAxisSize: MainAxisSize.min, children: widgets) // row body
207 | );
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/example/lib/subtitle_tab.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'dart:io';
21 |
22 | import 'package:flutter_ffmpeg/completed_ffmpeg_execution.dart';
23 | import 'package:flutter_ffmpeg/log.dart';
24 | import 'package:flutter_ffmpeg/statistics.dart';
25 | import 'package:flutter_ffmpeg_example/abstract.dart';
26 | import 'package:flutter_ffmpeg_example/flutter_ffmpeg_api_wrapper.dart';
27 | import 'package:flutter_ffmpeg_example/player.dart';
28 | import 'package:flutter_ffmpeg_example/popup.dart';
29 | import 'package:flutter_ffmpeg_example/tooltip.dart';
30 | import 'package:flutter_ffmpeg_example/video_util.dart';
31 | import 'package:video_player/video_player.dart';
32 |
33 | import 'util.dart';
34 |
35 | enum _State { IDLE, CREATING, BURNING }
36 |
37 | class SubtitleTab implements PlayerTab {
38 | VideoPlayerController? _videoPlayerController;
39 | late RefreshablePlayerDialogFactory _refreshablePlayerDialogFactory;
40 | late Statistics? _statistics;
41 | late _State _state;
42 | late int _executionId;
43 |
44 | void init(RefreshablePlayerDialogFactory refreshablePlayerDialogFactory) {
45 | _refreshablePlayerDialogFactory = refreshablePlayerDialogFactory;
46 | _statistics = null;
47 | _state = _State.IDLE;
48 | _executionId = 0;
49 | }
50 |
51 | void setActive() {
52 | print("Subtitle Tab Activated");
53 | enableLogCallback(logCallback);
54 | enableStatisticsCallback(statisticsCallback);
55 | showPopup(SUBTITLE_TEST_TOOLTIP_TEXT);
56 | }
57 |
58 | void logCallback(Log log) {
59 | ffprint(log.message);
60 | _refreshablePlayerDialogFactory.refresh();
61 | }
62 |
63 | void statisticsCallback(Statistics statistics) {
64 | this._statistics = statistics;
65 | updateProgressDialog();
66 | }
67 |
68 | void burnSubtitles() {
69 | VideoUtil.assetPath(VideoUtil.ASSET_1).then((image1Path) {
70 | VideoUtil.assetPath(VideoUtil.ASSET_2).then((image2Path) {
71 | VideoUtil.assetPath(VideoUtil.ASSET_3).then((image3Path) {
72 | VideoUtil.assetPath(VideoUtil.SUBTITLE_ASSET).then((subtitlePath) {
73 | getVideoFile().then((videoFile) {
74 | getVideoWithSubtitlesFile().then((videoWithSubtitlesFile) {
75 | // IF VIDEO IS PLAYING STOP PLAYBACK
76 | pause();
77 |
78 | try {
79 | videoFile.delete().catchError((_) {});
80 | } on Exception catch (_) {}
81 |
82 | try {
83 | videoWithSubtitlesFile.delete().catchError((_) {});
84 | } on Exception catch (_) {}
85 |
86 | ffprint("Testing SUBTITLE burning");
87 |
88 | showCreateProgressDialog();
89 |
90 | final ffmpegCommand = VideoUtil.generateEncodeVideoScript(
91 | image1Path,
92 | image2Path,
93 | image3Path,
94 | videoFile.path,
95 | "mpeg4",
96 | "");
97 |
98 | _state = _State.CREATING;
99 |
100 | executeAsyncFFmpeg(ffmpegCommand,
101 | (CompletedFFmpegExecution execution) {
102 | ffprint(
103 | "FFmpeg process exited with rc ${execution.returnCode}.");
104 |
105 | hideProgressDialog();
106 |
107 | if (execution.returnCode == 0) {
108 | ffprint(
109 | "Create completed successfully; burning subtitles.");
110 |
111 | String burnSubtitlesCommand =
112 | "-y -i ${videoFile.path} -vf subtitles=$subtitlePath:force_style='Fontname=Trueno' -c:v mpeg4 ${videoWithSubtitlesFile.path}";
113 |
114 | showBurnProgressDialog();
115 |
116 | ffprint(
117 | "FFmpeg process started with arguments\n\'$burnSubtitlesCommand\'.");
118 |
119 | _state = _State.BURNING;
120 |
121 | executeAsyncFFmpeg(burnSubtitlesCommand,
122 | (CompletedFFmpegExecution secondExecution) {
123 | ffprint(
124 | "FFmpeg process exited with rc ${secondExecution.returnCode}.");
125 | hideProgressDialog();
126 |
127 | if (secondExecution.returnCode == 0) {
128 | ffprint(
129 | "Burn subtitles completed successfully; playing video.");
130 | playVideo();
131 | } else if (secondExecution.returnCode == 255) {
132 | showPopup("Burn subtitles operation cancelled.");
133 | ffprint("Burn subtitles operation cancelled");
134 | } else {
135 | showPopup(
136 | "Burn subtitles failed. Please check log for the details.");
137 | ffprint(
138 | "Burn subtitles failed with rc=${secondExecution.returnCode}.");
139 | }
140 | }).then((executionId) {
141 | _executionId = executionId;
142 | ffprint(
143 | "Async FFmpeg process started with arguments '$burnSubtitlesCommand' and executionId $executionId.");
144 | });
145 | }
146 | }).then((executionId) {
147 | _executionId = executionId;
148 | ffprint(
149 | "Async FFmpeg process started with arguments '$ffmpegCommand' and executionId $executionId.");
150 | });
151 | });
152 | });
153 | });
154 | });
155 | });
156 | });
157 | }
158 |
159 | Future playVideo() async {
160 | if (_videoPlayerController != null) {
161 | await _videoPlayerController!.initialize();
162 | await _videoPlayerController!.play();
163 | }
164 | _refreshablePlayerDialogFactory.refresh();
165 | }
166 |
167 | Future pause() async {
168 | if (_videoPlayerController != null) {
169 | await _videoPlayerController!.pause();
170 | }
171 | _refreshablePlayerDialogFactory.refresh();
172 | }
173 |
174 | Future getVideoFile() async {
175 | final String video = "video.mp4";
176 | Directory documentsDirectory = await VideoUtil.documentsDirectory;
177 | return new File("${documentsDirectory.path}/$video");
178 | }
179 |
180 | Future getVideoWithSubtitlesFile() async {
181 | final String video = "video-with-subtitles.mp4";
182 | Directory documentsDirectory = await VideoUtil.documentsDirectory;
183 | return new File("${documentsDirectory.path}/$video");
184 | }
185 |
186 | void showCreateProgressDialog() {
187 | // CLEAN STATISTICS
188 | _statistics = null;
189 | resetStatistics();
190 | _refreshablePlayerDialogFactory.dialogShowCancellable(
191 | "Creating video", () => cancelExecution(_executionId));
192 | }
193 |
194 | void showBurnProgressDialog() {
195 | // CLEAN STATISTICS
196 | _statistics = null;
197 | resetStatistics();
198 | _refreshablePlayerDialogFactory.dialogShowCancellable(
199 | "Burning subtitles", () => cancelExecution(_executionId));
200 | }
201 |
202 | void updateProgressDialog() {
203 | if (_statistics == null) {
204 | return;
205 | }
206 |
207 | int timeInMilliseconds = this._statistics!.time;
208 | if (timeInMilliseconds > 0) {
209 | int totalVideoDuration = 9000;
210 |
211 | int completePercentage = (timeInMilliseconds * 100) ~/ totalVideoDuration;
212 |
213 | if (_state == _State.CREATING) {
214 | _refreshablePlayerDialogFactory
215 | .dialogUpdate("Creating video % $completePercentage");
216 | } else if (_state == _State.BURNING) {
217 | _refreshablePlayerDialogFactory
218 | .dialogUpdate("Burning subtitles % $completePercentage");
219 | }
220 | _refreshablePlayerDialogFactory.refresh();
221 | }
222 | }
223 |
224 | void hideProgressDialog() {
225 | _refreshablePlayerDialogFactory.dialogHide();
226 | }
227 |
228 | @override
229 | void setController(VideoPlayerController controller) {
230 | _videoPlayerController = controller;
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/example/lib/test_api.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'package:flutter_ffmpeg/log_level.dart';
21 |
22 | import 'flutter_ffmpeg_api_wrapper.dart';
23 | import 'util.dart';
24 |
25 | class Test {
26 | static void testCommonApiMethods() {
27 | ffprint("Testing common api methods.");
28 |
29 | getFFmpegVersion().then((version) => ffprint("FFmpeg version: $version"));
30 | getPlatform().then((platform) => ffprint("Platform: $platform"));
31 | getLogLevel().then(
32 | (level) => ffprint("Old log level: " + LogLevel.levelToString(level)));
33 | setLogLevel(LogLevel.AV_LOG_INFO);
34 | getLogLevel().then(
35 | (level) => ffprint("New log level: " + LogLevel.levelToString(level)));
36 | getPackageName()
37 | .then((packageName) => ffprint("Package name: $packageName"));
38 | getExternalLibraries().then((packageList) {
39 | packageList.forEach((value) => ffprint("External library: $value"));
40 | });
41 | }
42 |
43 | static void testParseArguments() {
44 | ffprint("Testing parseArguments.");
45 |
46 | _testParseSimpleCommand();
47 | _testParseSingleQuotesInCommand();
48 | _testParseDoubleQuotesInCommand();
49 | _testParseDoubleQuotesAndEscapesInCommand();
50 | }
51 |
52 | static void testPostExecutionCommands() {
53 | getLastCommandOutput()
54 | .then((output) => ffprint("Last command output: $output"));
55 | getLastReturnCode()
56 | .then((returnCode) => ffprint("Last return code: $returnCode"));
57 | getLastReceivedStatistics().then((statistics) => ffprint(
58 | "Last received statistics: executionId: ${statistics.executionId}, "
59 | "video frame number: ${statistics.videoFrameNumber}, video fps: ${statistics.videoFps}, "
60 | "video quality: ${statistics.videoQuality}, size: ${statistics.size}, time: ${statistics.time}, "
61 | "bitrate: ${statistics.bitrate}, speed: ${statistics.speed}"));
62 | }
63 |
64 | static void _testParseSimpleCommand() {
65 | var argumentArray = parseArguments(
66 | "-hide_banner -loop 1 -i file.jpg -filter_complex [0:v]setpts=PTS-STARTPTS[video] -map [video] -vsync 2 -async 1 video.mp4");
67 |
68 | assert(argumentArray != null);
69 | assert(argumentArray!.length == 14);
70 |
71 | assert("-hide_banner" == argumentArray![0]);
72 | assert("-loop" == argumentArray![1]);
73 | assert("1" == argumentArray![2]);
74 | assert("-i" == argumentArray![3]);
75 | assert("file.jpg" == argumentArray![4]);
76 | assert("-filter_complex" == argumentArray![5]);
77 | assert("[0:v]setpts=PTS-STARTPTS[video]" == argumentArray![6]);
78 | assert("-map" == argumentArray![7]);
79 | assert("[video]" == argumentArray![8]);
80 | assert("-vsync" == argumentArray![9]);
81 | assert("2" == argumentArray![10]);
82 | assert("-async" == argumentArray![11]);
83 | assert("1" == argumentArray![12]);
84 | assert("video.mp4" == argumentArray![13]);
85 | }
86 |
87 | static void _testParseSingleQuotesInCommand() {
88 | var argumentArray = parseArguments(
89 | "-loop 1 'file one.jpg' -filter_complex '[0:v]setpts=PTS-STARTPTS[video]' -map [video] video.mp4 ");
90 |
91 | assert(argumentArray != null);
92 | assert(argumentArray!.length == 8);
93 |
94 | assert("-loop" == argumentArray![0]);
95 | assert("1" == argumentArray![1]);
96 | assert("file one.jpg" == argumentArray![2]);
97 | assert("-filter_complex" == argumentArray![3]);
98 | assert("[0:v]setpts=PTS-STARTPTS[video]" == argumentArray![4]);
99 | assert("-map" == argumentArray![5]);
100 | assert("[video]" == argumentArray![6]);
101 | assert("video.mp4" == argumentArray![7]);
102 | }
103 |
104 | static void _testParseDoubleQuotesInCommand() {
105 | var argumentArray = parseArguments(
106 | "-loop 1 \"file one.jpg\" -filter_complex \"[0:v]setpts=PTS-STARTPTS[video]\" -map [video] video.mp4 ");
107 |
108 | assert(argumentArray != null);
109 | assert(argumentArray!.length == 8);
110 |
111 | assert("-loop" == argumentArray![0]);
112 | assert("1" == argumentArray![1]);
113 | assert("file one.jpg" == argumentArray![2]);
114 | assert("-filter_complex" == argumentArray![3]);
115 | assert("[0:v]setpts=PTS-STARTPTS[video]" == argumentArray![4]);
116 | assert("-map" == argumentArray![5]);
117 | assert("[video]" == argumentArray![6]);
118 | assert("video.mp4" == argumentArray![7]);
119 |
120 | argumentArray = parseArguments(
121 | " -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");
122 |
123 | assert(argumentArray != null);
124 | assert(argumentArray!.length == 13);
125 |
126 | assert("-i" == argumentArray![0]);
127 | assert("file:///tmp/input.mp4" == argumentArray![1]);
128 | assert("-vcodec" == argumentArray![2]);
129 | assert("libx264" == argumentArray![3]);
130 | assert("-vf" == argumentArray![4]);
131 | assert("scale=1024:1024,pad=width=1024:height=1024:x=0:y=0:color=black" ==
132 | argumentArray![5]);
133 | assert("-acodec" == argumentArray![6]);
134 | assert("copy" == argumentArray![7]);
135 | assert("-q:v" == argumentArray![8]);
136 | assert("0" == argumentArray![9]);
137 | assert("-q:a" == argumentArray![10]);
138 | assert("0" == argumentArray![11]);
139 | assert("video.mp4" == argumentArray![12]);
140 | }
141 |
142 | static void _testParseDoubleQuotesAndEscapesInCommand() {
143 | var argumentArray = parseArguments(
144 | " -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");
145 |
146 | assert(argumentArray != null);
147 | assert(argumentArray!.length == 13);
148 |
149 | assert("-i" == argumentArray![0]);
150 | assert("file:///tmp/input.mp4" == argumentArray![1]);
151 | assert("-vf" == argumentArray![2]);
152 | assert(
153 | "subtitles=file:///tmp/subtitles.srt:force_style='FontSize=16,PrimaryColour=&HFFFFFF&'" ==
154 | argumentArray![3]);
155 | assert("-vcodec" == argumentArray![4]);
156 | assert("libx264" == argumentArray![5]);
157 | assert("-acodec" == argumentArray![6]);
158 | assert("copy" == argumentArray![7]);
159 | assert("-q:v" == argumentArray![8]);
160 | assert("0" == argumentArray![9]);
161 | assert("-q:a" == argumentArray![10]);
162 | assert("0" == argumentArray![11]);
163 | assert("video.mp4" == argumentArray![12]);
164 |
165 | argumentArray = parseArguments(
166 | " -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");
167 |
168 | assert(argumentArray != null);
169 | assert(argumentArray!.length == 13);
170 |
171 | assert("-i" == argumentArray![0]);
172 | assert("file:///tmp/input.mp4" == argumentArray![1]);
173 | assert("-vf" == argumentArray![2]);
174 | assert(
175 | "subtitles=file:///tmp/subtitles.srt:force_style=\\\"FontSize=16,PrimaryColour=&HFFFFFF&\\\"" ==
176 | argumentArray![3]);
177 | assert("-vcodec" == argumentArray![4]);
178 | assert("libx264" == argumentArray![5]);
179 | assert("-acodec" == argumentArray![6]);
180 | assert("copy" == argumentArray![7]);
181 | assert("-q:v" == argumentArray![8]);
182 | assert("0" == argumentArray![9]);
183 | assert("-q:a" == argumentArray![10]);
184 | assert("0" == argumentArray![11]);
185 | assert("video.mp4" == argumentArray![12]);
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/example/lib/tooltip.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | const String COMMAND_TEST_TOOLTIP_TEXT =
21 | "Enter an FFmpeg command without 'ffmpeg' at the beginning and click one of the RUN buttons";
22 |
23 | const String VIDEO_TEST_TOOLTIP_TEXT =
24 | "Select a video codec and press ENCODE button";
25 |
26 | const String HTTPS_TEST_TOOLTIP_TEXT =
27 | "Enter the https url of a media file and click the button";
28 |
29 | const String AUDIO_TEST_TOOLTIP_TEXT =
30 | "Select an audio codec and press ENCODE button";
31 |
32 | const String SUBTITLE_TEST_TOOLTIP_TEXT =
33 | "Click the button to burn subtitles. Created video will play inside the frame below";
34 |
35 | const String VIDSTAB_TEST_TOOLTIP_TEXT =
36 | "Click the button to stabilize video. Original video will play above and stabilized video will play below";
37 |
38 | const String PIPE_TEST_TOOLTIP_TEXT =
39 | "Click the button to create a video using pipe redirection. Created video will play inside the frame below";
40 |
41 | const String CONCURRENT_EXECUTION_TEST_TOOLTIP_TEXT =
42 | " Use ENCODE and CANCEL buttons to start/stop multiple execution";
43 |
--------------------------------------------------------------------------------
/example/lib/util.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | String today() {
21 | var now = new DateTime.now();
22 | return "${now.year}-${now.month}-${now.day}";
23 | }
24 |
25 | String now() {
26 | var now = new DateTime.now();
27 | return "${now.year}-${now.month}-${now.day} ${now.hour}:${now.minute}:${now.second}.${now.millisecond}";
28 | }
29 |
30 | void ffprint(String text) {
31 | final pattern = new RegExp('.{1,900}');
32 | var nowString = now();
33 | pattern
34 | .allMatches(text)
35 | .forEach((match) => print("$nowString - " + match.group(0)!));
36 | }
37 |
--------------------------------------------------------------------------------
/example/lib/vid_stab_tab.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'dart:io';
21 |
22 | import 'package:flutter_ffmpeg/completed_ffmpeg_execution.dart';
23 | import 'package:flutter_ffmpeg/log.dart';
24 | import 'package:flutter_ffmpeg_example/abstract.dart';
25 | import 'package:flutter_ffmpeg_example/flutter_ffmpeg_api_wrapper.dart';
26 | import 'package:flutter_ffmpeg_example/player.dart';
27 | import 'package:flutter_ffmpeg_example/popup.dart';
28 | import 'package:flutter_ffmpeg_example/tooltip.dart';
29 | import 'package:flutter_ffmpeg_example/video_util.dart';
30 | import 'package:video_player/video_player.dart';
31 |
32 | import 'util.dart';
33 |
34 | class _ControllerWrapper implements PlayerTab {
35 | late VideoPlayerController? _controller;
36 |
37 | @override
38 | void setController(VideoPlayerController controller) {
39 | _controller = controller;
40 | }
41 | }
42 |
43 | class VidStabTab {
44 | late _ControllerWrapper videoController;
45 | late _ControllerWrapper stabilizedVideoController;
46 | late RefreshablePlayerDialogFactory _refreshablePlayerDialogFactory;
47 |
48 | void init(RefreshablePlayerDialogFactory refreshablePlayerDialogFactory) {
49 | _refreshablePlayerDialogFactory = refreshablePlayerDialogFactory;
50 | videoController = _ControllerWrapper();
51 | stabilizedVideoController = _ControllerWrapper();
52 | }
53 |
54 | void setActive() {
55 | print("VidStab Tab Activated");
56 | enableLogCallback(logCallback);
57 | enableStatisticsCallback(null);
58 | showPopup(VIDSTAB_TEST_TOOLTIP_TEXT);
59 | }
60 |
61 | void logCallback(Log log) {
62 | ffprint(log.message);
63 | _refreshablePlayerDialogFactory.refresh();
64 | }
65 |
66 | void stabilizeVideo() {
67 | VideoUtil.assetPath(VideoUtil.ASSET_1).then((image1Path) {
68 | VideoUtil.assetPath(VideoUtil.ASSET_2).then((image2Path) {
69 | VideoUtil.assetPath(VideoUtil.ASSET_3).then((image3Path) {
70 | getShakeResultsFile().then((shakeResultsFile) {
71 | getVideoFile().then((videoFile) {
72 | getStabilizedVideoFile().then((stabilizedVideoFile) {
73 | // IF VIDEO IS PLAYING STOP PLAYBACK
74 | pauseVideo();
75 | pauseStabilizedVideo();
76 |
77 | try {
78 | shakeResultsFile.delete().catchError((_) {});
79 | } on Exception catch (_) {}
80 |
81 | try {
82 | videoFile.delete().catchError((_) {});
83 | } on Exception catch (_) {}
84 |
85 | try {
86 | stabilizedVideoFile.delete().catchError((_) {});
87 | } on Exception catch (_) {}
88 |
89 | ffprint("Testing VID.STAB");
90 |
91 | showCreateProgressDialog();
92 |
93 | final ffmpegCommand = VideoUtil.generateShakingVideoScript(
94 | image1Path, image2Path, image3Path, videoFile.path);
95 |
96 | executeAsyncFFmpeg(ffmpegCommand,
97 | (CompletedFFmpegExecution execution) {
98 | ffprint(
99 | "FFmpeg process exited with rc ${execution.returnCode}.");
100 |
101 | hideProgressDialog();
102 |
103 | if (execution.returnCode == 0) {
104 | ffprint(
105 | "Create completed successfully; stabilizing video.");
106 |
107 | String analyzeVideoCommand =
108 | "-y -i ${videoFile.path} -vf vidstabdetect=shakiness=10:accuracy=15:result=${shakeResultsFile.path} -f null -";
109 |
110 | showStabilizeProgressDialog();
111 |
112 | executeAsyncFFmpeg(analyzeVideoCommand,
113 | (CompletedFFmpegExecution secondExecution) {
114 | ffprint(
115 | "FFmpeg process exited with rc ${secondExecution.returnCode}.");
116 |
117 | final String stabilizeVideoCommand =
118 | "-y -i ${videoFile.path} -vf vidstabtransform=smoothing=30:input=${shakeResultsFile.path} -c:v mpeg4 ${stabilizedVideoFile.path}";
119 |
120 | //@TODO check return code before starting the third execution
121 |
122 | executeAsyncFFmpeg(stabilizeVideoCommand,
123 | (CompletedFFmpegExecution thirdExecution) {
124 | hideProgressDialog();
125 |
126 | if (thirdExecution.returnCode == 0) {
127 | ffprint(
128 | "Stabilize video completed successfully; playing videos.");
129 | playVideo();
130 | playStabilizedVideo();
131 | } else {
132 | showPopup(
133 | "Stabilize video failed. Please check log for the details.");
134 | ffprint(
135 | "Stabilize video failed with rc=${thirdExecution.returnCode}.");
136 | }
137 | }).then((executionId) {
138 | ffprint(
139 | "Async FFmpeg process started with arguments '$stabilizeVideoCommand' and executionId $executionId.");
140 | });
141 | }).then((executionId) {
142 | ffprint(
143 | "Async FFmpeg process started with arguments '$analyzeVideoCommand' and executionId $executionId.");
144 | });
145 | }
146 | }).then((executionId) {
147 | ffprint(
148 | "Async FFmpeg process started with arguments '$ffmpegCommand' and executionId $executionId.");
149 | });
150 | });
151 | });
152 | });
153 | });
154 | });
155 | });
156 | }
157 |
158 | Future playVideo() async {
159 | if (videoController._controller != null) {
160 | await videoController._controller!.initialize();
161 | await videoController._controller!.play();
162 | }
163 | _refreshablePlayerDialogFactory.refresh();
164 | }
165 |
166 | Future pauseVideo() async {
167 | if (videoController._controller != null) {
168 | await videoController._controller!.pause();
169 | }
170 | _refreshablePlayerDialogFactory.refresh();
171 | }
172 |
173 | Future playStabilizedVideo() async {
174 | if (stabilizedVideoController._controller != null) {
175 | await stabilizedVideoController._controller!.initialize();
176 | await stabilizedVideoController._controller!.play();
177 | }
178 | _refreshablePlayerDialogFactory.refresh();
179 | }
180 |
181 | Future pauseStabilizedVideo() async {
182 | if (stabilizedVideoController._controller != null) {
183 | await stabilizedVideoController._controller!.pause();
184 | }
185 | _refreshablePlayerDialogFactory.refresh();
186 | }
187 |
188 | Future getShakeResultsFile() async {
189 | final String subtitle = "transforms.trf";
190 | Directory documentsDirectory = await VideoUtil.tempDirectory;
191 | return new File("${documentsDirectory.path}/$subtitle");
192 | }
193 |
194 | Future getVideoFile() async {
195 | final String video = "video-shaking.mp4";
196 | Directory documentsDirectory = await VideoUtil.documentsDirectory;
197 | return new File("${documentsDirectory.path}/$video");
198 | }
199 |
200 | Future getStabilizedVideoFile() async {
201 | final String video = "video-stabilized.mp4";
202 | Directory documentsDirectory = await VideoUtil.documentsDirectory;
203 | return new File("${documentsDirectory.path}/$video");
204 | }
205 |
206 | void showCreateProgressDialog() {
207 | _refreshablePlayerDialogFactory.dialogShow("Creating video");
208 | }
209 |
210 | void showStabilizeProgressDialog() {
211 | _refreshablePlayerDialogFactory.dialogShow("Stabilizing video");
212 | }
213 |
214 | void hideProgressDialog() {
215 | _refreshablePlayerDialogFactory.dialogHide();
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/example/lib/video_tab.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'dart:async';
21 | import 'dart:io';
22 |
23 | import 'package:flutter/material.dart';
24 | import 'package:flutter_ffmpeg/completed_ffmpeg_execution.dart';
25 | import 'package:flutter_ffmpeg/log.dart';
26 | import 'package:flutter_ffmpeg/statistics.dart';
27 | import 'package:flutter_ffmpeg_example/abstract.dart';
28 | import 'package:flutter_ffmpeg_example/player.dart';
29 | import 'package:flutter_ffmpeg_example/popup.dart';
30 | import 'package:flutter_ffmpeg_example/test_api.dart';
31 | import 'package:flutter_ffmpeg_example/tooltip.dart';
32 | import 'package:flutter_ffmpeg_example/util.dart';
33 | import 'package:flutter_ffmpeg_example/video_util.dart';
34 | import 'package:video_player/video_player.dart';
35 |
36 | import 'flutter_ffmpeg_api_wrapper.dart';
37 |
38 | class VideoTab implements PlayerTab {
39 | VideoPlayerController? _videoPlayerController;
40 | late RefreshablePlayerDialogFactory _refreshablePlayerDialogFactory;
41 | late String _selectedCodec;
42 | late Statistics? _statistics;
43 |
44 | void init(RefreshablePlayerDialogFactory refreshablePlayerDialogFactory) {
45 | _refreshablePlayerDialogFactory = refreshablePlayerDialogFactory;
46 | List> videoCodecList = getVideoCodecList();
47 | _selectedCodec = videoCodecList[0].value!;
48 | _statistics = null;
49 | }
50 |
51 | void setActive() {
52 | print("Video Tab Activated");
53 | enableLogCallback(logCallback);
54 | enableStatisticsCallback(statisticsCallback);
55 | showPopup(VIDEO_TEST_TOOLTIP_TEXT);
56 | }
57 |
58 | void logCallback(Log log) {
59 | ffprint(log.message);
60 | }
61 |
62 | void statisticsCallback(Statistics statistics) {
63 | this._statistics = statistics;
64 | updateProgressDialog();
65 | }
66 |
67 | void changedVideoCodec(String? selectedCodec) {
68 | _selectedCodec = selectedCodec!;
69 | _refreshablePlayerDialogFactory.refresh();
70 | }
71 |
72 | void encodeVideo() {
73 | ffprint(
74 | "Testing post execution commands before starting the new encoding.");
75 | Test.testPostExecutionCommands();
76 |
77 | VideoUtil.assetPath(VideoUtil.ASSET_1).then((image1Path) {
78 | VideoUtil.assetPath(VideoUtil.ASSET_2).then((image2Path) {
79 | VideoUtil.assetPath(VideoUtil.ASSET_3).then((image3Path) {
80 | getVideoFile().then((videoFile) {
81 | // IF VIDEO IS PLAYING STOP PLAYBACK
82 | pause();
83 |
84 | try {
85 | videoFile.delete().catchError((_) {});
86 | } on Exception catch (_) {}
87 |
88 | final String videoCodec = getSelectedVideoCodec();
89 |
90 | ffprint("Testing VIDEO encoding with '$videoCodec' codec");
91 |
92 | hideProgressDialog();
93 | showProgressDialog();
94 |
95 | final ffmpegCommand = VideoUtil.generateEncodeVideoScript(
96 | image1Path,
97 | image2Path,
98 | image3Path,
99 | videoFile.path,
100 | videoCodec,
101 | getCustomOptions());
102 |
103 | executeAsyncFFmpeg(ffmpegCommand,
104 | (CompletedFFmpegExecution execution) {
105 | hideProgressDialog();
106 |
107 | if (execution.returnCode == 0) {
108 | ffprint("Encode completed successfully; playing video.");
109 | playVideo();
110 | } else {
111 | ffprint("Encode failed with rc=${execution.returnCode}.");
112 | showPopup("Encode failed. Please check log for the details.");
113 | }
114 | }).then((executionId) {
115 | ffprint(
116 | "Async FFmpeg process started with arguments '$ffmpegCommand' and executionId $executionId.");
117 | });
118 | });
119 | });
120 | });
121 | });
122 | }
123 |
124 | Future playVideo() async {
125 | if (_videoPlayerController != null) {
126 | await _videoPlayerController!.initialize();
127 | await _videoPlayerController!.play();
128 | }
129 | _refreshablePlayerDialogFactory.refresh();
130 | }
131 |
132 | Future pause() async {
133 | if (_videoPlayerController != null) {
134 | await _videoPlayerController!.pause();
135 | }
136 | _refreshablePlayerDialogFactory.refresh();
137 | }
138 |
139 | String getSelectedVideoCodec() {
140 | String videoCodec = _selectedCodec;
141 |
142 | // VIDEO CODEC MENU HAS BASIC NAMES, FFMPEG NEEDS LONGER LIBRARY NAMES.
143 | // APPLYING NECESSARY TRANSFORMATION HERE
144 | switch (videoCodec) {
145 | case "x264":
146 | videoCodec = "libx264";
147 | break;
148 | case "openh264":
149 | videoCodec = "libopenh264";
150 | break;
151 | case "x265":
152 | videoCodec = "libx265";
153 | break;
154 | case "xvid":
155 | videoCodec = "libxvid";
156 | break;
157 | case "vp8":
158 | videoCodec = "libvpx";
159 | break;
160 | case "vp9":
161 | videoCodec = "libvpx-vp9";
162 | break;
163 | case "aom":
164 | videoCodec = "libaom-av1";
165 | break;
166 | case "kvazaar":
167 | videoCodec = "libkvazaar";
168 | break;
169 | case "theora":
170 | videoCodec = "libtheora";
171 | break;
172 | }
173 |
174 | return videoCodec;
175 | }
176 |
177 | Future getVideoFile() async {
178 | String videoCodec = _selectedCodec;
179 |
180 | String extension;
181 | switch (videoCodec) {
182 | case "vp8":
183 | case "vp9":
184 | extension = "webm";
185 | break;
186 | case "aom":
187 | extension = "mkv";
188 | break;
189 | case "theora":
190 | extension = "ogv";
191 | break;
192 | case "hap":
193 | extension = "mov";
194 | break;
195 | default:
196 | // mpeg4, x264, x265, xvid, kvazaar
197 | extension = "mp4";
198 | break;
199 | }
200 |
201 | final String video = "video." + extension;
202 | Directory documentsDirectory = await VideoUtil.documentsDirectory;
203 | return new File("${documentsDirectory.path}/$video");
204 | }
205 |
206 | String getCustomOptions() {
207 | String videoCodec = _selectedCodec;
208 |
209 | switch (videoCodec) {
210 | case "x265":
211 | return "-crf 28 -preset fast ";
212 | case "vp8":
213 | return "-b:v 1M -crf 10 ";
214 | case "vp9":
215 | return "-b:v 2M ";
216 | case "aom":
217 | return "-crf 30 -strict experimental ";
218 | case "theora":
219 | return "-qscale:v 7 ";
220 | case "hap":
221 | return "-format hap_q ";
222 | default:
223 | // kvazaar, mpeg4, x264, xvid
224 | return "";
225 | }
226 | }
227 |
228 | List> getVideoCodecList() {
229 | List> list = List.empty(growable: true);
230 |
231 | list.add(new DropdownMenuItem(
232 | value: "mpeg4",
233 | child: SizedBox(width: 100, child: Center(child: new Text("mpeg4")))));
234 | list.add(new DropdownMenuItem(
235 | value: "x264",
236 | child: SizedBox(width: 100, child: Center(child: new Text("x264")))));
237 | list.add(new DropdownMenuItem(
238 | value: "openh264",
239 | child:
240 | SizedBox(width: 100, child: Center(child: new Text("openh264")))));
241 | list.add(new DropdownMenuItem(
242 | value: "x265",
243 | child: SizedBox(width: 100, child: Center(child: new Text("x265")))));
244 | list.add(new DropdownMenuItem(
245 | value: "xvid",
246 | child: SizedBox(width: 100, child: Center(child: new Text("xvid")))));
247 | list.add(new DropdownMenuItem(
248 | value: "vp8",
249 | child: SizedBox(width: 100, child: Center(child: new Text("vp8")))));
250 | list.add(new DropdownMenuItem(
251 | value: "vp9",
252 | child: SizedBox(width: 100, child: Center(child: new Text("vp9")))));
253 | list.add(new DropdownMenuItem(
254 | value: "aom",
255 | child: SizedBox(width: 100, child: Center(child: new Text("aom")))));
256 | list.add(new DropdownMenuItem(
257 | value: "kvazaar",
258 | child:
259 | SizedBox(width: 100, child: Center(child: new Text("kvazaar")))));
260 | list.add(new DropdownMenuItem(
261 | value: "theora",
262 | child: SizedBox(width: 100, child: Center(child: new Text("theora")))));
263 | list.add(new DropdownMenuItem(
264 | value: "hap",
265 | child: SizedBox(width: 100, child: Center(child: new Text("hap")))));
266 |
267 | return list;
268 | }
269 |
270 | void showProgressDialog() {
271 | // CLEAN STATISTICS
272 | _statistics = null;
273 | resetStatistics();
274 | _refreshablePlayerDialogFactory.dialogShow("Encoding video");
275 | }
276 |
277 | void updateProgressDialog() {
278 | var statistics = this._statistics;
279 | if (statistics == null) {
280 | return;
281 | }
282 |
283 | int timeInMilliseconds = statistics.time;
284 | if (timeInMilliseconds > 0) {
285 | int totalVideoDuration = 9000;
286 |
287 | int completePercentage = (timeInMilliseconds * 100) ~/ totalVideoDuration;
288 |
289 | _refreshablePlayerDialogFactory
290 | .dialogUpdate("Encoding video % $completePercentage");
291 | _refreshablePlayerDialogFactory.refresh();
292 | }
293 | }
294 |
295 | void hideProgressDialog() {
296 | _refreshablePlayerDialogFactory.dialogHide();
297 | }
298 |
299 | String getSelectedCodec() => _selectedCodec;
300 |
301 | @override
302 | void setController(VideoPlayerController controller) {
303 | _videoPlayerController = controller;
304 | }
305 | }
306 |
--------------------------------------------------------------------------------
/example/lib/video_util.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'dart:async';
21 | import 'dart:io';
22 |
23 | import 'package:flutter/services.dart';
24 | import 'package:path/path.dart';
25 | import 'package:path_provider/path_provider.dart';
26 |
27 | import 'flutter_ffmpeg_api_wrapper.dart';
28 | import 'util.dart';
29 |
30 | class VideoUtil {
31 | static const String ASSET_1 = "pyramid.jpg";
32 | static const String ASSET_2 = "colosseum.jpg";
33 | static const String ASSET_3 = "tajmahal.jpg";
34 | static const String SUBTITLE_ASSET = "subtitle.srt";
35 | static const String FONT_ASSET_1 = "doppioone_regular.ttf";
36 | static const String FONT_ASSET_2 = "truenorg.otf";
37 |
38 | static void registerAppFont() {
39 | var fontNameMapping = Map();
40 | fontNameMapping["MyFontName"] = "Doppio One";
41 | VideoUtil.tempDirectory.then((tempDirectory) {
42 | setFontDirectory(tempDirectory.path, fontNameMapping);
43 | setEnvironmentVariable(
44 | "FFREPORT",
45 | "file=" +
46 | new File(tempDirectory.path + "/" + today() + "-ffreport.txt")
47 | .path);
48 | });
49 | }
50 |
51 | static void prepareAssets() async {
52 | await VideoUtil.assetToFile(ASSET_1);
53 | await VideoUtil.assetToFile(ASSET_2);
54 | await VideoUtil.assetToFile(ASSET_3);
55 | await VideoUtil.assetToFile(SUBTITLE_ASSET);
56 | await VideoUtil.assetToFile(FONT_ASSET_1);
57 | await VideoUtil.assetToFile(FONT_ASSET_2);
58 | }
59 |
60 | static Future assetToFile(String assetName) async {
61 | final ByteData assetByteData = await rootBundle.load('assets/$assetName');
62 |
63 | final List byteList = assetByteData.buffer
64 | .asUint8List(assetByteData.offsetInBytes, assetByteData.lengthInBytes);
65 |
66 | final String fullTemporaryPath =
67 | join((await tempDirectory).path, assetName);
68 |
69 | Future fileFuture = new File(fullTemporaryPath)
70 | .writeAsBytes(byteList, mode: FileMode.writeOnly, flush: true);
71 |
72 | ffprint('assets/$assetName saved to file at $fullTemporaryPath.');
73 |
74 | return fileFuture;
75 | }
76 |
77 | static void writeAssetToPipeAndClose(
78 | String assetName, String pipePath) async {
79 | final ByteData byteData = await rootBundle.load('assets/$assetName');
80 |
81 | var pipeFile = new File(pipePath);
82 | await pipeFile.writeAsBytes(
83 | byteData.buffer
84 | .asUint8List(byteData.offsetInBytes, byteData.lengthInBytes),
85 | mode: FileMode.writeOnly,
86 | flush: false);
87 |
88 | ffprint('assets/$assetName saved to pipe at $pipePath.');
89 |
90 | closeFFmpegPipe(pipePath);
91 | }
92 |
93 | static Future assetPath(String assetName) async {
94 | return join((await tempDirectory).path, assetName);
95 | }
96 |
97 | static Future get documentsDirectory async {
98 | return await getApplicationDocumentsDirectory();
99 | }
100 |
101 | static Future get tempDirectory async {
102 | return await getTemporaryDirectory();
103 | }
104 |
105 | static String generateEncodeVideoScript(
106 | String image1Path,
107 | String image2Path,
108 | String image3Path,
109 | String videoFilePath,
110 | String videoCodec,
111 | String customOptions) {
112 | return "-hide_banner -y -loop 1 -i '" +
113 | image1Path +
114 | "' " +
115 | "-loop 1 -i \"" +
116 | image2Path +
117 | "\" " +
118 | "-loop 1 -i \"" +
119 | image3Path +
120 | "\" " +
121 | "-filter_complex " +
122 | "\"[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];" +
123 | "[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];" +
124 | "[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];" +
125 | "[stream1out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=3,select=lte(n\\,90)[stream1overlaid];" +
126 | "[stream1out2]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=1,select=lte(n\\,30)[stream1ending];" +
127 | "[stream2out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=2,select=lte(n\\,60)[stream2overlaid];" +
128 | "[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];" +
129 | "[stream3out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=2,select=lte(n\\,60)[stream3overlaid];" +
130 | "[stream3out2]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=1,select=lte(n\\,30)[stream3starting];" +
131 | "[stream2starting][stream1ending]blend=all_expr='if(gte(X,(W/2)*T/1)*lte(X,W-(W/2)*T/1),B,A)':shortest=1[stream2blended];" +
132 | "[stream3starting][stream2ending]blend=all_expr='if(gte(X,(W/2)*T/1)*lte(X,W-(W/2)*T/1),B,A)':shortest=1[stream3blended];" +
133 | "[stream1overlaid][stream2blended][stream2overlaid][stream3blended][stream3overlaid]concat=n=5:v=1:a=0,scale=w=640:h=424,format=yuv420p[video]\"" +
134 | " -map [video] -vsync 2 -async 1 " +
135 | customOptions +
136 | "-c:v " +
137 | videoCodec +
138 | " -r 30 " +
139 | videoFilePath;
140 | }
141 |
142 | static String generateShakingVideoScript(
143 | final String image1Path,
144 | final String image2Path,
145 | final String image3Path,
146 | final String videoFilePath) {
147 | return "-hide_banner -y -loop 1 -i \"" +
148 | image1Path +
149 | "\" " +
150 | "-loop 1 -i '" +
151 | image2Path +
152 | "' " +
153 | "-loop 1 -i " +
154 | image3Path +
155 | " " +
156 | "-f lavfi -i color=black:s=640x427 " +
157 | "-filter_complex \"" +
158 | "[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];" +
159 | "[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];" +
160 | "[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];" +
161 | "[stream1out]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=3[stream1overlaid];" +
162 | "[stream2out]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=3[stream2overlaid];" +
163 | "[stream3out]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=3[stream3overlaid];" +
164 | "[3:v][stream1overlaid]overlay=x=\'2*mod(n,4)\':y=\'2*mod(n,2)\',trim=duration=3[stream1shaking];" +
165 | "[3:v][stream2overlaid]overlay=x=\'2*mod(n,4)\':y=\'2*mod(n,2)\',trim=duration=3[stream2shaking];" +
166 | "[3:v][stream3overlaid]overlay=x=\'2*mod(n,4)\':y=\'2*mod(n,2)\',trim=duration=3[stream3shaking];" +
167 | "[stream1shaking][stream2shaking][stream3shaking]concat=n=3:v=1:a=0,scale=w=640:h=424,format=yuv420p[video]\"" +
168 | " -map [video] -vsync 2 -async 1 -c:v mpeg4 -r 30 " +
169 | videoFilePath;
170 | }
171 |
172 | static String generateCreateVideoWithPipesScript(
173 | final String image1Pipe,
174 | final String image2Pipe,
175 | final String image3Pipe,
176 | final String videoFilePath) {
177 | return "-hide_banner -y -i \"" +
178 | image1Pipe +
179 | "\" " +
180 | "-i '" +
181 | image2Pipe +
182 | "' " +
183 | "-i " +
184 | image3Pipe +
185 | " " +
186 | "-filter_complex \"" +
187 | "[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];" +
188 | "[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];" +
189 | "[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];" +
190 | "[stream1out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=3,select=lte(n\\,90)[stream1overlaid];" +
191 | "[stream1out2]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=1,select=lte(n\\,30)[stream1ending];" +
192 | "[stream2out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=2,select=lte(n\\,60)[stream2overlaid];" +
193 | "[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];" +
194 | "[stream3out1]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=2,select=lte(n\\,60)[stream3overlaid];" +
195 | "[stream3out2]pad=width=640:height=427:x=(640-iw)/2:y=(427-ih)/2:color=#00000000,trim=duration=1,select=lte(n\\,30)[stream3starting];" +
196 | "[stream2starting][stream1ending]blend=all_expr=\'if(gte(X,(W/2)*T/1)*lte(X,W-(W/2)*T/1),B,A)\':shortest=1[stream2blended];" +
197 | "[stream3starting][stream2ending]blend=all_expr=\'if(gte(X,(W/2)*T/1)*lte(X,W-(W/2)*T/1),B,A)\':shortest=1[stream3blended];" +
198 | "[stream1overlaid][stream2blended][stream2overlaid][stream3blended][stream3overlaid]concat=n=5:v=1:a=0,scale=w=640:h=424,format=yuv420p[video]\"" +
199 | " -map [video] -vsync 2 -async 1 -c:v mpeg4 -r 30 " +
200 | videoFilePath;
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.6.1"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.1.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.2.0"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.0"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.15.0"
46 | cupertino_icons:
47 | dependency: "direct main"
48 | description:
49 | name: cupertino_icons
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.0.3"
53 | fake_async:
54 | dependency: transitive
55 | description:
56 | name: fake_async
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.2.0"
60 | ffi:
61 | dependency: transitive
62 | description:
63 | name: ffi
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "1.0.0"
67 | file:
68 | dependency: transitive
69 | description:
70 | name: file
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "6.1.0"
74 | flutter:
75 | dependency: "direct main"
76 | description: flutter
77 | source: sdk
78 | version: "0.0.0"
79 | flutter_ffmpeg:
80 | dependency: "direct main"
81 | description:
82 | name: flutter_ffmpeg
83 | url: "https://pub.dartlang.org"
84 | source: hosted
85 | version: "0.4.2"
86 | flutter_test:
87 | dependency: "direct dev"
88 | description: flutter
89 | source: sdk
90 | version: "0.0.0"
91 | flutter_web_plugins:
92 | dependency: transitive
93 | description: flutter
94 | source: sdk
95 | version: "0.0.0"
96 | fluttertoast:
97 | dependency: "direct main"
98 | description:
99 | name: fluttertoast
100 | url: "https://pub.dartlang.org"
101 | source: hosted
102 | version: "8.0.8"
103 | js:
104 | dependency: transitive
105 | description:
106 | name: js
107 | url: "https://pub.dartlang.org"
108 | source: hosted
109 | version: "0.6.3"
110 | matcher:
111 | dependency: transitive
112 | description:
113 | name: matcher
114 | url: "https://pub.dartlang.org"
115 | source: hosted
116 | version: "0.12.10"
117 | meta:
118 | dependency: transitive
119 | description:
120 | name: meta
121 | url: "https://pub.dartlang.org"
122 | source: hosted
123 | version: "1.3.0"
124 | path:
125 | dependency: "direct main"
126 | description:
127 | name: path
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "1.8.0"
131 | path_provider:
132 | dependency: "direct main"
133 | description:
134 | name: path_provider
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "2.0.2"
138 | path_provider_linux:
139 | dependency: transitive
140 | description:
141 | name: path_provider_linux
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "2.0.0"
145 | path_provider_macos:
146 | dependency: transitive
147 | description:
148 | name: path_provider_macos
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "2.0.0"
152 | path_provider_platform_interface:
153 | dependency: transitive
154 | description:
155 | name: path_provider_platform_interface
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "2.0.1"
159 | path_provider_windows:
160 | dependency: transitive
161 | description:
162 | name: path_provider_windows
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "2.0.0"
166 | platform:
167 | dependency: transitive
168 | description:
169 | name: platform
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "3.0.0"
173 | plugin_platform_interface:
174 | dependency: transitive
175 | description:
176 | name: plugin_platform_interface
177 | url: "https://pub.dartlang.org"
178 | source: hosted
179 | version: "2.0.0"
180 | process:
181 | dependency: transitive
182 | description:
183 | name: process
184 | url: "https://pub.dartlang.org"
185 | source: hosted
186 | version: "4.1.0"
187 | sky_engine:
188 | dependency: transitive
189 | description: flutter
190 | source: sdk
191 | version: "0.0.99"
192 | source_span:
193 | dependency: transitive
194 | description:
195 | name: source_span
196 | url: "https://pub.dartlang.org"
197 | source: hosted
198 | version: "1.8.1"
199 | stack_trace:
200 | dependency: transitive
201 | description:
202 | name: stack_trace
203 | url: "https://pub.dartlang.org"
204 | source: hosted
205 | version: "1.10.0"
206 | stream_channel:
207 | dependency: transitive
208 | description:
209 | name: stream_channel
210 | url: "https://pub.dartlang.org"
211 | source: hosted
212 | version: "2.1.0"
213 | string_scanner:
214 | dependency: transitive
215 | description:
216 | name: string_scanner
217 | url: "https://pub.dartlang.org"
218 | source: hosted
219 | version: "1.1.0"
220 | term_glyph:
221 | dependency: transitive
222 | description:
223 | name: term_glyph
224 | url: "https://pub.dartlang.org"
225 | source: hosted
226 | version: "1.2.0"
227 | test_api:
228 | dependency: transitive
229 | description:
230 | name: test_api
231 | url: "https://pub.dartlang.org"
232 | source: hosted
233 | version: "0.3.0"
234 | typed_data:
235 | dependency: transitive
236 | description:
237 | name: typed_data
238 | url: "https://pub.dartlang.org"
239 | source: hosted
240 | version: "1.3.0"
241 | vector_math:
242 | dependency: transitive
243 | description:
244 | name: vector_math
245 | url: "https://pub.dartlang.org"
246 | source: hosted
247 | version: "2.1.0"
248 | video_player:
249 | dependency: "direct main"
250 | description:
251 | name: video_player
252 | url: "https://pub.dartlang.org"
253 | source: hosted
254 | version: "2.1.12"
255 | video_player_platform_interface:
256 | dependency: transitive
257 | description:
258 | name: video_player_platform_interface
259 | url: "https://pub.dartlang.org"
260 | source: hosted
261 | version: "4.1.0"
262 | video_player_web:
263 | dependency: transitive
264 | description:
265 | name: video_player_web
266 | url: "https://pub.dartlang.org"
267 | source: hosted
268 | version: "2.0.0"
269 | win32:
270 | dependency: transitive
271 | description:
272 | name: win32
273 | url: "https://pub.dartlang.org"
274 | source: hosted
275 | version: "2.0.0"
276 | xdg_directories:
277 | dependency: transitive
278 | description:
279 | name: xdg_directories
280 | url: "https://pub.dartlang.org"
281 | source: hosted
282 | version: "0.2.0"
283 | sdks:
284 | dart: ">=2.12.0 <3.0.0"
285 | flutter: ">=2.0.0"
286 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_ffmpeg_example
2 | description: Demonstrates how to use the flutter_ffmpeg plugin.
3 | version: 0.4.2
4 | publish_to: 'none'
5 |
6 | environment:
7 | sdk: '>=2.12.0 <3.0.0'
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 | cupertino_icons: ^1.0.3
13 | path_provider: ^2.0.2
14 | path: ^1.8.0
15 | fluttertoast: ^8.0.8
16 | video_player: ^2.1.12
17 | flutter_ffmpeg: 0.4.2
18 |
19 | dev_dependencies:
20 | flutter_test:
21 | sdk: flutter
22 |
23 | flutter:
24 | uses-material-design: true
25 | assets:
26 | - assets/
27 |
--------------------------------------------------------------------------------
/flutter_ffmpeg.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 |
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tanersener/flutter-ffmpeg/fca32a79bc3ce7bb5fc76f5de0f5af5de9de3b61/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Classes/EmptyLogDelegate.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | #include
21 |
22 | /**
23 | * Empty log delegate.
24 | */
25 | @interface EmptyLogDelegate : NSObject
26 | @end
27 |
--------------------------------------------------------------------------------
/ios/Classes/EmptyLogDelegate.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | #include "EmptyLogDelegate.h"
21 |
22 | /**
23 | * Empty log delegate.
24 | */
25 | @implementation EmptyLogDelegate
26 |
27 | - (void)logCallback: (int)level :(NSString*)message {
28 | }
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterExecuteDelegate.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | #import
21 | #import
22 |
23 | /**
24 | * Execute delegate for async executions.
25 | */
26 | @interface FlutterExecuteDelegate : NSObject
27 |
28 | - (instancetype)initWithEventSink:(FlutterEventSink)eventSink;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterExecuteDelegate.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | #import "FlutterExecuteDelegate.h"
21 |
22 | static NSString *const EVENT_EXECUTE = @"FlutterFFmpegExecuteCallback";
23 |
24 | /**
25 | * Execute delegate for async executions.
26 | */
27 | @implementation FlutterExecuteDelegate {
28 | FlutterEventSink _eventSink;
29 | }
30 |
31 | - (instancetype)initWithEventSink:(FlutterEventSink)eventSink {
32 | self = [super init];
33 | if (self) {
34 | _eventSink = eventSink;
35 | }
36 |
37 | return self;
38 | }
39 |
40 | - (void)executeCallback:(long)executionId :(int)returnCode {
41 | NSMutableDictionary *executeDictionary = [[NSMutableDictionary alloc] init];
42 | executeDictionary[@"executionId"] = [NSNumber numberWithLong: executionId];
43 | executeDictionary[@"returnCode"] = [NSNumber numberWithInt: returnCode];
44 |
45 | NSMutableDictionary *eventDictionary = [[NSMutableDictionary alloc] init];
46 | eventDictionary[EVENT_EXECUTE] = executeDictionary;
47 |
48 | _eventSink(eventDictionary);
49 | }
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterFFmpegPlugin.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019-2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | #import
21 | #import
22 |
23 | /**
24 | * Flutter FFmpeg Plugin
25 | */
26 | @interface FlutterFFmpegPlugin : NSObject
27 | @end
28 |
--------------------------------------------------------------------------------
/ios/flutter_ffmpeg.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'flutter_ffmpeg'
3 | s.version = '0.4.2'
4 | s.summary = 'FFmpeg plugin for Flutter.'
5 | s.description = 'FFmpeg plugin based on mobile-ffmpeg for Flutter.'
6 | s.homepage = 'https://github.com/tanersener/flutter-ffmpeg'
7 |
8 | s.author = { 'Taner Sener' => 'tanersener@gmail.com' }
9 | s.license = { :file => '../LICENSE' }
10 |
11 | s.requires_arc = true
12 | s.static_framework = true
13 |
14 | s.source = { :path => '.' }
15 | s.source_files = 'Classes/**/*'
16 | s.public_header_files = 'Classes/**/*.h'
17 |
18 | s.default_subspec = 'https'
19 |
20 | s.dependency 'Flutter'
21 |
22 | s.subspec 'min' do |ss|
23 | ss.source_files = 'Classes/**/*'
24 | ss.public_header_files = 'Classes/**/*.h'
25 |
26 | ss.dependency 'mobile-ffmpeg-min', '4.4'
27 | ss.ios.deployment_target = '11.0'
28 | end
29 |
30 | s.subspec 'min-lts' do |ss|
31 | ss.source_files = 'Classes/**/*'
32 | ss.public_header_files = 'Classes/**/*.h'
33 |
34 | ss.dependency 'mobile-ffmpeg-min', '4.4.LTS'
35 | ss.ios.deployment_target = '9.3'
36 | end
37 |
38 | s.subspec 'min-gpl' do |ss|
39 | ss.source_files = 'Classes/**/*'
40 | ss.public_header_files = 'Classes/**/*.h'
41 |
42 | ss.dependency 'mobile-ffmpeg-min-gpl', '4.4'
43 | ss.ios.deployment_target = '11.0'
44 | end
45 |
46 | s.subspec 'min-gpl-lts' do |ss|
47 | ss.source_files = 'Classes/**/*'
48 | ss.public_header_files = 'Classes/**/*.h'
49 |
50 | ss.dependency 'mobile-ffmpeg-min-gpl', '4.4.LTS'
51 | ss.ios.deployment_target = '9.3'
52 | end
53 |
54 | s.subspec 'https' do |ss|
55 | ss.source_files = 'Classes/**/*'
56 | ss.public_header_files = 'Classes/**/*.h'
57 |
58 | ss.dependency 'mobile-ffmpeg-https', '4.4'
59 | ss.ios.deployment_target = '11.0'
60 | end
61 |
62 | s.subspec 'https-lts' do |ss|
63 | ss.source_files = 'Classes/**/*'
64 | ss.public_header_files = 'Classes/**/*.h'
65 |
66 | ss.dependency 'mobile-ffmpeg-https', '4.4.LTS'
67 | ss.ios.deployment_target = '9.3'
68 | end
69 |
70 | s.subspec 'https-gpl' do |ss|
71 | ss.source_files = 'Classes/**/*'
72 | ss.public_header_files = 'Classes/**/*.h'
73 |
74 | ss.dependency 'mobile-ffmpeg-https-gpl', '4.4'
75 | ss.ios.deployment_target = '11.0'
76 | end
77 |
78 | s.subspec 'https-gpl-lts' do |ss|
79 | ss.source_files = 'Classes/**/*'
80 | ss.public_header_files = 'Classes/**/*.h'
81 |
82 | ss.dependency 'mobile-ffmpeg-https-gpl', '4.4.LTS'
83 | ss.ios.deployment_target = '9.3'
84 | end
85 |
86 | s.subspec 'audio' do |ss|
87 | ss.source_files = 'Classes/**/*'
88 | ss.public_header_files = 'Classes/**/*.h'
89 |
90 | ss.dependency 'mobile-ffmpeg-audio', '4.4'
91 | ss.ios.deployment_target = '11.0'
92 | end
93 |
94 | s.subspec 'audio-lts' do |ss|
95 | ss.source_files = 'Classes/**/*'
96 | ss.public_header_files = 'Classes/**/*.h'
97 |
98 | ss.dependency 'mobile-ffmpeg-audio', '4.4.LTS'
99 | ss.ios.deployment_target = '9.3'
100 | end
101 |
102 | s.subspec 'video' do |ss|
103 | ss.source_files = 'Classes/**/*'
104 | ss.public_header_files = 'Classes/**/*.h'
105 |
106 | ss.dependency 'mobile-ffmpeg-video', '4.4'
107 | ss.ios.deployment_target = '11.0'
108 | end
109 |
110 | s.subspec 'video-lts' do |ss|
111 | ss.source_files = 'Classes/**/*'
112 | ss.public_header_files = 'Classes/**/*.h'
113 |
114 | ss.dependency 'mobile-ffmpeg-video', '4.4.LTS'
115 | ss.ios.deployment_target = '9.3'
116 | end
117 |
118 | s.subspec 'full' do |ss|
119 | ss.source_files = 'Classes/**/*'
120 | ss.public_header_files = 'Classes/**/*.h'
121 |
122 | ss.dependency 'mobile-ffmpeg-full', '4.4'
123 | ss.ios.deployment_target = '11.0'
124 | end
125 |
126 | s.subspec 'full-lts' do |ss|
127 | ss.source_files = 'Classes/**/*'
128 | ss.public_header_files = 'Classes/**/*.h'
129 |
130 | ss.dependency 'mobile-ffmpeg-full', '4.4.LTS'
131 | ss.ios.deployment_target = '9.3'
132 | end
133 |
134 | s.subspec 'full-gpl' do |ss|
135 | ss.source_files = 'Classes/**/*'
136 | ss.public_header_files = 'Classes/**/*.h'
137 |
138 | ss.dependency 'mobile-ffmpeg-full-gpl', '4.4'
139 | ss.ios.deployment_target = '11.0'
140 | end
141 |
142 | s.subspec 'full-gpl-lts' do |ss|
143 | ss.source_files = 'Classes/**/*'
144 | ss.public_header_files = 'Classes/**/*.h'
145 |
146 | ss.dependency 'mobile-ffmpeg-full-gpl', '4.4.LTS'
147 | ss.ios.deployment_target = '9.3'
148 | end
149 |
150 | end
151 |
--------------------------------------------------------------------------------
/lib/completed_ffmpeg_execution.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | /// Represents a completed FFmpeg execution.
21 | class CompletedFFmpegExecution {
22 | int executionId;
23 | int returnCode;
24 |
25 | CompletedFFmpegExecution(this.executionId, this.returnCode);
26 | }
27 |
--------------------------------------------------------------------------------
/lib/ffmpeg_execution.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | /// Represents an ongoing FFmpeg execution.
21 | class FFmpegExecution {
22 | int executionId;
23 | DateTime startTime;
24 | String command;
25 |
26 | FFmpegExecution(
27 | {required this.command,
28 | required this.executionId,
29 | required this.startTime});
30 | }
31 |
--------------------------------------------------------------------------------
/lib/log.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | class Log {
21 | int executionId;
22 | int level;
23 | String message;
24 |
25 | Log(this.executionId, this.level, this.message);
26 | }
27 |
--------------------------------------------------------------------------------
/lib/log_level.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | class LogLevel {
21 | ///
22 | /// This log level is used to specify logs printed to stderr by ffmpeg.
23 | /// Logs that has this level are not filtered and always redirected.
24 | static const int AV_LOG_STDERR = -16;
25 |
26 | /// Print no output.
27 | static const int AV_LOG_QUIET = -8;
28 |
29 | /// Something went really wrong and we will crash now.
30 | static const int AV_LOG_PANIC = 0;
31 |
32 | /// Something went wrong and recovery is not possible.
33 | /// For example, no header was found for a format which depends
34 | /// on headers or an illegal combination of parameters is used.
35 | static const int AV_LOG_FATAL = 8;
36 |
37 | /// Something went wrong and cannot losslessly be recovered.
38 | /// However, not all future data is affected.
39 | static const int AV_LOG_ERROR = 16;
40 |
41 | /// Something somehow does not look correct. This may or may not
42 | /// lead to problems. An example would be the use of '-vstrict -2'.
43 | static const int AV_LOG_WARNING = 24;
44 |
45 | /// int Standard information.
46 | static const int AV_LOG_INFO = 32;
47 |
48 | /// Detailed information.
49 | static const int AV_LOG_VERBOSE = 40;
50 |
51 | /// Stuff which is only useful for libav* developers.
52 | static const int AV_LOG_DEBUG = 48;
53 |
54 | /// Extremely verbose debugging, useful for libav* development.
55 | static const int AV_LOG_TRACE = 56;
56 |
57 | /// Returns log level string from int
58 | static String levelToString(int level) {
59 | switch (level) {
60 | case LogLevel.AV_LOG_TRACE:
61 | return "TRACE";
62 | case LogLevel.AV_LOG_DEBUG:
63 | return "DEBUG";
64 | case LogLevel.AV_LOG_VERBOSE:
65 | return "VERBOSE";
66 | case LogLevel.AV_LOG_INFO:
67 | return "INFO";
68 | case LogLevel.AV_LOG_WARNING:
69 | return "WARNING";
70 | case LogLevel.AV_LOG_ERROR:
71 | return "ERROR";
72 | case LogLevel.AV_LOG_FATAL:
73 | return "FATAL";
74 | case LogLevel.AV_LOG_PANIC:
75 | return "PANIC";
76 | case LogLevel.AV_LOG_STDERR:
77 | return "STDERR";
78 | case LogLevel.AV_LOG_QUIET:
79 | default:
80 | return "";
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/lib/media_information.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | import 'package:flutter_ffmpeg/stream_information.dart';
21 |
22 | class MediaInformation {
23 | Map? _allProperties;
24 |
25 | /// Creates a new [MediaInformation] instance
26 | MediaInformation(this._allProperties);
27 |
28 | /// Returns all streams
29 | List? getStreams() {
30 | List list =
31 | List.empty(growable: true);
32 | var streamList;
33 |
34 | if (_allProperties == null) {
35 | streamList = List.empty(growable: true);
36 | } else {
37 | streamList = _allProperties!["streams"];
38 | }
39 |
40 | if (streamList != null) {
41 | streamList.forEach((element) {
42 | list.add(new StreamInformation(element));
43 | });
44 | }
45 |
46 | return list;
47 | }
48 |
49 | /// Returns all media properties in a map or null if no media properties are found
50 | Map? getMediaProperties() {
51 | if (_allProperties == null) {
52 | return Map();
53 | } else {
54 | return _allProperties!["format"];
55 | }
56 | }
57 |
58 | /// Returns all properties in a map or null if no properties are found
59 | Map getAllProperties() {
60 | return _allProperties!;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/statistics.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | class Statistics {
21 | int executionId;
22 | int videoFrameNumber;
23 | double videoFps;
24 | double videoQuality;
25 | int size;
26 | int time;
27 | double bitrate;
28 | double speed;
29 |
30 | Statistics(this.executionId, this.videoFrameNumber, this.videoFps,
31 | this.videoQuality, this.size, this.time, this.bitrate, this.speed);
32 | }
33 |
--------------------------------------------------------------------------------
/lib/stream_information.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 Taner Sener
3 | *
4 | * This file is part of FlutterFFmpeg.
5 | *
6 | * FlutterFFmpeg is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * FlutterFFmpeg is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with FlutterFFmpeg. If not, see .
18 | */
19 |
20 | class StreamInformation {
21 | Map _allProperties;
22 |
23 | /// Creates a new [StreamInformation] instance
24 | StreamInformation(this._allProperties);
25 |
26 | /// Returns all properties in a map or null if no properties are found
27 | Map getAllProperties() {
28 | return _allProperties;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_ffmpeg
2 | description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
3 | version: 0.4.2
4 | homepage: https://github.com/tanersener/flutter-ffmpeg
5 |
6 | environment:
7 | sdk: '>=2.12.0 <3.0.0'
8 | flutter: ">=1.10.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 |
14 | dev_dependencies:
15 | path_provider: ^2.0.2
16 | path: ^1.8.0
17 |
18 | flutter:
19 | plugin:
20 | platforms:
21 | android:
22 | package: com.arthenica.flutter.ffmpeg
23 | pluginClass: FlutterFFmpegPlugin
24 | ios:
25 | pluginClass: FlutterFFmpegPlugin
26 |
--------------------------------------------------------------------------------