├── .github
├── FUNDING.yml
└── ISSUE_TEMPLATE
│ ├── bug_template.md
│ ├── enhancement_template.md
│ ├── help_template.md
│ └── other_template.md
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android
├── .gitignore
├── .idea
│ ├── .name
│ ├── caches
│ │ └── build_file_checksums.ser
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── misc.xml
│ ├── modules.xml
│ └── runConfigurations.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── kotlin
│ └── com
│ └── example
│ └── flutter_video_compress
│ ├── FFmpegCommander.kt
│ ├── FlutterVideoCompressPlugin.kt
│ ├── ThumbnailUtility.kt
│ ├── Utility.kt
│ └── VideoQuality.kt
├── doc
├── chinese.md
├── images
│ ├── logo.svg
│ └── preview.gif
└── japanese.md
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── flutter_video_compress_example
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── 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
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── 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
│ │ │ └── WorkspaceSettings.xcsettings
│ └── Runner
│ │ ├── AppDelegate.swift
│ │ ├── 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
│ │ └── Runner-Bridging-Header.h
├── lib
│ ├── main.dart
│ └── my-theme.dart
├── pubspec.lock
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── flutter_video_compress.iml
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── AvController.swift
│ ├── FlutterVideoCompressPlugin.h
│ ├── FlutterVideoCompressPlugin.m
│ ├── SwiftFlutterVideoCompressPlugin.swift
│ └── Utility.swift
└── flutter_video_compress.podspec
├── lib
├── android
│ └── media_metadata_retriever.dart
├── flutter_video_compress.dart
├── model
│ └── media_info.dart
└── src
│ ├── flutter_video_compress.dart
│ ├── observable_builder.dart
│ └── video_quality.dart
├── pubspec.yaml
└── test
└── flutter_video_compress_test.dart
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [TenkaiRuri]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: tenkairuri
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | custom: https://paypal.me/tenkairuri
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "🙀 Bug report"
3 | about: Report bugs to improve this library
4 | title: "[Bug]"
5 | labels: bug
6 | assignees: TenkaiRuri
7 |
8 | ---
9 |
10 | ### Description
11 | Provide detailed contextual explanations to help us understand
12 |
13 | ### Platform
14 | IOS|Android|Both|Other
15 |
16 | ### Backtracking step (if has)
17 | 1. open application
18 | 2. touch somewhere 5 times
19 | 3. crash
20 |
21 | ### Code Example (if has)
22 | ```dart
23 | void main() {
24 |
25 | }
26 | ```
27 |
28 | ### Expected solution
29 | Expect description of the solution
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/enhancement_template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "😳 enhancement"
3 | about: New features or enhancements you want
4 | title: "[Feature]"
5 | labels: enhancement
6 | assignees: TenkaiRuri
7 |
8 | ---
9 |
10 | ### Description
11 | Provide detailed contextual explanations to help us understand
12 |
13 | ### Platform
14 | IOS|Android|Both|Other
15 |
16 | ### Code Example (if has)
17 | ```dart
18 | void main() {
19 |
20 | }
21 | ```
22 |
23 | ### Expected solution
24 | Expect description of the solution
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/help_template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "🤔 Help wanted"
3 | about: Need some help
4 | title: "[help]"
5 | labels: "help wanted"
6 | assignees: TenkaiRuri
7 |
8 | ---
9 |
10 | ### Description
11 | Provide detailed contextual explanations to help us understand
12 |
13 | ### Platform
14 | IOS|Android|Both|Other
15 |
16 | ### Code Example (if has)
17 | ```dart
18 | void main() {
19 |
20 | }
21 | ```
22 |
23 | ### Expected solution
24 | Expect description of the solution
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/other_template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "🌏 Other"
3 | about: other
4 | title: ""
5 | labels: ""
6 | assignees: TenkaiRuri
7 |
8 | ---
9 |
10 | ### Description
11 | Provide detailed contextual explanations to help us understand
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .atom/
3 | .idea/
4 | .idea/*
5 | .vscode/
6 | .vscode/*
7 |
8 | .packages
9 | .pub/
10 | .dart_tool/
11 | pubspec.lock
12 |
13 | Podfile
14 | Podfile.lock
15 | Pods/
16 | .symlinks/
17 | **/Flutter/App.framework/
18 | **/Flutter/Flutter.framework/
19 | **/Flutter/Generated.xcconfig
20 | **/Flutter/flutter_assets/
21 | ServiceDefinitions.json
22 | xcuserdata/
23 | gen/
24 |
25 | local.properties
26 | keystore.properties
27 | .gradle/
28 | gradlew
29 | gradlew.bat
30 | gradle-wrapper.jar
31 | *.iml
32 |
33 | GeneratedPluginRegistrant.h
34 | GeneratedPluginRegistrant.m
35 | GeneratedPluginRegistrant.java
36 | build/
37 | .flutter-plugins
--------------------------------------------------------------------------------
/.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: 8661d8aecd626f7f57ccbcb735553edc05a2e713
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.3.7+8
2 | * docs change pubspec.yaml description
3 |
4 | ## 0.3.7+7
5 | * docs Better English documentation
6 |
7 | ## 0.3.7+6
8 | * docs better example
9 |
10 | ## 0.3.7+5
11 | * docs better example
12 |
13 | ## 0.3.7+4
14 | * docs better example
15 |
16 | ## 0.3.7+3
17 | * docs deleteAllCache api return whether the boolean value is deleted
18 |
19 | ## 0.3.7+2
20 | * fix `ios` path problem
21 | * fix `ios` deleteAllCache functional repair
22 | * docs `document` deleteAllCache api description
23 | * fix `android` ffmpeg keep metadata orientation by -noautorotate command
24 |
25 | ## 0.3.7+1
26 | * feat `both` add api deleteAllCache to delete cache, please do not put other things in the folder of this plugin, it will be cleared
27 |
28 | ## 0.3.7
29 | * **Breaking change** `both` rename startCompress to compressVideo, stopCompress to cancelCompression
30 | * feat `both` compressVideo api add params { int startTime, int duration, bool includeAudio, int frameRate }
31 | * feat `both` getMediaInfo returns add orientation
32 | * fix `android` getMediaInfo returns height and width flipped
33 | * fix `android` different quality compression size
34 | * style `example` fixed some problems with example
35 |
36 | ## 0.3.6
37 | * docs `document` ios swift version documents of how to fix Regift
38 | * refactor `android` reduce compressing time
39 | * refactor `android` remove updateProgress redundant code to improve performance
40 | * refactor `dart` change all print to debugPrint to improve release performance
41 |
42 | ## 0.3.5+4
43 | * Support latest dependencies
44 |
45 | ## 0.3.5+3
46 | * update documents
47 |
48 | ## 0.3.5+2
49 | * increased swift version to 5.0
50 |
51 | ## 0.3.5+1
52 | * reduced frame rate to 15 on android, use milliseconds for duration
53 |
54 | ## 0.3.5
55 | * **Breaking change** fix The old version does not scale the Android compressed video,
56 | That is, the startCompress `quality` properties has no effect.
57 | * fix IOS pod dependency `Regift`
58 | * feat enum `VideoQuality` add DefaultQuality, startCompress `quality` Use the default properties of the old version
59 | * fix IOS convertVideoToGif file Suffix
60 | * docs update example
61 |
62 | ## 0.3.4+1
63 | * fix example lost the IOS pod dependency `Regift`
64 |
65 | ## 0.3.4
66 | * docs update convertVideoToGif associated document
67 | * feat Created API for conversion of video files to gif with start time
68 | and duration parameters.
69 | For Android ffmpeg is used for conversion and for iOS Regift
70 | library is used.
71 | Also, added an example on how to use the API within example project.
72 |
73 | ## 0.3.3+3
74 | * docs update README.md
75 |
76 | ## 0.3.3+2
77 | * docs update README.md
78 |
79 | ## 0.3.3+1
80 | * docs docs folder to doc
81 |
82 | ## 0.3.3
83 | * docs international documentation increased
84 |
85 | ## 0.3.2
86 | * fix unsubscribe excess stream error
87 |
88 | ## 0.3.1
89 | * fix compressed video is null
90 | * fix Bad state: Stream has already been listened to
91 | * fix Video same address cannot be compressed for the second time
92 |
93 | ## 0.3.0
94 | * **Breaking change** refactor ios anderoid lib code
95 | * feat add getThumbnailWithFile and getMediaInfo api
96 | * fix android private folder permissions cause possible problems
97 | * change startCompress api return type
98 |
99 | ## 0.2.0+9
100 | * doc update document
101 |
102 | ## 0.2.0+8
103 | * doc update document
104 |
105 | ## 0.2.0+7
106 | * fix ios getThumbnail path error
107 | * Better tips of Compressing error
108 | * **Breaking change** start Compress api add whether to cancel the parameter
109 |
110 | ## 0.2.0+6
111 | * chore update swift 5
112 |
113 | ## 0.2.0+5
114 | * fix: fix the wrong Usage
115 |
116 | ## 0.2.0+4
117 | * fix: add some suggestions for reduce apk size
118 | * **Breaking change** compressVideo api renamed startCompress
119 | * feat: add stopCompress api
120 | * update README.md
121 |
122 | ## 0.2.0+3
123 | no change
124 |
125 | ## 0.2.0+2
126 | * fix: IOS fix import AVFoundation
127 |
128 | ## 0.2.0+1
129 | * fix: Android video type changed h264
130 |
131 | ## 0.2.0
132 | * **Breaking change** Migrated dependent sdk, Upgraded kotlin and gradle versions
133 | * fix: solved Android transcoding video error code 8
134 | * update example/README.md
135 |
136 | ## 0.1.7
137 | * docs: Better Readme
138 |
139 | ## 0.1.6
140 | * docs: Better documentation
141 |
142 | ## 0.1.5
143 | * CHANGELOG.md
144 | * fix: supported x86 arm64-v8a armeabi armeabi-v7a
145 |
146 | ## 0.1.4
147 | * fix:gradle config 'compile' replaced with 'implementation'
148 |
149 | ## 0.1.3
150 | * fix: fix ios 4.2 version bugs
151 |
152 | ## 0.1.2
153 | * fix: fix ios build swift_version
154 |
155 | ## 0.1.1
156 | * chore: update `AndroidX`
157 |
158 | ## 0.1.0
159 |
160 | * feature: get vedio thumbnail
161 | * feature: compress vedio
162 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Amami Ruri
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # flutter_video_compress
6 |
7 | Generate a new path by compressed video, Choose to keep the source video or delete it by a parameter. Get video thumbnail from a video path and provide video information. Easy to deal with compressed video. Considering reduce application size is not using FFmpeg in IOS.
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |

