diff --git a/README.md b/README.md index ef4e8da9..bf30f0e8 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Compress videos, remove audio, manipulate thumbnails, and make your video compat In addition, google chrome uses VP8/VP9, safari uses h264, and most of the time, it is necessary to encode the video in two formats, but not with this library. All video files are encoded in an MP4 container with AAC audio that allows 100% compatibility with safari, mozila, chrome, android and iOS. -Works on ANDROID and IOS. +Works on ANDROID and IOS, except for the '.gif' to '.mp4' transformation which currently only works on Android. @@ -164,6 +164,7 @@ class _Compress extends State { ### TODO - Add the trim video function - Add cancel function to Android +- Add gif to mp4 to iOS ## Methods | Functions | Parameters | Description | Returns | @@ -172,6 +173,7 @@ class _Compress extends State { | getFileThumbnail | 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` | | getMediaInfo | String `path`[video path] | get media information from video `path` | `Future` | | 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` | +| compressGifToVideo| String `path`[video path], VideoQuality `quality`[compressed video quality], bool `deleteOrigin`[delete the origin video], int `frameRate`[compressed video frame rate] | compression video at origin video `path` | `Future` | | cancelCompression | `none` | cancel compressing | `Future` | | deleteAllCache | `none` | Delete all files generated by 'video_compress' will delete all files located at 'video_compress' | `Future` | diff --git a/android/build.gradle b/android/build.gradle index c7301cea..fa826d9a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -42,4 +42,5 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.otaliastudios:transcoder:0.9.1' + implementation 'com.otaliastudios.gif:compressor:1.0.0' } diff --git a/android/src/main/kotlin/com/example/video_compress/VideoCompressPlugin.kt b/android/src/main/kotlin/com/example/video_compress/VideoCompressPlugin.kt index c072b4b3..8b5d410f 100644 --- a/android/src/main/kotlin/com/example/video_compress/VideoCompressPlugin.kt +++ b/android/src/main/kotlin/com/example/video_compress/VideoCompressPlugin.kt @@ -12,6 +12,10 @@ import com.otaliastudios.transcoder.strategy.DefaultVideoStrategy import com.otaliastudios.transcoder.strategy.RemoveTrackStrategy import com.otaliastudios.transcoder.strategy.TrackStrategy import com.otaliastudios.transcoder.strategy.size.* +import com.otaliastudios.gif.GIFCompressor +import com.otaliastudios.gif.GIFListener +import com.otaliastudios.gif.strategy.DefaultStrategy +import com.otaliastudios.gif.strategy.Strategy import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.BinaryMessenger import com.otaliastudios.transcoder.internal.Logger @@ -162,6 +166,67 @@ class VideoCompressPlugin : MethodCallHandler, FlutterPlugin { } }).transcode() } + "compressGifToVideo" -> { + //https://natario1.github.io/GIFCompressor/home for more options + val path = call.argument("path")!! + val quality = call.argument("quality")!! + val deleteOrigin = call.argument("deleteOrigin")!! + val frameRate = if (call.argument("frameRate")==null) 30 else call.argument("frameRate") + val tempDir: String = context.getExternalFilesDir("video_compress")!!.absolutePath + val out = SimpleDateFormat("yyyy-MM-dd hh-mm-ss").format(Date()) + val destPath: String = tempDir + File.separator + "VID_" + out + ".mp4" + + var trackStrategy: Strategy = DefaultStrategy.atMost(340).build(); + + when (quality) { + + 0 -> { + trackStrategy = DefaultStrategy.atMost(720).build() + } + + 1 -> { + trackStrategy = DefaultStrategy.atMost(360).build() + } + 2 -> { + trackStrategy = DefaultStrategy.atMost(640).build() + } + 3 -> { + trackStrategy = DefaultStrategy.Builder() + .keyFrameInterval(3f) + .bitRate(1280 * 720 * 4.toLong()) + .frameRate(frameRate!!) + .build() + } + } + + transcodeFuture = GIFCompressor.into(destPath) + .addDataSource(context, Uri.parse(path)) + .setStrategy(trackStrategy) + .setListener(object : GIFListener { + override fun onGIFCompressionFailed(exception: Throwable) { + result.success(null) + } + + override fun onGIFCompressionProgress(progress: Double) { + channel.invokeMethod("updateProgress", progress * 100.00) + } + + override fun onGIFCompressionCompleted() { + channel.invokeMethod("updateProgress", 100.00) + val json = Utility(channelName).getMediaInfoJson(context, destPath) + json.put("isCancel", false) + result.success(json.toString()) + if (deleteOrigin) { + File(path).delete() + } + } + + override fun onGIFCompressionCanceled() { + result.success(null) + } + + }).compress() + } else -> { result.notImplemented() } diff --git a/lib/src/video_compress/video_compressor.dart b/lib/src/video_compress/video_compressor.dart index aed55399..26d74f73 100644 --- a/lib/src/video_compress/video_compressor.dart +++ b/lib/src/video_compress/video_compressor.dart @@ -161,6 +161,55 @@ extension Compress on IVideoCompress { } } + + /// transform a '.gif' into a '.mp4' from [path] + /// returns [Future] + /// + /// you can choose its quality by [quality], + /// determine whether to delete his source file by [deleteOrigin] + /// optional parameters [frameRate] + Future compressGifToVideo( + String path, { + VideoQuality quality = VideoQuality.DefaultQuality, + bool deleteOrigin = false, + int startTime, + int duration, + int frameRate = 30, + }) async { + assert(path != null); + assert(Platform.isAndroid == true, "compressGifToVideo is currently only supported on Android"); + if (isCompressing) { + throw StateError('''VideoCompress Error: + Method: compressGifToVideo + Already have a compression process, you need to wait for the process to finish or stop it'''); + } + + if (compressProgress$.notSubscribed) { + debugPrint('''VideoCompress: You can try to subscribe to the + compressProgress\$ stream to know the compressing state.'''); + } + // ignore: invalid_use_of_protected_member + setProcessingStatus(true); + final jsonStr = await _invoke('compressGifToVideo', { + 'path': path, + 'quality': quality.index, + 'deleteOrigin': deleteOrigin, + 'startTime': startTime, + 'duration': duration, + 'frameRate': frameRate, + }); + + // ignore: invalid_use_of_protected_member + setProcessingStatus(false); + + if (jsonStr != null) { + final jsonMap = json.decode(jsonStr); + return MediaInfo.fromJson(jsonMap); + } else { + return null; + } + } + /// stop compressing the file that is currently being compressed. /// If there is no compression process, nothing will happen. Future cancelCompression() async {