18 |
19 |
20 | ## languages
21 | [English](https://github.com/TenkaiRuri/flutter_video_compress#flutter_video_compress) [简体中文](https://github.com/TenkaiRuri/flutter_video_compress/blob/master/doc/chinese.md#flutter_video_compress) [日本語](https://github.com/TenkaiRuri/flutter_video_compress/blob/master/doc/japanese.md#flutter_video_compress)
22 |
23 | ## Usage
24 |
25 | **Installing**
26 | add [flutter_video_compress](https://pub.dartlang.org/packages/flutter_video_compress) as a dependency in your pubspec.yaml file.
27 | ```yaml
28 | dependencies:
29 | flutter_video_compress: ^0.3.x
30 | ```
31 |
32 | **Create an instance**
33 | ```dart
34 | final _flutterVideoCompress = FlutterVideoCompress();
35 | ```
36 |
37 | **Get thumbnail from video path**
38 | ```dart
39 | final uint8list = await _flutterVideoCompress.getThumbnail(
40 | file.path,
41 | quality: 50, // default(100)
42 | position: -1 // default(-1)
43 | );
44 | ```
45 |
46 | **Get thumbnail file from video path**
47 | ```dart
48 | final thumbnailFile = await _flutterVideoCompress.getThumbnailWithFile(
49 | file.path,
50 | quality: 50, // default(100)
51 | position: -1 // default(-1)
52 | );
53 | ```
54 |
55 | **Convert video to a gif**
56 | ```dart
57 | final file = await _flutterVideoCompress.convertVideoToGif(
58 | videoFile.path,
59 | startTime: 0, // default(0)
60 | duration: 5, // default(-1)
61 | // endTime: -1 // default(-1)
62 | );
63 | debugPrint(file.path);
64 | ```
65 |
66 | **Get media information**
67 | > only support video now.
68 |
69 | ```dart
70 | final info = await _flutterVideoCompress.getMediaInfo(file.path);
71 | debugPrint(info.toJson().toString());
72 | ```
73 |
74 | **Compression Video**
75 | > Compatible with IOS, Android and Web after compression.
76 |
77 | ```dart
78 | final info = await _flutterVideoCompress.compressVideo(
79 | file.path,
80 | quality: VideoQuality.DefaultQuality, // default(VideoQuality.DefaultQuality)
81 | deleteOrigin: false, // default(false)
82 | );
83 | debugPrint(info.toJson().toString());
84 | ```
85 |
86 | **Check Compressing state**
87 | ```dart
88 | _flutterVideoCompress.isCompressing
89 | ```
90 |
91 | **Stop compression**
92 | > Will print InterruptedException in android, but not affect to use.
93 |
94 | ```dart
95 | await _flutterVideoCompress.cancelCompression()
96 | ```
97 |
98 | **delete all cache files**
99 | > Delete all files generated by this will delete all files located at 'flutter_video_compress', you shoule ought to know what are you doing.
100 |
101 | ```dart
102 | await _flutterVideoCompress.deleteAllCache()
103 | ```
104 |
105 | **Subscribe the compression progress steam**
106 | ```dart
107 | class ... extends State {
108 | Subscription _subscription;
109 |
110 | @override
111 | void initState() {
112 | super.initState();
113 | _subscription =
114 | _flutterVideoCompress.compressProgress$.subscribe((progress) {
115 | debugPrint('progress: $progress');
116 | });
117 | }
118 |
119 | @override
120 | void dispose() {
121 | super.dispose();
122 | _subscription.unsubscribe();
123 | }
124 | }
125 | ```
126 |
127 | ## Methods
128 | |Functions|Parameters|Description|Returns|
129 | |--|--|--|--|
130 | |getThumbnail|String `path`[video path], int `quality`(1-100)[thumbnail quality], int `position`[Get a thumbnail from video position]|get thumbnail from video `path`|`Future`|
131 | |getThumbnailWithFile|String `path`[video path], int `quality`(1-100)[thumbnail quality], int `position`[Get a thumbnail from video position]|get thumbnail file from video `path`|`Future`|
132 | |convertVideoToGif|String `path`[video path], int `startTime`(from 0 start)[convert video to gif start time], int `endTime`[convert video to gif end time], int `duration`[convert video to gif duration from start time]|convert video to gif from video `path`|`Future`|
133 | |getMediaInfo|String `path`[video path]|get media information from video `path`|`Future`|
134 | |compressVideo|String `path`[video path], VideoQuality `quality`[compressed video quality], bool `deleteOrigin`[delete the origin video], int `startTime`[compression video start time], int `duration`[compression video duration from start time], bool `includeAudio`[is include audio in compressed video], int `frameRate`[compressed video frame rate]|compression video at origin video `path`|`Future`|
135 | |cancelCompression|`none`|cancel compressing|`Future`|
136 | |deleteAllCache|`none`|Delete all files generated by 'flutter_video_compress' will delete all files located at 'flutter_video_compress'|`Future`|
137 |
138 | ## Subscriptions
139 | |Subscriptions|Description|Stream|
140 | |--|--|--|
141 | |compressProgress$|Subscribe the compression progress steam|double `progress`|
142 |
143 | ## Notice
144 | If your application is significantly larger after using the plugin, you can reduce the application size in the following way:
145 |
146 | * exclude `x86` related files (`./assets`)
147 |
148 | * This library not use `ffprobe`, only used `ffmpeg`, but the application still has `ffprobe`, so you will need to exclude (`asssets/arm` or `assets/x86`)
149 |
150 | add this config in `build.gradle`:
151 | * __Don't use__ `ignoreAssetsPattern "!x86"` in debug mode, will crash on the simulator
152 |
153 | ```gradle
154 | android {
155 | ...
156 | // Reduce your application size with this configuration
157 | aaptOptions {
158 | ignoreAssetsPattern "!x86:!*ffprobe"
159 | }
160 |
161 | buildTypes {
162 | ...
163 | }
164 | ```
165 | [look detail](https://github.com/bravobit/FFmpeg-Android/wiki/Reduce-APK-File-Size#exclude-architecture)
166 |
167 | ## Questions
168 |
169 | If your application is not enabled `AndroidX`, you will need to add the following code to the last line of the `android/build.gradle` file.
170 | ```groovy
171 | rootProject.allprojects {
172 | subprojects {
173 | project.configurations.all {
174 | resolutionStrategy.eachDependency { details ->
175 | if (details.requested.group == 'androidx.core' && !details.requested.name.contains('androidx')) {
176 | details.useVersion "1.0.1"
177 | }
178 | }
179 | }
180 | }
181 | }
182 | ```
183 |
184 | If your application not support swift, you need to add the following code in `ios/Podfile`.
185 | ```ruby
186 | target 'Runner' do
187 | use_frameworks! # <--- add this
188 | ...
189 | end
190 | ```
191 |
192 | [look detail](https://github.com/flutter/flutter/issues/16049#issuecomment-382629492)
193 |
194 |
195 | If your application never used a swift plugin before, maybe you would meet the error, you need to add the following code in `ios/Podfile`.
196 | > The 'Pods-Runner' target has transitive dependencies that include static binaries
197 |
198 | ```ruby
199 | pre_install do |installer|
200 | # workaround for https://github.com/CocoaPods/CocoaPods/issues/3289
201 | Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {}
202 | end
203 | ```
204 |
205 | **If the above method not work, you report bug of the error repository. The reason is「can't support swift」**
206 |
207 | [look detail](https://github.com/flutter/flutter/issues/16049#issue-309580132)
208 |
209 | if show error of `Regift`
210 |
211 | > - `Regift` does not specify a Swift version and none of the targets (`Runner`) integrating it have the `SWIFT_VERSION` attribute set. Please contact the author or set the `SWIFT_VERSION` attribute in at least one of the targets that integrate this pod.
212 |
213 | ```ruby
214 | pre_install do |installer|
215 | installer.analysis_result.specifications.each do |s|
216 | if s.name == 'Regift'
217 | s.swift_version = '4.0'
218 | end
219 | end
220 | end
221 |
222 | post_install do |installer|
223 | installer.pods_project.targets.each do |target|
224 | target.build_configurations.each do |config|
225 | config.build_settings['ENABLE_BITCODE'] = 'NO'
226 | end
227 | end
228 | end
229 | ```
230 |
231 | ## Contribute
232 |
233 | Contributions are always welcome!
234 |
--------------------------------------------------------------------------------
/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/.idea/.name:
--------------------------------------------------------------------------------
1 | flutter_video_compress
--------------------------------------------------------------------------------
/android/.idea/caches/build_file_checksums.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/android/.idea/caches/build_file_checksums.ser
--------------------------------------------------------------------------------
/android/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/android/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
--------------------------------------------------------------------------------
/android/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/android/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'com.example.flutter_video_compress'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.3.31'
6 | repositories {
7 | google()
8 | jcenter()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:3.4.1'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | rootProject.allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 | }
22 | }
23 |
24 | apply plugin: 'com.android.library'
25 | apply plugin: 'kotlin-android'
26 |
27 | android {
28 | compileSdkVersion 28
29 |
30 | sourceSets {
31 | main.java.srcDirs += 'src/main/kotlin'
32 | }
33 | defaultConfig {
34 | minSdkVersion 16
35 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
36 | }
37 | lintOptions {
38 | disable 'InvalidPackage'
39 | }
40 | }
41 |
42 | dependencies {
43 | implementation 'nl.bravobit:android-ffmpeg:1.1.7'
44 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
45 | }
46 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
3 | org.gradle.jvmargs=-Xmx1536M
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri May 17 15:24:59 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'flutter_video_compress'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/com/example/flutter_video_compress/FFmpegCommander.kt:
--------------------------------------------------------------------------------
1 | package com.example.flutter_video_compress
2 |
3 | import android.content.Context
4 | import io.flutter.plugin.common.BinaryMessenger
5 | import io.flutter.plugin.common.MethodChannel
6 | import nl.bravobit.ffmpeg.ExecuteBinaryResponseHandler
7 | import nl.bravobit.ffmpeg.FFmpeg
8 | import nl.bravobit.ffmpeg.FFtask
9 | import java.io.File
10 |
11 | class FFmpegCommander(private val context: Context, private val channelName: String) {
12 | private var stopCommand = false
13 | private var ffTask: FFtask? = null
14 | private val utility = Utility(channelName)
15 | private var totalTime: Long = 0
16 |
17 |
18 | fun compressVideo(path: String, quality: VideoQuality, deleteOrigin: Boolean,
19 | startTime: Int?, duration: Int? = null, includeAudio: Boolean?,
20 | frameRate: Int?, result: MethodChannel.Result,
21 | messenger: BinaryMessenger) {
22 |
23 | val ffmpeg = FFmpeg.getInstance(context)
24 |
25 | if (!ffmpeg.isSupported) {
26 | return result.error(channelName, "FlutterVideoCompress Error",
27 | "ffmpeg isn't supported this platform")
28 | }
29 |
30 | val dir = context.getExternalFilesDir("flutter_video_compress")
31 |
32 | if (dir != null && !dir.exists()) dir.mkdirs()
33 |
34 | val file = File(dir, path.substring(path.lastIndexOf("/")))
35 | utility.deleteFile(file)
36 |
37 | val scale = quality.getScaleString()
38 | val cmdArray = mutableListOf("-noautorotate", "-i", path, "-vcodec", "h264", "-crf", "28", "-movflags", "+faststart", "-vf", "scale=$scale:-2", "-preset:v", "ultrafast", "-b:v", "1000k")
39 |
40 | // Add high bitrate for the highest quality
41 | // if (quality.isHighQuality()) {
42 | // cmdArray.addAll(listOf("-preset:v", "ultrafast", "-b:v", "1000k"))
43 | // }
44 |
45 | if (startTime != null) {
46 | cmdArray.add("-ss")
47 | cmdArray.add(startTime.toString())
48 |
49 | if (duration != null) {
50 | cmdArray.add("-t")
51 | cmdArray.add(duration.toString())
52 | }
53 | }
54 |
55 | if (includeAudio != null && !includeAudio) {
56 | cmdArray.add("-an")
57 | }
58 |
59 | if (frameRate != null) {
60 | cmdArray.add("-r")
61 | cmdArray.add(frameRate.toString())
62 | }
63 |
64 | cmdArray.add(file.absolutePath)
65 |
66 | this.ffTask = ffmpeg.execute(cmdArray.toTypedArray(),
67 | object : ExecuteBinaryResponseHandler() {
68 | override fun onProgress(message: String) {
69 | notifyProgress(message, messenger)
70 |
71 | if (stopCommand) {
72 | print("FlutterVideoCompress: Video compression has stopped")
73 | stopCommand = false
74 | val json = utility.getMediaInfoJson(context, path)
75 | json.put("isCancel", true)
76 | result.success(json.toString())
77 | totalTime = 0
78 | ffTask?.killRunningProcess()
79 | }
80 | }
81 |
82 | override fun onFinish() {
83 | val json = utility.getMediaInfoJson(context, file.absolutePath)
84 | json.put("isCancel", false)
85 | result.success(json.toString())
86 | if (deleteOrigin) {
87 | File(path).delete()
88 | }
89 | totalTime = 0
90 | }
91 | })
92 | }
93 |
94 | private fun isLandscapeImage(orientation: Int) = orientation != 90 && orientation != 270
95 |
96 |
97 | fun convertVideoToGif(path: String, startTime: Long = 0, endTime: Long, duration: Long,
98 | result: MethodChannel.Result, messenger: BinaryMessenger) {
99 | var gifDuration = 0L
100 | if (endTime > 0) {
101 | if (startTime > endTime) {
102 | result.error(channelName, "FlutterVideoCompress Error",
103 | "startTime should be greater than startTime")
104 | } else {
105 | gifDuration = (endTime - startTime)
106 | }
107 | } else {
108 | gifDuration = duration
109 | }
110 |
111 | val ffmpeg = FFmpeg.getInstance(context)
112 |
113 | if (!ffmpeg.isSupported) {
114 | return result.error(channelName, "FlutterVideoCompress Error",
115 | "ffmpeg is not supported this platform")
116 | }
117 |
118 | val dir = context.getExternalFilesDir("flutter_video_compress")
119 |
120 | if (dir != null && !dir.exists()) dir.mkdirs()
121 |
122 |
123 | val file = File(dir, utility.getFileNameWithGifExtension(path))
124 | utility.deleteFile(file)
125 |
126 | val cmd = arrayOf("-i", path, "-ss", startTime.toString(), "-t", gifDuration.toString(),
127 | "-vf", "scale=640:-2", "-r", "15", file.absolutePath)
128 |
129 | this.ffTask = ffmpeg.execute(cmd, object : ExecuteBinaryResponseHandler() {
130 | override fun onProgress(message: String) {
131 | notifyProgress(message, messenger)
132 | }
133 |
134 | override fun onFinish() {
135 | result.success(file.absolutePath)
136 | }
137 | })
138 | }
139 |
140 | private fun notifyProgress(message: String, messenger: BinaryMessenger) {
141 | if ("Duration" in message) {
142 | val reg = Regex("""Duration: ((\d{2}:){2}\d{2}\.\d{2}).*""")
143 | val totalTimeStr = message.replace(reg, "$1")
144 | totalTime = utility.timeStrToTimestamp(totalTimeStr.trim())
145 | }
146 |
147 | if ("frame=" in message) {
148 | try {
149 | val reg = Regex("""frame.*time=((\d{2}:){2}\d{2}\.\d{2}).*""")
150 | val totalTimeStr = message.replace(reg, "$1")
151 | val time = utility.timeStrToTimestamp(totalTimeStr.trim())
152 | MethodChannel(messenger, channelName)
153 | .invokeMethod("updateProgress", ((time.toDouble() / totalTime.toDouble()) * 100).toString())
154 | } catch (e: Exception) {
155 | print(e.stackTrace)
156 | }
157 | }
158 |
159 | MethodChannel(messenger, channelName).invokeMethod("updateProgress", message)
160 | }
161 |
162 | fun cancelCompression() {
163 | if (ffTask != null && !ffTask!!.isProcessCompleted) {
164 | stopCommand = true
165 | }
166 | }
167 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/com/example/flutter_video_compress/FlutterVideoCompressPlugin.kt:
--------------------------------------------------------------------------------
1 | package com.example.flutter_video_compress
2 |
3 | import io.flutter.plugin.common.MethodCall
4 | import io.flutter.plugin.common.MethodChannel
5 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler
6 | import io.flutter.plugin.common.MethodChannel.Result
7 | import io.flutter.plugin.common.PluginRegistry.Registrar
8 |
9 |
10 | class FlutterVideoCompressPlugin : MethodCallHandler {
11 |
12 | private val channelName = "flutter_video_compress"
13 | private val utility = Utility(channelName)
14 | private var ffmpegCommander: FFmpegCommander? = null
15 |
16 | companion object {
17 | private lateinit var reg: Registrar
18 |
19 | @JvmStatic
20 | fun registerWith(registrar: Registrar) {
21 | val channel = MethodChannel(registrar.messenger(), "flutter_video_compress")
22 | channel.setMethodCallHandler(FlutterVideoCompressPlugin())
23 | reg = registrar
24 | }
25 | }
26 |
27 | override fun onMethodCall(call: MethodCall, result: Result) {
28 | initFfmpegCommanderIfNeeded()
29 | when (call.method) {
30 | "getThumbnail" -> {
31 | val path = call.argument("path")!!
32 | val quality = call.argument("quality")!!
33 | val position = call.argument("position")!!.toLong()
34 | ThumbnailUtility(channelName).getThumbnail(path, quality, position, result)
35 | }
36 | "getThumbnailWithFile" -> {
37 | val path = call.argument("path")!!
38 | val quality = call.argument("quality")!!
39 | val position = call.argument("position")!!.toLong()
40 | ThumbnailUtility(channelName).getThumbnailWithFile(reg.context(), path, quality,
41 | position, result)
42 | }
43 | "getMediaInfo" -> {
44 | val path = call.argument("path")!!
45 | result.success(utility.getMediaInfoJson(reg.context(), path).toString())
46 | }
47 | "compressVideo" -> {
48 | val path = call.argument("path")!!
49 | val quality = call.argument("quality")!!
50 | val deleteOrigin = call.argument("deleteOrigin")!!
51 | val startTime = call.argument("startTime")
52 | val duration = call.argument("duration")
53 | val includeAudio = call.argument("includeAudio")
54 | val frameRate = call.argument("frameRate")
55 |
56 | ffmpegCommander?.compressVideo(path, VideoQuality.from(quality), deleteOrigin,
57 | startTime, duration, includeAudio, frameRate, result, reg.messenger())
58 | }
59 | "cancelCompression" -> {
60 | ffmpegCommander?.cancelCompression()
61 | result.success("")
62 | }
63 | "convertVideoToGif" -> {
64 | val path = call.argument("path")!!
65 | val startTime = call.argument("startTime")!!.toLong()
66 | val endTime = call.argument("endTime")!!.toLong()
67 | val duration = call.argument("duration")!!.toLong()
68 |
69 | ffmpegCommander?.convertVideoToGif(path, startTime, endTime, duration, result,
70 | reg.messenger())
71 | }
72 | "deleteAllCache" -> {
73 | utility.deleteAllCache(reg.context(), result)
74 | }
75 | else -> result.notImplemented()
76 | }
77 | }
78 |
79 | private fun initFfmpegCommanderIfNeeded() {
80 | if (ffmpegCommander == null) {
81 | ffmpegCommander = FFmpegCommander(reg.context(), channelName)
82 | }
83 | }
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/com/example/flutter_video_compress/ThumbnailUtility.kt:
--------------------------------------------------------------------------------
1 | package com.example.flutter_video_compress
2 |
3 | import android.content.Context
4 | import android.graphics.Bitmap
5 | import io.flutter.plugin.common.MethodChannel
6 | import java.io.ByteArrayOutputStream
7 | import java.io.File
8 | import java.io.IOException
9 |
10 | class ThumbnailUtility(channelName: String) {
11 | private val utility = Utility(channelName)
12 |
13 | fun getThumbnail(path: String, quality: Int, position: Long, result: MethodChannel.Result) {
14 | val bmp = utility.getBitmap(path, position, result)
15 |
16 | val stream = ByteArrayOutputStream()
17 | bmp.compress(Bitmap.CompressFormat.JPEG, quality, stream)
18 | val byteArray = stream.toByteArray()
19 | bmp.recycle()
20 | result.success(byteArray.toList().toByteArray())
21 | }
22 |
23 | fun getThumbnailWithFile(context: Context, path: String, quality: Int, position: Long,
24 | result: MethodChannel.Result) {
25 | val bmp = utility.getBitmap(path, position, result)
26 |
27 | val dir = context.getExternalFilesDir("flutter_video_compress")
28 |
29 | if (dir != null && !dir.exists()) dir.mkdirs()
30 |
31 | val file = File(dir, path.substring(path.lastIndexOf('/'),
32 | path.lastIndexOf('.')) + ".jpg")
33 | utility.deleteFile(file)
34 |
35 | val stream = ByteArrayOutputStream()
36 | bmp.compress(Bitmap.CompressFormat.JPEG, quality, stream)
37 | val byteArray = stream.toByteArray()
38 |
39 | try {
40 | file.createNewFile()
41 | file.writeBytes(byteArray)
42 | } catch (e: IOException) {
43 | e.printStackTrace()
44 | }
45 |
46 | bmp.recycle()
47 |
48 | result.success(file.absolutePath)
49 | }
50 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/com/example/flutter_video_compress/Utility.kt:
--------------------------------------------------------------------------------
1 | package com.example.flutter_video_compress
2 |
3 | import android.content.Context
4 | import android.graphics.Bitmap
5 | import android.media.MediaMetadataRetriever
6 | import android.net.Uri
7 | import android.os.Build
8 | import io.flutter.plugin.common.MethodChannel
9 | import org.json.JSONObject
10 | import java.io.File
11 |
12 | class Utility(private val channelName: String) {
13 |
14 | fun isLandscapeImage(orientation: Int) = orientation != 90 && orientation != 270
15 |
16 | fun deleteFile(file: File) {
17 | if (file.exists()) {
18 | file.delete()
19 | }
20 | }
21 |
22 | fun timeStrToTimestamp(time: String): Long {
23 | val timeArr = time.split(":")
24 | val hour = Integer.parseInt(timeArr[0])
25 | val min = Integer.parseInt(timeArr[1])
26 | val secArr = timeArr[2].split(".")
27 | val sec = Integer.parseInt(secArr[0])
28 | val mSec = Integer.parseInt(secArr[1])
29 |
30 | val timeStamp = (hour * 3600 + min * 60 + sec) * 1000 + mSec
31 | return timeStamp.toLong()
32 | }
33 |
34 | fun getMediaInfoJson(context: Context, path: String): JSONObject {
35 | val file = File(path)
36 | val retriever = MediaMetadataRetriever()
37 |
38 | retriever.setDataSource(context, Uri.fromFile(file))
39 |
40 | val durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
41 | val title = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE) ?: ""
42 | val author = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_AUTHOR) ?: ""
43 | val widthStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
44 | val heightStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
45 | val duration = java.lang.Long.parseLong(durationStr)
46 | var width = java.lang.Long.parseLong(widthStr)
47 | var height = java.lang.Long.parseLong(heightStr)
48 | val filesize = file.length()
49 | val orientation = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
50 | retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)
51 | } else {
52 | null
53 | }
54 | val ori = orientation?.toIntOrNull()
55 | if (ori != null && isLandscapeImage(ori)) {
56 | val tmp = width
57 | width = height
58 | height = tmp
59 | }
60 |
61 | retriever.release()
62 |
63 | val json = JSONObject()
64 |
65 | json.put("path", path)
66 | json.put("title", title)
67 | json.put("author", author)
68 | json.put("width", width)
69 | json.put("height", height)
70 | json.put("duration", duration)
71 | json.put("filesize", filesize)
72 | if (ori != null) {
73 | json.put("orientation", ori)
74 | }
75 |
76 | return json
77 | }
78 |
79 | fun getBitmap(path: String, position: Long, result: MethodChannel.Result): Bitmap {
80 | var bitmap: Bitmap? = null
81 | val retriever = MediaMetadataRetriever()
82 |
83 | try {
84 | retriever.setDataSource(path)
85 | bitmap = retriever.getFrameAtTime(position, MediaMetadataRetriever.OPTION_CLOSEST_SYNC)
86 | } catch (ex: IllegalArgumentException) {
87 | result.error(channelName, "Assume this is a corrupt video file", null)
88 | } catch (ex: RuntimeException) {
89 | result.error(channelName, "Assume this is a corrupt video file", null)
90 | } finally {
91 | try {
92 | retriever.release()
93 | } catch (ex: RuntimeException) {
94 | result.error(channelName, "Ignore failures while cleaning up", null)
95 | }
96 | }
97 |
98 | if (bitmap == null) result.success(emptyArray())
99 |
100 | val width = bitmap!!.width
101 | val height = bitmap.height
102 | val max = Math.max(width, height)
103 | if (max > 512) {
104 | val scale = 512f / max
105 | val w = Math.round(scale * width)
106 | val h = Math.round(scale * height)
107 | bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true)
108 | }
109 |
110 | return bitmap!!
111 | }
112 |
113 | fun getFileNameWithGifExtension(path: String): String {
114 | val file = File(path)
115 | var fileName = ""
116 | val gifSuffix = "gif"
117 | val dotGifSuffix = ".$gifSuffix"
118 |
119 | if (file.exists()) {
120 | val name = file.name
121 | fileName = name.replaceAfterLast(".", gifSuffix)
122 |
123 | if (!fileName.endsWith(dotGifSuffix)) {
124 | fileName += dotGifSuffix
125 | }
126 | }
127 | return fileName
128 | }
129 |
130 | fun deleteAllCache(context: Context, result: MethodChannel.Result) {
131 | val dir = context.getExternalFilesDir("flutter_video_compress")
132 | result.success(dir?.deleteRecursively())
133 | }
134 | }
--------------------------------------------------------------------------------
/android/src/main/kotlin/com/example/flutter_video_compress/VideoQuality.kt:
--------------------------------------------------------------------------------
1 | package com.example.flutter_video_compress
2 |
3 | enum class VideoQuality(val value : Int) {
4 | DefaultQuality(0),
5 | LowQuality(1),
6 | MediumQuality(2),
7 | HighestQuality(3);
8 |
9 | companion object {
10 | fun from(findValue: Int): VideoQuality = values().first { it.value == findValue }
11 | }
12 |
13 | fun getScaleString(): String = when (this) {
14 | DefaultQuality, LowQuality -> "192"
15 | MediumQuality -> "480"
16 | HighestQuality -> "1280"
17 | }
18 |
19 | fun notDefault(): Boolean = this != DefaultQuality
20 |
21 | fun isHighQuality(): Boolean = this == HighestQuality
22 | }
--------------------------------------------------------------------------------
/doc/chinese.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # flutter_video_compress
6 |
7 | 压缩视频生成新路径,选择保留源视频或删除它。从视频路径获取视频缩略图并提供视频信息。方便的处理压缩视频。考虑减少应用程序大小不在IOS中使用FFmpeg
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |

18 |
19 |
20 | ## 语言
21 | [简体中文](https://github.com/TenkaiRuri/flutter_video_compress/blob/master/doc/chinese.md#flutter_video_compress) [日本語](https://github.com/TenkaiRuri/flutter_video_compress/blob/master/doc/japanese.md#flutter_video_compress) [English](https://github.com/TenkaiRuri/flutter_video_compress#flutter_video_compress)
22 |
23 | ## 用户指南
24 |
25 | **安装**
26 | 添加[flutter_video_compress](https://pub.dartlang.org/packages/flutter_video_compress)到你的pubspec.yaml文件中.
27 | ```yaml
28 | dependencies:
29 | flutter_video_compress: ^0.3.x
30 | ```
31 |
32 | **创建一个实例**
33 | ```dart
34 | final _flutterVideoCompress = FlutterVideoCompress();
35 | ```
36 |
37 | **获取缩略图**
38 | ```dart
39 | final uint8list = await _flutterVideoCompress.getThumbnail(
40 | file.path,
41 | quality: 50, // 默认(100)
42 | position: -1 // 默认(-1)
43 | );
44 | ```
45 |
46 | **获取缩略图文件**
47 | ```dart
48 | final thumbnailFile = await _flutterVideoCompress.getThumbnailWithFile(
49 | file.path,
50 | quality: 50, // 默认(100)
51 | position: -1 // 默认(-1)
52 | );
53 | ```
54 |
55 | **转换视频为gif**
56 | ```dart
57 | final file = await _flutterVideoCompress.convertVideoToGif(
58 | videoFile.path,
59 | startTime: 0, // 默认(0)
60 | duration: 5, // 默认(-1)
61 | // endTime: -1 // 默认(-1)
62 | );
63 | debugPrint(file.path);
64 | ```
65 |
66 | **获取媒体信息**
67 | > 现在支持持视频
68 |
69 | ```dart
70 | final info = await _flutterVideoCompress.getMediaInfo(file.path);
71 | debugPrint(info.toJson().toString());
72 | ```
73 |
74 | **压缩视频**
75 | > 移动端平台及web平台视频格式是兼容的
76 |
77 | ```dart
78 | final info = await _flutterVideoCompress.compressVideo(
79 | file.path,
80 | quality: VideoQuality.DefaultQuality, // 默认(VideoQuality.DefaultQuality)
81 | deleteOrigin: false, // 默认(false)
82 | );
83 | debugPrint(info.toJson().toString());
84 | ```
85 |
86 | **检查压缩状态**
87 | ```dart
88 | _flutterVideoCompress.isCompressing
89 | ```
90 |
91 | **停止压缩**
92 | > Android 会打印 InterruptedException 错误, 但是不会影响使用
93 |
94 | ```dart
95 | await _flutterVideoCompress.cancelCompression()
96 | ```
97 |
98 | **删除所有缓存文件**
99 | > 删除这个插件生成的所有文件,我想你应该知道你在做什么
100 |
101 | ```dart
102 | await _flutterVideoCompress.deleteAllCache()
103 | ```
104 |
105 | **订阅转换流**
106 | ```dart
107 | class ... extends State {
108 | Subscription _subscription;
109 |
110 | @override
111 | void initState() {
112 | super.initState();
113 | _subscription =
114 | _flutterVideoCompress.compressProgress$.subscribe((progress) {
115 | debugPrint('progress: $progress');
116 | });
117 | }
118 |
119 | @override
120 | void dispose() {
121 | super.dispose();
122 | _subscription.unsubscribe();
123 | }
124 | }
125 | ```
126 |
127 | ## 方法
128 | |Functions|Parameters|Description|Returns|
129 | |--|--|--|--|
130 | |getThumbnail|String `path`[视频路径], int `quality`(1-100)[缩略图质量], int `position`[通过位置(时间)获取是缩略图]|从`path`获取缩略图|`Future`|
131 | |getThumbnailWithFile|String `path`[视频路径], int `quality`(1-100)[缩略图质量], int `position`[通过位置(时间)获取是缩略图]|从`path`获取缩略图文件`path`|`Future`|
132 | |convertVideoToGif|String `path`[视频路径], int `startTime`(from 0 start)[转换视频为gif的开始时间], int `endTime`[转换视频为gif的结束时间], int `duration`[从开始时间转换视频为gif的持续时间]|从`path`将视频转换为gif|`Future`|
133 | |getMediaInfo|String `path`[视频路径]|从`path`获取到视频的媒体信息|`Future`|
134 | |compressVideo|String `path`[视频路径], VideoQuality `quality`[压缩视频质量], bool `deleteOrigin`[是否删除源视频文件], int `startTime`[压缩视频的开始时间], int `duration`[压缩视频的持续时间], bool `includeAudio`[是否包含音频], int `frameRate`[压缩视频帧率]|从`path`压缩视频|`Future`|
135 | |cancelCompression|`none`|取消压缩|`Future`|
136 | |deleteAllCache|`none`|删除位于'flutter_video_compress'的所有文件|`Future`|
137 |
138 | ## 订阅流
139 | |Subscriptions|Description|Stream|
140 | |--|--|--|
141 | |compressProgress$|订阅压缩状态流|double `progress`|
142 |
143 | ## 注意
144 | 如果你的程序在使用这个插件后体积明显增大,可以使用下面的方法优化你的体积
145 |
146 | * 将`x86`相关文件排除 (`./assets`)
147 |
148 | * 这个库不使用`ffprobe`,只使用`ffmpeg`,但你的应用中仍然有`ffprobe`,你需要排除他 (`asssets/arm` or `assets/x86`)
149 |
150 | 将此配置添加到`build.gradle`文件中
151 | **不要在Android模拟器**上使用**`ignoreAssetsPattern'!X86'`,将会崩溃
152 |
153 | ```gradle
154 | android {
155 | ...
156 |
157 | // 减小应用程序大小的配置
158 | aaptOptions {
159 | ignoreAssetsPattern "!x86:!*ffprobe"
160 | }
161 |
162 | buildTypes {
163 | ...
164 | }
165 | ```
166 | [详情](https://github.com/bravobit/FFmpeg-Android/wiki/Reduce-APK-File-Size#exclude-architecture)
167 |
168 | ## 问题列表
169 |
170 | 如果你的APP未开启`AndroidX`,你需要将下面代码添加到你的`android/build.gradle`文件里。
171 | ```groovy
172 | rootProject.allprojects {
173 | subprojects {
174 | project.configurations.all {
175 | resolutionStrategy.eachDependency { details ->
176 | if (details.requested.group == 'androidx.core' && !details.requested.name.contains('androidx')) {
177 | details.useVersion "1.0.1"
178 | }
179 | }
180 | }
181 | }
182 | }
183 | ```
184 |
185 | 如果你的程序不支持swift,你需要在`ios/Podfile`文件加入下例代码。
186 | ```ruby
187 | target 'Runner' do
188 | use_frameworks! # <--- add this
189 | ...
190 | end
191 | ```
192 |
193 | [详情](https://github.com/flutter/flutter/issues/16049#issuecomment-382629492)
194 |
195 | 如果你的程序从未使用过swift的插件,你或许会遇到下面的报错,这时候你需要添加下例代码`ios/Podfile`。
196 | > The 'Pods-Runner' target has transitive dependencies that include static binaries
197 |
198 | ```ruby
199 | pre_install do |installer|
200 | # workaround for https://github.com/CocoaPods/CocoaPods/issues/3289
201 | Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {}
202 | end
203 | ```
204 |
205 | **如果上述方法不管用,你可以去报错的仓库提issue,理由是 不能支持swift(Can't support swift)**
206 |
207 | [详情](https://github.com/flutter/flutter/issues/16049#issue-309580132)
208 |
209 | 如果遇到`Regift`报错你可能需要将你的配置文件改成这样
210 |
211 | > - `Regift` does not specify a Swift version and none of the targets (`Runner`) integrating it have the `SWIFT_VERSION` attribute set. Please contact the author or set the `SWIFT_VERSION` attribute in at least one of the targets that integrate this pod.
212 |
213 | ```ruby
214 | pre_install do |installer|
215 | installer.analysis_result.specifications.each do |s|
216 | if s.name == 'Regift'
217 | s.swift_version = '4.0'
218 | end
219 | end
220 | end
221 |
222 | post_install do |installer|
223 | installer.pods_project.targets.each do |target|
224 | target.build_configurations.each do |config|
225 | config.build_settings['ENABLE_BITCODE'] = 'NO'
226 | end
227 | end
228 | end
229 | ```
230 |
231 | ## 贡献指南
232 |
233 | 欢迎每一个贡献
234 |
--------------------------------------------------------------------------------
/doc/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
39 |
--------------------------------------------------------------------------------
/doc/images/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/doc/images/preview.gif
--------------------------------------------------------------------------------
/doc/japanese.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # flutter_video_compress
6 | 新しいパスで圧縮ビデオを生成します。ソースビデオを保持するか、パラメータで削除するかを選択します。ビデオのサムネイルを取得し、ビデオ情報を提供します。圧縮ビデオを扱うのが簡単です。アプリケーションサイズを縮小の考えIOSはFFmpegを使用しません。
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |

17 |
18 |
19 | ## ランゲージ
20 | [日本語](https://github.com/TenkaiRuri/flutter_video_compress/blob/master/doc/japanese.md#flutter_video_compress) [English](https://github.com/TenkaiRuri/flutter_video_compress#flutter_video_compress) [简体中文](https://github.com/TenkaiRuri/flutter_video_compress/blob/master/doc/chinese.md#flutter_video_compress)
21 |
22 | ## 使用方法
23 |
24 | **インストール**
25 | pubspec.yamlファイルに依存ライブラリーとして[flutter_video_compress](https://pub.dartlang.org/packages/flutter_video_compress)を追加します。
26 |
27 | ```yaml
28 | dependencies:
29 | flutter_video_compress: ^0.3.x
30 | ```
31 |
32 | **インスタンスを作成**
33 | ```dart
34 | final _flutterVideoCompress = FlutterVideoCompress();
35 | ```
36 |
37 | **ビデオファイルでサムネイルを取得する**
38 | ```dart
39 | final uint8list = await _flutterVideoCompress.getThumbnail(
40 | file.path,
41 | quality: 50, // default(100)
42 | position: -1 // default(-1)
43 | );
44 | ```
45 |
46 | **ビデオファイルでサムネイルファイルを取得する**
47 | ```dart
48 | final thumbnailFile = await _flutterVideoCompress.getThumbnailWithFile(
49 | file.path,
50 | quality: 50, // default(100)
51 | position: -1 // default(-1)
52 | );
53 | ```
54 |
55 | **ビデオをGIFに変換する**
56 | ```dart
57 | final file = await _flutterVideoCompress.convertVideoToGif(
58 | videoFile.path,
59 | startTime: 0, // default(0)
60 | duration: 5, // default(-1)
61 | // endTime: -1 // default(-1)
62 | );
63 | debugPrint(file.path);
64 | ```
65 |
66 | **メディア情報を入手する**
67 | > 現在はビデオのみをサポートしています
68 |
69 | ```dart
70 | final info = await _flutterVideoCompress.getMediaInfo(file.path);
71 | debugPrint(info.toJson().toString());
72 | ```
73 |
74 | **ビデオを圧縮する**
75 | > 圧縮後のアンドロイドおよびウェブとIOSの互換性ある
76 |
77 | ```dart
78 | final info = await _flutterVideoCompress.compressVideo(
79 | file.path,
80 | quality: VideoQuality.DefaultQuality, // default(VideoQuality.DefaultQuality)
81 | deleteOrigin: false, // default(false)
82 | );
83 | debugPrint(info.toJson().toString());
84 | ```
85 |
86 | **圧縮の状態を確認する**
87 | ```dart
88 | _flutterVideoCompress.isCompressing
89 | ```
90 |
91 | **圧縮を停止する**
92 | > アンドロイドはInterruptedExceptionを出力します、使用には影響しません
93 |
94 | ```dart
95 | await _flutterVideoCompress.cancelCompression()
96 | ```
97 |
98 | **すべてのキャッシュファイルを削除する**
99 | > このプラグインによって生成されたすべてのファイルを削除します、何をしているか知っておくべきだと思います。
100 |
101 | ```dart
102 | await _flutterVideoCompress.deleteAllCache()
103 | ```
104 |
105 | **変換の進行状況をサブスクリプションする**
106 | ```dart
107 | class ... extends State {
108 | Subscription _subscription;
109 |
110 | @override
111 | void initState() {
112 | super.initState();
113 | _subscription =
114 | _flutterVideoCompress.compressProgress$.subscribe((progress) {
115 | print('progress: $progress');
116 | });
117 | }
118 |
119 | @override
120 | void dispose() {
121 | super.dispose();
122 | _subscription.unsubscribe();
123 | }
124 | }
125 | ```
126 |
127 | ## メソッド
128 | |Functions|Parameters|Description|Returns|
129 | |--|--|--|--|
130 | |getThumbnail|String `path`[ビデオのパス], int `quality`(1-100)[サムネイルの品質], int `position`[ビデオ位置からサムネイルを取得します]|動画の`path`からサムネイルを取得します|`Future`|
131 | |getThumbnailWithFile|String `path`[ビデオのパス], int `quality`(1-100)[サムネイルの品質], int `position`[ビデオ位置からサムネイルを取得します]|動画の`path`からサムネイルファイルを取得します|`Future`|
132 | |convertVideoToGif|String `path`[ビデオのパス], int `startTime`(from 0 start)[ビデオをgifに変換するの始まり時間], int `endTime`[動画をgifに変換するの終了時間], int `duration`[始まり時間から動画の期間]|動画を`path`からgifに変換します|`Future`|
133 | |getMediaInfo|String `path`[ビデオのパス]|ビデオの`path`から情報を取得します|`Future`|
134 | |compressVideo|String `path`[ビデオのパス], VideoQuality `quality`[圧縮ビデオ品質], bool `deleteOrigin`[元の動画を削除する], int `startTime`[圧縮ビデオの開始時間], int `duration`[始まり時間から動画の期間], bool `includeAudio`[圧縮ビデオに音声を含める], int `frameRate`[圧縮ビデオフレームレート]|`path`からビデオを圧縮する|`Future`|
135 | |cancelCompression|`none`|圧縮をキャンセル|`Future`|
136 | |deleteAllCache|`none`|'flutter_video_compress'によって生成されたすべてのファイルを削除すると、 'flutter_video_compress'にあるすべてのファイルが削除されます|`Future`|
137 |
138 | ## サブスクリプション
139 | |Subscriptions|Description|Stream|
140 | |--|--|--|
141 | |compressProgress$|変換の進行状況をサブスクリプションする|double `progress`|
142 |
143 | ## お知らせ
144 | プラグインをインポートした後にアプリケーションのサイズが大幅に増加していることがわかった場合は、以下の処理方法が考えられます。
145 |
146 | * `x86`関連ファイルを排除する (`./assets`)
147 |
148 | このライブラリは `ffprobe`を使わず、` ffmpeg`だけを使いますが、あなたのアプリケーションにはまだ `ffprobe`があるので、あなたはそれを除外する必要があります。
149 |
150 | * このライブラリーは `ffprobe`を使用なし、`ffmpeg`だけを使いします、だけどアプリケーションにはまだ `ffprobe`があるので、それを除外する必要があります (`asssets/arm` or `assets/x86`)
151 |
152 | この設定を`build.gradle`ファイルに追加してください
153 | アンドロイドエミュレータで `ignoreAssetsPattern "!x86"`を**使わないでください**、クラッシュします
154 |
155 | ```gradle
156 | android {
157 | ...
158 | // Reduce your application size with this configuration
159 | aaptOptions {
160 | ignoreAssetsPattern "!x86:!*ffprobe"
161 | }
162 |
163 | buildTypes {
164 | ...
165 | }
166 | ```
167 |
168 | [詳細説明](https://github.com/bravobit/FFmpeg-Android/wiki/Reduce-APK-File-Size#exclude-architecture)
169 |
170 | ## 問題リスト
171 |
172 | アプリケーションが`AndroidX`に対応していない場合は、`android¥build.gradle`ファイルの最後の行に以下のコードを追加する必要があります。
173 | ```gradle
174 | rootProject.allprojects {
175 | subprojects {
176 | project.configurations.all {
177 | resolutionStrategy.eachDependency { details ->
178 | if (details.requested.group == 'androidx.core' && !details.requested.name.contains('androidx')) {
179 | details.useVersion "1.0.1"
180 | }
181 | }
182 | }
183 | }
184 | }
185 | ```
186 |
187 | アプリケーションがswiftをサポートしていない場合は、`ios¥Podfile`に以下のコードを追加する必要があります
188 | ```ruby
189 | target 'Runner' do
190 | use_frameworks! # <--- add this
191 | ...
192 | end
193 | ```
194 |
195 | [詳細説明](https://github.com/flutter/flutter/issues/16049#issuecomment-382629492)
196 |
197 |
198 | アプリケーションがswiftをサポートしていない場合は、`ios¥Podfile`に以下のコードを追加する必要があります
199 | > The 'Pods-Runner' target has transitive dependencies that include static binaries
200 |
201 | ```ruby
202 | pre_install do |installer|
203 | # workaround for https://github.com/CocoaPods/CocoaPods/issues/3289
204 | Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {}
205 | end
206 | ```
207 |
208 | **上記の方法が機能しない場合は、そのエラーリポジトリのバグを報告する。理由は「このリポジトリはswiftをサポートしません」、ありがとうございます**
209 |
210 | [詳細説明](https://github.com/flutter/flutter/issues/16049#issue-309580132)
211 |
212 | `Regift`のエラーを表示するなら
213 |
214 | > - `Regift` does not specify a Swift version and none of the targets (`Runner`) integrating it have the `SWIFT_VERSION` attribute set. Please contact the author or set the `SWIFT_VERSION` attribute in at least one of the targets that integrate this pod.
215 |
216 | ```ruby
217 | pre_install do |installer|
218 | installer.analysis_result.specifications.each do |s|
219 | if s.name == 'Regift'
220 | s.swift_version = '4.0'
221 | end
222 | end
223 | end
224 |
225 | post_install do |installer|
226 | installer.pods_project.targets.each do |target|
227 | target.build_configurations.each do |config|
228 | config.build_settings['ENABLE_BITCODE'] = 'NO'
229 | end
230 | end
231 | end
232 | ```
233 |
234 | ## 貢献ガイドライン
235 |
236 | 貢献はいつでも歓迎です!
237 |
238 |
--------------------------------------------------------------------------------
/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 |
30 | # Android related
31 | **/android/**/gradle-wrapper.jar
32 | **/android/.gradle
33 | **/android/captures/
34 | **/android/gradlew
35 | **/android/gradlew.bat
36 | **/android/local.properties
37 | **/android/**/GeneratedPluginRegistrant.java
38 |
39 | # iOS/XCode related
40 | **/ios/**/*.mode1v3
41 | **/ios/**/*.mode2v3
42 | **/ios/**/*.moved-aside
43 | **/ios/**/*.pbxuser
44 | **/ios/**/*.perspectivev3
45 | **/ios/**/*sync/
46 | **/ios/**/.sconsign.dblite
47 | **/ios/**/.tags*
48 | **/ios/**/.vagrant/
49 | **/ios/**/DerivedData/
50 | **/ios/**/Icon?
51 | **/ios/**/Pods/
52 | **/ios/**/.symlinks/
53 | **/ios/**/profile
54 | **/ios/**/xcuserdata
55 | **/ios/.generated/
56 | **/ios/Flutter/App.framework
57 | **/ios/Flutter/Flutter.framework
58 | **/ios/Flutter/Generated.xcconfig
59 | **/ios/Flutter/app.flx
60 | **/ios/Flutter/app.zip
61 | **/ios/Flutter/flutter_assets/
62 | **/ios/ServiceDefinitions.json
63 | **/ios/Runner/GeneratedPluginRegistrant.*
64 |
65 | # Exceptions to above rules.
66 | !**/ios/**/default.mode1v3
67 | !**/ios/**/default.mode2v3
68 | !**/ios/**/default.pbxuser
69 | !**/ios/**/default.perspectivev3
70 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
71 |
--------------------------------------------------------------------------------
/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: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flutter_video_compress_example
2 |
3 | ```dart
4 | import 'dart:io';
5 | import 'dart:async';
6 | import 'dart:typed_data';
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_video_compress/flutter_video_compress.dart';
10 | import 'package:image_picker/image_picker.dart' show ImagePicker, ImageSource;
11 |
12 | void main() => runApp(MyApp());
13 |
14 | class MyApp extends StatefulWidget {
15 | @override
16 | _MyAppState createState() => _MyAppState();
17 | }
18 |
19 | class _MyAppState extends State {
20 | final _flutterVideoCompress = FlutterVideoCompress();
21 | Uint8List _image;
22 | File _imageFile;
23 | Subscription _subscription;
24 |
25 | @override
26 | void initState() {
27 | super.initState();
28 | _subscription =
29 | _flutterVideoCompress.compressProgress$.subscribe((progress) {
30 | debugPrint('progress: $progress');
31 | });
32 | }
33 |
34 | @override
35 | void dispose() {
36 | super.dispose();
37 | _subscription.unsubscribe();
38 | }
39 |
40 | Future _videoPicker() async {
41 | if (mounted) {
42 | final file = await ImagePicker.pickVideo(source: ImageSource.camera);
43 | if (file?.path != null) {
44 | final thumbnail = await _flutterVideoCompress.getThumbnail(
45 | file.path,
46 | quality: 50,
47 | position: -1,
48 | );
49 |
50 | setState(() {
51 | _image = thumbnail;
52 | });
53 |
54 | final resultFile = await _flutterVideoCompress.getThumbnailWithFile(
55 | file.path,
56 | quality: 50,
57 | position: -1,
58 | );
59 | debugPrint(resultFile.path);
60 |
61 | assert(resultFile.existsSync());
62 |
63 | debugPrint('file Exists: ${resultFile.existsSync()}');
64 |
65 | final MediaInfo info = await _flutterVideoCompress.compressVideo(
66 | file.path,
67 | deleteOrigin: true,
68 | quality: VideoQuality.LowQuality,
69 | );
70 | debugPrint(info.toJson().toString());
71 | }
72 | }
73 | }
74 |
75 | Future _cancelCompression() async {
76 | await _flutterVideoCompress.cancelCompression();
77 | }
78 |
79 | Future _getMediaInfo() async {
80 | if (mounted) {
81 | final file = await ImagePicker.pickVideo(source: ImageSource.gallery);
82 | if (file?.path != null) {
83 | final info = await _flutterVideoCompress.getMediaInfo(file.path);
84 | debugPrint(info.toJson().toString());
85 | }
86 | }
87 | }
88 |
89 | Future _convertVideoToGif() async {
90 | if (mounted) {
91 | final file = await ImagePicker.pickVideo(source: ImageSource.gallery);
92 | if (file?.path != null) {
93 | final info = await _flutterVideoCompress.convertVideoToGif(
94 | file.path,
95 | startTime: 0,
96 | duration: 5,
97 | );
98 |
99 | debugPrint(info.path);
100 | setState(() {
101 | _imageFile = info;
102 | });
103 | }
104 | }
105 | }
106 |
107 | List _builColumnChildren() {
108 | // dart 2.3 before
109 | final _list = [
110 | FlatButton(child: Text('take video'), onPressed: _videoPicker),
111 | FlatButton(child: Text('stop compress'), onPressed: _cancelCompression),
112 | FlatButton(child: Text('get media info'), onPressed: _getMediaInfo),
113 | FlatButton(
114 | child: Text('convert video to gif'),
115 | onPressed: _convertVideoToGif,
116 | ),
117 | ];
118 | if (_imageFile != null) {
119 | _list.add(Flexible(child: Image.file(_imageFile)));
120 | } else if (_image != null) {
121 | _list.add(Flexible(child: Image.memory(_image)));
122 | }
123 | return _list;
124 |
125 | // dart 2.3
126 | // final _list = [
127 | // FlatButton(child: Text('take video'), onPressed: _videoPicker),
128 | // FlatButton(child: Text('stop compress'), onPressed: _cancelCompression),
129 | // FlatButton(child: Text('get media info'), onPressed: _getMediaInfo),
130 | // FlatButton(
131 | // child: Text('convert video to gif'),
132 | // onPressed: _convertVideoToGif,
133 | // ),
134 | // if (_imageFile != null)
135 | // Flexible(child: Image.file(_imageFile))
136 | // else
137 | // if (_image != null) Flexible(child: Image.memory(_image))
138 | // ];
139 | // return _list;
140 | }
141 |
142 | @override
143 | Widget build(BuildContext context) {
144 | return MaterialApp(
145 | home: Scaffold(
146 | appBar: AppBar(title: const Text('Plugin example app')),
147 | body: Column(
148 | crossAxisAlignment: CrossAxisAlignment.stretch,
149 | children: _builColumnChildren(),
150 | ),
151 | ),
152 | );
153 | }
154 | }
155 | ```
--------------------------------------------------------------------------------
/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 plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 28
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "com.example.flutter_video_compress_example"
42 | minSdkVersion 16
43 | targetSdkVersion 28
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
47 | }
48 |
49 | buildTypes {
50 | release {
51 | // TODO: Add your own signing config for the release build.
52 | // Signing with the debug keys for now, so `flutter run --release` works.
53 | signingConfig signingConfigs.debug
54 | }
55 | }
56 | }
57 |
58 | flutter {
59 | source '../..'
60 | }
61 |
62 | dependencies {
63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
64 | testImplementation 'junit:junit:4.12'
65 | androidTestImplementation 'androidx.test:runner:1.1.1'
66 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
67 | }
68 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
10 |
14 |
21 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/com/example/flutter_video_compress_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.flutter_video_compress_example
2 |
3 | import android.os.Bundle
4 |
5 | import io.flutter.app.FlutterActivity
6 | import io.flutter.plugins.GeneratedPluginRegistrant
7 |
8 | class MainActivity: FlutterActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | GeneratedPluginRegistrant.registerWith(this)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.31'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.4.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
3 | org.gradle.jvmargs=-Xmx1536M
4 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri May 17 10:22:14 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/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/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, '8.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | pod 'Regift'
8 |
9 | project 'Runner', {
10 | 'Debug' => :debug,
11 | 'Profile' => :release,
12 | 'Release' => :release,
13 | }
14 |
15 | def parse_KV_file(file, separator='=')
16 | file_abs_path = File.expand_path(file)
17 | if !File.exists? file_abs_path
18 | return [];
19 | end
20 | pods_ary = []
21 | skip_line_start_symbols = ["#", "/"]
22 | File.foreach(file_abs_path) { |line|
23 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
24 | plugin = line.split(pattern=separator)
25 | if plugin.length == 2
26 | podname = plugin[0].strip()
27 | path = plugin[1].strip()
28 | podpath = File.expand_path("#{path}", file_abs_path)
29 | pods_ary.push({:name => podname, :path => podpath});
30 | else
31 | puts "Invalid plugin specification: #{line}"
32 | end
33 | }
34 | return pods_ary
35 | end
36 |
37 | target 'Runner' do
38 | use_frameworks!
39 |
40 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
41 | # referring to absolute paths on developers' machines.
42 | system('rm -rf .symlinks')
43 | system('mkdir -p .symlinks/plugins')
44 |
45 | # Flutter Pods
46 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
47 | if generated_xcode_build_settings.empty?
48 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
49 | end
50 | generated_xcode_build_settings.map { |p|
51 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
52 | symlink = File.join('.symlinks', 'flutter')
53 | File.symlink(File.dirname(p[:path]), symlink)
54 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
55 | end
56 | }
57 |
58 | # Plugin Pods
59 | plugin_pods = parse_KV_file('../.flutter-plugins')
60 | plugin_pods.map { |p|
61 | symlink = File.join('.symlinks', 'plugins', p[:name])
62 | File.symlink(p[:path], symlink)
63 | pod p[:name], :path => File.join(symlink, 'ios')
64 | }
65 | end
66 |
67 | pre_install do |installer|
68 | installer.analysis_result.specifications.each do |s|
69 | if s.name == 'Regift'
70 | s.swift_version = '4.0'
71 | end
72 | end
73 | end
74 |
75 | post_install do |installer|
76 | installer.pods_project.targets.each do |target|
77 | target.build_configurations.each do |config|
78 | config.build_settings['ENABLE_BITCODE'] = 'NO'
79 | end
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - flutter_video_compress (0.3.0):
4 | - Flutter
5 | - Regift
6 | - image_picker (0.0.1):
7 | - Flutter
8 | - Regift (1.5.0)
9 | - video_player (0.0.1):
10 | - Flutter
11 |
12 | DEPENDENCIES:
13 | - Flutter (from `.symlinks/flutter/ios-release`)
14 | - flutter_video_compress (from `.symlinks/plugins/flutter_video_compress/ios`)
15 | - image_picker (from `.symlinks/plugins/image_picker/ios`)
16 | - Regift
17 | - video_player (from `.symlinks/plugins/video_player/ios`)
18 |
19 | SPEC REPOS:
20 | https://github.com/cocoapods/specs.git:
21 | - Regift
22 |
23 | EXTERNAL SOURCES:
24 | Flutter:
25 | :path: ".symlinks/flutter/ios-release"
26 | flutter_video_compress:
27 | :path: ".symlinks/plugins/flutter_video_compress/ios"
28 | image_picker:
29 | :path: ".symlinks/plugins/image_picker/ios"
30 | video_player:
31 | :path: ".symlinks/plugins/video_player/ios"
32 |
33 | SPEC CHECKSUMS:
34 | Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a
35 | flutter_video_compress: 3e6c882fbf1be6d16e615c2aa2ac74e9d460749a
36 | image_picker: 16e5fec1fbc87fd3b297c53e4048521eaf17cd06
37 | Regift: d097afd401021f4f42b102ee125ceeb125a38caf
38 | video_player: 3964090a33353060ed7f58aa6427c7b4b208ec21
39 |
40 | PODFILE CHECKSUM: 596239def9fc038f4ca3b58962ce3ad0da67feb5
41 |
42 | COCOAPODS: 1.7.2
43 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0910DC4AAB319CB550545C57 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE7CD6F4DADA18DACEE24635 /* Pods_Runner.framework */; };
11 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
13 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
14 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
15 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
16 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
17 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
18 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
19 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
20 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXCopyFilesBuildPhase section */
24 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
25 | isa = PBXCopyFilesBuildPhase;
26 | buildActionMask = 2147483647;
27 | dstPath = "";
28 | dstSubfolderSpec = 10;
29 | files = (
30 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
31 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
32 | );
33 | name = "Embed Frameworks";
34 | runOnlyForDeploymentPostprocessing = 0;
35 | };
36 | /* End PBXCopyFilesBuildPhase section */
37 |
38 | /* Begin PBXFileReference section */
39 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
40 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
41 | 2F308078F8D8AEC04C19831E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
42 | 39702881BB56947DF75349A3 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
43 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
44 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
45 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
46 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
47 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
48 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
49 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
50 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; };
51 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
52 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
53 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
54 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
55 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
56 | DD54B8BDABAEF5DA187193CF /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
57 | EE7CD6F4DADA18DACEE24635 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
58 | /* End PBXFileReference section */
59 |
60 | /* Begin PBXFrameworksBuildPhase section */
61 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
62 | isa = PBXFrameworksBuildPhase;
63 | buildActionMask = 2147483647;
64 | files = (
65 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
66 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
67 | 0910DC4AAB319CB550545C57 /* Pods_Runner.framework in Frameworks */,
68 | );
69 | runOnlyForDeploymentPostprocessing = 0;
70 | };
71 | /* End PBXFrameworksBuildPhase section */
72 |
73 | /* Begin PBXGroup section */
74 | 149AEF9825D5C8DB20AEF947 /* Pods */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 2F308078F8D8AEC04C19831E /* Pods-Runner.debug.xcconfig */,
78 | DD54B8BDABAEF5DA187193CF /* Pods-Runner.release.xcconfig */,
79 | 39702881BB56947DF75349A3 /* Pods-Runner.profile.xcconfig */,
80 | );
81 | path = Pods;
82 | sourceTree = "";
83 | };
84 | 317CB9D2ED33047C6C9A54A1 /* Frameworks */ = {
85 | isa = PBXGroup;
86 | children = (
87 | EE7CD6F4DADA18DACEE24635 /* Pods_Runner.framework */,
88 | );
89 | name = Frameworks;
90 | sourceTree = "";
91 | };
92 | 9740EEB11CF90186004384FC /* Flutter */ = {
93 | isa = PBXGroup;
94 | children = (
95 | 3B80C3931E831B6300D905FE /* App.framework */,
96 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
97 | 9740EEBA1CF902C7004384FC /* Flutter.framework */,
98 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
99 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
100 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
101 | );
102 | name = Flutter;
103 | sourceTree = "";
104 | };
105 | 97C146E51CF9000F007C117D = {
106 | isa = PBXGroup;
107 | children = (
108 | 9740EEB11CF90186004384FC /* Flutter */,
109 | 97C146F01CF9000F007C117D /* Runner */,
110 | 97C146EF1CF9000F007C117D /* Products */,
111 | 149AEF9825D5C8DB20AEF947 /* Pods */,
112 | 317CB9D2ED33047C6C9A54A1 /* Frameworks */,
113 | );
114 | sourceTree = "";
115 | };
116 | 97C146EF1CF9000F007C117D /* Products */ = {
117 | isa = PBXGroup;
118 | children = (
119 | 97C146EE1CF9000F007C117D /* Runner.app */,
120 | );
121 | name = Products;
122 | sourceTree = "";
123 | };
124 | 97C146F01CF9000F007C117D /* Runner */ = {
125 | isa = PBXGroup;
126 | children = (
127 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
128 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
129 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
130 | 97C147021CF9000F007C117D /* Info.plist */,
131 | 97C146F11CF9000F007C117D /* Supporting Files */,
132 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
133 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
134 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
135 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
136 | );
137 | path = Runner;
138 | sourceTree = "";
139 | };
140 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
141 | isa = PBXGroup;
142 | children = (
143 | );
144 | name = "Supporting Files";
145 | sourceTree = "";
146 | };
147 | /* End PBXGroup section */
148 |
149 | /* Begin PBXNativeTarget section */
150 | 97C146ED1CF9000F007C117D /* Runner */ = {
151 | isa = PBXNativeTarget;
152 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
153 | buildPhases = (
154 | 658D95BB1A5175F5E0460993 /* [CP] Check Pods Manifest.lock */,
155 | 9740EEB61CF901F6004384FC /* Run Script */,
156 | 97C146EA1CF9000F007C117D /* Sources */,
157 | 97C146EB1CF9000F007C117D /* Frameworks */,
158 | 97C146EC1CF9000F007C117D /* Resources */,
159 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
160 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
161 | 600D16CF5758BF2E958F6D05 /* [CP] Embed Pods Frameworks */,
162 | );
163 | buildRules = (
164 | );
165 | dependencies = (
166 | );
167 | name = Runner;
168 | productName = Runner;
169 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
170 | productType = "com.apple.product-type.application";
171 | };
172 | /* End PBXNativeTarget section */
173 |
174 | /* Begin PBXProject section */
175 | 97C146E61CF9000F007C117D /* Project object */ = {
176 | isa = PBXProject;
177 | attributes = {
178 | LastUpgradeCheck = 1020;
179 | ORGANIZATIONNAME = "The Chromium Authors";
180 | TargetAttributes = {
181 | 97C146ED1CF9000F007C117D = {
182 | CreatedOnToolsVersion = 7.3.1;
183 | LastSwiftMigration = 1020;
184 | };
185 | };
186 | };
187 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
188 | compatibilityVersion = "Xcode 3.2";
189 | developmentRegion = en;
190 | hasScannedForEncodings = 0;
191 | knownRegions = (
192 | en,
193 | Base,
194 | );
195 | mainGroup = 97C146E51CF9000F007C117D;
196 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
197 | projectDirPath = "";
198 | projectRoot = "";
199 | targets = (
200 | 97C146ED1CF9000F007C117D /* Runner */,
201 | );
202 | };
203 | /* End PBXProject section */
204 |
205 | /* Begin PBXResourcesBuildPhase section */
206 | 97C146EC1CF9000F007C117D /* Resources */ = {
207 | isa = PBXResourcesBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
211 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
212 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
213 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
214 | );
215 | runOnlyForDeploymentPostprocessing = 0;
216 | };
217 | /* End PBXResourcesBuildPhase section */
218 |
219 | /* Begin PBXShellScriptBuildPhase section */
220 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
221 | isa = PBXShellScriptBuildPhase;
222 | buildActionMask = 2147483647;
223 | files = (
224 | );
225 | inputPaths = (
226 | );
227 | name = "Thin Binary";
228 | outputPaths = (
229 | );
230 | runOnlyForDeploymentPostprocessing = 0;
231 | shellPath = /bin/sh;
232 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
233 | };
234 | 600D16CF5758BF2E958F6D05 /* [CP] Embed Pods Frameworks */ = {
235 | isa = PBXShellScriptBuildPhase;
236 | buildActionMask = 2147483647;
237 | files = (
238 | );
239 | inputPaths = (
240 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
241 | "${PODS_ROOT}/../.symlinks/flutter/ios-release/Flutter.framework",
242 | "${BUILT_PRODUCTS_DIR}/Regift/Regift.framework",
243 | "${BUILT_PRODUCTS_DIR}/flutter_video_compress/flutter_video_compress.framework",
244 | "${BUILT_PRODUCTS_DIR}/image_picker/image_picker.framework",
245 | "${BUILT_PRODUCTS_DIR}/video_player/video_player.framework",
246 | );
247 | name = "[CP] Embed Pods Frameworks";
248 | outputPaths = (
249 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
250 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Regift.framework",
251 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_video_compress.framework",
252 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker.framework",
253 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework",
254 | );
255 | runOnlyForDeploymentPostprocessing = 0;
256 | shellPath = /bin/sh;
257 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
258 | showEnvVarsInLog = 0;
259 | };
260 | 658D95BB1A5175F5E0460993 /* [CP] Check Pods Manifest.lock */ = {
261 | isa = PBXShellScriptBuildPhase;
262 | buildActionMask = 2147483647;
263 | files = (
264 | );
265 | inputPaths = (
266 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
267 | "${PODS_ROOT}/Manifest.lock",
268 | );
269 | name = "[CP] Check Pods Manifest.lock";
270 | outputPaths = (
271 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
272 | );
273 | runOnlyForDeploymentPostprocessing = 0;
274 | shellPath = /bin/sh;
275 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
276 | showEnvVarsInLog = 0;
277 | };
278 | 9740EEB61CF901F6004384FC /* Run Script */ = {
279 | isa = PBXShellScriptBuildPhase;
280 | buildActionMask = 2147483647;
281 | files = (
282 | );
283 | inputPaths = (
284 | );
285 | name = "Run Script";
286 | outputPaths = (
287 | );
288 | runOnlyForDeploymentPostprocessing = 0;
289 | shellPath = /bin/sh;
290 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
291 | };
292 | /* End PBXShellScriptBuildPhase section */
293 |
294 | /* Begin PBXSourcesBuildPhase section */
295 | 97C146EA1CF9000F007C117D /* Sources */ = {
296 | isa = PBXSourcesBuildPhase;
297 | buildActionMask = 2147483647;
298 | files = (
299 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
300 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
301 | );
302 | runOnlyForDeploymentPostprocessing = 0;
303 | };
304 | /* End PBXSourcesBuildPhase section */
305 |
306 | /* Begin PBXVariantGroup section */
307 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
308 | isa = PBXVariantGroup;
309 | children = (
310 | 97C146FB1CF9000F007C117D /* Base */,
311 | );
312 | name = Main.storyboard;
313 | sourceTree = "";
314 | };
315 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
316 | isa = PBXVariantGroup;
317 | children = (
318 | 97C147001CF9000F007C117D /* Base */,
319 | );
320 | name = LaunchScreen.storyboard;
321 | sourceTree = "";
322 | };
323 | /* End PBXVariantGroup section */
324 |
325 | /* Begin XCBuildConfiguration section */
326 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
327 | isa = XCBuildConfiguration;
328 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
329 | buildSettings = {
330 | ALWAYS_SEARCH_USER_PATHS = NO;
331 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
332 | CLANG_ANALYZER_NONNULL = YES;
333 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
334 | CLANG_CXX_LIBRARY = "libc++";
335 | CLANG_ENABLE_MODULES = YES;
336 | CLANG_ENABLE_OBJC_ARC = YES;
337 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
338 | CLANG_WARN_BOOL_CONVERSION = YES;
339 | CLANG_WARN_COMMA = YES;
340 | CLANG_WARN_CONSTANT_CONVERSION = YES;
341 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
342 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
343 | CLANG_WARN_EMPTY_BODY = YES;
344 | CLANG_WARN_ENUM_CONVERSION = YES;
345 | CLANG_WARN_INFINITE_RECURSION = YES;
346 | CLANG_WARN_INT_CONVERSION = YES;
347 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
348 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
349 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
350 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
351 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
352 | CLANG_WARN_STRICT_PROTOTYPES = YES;
353 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
354 | CLANG_WARN_UNREACHABLE_CODE = YES;
355 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
356 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
357 | COPY_PHASE_STRIP = NO;
358 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
359 | ENABLE_NS_ASSERTIONS = NO;
360 | ENABLE_STRICT_OBJC_MSGSEND = YES;
361 | GCC_C_LANGUAGE_STANDARD = gnu99;
362 | GCC_NO_COMMON_BLOCKS = YES;
363 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
364 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
365 | GCC_WARN_UNDECLARED_SELECTOR = YES;
366 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
367 | GCC_WARN_UNUSED_FUNCTION = YES;
368 | GCC_WARN_UNUSED_VARIABLE = YES;
369 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
370 | MTL_ENABLE_DEBUG_INFO = NO;
371 | SDKROOT = iphoneos;
372 | SWIFT_VERSION = 5.0;
373 | TARGETED_DEVICE_FAMILY = "1,2";
374 | VALIDATE_PRODUCT = YES;
375 | };
376 | name = Profile;
377 | };
378 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
379 | isa = XCBuildConfiguration;
380 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
381 | buildSettings = {
382 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
383 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
384 | ENABLE_BITCODE = NO;
385 | FRAMEWORK_SEARCH_PATHS = (
386 | "$(inherited)",
387 | "$(PROJECT_DIR)/Flutter",
388 | );
389 | INFOPLIST_FILE = Runner/Info.plist;
390 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
391 | LIBRARY_SEARCH_PATHS = (
392 | "$(inherited)",
393 | "$(PROJECT_DIR)/Flutter",
394 | );
395 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterVideoCompressExample;
396 | PRODUCT_NAME = "$(TARGET_NAME)";
397 | SWIFT_VERSION = 5.0;
398 | VERSIONING_SYSTEM = "apple-generic";
399 | };
400 | name = Profile;
401 | };
402 | 97C147031CF9000F007C117D /* Debug */ = {
403 | isa = XCBuildConfiguration;
404 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
405 | buildSettings = {
406 | ALWAYS_SEARCH_USER_PATHS = NO;
407 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
408 | CLANG_ANALYZER_NONNULL = YES;
409 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
410 | CLANG_CXX_LIBRARY = "libc++";
411 | CLANG_ENABLE_MODULES = YES;
412 | CLANG_ENABLE_OBJC_ARC = YES;
413 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
414 | CLANG_WARN_BOOL_CONVERSION = YES;
415 | CLANG_WARN_COMMA = YES;
416 | CLANG_WARN_CONSTANT_CONVERSION = YES;
417 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
418 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
419 | CLANG_WARN_EMPTY_BODY = YES;
420 | CLANG_WARN_ENUM_CONVERSION = YES;
421 | CLANG_WARN_INFINITE_RECURSION = YES;
422 | CLANG_WARN_INT_CONVERSION = YES;
423 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
424 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
425 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
426 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
427 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
428 | CLANG_WARN_STRICT_PROTOTYPES = YES;
429 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
430 | CLANG_WARN_UNREACHABLE_CODE = YES;
431 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
432 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
433 | COPY_PHASE_STRIP = NO;
434 | DEBUG_INFORMATION_FORMAT = dwarf;
435 | ENABLE_STRICT_OBJC_MSGSEND = YES;
436 | ENABLE_TESTABILITY = YES;
437 | GCC_C_LANGUAGE_STANDARD = gnu99;
438 | GCC_DYNAMIC_NO_PIC = NO;
439 | GCC_NO_COMMON_BLOCKS = YES;
440 | GCC_OPTIMIZATION_LEVEL = 0;
441 | GCC_PREPROCESSOR_DEFINITIONS = (
442 | "DEBUG=1",
443 | "$(inherited)",
444 | );
445 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
446 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
447 | GCC_WARN_UNDECLARED_SELECTOR = YES;
448 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
449 | GCC_WARN_UNUSED_FUNCTION = YES;
450 | GCC_WARN_UNUSED_VARIABLE = YES;
451 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
452 | MTL_ENABLE_DEBUG_INFO = YES;
453 | ONLY_ACTIVE_ARCH = YES;
454 | SDKROOT = iphoneos;
455 | SWIFT_VERSION = 5.0;
456 | TARGETED_DEVICE_FAMILY = "1,2";
457 | };
458 | name = Debug;
459 | };
460 | 97C147041CF9000F007C117D /* Release */ = {
461 | isa = XCBuildConfiguration;
462 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
463 | buildSettings = {
464 | ALWAYS_SEARCH_USER_PATHS = NO;
465 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
466 | CLANG_ANALYZER_NONNULL = YES;
467 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
468 | CLANG_CXX_LIBRARY = "libc++";
469 | CLANG_ENABLE_MODULES = YES;
470 | CLANG_ENABLE_OBJC_ARC = YES;
471 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
472 | CLANG_WARN_BOOL_CONVERSION = YES;
473 | CLANG_WARN_COMMA = YES;
474 | CLANG_WARN_CONSTANT_CONVERSION = YES;
475 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
476 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
477 | CLANG_WARN_EMPTY_BODY = YES;
478 | CLANG_WARN_ENUM_CONVERSION = YES;
479 | CLANG_WARN_INFINITE_RECURSION = YES;
480 | CLANG_WARN_INT_CONVERSION = YES;
481 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
482 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
483 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
484 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
485 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
486 | CLANG_WARN_STRICT_PROTOTYPES = YES;
487 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
488 | CLANG_WARN_UNREACHABLE_CODE = YES;
489 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
490 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
491 | COPY_PHASE_STRIP = NO;
492 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
493 | ENABLE_NS_ASSERTIONS = NO;
494 | ENABLE_STRICT_OBJC_MSGSEND = YES;
495 | GCC_C_LANGUAGE_STANDARD = gnu99;
496 | GCC_NO_COMMON_BLOCKS = YES;
497 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
498 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
499 | GCC_WARN_UNDECLARED_SELECTOR = YES;
500 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
501 | GCC_WARN_UNUSED_FUNCTION = YES;
502 | GCC_WARN_UNUSED_VARIABLE = YES;
503 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
504 | MTL_ENABLE_DEBUG_INFO = NO;
505 | SDKROOT = iphoneos;
506 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
507 | SWIFT_VERSION = 5.0;
508 | TARGETED_DEVICE_FAMILY = "1,2";
509 | VALIDATE_PRODUCT = YES;
510 | };
511 | name = Release;
512 | };
513 | 97C147061CF9000F007C117D /* Debug */ = {
514 | isa = XCBuildConfiguration;
515 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
516 | buildSettings = {
517 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
518 | CLANG_ENABLE_MODULES = YES;
519 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
520 | DEVELOPMENT_TEAM = BZV32XF67X;
521 | ENABLE_BITCODE = NO;
522 | FRAMEWORK_SEARCH_PATHS = (
523 | "$(inherited)",
524 | "$(PROJECT_DIR)/Flutter",
525 | );
526 | INFOPLIST_FILE = Runner/Info.plist;
527 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
528 | LIBRARY_SEARCH_PATHS = (
529 | "$(inherited)",
530 | "$(PROJECT_DIR)/Flutter",
531 | );
532 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterVideoCompressExample;
533 | PRODUCT_NAME = "$(TARGET_NAME)";
534 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
535 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
536 | SWIFT_VERSION = 5.0;
537 | VERSIONING_SYSTEM = "apple-generic";
538 | };
539 | name = Debug;
540 | };
541 | 97C147071CF9000F007C117D /* Release */ = {
542 | isa = XCBuildConfiguration;
543 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
544 | buildSettings = {
545 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
546 | CLANG_ENABLE_MODULES = YES;
547 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
548 | DEVELOPMENT_TEAM = BZV32XF67X;
549 | ENABLE_BITCODE = NO;
550 | FRAMEWORK_SEARCH_PATHS = (
551 | "$(inherited)",
552 | "$(PROJECT_DIR)/Flutter",
553 | );
554 | INFOPLIST_FILE = Runner/Info.plist;
555 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
556 | LIBRARY_SEARCH_PATHS = (
557 | "$(inherited)",
558 | "$(PROJECT_DIR)/Flutter",
559 | );
560 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterVideoCompressExample;
561 | PRODUCT_NAME = "$(TARGET_NAME)";
562 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
563 | SWIFT_VERSION = 5.0;
564 | VERSIONING_SYSTEM = "apple-generic";
565 | };
566 | name = Release;
567 | };
568 | /* End XCBuildConfiguration section */
569 |
570 | /* Begin XCConfigurationList section */
571 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
572 | isa = XCConfigurationList;
573 | buildConfigurations = (
574 | 97C147031CF9000F007C117D /* Debug */,
575 | 97C147041CF9000F007C117D /* Release */,
576 | 249021D3217E4FDB00AE95B9 /* Profile */,
577 | );
578 | defaultConfigurationIsVisible = 0;
579 | defaultConfigurationName = Release;
580 | };
581 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
582 | isa = XCConfigurationList;
583 | buildConfigurations = (
584 | 97C147061CF9000F007C117D /* Debug */,
585 | 97C147071CF9000F007C117D /* Release */,
586 | 249021D4217E4FDB00AE95B9 /* Profile */,
587 | );
588 | defaultConfigurationIsVisible = 0;
589 | defaultConfigurationName = Release;
590 | };
591 | /* End XCConfigurationList section */
592 | };
593 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
594 | }
595 |
--------------------------------------------------------------------------------
/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 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/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.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildSystemType
6 | Original
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/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_video_compress_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 | NSPhotoLibraryUsageDescription
45 | Example usage description
46 | NSCameraUsageDescription
47 | Example usage description
48 | NSMicrophoneUsageDescription
49 | Example usage description
50 | NSAppTransportSecurity
51 |
52 | NSAllowsArbitraryLoads
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_video_compress/flutter_video_compress.dart';
6 | import 'package:image_picker/image_picker.dart' show ImagePicker, ImageSource;
7 | import 'package:video_player/video_player.dart';
8 | import 'my-theme.dart';
9 |
10 | void main() => runApp(MyApp(title: 'Flutter Video Compress Example'));
11 |
12 | class MyApp extends StatefulWidget {
13 | MyApp({this.title});
14 |
15 | final String title;
16 |
17 | @override
18 | _MyAppState createState() => _MyAppState();
19 | }
20 |
21 | class _MyAppState extends State {
22 | final _flutterVideoCompress = FlutterVideoCompress();
23 | Subscription _subscription;
24 |
25 | Image _thumbnailFileImage;
26 | Image _gifFileImage;
27 |
28 | MediaInfo _originalVideoInfo = MediaInfo(path: '');
29 | MediaInfo _compressedVideoInfo = MediaInfo(path: '');
30 | String _taskName;
31 | double _progressState = 0;
32 |
33 | final _loadingStreamCtrl = StreamController.broadcast();
34 |
35 | @override
36 | void initState() {
37 | super.initState();
38 | _subscription =
39 | _flutterVideoCompress.compressProgress$.subscribe((progress) {
40 | setState(() {
41 | _progressState = progress;
42 | });
43 | });
44 | }
45 |
46 | @override
47 | void dispose() {
48 | super.dispose();
49 | _subscription.unsubscribe();
50 | _loadingStreamCtrl.close();
51 | }
52 |
53 | Future runFlutterVideoCompressMethods(File videoFile) async {
54 | _loadingStreamCtrl.sink.add(true);
55 |
56 | var _startDateTime = DateTime.now();
57 | print('[Compressing Video] start');
58 | _taskName = '[Compressing Video]';
59 | final compressedVideoInfo = await _flutterVideoCompress.compressVideo(
60 | videoFile.path,
61 | quality: VideoQuality.DefaultQuality,
62 | deleteOrigin: false,
63 | );
64 | _taskName = null;
65 | print(
66 | '[Compressing Video] done! ${DateTime.now().difference(_startDateTime).inSeconds}s');
67 |
68 | _startDateTime = DateTime.now();
69 |
70 | print('[Getting Thumbnail File] start');
71 | final thumbnailFile = await _flutterVideoCompress
72 | .getThumbnailWithFile(videoFile.path, quality: 50);
73 | print(
74 | '[Getting Thumbnail File] done! ${DateTime.now().difference(_startDateTime).inSeconds}s');
75 |
76 | _startDateTime = DateTime.now();
77 | print('[Getting Gif File] start');
78 | _taskName = '[Getting Gif File]';
79 | final gifFile = await _flutterVideoCompress
80 | .convertVideoToGif(videoFile.path, startTime: 0, duration: 5);
81 | print(
82 | '[Getting Gif File] done! ${DateTime.now().difference(_startDateTime).inSeconds}s');
83 | _taskName = null;
84 |
85 | final videoInfo = await _flutterVideoCompress.getMediaInfo(videoFile.path);
86 |
87 | setState(() {
88 | _thumbnailFileImage = Image.file(thumbnailFile);
89 | _gifFileImage = Image.file(gifFile);
90 | _originalVideoInfo = videoInfo;
91 | _compressedVideoInfo = compressedVideoInfo;
92 | });
93 | _loadingStreamCtrl.sink.add(false);
94 | }
95 |
96 | Widget _buildMaterialWarp(Widget body) {
97 | return MaterialApp(
98 | title: widget.title,
99 | theme: myTheme,
100 | home: Scaffold(
101 | appBar: AppBar(
102 | title: Text(widget.title),
103 | actions: [
104 | IconButton(
105 | onPressed: () async {
106 | await _flutterVideoCompress.deleteAllCache();
107 | },
108 | icon: Icon(Icons.delete_forever),
109 | ),
110 | ],
111 | ),
112 | body: body),
113 | );
114 | }
115 |
116 | Widget _buildRoundedRectangleButton(String text, ImageSource source) {
117 | return Padding(
118 | padding: const EdgeInsets.symmetric(horizontal: 32),
119 | child: RaisedButton(
120 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
121 | child: Text(text, style: TextStyle(color: Colors.white)),
122 | color: Colors.grey[800],
123 | onPressed: () async {
124 | final videoFile = await ImagePicker.pickVideo(source: source);
125 | if (videoFile != null) {
126 | runFlutterVideoCompressMethods(videoFile);
127 | }
128 | },
129 | ),
130 | );
131 | }
132 |
133 | String _infoConvert(MediaInfo info) {
134 | return 'path: ${info.path}\n'
135 | 'duration: ${info.duration} microseconds\n'
136 | 'size: ${info.filesize} bytes\n'
137 | 'size: ${info.width} x ${info.height}\n'
138 | 'orientation: ${info.orientation}°\n'
139 | 'compression cancelled: ${info.isCancel}\n'
140 | 'author: ${info.author}';
141 | }
142 |
143 | List _buildInfoPanel(String title,
144 | {MediaInfo info, Image image, bool isVideoModel = false}) {
145 | if (info?.file == null && image == null && !isVideoModel) return [];
146 | return [
147 | if (!isVideoModel || info?.file != null)
148 | Card(
149 | child: ListTile(
150 | title: Text(title),
151 | dense: true,
152 | ),
153 | ),
154 | if (info?.file != null)
155 | Padding(
156 | padding: const EdgeInsets.all(8),
157 | child: Text(_infoConvert(info)),
158 | ),
159 | if (image != null) image,
160 | if (isVideoModel && info?.file != null) VideoPlayerView(file: info.file)
161 | ];
162 | }
163 |
164 | @override
165 | Widget build(context) {
166 | return _buildMaterialWarp(
167 | Stack(children: [
168 | ListView(
169 | children: [
170 | const SizedBox(height: 20),
171 | _buildRoundedRectangleButton(
172 | 'Take video from camera with Image Picker',
173 | ImageSource.camera,
174 | ),
175 | _buildRoundedRectangleButton(
176 | 'Take video from gallery with Image Picker',
177 | ImageSource.gallery,
178 | ),
179 | ..._buildInfoPanel(
180 | 'Original video information',
181 | info: _originalVideoInfo,
182 | ),
183 | ..._buildInfoPanel(
184 | 'Original video view',
185 | info: _originalVideoInfo,
186 | isVideoModel: true,
187 | ),
188 | ..._buildInfoPanel(
189 | 'Compressed video information',
190 | info: _compressedVideoInfo,
191 | ),
192 | ..._buildInfoPanel(
193 | 'Compressed video view',
194 | info: _compressedVideoInfo,
195 | isVideoModel: true,
196 | ),
197 | ..._buildInfoPanel(
198 | 'Thumbnail image from file preview',
199 | image: _thumbnailFileImage,
200 | ),
201 | ..._buildInfoPanel(
202 | 'Gif image from original video preview',
203 | image: _gifFileImage,
204 | ),
205 | ].expand((widget) {
206 | if (widget is SizedBox || widget is Card) {
207 | return [widget];
208 | }
209 | return [widget, const SizedBox(height: 8)];
210 | }).toList(),
211 | ),
212 | StreamBuilder(
213 | stream: _loadingStreamCtrl.stream,
214 | builder: (context, AsyncSnapshot snapshot) {
215 | if (snapshot.data == true) {
216 | return GestureDetector(
217 | onTap: () {
218 | _flutterVideoCompress.cancelCompression();
219 | },
220 | child: Card(
221 | child: Container(
222 | color: Colors.black54,
223 | width: MediaQuery.of(context).size.width,
224 | height: MediaQuery.of(context).size.height,
225 | child: Column(
226 | mainAxisAlignment: MainAxisAlignment.center,
227 | mainAxisSize: MainAxisSize.max,
228 | children: [
229 | CircularProgressIndicator(),
230 | if (_taskName != null)
231 | Padding(
232 | padding: const EdgeInsets.all(8),
233 | child: Text('[$_taskName] $_progressState%'),
234 | ),
235 | Padding(
236 | padding: const EdgeInsets.all(8),
237 | child: const Text('click cancel...'),
238 | )
239 | ],
240 | ),
241 | ),
242 | ),
243 | );
244 | }
245 | return Container();
246 | },
247 | ),
248 | ]),
249 | );
250 | }
251 | }
252 |
253 | class VideoPlayerView extends StatefulWidget {
254 | VideoPlayerView({this.file});
255 |
256 | final File file;
257 |
258 | @override
259 | _VideoPlayerViewState createState() => _VideoPlayerViewState();
260 | }
261 |
262 | class _VideoPlayerViewState extends State {
263 | VideoPlayerController _controller;
264 |
265 | @override
266 | void initState() {
267 | super.initState();
268 | _controller = VideoPlayerController.file(widget.file)
269 | ..initialize().then((_) {
270 | setState(() {});
271 | })
272 | ..setVolume(1)
273 | ..play();
274 | }
275 |
276 | @override
277 | void dispose() {
278 | super.dispose();
279 | _controller.dispose();
280 | }
281 |
282 | @override
283 | Widget build(context) {
284 | return GestureDetector(
285 | onTap: () {
286 | setState(() {
287 | _controller.value.isPlaying
288 | ? _controller.pause()
289 | : _controller.play();
290 | });
291 | },
292 | child: SingleChildScrollView(
293 | child: Column(
294 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
295 | mainAxisSize: MainAxisSize.max,
296 | children: [
297 | Stack(
298 | alignment: Alignment.center,
299 | children: [
300 | _controller.value.initialized
301 | ? AspectRatio(
302 | aspectRatio: _controller.value.aspectRatio,
303 | child: VideoPlayer(_controller),
304 | )
305 | : Container(),
306 | Icon(
307 | _controller.value.isPlaying ? null : Icons.play_arrow,
308 | size: 80,
309 | ),
310 | ],
311 | ),
312 | VideoProgressIndicator(
313 | _controller,
314 | allowScrubbing: true,
315 | colors: VideoProgressColors(
316 | playedColor: Color.fromRGBO(255, 255, 255, 0.1),
317 | ),
318 | ),
319 | ],
320 | ),
321 | ),
322 | );
323 | }
324 | }
325 |
--------------------------------------------------------------------------------
/example/lib/my-theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | final ThemeData myTheme = ThemeData(
4 | primarySwatch: MaterialColor(
5 | 4280361249,
6 | {
7 | 50: Color(0xfff2f2f2),
8 | 100: Color(0xffe6e6e6),
9 | 200: Color(0xffcccccc),
10 | 300: Color(0xffb3b3b3),
11 | 400: Color(0xff999999),
12 | 500: Color(0xff808080),
13 | 600: Color(0xff666666),
14 | 700: Color(0xff4d4d4d),
15 | 800: Color(0xff333333),
16 | 900: Color(0xff191919)
17 | },
18 | ),
19 | brightness: Brightness.dark,
20 | primaryColor: Color(0xff212121),
21 | primaryColorBrightness: Brightness.dark,
22 | primaryColorLight: Color(0xff9e9e9e),
23 | primaryColorDark: Color(0xff000000),
24 | accentColor: Color(0xffd8333f),
25 | accentColorBrightness: Brightness.light,
26 | canvasColor: Color(0xff303030),
27 | scaffoldBackgroundColor: Color(0xff303030),
28 | bottomAppBarColor: Color(0xff424242),
29 | cardColor: Color(0xff424242),
30 | dividerColor: Color(0x1fffffff),
31 | highlightColor: Color(0x40cccccc),
32 | splashColor: Color(0x40cccccc),
33 | selectedRowColor: Color(0xfff5f5f5),
34 | unselectedWidgetColor: Color(0xb3ffffff),
35 | disabledColor: Color(0x4dffffff),
36 | buttonColor: Color(0xff666666),
37 | toggleableActiveColor: Color(0xffd9333e),
38 | secondaryHeaderColor: Color(0xff616161),
39 | textSelectionColor: Color(0xfff44336),
40 | cursorColor: Color(0xff4285f4),
41 | textSelectionHandleColor: Color(0xfff44336),
42 | backgroundColor: Color(0xff616161),
43 | dialogBackgroundColor: Color(0xff424242),
44 | indicatorColor: Color(0xffd9333f),
45 | hintColor: Color(0x80ffffff),
46 | errorColor: Color(0xffd32f2f),
47 | buttonTheme: ButtonThemeData(
48 | textTheme: ButtonTextTheme.normal,
49 | minWidth: 88.0,
50 | height: 36.0,
51 | padding: EdgeInsets.only(top: 0.0, bottom: 0.0, left: 16.0, right: 16.0),
52 | shape: RoundedRectangleBorder(
53 | side: BorderSide(
54 | color: Color(0xff000000),
55 | width: 0.0,
56 | style: BorderStyle.none,
57 | ),
58 | borderRadius: BorderRadius.all(
59 | Radius.circular(2.0),
60 | ),
61 | ),
62 | alignedDropdown: false,
63 | buttonColor: Color(0xff666666),
64 | disabledColor: Color(0x4dffffff),
65 | highlightColor: Color(0x29ffffff),
66 | splashColor: Color(0x1fffffff),
67 | colorScheme: ColorScheme(
68 | primary: Color(0xff999999),
69 | primaryVariant: Color(0xff000000),
70 | secondary: Color(0xff64ffda),
71 | secondaryVariant: Color(0xff00bfa5),
72 | surface: Color(0xff424242),
73 | background: Color(0xff616161),
74 | error: Color(0xffd32f2f),
75 | onPrimary: Color(0xffffffff),
76 | onSecondary: Color(0xff000000),
77 | onSurface: Color(0xffffffff),
78 | onBackground: Color(0xffffffff),
79 | onError: Color(0xff000000),
80 | brightness: Brightness.dark,
81 | ),
82 | ),
83 | textTheme: TextTheme(
84 | display4: TextStyle(
85 | color: Color(0xb3ffffff),
86 | fontSize: 96.0,
87 | fontWeight: FontWeight.w300,
88 | fontStyle: FontStyle.normal,
89 | ),
90 | display3: TextStyle(
91 | color: Color(0xb3ffffff),
92 | fontSize: 60.0,
93 | fontWeight: FontWeight.w300,
94 | fontStyle: FontStyle.normal,
95 | ),
96 | display2: TextStyle(
97 | color: Color(0xb3ffffff),
98 | fontSize: 48.0,
99 | fontWeight: FontWeight.w400,
100 | fontStyle: FontStyle.normal,
101 | ),
102 | display1: TextStyle(
103 | color: Color(0xb3ffffff),
104 | fontSize: 34.0,
105 | fontWeight: FontWeight.w400,
106 | fontStyle: FontStyle.normal,
107 | ),
108 | headline: TextStyle(
109 | color: Color(0xffffffff),
110 | fontSize: 24.0,
111 | fontWeight: FontWeight.w400,
112 | fontStyle: FontStyle.normal,
113 | ),
114 | title: TextStyle(
115 | color: Color(0xffffffff),
116 | fontSize: 20.0,
117 | fontWeight: FontWeight.w500,
118 | fontStyle: FontStyle.normal,
119 | ),
120 | subhead: TextStyle(
121 | color: Color(0xffffffff),
122 | fontSize: 16.0,
123 | fontWeight: FontWeight.w400,
124 | fontStyle: FontStyle.normal,
125 | ),
126 | body2: TextStyle(
127 | color: Color(0xffffffff),
128 | fontSize: 14.0,
129 | fontWeight: FontWeight.w400,
130 | fontStyle: FontStyle.normal,
131 | ),
132 | body1: TextStyle(
133 | color: Color(0xffffffff),
134 | fontSize: 16.0,
135 | fontWeight: FontWeight.w400,
136 | fontStyle: FontStyle.normal,
137 | ),
138 | caption: TextStyle(
139 | color: Color(0xb3ffffff),
140 | fontSize: 12.0,
141 | fontWeight: FontWeight.w400,
142 | fontStyle: FontStyle.normal,
143 | ),
144 | button: TextStyle(
145 | color: Color(0xffffffff),
146 | fontSize: 14.0,
147 | fontWeight: FontWeight.w500,
148 | fontStyle: FontStyle.normal,
149 | ),
150 | subtitle: TextStyle(
151 | color: Color(0xffffffff),
152 | fontSize: 14.0,
153 | fontWeight: FontWeight.w500,
154 | fontStyle: FontStyle.normal,
155 | ),
156 | overline: TextStyle(
157 | color: Color(0xffffffff),
158 | fontSize: 10.0,
159 | fontWeight: FontWeight.w400,
160 | fontStyle: FontStyle.normal,
161 | ),
162 | ),
163 | primaryTextTheme: TextTheme(
164 | display4: TextStyle(
165 | color: Color(0xb3ffffff),
166 | fontSize: 96.0,
167 | fontWeight: FontWeight.w300,
168 | fontStyle: FontStyle.normal,
169 | ),
170 | display3: TextStyle(
171 | color: Color(0xb3ffffff),
172 | fontSize: 60.0,
173 | fontWeight: FontWeight.w300,
174 | fontStyle: FontStyle.normal,
175 | ),
176 | display2: TextStyle(
177 | color: Color(0xb3ffffff),
178 | fontSize: 48.0,
179 | fontWeight: FontWeight.w400,
180 | fontStyle: FontStyle.normal,
181 | ),
182 | display1: TextStyle(
183 | color: Color(0xb3ffffff),
184 | fontSize: 34.0,
185 | fontWeight: FontWeight.w400,
186 | fontStyle: FontStyle.normal,
187 | ),
188 | headline: TextStyle(
189 | color: Color(0xffffffff),
190 | fontSize: 24.0,
191 | fontWeight: FontWeight.w400,
192 | fontStyle: FontStyle.normal,
193 | ),
194 | title: TextStyle(
195 | color: Color(0xffffffff),
196 | fontSize: 20.0,
197 | fontWeight: FontWeight.w500,
198 | fontStyle: FontStyle.normal,
199 | ),
200 | subhead: TextStyle(
201 | color: Color(0xffffffff),
202 | fontSize: 16.0,
203 | fontWeight: FontWeight.w400,
204 | fontStyle: FontStyle.normal,
205 | ),
206 | body2: TextStyle(
207 | color: Color(0xffffffff),
208 | fontSize: 14.0,
209 | fontWeight: FontWeight.w400,
210 | fontStyle: FontStyle.normal,
211 | ),
212 | body1: TextStyle(
213 | color: Color(0xffffffff),
214 | fontSize: 16.0,
215 | fontWeight: FontWeight.w400,
216 | fontStyle: FontStyle.normal,
217 | ),
218 | caption: TextStyle(
219 | color: Color(0xb3ffffff),
220 | fontSize: 12.0,
221 | fontWeight: FontWeight.w400,
222 | fontStyle: FontStyle.normal,
223 | ),
224 | button: TextStyle(
225 | color: Color(0xffffffff),
226 | fontSize: 14.0,
227 | fontWeight: FontWeight.w500,
228 | fontStyle: FontStyle.normal,
229 | ),
230 | subtitle: TextStyle(
231 | color: Color(0xffffffff),
232 | fontSize: 14.0,
233 | fontWeight: FontWeight.w500,
234 | fontStyle: FontStyle.normal,
235 | ),
236 | overline: TextStyle(
237 | color: Color(0xffffffff),
238 | fontSize: 10.0,
239 | fontWeight: FontWeight.w400,
240 | fontStyle: FontStyle.normal,
241 | ),
242 | ),
243 | accentTextTheme: TextTheme(
244 | display4: TextStyle(
245 | color: Color(0x8a000000),
246 | fontSize: 96.0,
247 | fontWeight: FontWeight.w300,
248 | fontStyle: FontStyle.normal,
249 | ),
250 | display3: TextStyle(
251 | color: Color(0x8a000000),
252 | fontSize: 60.0,
253 | fontWeight: FontWeight.w300,
254 | fontStyle: FontStyle.normal,
255 | ),
256 | display2: TextStyle(
257 | color: Color(0x8a000000),
258 | fontSize: 48.0,
259 | fontWeight: FontWeight.w400,
260 | fontStyle: FontStyle.normal,
261 | ),
262 | display1: TextStyle(
263 | color: Color(0x8a000000),
264 | fontSize: 34.0,
265 | fontWeight: FontWeight.w400,
266 | fontStyle: FontStyle.normal,
267 | ),
268 | headline: TextStyle(
269 | color: Color(0xdd000000),
270 | fontSize: 24.0,
271 | fontWeight: FontWeight.w400,
272 | fontStyle: FontStyle.normal,
273 | ),
274 | title: TextStyle(
275 | color: Color(0xdd000000),
276 | fontSize: 20.0,
277 | fontWeight: FontWeight.w500,
278 | fontStyle: FontStyle.normal,
279 | ),
280 | subhead: TextStyle(
281 | color: Color(0xdd000000),
282 | fontSize: 16.0,
283 | fontWeight: FontWeight.w400,
284 | fontStyle: FontStyle.normal,
285 | ),
286 | body2: TextStyle(
287 | color: Color(0xdd000000),
288 | fontSize: 14.0,
289 | fontWeight: FontWeight.w400,
290 | fontStyle: FontStyle.normal,
291 | ),
292 | body1: TextStyle(
293 | color: Color(0xdd000000),
294 | fontSize: 16.0,
295 | fontWeight: FontWeight.w400,
296 | fontStyle: FontStyle.normal,
297 | ),
298 | caption: TextStyle(
299 | color: Color(0x8a000000),
300 | fontSize: 12.0,
301 | fontWeight: FontWeight.w400,
302 | fontStyle: FontStyle.normal,
303 | ),
304 | button: TextStyle(
305 | color: Color(0xdd000000),
306 | fontSize: 14.0,
307 | fontWeight: FontWeight.w500,
308 | fontStyle: FontStyle.normal,
309 | ),
310 | subtitle: TextStyle(
311 | color: Color(0xff000000),
312 | fontSize: 14.0,
313 | fontWeight: FontWeight.w500,
314 | fontStyle: FontStyle.normal,
315 | ),
316 | overline: TextStyle(
317 | color: Color(0xff000000),
318 | fontSize: 10.0,
319 | fontWeight: FontWeight.w400,
320 | fontStyle: FontStyle.normal,
321 | ),
322 | ),
323 | inputDecorationTheme: InputDecorationTheme(
324 | labelStyle: TextStyle(
325 | color: Color(0xffffffff),
326 | fontSize: 16.0,
327 | fontWeight: FontWeight.w400,
328 | fontStyle: FontStyle.normal,
329 | ),
330 | helperStyle: TextStyle(
331 | color: Color(0xffffffff),
332 | fontSize: 16.0,
333 | fontWeight: FontWeight.w400,
334 | fontStyle: FontStyle.normal,
335 | ),
336 | hintStyle: TextStyle(
337 | color: Color(0xffffffff),
338 | fontSize: 16.0,
339 | fontWeight: FontWeight.w400,
340 | fontStyle: FontStyle.normal,
341 | ),
342 | errorStyle: TextStyle(
343 | color: Color(0xffffffff),
344 | fontSize: 16.0,
345 | fontWeight: FontWeight.w400,
346 | fontStyle: FontStyle.normal,
347 | ),
348 | errorMaxLines: null,
349 | hasFloatingPlaceholder: true,
350 | isDense: false,
351 | contentPadding:
352 | EdgeInsets.only(top: 12.0, bottom: 12.0, left: 0.0, right: 0.0),
353 | isCollapsed: false,
354 | prefixStyle: TextStyle(
355 | color: Color(0xffffffff),
356 | fontSize: 16.0,
357 | fontWeight: FontWeight.w400,
358 | fontStyle: FontStyle.normal,
359 | ),
360 | suffixStyle: TextStyle(
361 | color: Color(0xffffffff),
362 | fontSize: 16.0,
363 | fontWeight: FontWeight.w400,
364 | fontStyle: FontStyle.normal,
365 | ),
366 | counterStyle: TextStyle(
367 | color: Color(0xffffffff),
368 | fontSize: 16.0,
369 | fontWeight: FontWeight.w400,
370 | fontStyle: FontStyle.normal,
371 | ),
372 | filled: false,
373 | fillColor: Color(0x00000000),
374 | errorBorder: UnderlineInputBorder(
375 | borderSide: BorderSide(
376 | color: Color(0xff000000),
377 | width: 1.0,
378 | style: BorderStyle.solid,
379 | ),
380 | borderRadius: BorderRadius.all(
381 | Radius.circular(4.0),
382 | ),
383 | ),
384 | focusedBorder: UnderlineInputBorder(
385 | borderSide: BorderSide(
386 | color: Color(0xff000000),
387 | width: 1.0,
388 | style: BorderStyle.solid,
389 | ),
390 | borderRadius: BorderRadius.all(
391 | Radius.circular(4.0),
392 | ),
393 | ),
394 | focusedErrorBorder: UnderlineInputBorder(
395 | borderSide: BorderSide(
396 | color: Color(0xff000000),
397 | width: 1.0,
398 | style: BorderStyle.solid,
399 | ),
400 | borderRadius: BorderRadius.all(
401 | Radius.circular(4.0),
402 | ),
403 | ),
404 | disabledBorder: UnderlineInputBorder(
405 | borderSide: BorderSide(
406 | color: Color(0xff000000),
407 | width: 1.0,
408 | style: BorderStyle.solid,
409 | ),
410 | borderRadius: BorderRadius.all(
411 | Radius.circular(4.0),
412 | ),
413 | ),
414 | enabledBorder: UnderlineInputBorder(
415 | borderSide: BorderSide(
416 | color: Color(0xff000000),
417 | width: 1.0,
418 | style: BorderStyle.solid,
419 | ),
420 | borderRadius: BorderRadius.all(
421 | Radius.circular(4.0),
422 | ),
423 | ),
424 | border: UnderlineInputBorder(
425 | borderSide: BorderSide(
426 | color: Color(0xff000000),
427 | width: 1.0,
428 | style: BorderStyle.solid,
429 | ),
430 | borderRadius: BorderRadius.all(
431 | Radius.circular(4.0),
432 | ),
433 | ),
434 | ),
435 | iconTheme: IconThemeData(
436 | color: Color(0xffffffff),
437 | opacity: 1.0,
438 | size: 24.0,
439 | ),
440 | primaryIconTheme: IconThemeData(
441 | color: Color(0xffffffff),
442 | opacity: 1.0,
443 | size: 24.0,
444 | ),
445 | accentIconTheme: IconThemeData(
446 | color: Color(0xff000000),
447 | opacity: 1.0,
448 | size: 24.0,
449 | ),
450 | sliderTheme: SliderThemeData(
451 | activeTrackColor: Color(0xff212121),
452 | inactiveTrackColor: Color(0x3d212121),
453 | disabledActiveTrackColor: Color(0x52000000),
454 | disabledInactiveTrackColor: Color(0x1f000000),
455 | activeTickMarkColor: Color(0x8a9e9e9e),
456 | inactiveTickMarkColor: Color(0x8a212121),
457 | disabledActiveTickMarkColor: Color(0x1f9e9e9e),
458 | disabledInactiveTickMarkColor: Color(0x1f000000),
459 | thumbColor: Color(0xff212121),
460 | disabledThumbColor: Color(0x52000000),
461 | thumbShape: RoundSliderThumbShape(),
462 | overlayColor: Color(0x29212121),
463 | valueIndicatorColor: Color(0xff212121),
464 | valueIndicatorShape: PaddleSliderValueIndicatorShape(),
465 | showValueIndicator: ShowValueIndicator.onlyForDiscrete,
466 | valueIndicatorTextStyle: TextStyle(
467 | color: Color(0xdd000000),
468 | fontSize: 14.0,
469 | fontWeight: FontWeight.w400,
470 | fontStyle: FontStyle.normal,
471 | ),
472 | overlayShape: SliderComponentShape.noOverlay,
473 | trackHeight: 0,
474 | trackShape: RectangularSliderTrackShape(disabledThumbGapWidth: 0),
475 | tickMarkShape: SliderTickMarkShape.noTickMark,
476 | ),
477 | tabBarTheme: TabBarTheme(
478 | indicatorSize: TabBarIndicatorSize.tab,
479 | labelColor: Color(0xffffffff),
480 | unselectedLabelColor: Color(0xb2ffffff),
481 | ),
482 | chipTheme: ChipThemeData(
483 | backgroundColor: Color(0x1fffffff),
484 | brightness: Brightness.dark,
485 | deleteIconColor: Color(0xdeffffff),
486 | disabledColor: Color(0x0cffffff),
487 | labelPadding: EdgeInsets.only(top: 0.0, bottom: 0.0, left: 8.0, right: 8.0),
488 | labelStyle: TextStyle(
489 | color: Color(0xdeffffff),
490 | fontSize: 14.0,
491 | fontWeight: FontWeight.w400,
492 | fontStyle: FontStyle.normal,
493 | ),
494 | padding: EdgeInsets.only(top: 4.0, bottom: 4.0, left: 4.0, right: 4.0),
495 | secondaryLabelStyle: TextStyle(
496 | color: Color(0x3dffffff),
497 | fontSize: 14.0,
498 | fontWeight: FontWeight.w400,
499 | fontStyle: FontStyle.normal,
500 | ),
501 | secondarySelectedColor: Color(0x3d212121),
502 | selectedColor: Color(0x3dffffff),
503 | shape: StadiumBorder(
504 | side: BorderSide(
505 | color: Color(0xff000000),
506 | width: 0.0,
507 | style: BorderStyle.none,
508 | ),
509 | ),
510 | ),
511 | dialogTheme: DialogTheme(
512 | shape: RoundedRectangleBorder(
513 | side: BorderSide(
514 | color: Color(0xff000000),
515 | width: 0.0,
516 | style: BorderStyle.none,
517 | ),
518 | borderRadius: BorderRadius.all(
519 | Radius.circular(0.0),
520 | ),
521 | ),
522 | ),
523 | );
524 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://www.dartlang.org/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.1.0"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "1.0.4"
18 | charcode:
19 | dependency: transitive
20 | description:
21 | name: charcode
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.1.2"
25 | collection:
26 | dependency: transitive
27 | description:
28 | name: collection
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.14.11"
32 | cupertino_icons:
33 | dependency: "direct main"
34 | description:
35 | name: cupertino_icons
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "0.1.2"
39 | flutter:
40 | dependency: "direct main"
41 | description: flutter
42 | source: sdk
43 | version: "0.0.0"
44 | flutter_test:
45 | dependency: "direct dev"
46 | description: flutter
47 | source: sdk
48 | version: "0.0.0"
49 | flutter_video_compress:
50 | dependency: "direct dev"
51 | description:
52 | path: ".."
53 | relative: true
54 | source: path
55 | version: "0.3.7+8"
56 | image_picker:
57 | dependency: "direct main"
58 | description:
59 | name: image_picker
60 | url: "https://pub.dartlang.org"
61 | source: hosted
62 | version: "0.6.0+10"
63 | matcher:
64 | dependency: transitive
65 | description:
66 | name: matcher
67 | url: "https://pub.dartlang.org"
68 | source: hosted
69 | version: "0.12.5"
70 | meta:
71 | dependency: transitive
72 | description:
73 | name: meta
74 | url: "https://pub.dartlang.org"
75 | source: hosted
76 | version: "1.1.6"
77 | path:
78 | dependency: transitive
79 | description:
80 | name: path
81 | url: "https://pub.dartlang.org"
82 | source: hosted
83 | version: "1.6.2"
84 | pedantic:
85 | dependency: transitive
86 | description:
87 | name: pedantic
88 | url: "https://pub.dartlang.org"
89 | source: hosted
90 | version: "1.5.0"
91 | quiver:
92 | dependency: transitive
93 | description:
94 | name: quiver
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "2.0.2"
98 | sky_engine:
99 | dependency: transitive
100 | description: flutter
101 | source: sdk
102 | version: "0.0.99"
103 | source_span:
104 | dependency: transitive
105 | description:
106 | name: source_span
107 | url: "https://pub.dartlang.org"
108 | source: hosted
109 | version: "1.5.5"
110 | stack_trace:
111 | dependency: transitive
112 | description:
113 | name: stack_trace
114 | url: "https://pub.dartlang.org"
115 | source: hosted
116 | version: "1.9.3"
117 | stream_channel:
118 | dependency: transitive
119 | description:
120 | name: stream_channel
121 | url: "https://pub.dartlang.org"
122 | source: hosted
123 | version: "2.0.0"
124 | string_scanner:
125 | dependency: transitive
126 | description:
127 | name: string_scanner
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "1.0.4"
131 | term_glyph:
132 | dependency: transitive
133 | description:
134 | name: term_glyph
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "1.1.0"
138 | test_api:
139 | dependency: transitive
140 | description:
141 | name: test_api
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "0.2.4"
145 | typed_data:
146 | dependency: transitive
147 | description:
148 | name: typed_data
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "1.1.6"
152 | vector_math:
153 | dependency: transitive
154 | description:
155 | name: vector_math
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "2.0.8"
159 | video_player:
160 | dependency: "direct main"
161 | description:
162 | name: video_player
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "0.10.1+3"
166 | sdks:
167 | dart: ">=2.2.2 <3.0.0"
168 | flutter: ">=1.5.0 <2.0.0"
169 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_video_compress_example
2 | description: Demonstrates how to use the flutter_video_compress plugin.
3 | publish_to: 'none'
4 |
5 | environment:
6 | sdk: ">=2.2.2 <3.0.0"
7 |
8 | dependencies:
9 | flutter:
10 | sdk: flutter
11 |
12 | # The following adds the Cupertino Icons font to your application.
13 | # Use with the CupertinoIcons class for iOS style icons.
14 | cupertino_icons: ^0.1.2
15 | image_picker: ^0.6.0+10
16 | video_player: ^0.10.1+3
17 |
18 | dev_dependencies:
19 | flutter_test:
20 | sdk: flutter
21 |
22 | flutter_video_compress:
23 | path: ../
24 |
25 | # For information on the generic Dart part of this file, see the
26 | # following page: https://www.dartlang.org/tools/pub/pubspec
27 |
28 | # The following section is specific to Flutter.
29 | flutter:
30 |
31 | # The following line ensures that the Material Icons font is
32 | # included with your application, so that you can use the icons in
33 | # the material Icons class.
34 | uses-material-design: true
35 |
36 | # To add assets to your application, add an assets section, like this:
37 | # assets:
38 | # - images/a_dot_burr.jpeg
39 | # - images/a_dot_ham.jpeg
40 |
41 | # An image asset can refer to one or more resolution-specific "variants", see
42 | # https://flutter.dev/assets-and-images/#resolution-aware.
43 |
44 | # For details regarding adding assets from package dependencies, see
45 | # https://flutter.dev/assets-and-images/#from-packages
46 |
47 | # To add custom fonts to your application, add a fonts section here,
48 | # in this "flutter" section. Each entry in this list should have a
49 | # "family" key with the font family name, and a "fonts" key with a
50 | # list giving the asset and other descriptors for the font. For
51 | # example:
52 | # fonts:
53 | # - family: Schyler
54 | # fonts:
55 | # - asset: fonts/Schyler-Regular.ttf
56 | # - asset: fonts/Schyler-Italic.ttf
57 | # style: italic
58 | # - family: Trajan Pro
59 | # fonts:
60 | # - asset: fonts/TrajanPro.ttf
61 | # - asset: fonts/TrajanPro_Bold.ttf
62 | # weight: 700
63 | #
64 | # For details regarding fonts from package dependencies,
65 | # see https://flutter.dev/custom-fonts/#from-packages
66 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | // import 'package:flutter/material.dart';
9 | // import 'package:flutter_test/flutter_test.dart';
10 |
11 | // import 'package:flutter_video_compress_example/main.dart';
12 |
13 | // void main() {
14 |
15 | // }
16 |
--------------------------------------------------------------------------------
/flutter_video_compress.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 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/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/rurico/flutter_video_compress/f2f33ecf5274777e464e137abcdf0ff9d1799ed5/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Classes/AvController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AvController.swift
3 | // flutter_video_compress
4 | //
5 | // Created by ryu on 2019/6/21.
6 | //
7 |
8 | import AVFoundation
9 | import MobileCoreServices
10 |
11 | class AvController: NSObject {
12 | public func getVideoAsset(_ url:URL)->AVURLAsset {
13 | return AVURLAsset(url: url)
14 | }
15 |
16 | public func getTrack(_ asset: AVURLAsset)->AVAssetTrack? {
17 | var track : AVAssetTrack? = nil
18 | let group = DispatchGroup()
19 | group.enter()
20 | asset.loadValuesAsynchronously(forKeys: ["tracks"], completionHandler: {
21 | var error: NSError? = nil;
22 | let status = asset.statusOfValue(forKey: "tracks", error: &error)
23 | if (status == .loaded) {
24 | track = asset.tracks(withMediaType: AVMediaType.video).first
25 | }
26 | group.leave()
27 | })
28 | group.wait()
29 | return track
30 | }
31 |
32 | public func getVideoOrientation(_ path:String)-> Int? {
33 | let url = Utility.getPathUrl(path)
34 | let asset = getVideoAsset(url)
35 | guard let track = getTrack(asset) else {
36 | return nil
37 | }
38 | let size = track.naturalSize
39 | let txf = track.preferredTransform
40 | if size.width == txf.tx && size.height == txf.ty {
41 | return 0
42 | } else if txf.tx == 0 && txf.ty == 0 {
43 | return 90
44 | } else if txf.tx == 0 && txf.ty == size.width {
45 | return 180
46 | } else {
47 | return 270
48 | }
49 | }
50 |
51 | public func getMetaDataByTag(_ asset:AVAsset,key:String)->String {
52 | for item in asset.commonMetadata {
53 | if item.commonKey?.rawValue == key {
54 | return item.stringValue ?? "";
55 | }
56 | }
57 | return ""
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterVideoCompressPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface FlutterVideoCompressPlugin : NSObject
4 | @end
5 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterVideoCompressPlugin.m:
--------------------------------------------------------------------------------
1 | #import "FlutterVideoCompressPlugin.h"
2 | #import
3 |
4 | @implementation FlutterVideoCompressPlugin
5 | + (void)registerWithRegistrar:(NSObject*)registrar {
6 | [SwiftFlutterVideoCompressPlugin registerWithRegistrar:registrar];
7 | }
8 | @end
9 |
--------------------------------------------------------------------------------
/ios/Classes/SwiftFlutterVideoCompressPlugin.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import AVFoundation
3 | import Regift
4 |
5 | public class SwiftFlutterVideoCompressPlugin: NSObject, FlutterPlugin {
6 | private let channelName = "flutter_video_compress"
7 | private var exporter: AVAssetExportSession? = nil
8 | private var stopCommand = false
9 | private let channel: FlutterMethodChannel
10 | private let avController = AvController()
11 |
12 | init(channel: FlutterMethodChannel) {
13 | self.channel = channel
14 | }
15 |
16 | public static func register(with registrar: FlutterPluginRegistrar) {
17 | let channel = FlutterMethodChannel(name: "flutter_video_compress", binaryMessenger: registrar.messenger())
18 | let instance = SwiftFlutterVideoCompressPlugin(channel: channel)
19 | registrar.addMethodCallDelegate(instance, channel: channel)
20 | }
21 |
22 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
23 | let args = call.arguments as? Dictionary
24 | switch call.method {
25 | case "getThumbnail":
26 | let path = args!["path"] as! String
27 | let quality = args!["quality"] as! NSNumber
28 | let position = args!["position"] as! NSNumber
29 | getThumbnail(path, quality, position, result)
30 | case "getThumbnailWithFile":
31 | let path = args!["path"] as! String
32 | let quality = args!["quality"] as! NSNumber
33 | let position = args!["position"] as! NSNumber
34 | getThumbnailWithFile(path, quality, position, result)
35 | case "getMediaInfo":
36 | let path = args!["path"] as! String
37 | getMediaInfo(path, result)
38 | case "compressVideo":
39 | let path = args!["path"] as! String
40 | let quality = args!["quality"] as! NSNumber
41 | let deleteOrigin = args!["deleteOrigin"] as! Bool
42 | let startTime = args!["startTime"] as? Double
43 | let duration = args!["duration"] as? Double
44 | let includeAudio = args!["includeAudio"] as? Bool
45 | let frameRate = args!["frameRate"] as? Int
46 | compressVideo(path, quality, deleteOrigin, startTime, duration, includeAudio,
47 | frameRate, result)
48 | case "cancelCompression":
49 | cancelCompression(result)
50 | case "convertVideoToGif":
51 | let path = args!["path"] as! String
52 | let startTime = args!["startTime"] as! NSNumber
53 | let endTime = args!["endTime"] as! NSNumber
54 | let duration = args!["duration"] as! NSNumber
55 | convertVideoToGif(path, startTime, endTime, duration, result)
56 | case "deleteAllCache":
57 | Utility.deleteFile(Utility.basePath(), clear: true)
58 | result(true)
59 | default:
60 | result(FlutterMethodNotImplemented)
61 | }
62 | }
63 |
64 | private func getBitMap(_ path: String,_ quality: NSNumber,_ position: NSNumber,_ result: FlutterResult)-> Data? {
65 | let url = Utility.getPathUrl(path)
66 | let asset = avController.getVideoAsset(url)
67 | guard let track = avController.getTrack(asset) else { return nil }
68 |
69 | let assetImgGenerate = AVAssetImageGenerator(asset: asset)
70 | assetImgGenerate.appliesPreferredTrackTransform = true
71 |
72 | let timeScale = CMTimeScale(track.nominalFrameRate)
73 | let time = CMTimeMakeWithSeconds(Float64(truncating: position),preferredTimescale: timeScale)
74 | guard let img = try? assetImgGenerate.copyCGImage(at:time, actualTime: nil) else {
75 | return nil
76 | }
77 | let thumbnail = UIImage(cgImage: img)
78 | let compressionQuality = CGFloat(0.01 * Double(truncating: quality))
79 | return thumbnail.jpegData(compressionQuality: compressionQuality)
80 | }
81 |
82 | private func getThumbnail(_ path: String,_ quality: NSNumber,_ position: NSNumber,_ result: FlutterResult) {
83 | if let bitmap = getBitMap(path,quality,position,result) {
84 | result(bitmap)
85 | }
86 | }
87 |
88 | private func getThumbnailWithFile(_ path: String,_ quality: NSNumber,_ position: NSNumber,_ result: FlutterResult) {
89 | let fileName = Utility.getFileName(path)
90 | let url = Utility.getPathUrl("\(Utility.basePath())/\(fileName).jpg")
91 | Utility.deleteFile(path)
92 | if let bitmap = getBitMap(path,quality,position,result) {
93 | guard (try? bitmap.write(to: url)) != nil else {
94 | return result(FlutterError(code: channelName,message: "getThumbnailWithFile error",details: "getThumbnailWithFile error"))
95 | }
96 | result(Utility.excludeFileProtocol(url.absoluteString))
97 | }
98 | }
99 |
100 | public func getMediaInfoJson(_ path: String)->[String : Any?] {
101 | let url = Utility.getPathUrl(path)
102 | let asset = avController.getVideoAsset(url)
103 | guard let track = avController.getTrack(asset) else { return [:] }
104 |
105 | let playerItem = AVPlayerItem(url: url)
106 | let metadataAsset = playerItem.asset
107 |
108 | let orientation = avController.getVideoOrientation(path)
109 |
110 | let title = avController.getMetaDataByTag(metadataAsset,key: "title")
111 | let author = avController.getMetaDataByTag(metadataAsset,key: "author")
112 |
113 | let duration = asset.duration.seconds * 1000
114 | let filesize = track.totalSampleDataLength
115 |
116 | let size = track.naturalSize.applying(track.preferredTransform)
117 |
118 | let width = abs(size.width)
119 | let height = abs(size.height)
120 |
121 | let dictionary = [
122 | "path":Utility.excludeFileProtocol(path),
123 | "title":title,
124 | "author":author,
125 | "width":width,
126 | "height":height,
127 | "duration":duration,
128 | "filesize":filesize,
129 | "orientation":orientation
130 | ] as [String : Any?]
131 | return dictionary
132 | }
133 |
134 | private func getMediaInfo(_ path: String,_ result: FlutterResult) {
135 | let json = getMediaInfoJson(path)
136 | let string = Utility.keyValueToJson(json)
137 | result(string)
138 | }
139 |
140 |
141 | @objc private func updateProgress(timer:Timer) {
142 | let asset = timer.userInfo as! AVAssetExportSession
143 | if(!stopCommand) {
144 | channel.invokeMethod("updateProgress", arguments: "\(String(describing: asset.progress * 100))")
145 | }
146 | }
147 |
148 | private func getExportPreset(_ quality: NSNumber)->String {
149 | switch(quality) {
150 | case 2:
151 | return AVAssetExportPresetMediumQuality
152 | case 3:
153 | return AVAssetExportPresetHighestQuality
154 | default:
155 | return AVAssetExportPresetLowQuality
156 | }
157 | }
158 |
159 | private func getComposition(_ isIncludeAudio: Bool,_ timeRange: CMTimeRange, _ sourceVideoTrack: AVAssetTrack)->AVAsset {
160 | let composition = AVMutableComposition()
161 | if !isIncludeAudio {
162 | let compressionVideoTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
163 | compressionVideoTrack!.preferredTransform = sourceVideoTrack.preferredTransform
164 | try? compressionVideoTrack!.insertTimeRange(timeRange, of: sourceVideoTrack, at: CMTime.zero)
165 | } else {
166 | return sourceVideoTrack.asset!
167 | }
168 |
169 | return composition
170 | }
171 |
172 | private func compressVideo(_ path: String,_ quality: NSNumber,_ deleteOrigin: Bool,_ startTime: Double?,
173 | _ duration: Double?,_ includeAudio: Bool?,_ frameRate: Int?,
174 | _ result: @escaping FlutterResult) {
175 | let sourceVideoUrl = Utility.getPathUrl(path)
176 | let sourceVideoType = sourceVideoUrl.pathExtension
177 |
178 | let sourceVideoAsset = avController.getVideoAsset(sourceVideoUrl)
179 | let sourceVideoTrack = avController.getTrack(sourceVideoAsset)
180 |
181 | let compressionUrl =
182 | Utility.getPathUrl("\(Utility.basePath())/\(Utility.getFileName(path)).\(sourceVideoType)")
183 |
184 | let timescale = sourceVideoAsset.duration.timescale
185 | let minStartTime = Double(startTime ?? 0)
186 |
187 | let videoDuration = sourceVideoAsset.duration.seconds
188 | let minDuration = Double(duration ?? videoDuration)
189 | let maxDurationTime = minStartTime + minDuration < videoDuration ? minDuration : videoDuration
190 |
191 | let cmStartTime = CMTimeMakeWithSeconds(minStartTime, preferredTimescale: timescale)
192 | let cmDurationTime = CMTimeMakeWithSeconds(maxDurationTime, preferredTimescale: timescale)
193 | let timeRange: CMTimeRange = CMTimeRangeMake(start: cmStartTime, duration: cmDurationTime)
194 |
195 | let isIncludeAudio = includeAudio != nil ? includeAudio! : false
196 |
197 | let session = getComposition(isIncludeAudio, timeRange, sourceVideoTrack!)
198 |
199 | let exporter = AVAssetExportSession(asset: session, presetName: getExportPreset(quality))!
200 |
201 | exporter.outputURL = compressionUrl
202 | exporter.outputFileType = AVFileType.mp4
203 | exporter.shouldOptimizeForNetworkUse = true
204 |
205 | if frameRate != nil {
206 | let videoComposition = AVMutableVideoComposition(propertiesOf: sourceVideoAsset)
207 | videoComposition.frameDuration = CMTimeMake(value: 1, timescale: Int32(frameRate!))
208 | exporter.videoComposition = videoComposition
209 | }
210 |
211 | if !isIncludeAudio {
212 | exporter.timeRange = timeRange
213 | }
214 |
215 | Utility.deleteFile(compressionUrl.absoluteString)
216 |
217 | let timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.updateProgress),
218 | userInfo: exporter, repeats: true)
219 |
220 | exporter.exportAsynchronously(completionHandler: {
221 | if(self.stopCommand) {
222 | timer.invalidate()
223 | self.stopCommand = false
224 | var json = self.getMediaInfoJson(path)
225 | json["isCancel"] = true
226 | let jsonString = Utility.keyValueToJson(json)
227 | return result(jsonString)
228 | }
229 | if deleteOrigin {
230 | timer.invalidate()
231 | let fileManager = FileManager.default
232 | do {
233 | if fileManager.fileExists(atPath: path) {
234 | try fileManager.removeItem(atPath: path)
235 | }
236 | self.exporter = nil
237 | self.stopCommand = false
238 | }
239 | catch let error as NSError {
240 | print(error)
241 | }
242 | }
243 | var json = self.getMediaInfoJson(compressionUrl.absoluteString)
244 | json["isCancel"] = false
245 | let jsonString = Utility.keyValueToJson(json)
246 | result(jsonString)
247 | })
248 | }
249 |
250 | private func cancelCompression(_ result: FlutterResult) {
251 | exporter?.cancelExport()
252 | stopCommand = true
253 | result("")
254 | }
255 |
256 | private func convertVideoToGif(_ path: String,_ startTime: NSNumber,_ endTime: NSNumber, _ duration:NSNumber,
257 | _ result: FlutterResult) {
258 | let gifStartTime = Float(truncating: startTime)
259 | var gifDuration = Float(truncating: 0)
260 |
261 | if endTime as! Int > 0 {
262 | if startTime.intValue > endTime.intValue {
263 | result(FlutterError(code: channelName, message: "startTime should preceed endTime",
264 | details: nil))
265 | } else {
266 | gifDuration = Float(Float(truncating: endTime) - gifStartTime)
267 | }
268 | } else {
269 | gifDuration = Float(truncating: duration)
270 | }
271 |
272 | let frameRate = Int(15)
273 | let loopCount = Int(0)
274 |
275 | let sourceFileURL = Utility.getPathUrl(path)
276 | let destinationUrl = Utility.getPathUrl("\(Utility.basePath())/\(Utility.getFileName(path)).gif")
277 |
278 | let trimmedRegift = Regift(sourceFileURL: sourceFileURL, destinationFileURL: destinationUrl,
279 | startTime: gifStartTime, duration: gifDuration, frameRate: frameRate,
280 | loopCount: loopCount, size: nil)
281 |
282 | let destinationPath = trimmedRegift.createGif();
283 |
284 | result(Utility.excludeFileProtocol(destinationPath!.absoluteString))
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/ios/Classes/Utility.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utility.swift
3 | // flutter_video_compress
4 | //
5 | // Created by ryu on 2019/6/21.
6 | //
7 |
8 | class Utility: NSObject {
9 | static let fileManager = FileManager.default
10 |
11 | static func basePath()->String {
12 | let path = "\(NSTemporaryDirectory())flutter_video_compress"
13 | do {
14 | if !fileManager.fileExists(atPath: path) {
15 | try! fileManager.createDirectory(atPath: path,
16 | withIntermediateDirectories: true, attributes: nil)
17 | }
18 | }
19 | return path
20 | }
21 |
22 | static func stripFileExtension(_ fileName:String)->String {
23 | var components = fileName.components(separatedBy: ".")
24 | if components.count > 1 {
25 | components.removeLast()
26 | return components.joined(separator: ".")
27 | } else {
28 | return fileName
29 | }
30 | }
31 | static func getFileName(_ path: String)->String {
32 | return stripFileExtension((path as NSString).lastPathComponent)
33 | }
34 |
35 | static func getPathUrl(_ path: String)->URL {
36 | return URL(fileURLWithPath: excludeFileProtocol(path))
37 | }
38 |
39 | static func excludeFileProtocol(_ path: String)->String {
40 | return path.replacingOccurrences(of: "file://", with: "")
41 | }
42 |
43 | static func keyValueToJson(_ keyAndValue: [String : Any?])->String {
44 | let data = try! JSONSerialization.data(withJSONObject: keyAndValue as NSDictionary, options: [])
45 | let jsonString = NSString(data:data as Data,encoding: String.Encoding.utf8.rawValue)
46 | return jsonString! as String
47 | }
48 |
49 | static func deleteFile(_ path: String, clear: Bool = false) {
50 | let url = getPathUrl(path)
51 | if fileManager.fileExists(atPath: url.absoluteString) {
52 | try? fileManager.removeItem(at: url)
53 | }
54 | if clear {
55 | try? fileManager.removeItem(at: url)
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/ios/flutter_video_compress.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
3 | #
4 | Pod::Spec.new do |s|
5 | s.name = 'flutter_video_compress'
6 | s.version = '0.3.0'
7 | s.swift_version = '5.0'
8 | s.summary = 'A new flutter plugin project.'
9 | s.description = <<-DESC
10 | A new flutter plugin project.
11 | DESC
12 | s.homepage = 'https://github.com/TenkaiRuri/flutter_video_compress'
13 | s.license = { :file => '../LICENSE' }
14 | s.author = { 'FlutterVideoCompress Team' => 'babichan@outlook.com' }
15 | s.source = { :path => '.' }
16 | s.source_files = 'Classes/**/*'
17 | s.public_header_files = 'Classes/**/*.h'
18 | s.dependency 'Flutter'
19 | s.dependency 'Regift'
20 | s.preserve_paths = 'Regift.framework'
21 | s.xcconfig = { 'OTHER_LDFLAGS' => '-framework Regift' }
22 | s.vendored_frameworks = 'Regift.framework'
23 |
24 | s.ios.deployment_target = '8.0'
25 | end
26 |
27 |
--------------------------------------------------------------------------------
/lib/android/media_metadata_retriever.dart:
--------------------------------------------------------------------------------
1 | abstract class Enum {
2 | final T _value;
3 |
4 | const Enum(this._value);
5 |
6 | T get value => _value;
7 | }
8 |
9 | class MediaMetadataRetriever extends Enum {
10 | /// [Android] API level 10
11 | static const METADATA_KEY_ALBUM = MediaMetadataRetriever(1);
12 |
13 | /// [Android] API level 10
14 | static const METADATA_KEY_ALBUMARTIST = MediaMetadataRetriever(13);
15 |
16 | /// [Android] API level 10
17 | static const METADATA_KEY_ARTIST = MediaMetadataRetriever(2);
18 |
19 | /// [Android] API level 10
20 | static const METADATA_KEY_AUTHOR = MediaMetadataRetriever(3);
21 |
22 | /// [Android] API level 14
23 | static const METADATA_KEY_BITRATE = MediaMetadataRetriever(20);
24 |
25 | /// [Android] API level 23
26 | static const METADATA_KEY_CAPTURE_FRAMERATE = MediaMetadataRetriever(25);
27 |
28 | /// [Android] API level 10
29 | static const METADATA_KEY_CD_TRACK_NUMBER = MediaMetadataRetriever(0);
30 |
31 | /// [Android] API level 10
32 | static const METADATA_KEY_COMPILATION = MediaMetadataRetriever(15);
33 |
34 | /// [Android] API level 10
35 | static const METADATA_KEY_COMPOSER = MediaMetadataRetriever(4);
36 |
37 | /// [Android] API level 10
38 | static const METADATA_KEY_DATE = MediaMetadataRetriever(5);
39 |
40 | /// [Android] API level 10
41 | static const METADATA_KEY_DISC_NUMBER = MediaMetadataRetriever(14);
42 |
43 | /// [Android] API level 10
44 | static const METADATA_KEY_DURATION = MediaMetadataRetriever(9);
45 |
46 | /// [Android] API level Q
47 | static const METADATA_KEY_EXIF_LENGTH = MediaMetadataRetriever(34);
48 |
49 | /// [Android] API level Q
50 | static const METADATA_KEY_EXIF_OFFSET = MediaMetadataRetriever(33);
51 |
52 | /// [Android] API level 10
53 | static const METADATA_KEY_GENRE = MediaMetadataRetriever(6);
54 |
55 | /// [Android] API level 14
56 | static const METADATA_KEY_HAS_AUDIO = MediaMetadataRetriever(16);
57 |
58 | /// [Android] API level 28
59 | static const METADATA_KEY_HAS_IMAGE = MediaMetadataRetriever(26);
60 |
61 | /// [Android] API level 14
62 | static const METADATA_KEY_HAS_VIDEO = MediaMetadataRetriever(17);
63 |
64 | /// [Android] API level 28
65 | static const METADATA_KEY_IMAGE_COUNT = MediaMetadataRetriever(27);
66 |
67 | /// [Android] API level 28
68 | static const METADATA_KEY_IMAGE_HEIGHT = MediaMetadataRetriever(30);
69 |
70 | /// [Android] API level 28
71 | static const METADATA_KEY_IMAGE_PRIMARY = MediaMetadataRetriever(28);
72 |
73 | /// [Android] API level 28
74 | static const METADATA_KEY_IMAGE_ROTATION = MediaMetadataRetriever(31);
75 |
76 | /// [Android] API level 28
77 | static const METADATA_KEY_IMAGE_WIDTH = MediaMetadataRetriever(29);
78 |
79 | /// [Android] API level 15
80 | static const METADATA_KEY_LOCATION = MediaMetadataRetriever(23);
81 |
82 | /// [Android] API level 10
83 | static const METADATA_KEY_MIMETYPE = MediaMetadataRetriever(12);
84 |
85 | /// [Android] API level 10
86 | static const METADATA_KEY_NUM_TRACKS = MediaMetadataRetriever(10);
87 |
88 | /// [Android] API level 10
89 | static const METADATA_KEY_TITLE = MediaMetadataRetriever(7);
90 |
91 | /// [Android] API level 28
92 | static const METADATA_KEY_VIDEO_FRAME_COUNT = MediaMetadataRetriever(32);
93 |
94 | /// [Android] API level 14
95 | static const METADATA_KEY_VIDEO_HEIGHT = MediaMetadataRetriever(19);
96 |
97 | /// [Android] API level 17
98 | static const METADATA_KEY_VIDEO_ROTATION = MediaMetadataRetriever(24);
99 |
100 | /// [Android] API level 14
101 | static const METADATA_KEY_VIDEO_WIDTH = MediaMetadataRetriever(18);
102 |
103 | /// [Android] API level 10
104 | static const METADATA_KEY_WRITER = MediaMetadataRetriever(11);
105 |
106 | /// [Android] API level 10
107 | static const METADATA_KEY_YEAR = MediaMetadataRetriever(8);
108 |
109 | const MediaMetadataRetriever(int value) : super(value);
110 | }
111 |
--------------------------------------------------------------------------------
/lib/flutter_video_compress.dart:
--------------------------------------------------------------------------------
1 | /// Compressed video generates a new path, keep the source video or delete it.
2 | /// provide get video information or get thumbnail of the video file.
3 |
4 | library flutter_video_compress;
5 |
6 | import 'dart:async';
7 | import 'dart:convert';
8 | import 'dart:io';
9 | import 'dart:typed_data';
10 |
11 | import 'dart:ui';
12 |
13 | import 'package:flutter/foundation.dart';
14 | import 'package:flutter/services.dart';
15 |
16 | part 'src/flutter_video_compress.dart';
17 | part 'src/video_quality.dart';
18 | part 'src/observable_builder.dart';
19 | part 'model/media_info.dart';
20 |
--------------------------------------------------------------------------------
/lib/model/media_info.dart:
--------------------------------------------------------------------------------
1 | part of flutter_video_compress;
2 |
3 | class MediaInfo {
4 | String path;
5 | String title;
6 | String author;
7 | int width;
8 | int height;
9 |
10 | /// [Android] API level 17
11 | int orientation;
12 |
13 | /// bytes
14 | int filesize; // filesize
15 | /// microsecond
16 | double duration;
17 | bool isCancel;
18 | File file;
19 |
20 | MediaInfo({
21 | @required this.path,
22 | this.title,
23 | this.author,
24 | this.width,
25 | this.height,
26 | this.orientation,
27 | this.filesize,
28 | this.duration,
29 | this.isCancel,
30 | this.file,
31 | });
32 |
33 | MediaInfo.fromJson(Map json) {
34 | path = json['path'];
35 | title = json['title'];
36 | author = json['author'];
37 | width = json['width'];
38 | height = json['height'];
39 | orientation = json['orientation'];
40 | filesize = json['filesize'];
41 | duration = double.tryParse('${json['duration']}');
42 | isCancel = json['isCancel'];
43 | file = File(path);
44 | }
45 |
46 | Map toJson() {
47 | final Map data = Map();
48 | data['path'] = this.path;
49 | data['title'] = this.title;
50 | data['author'] = this.author;
51 | data['width'] = this.width;
52 | data['height'] = this.height;
53 | if (this.orientation != null) {
54 | data['orientation'] = this.orientation;
55 | }
56 | data['filesize'] = this.filesize;
57 | data['duration'] = this.duration;
58 | if (this.isCancel != null) {
59 | data['isCancel'] = this.isCancel;
60 | }
61 | data['file'] = File(path).toString();
62 | return data;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/src/flutter_video_compress.dart:
--------------------------------------------------------------------------------
1 | part of flutter_video_compress;
2 |
3 | class FlutterVideoCompress {
4 | static const _channel = const MethodChannel('flutter_video_compress');
5 |
6 | factory FlutterVideoCompress() => FlutterVideoCompress._();
7 |
8 | /// Subscribe the conversion progress
9 | final compressProgress$ = ObservableBuilder();
10 |
11 | /// get is Compressing state
12 | bool get isCompressing => _isCompressing;
13 |
14 | bool _isCompressing = false;
15 |
16 | FlutterVideoCompress._() {
17 | _channel.setMethodCallHandler(_handleCallback);
18 | }
19 |
20 | Future _handleCallback(MethodCall call) async {
21 | switch (call.method) {
22 | case 'updateProgress':
23 | final progress = double.tryParse(call.arguments);
24 | _updateProgressState(progress);
25 | break;
26 | }
27 | }
28 |
29 | void _updateProgressState(double state) {
30 | if (state != null) {
31 | compressProgress$.next(state);
32 | }
33 | }
34 |
35 | Future _invoke(String name, [Map params]) async {
36 | T result;
37 | try {
38 | result = params != null
39 | ? await _channel.invokeMethod(name, params)
40 | : await _channel.invokeMethod(name);
41 | } on PlatformException catch (e) {
42 | debugPrint('''FlutterVideoCompress Error:
43 | Method: $name
44 | $e''');
45 | }
46 | return result;
47 | }
48 |
49 | /// get thumbnail from [path]
50 | ///
51 | /// get thumbnail from [path] return [Future],
52 | /// quality can be controlled by [quality] from 1 to 100,
53 | /// select the position unit in the video by [position] is seconds, don't worry about crossing the boundary
54 | ///
55 | /// ## example
56 | /// ```dart
57 | /// final uint8list = await _flutterVideoCompress.getThumbnail(
58 | /// file.path,
59 | /// quality: 50,
60 | /// );
61 | /// ```
62 | Future getThumbnail(
63 | String path, {
64 | int quality = 100,
65 | int position = -1,
66 | }) async {
67 | assert(path != null);
68 | assert(quality > 1 || quality < 100);
69 |
70 | return await _invoke('getThumbnail', {
71 | 'path': path,
72 | 'quality': quality,
73 | 'position': position,
74 | });
75 | }
76 |
77 | /// get thumbnail file from [path] return [Future]
78 | ///
79 | /// quality can be controlled by [quality] from 1 to 100,
80 | /// select the position unit in the video by [position] is seconds, don't worry about crossing the boundary
81 | ///
82 | /// ## example
83 | /// ```dart
84 | /// final file = await _flutterVideoCompress.getThumbnailWithFile(
85 | /// file.path,
86 | /// quality: 50,
87 | /// );
88 | /// ```
89 | Future getThumbnailWithFile(
90 | String path, {
91 | int quality = 100,
92 | int position = -1,
93 | }) async {
94 | assert(path != null);
95 | assert(quality > 1 || quality < 100);
96 |
97 | final filePath = await _invoke('getThumbnailWithFile', {
98 | 'path': path,
99 | 'quality': quality,
100 | 'position': position,
101 | });
102 |
103 | final file = File(filePath);
104 |
105 | return file;
106 | }
107 |
108 | /// converts provided video to a gif. Video is cut starting from [startTime]
109 | /// for the provided [duration] or until [endTime] if set. Either [endTime] or
110 | /// [duration] has to be set where if both set, [endTime] has priority. [endTime]
111 | /// has to be greater than [startTime] in order for this method to work.
112 | /// All time variables should be in seconds. Take care of about [duration]
113 | /// of the video since the plugin doesn't check if [startTime] is within the
114 | /// length of the provided video.
115 | ///
116 | /// ## example
117 | /// ```dart
118 | /// final file = await _flutterVideoCompress.convertVideoToGif(
119 | /// videoFile.path,
120 | /// startTime: 0,
121 | /// duration: 5,
122 | /// );
123 | /// debugPrint(file.path);
124 | /// ```
125 | Future convertVideoToGif(
126 | String path, {
127 | int startTime = 0,
128 | int endTime = -1,
129 | int duration = -1, // When you do not know the end time
130 | }) async {
131 | assert(path != null);
132 | if (endTime > 0) {
133 | assert(startTime <= endTime);
134 | }
135 | final filePath = await _invoke('convertVideoToGif', {
136 | 'path': path,
137 | 'startTime': startTime,
138 | 'endTime': endTime,
139 | 'duration': duration
140 | });
141 |
142 | final file = File(filePath);
143 |
144 | return file;
145 | }
146 |
147 | /// get media information from [path]
148 | ///
149 | /// get media information from [path] return [Future]
150 | ///
151 | /// ## example
152 | /// ```dart
153 | /// final info = await _flutterVideoCompress.getMediaInfo(file.path);
154 | /// debugPrint(info.toJson());
155 | /// ```
156 | Future getMediaInfo(String path) async {
157 | assert(path != null);
158 | final jsonStr = await _invoke('getMediaInfo', {'path': path});
159 | final jsonMap = json.decode(jsonStr);
160 | return MediaInfo.fromJson(jsonMap);
161 | }
162 |
163 | /// compress video from [path]
164 | /// compress video from [path] return [Future]
165 | ///
166 | /// you can choose its quality by [quality],
167 | /// determine whether to delete his source file by [deleteOrigin]
168 | /// optional parameters [startTime] [duration] [includeAudio] [frameRate]
169 | ///
170 | /// ## example
171 | /// ```dart
172 | /// final info = await _flutterVideoCompress.compressVideo(
173 | /// file.path,
174 | /// deleteOrigin: true,
175 | /// );
176 | /// debugPrint(info.toJson());
177 | /// ```
178 | Future compressVideo(
179 | String path, {
180 | VideoQuality quality = VideoQuality.DefaultQuality,
181 | bool deleteOrigin = false,
182 | int startTime,
183 | int duration,
184 | bool includeAudio,
185 | int frameRate,
186 | }) async {
187 | assert(path != null);
188 | if (_isCompressing) {
189 | throw StateError('''FlutterVideoCompress Error:
190 | Method: compressVideo
191 | Already have a compression process, you need to wait for the process to finish or stop it''');
192 | }
193 | _isCompressing = true;
194 | if (compressProgress$._notSubscribed) {
195 | debugPrint('''FlutterVideoCompress: You can try to subscribe to the
196 | compressProgress\$ stream to know the compressing state.''');
197 | }
198 | final jsonStr = await _invoke('compressVideo', {
199 | 'path': path,
200 | 'quality': quality.index,
201 | 'deleteOrigin': deleteOrigin,
202 | 'startTime': startTime,
203 | 'duration': duration,
204 | 'includeAudio': includeAudio,
205 | 'frameRate': frameRate,
206 | });
207 | _isCompressing = false;
208 | final jsonMap = json.decode(jsonStr);
209 | return MediaInfo.fromJson(jsonMap);
210 | }
211 |
212 | /// stop compressing the file that is currently being compressed.
213 | /// If there is no compression process, nothing will happen.
214 | ///
215 | /// ## example
216 | /// ```dart
217 | /// await _flutterVideoCompress.cancelCompression();
218 | /// ```
219 | Future cancelCompression() async {
220 | await _invoke('cancelCompression');
221 | }
222 |
223 | /// delete the cache folder, please do not put other things
224 | /// in the folder of this plugin, it will be cleared
225 | ///
226 | /// ## example
227 | /// ```dart
228 | /// await _flutterVideoCompress.deleteAllCache();
229 | /// ```
230 | Future deleteAllCache() async {
231 | return await _invoke('deleteAllCache');
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/lib/src/observable_builder.dart:
--------------------------------------------------------------------------------
1 | /// If you read this source code, you may find this surprise.
2 | /// a small subscription library
3 |
4 | part of flutter_video_compress;
5 |
6 | class ObservableBuilder {
7 | final StreamController _observable = StreamController();
8 | bool _notSubscribed = true;
9 |
10 | void next(T value) {
11 | _observable.add(value);
12 | }
13 |
14 | Subscription subscribe(void onData(T event),
15 | {Function onError, void onDone(), bool cancelOnError}) {
16 | _notSubscribed = false;
17 | _observable.stream.listen(onData,
18 | onError: onError, onDone: onDone, cancelOnError: cancelOnError);
19 | return Subscription(_observable.close);
20 | }
21 | }
22 |
23 | class Subscription {
24 | final VoidCallback unsubscribe;
25 | const Subscription(this.unsubscribe);
26 | }
27 |
--------------------------------------------------------------------------------
/lib/src/video_quality.dart:
--------------------------------------------------------------------------------
1 | part of flutter_video_compress;
2 |
3 | enum VideoQuality { DefaultQuality, LowQuality, MediumQuality, HighestQuality }
4 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_video_compress
2 | description: Generate a new file by compressed video, and provide metadata. Get video thumbnail from a video path, supports JPEG/GIF. To reduce app size not using FFmpeg in IOS.
3 | version: 0.3.7+8
4 | author: Tenkai Ruri
5 | homepage: https://github.com/TenkaiRuri/flutter_video_compress
6 |
7 | environment:
8 | sdk: ">=2.0.0 <3.0.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 |
14 | dev_dependencies:
15 | flutter_test:
16 | sdk: flutter
17 | image_picker: ^0.6.0+10
18 | video_player: ^0.10.1+3
19 |
20 |
21 | # For information on the generic Dart part of this file, see the
22 | # following page: https://www.dartlang.org/tools/pub/pubspec
23 |
24 | # The following section is specific to Flutter.
25 | flutter:
26 | # This section identifies this Flutter project as a plugin project.
27 | # The androidPackage and pluginClass identifiers should not ordinarily
28 | # be modified. They are used by the tooling to maintain consistency when
29 | # adding or updating assets for this project.
30 | plugin:
31 | androidPackage: com.example.flutter_video_compress
32 | pluginClass: FlutterVideoCompressPlugin
33 |
34 | # To add assets to your plugin package, add an assets section, like this:
35 | # assets:
36 | # - images/a_dot_burr.jpeg
37 | # - images/a_dot_ham.jpeg
38 | #
39 | # For details regarding assets in packages, see
40 | # https://flutter.dev/assets-and-images/#from-packages
41 | #
42 | # An image asset can refer to one or more resolution-specific "variants", see
43 | # https://flutter.dev/assets-and-images/#resolution-aware.
44 |
45 | # To add custom fonts to your plugin package, add a fonts section here,
46 | # in this "flutter" section. Each entry in this list should have a
47 | # "family" key with the font family name, and a "fonts" key with a
48 | # list giving the asset and other descriptors for the font. For
49 | # example:
50 | # fonts:
51 | # - family: Schyler
52 | # fonts:
53 | # - asset: fonts/Schyler-Regular.ttf
54 | # - asset: fonts/Schyler-Italic.ttf
55 | # style: italic
56 | # - family: Trajan Pro
57 | # fonts:
58 | # - asset: fonts/TrajanPro.ttf
59 | # - asset: fonts/TrajanPro_Bold.ttf
60 | # weight: 700
61 | #
62 | # For details regarding fonts in packages, see
63 | # https://flutter.dev/custom-fonts/#from-packages
64 |
--------------------------------------------------------------------------------
/test/flutter_video_compress_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/services.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 |
4 | void main() {
5 | const MethodChannel channel = MethodChannel('flutter_video_compress');
6 |
7 | tearDown(() {
8 | channel.setMockMethodCallHandler(null);
9 | });
10 | }
11 |
--------------------------------------------------------------------------------