diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index a5f05cd..e34606c 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -21,5 +21,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/README-CN.md b/README-CN.md
new file mode 100644
index 0000000..4bc972c
--- /dev/null
+++ b/README-CN.md
@@ -0,0 +1,237 @@
+
+![FFmpegCommand](images/ffmpeg-command.png)
+
+> 致`FFmpegCommand`使用者:
+>
+> 首先感谢大家对此库的支持,感谢你们的使用才让我们有了继续开源下去的动力,感谢你们提出的问题,让这个库更加的完善。
+>
+> 在`1.2.0`之前提供了异步处理和多代码执行,但是很多人反馈,无法执行异步而且多代码用处不大,所以经过反复考虑将在`1.2.0`及之后的版本作出如下更改:
+>
+> * 取消`runCmdAsync`和`runCmdSync`方法,统一更改为`runCmd`执行`FFmpeg`命令
+> * 取消多命令`runMoreAsync`和`runMoreSync`方法,`runCmd`内部自动实现同步顺序执行
+> * 新增错误日志提示,发生错误时使用`ffmpeg-cmd`进行筛选错误日志
+>
+> 此次修改对您造成的不便,敬请谅解。
+
+[【README-English】](./README.md)
+
+## 前景提要
+在我们的开发中,经常会用到音视频相关内容,一般我们都会选择[FFmpeg](https://www.ffmpeg.org/),但是其交叉编译对于我们来说是一件很麻烦的事情.所以这里方便日后使用就编写了这个`FFmpegCommand`,`FFmpegCommand`是由`FFmpeg`核心库,并且集成了`lame`、`libx264`、`fdk-aac`和`libopencore-amr`主流音视频处理程序构成的Android程序
+
+**注意:当前库只适用于Android**
+
+如果访问不了全部信息,请跳转[【国内镜像】](https://gitee.com/anjoiner/FFmpegCommand)
+
+## 主要功能
+[ ![Download](https://api.bintray.com/packages/sourfeng/repositories/ffmpeg/images/download.svg) ](https://bintray.com/sourfeng/repositories/ffmpeg/_latestVersion)[![License](https://img.shields.io/badge/license-Apache%202-success.svg)](https://www.apache.org/licenses/LICENSE-2.0)[ ![FFmpeg](https://img.shields.io/badge/FFmpeg-4.2.1-orange.svg)](https://ffmpeg.org/releases/ffmpeg-4.2.1.tar.bz2)[ ![X264](https://img.shields.io/badge/X264-20191217.2245-yellow.svg)](http://download.videolan.org/pub/videolan/x264/snapshots/x264-snapshot-20191217-2245-stable.tar.bz2)[ ![mp3lame](https://img.shields.io/badge/mp3lame-3.100-critical.svg)](https://sourceforge.net/projects/lame/files/latest/download)[ ![fdk-aac](https://img.shields.io/badge/fdkaac-2.0.1-ff69b4.svg)](https://downloads.sourceforge.net/opencore-amr/fdk-aac-2.0.1.tar.gz)[ ![fdk-aac](https://img.shields.io/badge/opencoreamr-1.1.5-critical.svg)](https://sourceforge.net/projects/opencore-amr/files/opencore-amr/opencore-amr-0.1.5.tar.gz)
+
+* **支持所有FFmpeg命令**
+* **支持视频格式转换 mp4->flv**
+* **支持音频编解码 mp3->pcm pcm->mp3 pcm->aac**
+* **支持音频转码 mp3->aac mp3->amr**
+* **支持视频编解码 mp4->yuv yuv->h264**
+* **支持视频转码 mp4->flv mp4->avi**
+* **支持音视频的剪切、拼接**
+* **支持视频转图片 mp4->png mp4->gif**
+* **支持音频声音大小控制以及混音(比如朗读的声音加上背景音乐)**
+* **支持部分滤镜 音频淡入、淡出效果、视频亮度和对比度以及添加水印**
+* **支持生成静音音频**
+* **支持获取媒体文件信息**
+* **支持连续执行FFmpeg命令**
+
+|执行FFmpeg|获取媒体信息|
+|---------| ----------------------------------|
+|||
+
+
+## 引入
+
+下面两种引入只选择一种即可,并根据最新版本替换下面的`${latestVersion}`,当前最新版本[ ![Download](https://api.bintray.com/packages/sourfeng/repositories/ffmpeg/images/download.svg) ](https://bintray.com/sourfeng/repositories/ffmpeg/_latestVersion)
+
+```groovy
+// 全部编解码-体积较大
+implementation 'com.coder.command:ffmpeg:${latestVersion}'
+// 部分常用编解码-体积较小,比上面引入减少大约6M
+implementation 'com.coder.command:ffmpeg-mini:${latestVersion}'
+```
+
+更改module下build.gradle,当前库只支持`armeabi-v7a`和`arm64-v8a`,当然也可以只使用一种(一般使用`armeabi-v7a`可以向下兼容),可以参考[【Android ABI】](https://developer.android.com/ndk/guides/abis)
+
+```groovy
+android {
+ defaultConfig {
+ ndk {
+ abiFilters "armeabi-v7a",'arm64-v8a'
+ moduleName "app"
+ }
+ }
+}
+```
+
+**如果没有特别的编解码需求,强烈推荐建议使用`ffmpeg-mini`**
+
+当然如果有特别的编解码需求,或者对包的大小有超高要求的,可以通过下方的群联系我进行私人定制。当然这个定制是**有偿的**,毕竟撸码不易,光阴似箭~~
+
+## 使用
+
+下面只展示部分使用,其他可以参考 **[【WIKI】](ffmpeg-wiki/Home.md)**
+
+### FFmpegCommand方法
+
+|方法 |功能 |
+|:---|----|
+|FFmpegCommand->setDebug(debug: Boolean)|Dubug模式,可打印日志,默认true|
+|FFmpegCommand->runCmd(cmd: Array)|执行ffmpeg命令,无回调|
+|FFmpegCommand->runCmd(cmd: Array callBack: IFFmpegCallBack?)|执行ffmpeg命令,并回调 开始,完成,取消,进度,错误|
+|FFmpegCommand->getMediaInfo(path: String?, @MediaAttribute type: Int)|获取媒体信息:视频宽高、比特率...|
+|FFmpegCommand->getSupportFormat(@FormatAttribute formatType: Int)|获取当前库支持的封装、解封装格式|
+|FFmpegCommand->getSupportCodec(@CodecAttribute codecType: Int)| 获取当前库支持的编解码 |
+|FFmpegCommand->cancel()|退出FFmpeg命令执行|
+
+### runCmd
+以`runCmd`调用`FFmpeg`为同步执行FFmpeg命令,外部需增加线程,否则会造成应用无响应。
+直接调用`FFmpegCommand.runCmd(cmd: Array callBack: IFFmpegCallBack?)`方法,其中第一个参数由`FFmpegUtils`工具类提供,也可以自己添加
+
+**不支持异步执行FFmpeg命令,毕竟C是面向过程语言,会出现资源占用问题**
+
+```kotlin
+GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.transformAudio(audioPath, targetPath), callback("音频转码完成", targetPath))
+}
+```
+
+第二个参数是回调方法
+```kotlin
+open class CommonCallBack : IFFmpegCallBack {
+ // 开始回调
+ override fun onStart() {}
+ // 进度回调
+ override fun onProgress(progress: Int, pts: Long) {}
+ // 取消回调
+ override fun onCancel() {}
+ // 完成回调
+ override fun onComplete() {}
+ // 错误回调
+ override fun onError(errorCode: Int, errorMsg: String?) {}
+}
+```
+
+需要注意的是在`onProgress`方法中,可以看到回调回了2个值:
+
+* progress:进度,参考第一个输入文件(即是第1个`i`之后的输入文件)计算得出,多个输入文件时可能出现不正确的情况
+* pts:已执行时间,progress出现不正确的使用当前值进行计算,计算方法如下
+
+```kotlin
+var duration :Int? = FFmpegCommand.getMediaInfo(mAudioPath,MediaAttribute.DURATION)
+var progress = pts/duration!!
+```
+
+### 自定义FFmpeg命令
+
+这里只是演示了音频剪切,很多如上述功能请自行查阅[FFmpegUtils](ffmpeg/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.java)
+如果其中不满足需求,可添加自己的FFmpeg命令.例如:
+
+```kotlin
+var command = "ffmpeg -y -i %s -vn -acodec copy -ss %d -t %d %s"
+command = String.format(command, srcFile, startTime, duration, targetFile)
+
+GlobalScope.launch {
+ FFmpegCommand.runCmd(command.split(" ").toTypedArray(), callback("音频剪切完成", targetPath))
+}
+
+```
+
+### 多进程执行
+由于底层暂时无法实现多线程(毕竟C是面向过程的语言),所以如果需要在推流的同时,是无法再同时执行其他命令。
+为了解决这个问题,可以使用如下多进程方法:
+
+1. 定义与主进程不同的其他进程
+```xml
+
+
+```
+
+2. 在其他进程中执行推流的操作
+```
+class FFmpegCommandService : Service() {
+ override fun onBind(intent: Intent): IBinder? {
+ return null
+ }
+
+ override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
+ val videoPath = File(externalCacheDir, "test.mp4").absolutePath
+ val output = File(externalCacheDir, "output.yuv").absolutePath
+ val cmd = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s"
+ val result = String.format(Locale.CHINA, cmd, videoPath, output)
+ val strings: Array = result.split(" ").toTypedArray()
+ FFmpegCommand.runCmd(strings)
+ return super.onStartCommand(intent, flags, startId)
+ }
+}
+```
+
+### 取消执行
+执行下面方法后将会回调 `CommonCallBack->onCancel()` 方法
+
+```java
+FFmpegCommand.cancel();
+```
+
+**[【常见问题】](ffmpeg-wiki/常见问题.md)**
+
+**[【版本更新】](UPDATE.md)**
+
+## 参考
+
+**[【KFFmpegCommandActivity-命令使用参考】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCommandActivity.kt)**
+**[【KFFmpegInfoActivity-媒体信息参考】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegInfoActivity.kt)**
+**[【KFFmppegFormatActivity-支持封装格式】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmppegFormatActivity.kt)**
+**[【KFFmpegCodecActivity-支持编解码】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCodecActivity.kt)**
+
+## 兼容性
+
+兼容Android minSdkVersion >=14(使用版本需要`1.1.4`及以上)
+
+
+
+
+
+## 编译SO
+
+[【编译FFmpeg在Android中使用】](ffmpeg-wiki/编译FFmpeg.md)
+[【自定义MP3编码器】](ffmpeg-wiki/自定义MP3编码器.md)
+
+
+## 体验交流
+
+| 扫码下载|[点击下载](http://fir.readdown.com/nfyz) | 交流|微信赞赏|
+| :--------: |:--------: |:--------: |
+| | | |
+
+## Start
+
+如果觉得对你有所帮助,给个Start支持一下吧,也欢迎多多fork!
+
+## 混淆
+
+```
+-keep class com.coder.ffmpeg.** {*;}
+-dontwarn com.coder.ffmpeg.**
+```
+
+## License
+```
+Copyright 2019 AnJoiner
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+```
diff --git a/README.md b/README.md
index cfc13f0..8e8203d 100644
--- a/README.md
+++ b/README.md
@@ -1,254 +1,216 @@
![FFmpegCommand](images/ffmpeg-command.png)
-
-## 前景提要
-在我们的开发中,经常会用到音视频相关内容,一般我们都会选择[FFmpeg](https://www.ffmpeg.org/),但是其交叉编译对于我们来说是一件很麻烦的事情.所以这里方便日后使用就编写了这个`FFmpegCommand`,`FFmpegCommand`是由`FFmpeg`核心库,并且集成了`lame`、`libx264`、`fdk-aac`和`libopencore-amr`主流音视频处理程序构成的Android程序
-
-**注意:当前库只适用于Android**
-
-如果访问不了全部信息,请跳转[【Gitee仓库】](https://gitee.com/anjoiner/FFmpegCommand)
-
-## 主要功能
+> To users of `FFmpegCommand`:
+>
+> First of all, thank you all for your support of this library. Thank you for using it so that we have the motivation to continue to open source. Thank you for your questions and make this library more perfect.
+>
+> Asynchronous processing and multi-code execution were provided before `1.2.0`, but many people reported that it is impossible to perform asynchronous and multi-code is not very useful, so after repeated consideration, the following changes will be made in `1.2.0` and later versions :
+>
+> * Delete the `runCmdAsync` and `runCmdSync` methods and change them to `runCmd` to execute the `FFmpeg` command
+> * Delete multi-command `runMoreAsync` and `runMoreSync` methods, `runCmd` internally realizes automatic synchronization and sequential execution
+> * Added error log prompt, use `ffmpeg-cmd` to filter the error log when an error occurs
+>
+> We apologize for the inconvenience caused by this modification.
+
+[【README-中文】](./README-CN.md)
+
+## Summary
+In our development, audio and video related content is often used, generally we will choose [FFmpeg](https://www.ffmpeg.org/), but its cross-compilation is a very troublesome thing for us . So here for the convenience of future use, I wrote this `FFmpegCommand`, `FFmpegCommand` is composed of `FFmpeg` core library, and integrates `lame`, `libx264`, `fdk-aac` and `libopencore-amr` mainstream audio and video processing Android program
+**Note: The current library is only available for Android**
+
+If you can’t access all the information, please go to[【Domestic Mirror】](https://gitee.com/anjoiner/FFmpegCommand)
+
+## The main function
[ ![Download](https://api.bintray.com/packages/sourfeng/repositories/ffmpeg/images/download.svg) ](https://bintray.com/sourfeng/repositories/ffmpeg/_latestVersion)[![License](https://img.shields.io/badge/license-Apache%202-success.svg)](https://www.apache.org/licenses/LICENSE-2.0)[ ![FFmpeg](https://img.shields.io/badge/FFmpeg-4.2.1-orange.svg)](https://ffmpeg.org/releases/ffmpeg-4.2.1.tar.bz2)[ ![X264](https://img.shields.io/badge/X264-20191217.2245-yellow.svg)](http://download.videolan.org/pub/videolan/x264/snapshots/x264-snapshot-20191217-2245-stable.tar.bz2)[ ![mp3lame](https://img.shields.io/badge/mp3lame-3.100-critical.svg)](https://sourceforge.net/projects/lame/files/latest/download)[ ![fdk-aac](https://img.shields.io/badge/fdkaac-2.0.1-ff69b4.svg)](https://downloads.sourceforge.net/opencore-amr/fdk-aac-2.0.1.tar.gz)[ ![fdk-aac](https://img.shields.io/badge/opencoreamr-1.1.5-critical.svg)](https://sourceforge.net/projects/opencore-amr/files/opencore-amr/opencore-amr-0.1.5.tar.gz)
-* **支持所有FFmpeg命令**
-* **支持视频格式转换 mp4->flv**
-* **支持音频编解码 mp3->pcm pcm->mp3 pcm->aac**
-* **支持音频转码 mp3->aac mp3->amr**
-* **支持视频编解码 mp4->yuv yuv->h264**
-* **支持视频转码 mp4->flv mp4->avi**
-* **支持音视频的剪切、拼接**
-* **支持视频转图片 mp4->png mp4->gif**
-* **支持音频声音大小控制以及混音(比如朗读的声音加上背景音乐)**
-* **支持部分滤镜 音频淡入、淡出效果、视频亮度和对比度以及添加水印**
-* **支持获取媒体文件信息**
-* **支持多命令同步执行**
-
-|执行FFmpeg|获取媒体信息|
+* **Support all FFmpeg commands**
+* **Support video format conversion : mp4->flv**
+* **Support audio codec : mp3->pcm pcm->mp3 pcm->aac**
+* **Support audio transcoding : mp3->aac mp3->amr**
+* **Support video codec : mp4->yuv yuv->h264**
+* **Support video transcoding : mp4->flv mp4->avi**
+* **Support cutting and splicing of audio and video**
+* **Support video to picture : mp4->png mp4->gif**
+* **Support audio sound size control and mixing (such as reading sound plus background music)**
+* **Support some filters, audio fade in, fade out effects, video brightness and contrast, and add watermark**
+* **Support for generating silent audio**
+* **Support for obtaining media file information**
+* **Support continuous execution of FFmpeg commands**
+
+|Run FFmpeg|Get media information|
|---------| ----------------------------------|
|||
-## 引入
+## Introduce
-下面两种引入只选择一种即可,并根据最新版本替换下面的`${latestVersion}`,当前最新版本[ ![Download](https://api.bintray.com/packages/sourfeng/repositories/ffmpeg/images/download.svg) ](https://bintray.com/sourfeng/repositories/ffmpeg/_latestVersion)
+Choose only one of the following two introductions, and replace the following according to the latest version `${latestVersion}`,Current latest version[ ![Download](https://api.bintray.com/packages/sourfeng/repositories/ffmpeg/images/download.svg) ](https://bintray.com/sourfeng/repositories/ffmpeg/_latestVersion)
```groovy
-// 全部编解码-体积较大
+// All codecs-larger size
implementation 'com.coder.command:ffmpeg:${latestVersion}'
-// 部分常用编解码-体积较小,比上面引入减少大约6M
+// Some commonly used codecs-smaller in size, about 6M less than the introduction above
implementation 'com.coder.command:ffmpeg-mini:${latestVersion}'
```
-**如果没有特别的编解码需求,强烈推荐建议使用`ffmpeg-mini`**
-
-当然如果有特别的编解码需求,或者对包的大小有超高要求的,可以通过下方的群联系我进行私人定制。当然这个定制是**有偿的**,毕竟撸码不易,光阴似箭~~
+Change build.gradle under module, the current library only supports `armeabi-v7a` and `arm64-v8a`, of course you can use only one (usually using `armeabi-v7a` for backward compatibility). You can Can refer to [【Android ABI】](https://developer.android.com/ndk/guides/abis)
-## 使用
+```groovy
+android {
+ defaultConfig {
+ ndk {
+ abiFilters "armeabi-v7a",'arm64-v8a'
+ moduleName "app"
+ }
+ }
+}
+```
-下面只展示部分使用,其他可以参考 **[【WIKI】](ffmpeg-wiki/Home.md)**
+**If there is no special codec requirement, it is strongly recommended to use `ffmpeg-mini`**
-### FFmpegCommand方法
+Of course, if you have special coding and decoding requirements, or have high requirements on the size of the package, you can contact me through the group below for private customization. Of course, this customization is **paid**, after all, it is not easy to code, the time is like an arrow~~
-|方法 |功能 |
-|:---|----|
-|FFmpegCommand->setDebug(boolean debug)|Dubug模式,可打印日志,默认true|
-|FFmpegCommand->runSync(final String[] cmd)|同步执行ffmpeg命令,外部需添加延时线程|
-|FFmpegCommand->runSync(final String[] cmd, OnFFmpegCommandListener listener)|同步执行ffmpeg命令,并回调 完成,取消,进度|
-|FFmpegCommand->runAsync(final String[] cmd, IFFmpegCallBack callBack)|异步执行,外部无需添加延时线程,并回调 开始,完成,取消,进度|
-|FFmpegCommand->getInfoSync(String path,@Attribute int type)|获取媒体信息,type值必须为`@Attribute`中注解参数|
-|FFmpegCommand->cancel()| 退出当前ffmpeg执行 |
-|FFmpegCommand->runMoreSync(List cmds, OnFFmpegCommandListener listener)|同步多命令执行,并回调 完成,取消,进度|
-
-### 使用runAsync
-以`runAsync`调用`FFmpeg`为异步方式,不需要单独开启子线程。强烈建议使用此方法进行音视频处理!!!
-直接调用`FFmpegCommand.runAsync(String[] cmd, IFFmpegCallBack callback)`方法,其中第一个参数由`FFmpegUtils`工具类提供,也可以自己添加
+## Use
-```java
-final long startTime = System.currentTimeMillis();
+### FFmpegCommand Method
-FFmpegCommand.runAsync(FFmpegUtils.cutAudio(input, "00:00:30", "00:00:40", output),
- new CommonCallBack() {
- @Override
- public void onComplete() {
- Log.d("FFmpegTest", "run: 耗时:" + (System.currentTimeMillis() - startTime));
+|Method |Function |
+|:---|----|
+|FFmpegCommand->setDebug(debug: Boolean)|Debug mode, printable log, default true|
+|FFmpegCommand->runCmd(cmd: Array)|Execute ffmpeg command without callback|
+|FFmpegCommand->runCmd(cmd: Array callBack: IFFmpegCallBack?)|Execute ffmpeg command and call back start, complete, cancel, progress, error|
+|FFmpegCommand->getMediaInfo(path: String?, @MediaAttribute type: Int)|Get media information: video width and height, bit rate...|
+|FFmpegCommand->getSupportFormat(@FormatAttribute formatType: Int)|Get the encapsulation and decapsulation formats supported by the current library|
+|FFmpegCommand->getSupportCodec(@CodecAttribute codecType: Int)| Get the codec supported by the current library |
+|FFmpegCommand->cancel()|Exit FFmpeg command execution|
- @Override
- public void onCancel() {
- Log.d("FFmpegTest", "Cancel");
- }
+### runCmd
+Use `runCmd` to call `FFmpeg` to execute FFmpeg commands synchronously. External threads need to be added, otherwise the application will become unresponsive.
+Direct call `FFmpegCommand.runCmd(cmd: Array callBack: IFFmpegCallBack?)` method,The first parameter is provided by the `FFmpegUtils` tool class, or you can add it yourself
- @Override
- public void onProgress(int progress) {
- Log.d("FFmpegTest",progress+"");
- }
- }
-});
+**Does not support asynchronous execution of FFmpeg commands, after all, C is a process-oriented language, and resource occupation problems will occur**
+```kotlin
+GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.transformAudio(audioPath, targetPath), callback("transcoding complete", targetPath))
+}
```
-### 自定义FFmpeg命令
-
-这里只是演示了音频剪切,很多如上述功能请自行查阅[FFmpegUtils](https://github.com/AnJoiner/FFmpegCommand/blob/master/ffmpeg/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.java)
-如果其中不满足需求,可添加自己的FFmpeg命令.例如:
-```java
-String cmd = "ffmpeg -y -i %s -vn -acodec copy -ss %s -t %s %s";
-String result = String.format(cmd, input, "00:00:30", "00:00:40", output);
-
-FFmpegCommand.runAsync(result.split(" "), new CommonCallBack() {
- @Override
- public void onComplete() {
- Log.d("FFmpegTest", "run: 耗时:" + (System.currentTimeMillis() - startTime));
- }
-
- @Override
- public void onCancel() {
- Log.d("FFmpegTest", "Cancel");
- }
-
- @Override
- public void onProgress(int progress) {
- Log.d("FFmpegTest",progress+"");
- }
-})
+The second parameter is the callback method
+```kotlin
+open class CommonCallBack : IFFmpegCallBack {
+ // Start callback
+ override fun onStart() {}
+ // Progress callback
+ override fun onProgress(progress: Int, pts: Long) {}
+ // Cancel callback
+ override fun onCancel() {}
+ // Complete callback
+ override fun onComplete() {}
+ // Error callback
+ override fun onError(errorCode: Int, errorMsg: String?) {}
+}
```
-### 多命令执行
-
-在`1.1.5`版本新增了多命令执行方式,可以多条命令一同执行,可返回总进度,提供了两种方式去实现。
+It should be noted that in the `onProgress` method, you can see that the callback returns 2 values:
-* **runMoreSync** 多条命令同步执行
-* **runMoreAsync** 多条命令异步执行
+* progress:progress, calculated by referring to the first input file (that is the input file after the first `-i`), and it may be incorrect when there are multiple input files
+* pts:Elapsed time, progress appears incorrectly using the current value for calculation, the calculation method is as follows
```kotlin
-
-FFmpegCommand.runMoreSync(st, object : FFmpegCommand.OnFFmpegCommandListener {
- override fun onProgress(progress: Int) {
- val msg = Message()
- msg.what = 1
- msg.arg1 = progress
- handler.sendMessage(msg)
- Log.d("runMoreSync", "globalProgress:$progress")
- }
-
- override fun onCancel() {
- Log.d("runMoreSync", "onCancel")
- val msg = Message()
- msg.what = -1
- handler.sendMessage(msg)
- }
-
- override fun onComplete() {
- val target = targetAAC + "\n" + targetAVI + "\n" + targetYUV
-
- val msg = Message()
- msg.what = 0
- msg.obj = target
- handler.sendMessage(msg)
-
- Log.d("runMoreSync", "onComplete")
- }
-})
+var duration :Int? = FFmpegCommand.getMediaInfo(mAudioPath,MediaAttribute.DURATION)
+var progress = pts/duration!!
```
-需要注意的是:
+### Custom FFmpeg command
-在`1.1.5`版本之后可以使用**多条同步命令**进行执行,但**不可**同时使用**多条异步命令**
+This is just a demonstration of audio cutting, many functions such as the above, please refer to it yourself [FFmpegUtils](ffmpeg/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.java)
+If the requirements are not met, you can add your own FFmpeg command, E.g:
```kotlin
-Thread(Runnable {
- FFmpegCommand.runSync(FFmpegUtils.transformAudio(audioPath, targetPath), object : FFmpegCommand.OnFFmpegCommandListener{})
- FFmpegCommand.runSync(FFmpegUtils.decode2YUV(mVideoPath, targetPath), object : FFmpegCommand.OnFFmpegCommandListener{})
- FFmpegCommand.runSync(FFmpegUtils.transformVideo(videoPath, targetPath), object : FFmpegCommand.OnFFmpegCommandListener())
-}).start()
+var command = "ffmpeg -y -i %s -vn -acodec copy -ss %d -t %d %s"
+command = String.format(command, srcFile, startTime, duration, targetFile)
+
+GlobalScope.launch {
+ FFmpegCommand.runCmd(command.split(" ").toTypedArray(), callback("Audio cut is complete", targetPath))
+}
+
```
-### 多进程执行
-由于底层暂时无法实现多线程(资源占用问题),所以如果需要在推流的同时,是无法再同时执行其他命令。为了解决这个问题,可以使用如下多进程方法:
+### Multi-process execution
+Since the bottom layer is temporarily unable to implement multithreading (after all, C is a process-oriented language), if you need to push the stream at the same time, it is impossible to execute other commands at the same time.
+To solve this problem, you can use the following multi-process method:
-1. 定义与主进程不同的其他进程
+1. Define other processes different from the main process
```xml
```
-2. 在其他进程中执行推流的操作
+2. Perform push operations in other processes
```
-public class FFmpegCommandService2 extends Service {
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- return null;
+class FFmpegCommandService : Service() {
+ override fun onBind(intent: Intent): IBinder? {
+ return null
}
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
-
- String videoPath = new File(getExternalCacheDir(), "test.mp4").getAbsolutePath();
-
- String output = new File(getExternalCacheDir(), "output3.yuv").getAbsolutePath();
-
- String cmd = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s";
- final String result = String.format(Locale.CHINA, cmd, videoPath, output);
- final String[] strings = result.split(" ");
-
- new Thread(new Runnable() {
- @Override
- public void run() {
- FFmpegCommand.runSync(strings);
- }
- }).start();
-
- return super.onStartCommand(intent, flags, startId);
+ override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
+ val videoPath = File(externalCacheDir, "test.mp4").absolutePath
+ val output = File(externalCacheDir, "output.yuv").absolutePath
+ val cmd = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s"
+ val result = String.format(Locale.CHINA, cmd, videoPath, output)
+ val strings: Array = result.split(" ").toTypedArray()
+ FFmpegCommand.runCmd(strings)
+ return super.onStartCommand(intent, flags, startId)
}
}
```
-### 取消执行
-执行下面方法后将会回调 `CommonCallBack->onCancel()` 或 `OnFFmpegCommandListener->onCancel()` 方法
+### Cancel execution
+After executing the following method, the `CommonCallBack->onCancel()` method will be called back
```java
FFmpegCommand.cancel();
```
-**[【功能详解】](ffmpeg-wiki/详细功能.md)**
+**[【common problem】](ffmpeg-wiki/常见问题.md)**
-**[【常见问题】](ffmpeg-wiki/常见问题.md)**
+**[【new version update】](UPDATE.md)**
-**[【版本更新】](UPDATE.md)**
+## Reference
-## 参考
+**[【KFFmpegCommandActivity-Command reference】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCommandActivity.kt)**
+**[【KFFmpegInfoActivity-Media Information Reference】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegInfoActivity.kt)**
+**[【KFFmppegFormatActivity-Support package format】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmppegFormatActivity.kt)**
+**[【KFFmpegCodecActivity-Support codec】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCodecActivity.kt)**
-**[【KFFmpegCommandActivity】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCommandActivity.kt)**
-**[【KFFmpegMoreCommandActivity】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegMoreCommandActivity.kt)**
-**[【KFFmpegInfoActivity】](app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegInfoActivity.kt)**
+## Compatibility
-## 兼容性
-兼容Android minSdkVersion >=14(version>=1.1.4,此前的版本只兼容minSdkVersion >=21)
+Compatible with Android minSdkVersion >=14 (use version requires `1.1.4` and above)
-## 编译SO
+## Compile SO
-[【编译FFmpeg在Android中使用】](ffmpeg-wiki/编译FFmpeg.md)
-[【自定义MP3编码器】](ffmpeg-wiki/自定义MP3编码器.md)
+[【Compile FFmpeg for use in Android】](ffmpeg-wiki/编译FFmpeg.md)
+[【Custom MP3 encoder】](ffmpeg-wiki/自定义MP3编码器.md)
-## 体验交流
+## Experiential exchange
-| 扫码下载|[点击下载](http://fir.readdown.com/nfyz) | 交流|微信赞赏|
+| Scan code to download|[click to download](http://fir.readdown.com/nfyz) | communication |WeChat appreciation|
| :--------: |:--------: |:--------: |
-| | | |
+| | | |
## Start
-如果觉得对你有所帮助,给个Start支持一下吧,也欢迎多多fork!
+If you think it is helpful to you, give a start to support it, and welcome a lot of forks!
-## 混淆
+## Confuse
```
-keep class com.coder.ffmpeg.** {*;}
diff --git a/app/build.gradle b/app/build.gradle
index a79220e..af70c31 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -26,8 +26,8 @@ android {
applicationId "com.coder.ffmpegtest"
minSdkVersion 15
targetSdkVersion 29
- versionCode 2
- versionName "1.1.7"
+ versionCode 3
+ versionName "1.2.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
@@ -62,12 +62,11 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
- compile project(path: ':ffmpeg-mini')
+ implementation project(path: ':ffmpeg-mini')
implementation 'androidx.recyclerview:recyclerview:1.1.0'
- implementation 'com.jakewharton:butterknife:10.2.1'
- annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
- debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4'
// 腾讯bugly
implementation 'com.tencent.bugly:crashreport:latest.release'
implementation 'com.tencent.bugly:nativecrashreport:latest.release'
+
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1"
}
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/coder/ffmpegtest/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/coder/ffmpegtest/ExampleInstrumentedTest.java
deleted file mode 100644
index a0bb1d1..0000000
--- a/app/src/androidTest/java/com/coder/ffmpegtest/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.coder.ffmpegtest;
-
-import android.content.Context;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- assertEquals("com.coder.ffmpegtest", appContext.getPackageName());
- }
-}
diff --git a/app/src/androidTest/java/com/coder/ffmpegtest/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/coder/ffmpegtest/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..9702591
--- /dev/null
+++ b/app/src/androidTest/java/com/coder/ffmpegtest/ExampleInstrumentedTest.kt
@@ -0,0 +1,22 @@
+package com.coder.ffmpegtest
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see [Testing documentation](http://d.android.com/tools/testing)
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ Assert.assertEquals("com.coder.ffmpegtest", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/BaseApplication.java b/app/src/main/java/com/coder/ffmpegtest/BaseApplication.java
deleted file mode 100644
index f4c7a65..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/BaseApplication.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.coder.ffmpegtest;
-
-import android.app.Application;
-
-import com.coder.ffmpeg.jni.FFmpegCommand;
-import com.tencent.bugly.crashreport.CrashReport;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-20
- */
-public class BaseApplication extends Application {
- private static BaseApplication instance;
-
- @Override
- public void onCreate() {
- super.onCreate();
- instance = this;
- FFmpegCommand.setDebug(true);
- CrashReport.initCrashReport(getApplicationContext(), "d7b0e14940", true);
- }
-
- public static BaseApplication getInstance() {
- return instance;
- }
-}
diff --git a/app/src/main/java/com/coder/ffmpegtest/BaseApplication.kt b/app/src/main/java/com/coder/ffmpegtest/BaseApplication.kt
new file mode 100644
index 0000000..47f7e29
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/BaseApplication.kt
@@ -0,0 +1,23 @@
+package com.coder.ffmpegtest
+
+import android.app.Application
+import com.coder.ffmpeg.jni.FFmpegCommand
+import com.tencent.bugly.crashreport.CrashReport
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-20
+ */
+class BaseApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ FFmpegCommand.setDebug(true)
+ CrashReport.initCrashReport(applicationContext, "d7b0e14940", true)
+ }
+
+ companion object {
+ var instance: BaseApplication? = null
+ private set
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/base/BaseDialog.java b/app/src/main/java/com/coder/ffmpegtest/base/BaseDialog.java
deleted file mode 100644
index 144e731..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/base/BaseDialog.java
+++ /dev/null
@@ -1,240 +0,0 @@
-package com.coder.ffmpegtest.base;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-
-import java.lang.reflect.Field;
-
-import androidx.annotation.IdRes;
-import androidx.annotation.LayoutRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.DialogFragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentTransaction;
-import butterknife.ButterKnife;
-import butterknife.Unbinder;
-import io.reactivex.disposables.CompositeDisposable;
-
-public abstract class BaseDialog extends DialogFragment {
-
-
- protected View mRootView;
- protected LayoutInflater inflater;
- // 标志位 标志已经初始化完成。
- protected boolean isPrepared;
- //标志位 fragment是否可见
- protected boolean isVisible;
-
- protected Context mContext;
- protected Activity mActivity;
- protected CompositeDisposable disposables;
-
- private Unbinder unBinder;
-
-
- protected OnDismissListener mOnDismissListener;
-
-
- public void setOnDismissListener(OnDismissListener onDismissListener) {
- mOnDismissListener = onDismissListener;
- }
-
- @Override
- public void onAttach(Context context) {
- mActivity = (Activity) context;
- mContext = context;
- super.onAttach(context);
- }
-
- @Nullable
- @Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable
- Bundle savedInstanceState) {
- getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
- if (mRootView != null) {
- ViewGroup parent = (ViewGroup) mRootView.getParent();
- if (parent != null) {
- parent.removeView(mRootView);
- }
- } else {
- mRootView = inflater.inflate(getLayoutId(), container, false);
- mActivity = getActivity();
- mContext = mActivity;
- this.inflater = inflater;
- }
- unBinder = ButterKnife.bind(this, mRootView);
-
- return mRootView;
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
- getDialog().getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.MATCH_PARENT);
- getDialog().setCancelable(false);
- getDialog().setCanceledOnTouchOutside(true);
- }
-
- @Override
- public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- disposables = new CompositeDisposable();
- isPrepared = true;
- init();
- lazyLoad();
- }
-
- @Override
- public void onSaveInstanceState(@NonNull Bundle outState) {
- super.onSaveInstanceState(outState);
- }
-
-
- /**
- * 获取布局
- */
- @LayoutRes
- public abstract int getLayoutId();
-
- /**
- * 初始化
- */
- protected abstract void init();
-
- public View findViewById(@IdRes int id) {
- View view;
- if (mRootView != null) {
- view = mRootView.findViewById(id);
- return view;
- }
- return null;
- }
-
- /**
- * 懒加载
- */
- private void lazyLoad() {
- if (!isPrepared || !isVisible) {
- return;
- }
- lazyLoadData();
- isPrepared = false;
- }
-
- /**
- * 懒加载
- */
- protected void lazyLoadData() {
-
- }
-
- protected void onVisible() {
- lazyLoad();
- }
-
- protected void onInvisible() {
-
- }
-
- public interface OnDismissListener {
- void onDismiss(DialogInterface dialog);
- }
-
-
- @Override
- public void setUserVisibleHint(boolean isVisibleToUser) {
- super.setUserVisibleHint(isVisibleToUser);
- if (getUserVisibleHint()) {
- isVisible = true;
- onVisible();
- } else {
- isVisible = false;
- onInvisible();
- }
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- unBinder.unbind();
- disposables.dispose();
- }
-
- @Override
- public void onDetach() {
- this.mActivity = null;
- super.onDetach();
- }
-
- @Override
- public void onDismiss(DialogInterface dialog) {
- super.onDismiss(dialog);
- if (mOnDismissListener != null) {
- mOnDismissListener.onDismiss(dialog);
- }
- }
-
-
- protected void hideSoftKeyboard(View view) {
- try {
- InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context
- .INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
- }
- } catch (Exception e) {
-
- }
- }
-
- /**
- * dialog fragment中如下方法不启用。
- */
-
- public void setCanceledOnTouchOutside(boolean canceledOnTouchOutside) {
- getDialog().setCanceledOnTouchOutside(canceledOnTouchOutside);
- }
-
-
- @Override
- public void show(FragmentManager manager, String tag) {
- reflection();
- FragmentTransaction ft = manager.beginTransaction();
- ft.add(this, tag);
- // 防止: Caused by: java.lang.IllegalStateException: Can not perform this action after
- // onSaveInstanceState
- ft.commitAllowingStateLoss();
- }
-
- /**
- * 通过反射操作 mDismissed,mShownByMe两个参数
- */
- private void reflection() {
- try {
- Field protectDismissed = DialogFragment.class.getDeclaredField("mDismissed");
- Field protectShownByMe = DialogFragment.class.getDeclaredField("mShownByMe");
- protectDismissed.setAccessible(true);
- protectShownByMe.setAccessible(true);
- protectDismissed.setBoolean(this, false);
- protectShownByMe.setBoolean(this, true);
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
-
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/base/BaseDialog.kt b/app/src/main/java/com/coder/ffmpegtest/base/BaseDialog.kt
new file mode 100644
index 0000000..77f178f
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/base/BaseDialog.kt
@@ -0,0 +1,181 @@
+package com.coder.ffmpegtest.base
+
+import android.app.Activity
+import android.content.Context
+import android.content.DialogInterface
+import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.view.*
+import android.view.inputmethod.InputMethodManager
+import androidx.annotation.IdRes
+import androidx.annotation.LayoutRes
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentManager
+
+abstract class BaseDialog : DialogFragment() {
+ protected var mRootView: View? = null
+ protected var inflater: LayoutInflater? = null
+
+ // 标志位 标志已经初始化完成。
+ protected var isPrepared = false
+
+ //标志位 fragment是否可见
+ protected var isVisible:Boolean? = false
+ protected var mContext: Context? = null
+ protected var mActivity: Activity? = null
+ protected var mOnDismissListener: OnDismissListener? = null
+ fun setOnDismissListener(onDismissListener: OnDismissListener?) {
+ mOnDismissListener = onDismissListener
+ }
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ mActivity = context as Activity
+ mContext = context
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ dialog!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
+ if (mRootView != null) {
+ val parent = mRootView!!.parent as ViewGroup
+ parent?.removeView(mRootView)
+ } else {
+ mRootView = inflater.inflate(layoutId, container, false)
+ mActivity = activity
+ mContext = mActivity
+ this.inflater = inflater
+ }
+ return mRootView
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ dialog!!.window!!.setBackgroundDrawable(ColorDrawable(0x00000000))
+ dialog!!.window!!.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT)
+ dialog!!.setCancelable(false)
+ dialog!!.setCanceledOnTouchOutside(true)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ isPrepared = true
+ init()
+ lazyLoad()
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ }
+
+ /**
+ * 获取布局
+ */
+ @get:LayoutRes
+ abstract val layoutId: Int
+
+ /**
+ * 初始化
+ */
+ protected abstract fun init()
+ fun findViewById(@IdRes id: Int): View? {
+ val view: View
+ if (mRootView != null) {
+ view = mRootView!!.findViewById(id)
+ return view
+ }
+ return null
+ }
+
+ /**
+ * 懒加载
+ */
+ private fun lazyLoad() {
+ if (!isPrepared || !isVisible!!) {
+ return
+ }
+ lazyLoadData()
+ isPrepared = false
+ }
+
+ /**
+ * 懒加载
+ */
+ protected fun lazyLoadData() {}
+ protected fun onVisible() {
+ lazyLoad()
+ }
+
+ protected fun onInvisible() {}
+ interface OnDismissListener {
+ fun onDismiss(dialog: DialogInterface?)
+ }
+
+ override fun setUserVisibleHint(isVisibleToUser: Boolean) {
+ super.setUserVisibleHint(isVisibleToUser)
+ if (userVisibleHint) {
+ isVisible = true
+ onVisible()
+ } else {
+ isVisible = false
+ onInvisible()
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ }
+
+ override fun onDetach() {
+ mActivity = null
+ super.onDetach()
+ }
+
+ override fun onDismiss(dialog: DialogInterface) {
+ super.onDismiss(dialog)
+ if (mOnDismissListener != null) {
+ mOnDismissListener!!.onDismiss(dialog)
+ }
+ }
+
+ protected fun hideSoftKeyboard(view: View) {
+ try {
+ val imm = mContext!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ imm?.hideSoftInputFromWindow(view.windowToken, 0)
+ } catch (e: Exception) {
+ }
+ }
+
+ /**
+ * dialog fragment中如下方法不启用。
+ */
+ fun setCanceledOnTouchOutside(canceledOnTouchOutside: Boolean) {
+ dialog!!.setCanceledOnTouchOutside(canceledOnTouchOutside)
+ }
+
+ override fun show(manager: FragmentManager, tag: String?) {
+ reflection()
+ val ft = manager.beginTransaction()
+ ft.add(this, tag)
+ // 防止: Caused by: java.lang.IllegalStateException: Can not perform this action after
+ // onSaveInstanceState
+ ft.commitAllowingStateLoss()
+ }
+
+ /**
+ * 通过反射操作 mDismissed,mShownByMe两个参数
+ */
+ private fun reflection() {
+ try {
+ val protectDismissed = DialogFragment::class.java.getDeclaredField("mDismissed")
+ val protectShownByMe = DialogFragment::class.java.getDeclaredField("mShownByMe")
+ protectDismissed.isAccessible = true
+ protectShownByMe.isAccessible = true
+ protectDismissed.setBoolean(this, false)
+ protectShownByMe.setBoolean(this, true)
+ } catch (e: NoSuchFieldException) {
+ e.printStackTrace()
+ } catch (e: IllegalAccessException) {
+ e.printStackTrace()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/model/CommandBean.java b/app/src/main/java/com/coder/ffmpegtest/model/CommandBean.java
deleted file mode 100644
index 7cd9b0a..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/model/CommandBean.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.coder.ffmpegtest.model;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-30
- */
-public class CommandBean {
- private String name;
- private int id;
-
- public CommandBean(String name, int id) {
- this.name = name;
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-}
diff --git a/app/src/main/java/com/coder/ffmpegtest/model/CommandBean.kt b/app/src/main/java/com/coder/ffmpegtest/model/CommandBean.kt
new file mode 100644
index 0000000..d181465
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/model/CommandBean.kt
@@ -0,0 +1,7 @@
+package com.coder.ffmpegtest.model
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-30
+ */
+class CommandBean(var name: String, var id: Int)
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService.java b/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService.java
deleted file mode 100644
index 9687aea..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.coder.ffmpegtest.service;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-import com.coder.ffmpeg.jni.FFmpegCommand;
-
-import java.io.File;
-import java.util.Locale;
-
-import androidx.annotation.Nullable;
-
-/**
- * @author: AnJoiner
- * @datetime: 20-6-28
- */
-public class FFmpegCommandService extends Service {
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
-// String videoPath =
-// Environment.getExternalStorageDirectory().getPath() + File.separator +
-// "DCIM" + File.separator + "test.mp4";
- String videoPath = new File(getExternalCacheDir(), "test.mp4").getAbsolutePath();
-
- String output = new File(getExternalCacheDir(), "output.yuv").getAbsolutePath();
-
- String cmd = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s";
- final String result = String.format(Locale.CHINA, cmd, videoPath, output);
- final String[] strings = result.split(" ");
-
- new Thread(new Runnable() {
- @Override
- public void run() {
- FFmpegCommand.runSync(strings);
- }
- }).start();
-
- return super.onStartCommand(intent, flags, startId);
- }
-}
diff --git a/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService.kt b/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService.kt
new file mode 100644
index 0000000..2f003b2
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService.kt
@@ -0,0 +1,28 @@
+package com.coder.ffmpegtest.service
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import com.coder.ffmpeg.jni.FFmpegCommand
+import java.io.File
+import java.util.*
+
+/**
+ * @author: AnJoiner
+ * @datetime: 20-6-28
+ */
+class FFmpegCommandService : Service() {
+ override fun onBind(intent: Intent): IBinder? {
+ return null
+ }
+
+ override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
+ val videoPath = File(externalCacheDir, "test.mp4").absolutePath
+ val output = File(externalCacheDir, "output.yuv").absolutePath
+ val cmd = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s"
+ val result = String.format(Locale.CHINA, cmd, videoPath, output)
+ val strings: Array = result.split(" ").toTypedArray()
+ FFmpegCommand.runCmd(strings)
+ return super.onStartCommand(intent, flags, startId)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService2.java b/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService2.java
deleted file mode 100644
index 8671a17..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService2.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.coder.ffmpegtest.service;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-import com.coder.ffmpeg.jni.FFmpegCommand;
-
-import java.io.File;
-import java.util.Locale;
-
-import androidx.annotation.Nullable;
-
-/**
- * @author: AnJoiner
- * @datetime: 20-6-28
- */
-public class FFmpegCommandService2 extends Service {
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
-
- String videoPath = new File(getExternalCacheDir(), "test.mp4").getAbsolutePath();
-
- String output = new File(getExternalCacheDir(), "output3.yuv").getAbsolutePath();
-
- String cmd = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s";
- final String result = String.format(Locale.CHINA, cmd, videoPath, output);
- final String[] strings = result.split(" ");
-
- new Thread(new Runnable() {
- @Override
- public void run() {
- FFmpegCommand.runSync(strings);
- }
- }).start();
-
- return super.onStartCommand(intent, flags, startId);
- }
-}
diff --git a/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService2.kt b/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService2.kt
new file mode 100644
index 0000000..3390e30
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/service/FFmpegCommandService2.kt
@@ -0,0 +1,28 @@
+package com.coder.ffmpegtest.service
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import com.coder.ffmpeg.jni.FFmpegCommand
+import java.io.File
+import java.util.*
+
+/**
+ * @author: AnJoiner
+ * @datetime: 20-6-28
+ */
+class FFmpegCommandService2 : Service() {
+ override fun onBind(intent: Intent): IBinder? {
+ return null
+ }
+
+ override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
+ val videoPath = File(externalCacheDir, "test.mp4").absolutePath
+ val output = File(externalCacheDir, "output3.yuv").absolutePath
+ val cmd = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s"
+ val result = String.format(Locale.CHINA, cmd, videoPath, output)
+ val strings: Array = result.split(" ").toTypedArray()
+ FFmpegCommand.runCmd(strings)
+ return super.onStartCommand(intent, flags, startId)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/FFmpegCommandActivity.java b/app/src/main/java/com/coder/ffmpegtest/ui/FFmpegCommandActivity.java
deleted file mode 100644
index b2114cc..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/ui/FFmpegCommandActivity.java
+++ /dev/null
@@ -1,545 +0,0 @@
-package com.coder.ffmpegtest.ui;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.widget.TextView;
-
-import com.coder.ffmpeg.annotation.Direction;
-import com.coder.ffmpeg.annotation.ImageFormat;
-import com.coder.ffmpeg.annotation.Transpose;
-import com.coder.ffmpeg.call.CommonCallBack;
-import com.coder.ffmpeg.jni.FFmpegCommand;
-import com.coder.ffmpeg.utils.FFmpegUtils;
-import com.coder.ffmpegtest.R;
-import com.coder.ffmpegtest.model.CommandBean;
-import com.coder.ffmpegtest.ui.adapter.FFmpegCommandAdapter;
-import com.coder.ffmpegtest.ui.dialog.PromptDialog;
-import com.coder.ffmpegtest.utils.CustomProgressDialog;
-import com.coder.ffmpegtest.utils.FileUtils;
-import com.coder.ffmpegtest.utils.ToastUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-30
- */
-public class FFmpegCommandActivity extends AppCompatActivity {
-
-
- private String mAudioPath;
- private String mVideoPath;
- private String targetPath;
- private String mAudioBgPath;
- private String mImagePath;
-
- private TextView tvContent;
-
- private RecyclerView mRecyclerView;
- private FFmpegCommandAdapter mAdapter;
-
- private PromptDialog mErrorDialog;
-
- public static void start(Context context) {
- Intent starter = new Intent(context, FFmpegCommandActivity.class);
- context.startActivity(starter);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_ffmpeg_command);
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
- != PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(this,
- new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
- 100);
- }
- init();
- }
-
- private void init(){
- initView();
- initData();
- initListener();
- }
-
- private void initView(){
- mRecyclerView = findViewById(R.id.rv);
- tvContent = findViewById(R.id.tv_content);
- }
-
- private void initData(){
- FileUtils.copy2Memory(this, "test.mp3");
- FileUtils.copy2Memory(this, "test.mp4");
- FileUtils.copy2Memory(this, "testbg.mp3");
- FileUtils.copy2Memory(this, "water.png");
-
- mAudioPath = new File(getExternalCacheDir(), "test.mp3").getAbsolutePath();
- mVideoPath = new File(getExternalCacheDir(), "test.mp4").getAbsolutePath();
- mAudioBgPath = new File(getExternalCacheDir(), "testbg.mp3").getAbsolutePath();
- mImagePath = new File(getExternalCacheDir(), "water.png").getAbsolutePath();
-
- String[] commands=this.getResources().getStringArray(R.array.commands);
- List beans = new ArrayList<>();
- for (int i = 0; i < commands.length; i++) {
- beans.add(new CommandBean(commands[i],i));
- }
- mAdapter = new FFmpegCommandAdapter(beans);
- mRecyclerView.setLayoutManager(new GridLayoutManager(this,3));
- mRecyclerView.setAdapter(mAdapter);
- }
-
-
- private void initListener(){
- mAdapter.setItemClickListener(new FFmpegCommandAdapter.ItemClickListener() {
- @Override
- public void itemClick(int position) {
- switch (position) {
- case 0:
- transformAudio();
- break;
- case 1:
- transformVideo();
- break;
- case 2:
- cutAudio();
- break;
- case 3:
- cutVideo();
- break;
- case 4:
- concatAudio();
- break;
- case 5:
- concatVideo();
- break;
- case 6:
- extractAudio();
- break;
- case 7:
- extractVideo();
- break;
- case 8:
- mixAudioVideo();
- break;
- case 9:
- screenShot();
- break;
- case 10:
- video2Image();
- break;
- case 11:
- video2Gif();
- break;
- case 12:
- addWaterMark();
- break;
- case 13:
- image2Video();
- break;
- case 14:
- decodeAudio();
- break;
- case 15:
- encodeAudio();
- break;
- case 16:
- multiVideo();
- break;
- case 17:
- reverseVideo();
- break;
- case 18:
- picInPic();
- break;
- case 19:
- mixAudio();
- break;
- case 20:
- videoDoubleDown();
- break;
- case 21:
- videoSpeed2();
- break;
- case 22:
- denoiseVideo();
- break;
- case 23:
- reduceAudio();
- break;
- case 24:
- video2YUV();
- break;
- case 25:
- yuv2H264();
- break;
- case 26:
- fadeIn();
- break;
- case 27:
- fadeOut();
- break;
- case 28:
- bright();
- break;
- case 29:
- contrast();
- break;
- case 30:
- rotate();
- break;
- case 31:
- videoScale();
- break;
- case 32:
- frame2Image();
- break;
- case 33:
- audio2Fdkaac();
- break;
- case 34:
- audio2Mp3lame();
- break;
- case 35:
- videoHLS();
- break;
- }
- }
- });
- }
-
- private void transformAudio() {
- CustomProgressDialog.showLoading(this);
- targetPath = getExternalCacheDir() + File.separator + "target.aac";
- FFmpegCommand.runAsync(FFmpegUtils.transformAudio(mAudioPath, targetPath),callback("音频转码完成", targetPath));
- }
-
- private void transformVideo() {
- targetPath = getExternalCacheDir() + File.separator + "target.avi";
- FFmpegCommand.runAsync(FFmpegUtils.transformVideo(mVideoPath, targetPath),callback("视频转码完成", targetPath));
- }
-
-
- private void cutAudio() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp3";
- FFmpegCommand.runAsync(FFmpegUtils.cutAudio(mAudioPath, 5, 10, targetPath),callback("音频剪切完成", targetPath));
- }
-
- private void cutVideo() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.cutVideo(mVideoPath, 5, 10, targetPath),callback("视频剪切完成", targetPath));
- }
-
-
- private void concatAudio() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp3";
- FFmpegCommand.runAsync(FFmpegUtils.concatAudio(mAudioPath, mAudioPath, targetPath),callback("音频拼接完成", targetPath));
- }
-
- private void concatVideo() {
- String path = FileUtils.createInputFile(this, mVideoPath, mVideoPath, mVideoPath);
- if (TextUtils.isEmpty(path)) {
- return;
- }
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.concatVideo(path, targetPath),callback("视频拼接完成", targetPath));
- }
-
-
- /**
- * 变更声音
- */
- private void reduceAudio() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp3";
- FFmpegCommand.runAsync(FFmpegUtils.changeVolume(mAudioBgPath, 0.5f, targetPath),
- new CommonCallBack() {
- @Override
- public void onComplete() {
- ToastUtils.show("音频降音完成");
- }
- });
- }
-
- private void extractAudio() {
- targetPath = getExternalCacheDir() + File.separator + "target.aac";
- FFmpegCommand.runAsync(FFmpegUtils.extractAudio(mVideoPath, targetPath),callback("抽取音频完成", targetPath));
- }
-
- private void extractVideo() {
- targetPath = getExternalCacheDir() + File.separator + "out.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.extractVideo(mVideoPath, targetPath),callback("抽取视频完成", targetPath));
- }
-
-
- private void mixAudioVideo() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- String video = getExternalCacheDir() + File.separator + "out.mp4";
- String audio = getExternalCacheDir() + File.separator + "target.aac";
- if (!new File(video).exists()) {
- ToastUtils.show("请先执行抽取视频");
- return;
- }
- if (!new File(audio).exists()) {
- ToastUtils.show("请先执行抽取音频");
- return;
- }
-
- FFmpegCommand.runAsync(FFmpegUtils.mixAudioVideo(video, audio, targetPath),callback("音视频合成完成", targetPath));
- }
-
-
- private void screenShot() {
- targetPath = getExternalCacheDir() + File.separator + "target.jpeg";
- FFmpegCommand.runAsync(FFmpegUtils.screenShot(mVideoPath, targetPath),callback("视频截图完成", targetPath));
- }
-
- private void video2Image() {
- File dir = new File(getExternalCacheDir(), "images");
- if (!dir.exists()) {
- dir.mkdir();
- } else {
- File[] files = dir.listFiles();
- for (File file : files) {
- file.delete();
- }
- }
- targetPath = dir.getAbsolutePath();
- FFmpegCommand.runAsync(FFmpegUtils.video2Image(mVideoPath, targetPath,
- ImageFormat.JPG),callback("视频截图完成", targetPath));
- }
-
-
- private void video2Gif() {
- targetPath = getExternalCacheDir() + File.separator + "target.gif";
- FFmpegCommand.runAsync(FFmpegUtils.video2Gif(mVideoPath, 0, 10, targetPath),callback("视频转Gif完成", targetPath));
- }
-
- private void addWaterMark() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.addWaterMark(mVideoPath, mImagePath, targetPath),callback("添加视频水印完成", targetPath));
- }
-
- private void image2Video() {
- File dir = new File(getExternalCacheDir(), "images");
- if (!dir.exists()) {
- ToastUtils.show("请先执行视频转图片");
- return;
- }
- targetPath = getExternalCacheDir() + File.separator + "images" + File.separator + "target" +
- ".mp4";
- FFmpegCommand.runAsync(FFmpegUtils.image2Video(dir.getAbsolutePath(),
- ImageFormat.JPG, targetPath),callback("图片转视频完成", targetPath));
- }
-
-
- private void decodeAudio() {
- targetPath = getExternalCacheDir() + File.separator + "target.pcm";
- FFmpegCommand.runAsync(FFmpegUtils.decodeAudio(mAudioPath, targetPath, 44100, 2),callback("音频解码PCM完成", targetPath));
- }
-
- private void encodeAudio() {
- String pcm = getExternalCacheDir() + File.separator + "target.pcm";
- if (!new File(pcm).exists()) {
- ToastUtils.show("请先执行音频解码PCM");
- return;
- }
- targetPath = getExternalCacheDir() + File.separator + "target.wav";
- FFmpegCommand.runAsync(FFmpegUtils.encodeAudio(pcm, targetPath, 44100, 2),callback("音频编码完成", targetPath));
- }
-
-
- private void multiVideo() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.multiVideo(mVideoPath, mVideoPath, targetPath,
- Direction.LAYOUT_HORIZONTAL),callback("多画面拼接完成", targetPath));
- }
-
-
- private void reverseVideo() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.reverseVideo(mVideoPath, targetPath),callback("反序播放完成", targetPath));
- }
-
- private void picInPic() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.picInPicVideo(mVideoPath, mVideoPath, 100, 100,
- targetPath), callback("画中画完成", targetPath));
- }
-
- private void mixAudio() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp3";
- FFmpegCommand.runAsync(FFmpegUtils.mixAudio(mAudioPath, mAudioBgPath, targetPath),callback("音频混合完成", targetPath));
- }
-
- private void videoDoubleDown() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.videoDoubleDown(mVideoPath, targetPath),callback("视频缩小一倍完成", targetPath));
- }
-
-
- private void videoSpeed2() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.videoSpeed2(mVideoPath, targetPath),callback("视频倍速完成", targetPath));
- }
-
- private void denoiseVideo() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.denoiseVideo(mVideoPath, targetPath),callback("视频降噪完成", targetPath));
- }
-
- private void video2YUV() {
- targetPath = getExternalCacheDir() + File.separator + "target.yuv";
- FFmpegCommand.runAsync(FFmpegUtils.decode2YUV(mVideoPath, targetPath),callback("视频解码YUV完成", targetPath));
- }
-
-
- private void yuv2H264() {
- targetPath = getExternalCacheDir() + File.separator + "target.h264";
- String video = getExternalCacheDir() + File.separator + "target.yuv";
- if (!new File(video).exists()) {
- ToastUtils.show("请先执行视频解码YUV");
- return;
- }
- FFmpegCommand.runAsync(FFmpegUtils.yuv2H264(video, targetPath),callback("视频编码H264完成", targetPath));
- }
-
-
- private void fadeIn() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp3";
- FFmpegCommand.runAsync(FFmpegUtils.audioFadeIn(mAudioPath, targetPath),callback("音频淡入完成", targetPath));
- }
-
- private void fadeOut() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp3";
- FFmpegCommand.runAsync(FFmpegUtils.audioFadeOut(mAudioPath, targetPath, 34, 5), callback("音频淡出完成", targetPath));
- }
-
- private void bright() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.videoBright(mVideoPath, targetPath, 0.25f),callback("视频提高亮度完成", targetPath) );
- }
-
- private void contrast() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.videoContrast(mVideoPath, targetPath, 1.5f), callback(
- "视频修改对比度完成", targetPath));
- }
-
- private void rotate() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.videoRotation(mVideoPath, targetPath,
- Transpose.CLOCKWISE_ROTATION_90), callback("视频旋转完成", targetPath));
- }
-
-
- private void videoScale() {
- targetPath = getExternalCacheDir() + File.separator + "target.mp4";
- FFmpegCommand.runAsync(FFmpegUtils.videoScale(mVideoPath, targetPath, 360, 640),
- callback("视频缩放完成", targetPath));
- }
-
-
- private void frame2Image(){
- targetPath = getExternalCacheDir() + File.separator + "target.png";
- FFmpegCommand.runAsync(FFmpegUtils.frame2Image(mVideoPath,targetPath,"00:00:10.234"),callback("获取一帧图片成功",targetPath));
- }
-
- private void audio2Fdkaac(){
- targetPath = getExternalCacheDir() + File.separator+"target.aac";
- FFmpegCommand.runAsync(FFmpegUtils.audio2Fdkaac(mAudioPath, targetPath), callback("mp3转aac成功",targetPath));
- }
-
- private void audio2Mp3lame(){
- targetPath = getExternalCacheDir() + File.separator+"target.mp3";
- String aac = getExternalCacheDir() + File.separator + "target.aac";
- if (!new File(aac).exists()) {
- ToastUtils.show("请先执行音频转fdk_aac");
- return;
- }
- FFmpegCommand.runAsync(FFmpegUtils.audio2Mp3lame(aac, targetPath), callback("aac转mp3成功",targetPath));
- }
-
-
- private void videoHLS(){
- File dir = new File(getExternalCacheDir(), "hls");
- if (!dir.exists()) {
- dir.mkdir();
- } else {
- File[] files = dir.listFiles();
- for (File file : files) {
- file.delete();
- }
- }
- targetPath = dir+File.separator+"target.m3u8";
- new Thread(new Runnable() {
- @Override
- public void run() {
- FFmpegCommand.runSync(FFmpegUtils.videoHLS(mVideoPath, targetPath, 10),new FFmpegCommand.OnFFmpegProgressListener() {
- @Override
- public void onProgress(int progress) {
- Log.d("CmdProgress", progress + "");
- }
- });
- }
- }).start();
-// FFmpegCommand.runAsync(FFmpegUtils.videoHLS(mVideoPath,targetPath,10),callback("切片成功",targetPath));
- }
-
- private CommonCallBack callback(final String msg, final String targetPath) {
- tvContent.setText("");
- if (mErrorDialog == null) {
- mErrorDialog = PromptDialog.newInstance("进度", msg, "", "停止");
- mErrorDialog.setHasNegativeButton(false);
- mErrorDialog.setOnPromptListener(new PromptDialog.OnPromptListener() {
- @Override
- public void onPrompt(boolean isPositive) {
- mErrorDialog.setContent(0);
- FFmpegCommand.cancel();
- }
- });
- }
- return new CommonCallBack() {
- @Override
- public void onStart() {
- mErrorDialog.show(getSupportFragmentManager(), "Dialog");
- }
-
- @Override
- public void onComplete() {
- mErrorDialog.setContent(0);
- mErrorDialog.dismissAllowingStateLoss();
- Log.d("CmdProgress", "onComplete");
- ToastUtils.show(msg);
- tvContent.setText(targetPath);
- }
-
- @Override
- public void onCancel() {
- ToastUtils.show("用户取消");
- Log.d("CmdProgress", "Cancel");
- }
-
- @Override
- public void onProgress(int progress) {
- Log.d("CmdProgress",progress+"");
- mErrorDialog.setContent(progress);
- }
- };
- }
-
-}
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCodecActivity.kt b/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCodecActivity.kt
index 094f7a3..a689a9b 100644
--- a/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCodecActivity.kt
+++ b/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCodecActivity.kt
@@ -60,18 +60,20 @@ class KFFmpegCodecActivity : AppCompatActivity(){
}
private fun initListener() {
- mAdapter!!.setItemClickListener { position ->
- when (position) {
- 0 -> enCodecs()
- 1 -> deCodecs()
- 2 -> audioCodecs()
- 3 -> audioDeCodecs()
- 4 -> videoCodecs()
- 5 -> videoDeCodecs()
- 6 -> otherCodecs()
- 7 -> otherDeCodecs()
+ mAdapter!!.setItemClickListener (object : FFmpegCommandAdapter.ItemClickListener {
+ override fun itemClick(id: Int) {
+ when (id) {
+ 0 -> enCodecs()
+ 1 -> deCodecs()
+ 2 -> audioCodecs()
+ 3 -> audioDeCodecs()
+ 4 -> videoCodecs()
+ 5 -> videoDeCodecs()
+ 6 -> otherCodecs()
+ 7 -> otherDeCodecs()
+ }
}
- }
+ })
}
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCommandActivity.kt b/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCommandActivity.kt
index 822315f..549beb1 100644
--- a/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCommandActivity.kt
+++ b/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegCommandActivity.kt
@@ -15,6 +15,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.coder.ffmpeg.annotation.Direction
import com.coder.ffmpeg.annotation.ImageFormat
+import com.coder.ffmpeg.annotation.MediaAttribute
import com.coder.ffmpeg.annotation.Transpose
import com.coder.ffmpeg.call.CommonCallBack
import com.coder.ffmpeg.jni.FFmpegCommand
@@ -23,11 +24,14 @@ import com.coder.ffmpegtest.R
import com.coder.ffmpegtest.model.CommandBean
import com.coder.ffmpegtest.ui.adapter.FFmpegCommandAdapter
import com.coder.ffmpegtest.ui.dialog.PromptDialog
-import com.coder.ffmpegtest.utils.CustomProgressDialog
+import com.coder.ffmpegtest.ui.dialog.PromptDialog.OnPromptListener
import com.coder.ffmpegtest.utils.FileUtils
import com.coder.ffmpegtest.utils.ToastUtils
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
import java.io.File
import java.util.*
+import java.util.concurrent.Executors
/**
*
@@ -91,84 +95,110 @@ class KFFmpegCommandActivity : AppCompatActivity() {
private fun initListener() {
- mAdapter!!.setItemClickListener { position ->
- when (position) {
- 0 -> transformAudio()
- 1 -> transformVideo()
- 2 -> cutAudio()
- 3 -> cutVideo()
- 4 -> concatAudio()
- 5 -> concatVideo()
- 6 -> extractAudio()
- 7 -> extractVideo()
- 8 -> mixAudioVideo()
- 9 -> screenShot()
- 10 -> video2Image()
- 11 -> video2Gif()
- 12 -> addWaterMark()
- 13 -> image2Video()
- 14 -> decodeAudio()
- 15 -> encodeAudio()
- 16 -> multiVideo()
- 17 -> reverseVideo()
- 18 -> picInPic()
- 19 -> mixAudio()
- 20 -> videoDoubleDown()
- 21 -> videoSpeed2()
- 22 -> denoiseVideo()
- 23 -> reduceAudio()
- 24 -> video2YUV()
- 25 -> yuv2H264()
- 26 -> fadeIn()
- 27 -> fadeOut()
- 28 -> bright()
- 29 -> contrast()
- 30 -> rotate()
- 31 -> videoScale()
- 32 -> frame2Image()
- 33 -> audio2Fdkaac()
- 34 -> audio2Mp3lame()
- 35 -> video2HLS()
- 36 -> hls2Video()
- 37 -> audio2Amr()
+ mAdapter!!.setItemClickListener(object : FFmpegCommandAdapter.ItemClickListener {
+ override fun itemClick(id: Int) {
+ tvContent!!.text = ""
+ if (mErrorDialog == null) {
+ mErrorDialog = PromptDialog.newInstance("进度", "完成", "", "停止")
+ mErrorDialog?.setHasNegativeButton(false)
+ mErrorDialog?.setOnPromptListener(object : OnPromptListener {
+ override fun onPrompt(isPositive: Boolean) {
+ if (isPositive) FFmpegCommand.cancel()
+ }
+ })
+ }
+ when (id) {
+ 0 -> transformAudio()
+ 1 -> transformVideo()
+ 2 -> cutAudio()
+ 3 -> cutVideo()
+ 4 -> concatAudio()
+ 5 -> concatVideo()
+ 6 -> extractAudio()
+ 7 -> extractVideo()
+ 8 -> mixAudioVideo()
+ 9 -> screenShot()
+ 10 -> video2Image()
+ 11 -> video2Gif()
+ 12 -> addWaterMark()
+ 13 -> image2Video()
+ 14 -> decodeAudio()
+ 15 -> encodeAudio()
+ 16 -> multiVideo()
+ 17 -> reverseVideo()
+ 18 -> picInPic()
+ 19 -> mixAudio()
+ 20 -> videoDoubleDown()
+ 21 -> videoSpeed2()
+ 22 -> denoiseVideo()
+ 23 -> reduceAudio()
+ 24 -> video2YUV()
+ 25 -> yuv2H264()
+ 26 -> fadeIn()
+ 27 -> fadeOut()
+ 28 -> bright()
+ 29 -> contrast()
+ 30 -> rotate()
+ 31 -> videoScale()
+ 32 -> frame2Image()
+ 33 -> audio2Fdkaac()
+ 34 -> audio2Mp3lame()
+ 35 -> video2HLS()
+ 36 -> hls2Video()
+ 37 -> audio2Amr()
+ 38 -> makeMuteAudio()
+ }
}
- }
+ })
}
private fun transformAudio() {
targetPath = externalCacheDir.toString() + File.separator + "target.aac"
- FFmpegCommand.runAsync(FFmpegUtils.transformAudio(mAudioPath, targetPath), callback("音频转码完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.transformAudio(mAudioPath, targetPath), callback("音频转码完成", targetPath))
+ }
}
private fun transformVideo() {
targetPath = externalCacheDir.toString() + File.separator + "target.avi"
- FFmpegCommand.runAsync(FFmpegUtils.transformVideo(mVideoPath, targetPath), callback("视频转码完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.transformVideo(mVideoPath, targetPath), callback("视频转码完成", targetPath))
+ }
}
private fun cutAudio() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp3"
- FFmpegCommand.runAsync(FFmpegUtils.cutAudio(mAudioPath, 5, 10, targetPath), callback("音频剪切完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.cutAudio(mAudioPath, 5, 10, targetPath), callback("音频剪切完成", targetPath))
+ }
}
private fun cutVideo() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.cutVideo(mVideoPath, 5, 10, targetPath), callback("视频剪切完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.cutVideo(mVideoPath, 5, 10, targetPath), callback("视频剪切完成", targetPath))
+ }
}
private fun concatAudio() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp3"
- FFmpegCommand.runAsync(FFmpegUtils.concatAudio(mAudioPath, mAudioPath, targetPath), callback("音频拼接完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.concatAudio(mAudioPath, mAudioPath, targetPath), callback("音频拼接完成", targetPath))
+ }
}
private fun concatVideo() {
- val path = FileUtils.createInputFile(this, mVideoPath, mVideoPath, mVideoPath)
+ val path = FileUtils.createInputFile(this, mVideoPath!!, mVideoPath!!, mVideoPath!!)
+ //val path = FileUtils.createInputFile(this, mVideoPath, mVideoPath, mVideoPath)
if (TextUtils.isEmpty(path)) {
return
}
- targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.concatVideo(path, targetPath), callback("视频拼接完成", targetPath))
+ GlobalScope.launch {
+ targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
+ FFmpegCommand.runCmd(FFmpegUtils.concatVideo(path, targetPath), callback("视频拼接完成", targetPath))
+ }
}
@@ -177,22 +207,23 @@ class KFFmpegCommandActivity : AppCompatActivity() {
*/
private fun reduceAudio() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp3"
- FFmpegCommand.runAsync(FFmpegUtils.changeVolume(mAudioBgPath, 0.5f, targetPath),
- object : CommonCallBack() {
- override fun onComplete() {
- ToastUtils.show("音频降音完成")
- }
- })
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.changeVolume(mAudioBgPath, 0.5f, targetPath), callback("音频降音完成", targetPath))
+ }
}
private fun extractAudio() {
targetPath = externalCacheDir.toString() + File.separator + "target.aac"
- FFmpegCommand.runAsync(FFmpegUtils.extractAudio(mVideoPath, targetPath), callback("抽取音频完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.extractAudio(mVideoPath, targetPath), callback("抽取音频完成", targetPath))
+ }
}
private fun extractVideo() {
targetPath = externalCacheDir.toString() + File.separator + "out.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.extractVideo(mVideoPath, targetPath), callback("抽取视频完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.extractVideo(mVideoPath, targetPath), callback("抽取视频完成", targetPath))
+ }
}
@@ -208,13 +239,17 @@ class KFFmpegCommandActivity : AppCompatActivity() {
ToastUtils.show("请先执行抽取音频")
return
}
- FFmpegCommand.runAsync(FFmpegUtils.mixAudioVideo(video, audio, targetPath), callback("音视频合成完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.mixAudioVideo(video, audio, targetPath), callback("音视频合成完成", targetPath))
+ }
}
private fun screenShot() {
targetPath = externalCacheDir.toString() + File.separator + "target.jpeg"
- FFmpegCommand.runAsync(FFmpegUtils.screenShot(mVideoPath, targetPath), callback("视频截图完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.screenShot(mVideoPath, targetPath), callback("视频截图完成", targetPath))
+ }
}
private fun video2Image() {
@@ -228,19 +263,25 @@ class KFFmpegCommandActivity : AppCompatActivity() {
}
}
targetPath = dir.absolutePath
- FFmpegCommand.runAsync(FFmpegUtils.video2Image(mVideoPath, targetPath,
- ImageFormat.JPG), callback("视频截图完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.video2Image(mVideoPath, targetPath,
+ ImageFormat.JPG), callback("视频截图完成", targetPath))
+ }
}
private fun video2Gif() {
targetPath = externalCacheDir.toString() + File.separator + "target.gif"
- FFmpegCommand.runAsync(FFmpegUtils.video2Gif(mVideoPath, 0, 10, targetPath), callback("视频转Gif完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.video2Gif(mVideoPath, 0, 10, targetPath), callback("视频转Gif完成", targetPath))
+ }
}
private fun addWaterMark() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.addWaterMark(mVideoPath, mImagePath, targetPath), callback("添加视频水印完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.addWaterMark(mVideoPath, mImagePath, targetPath), callback("添加视频水印完成", targetPath))
+ }
}
private fun image2Video() {
@@ -251,14 +292,18 @@ class KFFmpegCommandActivity : AppCompatActivity() {
}
targetPath = externalCacheDir.toString() + File.separator + "images" + File.separator + "target" +
".mp4"
- FFmpegCommand.runAsync(FFmpegUtils.image2Video(dir.absolutePath,
- ImageFormat.JPG, targetPath), callback("图片转视频完成", targetPath))
- }
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.image2Video(dir.absolutePath,
+ ImageFormat.JPG, targetPath), callback("图片转视频完成", targetPath))
+ }
+ }
private fun decodeAudio() {
targetPath = externalCacheDir.toString() + File.separator + "target.pcm"
- FFmpegCommand.runAsync(FFmpegUtils.decodeAudio(mAudioPath, targetPath, 16000, 1), callback("音频解码PCM完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.decodeAudio(mAudioPath, targetPath, 44100, 2), callback("音频解码PCM完成", targetPath))
+ }
}
private fun encodeAudio() {
@@ -268,52 +313,70 @@ class KFFmpegCommandActivity : AppCompatActivity() {
return
}
targetPath = externalCacheDir.toString() + File.separator + "target.wav"
- FFmpegCommand.runAsync(FFmpegUtils.encodeAudio(pcm, targetPath, 44100, 2), callback("音频编码完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.encodeAudio(pcm, targetPath, 44100, 2), callback("音频编码完成", targetPath))
+ }
}
private fun multiVideo() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.multiVideo(mVideoPath, mVideoPath, targetPath,
- Direction.LAYOUT_HORIZONTAL), callback("多画面拼接完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.multiVideo(mVideoPath, mVideoPath, targetPath,
+ Direction.LAYOUT_HORIZONTAL), callback("多画面拼接完成", targetPath))
+ }
}
private fun reverseVideo() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.reverseVideo(mVideoPath, targetPath), callback("反序播放完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.reverseVideo(mVideoPath, targetPath), callback("反序播放完成", targetPath))
+ }
}
private fun picInPic() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.picInPicVideo(mVideoPath, mVideoPath, 100, 100,
- targetPath), callback("画中画完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.picInPicVideo(mVideoPath, mVideoPath, 100, 100,
+ targetPath), callback("画中画完成", targetPath))
+ }
}
private fun mixAudio() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp3"
- FFmpegCommand.runAsync(FFmpegUtils.mixAudio(mAudioPath, mAudioBgPath, targetPath), callback("音频混合完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.mixAudio(mAudioPath, mAudioBgPath, targetPath), callback("音频混合完成", targetPath))
+ }
}
private fun videoDoubleDown() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.videoDoubleDown(mVideoPath, targetPath), callback("视频缩小一倍完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.videoDoubleDown(mVideoPath, targetPath), callback("视频缩小一倍完成", targetPath))
+ }
}
private fun videoSpeed2() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.videoSpeed2(mVideoPath, targetPath), callback("视频倍速完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.videoSpeed2(mVideoPath, targetPath), callback("视频倍速完成", targetPath))
+ }
}
private fun denoiseVideo() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.denoiseVideo(mVideoPath, targetPath), callback("视频降噪完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.denoiseVideo(mVideoPath, targetPath), callback("视频降噪完成", targetPath))
+ }
}
private fun video2YUV() {
targetPath = externalCacheDir.toString() + File.separator + "target.yuv"
- FFmpegCommand.runAsync(FFmpegUtils.decode2YUV(mVideoPath, targetPath), callback("视频解码YUV完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.decode2YUV(mVideoPath, targetPath), callback("视频解码YUV完成", targetPath))
+ }
}
@@ -324,53 +387,71 @@ class KFFmpegCommandActivity : AppCompatActivity() {
ToastUtils.show("请先执行视频解码YUV")
return
}
- FFmpegCommand.runAsync(FFmpegUtils.yuv2H264(video, targetPath), callback("视频编码H264完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.yuv2H264(video, targetPath), callback("视频编码H264完成", targetPath))
+ }
}
private fun fadeIn() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp3"
- FFmpegCommand.runAsync(FFmpegUtils.audioFadeIn(mAudioPath, targetPath), callback("音频淡入完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.audioFadeIn(mAudioPath, targetPath), callback("音频淡入完成", targetPath))
+ }
}
private fun fadeOut() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp3"
- FFmpegCommand.runAsync(FFmpegUtils.audioFadeOut(mAudioPath, targetPath, 34, 5), callback("音频淡出完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.audioFadeOut(mAudioPath, targetPath, 34, 5), callback("音频淡出完成", targetPath))
+ }
}
private fun bright() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.videoBright(mVideoPath, targetPath, 0.25f), callback("视频提高亮度完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.videoBright(mVideoPath, targetPath, 0.25f), callback("视频提高亮度完成", targetPath))
+ }
}
private fun contrast() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.videoContrast(mVideoPath, targetPath, 1.5f), callback(
- "视频修改对比度完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.videoContrast(mVideoPath, targetPath, 1.5f), callback(
+ "视频修改对比度完成", targetPath))
+ }
}
private fun rotate() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.videoRotation(mVideoPath, targetPath,
- Transpose.CLOCKWISE_ROTATION_90), callback("视频旋转完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.videoRotation(mVideoPath, targetPath,
+ Transpose.CLOCKWISE_ROTATION_90), callback("视频旋转完成", targetPath))
+ }
}
private fun videoScale() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.videoScale(mVideoPath, targetPath, 360, 640),
- callback("视频缩放完成", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.videoScale(mVideoPath, targetPath, 360, 640),
+ callback("视频缩放完成", targetPath))
+ }
}
private fun frame2Image() {
targetPath = externalCacheDir.toString() + File.separator + "target.png"
- FFmpegCommand.runAsync(FFmpegUtils.frame2Image(mVideoPath, targetPath, "00:00:10.234"), callback("获取一帧图片成功", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.frame2Image(mVideoPath, targetPath, "00:00:10.234"), callback("获取一帧图片成功", targetPath))
+ }
}
private fun audio2Fdkaac() {
targetPath = externalCacheDir.toString() + File.separator + "target.aac"
- FFmpegCommand.runAsync(FFmpegUtils.audio2Fdkaac(mAudioPath, targetPath), callback("mp3转aac成功", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.audio2Fdkaac(mAudioPath, targetPath), callback("mp3转aac成功", targetPath))
+ }
}
private fun audio2Mp3lame() {
@@ -380,7 +461,9 @@ class KFFmpegCommandActivity : AppCompatActivity() {
ToastUtils.show("请先执行音频转fdk_aac")
return
}
- FFmpegCommand.runAsync(FFmpegUtils.audio2Mp3lame(aac, targetPath), callback("acc转mp3成功", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.audio2Mp3lame(aac, targetPath), callback("acc转mp3成功", targetPath))
+ }
}
@@ -396,7 +479,9 @@ class KFFmpegCommandActivity : AppCompatActivity() {
}
targetPath = dir.toString() + File.separator + "target.m3u8"
- FFmpegCommand.runAsync(FFmpegUtils.video2HLS(mVideoPath, targetPath, 10), callback("切片成功", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.video2HLS(mVideoPath, targetPath, 10), callback("切片成功", targetPath))
+ }
}
private fun hls2Video() {
@@ -411,55 +496,75 @@ class KFFmpegCommandActivity : AppCompatActivity() {
return
}
targetPath = dir.toString() + File.separator + "target.mp4"
- FFmpegCommand.runAsync(FFmpegUtils.hls2Video(videoIndexFile.absolutePath, targetPath), callback("合成切片成功", targetPath))
-
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.hls2Video(videoIndexFile.absolutePath, targetPath), callback("合成切片成功", targetPath))
+ }
}
- private fun audio2Amr(){
+ private fun audio2Amr() {
targetPath = externalCacheDir.toString() + File.separator + "target.amr"
- FFmpegCommand.runAsync(FFmpegUtils.audio2Amr(mAudioPath, targetPath), callback("mp3转amr成功", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.audio2Amr(mAudioPath, targetPath), callback("mp3转amr成功", targetPath))
+ }
}
- private fun errorTest(){
+ private fun makeMuteAudio() {
targetPath = externalCacheDir.toString() + File.separator + "target.mp3"
- var command = "ffmpeg -y -f lavfi -t 10 -i anullsrc $targetPath"
- FFmpegCommand.runAsync(command.split(" ").toTypedArray(), callback("生成静音文件成功", targetPath))
+ GlobalScope.launch {
+ FFmpegCommand.runCmd(FFmpegUtils.makeMuteAudio(targetPath), callback("生成静音文件成功", targetPath))
+ }
}
- private fun callback(msg: String, targetPath: String?): CommonCallBack? {
- tvContent!!.text = ""
- if (mErrorDialog == null) {
- mErrorDialog = PromptDialog.newInstance("进度", msg, "", "停止")
- mErrorDialog?.setHasNegativeButton(false)
- mErrorDialog?.setOnPromptListener { isPositive ->
- run {
- mErrorDialog?.setContent(0)
- FFmpegCommand.cancel()
- }
- }
+ private fun splitmute(){
+ GlobalScope.launch {
+ var commands = "ffmpeg -i %s -af pan=1c|c0=c0,silencedetect=n=-35dB:d=0.100 -f null /dev/null"
+ var cmd:Array = String.format(commands,mAudioPath).split(" ").toTypedArray()
+ FFmpegCommand.runCmd(cmd, callback("检测静音", targetPath))
}
+ }
+ private fun callback(msg: String, targetPath: String?): CommonCallBack? {
return object : CommonCallBack() {
override fun onStart() {
- mErrorDialog?.show(supportFragmentManager, "Dialog")
+ Log.d("FFmpegCmd", "onStart")
+ runOnUiThread {
+ mErrorDialog?.show(supportFragmentManager, "Dialog")
+ }
}
override fun onComplete() {
- Log.d("CmdProgress", "onComplete")
- mErrorDialog?.setContent(0)
- mErrorDialog?.dismissAllowingStateLoss()
- ToastUtils.show(msg)
- tvContent!!.text = targetPath
+ Log.d("FFmpegCmd", "onComplete")
+ runOnUiThread {
+ ToastUtils.show(msg)
+ mErrorDialog?.setContent(0)
+ mErrorDialog?.dismissAllowingStateLoss()
+ tvContent?.text = targetPath
+ }
+
}
override fun onCancel() {
- ToastUtils.show("用户取消")
- Log.d("CmdProgress", "Cancel")
+ runOnUiThread {
+ ToastUtils.show("用户取消")
+ mErrorDialog?.setContent(0)
+ }
+ Log.d("FFmpegCmd", "Cancel")
}
- override fun onProgress(progress: Int) {
- Log.d("CmdProgress", progress.toString() + "")
- mErrorDialog?.setContent(progress)
+ override fun onProgress(progress: Int, pts: Long) {
+ var duration :Int? = FFmpegCommand.getMediaInfo(mAudioPath,MediaAttribute.DURATION)
+ var progressN = pts/duration!!
+ Log.d("FFmpegCmd", progress.toString() + "")
+ runOnUiThread { mErrorDialog?.setContent(progress) }
+ }
+
+ override fun onError(errorCode: Int, errorMsg: String?) {
+ Log.d("FFmpegCmd", errorMsg)
+ runOnUiThread {
+ ToastUtils.show(errorMsg)
+ mErrorDialog?.setContent(0)
+ mErrorDialog?.dismissAllowingStateLoss()
+ }
}
}
}
@@ -470,5 +575,4 @@ class KFFmpegCommandActivity : AppCompatActivity() {
context.startActivity(intent)
}
}
-
}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegInfoActivity.kt b/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegInfoActivity.kt
index 493aff9..eac47cd 100644
--- a/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegInfoActivity.kt
+++ b/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegInfoActivity.kt
@@ -8,7 +8,7 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.coder.ffmpeg.annotation.Attribute
+import com.coder.ffmpeg.annotation.MediaAttribute
import com.coder.ffmpeg.jni.FFmpegCommand
import com.coder.ffmpegtest.R
import com.coder.ffmpegtest.model.CommandBean
@@ -65,73 +65,75 @@ class KFFmpegInfoActivity : AppCompatActivity() {
}
private fun initListener() {
- mAdapter!!.setItemClickListener { position ->
- when (position) {
- 0 -> getDuration()
- 1 -> getWidth()
- 2 -> getHeight()
- 3 -> getVideoBitRate()
- 4 -> getVideoFPS()
- 5 -> getChannels()
- 6 -> getSampleRate()
- 7 -> getAudioBitRate()
+ mAdapter!!.setItemClickListener (object : FFmpegCommandAdapter.ItemClickListener {
+ override fun itemClick(id: Int) {
+ when (id) {
+ 0 -> getDuration()
+ 1 -> getWidth()
+ 2 -> getHeight()
+ 3 -> getVideoBitRate()
+ 4 -> getVideoFPS()
+ 5 -> getChannels()
+ 6 -> getSampleRate()
+ 7 -> getAudioBitRate()
+ }
}
- }
+ })
}
private fun getDuration() {
val AV_TIME_BASE = 1000000;
- val duration = FFmpegCommand.getInfoSync(mVideoPath, Attribute.DURATION)
+ val duration = FFmpegCommand.getMediaInfo(mVideoPath, MediaAttribute.DURATION)
Log.d("FFmpeg", "duration: $duration")
- var secs = duration / AV_TIME_BASE
- val us = duration % AV_TIME_BASE
- var mins = secs / 60
- secs %= 60
- val hours = mins / 60
- mins %= 60
-
- val result = String.format("%02d:%02d:%02d.%02d", hours, mins, secs, (100 * us) / AV_TIME_BASE)
+ var secs = duration?.div(AV_TIME_BASE)
+ val us = duration?.rem(AV_TIME_BASE)
+ var mins = secs?.div(60)
+ secs = secs?.rem(60)
+ val hours = mins?.div(60)
+ mins = mins?.rem(60)
+
+ val result = String.format("%02d:%02d:%02d.%02d", hours, mins, secs, (100 * us!!) / AV_TIME_BASE)
tvContent?.text = result
}
private fun getWidth() {
- val width = FFmpegCommand.getInfoSync(mVideoPath, Attribute.WIDTH)
+ val width = FFmpegCommand.getMediaInfo(mVideoPath, MediaAttribute.WIDTH)
val result = String.format("width = %s", width)
tvContent?.text = result
}
private fun getHeight() {
- val height = FFmpegCommand.getInfoSync(mVideoPath, Attribute.HEIGHT)
+ val height = FFmpegCommand.getMediaInfo(mVideoPath, MediaAttribute.HEIGHT)
val result = String.format("height = %s", height)
tvContent?.text = result
}
private fun getVideoBitRate() {
- val bitRate = FFmpegCommand.getInfoSync(mVideoPath, Attribute.VIDEO_BIT_RATE)
+ val bitRate = FFmpegCommand.getMediaInfo(mVideoPath, MediaAttribute.VIDEO_BIT_RATE)
val result = String.format("bitRate = %s", bitRate)
tvContent?.text = result
}
private fun getVideoFPS() {
- val fps = FFmpegCommand.getInfoSync(mVideoPath, Attribute.FPS)
+ val fps = FFmpegCommand.getMediaInfo(mVideoPath, MediaAttribute.FPS)
val result = String.format("fps = %s", fps)
tvContent?.text = result
}
private fun getChannels() {
- val channels = FFmpegCommand.getInfoSync(mVideoPath, Attribute.CHANNELS)
+ val channels = FFmpegCommand.getMediaInfo(mVideoPath, MediaAttribute.CHANNELS)
val result = String.format("channels = %s", channels)
tvContent?.text = result
}
private fun getSampleRate() {
- val sampleRate = FFmpegCommand.getInfoSync(mVideoPath, Attribute.SAMPLE_RATE)
+ val sampleRate = FFmpegCommand.getMediaInfo(mVideoPath, MediaAttribute.SAMPLE_RATE)
val result = String.format("sampleRate = %s", sampleRate)
tvContent?.text = result
}
private fun getAudioBitRate() {
- val bitRate = FFmpegCommand.getInfoSync(mVideoPath, Attribute.AUDIO_BIT_RATE)
+ val bitRate = FFmpegCommand.getMediaInfo(mVideoPath, MediaAttribute.AUDIO_BIT_RATE)
val result = String.format("bitRate = %s", bitRate)
tvContent?.text = result
}
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegMoreCommandActivity.kt b/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegMoreCommandActivity.kt
deleted file mode 100644
index 7d55ad2..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/ui/KFFmpegMoreCommandActivity.kt
+++ /dev/null
@@ -1,294 +0,0 @@
-package com.coder.ffmpegtest.ui
-
-import android.Manifest
-import android.annotation.SuppressLint
-import android.content.Context
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.os.Bundle
-import android.os.Handler
-import android.os.Message
-import android.util.Log
-import android.widget.Button
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import com.coder.ffmpeg.call.CommonCallBack
-import com.coder.ffmpeg.jni.FFmpegCommand
-import com.coder.ffmpeg.utils.FFmpegUtils
-import com.coder.ffmpegtest.R
-import com.coder.ffmpegtest.service.FFmpegCommandService
-import com.coder.ffmpegtest.service.FFmpegCommandService2
-import com.coder.ffmpegtest.ui.dialog.PromptDialog
-import com.coder.ffmpegtest.utils.FileUtils
-import com.coder.ffmpegtest.utils.ToastUtils
-import java.io.File
-
-/**
- *
- * @author: AnJoiner
- * @datetime: 20-6-22
- */
-class KFFmpegMoreCommandActivity : AppCompatActivity() {
-
- private var mAudioPath: String? = null
- private var mVideoPath: String? = null
- private var tvContent: TextView? = null
- private var mCommandBtn: Button? = null
- private var mCommand2Btn: Button? = null
- private var mCommand3Btn: Button? = null
- private var mCommand4Btn: Button? = null
-
- private var mErrorDialog: PromptDialog? = null
-
- val stringBuilder = StringBuilder()
-
- private var handler: Handler = @SuppressLint("HandlerLeak")
- object : Handler() {
- override fun handleMessage(msg: Message) {
- super.handleMessage(msg)
- if (msg.what == 1){
- mErrorDialog?.setContent(msg.arg1)
- }else if (msg.what == 0){
- mErrorDialog?.setContent(0)
- mErrorDialog?.dismissAllowingStateLoss()
- ToastUtils.show("多命令执行完成")
- val target:String = msg.obj as String
- tvContent!!.text = target
- }else if(msg.what == -1){
- ToastUtils.show("用户取消")
- }else if (msg.what == 2) {
- val targetPath = msg.obj
- stringBuilder.append(targetPath).append("\n")
- tvContent?.text = stringBuilder.toString()
- }
- }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_ffmpeg_more_command)
-
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
- != PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
- 100)
- }
- init()
- }
-
- private fun init() {
- initView()
- initData()
- initListener()
- }
-
- private fun initView() {
- mCommandBtn = findViewById(R.id.btn_more_command)
- mCommand2Btn = findViewById(R.id.btn_more_command2)
- mCommand3Btn = findViewById(R.id.btn_more_command3)
- mCommand4Btn = findViewById(R.id.btn_more_command4)
-
- tvContent = findViewById(R.id.tv_content)
-
- if (mErrorDialog == null) {
- mErrorDialog = PromptDialog.newInstance("进度", "多命令行执行", "", "停止")
- mErrorDialog?.setHasNegativeButton(false)
- mErrorDialog?.setOnPromptListener { isPositive ->
- run {
- mErrorDialog?.setContent(0)
- FFmpegCommand.cancel()
- }
- }
- }
- }
-
- private fun initData() {
- FileUtils.copy2Memory(this, "test.mp3")
- FileUtils.copy2Memory(this, "test.mp4")
- mAudioPath = File(externalCacheDir, "test.mp3").absolutePath
- mVideoPath = File(externalCacheDir, "test.mp4").absolutePath
- }
-
- private fun initListener() {
- mCommand2Btn?.setOnClickListener {
- tvContent!!.text = ""
- mErrorDialog?.show(supportFragmentManager, "Dialog")
-//
- Thread(Runnable {
- moreSyncCommand()
- }).start()
- }
- mCommandBtn?.setOnClickListener {
- stringBuilder.clear()
- tvContent!!.text = ""
- Thread(Runnable {
- transformAudio()
- video2YUV()
- transformVideo()
- }).start()
- }
-
- mCommand3Btn?.setOnClickListener {
- tvContent!!.text = ""
- moreAsyncCommand()
- }
-
- mCommand4Btn?.setOnClickListener {
- val intent = Intent(this@KFFmpegMoreCommandActivity, FFmpegCommandService::class.java)
- startService(intent)
-
- val intent2 = Intent(this@KFFmpegMoreCommandActivity, FFmpegCommandService2::class.java)
- startService(intent2)
- }
- }
-
- private fun moreAsyncCommand(){
- val targetAAC = externalCacheDir.toString() + File.separator + "target.aac"
- val targetAVI = externalCacheDir.toString() + File.separator + "target.avi"
- val targetYUV = externalCacheDir.toString() + File.separator + "target.yuv"
- val cmdAAC = FFmpegUtils.transformAudio(mAudioPath, targetAAC)
- val cmdAVI = FFmpegUtils.transformVideo(mVideoPath, targetAVI)
- val cmdYUV = FFmpegUtils.decode2YUV(mVideoPath, targetYUV)
- val st = ArrayList>()
- st.add(cmdAAC)
- st.add(cmdAVI)
- st.add(cmdYUV)
-
- FFmpegCommand.runMoreAsync(st, object : CommonCallBack() {
- override fun onStart() {
- mErrorDialog?.show(supportFragmentManager, "Dialog")
- }
-
- override fun onComplete() {
- mErrorDialog?.setContent(0)
- mErrorDialog?.dismissAllowingStateLoss()
- ToastUtils.show("多命令执行完成")
- val target = targetAAC + "\n" + targetAVI + "\n" + targetYUV
- tvContent!!.text = target
- }
-
- override fun onCancel() {
- ToastUtils.show("用户取消")
- }
-
- override fun onProgress(progress: Int) {
- mErrorDialog?.setContent(progress)
- }
- })
- }
-
- private fun moreSyncCommand() {
- val targetAAC = externalCacheDir.toString() + File.separator + "target.aac"
- val targetAVI = externalCacheDir.toString() + File.separator + "target.avi"
- val targetYUV = externalCacheDir.toString() + File.separator + "target.yuv"
- val cmdAAC = FFmpegUtils.transformAudio(mAudioPath, targetAAC)
- val cmdAVI = FFmpegUtils.transformVideo(mVideoPath, targetAVI)
- val cmdYUV = FFmpegUtils.decode2YUV(mVideoPath, targetYUV)
- val st = ArrayList>()
- st.add(cmdAAC)
- st.add(cmdAVI)
- st.add(cmdYUV)
-
- FFmpegCommand.runMoreSync(st, object : FFmpegCommand.OnFFmpegCommandListener {
- override fun onProgress(progress: Int) {
- val msg = Message()
- msg.what = 1
- msg.arg1 = progress
- handler.sendMessage(msg)
- Log.d("runMoreSync", "globalProgress:$progress")
- }
-
- override fun onCancel() {
- Log.d("runMoreSync", "onCancel")
- val msg = Message()
- msg.what = -1
- handler.sendMessage(msg)
- }
-
- override fun onComplete() {
- val target = targetAAC + "\n" + targetAVI + "\n" + targetYUV
-
- val msg = Message()
- msg.what = 0
- msg.obj = target
- handler.sendMessage(msg)
-
- Log.d("runMoreSync", "onComplete")
- }
- })
- }
-
-
- private fun transformAudio() {
- val targetPath = externalCacheDir.toString() + File.separator + "target.aac"
- FFmpegCommand.runSync(FFmpegUtils.transformAudio(mAudioPath, targetPath), object : FFmpegCommand.OnFFmpegCommandListener {
- override fun onProgress(progress: Int) {
- Log.d("CmdProress",""+progress)
- }
-
- override fun onCancel() {
- }
-
- override fun onComplete() {
- val message = Message()
- message.what = 2
- message.obj = targetPath
- handler.sendMessage(message)
-
- }
- })
- }
-
- private fun transformVideo() {
- val targetPath = externalCacheDir.toString() + File.separator + "target.avi"
- FFmpegCommand.runSync(FFmpegUtils.transformVideo(mVideoPath, targetPath), object : FFmpegCommand.OnFFmpegCommandListener {
- override fun onProgress(progress: Int) {
- Log.d("CmdProress",""+progress)
- }
-
- override fun onCancel() {
- }
-
- override fun onComplete() {
- val message = Message()
- message.what = 2
- message.obj = targetPath
- handler.sendMessage(message)
- }
- })
- }
-
- private fun video2YUV() {
- val targetPath = externalCacheDir.toString() + File.separator + "target.yuv"
- FFmpegCommand.runSync(FFmpegUtils.decode2YUV(mVideoPath, targetPath), object : FFmpegCommand.OnFFmpegCommandListener {
- override fun onProgress(progress: Int) {
- Log.d("CmdProress",""+progress)
- }
-
- override fun onCancel() {
- }
-
- override fun onComplete() {
- val message = Message()
- message.what = 2
- message.obj = targetPath
- handler.sendMessage(message)
- }
- })
- }
-
- override fun onBackPressed() {
- super.onBackPressed()
- FFmpegCommand.cancel()
- }
-
- companion object {
- fun start(context: Context) {
- val intent = Intent(context, KFFmpegMoreCommandActivity::class.java)
- context.startActivity(intent)
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/MainActivity.java b/app/src/main/java/com/coder/ffmpegtest/ui/MainActivity.java
deleted file mode 100644
index d693cf6..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/ui/MainActivity.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.coder.ffmpegtest.ui;
-
-import android.annotation.SuppressLint;
-import android.os.Build;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import com.coder.ffmpegtest.R;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-@SuppressLint("NonConstantResourceId")
-public class MainActivity extends AppCompatActivity implements View.OnClickListener {
-
- Button mCommandBtn;
- Button mInfoBtn;
- Button mMoreCommandBtn;
- Button mFormatBtn;
- Button mCodecBtn;
- TextView mAbiText;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- intiView();
- initListener();
-
- }
-
- private void intiView(){
- mCommandBtn = findViewById(R.id.btn_command);
- mInfoBtn = findViewById(R.id.btn_info);
- mMoreCommandBtn = findViewById(R.id.btn_more_command);
- mFormatBtn = findViewById(R.id.btn_format);
- mCodecBtn = findViewById(R.id.btn_codec);
- mAbiText = findViewById(R.id.tv_abi);
-
- mAbiText.setText(String.format("当前使用cpu-abi:%s", Build.CPU_ABI));
- }
-
- private void initListener(){
- mCommandBtn.setOnClickListener(this);
- mInfoBtn.setOnClickListener(this);
- mMoreCommandBtn.setOnClickListener(this);
- mFormatBtn.setOnClickListener(this);
- mCodecBtn.setOnClickListener(this);
- }
-
-
- @Override
- public void onClick(View v) {
- switch (v.getId()){
- case R.id.btn_command:
- KFFmpegCommandActivity.Companion.start(this);
- break;
- case R.id.btn_info:
- KFFmpegInfoActivity.Companion.start(this);
- break;
- case R.id.btn_more_command:
- KFFmpegMoreCommandActivity.Companion.start(this);
- break;
- case R.id.btn_format:
- KFFmppegFormatActivity.Companion.start(this);
- break;
- case R.id.btn_codec:
- KFFmpegCodecActivity.Companion.start(this);
- break;
- }
-
- }
-}
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/MainActivity.kt b/app/src/main/java/com/coder/ffmpegtest/ui/MainActivity.kt
new file mode 100644
index 0000000..fdd64af
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/ui/MainActivity.kt
@@ -0,0 +1,50 @@
+package com.coder.ffmpegtest.ui
+
+import android.annotation.SuppressLint
+import android.os.Build
+import android.os.Bundle
+import android.view.View
+import android.widget.Button
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import com.coder.ffmpegtest.R
+
+@SuppressLint("NonConstantResourceId")
+class MainActivity : AppCompatActivity(), View.OnClickListener {
+ var mCommandBtn: Button? = null
+ var mInfoBtn: Button? = null
+ var mFormatBtn: Button? = null
+ var mCodecBtn: Button? = null
+ var mAbiText: TextView? = null
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ intiView()
+ initListener()
+ }
+
+ private fun intiView() {
+ mCommandBtn = findViewById(R.id.btn_command)
+ mInfoBtn = findViewById(R.id.btn_info)
+ mFormatBtn = findViewById(R.id.btn_format)
+ mCodecBtn = findViewById(R.id.btn_codec)
+ mAbiText = findViewById(R.id.tv_abi)
+ mAbiText?.text = String.format("当前使用cpu-abi:%s", Build.CPU_ABI)
+ }
+
+ private fun initListener() {
+ mCommandBtn!!.setOnClickListener(this)
+ mInfoBtn!!.setOnClickListener(this)
+ mFormatBtn!!.setOnClickListener(this)
+ mCodecBtn!!.setOnClickListener(this)
+ }
+
+ override fun onClick(v: View) {
+ when (v.id) {
+ R.id.btn_command -> KFFmpegCommandActivity.start(this)
+ R.id.btn_info -> KFFmpegInfoActivity.start(this)
+ R.id.btn_format -> KFFmppegFormatActivity.start(this)
+ R.id.btn_codec -> KFFmpegCodecActivity.start(this)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/adapter/FFmpegCommandAdapter.java b/app/src/main/java/com/coder/ffmpegtest/ui/adapter/FFmpegCommandAdapter.java
deleted file mode 100644
index 3dbae2b..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/ui/adapter/FFmpegCommandAdapter.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.coder.ffmpegtest.ui.adapter;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.coder.ffmpegtest.R;
-import com.coder.ffmpegtest.model.CommandBean;
-import com.coder.ffmpegtest.ui.vh.FFmpegCommandViewHolder;
-
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-30
- */
-public class FFmpegCommandAdapter extends RecyclerView.Adapter {
-
- private List mStrings;
- private ItemClickListener mItemClickListener;
-
- public void setItemClickListener(ItemClickListener itemClickListener) {
- mItemClickListener = itemClickListener;
- }
-
- public FFmpegCommandAdapter(List strings) {
- mStrings = strings;
- }
-
- @NonNull
- @Override
- public FFmpegCommandViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_ffmpeg_command,parent,false);
- return new FFmpegCommandViewHolder(view);
- }
-
- @Override
- public void onBindViewHolder(@NonNull final FFmpegCommandViewHolder holder, int position) {
- holder.mButton.setText(mStrings.get(position).getName());
- holder.mButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mItemClickListener!=null){
- mItemClickListener.itemClick(mStrings.get(holder.getAdapterPosition()).getId());
- }
- }
- });
- }
-
- @Override
- public int getItemCount() {
- return mStrings.size();
- }
-
- public interface ItemClickListener{
- void itemClick(int id);
- }
-}
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/adapter/FFmpegCommandAdapter.kt b/app/src/main/java/com/coder/ffmpegtest/ui/adapter/FFmpegCommandAdapter.kt
new file mode 100644
index 0000000..e28dee1
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/ui/adapter/FFmpegCommandAdapter.kt
@@ -0,0 +1,43 @@
+package com.coder.ffmpegtest.ui.adapter
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.coder.ffmpegtest.R
+import com.coder.ffmpegtest.model.CommandBean
+import com.coder.ffmpegtest.ui.vh.FFmpegCommandViewHolder
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-30
+ */
+class FFmpegCommandAdapter(private val mStrings: List) : RecyclerView.Adapter() {
+ private var mItemClickListener: ItemClickListener? = null
+
+ fun setItemClickListener(itemClickListener: ItemClickListener) {
+ mItemClickListener = itemClickListener
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FFmpegCommandViewHolder {
+ val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_ffmpeg_command, parent, false)
+ return FFmpegCommandViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: FFmpegCommandViewHolder, position: Int) {
+ holder.mButton.text = mStrings[position].name
+ holder.mButton.setOnClickListener {
+ if (mItemClickListener != null) {
+ mItemClickListener!!.itemClick(mStrings[position].id)
+ }
+ }
+ }
+
+ override fun getItemCount(): Int {
+ return mStrings.size
+ }
+
+ interface ItemClickListener {
+ fun itemClick(id: Int)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/dialog/PromptDialog.java b/app/src/main/java/com/coder/ffmpegtest/ui/dialog/PromptDialog.java
deleted file mode 100644
index ad0a828..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/ui/dialog/PromptDialog.java
+++ /dev/null
@@ -1,163 +0,0 @@
-package com.coder.ffmpegtest.ui.dialog;
-
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.coder.ffmpegtest.R;
-import com.coder.ffmpegtest.base.BaseDialog;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import butterknife.BindView;
-import butterknife.OnClick;
-
-public class PromptDialog extends BaseDialog {
-
- @BindView(R.id.tv_title)
- TextView tvTitle;
- @BindView(R.id.tv_content)
- TextView tvContent;
- @BindView(R.id.btn_positive)
- Button btnPositive;
- @BindView(R.id.btn_negative)
- Button btnNegative;
- @BindView(R.id.vi_line)
- View line;
- @BindView(R.id.progress_bar)
- ProgressBar mProgressBar;
-
- private int gravity = Gravity.LEFT;
- private boolean hasNegativeButton = true;
- private boolean canceledOutside = true;
-
-
- public static PromptDialog newInstance(@Nullable String title, @NonNull String content,
- @Nullable String negative, @Nullable String positive) {
- PromptDialog fragment = new PromptDialog();
- Bundle args = new Bundle();
- args.putString("title", title);
- args.putString("content", content);
- args.putString("negative", negative);
- args.putString("positive", positive);
- fragment.setArguments(args);
- return fragment;
- }
-
- private OnPromptListener mOnPromptListener;
-
- public void setContentGravity(int gravity) {
- this.gravity = gravity;
- }
-
- @Override
- public int getLayoutId() {
- return R.layout.dialog_prompt;
- }
-
- @Override
- protected void init() {
- initData();
- }
-
- private void initData() {
- Bundle args = getArguments();
-
- setTitle(args.getString("title"));
- setNegativeStr(args.getString("negative"));
- setPositiveStr(args.getString("positive"));
-
- tvContent.setGravity(gravity);
-
- btnNegative.setVisibility(hasNegativeButton ? View.VISIBLE : View.GONE);
- line.setVisibility(hasNegativeButton ? View.VISIBLE : View.GONE);
- setCanceledOnTouchOutside(canceledOutside);
- }
-
- public void setHasNegativeButton(boolean hasNegativeButton) {
- this.hasNegativeButton = hasNegativeButton;
- }
-
- @OnClick({R.id.btn_positive, R.id.btn_negative})
- public void onClick(View view) {
- if (view.getId() ==R.id.btn_positive ){
- if (mOnPromptListener != null) {
- mOnPromptListener.onPrompt(true);
- }
- }else {
- if (mOnPromptListener != null) {
- mOnPromptListener.onPrompt(false);
- }
- }
- dismissAllowingStateLoss();
- }
-
- public void setOnPromptListener(OnPromptListener onPromptListener) {
- mOnPromptListener = onPromptListener;
- }
-
- public void setCanceledOutside(boolean canceledOutside) {
- this.canceledOutside = canceledOutside;
- }
-
- public void setTitle(String title) {
- if (!TextUtils.isEmpty(title)) {
- tvTitle.setVisibility(View.VISIBLE);
- tvTitle.setText(title);
- } else {
- tvTitle.setVisibility(View.GONE);
- }
- }
-
- public void setContent(int progress) {
- if (tvContent!=null){
- tvContent.setText(String.format("进度: %s", progress));
- }
- if (mProgressBar!=null){
- mProgressBar.setProgress(progress);
- }
-
- }
-
- private void setPositiveStr(String positiveStr) {
- if (!TextUtils.isEmpty(positiveStr)) {
- btnPositive.setText(positiveStr);
- }
- }
-
- private void setNegativeStr(String negativeStr) {
- if (!TextUtils.isEmpty(negativeStr)) {
- btnNegative.setText(negativeStr);
- }
- }
-
- public interface OnPromptListener {
- /**
- * 返回用户点击【确定】/【取消】
- *
- * @param isPositive 是否点击确定
- */
- void onPrompt(boolean isPositive);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- getView().setFocusableInTouchMode(true);
- getView().requestFocus();
- getView().setOnKeyListener(new View.OnKeyListener() {
- @Override
- public boolean onKey(View view, int i, KeyEvent keyEvent) {
- if(keyEvent.getAction() == KeyEvent.ACTION_DOWN && i == KeyEvent.KEYCODE_BACK){
- return true;
- }
- return false;
- }
- });
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/dialog/PromptDialog.kt b/app/src/main/java/com/coder/ffmpegtest/ui/dialog/PromptDialog.kt
new file mode 100644
index 0000000..700a266
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/ui/dialog/PromptDialog.kt
@@ -0,0 +1,152 @@
+package com.coder.ffmpegtest.ui.dialog
+
+import android.annotation.SuppressLint
+import android.os.Bundle
+import android.text.TextUtils
+import android.view.Gravity
+import android.view.KeyEvent
+import android.view.View
+import android.widget.Button
+import android.widget.ProgressBar
+import android.widget.TextView
+import com.coder.ffmpegtest.R
+import com.coder.ffmpegtest.base.BaseDialog
+
+@SuppressLint("NonConstantResourceId")
+class PromptDialog : BaseDialog(),View.OnClickListener {
+ var tvTitle: TextView? = null
+ var tvContent: TextView? = null
+ var btnPositive: Button? = null
+ var btnNegative: Button? = null
+ var line: View? = null
+ var mProgressBar: ProgressBar? = null
+
+ private var gravity = Gravity.LEFT
+ private var hasNegativeButton = true
+ private var canceledOutside = true
+ private var mOnPromptListener: OnPromptListener? = null
+ fun setContentGravity(gravity: Int) {
+ this.gravity = gravity
+ }
+
+ override val layoutId: Int
+ get() = R.layout.dialog_prompt
+
+ override fun init() {
+ initView()
+ initData()
+ }
+
+ private fun initView(){
+ tvTitle = findViewById(R.id.tv_title) as TextView?
+ tvContent = findViewById(R.id.tv_content) as TextView?
+ btnPositive = findViewById(R.id.btn_positive) as Button?
+ btnNegative = findViewById(R.id.btn_negative) as Button?
+ line = findViewById(R.id.vi_line)
+ mProgressBar = findViewById(R.id.progress_bar) as ProgressBar?
+
+ btnNegative?.setOnClickListener(this)
+ btnPositive?.setOnClickListener(this)
+ }
+
+ private fun initData() {
+ val args = arguments
+ setTitle(args!!.getString("title"))
+ setNegativeStr(args.getString("negative"))
+ setPositiveStr(args.getString("positive"))
+ tvContent!!.gravity = gravity
+ btnNegative!!.visibility = if (hasNegativeButton) View.VISIBLE else View.GONE
+ line!!.visibility = if (hasNegativeButton) View.VISIBLE else View.GONE
+ setCanceledOnTouchOutside(canceledOutside)
+ }
+
+ fun setHasNegativeButton(hasNegativeButton: Boolean) {
+ this.hasNegativeButton = hasNegativeButton
+ }
+
+
+ override fun onClick(view: View) {
+ if (view.id == R.id.btn_positive) {
+ if (mOnPromptListener != null) {
+ mOnPromptListener!!.onPrompt(true)
+ }
+ } else {
+ if (mOnPromptListener != null) {
+ mOnPromptListener!!.onPrompt(false)
+ }
+ }
+ dismissAllowingStateLoss()
+ }
+
+ fun setOnPromptListener(onPromptListener: OnPromptListener?) {
+ mOnPromptListener = onPromptListener
+ }
+
+ fun setCanceledOutside(canceledOutside: Boolean) {
+ this.canceledOutside = canceledOutside
+ }
+
+ fun setTitle(title: String?) {
+ if (!TextUtils.isEmpty(title)) {
+ tvTitle?.visibility = View.VISIBLE
+ tvTitle?.text = title
+ } else {
+ tvTitle?.visibility = View.GONE
+ }
+ }
+
+ fun setContent(progress: Int) {
+ if (tvContent != null) {
+ tvContent?.text = String.format("进度: %s", progress)
+ }
+ if (mProgressBar != null) {
+ mProgressBar?.progress = progress
+ }
+ }
+
+ private fun setPositiveStr(positiveStr: String?) {
+ if (!TextUtils.isEmpty(positiveStr)) {
+ btnPositive?.text = positiveStr
+ }
+ }
+
+ private fun setNegativeStr(negativeStr: String?) {
+ if (!TextUtils.isEmpty(negativeStr)) {
+ btnNegative?.text = negativeStr
+ }
+ }
+
+ interface OnPromptListener {
+ /**
+ * 返回用户点击【确定】/【取消】
+ *
+ * @param isPositive 是否点击确定
+ */
+ fun onPrompt(isPositive: Boolean)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ view!!.isFocusableInTouchMode = true
+ view!!.requestFocus()
+ view!!.setOnKeyListener { view, i, keyEvent ->
+ if (keyEvent.action == KeyEvent.ACTION_DOWN && i == KeyEvent.KEYCODE_BACK) {
+ true
+ } else false
+ }
+ }
+
+ companion object {
+ fun newInstance(title: String?, content: String,
+ negative: String?, positive: String?): PromptDialog {
+ val fragment = PromptDialog()
+ val args = Bundle()
+ args.putString("title", title)
+ args.putString("content", content)
+ args.putString("negative", negative)
+ args.putString("positive", positive)
+ fragment.arguments = args
+ return fragment
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/vh/FFmpegCommandViewHolder.java b/app/src/main/java/com/coder/ffmpegtest/ui/vh/FFmpegCommandViewHolder.java
deleted file mode 100644
index 2b0e338..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/ui/vh/FFmpegCommandViewHolder.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.coder.ffmpegtest.ui.vh;
-
-import android.view.View;
-import android.widget.Button;
-
-import com.coder.ffmpegtest.R;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-30
- */
-public class FFmpegCommandViewHolder extends RecyclerView.ViewHolder {
-
- public Button mButton;
- public FFmpegCommandViewHolder(@NonNull View itemView) {
- super(itemView);
- mButton = itemView.findViewById(R.id.btn_transform_audio);
- }
-}
diff --git a/app/src/main/java/com/coder/ffmpegtest/ui/vh/FFmpegCommandViewHolder.kt b/app/src/main/java/com/coder/ffmpegtest/ui/vh/FFmpegCommandViewHolder.kt
new file mode 100644
index 0000000..ec32c13
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/ui/vh/FFmpegCommandViewHolder.kt
@@ -0,0 +1,15 @@
+package com.coder.ffmpegtest.ui.vh
+
+import android.view.View
+import android.widget.Button
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import com.coder.ffmpegtest.R
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-30
+ */
+class FFmpegCommandViewHolder(itemView: View) : ViewHolder(itemView) {
+ var mButton: Button = itemView.findViewById(R.id.btn_transform_audio)
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/utils/CustomProgressDialog.java b/app/src/main/java/com/coder/ffmpegtest/utils/CustomProgressDialog.java
deleted file mode 100644
index b3cb860..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/utils/CustomProgressDialog.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.coder.ffmpegtest.utils;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.coder.ffmpegtest.R;
-
-import java.lang.ref.WeakReference;
-
-public class CustomProgressDialog extends Dialog implements DialogInterface.OnCancelListener {
-
- private WeakReference mContext = new WeakReference<>(null);
- private static CustomProgressDialog sDialog;
- private TextView mMessageText;
-
- private CustomProgressDialog(Context context, CharSequence message) {
- super(context, R.style.CustomProgressDialog);
-
- mContext = new WeakReference<>(context);
-
- View view = LayoutInflater.from(mContext.get()).inflate(R.layout.dialog_custom_progress,
- null);
- mMessageText = view.findViewById(R.id.tv_message);
- if (!TextUtils.isEmpty(message)) {
- mMessageText.setText(message);
- }
- ViewGroup.LayoutParams lp =
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- addContentView(view, lp);
-
- setOnCancelListener(this);
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- // 点手机返回键等触发Dialog消失,应该取消正在进行的网络请求等
- Context context = mContext.get();
- if (context != null) {
-// Toast.makeText(context, "cancel", Toast.LENGTH_SHORT).show();
- }
- }
-
- public static synchronized void showLoading(Context context) {
- showLoading(context, "loading...");
- }
-
- public static synchronized void showLoading(Context context, boolean cancelable) {
- showLoading(context, "loading...", cancelable);
- }
-
- public static synchronized void showLoading(Context context, CharSequence message) {
- showLoading(context, message, true);
- }
-
- public static synchronized void showLoading(Context context, CharSequence message,
- boolean cancelable) {
- if (sDialog != null && sDialog.isShowing()) {
- sDialog.dismiss();
- }
-
- if (!(context instanceof Activity)) {
- return;
- }
- sDialog = new CustomProgressDialog(context, message);
- sDialog.setCancelable(cancelable);
-
- if (sDialog != null && !sDialog.isShowing() && !((Activity) context).isFinishing()) {
- sDialog.show();
- }
- }
-
- public static synchronized void stopLoading() {
- if (sDialog != null && sDialog.isShowing()) {
- sDialog.dismiss();
- }
- sDialog = null;
- }
-
- public static synchronized void showText(String progress){
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/utils/CustomProgressDialog.kt b/app/src/main/java/com/coder/ffmpegtest/utils/CustomProgressDialog.kt
new file mode 100644
index 0000000..2e70c44
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/utils/CustomProgressDialog.kt
@@ -0,0 +1,85 @@
+package com.coder.ffmpegtest.utils
+
+import android.app.Activity
+import android.app.Dialog
+import android.content.Context
+import android.content.DialogInterface
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.TextView
+import com.coder.ffmpegtest.R
+import java.lang.ref.WeakReference
+
+class CustomProgressDialog private constructor(context: Context, message: CharSequence?) : Dialog(context, R.style.CustomProgressDialog), DialogInterface.OnCancelListener {
+ private var mContext = WeakReference(null)
+ private val mMessageText: TextView
+ override fun onCancel(dialog: DialogInterface) {
+ // 点手机返回键等触发Dialog消失,应该取消正在进行的网络请求等
+ val context = mContext.get()
+ if (context != null) {
+// Toast.makeText(context, "cancel", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ companion object {
+ private var sDialog: CustomProgressDialog? = null
+
+ @Synchronized
+ fun showLoading(context: Context?) {
+ showLoading(context, "loading...")
+ }
+
+ @Synchronized
+ fun showLoading(context: Context?, cancelable: Boolean) {
+ showLoading(context, "loading...", cancelable)
+ }
+
+ @Synchronized
+ fun showLoading(context: Context?, message: CharSequence?) {
+ showLoading(context, message, true)
+ }
+
+ @Synchronized
+ fun showLoading(context: Context?, message: CharSequence?,
+ cancelable: Boolean) {
+ if (sDialog != null && sDialog!!.isShowing) {
+ sDialog!!.dismiss()
+ }
+ if (context !is Activity) {
+ return
+ }
+ sDialog = CustomProgressDialog(context, message)
+ sDialog!!.setCancelable(cancelable)
+ if (sDialog != null && !sDialog!!.isShowing && !context.isFinishing) {
+ sDialog!!.show()
+ }
+ }
+
+ @Synchronized
+ fun stopLoading() {
+ if (sDialog != null && sDialog!!.isShowing) {
+ sDialog!!.dismiss()
+ }
+ sDialog = null
+ }
+
+ @Synchronized
+ fun showText(progress: String?) {
+ }
+ }
+
+ init {
+ mContext = WeakReference(context)
+ val view = LayoutInflater.from(mContext.get()).inflate(R.layout.dialog_custom_progress,
+ null)
+ mMessageText = view.findViewById(R.id.tv_message)
+ if (!TextUtils.isEmpty(message)) {
+ mMessageText.text = message
+ }
+ val lp = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT)
+ addContentView(view, lp)
+ setOnCancelListener(this)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/utils/FileUtils.java b/app/src/main/java/com/coder/ffmpegtest/utils/FileUtils.java
deleted file mode 100644
index 7bf7fb5..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/utils/FileUtils.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.coder.ffmpegtest.utils;
-
-import android.content.Context;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-20
- */
-public class FileUtils {
-
- /**
- * 将asset文件写入缓存
- */
- public static boolean copy2Memory(Context context, String fileName) {
- try {
- File cacheDir = context.getExternalCacheDir();
- if (!cacheDir.exists()) {
- cacheDir.mkdirs();
- }
- File outFile = new File(cacheDir, fileName);
- Log.d("==>>>>", outFile.getAbsolutePath());
- if (!outFile.exists()) {
- boolean res = outFile.createNewFile();
- if (!res) {
- return false;
- }
- } else {
- if (outFile.length() > 10) {//表示已经写入一次
- return true;
- }
- }
- InputStream is = context.getAssets().open(fileName);
- FileOutputStream fos = new FileOutputStream(outFile);
- byte[] buffer = new byte[1024];
- int byteCount;
- while ((byteCount = is.read(buffer)) != -1) {
- fos.write(buffer, 0, byteCount);
- }
- fos.flush();
- is.close();
- fos.close();
- return true;
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- return false;
- }
-
- public static String getFileName(String filePath){
- return filePath.substring(filePath.lastIndexOf(File.separator)+1);
- }
-
- /**
- * 创建写入内容文件
- * 请注意一定要申请文件读写权限
- * @return
- */
- public static String createInputFile(Context context,String... filePaths) {
- File file = new File(context.getExternalCacheDir(), "input.txt");
- String content ="";
- for (String filePath : filePaths) {
- content+="file "+FileUtils.getFileName(filePath)+"\n";
- }
- if (!file.exists()) {
- try {
- file.createNewFile();
- RandomAccessFile raf = new RandomAccessFile(file, "rwd");
- raf.seek(file.length());
- raf.write(content.getBytes());
- raf.close();
- return file.getAbsolutePath();
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- } else {
- file.delete();
- return createInputFile(context, filePaths);
- }
- }
-}
-
diff --git a/app/src/main/java/com/coder/ffmpegtest/utils/FileUtils.kt b/app/src/main/java/com/coder/ffmpegtest/utils/FileUtils.kt
new file mode 100644
index 0000000..c8144b5
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/utils/FileUtils.kt
@@ -0,0 +1,88 @@
+package com.coder.ffmpegtest.utils
+
+import android.content.Context
+import android.util.Log
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.RandomAccessFile
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-20
+ */
+object FileUtils {
+ /**
+ * 将asset文件写入缓存
+ */
+ fun copy2Memory(context: Context, fileName: String?): Boolean {
+ try {
+ val cacheDir = context.externalCacheDir
+ if (!cacheDir!!.exists()) {
+ cacheDir.mkdirs()
+ }
+ val outFile = File(cacheDir, fileName)
+ Log.d("==>>>>", outFile.absolutePath)
+ if (!outFile.exists()) {
+ val res = outFile.createNewFile()
+ if (!res) {
+ return false
+ }
+ } else {
+ if (outFile.length() > 10) { //表示已经写入一次
+ return true
+ }
+ }
+ val `is` = context.assets.open(fileName!!)
+ val fos = FileOutputStream(outFile)
+ val buffer = ByteArray(1024)
+ var byteCount: Int
+ while (`is`.read(buffer).also { byteCount = it } != -1) {
+ fos.write(buffer, 0, byteCount)
+ }
+ fos.flush()
+ `is`.close()
+ fos.close()
+ return true
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ return false
+ }
+
+ fun getFileName(filePath: String): String {
+ return filePath.substring(filePath.lastIndexOf(File.separator) + 1)
+ }
+
+ /**
+ * 创建写入内容文件
+ * 请注意一定要申请文件读写权限
+ * @return
+ */
+ fun createInputFile(context: Context, vararg filePaths: String): String? {
+ val file = File(context.externalCacheDir, "input.txt")
+ var content = ""
+ for (filePath in filePaths) {
+ content += """
+ file ${getFileName(filePath)}
+
+ """.trimIndent()
+ }
+ return if (!file.exists()) {
+ try {
+ file.createNewFile()
+ val raf = RandomAccessFile(file, "rwd")
+ raf.seek(file.length())
+ raf.write(content.toByteArray())
+ raf.close()
+ file.absolutePath
+ } catch (e: IOException) {
+ e.printStackTrace()
+ null
+ }
+ } else {
+ file.delete()
+ createInputFile(context, *filePaths)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/coder/ffmpegtest/utils/ToastUtils.java b/app/src/main/java/com/coder/ffmpegtest/utils/ToastUtils.java
deleted file mode 100644
index c33db73..0000000
--- a/app/src/main/java/com/coder/ffmpegtest/utils/ToastUtils.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.coder.ffmpegtest.utils;
-
-import android.widget.Toast;
-
-import com.coder.ffmpegtest.BaseApplication;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-20
- */
-public class ToastUtils {
- public static void show(String msg) {
- Toast.makeText(BaseApplication.getInstance().getApplicationContext(), msg,
- Toast.LENGTH_SHORT).show();
- }
-}
diff --git a/app/src/main/java/com/coder/ffmpegtest/utils/ToastUtils.kt b/app/src/main/java/com/coder/ffmpegtest/utils/ToastUtils.kt
new file mode 100644
index 0000000..7c9a7b7
--- /dev/null
+++ b/app/src/main/java/com/coder/ffmpegtest/utils/ToastUtils.kt
@@ -0,0 +1,15 @@
+package com.coder.ffmpegtest.utils
+
+import android.widget.Toast
+import com.coder.ffmpegtest.BaseApplication
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-20
+ */
+object ToastUtils {
+ fun show(msg: String?) {
+ Toast.makeText(BaseApplication.Companion.instance?.getApplicationContext(), msg,
+ Toast.LENGTH_SHORT).show()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 28f421b..eca73d5 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -9,13 +9,7 @@
android:gravity="center"
tools:context=".ui.MainActivity">
-
+
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index db986f7..8d41ce0 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -66,6 +66,7 @@
- 视频切片-hls
- 合成hls-视频
- 音频转码amr
+ - 生成静音音频
\ No newline at end of file
diff --git a/app/src/test/java/com/coder/ffmpegtest/ExampleUnitTest.java b/app/src/test/java/com/coder/ffmpegtest/ExampleUnitTest.java
deleted file mode 100644
index 79ecc24..0000000
--- a/app/src/test/java/com/coder/ffmpegtest/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.coder.ffmpegtest;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/app/src/test/java/com/coder/ffmpegtest/ExampleUnitTest.kt b/app/src/test/java/com/coder/ffmpegtest/ExampleUnitTest.kt
new file mode 100644
index 0000000..e1f2638
--- /dev/null
+++ b/app/src/test/java/com/coder/ffmpegtest/ExampleUnitTest.kt
@@ -0,0 +1,16 @@
+package com.coder.ffmpegtest
+
+import org.junit.Assert
+import org.junit.Test
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see [Testing documentation](http://d.android.com/tools/testing)
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ Assert.assertEquals(4, 2 + 2.toLong())
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index d96cc1d..c51c449 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
-buildscript {
+buildscript {
+ ext.kotlin_version = '1.4.20'
ext.kotlin_version = '1.3.61'
repositories {
@@ -12,7 +13,6 @@ buildscript {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
- classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }
diff --git a/ffmpeg-mini/bintray.gradle b/ffmpeg-mini/bintray.gradle
index fed050e..766ece9 100644
--- a/ffmpeg-mini/bintray.gradle
+++ b/ffmpeg-mini/bintray.gradle
@@ -1,7 +1,7 @@
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'
-version = "1.1.8" //版本号,以后每次更新library都得更改
+version = "1.2.0-beta1" //版本号,以后每次更新library都得更改
def siteUrl = 'https://github.com/AnJoiner/FFmpegCommand' //Homepage URL of the library
def gitUrl = 'https://github.com/AnJoiner/FFmpegCommand.git' //Git repository url
def issueUrl = 'https://github.com/AnJoiner/FFmpegCommand/issues' //issue url of the library
diff --git a/ffmpeg-mini/build.gradle b/ffmpeg-mini/build.gradle
index 4206219..01b20b2 100644
--- a/ffmpeg-mini/build.gradle
+++ b/ffmpeg-mini/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
android {
compileSdkVersion 29
@@ -35,4 +36,9 @@ dependencies {
compile 'io.reactivex.rxjava2:rxandroid:2.1.1'
apply from: 'bintray.gradle'
+ implementation "androidx.core:core-ktx:+"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
+repositories {
+ mavenCentral()
}
diff --git a/ffmpeg-mini/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.java b/ffmpeg-mini/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.java
deleted file mode 100644
index bfc9401..0000000
--- a/ffmpeg-mini/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.coder.ffmpeg;
-
-import android.content.Context;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- assertEquals("com.coder.ffmpeg.test", appContext.getPackageName());
- }
-}
diff --git a/ffmpeg-mini/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.kt b/ffmpeg-mini/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..714933b
--- /dev/null
+++ b/ffmpeg-mini/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.kt
@@ -0,0 +1,22 @@
+package com.coder.ffmpeg
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see [Testing documentation](http://d.android.com/tools/testing)
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ Assert.assertEquals("com.coder.ffmpeg.test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Attribute.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Attribute.java
deleted file mode 100644
index ed9f4fe..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Attribute.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.IntDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 20-4-8
- */
-@IntDef({
- Attribute.DURATION,
- Attribute.WIDTH,
- Attribute.HEIGHT,
- Attribute.VIDEO_BIT_RATE,
- Attribute.FPS,
- Attribute.CHANNELS,
- Attribute.SAMPLE_RATE,
- Attribute.AUDIO_BIT_RATE
-})
-public @interface Attribute {
- int DURATION = 0; // 时长
- int WIDTH = 1; //视频宽(分辨率px)
- int HEIGHT = 2; // 视频高(分辨率px)
- int VIDEO_BIT_RATE = 3; //视频比特率
- int FPS = 4; //帧率
- int CHANNELS = 5; //音频声道数
- int SAMPLE_RATE = 6; //音频采样率
- int AUDIO_BIT_RATE = 7; //音频比特率
-}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Attribute.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Attribute.kt
new file mode 100644
index 0000000..960e2b1
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Attribute.kt
@@ -0,0 +1,22 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 20-4-8
+ */
+@IntDef(Attribute.DURATION, Attribute.WIDTH, Attribute.HEIGHT, Attribute.VIDEO_BIT_RATE, Attribute.FPS, Attribute.CHANNELS, Attribute.SAMPLE_RATE, Attribute.AUDIO_BIT_RATE)
+@Deprecated("")
+annotation class Attribute {
+ companion object {
+ const val DURATION = 0 // 时长
+ const val WIDTH = 1 //视频宽(分辨率px)
+ const val HEIGHT = 2 // 视频高(分辨率px)
+ const val VIDEO_BIT_RATE = 3 //视频比特率
+ const val FPS = 4 //帧率
+ const val CHANNELS = 5 //音频声道数
+ const val SAMPLE_RATE = 6 //音频采样率
+ const val AUDIO_BIT_RATE = 7 //音频比特率
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.java
deleted file mode 100644
index e65d2ab..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.IntDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 20-9-9
- */
-@IntDef({
- CodecAttribute.ENCODE,
- CodecAttribute.DECODE,
- CodecAttribute.ENCODE_AUDIO,
- CodecAttribute.DECODE_AUDIO,
- CodecAttribute.ENCODE_VIDEO,
- CodecAttribute.DECODE_VIDEO,
- CodecAttribute.ENCODE_OTHER,
- CodecAttribute.DECODE_OTHER
-})
-public @interface CodecAttribute {
- int ENCODE = 1; // 编码格式
- int DECODE = 2; // 解码格式
- int ENCODE_AUDIO = 3; // 音频编码格式
- int DECODE_AUDIO = 4; // 音频解码格式
- int ENCODE_VIDEO = 5; // 视频编码格式
- int DECODE_VIDEO = 6; // 视频解码格式
- int ENCODE_OTHER = 7; // 其他编码格式
- int DECODE_OTHER = 8; // 其他解码格式
-}
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.kt
new file mode 100644
index 0000000..c01b918
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.kt
@@ -0,0 +1,28 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 20-9-9
+ */
+@IntDef(CodecAttribute.ENCODE,
+ CodecAttribute.DECODE,
+ CodecAttribute.ENCODE_AUDIO,
+ CodecAttribute.DECODE_AUDIO,
+ CodecAttribute.ENCODE_VIDEO,
+ CodecAttribute.DECODE_VIDEO,
+ CodecAttribute.ENCODE_OTHER,
+ CodecAttribute.DECODE_OTHER)
+annotation class CodecAttribute {
+ companion object {
+ const val ENCODE = 1 // 编码格式
+ const val DECODE = 2 // 解码格式
+ const val ENCODE_AUDIO = 3 // 音频编码格式
+ const val DECODE_AUDIO = 4 // 音频解码格式
+ const val ENCODE_VIDEO = 5 // 视频编码格式
+ const val DECODE_VIDEO = 6 // 视频解码格式
+ const val ENCODE_OTHER = 7 // 其他编码格式
+ const val DECODE_OTHER = 8 // 其他解码格式
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Direction.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Direction.java
deleted file mode 100644
index 2b5b536..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Direction.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.IntDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-19
- */
-@IntDef({
- Direction.LAYOUT_HORIZONTAL,
- Direction.LAYOUT_VERTICAL,
-})
-public @interface Direction {
- int LAYOUT_HORIZONTAL = 1;
- int LAYOUT_VERTICAL = 2;
-}
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Direction.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Direction.kt
new file mode 100644
index 0000000..293d7cc
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Direction.kt
@@ -0,0 +1,16 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-19
+ */
+@IntDef(Direction.LAYOUT_HORIZONTAL,
+ Direction.LAYOUT_VERTICAL)
+annotation class Direction {
+ companion object {
+ const val LAYOUT_HORIZONTAL = 1
+ const val LAYOUT_VERTICAL = 2
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.java
deleted file mode 100644
index 08b3b20..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.IntDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 20-9-9
- */
-@IntDef({
- FormatAttribute.INPUT_FORMAT,
- FormatAttribute.OUTPUT_FORMAT
-})
-public @interface FormatAttribute {
- int INPUT_FORMAT = 1; // 输入格式
- int OUTPUT_FORMAT = 2; // 输出格式
-}
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.kt
new file mode 100644
index 0000000..3aa504d
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.kt
@@ -0,0 +1,16 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 20-9-9
+ */
+@IntDef(FormatAttribute.INPUT_FORMAT,
+ FormatAttribute.OUTPUT_FORMAT)
+annotation class FormatAttribute {
+ companion object {
+ const val INPUT_FORMAT = 1 // 输入格式
+ const val OUTPUT_FORMAT = 2 // 输出格式
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.java
deleted file mode 100644
index 95fe587..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.StringDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-22
- */
-@StringDef({
- ImageFormat.PNG,
- ImageFormat.JPG,
-})
-public @interface ImageFormat {
- String PNG = "png";
- String JPG = "jpg";
-}
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.kt
new file mode 100644
index 0000000..e4d8034
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.kt
@@ -0,0 +1,16 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.StringDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-22
+ */
+@StringDef(ImageFormat.PNG,
+ ImageFormat.JPG)
+annotation class ImageFormat {
+ companion object {
+ const val PNG = "png"
+ const val JPG = "jpg"
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/MediaAttribute.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/MediaAttribute.kt
new file mode 100644
index 0000000..cd36e0a
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/MediaAttribute.kt
@@ -0,0 +1,29 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+import com.coder.ffmpeg.annotation.MediaAttribute
+
+/**
+ * @author: AnJoiner
+ * @datetime: 20-11-22
+ */
+@IntDef(MediaAttribute.DURATION,
+ MediaAttribute.WIDTH,
+ MediaAttribute.HEIGHT,
+ MediaAttribute.VIDEO_BIT_RATE,
+ MediaAttribute.FPS,
+ MediaAttribute.CHANNELS,
+ MediaAttribute.SAMPLE_RATE,
+ MediaAttribute.AUDIO_BIT_RATE)
+annotation class MediaAttribute {
+ companion object {
+ const val DURATION = 0 // 时长
+ const val WIDTH = 1 //视频宽(分辨率px)
+ const val HEIGHT = 2 // 视频高(分辨率px)
+ const val VIDEO_BIT_RATE = 3 //视频比特率
+ const val FPS = 4
+ const val CHANNELS = 5 //音频声道数
+ const val SAMPLE_RATE = 6 //音频采样率
+ const val AUDIO_BIT_RATE = 7 //音频比特率
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Transpose.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Transpose.java
deleted file mode 100644
index ada3536..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Transpose.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.IntDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-30
- */
-@IntDef({
- Transpose.CLOCKWISE_ROTATION_90,
- Transpose.ANTICLOCKWISE_ROTATION_90,
- Transpose.CLOCKWISE_ROTATION_90_FLIP,
- Transpose.ANTICLOCKWISE_ROTATION_90_FLIP
-})
-public @interface Transpose { ;
- //顺时针旋转画面90度
- int CLOCKWISE_ROTATION_90 = 1;
- // 逆时针旋转画面90度
- int ANTICLOCKWISE_ROTATION_90 = 2;
- //顺时针旋转画面90度再水平翻转
- int CLOCKWISE_ROTATION_90_FLIP = 3;
- // 逆时针旋转画面90度水平翻转
- int ANTICLOCKWISE_ROTATION_90_FLIP = 0;
-}
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Transpose.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Transpose.kt
new file mode 100644
index 0000000..e25633f
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/annotation/Transpose.kt
@@ -0,0 +1,27 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-30
+ */
+@IntDef(Transpose.CLOCKWISE_ROTATION_90,
+ Transpose.ANTICLOCKWISE_ROTATION_90,
+ Transpose.CLOCKWISE_ROTATION_90_FLIP,
+ Transpose.ANTICLOCKWISE_ROTATION_90_FLIP)
+annotation class Transpose {
+ companion object {
+ //顺时针旋转画面90度
+ const val CLOCKWISE_ROTATION_90 = 1
+
+ // 逆时针旋转画面90度
+ const val ANTICLOCKWISE_ROTATION_90 = 2
+
+ //顺时针旋转画面90度再水平翻转
+ const val CLOCKWISE_ROTATION_90_FLIP = 3
+
+ // 逆时针旋转画面90度水平翻转
+ const val ANTICLOCKWISE_ROTATION_90_FLIP = 0
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/CommonCallBack.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/CommonCallBack.java
deleted file mode 100644
index 40682e7..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/CommonCallBack.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.coder.ffmpeg.call;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-17
- */
-public abstract class CommonCallBack implements IFFmpegCallBack {
-
- @Override
- public void onError(Throwable t) {
- t.printStackTrace();
- }
-
- @Override
- public void onStart() {
-
- }
-
- @Override
- public void onProgress(int progress) {
-
- }
-
- @Override
- public void onCancel() {
-
- }
-}
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/CommonCallBack.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/CommonCallBack.kt
new file mode 100644
index 0000000..1cfb5f3
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/CommonCallBack.kt
@@ -0,0 +1,13 @@
+package com.coder.ffmpeg.call
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-17
+ */
+open class CommonCallBack : IFFmpegCallBack {
+ override fun onStart() {}
+ override fun onProgress(progress: Int, pts: Long) {}
+ override fun onCancel() {}
+ override fun onComplete() {}
+ override fun onError(errorCode: Int, errorMsg: String?) {}
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/ICallBack.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/ICallBack.java
deleted file mode 100644
index 0d0f475..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/ICallBack.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.coder.ffmpeg.call;
-
-public interface ICallBack {
- void onError(Throwable t);
-
- /**
- * 进度回调
- * @param progress
- */
- void onProgress(int progress);
-
- void onComplete();
-
- void onStart();
-
-}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.java
deleted file mode 100644
index 3d6e6fd..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.coder.ffmpeg.call;
-
-/**
- * @author: AnJoiner
- * @datetime: 20-4-22
- */
-public interface IFFmpegCallBack extends ICallBack {
-
- /**
- * 取消回调
- */
- void onCancel();
-}
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.kt
new file mode 100644
index 0000000..64f502c
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.kt
@@ -0,0 +1,34 @@
+package com.coder.ffmpeg.call
+
+interface IFFmpegCallBack {
+ /**
+ * 开始回调
+ */
+ fun onStart()
+
+ /**
+ * 进度回调
+ *
+ * @param progress 当前执行进度
+ * @param pts 当前执行长度
+ */
+ fun onProgress(progress: Int, pts: Long)
+
+ /**
+ * 取消回调
+ */
+ fun onCancel()
+
+ /**
+ * 完成回调
+ */
+ fun onComplete()
+
+ /**
+ * 错误回调
+ *
+ * @param errorCode 错误码
+ * @param errorMsg 错误内容
+ */
+ fun onError(errorCode: Int, errorMsg: String?)
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.java
deleted file mode 100644
index b56c5f1..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.coder.ffmpeg.jni;
-
-import android.util.Log;
-
-import com.coder.ffmpeg.annotation.Attribute;
-import com.coder.ffmpeg.annotation.CodecAttribute;
-import com.coder.ffmpeg.annotation.FormatAttribute;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-17
- */
-class FFmpegCmd {
-
- @Deprecated
- private FFmpegCommand.OnFFmpegProgressListener mCmdListener;
-
- private FFmpegCommand.OnFFmpegCommandListener mCommandListener;
-
- static boolean DEBUG = true;
-
-
- static {
- System.loadLibrary("avdevice");
- System.loadLibrary("avutil");
- System.loadLibrary("avcodec");
- System.loadLibrary("swresample");
- System.loadLibrary("avformat");
- System.loadLibrary("swscale");
- System.loadLibrary("avfilter");
- System.loadLibrary("postproc");
- System.loadLibrary("ffmpeg-invoke");
- }
-
- private native int runSync(int cmdLen,String[] cmd);
-
- private native int runAsync(int cmdLen,String[] cmd);
-
- int runCmdSync(String[] cmd) {
- cmd = command(cmd);
- return runSync(cmd.length, cmd);
- }
-
- @Deprecated
- int runCmdSync(String[] cmd, FFmpegCommand.OnFFmpegProgressListener cmdListener) {
- cmd = command(cmd);
- mCmdListener = cmdListener;
- int result = runSync(cmd.length, cmd);
- if (mCmdListener != null) {
- mCmdListener = null;
- }
- return result;
- }
-
- int runCmdSync(String[] cmd, FFmpegCommand.OnFFmpegCommandListener cmdListener) {
- cmd = command(cmd);
- mCommandListener = cmdListener;
-
- int result = runSync(cmd.length, cmd);
- if (mCommandListener != null) {
- mCommandListener = null;
- }
- return result;
- }
-
- int getInfo(String videoPath, @Attribute int type) {
- return info(videoPath, type);
- }
-
- private native int info(String videoPath, int type);
-
- public String getFormatInfo(@FormatAttribute int format){
- return formatInfo(format);
- }
-
- private native String formatInfo(int format);
-
- public String getCodecInfo(@CodecAttribute int codec){
- return codecInfo(codec);
- }
-
- private native String codecInfo(int codec);
-
- private String[] command(String[] cmd) {
- String[] cmds = new String[cmd.length + 1];
- for (int i = 0; i < cmds.length; i++) {
- if (i < 1) {
- cmds[i] = cmd[i];
- } else if (i == 1) {
- cmds[i] = "-d";
- } else {
- cmds[i] = cmd[i - 1];
- }
- }
- return DEBUG ? cmds : cmd;
- }
-
- native int getProgress();
-
- @Deprecated
- native void exit();
-
- native void cancel();
-
-
- void onStart(){
-
- }
-
- void onProgress(int progress) {
- if (mCmdListener != null) {
- mCmdListener.onProgress(progress);
- }
- if (mCommandListener != null) {
- mCommandListener.onProgress(progress);
- }
- }
-
- void onCancel() {
- if (mCommandListener != null) {
- mCommandListener.onCancel();
- }
- // 移除当前对象,释放内存
- FFmpegCommand.cmds.remove(this);
- }
-
- void onComplete(){
- if (mCommandListener != null) {
- mCommandListener.onComplete();
- }
- // 移除当前对象,释放内存
- FFmpegCommand.cmds.remove(this);
- }
-
- void onError(int errorCode, String errorMsg){
- Log.e("CmdError","errorCode:"+errorCode+", errorMsg:"+errorMsg);
- // 移除当前对象,释放内存
- FFmpegCommand.cmds.remove(this);
- }
-}
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.kt
new file mode 100644
index 0000000..57af771
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.kt
@@ -0,0 +1,223 @@
+package com.coder.ffmpeg.jni
+
+import com.coder.ffmpeg.annotation.CodecAttribute
+import com.coder.ffmpeg.annotation.FormatAttribute
+import com.coder.ffmpeg.annotation.MediaAttribute
+import com.coder.ffmpeg.call.IFFmpegCallBack
+import java.util.*
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-17
+ */
+internal class FFmpegCmd private constructor() {
+ // Program execution callback
+ private val mCallBacks = Collections.synchronizedList(ArrayList())
+ // debugging mode
+ private var ffdebug = true
+
+ companion object {
+ var instance: FFmpegCmd? = null
+ get() {
+ if (field == null) {
+ field = FFmpegCmd()
+ }
+ return field
+ }
+ private set
+
+ init {
+ System.loadLibrary("avdevice")
+ System.loadLibrary("avutil")
+ System.loadLibrary("avcodec")
+ System.loadLibrary("swresample")
+ System.loadLibrary("avformat")
+ System.loadLibrary("swscale")
+ System.loadLibrary("avfilter")
+ System.loadLibrary("postproc")
+ System.loadLibrary("ffmpeg-invoke")
+ }
+ }
+
+
+ /**
+ * Whether to enable debugging mode
+ * @param debug true or false
+ */
+ fun setDebug(debug: Boolean) {
+ this.ffdebug = debug
+ }
+
+ /**
+ * Provide ffmpeg command method to execute
+ * @param command ffmeng command
+ * @return execute status
+ */
+ fun runCmd(command: Array): Int {
+ var cmd = command
+ cmd = buildCommand(cmd)
+ return run(getCommand(cmd))
+ }
+
+ /**
+ * Provide ffmpeg command method to execute
+ * @param command ffmeng command
+ * @param callBack callback result
+ * @return execute status
+ */
+ fun runCmd(command: Array, callBack: IFFmpegCallBack?): Int {
+ var cmd = command
+ mCallBacks.add(callBack)
+ cmd = buildCommand(cmd)
+ return run(getCommand(cmd))
+ }
+
+ /**
+ * Return the complete command
+ * @param cmd ffmeng command
+ * @return ffmeng command
+ */
+ private fun getCommand(cmd: Array): String {
+ val stringBuffer = StringBuilder()
+ for (i in cmd.indices) {
+ stringBuffer.append(cmd[i])
+ if (i < cmd.size - 1) {
+ stringBuffer.append(" ")
+ }
+ }
+ return stringBuffer.toString()
+ }
+
+ /**
+ * Re-survive command according to whether it is debug mode
+ * @param cmd ffmeng command
+ * @return ffmeng command
+ */
+ private fun buildCommand(cmd: Array): Array {
+ val cmds = arrayOfNulls(cmd.size + 1)
+ for (i in cmds.indices) {
+ when {
+ i < 1 -> {
+ cmds[i] = cmd[i]
+ }
+ i == 1 -> {
+ cmds[i] = "-d"
+ }
+ else -> {
+ cmds[i] = cmd[i - 1]
+ }
+ }
+ }
+ return if (ffdebug) cmds else cmd
+ }
+
+ /**
+ * Execute ffmpeg command method
+ * @param command ffmeng command
+ * @return execute status
+ */
+ private external fun run(command: String?): Int
+
+ /**
+ * Get media information
+ * @param videoPath media path
+ * @param type information type
+ * @return media information
+ */
+ fun getMediaInfo(videoPath: String?, @MediaAttribute type: Int): Int? {
+ return info(videoPath, type)
+ }
+
+ /**
+ * Call native to get media information.
+ * @param videoPath media path
+ * @param type information type.
+ */
+ private external fun info(videoPath: String?, type: Int): Int
+
+ /**
+ * Provide method to get format info .
+ * @param format format type.
+ */
+ fun getFormatInfo(@FormatAttribute format: Int): String {
+ return formatInfo(format)
+ }
+
+ /**
+ * Call native to get support format.
+ * @param format format type.
+ *
+ */
+ private external fun formatInfo(format: Int): String
+
+ /**
+ * Provide method to get Codec info .
+ * @param codec codec type.
+ */
+ fun getCodecInfo(@CodecAttribute codec: Int): String {
+ return codecInfo(codec)
+ }
+ /**
+ * Call native to get support codec.
+ * @param codec codec type.
+ *
+ */
+ private external fun codecInfo(codec: Int): String
+
+ @Deprecated("")
+ external fun exit()
+
+ external fun cancel()
+
+ /**
+ * Provide the callback of start execute commands.
+ */
+ fun onStart() {
+ for (callBack in mCallBacks) {
+ callBack.onStart()
+ }
+ }
+
+ /**
+ * Provide the callback of progress
+ * @param progress progress for ffmpeg
+ * @param pts duration of current ffmepg command execution
+ */
+ fun onProgress(progress: Int, pts: Long) {
+ for (callBack in mCallBacks) {
+ callBack.onProgress(progress, pts)
+ }
+ }
+
+ /**
+ * Provide the callback of cancel execute commands.
+ */
+ fun onCancel() {
+ for (callBack in mCallBacks) {
+ callBack.onCancel()
+ mCallBacks.remove(callBack)
+ }
+ }
+
+ /**
+ * Provide the callback of execute commands is completed.
+ */
+ fun onComplete() {
+ for (callBack in mCallBacks) {
+ callBack.onComplete()
+ mCallBacks.remove(callBack)
+ }
+ }
+
+ /**
+ * Provide the callback of error execute commands.
+ * @param errorCode error code of execute commands.
+ * @param errorMsg error message of execute commands.
+ */
+ fun onError(errorCode: Int, errorMsg: String?) {
+ for (callBack in mCallBacks) {
+ callBack.onError(errorCode, errorMsg)
+ mCallBacks.remove(callBack)
+ }
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.java
deleted file mode 100644
index 21a86f0..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.java
+++ /dev/null
@@ -1,524 +0,0 @@
-package com.coder.ffmpeg.jni;
-
-import android.annotation.SuppressLint;
-import android.os.Handler;
-import android.os.Message;
-
-import com.coder.ffmpeg.annotation.Attribute;
-import com.coder.ffmpeg.annotation.CodecAttribute;
-import com.coder.ffmpeg.annotation.FormatAttribute;
-import com.coder.ffmpeg.call.ICallBack;
-import com.coder.ffmpeg.call.IFFmpegCallBack;
-
-import org.reactivestreams.Subscriber;
-import org.reactivestreams.Subscription;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import io.reactivex.BackpressureStrategy;
-import io.reactivex.Flowable;
-import io.reactivex.FlowableEmitter;
-import io.reactivex.FlowableOnSubscribe;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.schedulers.Schedulers;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-17
- */
-public class FFmpegCommand {
-
-
- static List cmds;
- static List listeners;
- static int globalProgress = 0;
-
- static {
- cmds = new ArrayList<>();
- listeners = new ArrayList<>();
- }
-
- /**
- * 切换到主线程并返回进度
- */
- @SuppressLint("HandlerLeak")
- private static Handler mHandler = new Handler(){
- @Override
- public void handleMessage(@NonNull Message msg) {
- super.handleMessage(msg);
- if (msg.what == 1){
- IFFmpegCallBack callBack = (IFFmpegCallBack) msg.obj;
- if (callBack!=null){
- int progress = msg.arg1;
- int size = msg.arg2;
- globalAsyncCallbackProgress(callBack,progress,size);
- }
- }else if (msg.what == -1){
- IFFmpegCallBack callBack = (IFFmpegCallBack) msg.obj;
- if (callBack!=null){
- callBack.onCancel();
- listeners.clear();
- }
- }else if (msg.what == 0){
- IFFmpegCallBack callBack = (IFFmpegCallBack) msg.obj;
- if (callBack!=null){
- int size = msg.arg2;
- globalAsyncCallbackComplete(callBack,size);
- }
- }
- }
- };
-
- /**
- * 是否开启debug模式
- *
- * @param debug true:开启 false :不开启
- */
- public static void setDebug(boolean debug) {
- FFmpegCmd.DEBUG = debug;
- }
-
- /**
- * 同步执行 获取媒体信息
- *
- * @param path 媒体地址
- * @param type 属性类型 {@link com.coder.ffmpeg.annotation.Attribute}
- * @return 媒体信息
- */
- public static int getInfoSync(String path, @Attribute int type) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- return ffmpegCmd.getInfo(path, type);
- }
-
- /**
- * 获取支持解封装格式
- * @param formatType 格式类型 {@link FormatAttribute}
- * @return 格式信息
- */
- public static String getSupportFormat(@FormatAttribute int formatType){
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- return ffmpegCmd.getFormatInfo(formatType);
- }
-
- /**
- * 获取支持编解码
- * @param codecType 编解码类型 {@link CodecAttribute}
- * @return 编解码信息
- */
- public static String getSupportCodec(@CodecAttribute int codecType){
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- return ffmpegCmd.getCodecInfo(codecType);
- }
-
- /**
- * 同步执行 ffmpeg命令
- *
- * @param cmd ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- */
- public static void runSync(final String[] cmd) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- cmds.add(ffmpegCmd);
- ffmpegCmd.runCmdSync(cmd);
- }
-
- /**
- * 同步执行 ffmpeg命令
- *当前方法已被弃用,请参考{@link FFmpegCommand#runSync(String[], OnFFmpegCommandListener)}
- * @param cmd ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param listener {@link OnFFmpegProgressListener}
- */
- @Deprecated
- public static void runSync(final String[] cmd, OnFFmpegProgressListener listener) {
- if (listener != null) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- cmds.add(ffmpegCmd);
- ffmpegCmd.runCmdSync(cmd, listener);
- }
- }
-
- /**
- * 同步执行 ffmpeg命令
- *
- * @param cmd ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param listener {@link OnFFmpegCommandListener}
- */
- public static void runSync(final String[] cmd, OnFFmpegCommandListener listener) {
- if (listener != null) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- cmds.add(ffmpegCmd);
- ffmpegCmd.runCmdSync(cmd, listener);
- }
- }
-
- /**
- * 同步多命令执行 ffmpeg命令
- *
- * @param cmds 命令集合, ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param listener {@link OnFFmpegCommandListener}
- */
- public static void runMoreSync(final List cmds, final OnFFmpegCommandListener listener) {
- if (listener != null) {
- globalProgress = 0;
- for (String[] cmd : cmds) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- FFmpegCommand.cmds.add(ffmpegCmd);
- OnFFmpegCommandListener commandListener = new OnFFmpegCommandListener() {
- @Override
- public void onProgress(int progress) {
- globalSyncCallbackProgress(listener,progress,cmds.size());
- }
-
- @Override
- public void onCancel() {
- listener.onCancel();
- listeners.clear();
- }
-
- @Override
- public void onComplete() {
- globalSyncCallbackComplete(listener,cmds.size());
- }
- };
- listeners.add(commandListener);
- ffmpegCmd.runCmdSync(cmd, commandListener);
- }
- }
- }
-
- /**
- * 全局进度回调处理
- * @param listener {@link OnFFmpegCommandListener}
- * @param progress 进度
- * @param size 命令数量
- */
- private static void globalSyncCallbackProgress(OnFFmpegCommandListener listener, int progress, int size){
- int temp;
- if (progress == 100){
- globalProgress+=progress;
- temp = globalProgress;
- }else {
- temp = globalProgress+progress;
- }
- if (listener!=null && size>0){
- listener.onProgress(temp/size);
- }
- }
-
-
- /**
- * 全局完成回调处理
- * @param listener listener {@link OnFFmpegCommandListener}
- * @param size 命令数量
- */
- private static void globalSyncCallbackComplete(OnFFmpegCommandListener listener,int size){
- if (listener!=null && size>0 && globalProgress/size == 100){
- listener.onComplete();
- listeners.clear();
- }
- }
-
- /**
- * 当前方法已被弃用,请参考{@link FFmpegCommand#runAsync(String[], IFFmpegCallBack)}
- * 异步执行 ffmpeg命令
- *
- * @param cmd ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param callBack {@link ICallBack}
- */
- @Deprecated
- public static void runAsync(final String[] cmd, final ICallBack callBack) {
- Flowable.create(new FlowableOnSubscribe() {
- @Override
- public void subscribe(final FlowableEmitter emitter) throws Exception {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- cmds.add(ffmpegCmd);
- ffmpegCmd.runCmdSync(cmd, new OnFFmpegProgressListener() {
- @Override
- public void onProgress(int progress) {
- if (callBack != null) {
- callBack.onProgress(progress);
- }
- }
- });
- emitter.onComplete();
- }
- }, BackpressureStrategy.BUFFER)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Subscriber() {
- @Override
- public void onSubscribe(Subscription s) {
- if (callBack != null) {
- callBack.onStart();
- }
- }
-
- @Override
- public void onNext(Integer integer) {
- }
-
- @Override
- public void onError(Throwable t) {
- cmds.clear();
- if (callBack != null) {
- callBack.onError(t);
- }
- }
-
- @Override
- public void onComplete() {
- if (callBack != null) {
- callBack.onComplete();
- }
- }
- });
- }
-
-
- /**
- * 异步执行 ffmpeg命令
- *
- * @param cmd ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param callBack {@link IFFmpegCallBack}
- */
- public static void runAsync(final String[] cmd, final IFFmpegCallBack callBack) {
- Flowable.create(new FlowableOnSubscribe() {
- @Override
- public void subscribe(final FlowableEmitter emitter) throws Exception {
- globalProgress = 0;
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- cmds.add(ffmpegCmd);
- OnFFmpegCommandListener commandListener = new OnFFmpegCommandListener() {
- @Override
- public void onProgress(int progress) {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = 1;
- msg.obj = callBack;
- msg.arg1 = progress;
- msg.arg2 = 1;
- mHandler.sendMessage(msg);
- }
- }
- }
-
- @Override
- public void onCancel() {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = -1;
- msg.obj = callBack;
- msg.arg2 = 1;
- mHandler.sendMessage(msg);
- }
- }
- }
-
- @Override
- public void onComplete() {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = 0;
- msg.obj = callBack;
- msg.arg2 = 1;
- mHandler.sendMessage(msg);
- }
- }
- }
- };
- ffmpegCmd.runCmdSync(cmd,commandListener);
- }
- }, BackpressureStrategy.BUFFER)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Subscriber() {
- @Override
- public void onSubscribe(Subscription s) {
- if (callBack != null) {
- callBack.onStart();
- }
- }
-
- @Override
- public void onNext(Boolean isComplete) {
- // 验证是否是完成
- }
-
- @Override
- public void onError(Throwable t) {
- cmds.clear();
- if (callBack != null) {
- callBack.onError(t);
- }
- }
-
- @Override
- public void onComplete() {
- }
- });
- }
-
-
- /**
- * 异步执行 ffmpeg命令
- *
- * @param cmds 命令集合, ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param callBack {@link IFFmpegCallBack}
- */
- public static void runMoreAsync(final List cmds, final IFFmpegCallBack callBack) {
- Flowable.create(new FlowableOnSubscribe() {
- @Override
- public void subscribe(final FlowableEmitter emitter) throws Exception {
- globalProgress = 0;
- for (String[] cmd : cmds) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- FFmpegCommand.cmds.add(ffmpegCmd);
- OnFFmpegCommandListener commandListener = new OnFFmpegCommandListener() {
- @Override
- public void onProgress(int progress) {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = 1;
- msg.obj = callBack;
- msg.arg1 = progress;
- msg.arg2 = cmds.size();
- mHandler.sendMessage(msg);
- }
- }
- }
-
- @Override
- public void onCancel() {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = -1;
- msg.obj = callBack;
- msg.arg2 = cmds.size();
- mHandler.sendMessage(msg);
- }
- }
- }
-
- @Override
- public void onComplete() {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = 0;
- msg.obj = callBack;
- msg.arg2 = cmds.size();
- mHandler.sendMessage(msg);
- }
- }
- }
- };
- FFmpegCommand.listeners.add(commandListener);
- ffmpegCmd.runCmdSync(cmd,commandListener);
-
- }
- }
- }, BackpressureStrategy.BUFFER)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Subscriber() {
- @Override
- public void onSubscribe(Subscription s) {
- if (callBack != null) {
- callBack.onStart();
- }
- }
-
- @Override
- public void onNext(Boolean isComplete) {
- // 验证是否是完成
- }
-
- @Override
- public void onError(Throwable t) {
- cmds.clear();
- if (callBack != null) {
- callBack.onError(t);
- }
- }
-
- @Override
- public void onComplete() {
- }
- });
- }
-
- /**
- * 全局进度回调处理
- * @param callBack {@link IFFmpegCallBack}
- * @param progress 进度
- * @param size 命令数量
- */
- private static void globalAsyncCallbackProgress(IFFmpegCallBack callBack, int progress, int size){
- int temp;
- // 多命令进度回调,当进度100时直接进入下一条命令进度中
- if (progress == 100){
- if (globalProgress < size * 100){
- globalProgress+=progress;
- }
- temp = globalProgress;
- }else {
- temp = globalProgress+progress;
- }
- if (callBack!=null && size>0){
- callBack.onProgress(temp/size);
- }
- }
-
-
- /**
- * 全局完成回调处理
- * @param callBack {@link IFFmpegCallBack}
- * @param size 命令数量
- */
- private static void globalAsyncCallbackComplete(IFFmpegCallBack callBack,int size){
- if (callBack!=null && size>0 && globalProgress/size == 100){
- callBack.onComplete();
- listeners.clear();
- }
- }
-
- /**
- * 当前方法已被弃用,请参考{@link FFmpegCommand#cancel()}}
- * 退出执行
- */
- @Deprecated
- public static void exit(){
- for (FFmpegCmd cmd : cmds) {
- cmd.exit();
- }
- }
-
- /**
- * 退出执行
- */
- public static void cancel(){
- for (FFmpegCmd cmd : cmds) {
- cmd.cancel();
- }
- }
-
- @Deprecated
- public interface OnFFmpegProgressListener {
- void onProgress(int progress);
- }
-
- public interface OnFFmpegCommandListener {
-
- void onProgress(int progress);
-
- void onCancel();
-
- void onComplete();
- }
-}
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.kt
new file mode 100644
index 0000000..eee9850
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.kt
@@ -0,0 +1,77 @@
+package com.coder.ffmpeg.jni
+
+import com.coder.ffmpeg.annotation.CodecAttribute
+import com.coder.ffmpeg.annotation.FormatAttribute
+import com.coder.ffmpeg.annotation.MediaAttribute
+import com.coder.ffmpeg.call.IFFmpegCallBack
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-17
+ */
+object FFmpegCommand {
+ /**
+ * Whether to enable debug mode
+ *
+ * @param debug true:enable false :not enable
+ */
+ fun setDebug(debug: Boolean) {
+ FFmpegCmd.instance?.setDebug(debug)
+ }
+
+ /**
+ * Get media information
+ *
+ * @param path media path
+ * @param type media attribute type [MediaAttribute]
+ * @return media information
+ */
+ fun getMediaInfo(path: String?, @MediaAttribute type: Int): Int? {
+ return FFmpegCmd.instance?.getMediaInfo(path, type)
+ }
+
+ /**
+ * Get support for unpacking format
+ *
+ * @param formatType format attribute type [FormatAttribute]
+ * @return format information
+ */
+ fun getSupportFormat(@FormatAttribute formatType: Int): String? {
+ return FFmpegCmd.instance?.getFormatInfo(formatType)
+ }
+
+ /**
+ * Get support codec
+ *
+ * @param codecType codec attribute type [CodecAttribute]
+ * @return codec info
+ */
+ fun getSupportCodec(@CodecAttribute codecType: Int): String? {
+ return FFmpegCmd.instance?.getCodecInfo(codecType)
+ }
+
+ /**
+ * Execute FFmpeg commands.
+ *
+ * @param cmd ffmpeg commands [com.coder.ffmpeg.utils.FFmpegUtils]
+ */
+ fun runCmd(cmd: Array, callBack: IFFmpegCallBack?): Int? {
+ return FFmpegCmd.instance?.runCmd(cmd, callBack)
+ }
+
+ /**
+ * Execute FFmpeg commands.
+ *
+ * @param cmd ffmpeg commands [com.coder.ffmpeg.utils.FFmpegUtils]
+ */
+ fun runCmd(cmd: Array): Int? {
+ return FFmpegCmd.instance?.runCmd(cmd)
+ }
+
+ /**
+ * Quit execute.
+ */
+ fun cancel() {
+ FFmpegCmd.instance?.cancel()
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.java b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.java
deleted file mode 100644
index cb4eeca..0000000
--- a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.java
+++ /dev/null
@@ -1,863 +0,0 @@
-package com.coder.ffmpeg.utils;
-
-import android.annotation.SuppressLint;
-import android.util.Log;
-
-import com.coder.ffmpeg.annotation.Direction;
-import com.coder.ffmpeg.annotation.ImageFormat;
-import com.coder.ffmpeg.annotation.Transpose;
-
-import java.util.Locale;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-17
- */
-public class FFmpegUtils {
- /**
- * 使用ffmpeg命令行进行音频转码
- *
- * @param srcFile 源文件
- * @param targetFile 目标文件(后缀指定转码格式)
- * @return 转码后的文件
- */
-
- public static String[] transformAudio(String srcFile, String targetFile) {
- String command = "ffmpeg -y -i %s %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行音频剪切
- *
- * @param srcFile 源文件
- * @param startTime 剪切的开始时间(单位为秒)
- * @param duration 剪切时长(单位为秒)
- * @param targetFile 目标文件
- * @return 剪切后的文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] cutAudio(String srcFile, int startTime, int duration,
- String targetFile) {
- String command = "ffmpeg -y -i %s -vn -acodec copy -ss %d -t %d %s";
- command = String.format(command, srcFile, startTime, duration, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * @param srcFile 源文件
- * @param startTime 剪切的开始时间(单位为秒)
- * @param endTime 剪切的结束时间(单位为秒)
- * @param targetFile 目标文件
- * @return 剪切后的文件
- */
- public static String[] cutAudio(String srcFile, String startTime, String endTime,
- String targetFile) {
- String cmd = "ffmpeg -y -i %s -vn -acodec copy -ss %s -t %s %s";
- String command = String.format(cmd, srcFile, startTime, endTime, targetFile);
-
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行音频合并
- *
- * @param srcFile 源文件
- * @param appendFile 待追加的文件
- * @param targetFile 目标文件
- * @return 合并后的文件
- */
- public static String[] concatAudio(String srcFile, String appendFile, String targetFile) {
- //ffmpeg -y -i "concat:123.mp3|124.mp3" -acodec copy output.mp3
- //ffmpeg -i 1.mp3 -i 2.mp3 -filter_complex '[0:0] [1:0] concat=n=2:v=0:a=1 [a]' -map [a] new.mp3
- String command = "ffmpeg -y -i concat:%s|%s -acodec copy %s";
- command = String.format(command, srcFile, appendFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行音频混合
- *
- * @param srcFile 源文件
- * @param mixFile 待混合文件
- * @param targetFile 目标文件
- * @return 混合后的文件
- */
- public static String[] mixAudio(String srcFile, String mixFile, String targetFile) {
- //-filter_complex
- // amix=inputs=2:duration=shortest output.wav
- String command = "ffmpeg -y -i %s -i %s -filter_complex amix=inputs=2:duration=shortest %s";
-
- command = String.format(command, srcFile, mixFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * @param audio 源文件
- * @param reduce 音量(单位dB)
- * @param outPath 输出地址
- * @return 命令
- */
- public static String[] changeVolume(String audio, int reduce, String outPath) {
- String cmd = "ffmpeg -y -i %s -af volume=%sdB %s";
- String command = String.format(cmd, audio, reduce, outPath);
- Log.d("FFMEPG", command);
- return command.split(" ");
- }
-
-
- /**
- * @param audio 源文件
- * @param reduce 音量倍数
- * @param outPath 输出地址
- * @return 命令
- */
- public static String[] changeVolume(String audio, float reduce, String outPath) {
- String cmd = "ffmpeg -y -i %s -af volume=%s %s";
- String command = String.format(cmd, audio, reduce, outPath);
- Log.d("FFMEPG", command);
- return command.split(" ");
- }
-
-
- /**
- * 使用ffmpeg命令行进行音视频合成
- *
- * @param videoFile 视频文件
- * @param audioFile 音频文件
- * @param duration 视频时长
- * @param muxFile 目标文件
- * @return 合成后的文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] mixAudioVideo(String videoFile, String audioFile, int duration,
- String muxFile) {
- //-t:时长 如果忽略音视频时长,则把"-t %d"去掉
- String command = "ffmpeg -y -i %s -i %s -t %d %s";
- command = String.format(command, videoFile, audioFile, duration, muxFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行音视频合成
- *
- * @param videoFile 视频文件
- * @param audioFile 音频文件 (aac 格式)
- * @param muxFile 目标文件
- * @return 合成后的文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] mixAudioVideo(String videoFile, String audioFile,
- String muxFile) {
- //-t:时长 如果忽略音视频时长,则把"-t %d"去掉
- String command = "ffmpeg -y -i %s -i %s -codec copy %s";
- command = String.format(command, videoFile, audioFile, muxFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行抽取音频
- *
- * @param srcFile 原文件
- * @param targetFile 目标文件
- * @return 抽取后的音频文件
- */
- public static String[] extractAudio(String srcFile, String targetFile) {
- //-vn:video not
- String command = "ffmpeg -y -i %s -acodec copy -vn %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行抽取视频
- *
- * @param srcFile 原文件
- * @param targetFile 目标文件
- * @return 抽取后的视频文件
- */
- public static String[] extractVideo(String srcFile, String targetFile) {
- //-an audio not
- String command = "ffmpeg -y -i %s -vcodec copy -an %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行视频转码
- *
- * @param srcFile 源文件
- * @param targetFile 目标文件(后缀指定转码格式)
- * @return 转码后的文件
- */
- public static String[] transformVideo(String srcFile, String targetFile) {
- //指定目标视频的帧率、码率、分辨率
-// String transformVideoCmd = "ffmpeg -i %s -r 25 -b 200 -s 1080x720 %s";
- String command = "ffmpeg -y -i %s -vcodec copy -acodec copy %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行视频剪切(不包含)
- *
- * @param srcFile 源文件
- * @param startTime 剪切的开始时间(单位为秒)
- * @param duration 剪切时长(单位为秒)
- * @param targetFile 目标文件
- * @return 剪切后的文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] cutVideo(String srcFile, int startTime, int duration,
- String targetFile) {
- String command = "ffmpeg -y -i %s -ss %d -t %d -c copy %s";
- command = String.format(command, srcFile, startTime, duration, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行视频剪切(包含关键帧)
- *
- * @param srcFile 源文件
- * @param startTime 剪切的开始时间(单位为秒)
- * @param duration 剪切时长(单位为秒)
- * @param targetFile 目标文件
- * @return 剪切后的文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] cutVideo2(String srcFile, int startTime, int duration,
- String targetFile) {
- String command = "ffmpeg -y -ss %d -t %d -accurate_seek -i %s -codec copy " +
- "-avoid_negative_ts 1 %s";
- command = String.format(command, startTime, duration, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行音频合并
- *
- * @param inputFile 输入文件(.txt格式)
- * @param targetFile 目标文件
- * @return 合并后的文件
- */
- public static String[] concatVideo(String inputFile, String targetFile) {
- // ffmpeg -f concat -i inputs.txt -c copy output.flv
- String command = "ffmpeg -y -f concat -i %s -codec copy %s";
- command = String.format(command, inputFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行视频截图
- *
- * @param srcFile 源文件
- * @param size 图片尺寸大小
- * @param targetFile 目标文件
- * @return 截图后的文件
- */
- public static String[] screenShot(String srcFile, String size, String targetFile) {
- String command = "ffmpeg -y -i %s -f image2 -t 0.001 -s %s %s";
- command = String.format(command, srcFile, size, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行视频截图
- *
- * @param srcFile 源文件
- * @param targetFile 目标文件
- * @return 截图后的文件
- */
- public static String[] screenShot(String srcFile, String targetFile) {
- String command = "ffmpeg -y -i %s -f image2 -t 0.001 %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * @param inputFile 视频源文件
- * @param targetDir 图片生成目录
- * @param format 图片格式
- * @return 图片文件
- */
- public static String[] video2Image(String inputFile, String targetDir,
- @ImageFormat String format) {
- //ffmpeg -i ss.mp4 -r 1 -f image2 image-%3d.jpeg
- String command = "ffmpeg -y -i %s -r 1 -f image2 %s";
- command = String.format(Locale.CHINESE, command, inputFile, targetDir);
- command = command + "/%3d." + format;
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 视频抽帧转成图片
- *
- * @param inputFile 输入文件
- * @param startTime 开始时间
- * @param duration 持续时间
- * @param frameRate 帧率
- * @param targetFile 输出文件
- * @param format 图片格式
- * @return 视频抽帧的命令行
- */
- public static String[] video2Image(String inputFile, int startTime, int duration,
- int frameRate, String targetFile,
- @ImageFormat String format) {
- //-ss:开始时间,单位为秒
- //-t:持续时间,单位为秒
- //-r:帧率,每秒抽多少帧
- String command = "ffmpeg -y -i %s -ss %s -t %s -r %s %s";
- command = String.format(Locale.CHINESE, command, inputFile, startTime, duration,
- frameRate, targetFile);
- command = command + "%3d." + format;
- return command.split(" ");
- }
-
- /**
- * 使用ffmpeg命令行给视频添加水印
- *
- * @param srcFile 源文件
- * @param waterMark 水印文件路径
- * @param targetFile 目标文件
- * @return 添加水印后的文件
- */
- public static String[] addWaterMark(String srcFile, String waterMark, String targetFile) {
- String command = "ffmpeg -y -i %s -i %s -filter_complex overlay=40:40 %s";
- command = String.format(command, srcFile, waterMark, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行视频转成Gif动图
- *
- * @param srcFile 源文件
- * @param startTime 开始时间
- * @param duration 截取时长
- * @param targetFile 目标文件
- * @return Gif文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] video2Gif(String srcFile, int startTime, int duration,
- String targetFile) {
- //String screenShotCmd = "ffmpeg -i %s -vframes %d -s 320x240 -f gif %s";
- String command = "ffmpeg -y -i %s -ss %d -t %d -f gif %s";
- command = String.format(command, srcFile, startTime, duration, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行屏幕录制
- *
- * @param size 视频尺寸大小
- * @param recordTime 录屏时间
- * @param targetFile 目标文件
- * @return 屏幕录制文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] screenRecord(String size, int recordTime, String targetFile) {
- //-vd x11:0,0 指录制所使用的偏移为 x=0 和 y=0
- //String screenRecordCmd = "ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s %s %s";
- String command = "ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s %s -t " +
- "%d %s";
- command = String.format(command, size, recordTime, targetFile);
- Log.i("VideoHandleActivity", "screenRecordCmd=" + command);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行图片合成视频
- *
- * @param srcFile 源文件
- * @param targetFile 目标文件
- * @return 合成的视频文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] image2Video(String srcFile, String targetFile) {
- //-f image2:代表使用image2格式,需要放在输入文件前面
- String command = "ffmpeg -y -f image2 -r 1 -i %simg#d.jpg -vcodec mpeg4 %s";
- command = String.format(command, srcFile, targetFile);
- command = command.replace("#", "%");
- Log.i("VideoHandleActivity", "combineVideo=" + command);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- public static String[] image2Video(String srcDir, @ImageFormat String format,
- String targetFile) {
- //-f image2:代表使用image2格式,需要放在输入文件前面
- // ffmpeg -f image2 -i image-%3d.jpeg images.mp4
- String command = "ffmpeg -y -f image2 -r 1 -i %s/#3d.%s -vcodec mpeg4 %s";
- command = String.format(command, srcDir, format, targetFile);
- command = command.replace("#", "%");
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 音频解码
- *
- * @param srcFile 源文件
- * @param targetFile 目标文件
- * @param sampleRate 采样率
- * @param channel 声道:单声道为1/立体声道为2
- * @return 音频解码的命令行
- */
- public static String[] decodeAudio(String srcFile, String targetFile, int sampleRate,
- int channel) {
- String command = "ffmpeg -y -i %s -acodec pcm_s16le -ar %d -ac %d -f s16le %s";
- command = String.format(command, srcFile, sampleRate, channel, targetFile);
- return command.split(" ");
- }
-
- /**
- * 音频编码
- *
- * @param srcFile 源文件pcm裸流
- * @param targetFile 编码后目标文件
- * @param sampleRate 采样率
- * @param channel 声道:单声道为1/立体声道为2
- * @return 音频编码的命令行
- */
- @SuppressLint("DefaultLocale")
- public static String[] encodeAudio(String srcFile, String targetFile, int sampleRate,
- int channel) {
- String command = "ffmpeg -y -f s16le -ar %d -ac %d -acodec pcm_s16le -i %s %s";
- command = String.format(command, sampleRate, channel, srcFile, targetFile);
- return command.split(" ");
- }
-
- /**
- * 多画面拼接视频
- *
- * @param input1 输入文件1
- * @param input2 输入文件2
- * @param direction 视频布局方向
- * @param targetFile 画面拼接文件
- * @return 画面拼接的命令行
- */
- public static String[] multiVideo(String input1, String input2, String targetFile,
- @Direction int direction) {
-// String multiVideo = "ffmpeg -i %s -i %s -i %s -i %s -filter_complex " +
-// "\"[0:v]pad=iw*2:ih*2[a];[a][1:v]overlay=w[b];[b][2:v]overlay=0:h[c];
-// [c][3:v]overlay=w:h\" %s";
- String command = "ffmpeg -y -i %s -i %s -filter_complex hstack %s";//hstack:水平拼接,默认
- if (direction == Direction.LAYOUT_VERTICAL) {//vstack:垂直拼接
- command = command.replace("hstack", "vstack");
- }
- command = String.format(command, input1, input2, targetFile);
- return command.split(" ");
- }
-
- /**
- * 视频反序倒播
- *
- * @param inputFile 输入文件
- * @param targetFile 反序文件
- * @return 视频反序的命令行
- */
- public static String[] reverseVideo(String inputFile, String targetFile) {
- String command = "ffmpeg -y -i %s -filter_complex [0:v]reverse[v] -map [v] %s";//单纯视频反序
- command = String.format(command, inputFile, targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频反序倒播
- *
- * @param inputFile 输入文件
- * @param targetFile 反序文件
- * @return 视频反序的命令行
- */
- public static String[] reverseAudioVideo(String inputFile, String targetFile) {
- String command = "ffmpeg -y -i %s -filter_complex [0:v]reverse[v];[0:a]reverse[a] -map [v] -map [a] %s";
- command = String.format(command, inputFile, targetFile);
- return command.split(" ");
- }
-
- /**
- * 视频降噪
- *
- * @param inputFile 输入文件
- * @param targetFile 输出文件
- * @return 视频降噪的命令行
- */
- public static String[] denoiseVideo(String inputFile, String targetFile) {
- String command = "ffmpeg -y -i %s -nr 500 %s";
- command = String.format(command, inputFile, targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频叠加成画中画
- *
- * @param inputFile1 输入文件
- * @param inputFile2 输入文件
- * @param targetFile 输出文件
- * @param x 小视频起点x坐标
- * @param y 小视频起点y坐标
- * @return 视频画中画的命令行
- */
- @SuppressLint("DefaultLocale")
- public static String[] picInPicVideo(String inputFile1, String inputFile2, int x, int y,
- String targetFile) {
- String command = "ffmpeg -y -i %s -i %s -filter_complex overlay=%d:%d %s";
- command = String.format(command, inputFile1, inputFile2, x, y, targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频缩小一倍
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 视频缩小一倍 命令行
- */
- public static String[] videoDoubleDown(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -vf scale=iw/2:-1 %s";
- command = String.format(command,srcFile,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频放大一倍
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 视频放大一倍命令行
- */
- @Deprecated
- public static String[] videoDouble(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -vf scale=iw*2:-1 %s";
- command = String.format(command,srcFile,targetFile);
- return command.split(" ");
- }
-
- /**
- * 视频放大一倍
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 视频放大一倍命令行
- */
- public static String[] videoDoubleUp(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -vf scale=iw*2:-1 %s";
- command = String.format(command,srcFile,targetFile);
- return command.split(" ");
- }
-
- /**
- * 视频缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @param height 缩放后高度
- * @return 视频缩放命令行
- */
- public static String[] videoScale(String srcFile, String targetFile, int width, int height){
- return scale(srcFile, targetFile, width, height);
- }
-
- /**
- * 视频缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @return 视频缩放命令行
- */
- public static String[] videoScale(String srcFile, String targetFile, int width){
- return scale(srcFile, targetFile, width);
- }
-
- /**
- * 图片缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @param height 缩放后高度
- * @return 视频缩放命令行
- */
- public static String[] imageScale(String srcFile, String targetFile, int width, int height){
- return scale(srcFile, targetFile, width, height);
- }
-
- /**
- * 图片缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @return 视频缩放命令行
- */
- public static String[] imageScale(String srcFile, String targetFile, int width){
- return scale(srcFile, targetFile, width);
- }
-
- /**
- * 缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @param height 缩放后高度
- * @return 缩放命令行
- */
- public static String[] scale(String srcFile, String targetFile, int width, int height){
- String command = "ffmpeg -y -i %s -vf scale=%d:%d %s";
- command = String.format(Locale.CHINA,command,srcFile,width,height,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 按宽度等比例缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @return 缩放命令行
- */
- public static String[] scale(String srcFile, String targetFile,int width ){
- String command = "ffmpeg -y -i %s -vf scale=%d:-1 %s";
- command = String.format(Locale.CHINA,command,srcFile,width,targetFile);
- return command.split(" ");
- }
-
- /**
- * 倍速播放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 倍速播放命令行
- */
- public static String[] videoSpeed2(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -filter_complex [0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a] -map [v] -map [a] %s";
- command = String.format(command,srcFile,targetFile);
- return command.split(" ");
- }
-
- /**
- * 解码成YUV原始数据
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 视频解码命令行
- */
- public static String[] decode2YUV(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s";
- command = String.format(command,srcFile,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * YUV 转 H264
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return YUV 转 H264命令行
- */
- public static String[] yuv2H264(String srcFile, String targetFile){
-// String command = "ffmpeg -y -f rawvideo -pix_fmt yuv420p -s 720x1280 -r 30 -i %s -c:v libx264 -f rawvideo %s";
-// command = String.format(command,srcFile,targetFile);
- return yuv2H264(srcFile, targetFile,720,1280);
- }
-
-
- /**
- * YUV 编码 H264
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 输出文件宽
- * @param height 输出文件高
- * @return YUV 转 H264命令行
- */
- public static String[] yuv2H264(String srcFile, String targetFile,int width, int height){
- String command = "ffmpeg -y -f rawvideo -pix_fmt yuv420p -s #wx#h -r 30 -i %s -c:v libx264 -f rawvideo %s";
- command = String.format(Locale.CHINA,command,srcFile,targetFile);
- command = command.replace("#wx#h",width+"x"+height);
- return command.split(" ");
- }
-
-
- /**
- * 音频前5s淡入
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 音频前5s淡入命令行
- */
- public static String [] audioFadeIn(String srcFile, String targetFile){
- return audioFadeIn(srcFile, targetFile,0,5);
- }
-
- /**
- * 音频淡入
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param start 开始位置(s)
- * @param duration 持续时间(s)
- * @return 音频淡入命令行
- */
- public static String[] audioFadeIn(String srcFile, String targetFile, int start, int duration){
- String command = "ffmpeg -y -i %s -filter_complex afade=t=in:ss=%d:d=%d %s";
- command = String.format(Locale.CHINA, command,srcFile,start,duration,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 音频淡出
- * @param srcFile 音频源文件
- * @param targetFile 输出文件
- * @param start 开始位置(s)
- * @param duration 持续时间(s)
- * @return 音频淡出命令行
- */
- public static String[] audioFadeOut(String srcFile, String targetFile, int start, int duration){
- String command = "ffmpeg -y -i %s -filter_complex afade=t=out:st=%d:d=%d %s";
- command = String.format(Locale.CHINA, command,srcFile,start,duration,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频亮度
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param bright 亮度(-1.0 ~ 1.0), 默认0
- * @return 视频亮度命令行
- */
- public static String[] videoBright(String srcFile, String targetFile, float bright){
- String command = "ffmpeg -y -i %s -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=brightness=%f -f mp4 %s";
- command = String.format(Locale.CHINA,command,srcFile,bright,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频对比度
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param contrast 对比度(-2.0 ~ 2.0), 默认1.0
- * @return
- */
- public static String[] videoContrast(String srcFile, String targetFile, float contrast){
- String command ="ffmpeg -y -i %s -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=contrast=%f -f mp4 %s";
- command = String.format(Locale.CHINA,command,srcFile,contrast,targetFile);
- return command.split(" ");
- }
-
- /**
- * 视频旋转
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param transpose
- * @return 视频旋转命令行
- */
- public static String[] videoRotation(String srcFile, String targetFile,@Transpose int transpose){
- String command = "ffmpeg -y -i %s -vf transpose=%d -b:v 600k %s";
- command = String.format(Locale.CHINA,command,srcFile,transpose,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 从视频中获取一帧输出图片
- * @param srcFile 源文件
- * @param targetFile 目标文件(png 或 jpg)
- * @param time 一帧的时间:hh:mm:ss.xxx
- * @return 从视频中获取一帧输出图片命令行
- */
- public static String[] frame2Image(String srcFile, String targetFile, String time){
- String command = "ffmpeg -y -i %s -ss %s -vframes 1 %s";
- command = String.format(command, srcFile,time, targetFile);
- return command.split(" ");
- }
-
- /**
- * 将音频进行fdk_aac编码
- * @param srcFile 音频源文件
- * @param targetFile 音频输出文件(m4a或aac)
- * @return 将音频进行fdk_aac编码命令
- */
- public static String[] audio2Fdkaac(String srcFile,String targetFile){
- String command = "ffmpeg -y -i %s -c:a libfdk_aac %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 将音频进行VBR MP3编码
- * @param srcFile 音频源文件
- * @param targetFile 音频输出文件(mp3)
- * @return 将音频进行VBR MP3编码命令
- */
- public static String[] audio2Mp3lame(String srcFile,String targetFile){
- String command = "ffmpeg -y -i %s -c:a libmp3lame %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 将格式视频进行切片,形成m3u8的视频流(m3u8格式一般用于直播或者点播)
- * @param srcFile 视频路径
- * @param targetFile 目标路径(以xxx.m3u8为输出)
- * @param splitTime 切割时间 (单位:秒)
- * @return 返回以target文件名开头的ts系列文件 如:out0.ts out1.ts ...
- *
- * {@link FFmpegUtils#video2HLS}
- */
- @Deprecated
- public static String[] videoHLS(String srcFile, String targetFile,int splitTime){
- String command = "ffmpeg -y -i %s -c copy -bsf:v h264_mp4toannexb -hls_time %s %s";
- command = String.format(command, srcFile, splitTime ,targetFile);
- return command.split(" ");
- }
-
- /**
- * 将格式视频进行切片,形成m3u8的视频流(m3u8格式一般用于直播或者点播)
- * @param srcFile 视频路径
- * @param targetFile 目标路径(以xxx.m3u8为输出)
- * @param splitTime 切割时间 (单位:秒)
- * @return 返回以target文件名开头的ts系列文件 如:out0.ts out1.ts ...
- */
- public static String[] video2HLS(String srcFile, String targetFile,int splitTime){
- String command = "ffmpeg -y -i %s -c copy -bsf:v h264_mp4toannexb -hls_time %s %s";
- command = String.format(command, srcFile, splitTime ,targetFile);
- return command.split(" ");
- }
-
- /**
- * 将ts视频流合成视频
- * @param m3u8Index xx.m3u8视频索引
- * @param targetFile 目标路径
- * @return 返回合成视频
- */
- public static String[] hls2Video(String m3u8Index, String targetFile){
- String command = "ffmpeg -y -i %s -c copy %s";
- command = String.format(command, m3u8Index ,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 将音频转为amr格式
- * @param srcFile 音频源文件
- * @param targetFile 目标文件
- * @return amr格式音频
- */
- public static String[] audio2Amr(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -c:a libopencore_amrnb -ar 8000 -ac 1 %s";
- command = String.format(command, srcFile ,targetFile);
- return command.split(" ");
- }
-}
diff --git a/ffmpeg-mini/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.kt b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.kt
new file mode 100644
index 0000000..45e37bd
--- /dev/null
+++ b/ffmpeg-mini/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.kt
@@ -0,0 +1,849 @@
+package com.coder.ffmpeg.utils
+
+import android.annotation.SuppressLint
+import android.util.Log
+import com.coder.ffmpeg.annotation.Direction
+import com.coder.ffmpeg.annotation.ImageFormat
+import com.coder.ffmpeg.annotation.Transpose
+import java.util.*
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-17
+ */
+object FFmpegUtils {
+ /**
+ * 使用ffmpeg命令行进行音频转码
+ *
+ * @param srcFile 源文件
+ * @param targetFile 目标文件(后缀指定转码格式)
+ * @return 转码后的文件
+ */
+ fun transformAudio(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音频剪切
+ *
+ * @param srcFile 源文件
+ * @param startTime 剪切的开始时间(单位为秒)
+ * @param duration 剪切时长(单位为秒)
+ * @param targetFile 目标文件
+ * @return 剪切后的文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun cutAudio(srcFile: String?, startTime: Int, duration: Int,
+ targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -vn -acodec copy -ss %d -t %d %s"
+ command = String.format(command, srcFile, startTime, duration, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * @param srcFile 源文件
+ * @param startTime 剪切的开始时间(单位为秒)
+ * @param endTime 剪切的结束时间(单位为秒)
+ * @param targetFile 目标文件
+ * @return 剪切后的文件
+ */
+ fun cutAudio(srcFile: String?, startTime: String?, endTime: String?,
+ targetFile: String?): Array {
+ val cmd = "ffmpeg -y -i %s -vn -acodec copy -ss %s -t %s %s"
+ val command = String.format(cmd, srcFile, startTime, endTime, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音频合并
+ *
+ * @param srcFile 源文件
+ * @param appendFile 待追加的文件
+ * @param targetFile 目标文件
+ * @return 合并后的文件
+ */
+ fun concatAudio(srcFile: String?, appendFile: String?, targetFile: String?): Array {
+ //ffmpeg -y -i "concat:123.mp3|124.mp3" -acodec copy output.mp3
+ //ffmpeg -i 1.mp3 -i 2.mp3 -filter_complex '[0:0] [1:0] concat=n=2:v=0:a=1 [a]' -map [a] new.mp3
+ var command = "ffmpeg -y -i concat:%s|%s -acodec copy %s"
+ command = String.format(command, srcFile, appendFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音频混合
+ *
+ * @param srcFile 源文件
+ * @param mixFile 待混合文件
+ * @param targetFile 目标文件
+ * @return 混合后的文件
+ */
+ fun mixAudio(srcFile: String?, mixFile: String?, targetFile: String?): Array {
+ //-filter_complex
+ // amix=inputs=2:duration=shortest output.wav
+ var command = "ffmpeg -y -i %s -i %s -filter_complex amix=inputs=2:duration=shortest %s"
+ command = String.format(command, srcFile, mixFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * @param audio 源文件
+ * @param reduce 音量(单位dB)
+ * @param outPath 输出地址
+ * @return 命令
+ */
+ fun changeVolume(audio: String?, reduce: Int, outPath: String?): Array {
+ val cmd = "ffmpeg -y -i %s -af volume=%sdB %s"
+ val command = String.format(cmd, audio, reduce, outPath)
+ Log.d("FFMEPG", command)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * @param audio 源文件
+ * @param reduce 音量倍数
+ * @param outPath 输出地址
+ * @return 命令
+ */
+ fun changeVolume(audio: String?, reduce: Float, outPath: String?): Array {
+ val cmd = "ffmpeg -y -i %s -af volume=%s %s"
+ val command = String.format(cmd, audio, reduce, outPath)
+ Log.d("FFMEPG", command)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音视频合成
+ *
+ * @param videoFile 视频文件
+ * @param audioFile 音频文件
+ * @param duration 视频时长
+ * @param muxFile 目标文件
+ * @return 合成后的文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun mixAudioVideo(videoFile: String?, audioFile: String?, duration: Int,
+ muxFile: String?): Array {
+ //-t:时长 如果忽略音视频时长,则把"-t %d"去掉
+ var command = "ffmpeg -y -i %s -i %s -t %d %s"
+ command = String.format(command, videoFile, audioFile, duration, muxFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音视频合成
+ *
+ * @param videoFile 视频文件
+ * @param audioFile 音频文件 (aac 格式)
+ * @param muxFile 目标文件
+ * @return 合成后的文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun mixAudioVideo(videoFile: String?, audioFile: String?,
+ muxFile: String?): Array {
+ //-t:时长 如果忽略音视频时长,则把"-t %d"去掉
+ var command = "ffmpeg -y -i %s -i %s -codec copy %s"
+ command = String.format(command, videoFile, audioFile, muxFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行抽取音频
+ *
+ * @param srcFile 原文件
+ * @param targetFile 目标文件
+ * @return 抽取后的音频文件
+ */
+ fun extractAudio(srcFile: String?, targetFile: String?): Array {
+ //-vn:video not
+ var command = "ffmpeg -y -i %s -acodec copy -vn %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行抽取视频
+ *
+ * @param srcFile 原文件
+ * @param targetFile 目标文件
+ * @return 抽取后的视频文件
+ */
+ fun extractVideo(srcFile: String?, targetFile: String?): Array {
+ //-an audio not
+ var command = "ffmpeg -y -i %s -vcodec copy -an %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频转码
+ *
+ * @param srcFile 源文件
+ * @param targetFile 目标文件(后缀指定转码格式)
+ * @return 转码后的文件
+ */
+ fun transformVideo(srcFile: String?, targetFile: String?): Array {
+ //指定目标视频的帧率、码率、分辨率
+// String transformVideoCmd = "ffmpeg -i %s -r 25 -b 200 -s 1080x720 %s";
+ var command = "ffmpeg -y -i %s -vcodec copy -acodec copy %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频剪切(不包含)
+ *
+ * @param srcFile 源文件
+ * @param startTime 剪切的开始时间(单位为秒)
+ * @param duration 剪切时长(单位为秒)
+ * @param targetFile 目标文件
+ * @return 剪切后的文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun cutVideo(srcFile: String?, startTime: Int, duration: Int,
+ targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -ss %d -t %d -c copy %s"
+ command = String.format(command, srcFile, startTime, duration, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频剪切(包含关键帧)
+ *
+ * @param srcFile 源文件
+ * @param startTime 剪切的开始时间(单位为秒)
+ * @param duration 剪切时长(单位为秒)
+ * @param targetFile 目标文件
+ * @return 剪切后的文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun cutVideo2(srcFile: String?, startTime: Int, duration: Int,
+ targetFile: String?): Array {
+ var command = "ffmpeg -y -ss %d -t %d -accurate_seek -i %s -codec copy " +
+ "-avoid_negative_ts 1 %s"
+ command = String.format(command, startTime, duration, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音频合并
+ *
+ * @param inputFile 输入文件(.txt格式)
+ * @param targetFile 目标文件
+ * @return 合并后的文件
+ */
+ fun concatVideo(inputFile: String?, targetFile: String?): Array {
+ // ffmpeg -f concat -i inputs.txt -c copy output.flv
+ var command = "ffmpeg -y -f concat -i %s -codec copy %s"
+ command = String.format(command, inputFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频截图
+ *
+ * @param srcFile 源文件
+ * @param size 图片尺寸大小
+ * @param targetFile 目标文件
+ * @return 截图后的文件
+ */
+ fun screenShot(srcFile: String?, size: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -f image2 -t 0.001 -s %s %s"
+ command = String.format(command, srcFile, size, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频截图
+ *
+ * @param srcFile 源文件
+ * @param targetFile 目标文件
+ * @return 截图后的文件
+ */
+ fun screenShot(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -f image2 -t 0.001 %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * @param inputFile 视频源文件
+ * @param targetDir 图片生成目录
+ * @param format 图片格式
+ * @return 图片文件
+ */
+ fun video2Image(inputFile: String?, targetDir: String?,
+ @ImageFormat format: String): Array {
+ //ffmpeg -i ss.mp4 -r 1 -f image2 image-%3d.jpeg
+ var command = "ffmpeg -y -i %s -r 1 -f image2 %s"
+ command = String.format(Locale.CHINESE, command, inputFile, targetDir)
+ command = "$command/%3d.$format"
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 视频抽帧转成图片
+ *
+ * @param inputFile 输入文件
+ * @param startTime 开始时间
+ * @param duration 持续时间
+ * @param frameRate 帧率
+ * @param targetFile 输出文件
+ * @param format 图片格式
+ * @return 视频抽帧的命令行
+ */
+ fun video2Image(inputFile: String?, startTime: Int, duration: Int,
+ frameRate: Int, targetFile: String?,
+ @ImageFormat format: String): Array {
+ //-ss:开始时间,单位为秒
+ //-t:持续时间,单位为秒
+ //-r:帧率,每秒抽多少帧
+ var command = "ffmpeg -y -i %s -ss %s -t %s -r %s %s"
+ command = String.format(Locale.CHINESE, command, inputFile, startTime, duration,
+ frameRate, targetFile)
+ command = "$command%3d.$format"
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行给视频添加水印
+ *
+ * @param srcFile 源文件
+ * @param waterMark 水印文件路径
+ * @param targetFile 目标文件
+ * @return 添加水印后的文件
+ */
+ fun addWaterMark(srcFile: String?, waterMark: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -i %s -filter_complex overlay=40:40 %s"
+ command = String.format(command, srcFile, waterMark, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频转成Gif动图
+ *
+ * @param srcFile 源文件
+ * @param startTime 开始时间
+ * @param duration 截取时长
+ * @param targetFile 目标文件
+ * @return Gif文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun video2Gif(srcFile: String?, startTime: Int, duration: Int,
+ targetFile: String?): Array {
+ //String screenShotCmd = "ffmpeg -i %s -vframes %d -s 320x240 -f gif %s";
+ var command = "ffmpeg -y -i %s -ss %d -t %d -f gif %s"
+ command = String.format(command, srcFile, startTime, duration, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行屏幕录制
+ *
+ * @param size 视频尺寸大小
+ * @param recordTime 录屏时间
+ * @param targetFile 目标文件
+ * @return 屏幕录制文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun screenRecord(size: String?, recordTime: Int, targetFile: String?): Array {
+ //-vd x11:0,0 指录制所使用的偏移为 x=0 和 y=0
+ //String screenRecordCmd = "ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s %s %s";
+ var command = "ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s %s -t " +
+ "%d %s"
+ command = String.format(command, size, recordTime, targetFile)
+ Log.i("VideoHandleActivity", "screenRecordCmd=$command")
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行图片合成视频
+ *
+ * @param srcFile 源文件
+ * @param targetFile 目标文件
+ * @return 合成的视频文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun image2Video(srcFile: String?, targetFile: String?): Array {
+ //-f image2:代表使用image2格式,需要放在输入文件前面
+ var command = "ffmpeg -y -f image2 -r 1 -i %simg#d.jpg -vcodec mpeg4 %s"
+ command = String.format(command, srcFile, targetFile)
+ command = command.replace("#", "%")
+ Log.i("VideoHandleActivity", "combineVideo=$command")
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ fun image2Video(srcDir: String?, @ImageFormat format: String?,
+ targetFile: String?): Array {
+ //-f image2:代表使用image2格式,需要放在输入文件前面
+ // ffmpeg -f image2 -i image-%3d.jpeg images.mp4
+ var command = "ffmpeg -y -f image2 -r 1 -i %s/#3d.%s -vcodec mpeg4 %s"
+ command = String.format(command, srcDir, format, targetFile)
+ command = command.replace("#", "%")
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 音频解码
+ *
+ * @param srcFile 源文件
+ * @param targetFile 目标文件
+ * @param sampleRate 采样率
+ * @param channel 声道:单声道为1/立体声道为2
+ * @return 音频解码的命令行
+ */
+ fun decodeAudio(srcFile: String?, targetFile: String?, sampleRate: Int,
+ channel: Int): Array {
+ var command = "ffmpeg -y -i %s -acodec pcm_s16le -ar %d -ac %d -f s16le %s"
+ command = String.format(command, srcFile, sampleRate, channel, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 音频编码
+ *
+ * @param srcFile 源文件pcm裸流
+ * @param targetFile 编码后目标文件
+ * @param sampleRate 采样率
+ * @param channel 声道:单声道为1/立体声道为2
+ * @return 音频编码的命令行
+ */
+ @SuppressLint("DefaultLocale")
+ fun encodeAudio(srcFile: String?, targetFile: String?, sampleRate: Int,
+ channel: Int): Array {
+ var command = "ffmpeg -y -f s16le -ar %d -ac %d -acodec pcm_s16le -i %s %s"
+ command = String.format(command, sampleRate, channel, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 多画面拼接视频
+ *
+ * @param input1 输入文件1
+ * @param input2 输入文件2
+ * @param direction 视频布局方向
+ * @param targetFile 画面拼接文件
+ * @return 画面拼接的命令行
+ */
+ fun multiVideo(input1: String?, input2: String?, targetFile: String?,
+ @Direction direction: Int): Array {
+// String multiVideo = "ffmpeg -i %s -i %s -i %s -i %s -filter_complex " +
+// "\"[0:v]pad=iw*2:ih*2[a];[a][1:v]overlay=w[b];[b][2:v]overlay=0:h[c];
+// [c][3:v]overlay=w:h\" %s";
+ var command = "ffmpeg -y -i %s -i %s -filter_complex hstack %s" //hstack:水平拼接,默认
+ if (direction == Direction.LAYOUT_VERTICAL) { //vstack:垂直拼接
+ command = command.replace("hstack", "vstack")
+ }
+ command = String.format(command, input1, input2, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频反序倒播
+ *
+ * @param inputFile 输入文件
+ * @param targetFile 反序文件
+ * @return 视频反序的命令行
+ */
+ fun reverseVideo(inputFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -filter_complex [0:v]reverse[v] -map [v] %s" //单纯视频反序
+ command = String.format(command, inputFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频反序倒播
+ *
+ * @param inputFile 输入文件
+ * @param targetFile 反序文件
+ * @return 视频反序的命令行
+ */
+ fun reverseAudioVideo(inputFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -filter_complex [0:v]reverse[v];[0:a]reverse[a] -map [v] -map [a] %s"
+ command = String.format(command, inputFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频降噪
+ *
+ * @param inputFile 输入文件
+ * @param targetFile 输出文件
+ * @return 视频降噪的命令行
+ */
+ fun denoiseVideo(inputFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -nr 500 %s"
+ command = String.format(command, inputFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频叠加成画中画
+ *
+ * @param inputFile1 输入文件
+ * @param inputFile2 输入文件
+ * @param targetFile 输出文件
+ * @param x 小视频起点x坐标
+ * @param y 小视频起点y坐标
+ * @return 视频画中画的命令行
+ */
+ @SuppressLint("DefaultLocale")
+ fun picInPicVideo(inputFile1: String?, inputFile2: String?, x: Int, y: Int,
+ targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -i %s -filter_complex overlay=%d:%d %s"
+ command = String.format(command, inputFile1, inputFile2, x, y, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频缩小一倍
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 视频缩小一倍 命令行
+ */
+ fun videoDoubleDown(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -vf scale=iw/2:-1 %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频放大一倍
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 视频放大一倍命令行
+ */
+ @Deprecated("")
+ fun videoDouble(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -vf scale=iw*2:-1 %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频放大一倍
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 视频放大一倍命令行
+ */
+ fun videoDoubleUp(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -vf scale=iw*2:-1 %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @param height 缩放后高度
+ * @return 视频缩放命令行
+ */
+ fun videoScale(srcFile: String?, targetFile: String?, width: Int, height: Int): Array {
+ return scale(srcFile, targetFile, width, height)
+ }
+
+ /**
+ * 视频缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @return 视频缩放命令行
+ */
+ fun videoScale(srcFile: String?, targetFile: String?, width: Int): Array {
+ return scale(srcFile, targetFile, width)
+ }
+
+ /**
+ * 图片缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @param height 缩放后高度
+ * @return 视频缩放命令行
+ */
+ fun imageScale(srcFile: String?, targetFile: String?, width: Int, height: Int): Array {
+ return scale(srcFile, targetFile, width, height)
+ }
+
+ /**
+ * 图片缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @return 视频缩放命令行
+ */
+ fun imageScale(srcFile: String?, targetFile: String?, width: Int): Array {
+ return scale(srcFile, targetFile, width)
+ }
+
+ /**
+ * 缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @param height 缩放后高度
+ * @return 缩放命令行
+ */
+ fun scale(srcFile: String?, targetFile: String?, width: Int, height: Int): Array {
+ var command = "ffmpeg -y -i %s -vf scale=%d:%d %s"
+ command = String.format(Locale.CHINA, command, srcFile, width, height, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 按宽度等比例缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @return 缩放命令行
+ */
+ fun scale(srcFile: String?, targetFile: String?, width: Int): Array {
+ var command = "ffmpeg -y -i %s -vf scale=%d:-1 %s"
+ command = String.format(Locale.CHINA, command, srcFile, width, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 倍速播放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 倍速播放命令行
+ */
+ fun videoSpeed2(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -filter_complex [0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a] -map [v] -map [a] %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 解码成YUV原始数据
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 视频解码命令行
+ */
+ fun decode2YUV(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+ /**
+ * YUV 编码 H264
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 输出文件宽
+ * @param height 输出文件高
+ * @return YUV 转 H264命令行
+ */
+ /**
+ * YUV 转 H264
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return YUV 转 H264命令行
+ */
+ @JvmOverloads
+ fun yuv2H264(srcFile: String?, targetFile: String?, width: Int = 720, height: Int = 1280): Array {
+ var command = "ffmpeg -y -f rawvideo -pix_fmt yuv420p -s #wx#h -r 30 -i %s -c:v libx264 -f rawvideo %s"
+ command = String.format(Locale.CHINA, command, srcFile, targetFile)
+ command = command.replace("#wx#h", width.toString() + "x" + height)
+ return command.split(" ").toTypedArray()
+ }
+ /**
+ * 音频淡入
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param start 开始位置(s)
+ * @param duration 持续时间(s)
+ * @return 音频淡入命令行
+ */
+ /**
+ * 音频前5s淡入
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 音频前5s淡入命令行
+ */
+ @JvmOverloads
+ fun audioFadeIn(srcFile: String?, targetFile: String?, start: Int = 0, duration: Int = 5): Array {
+ var command = "ffmpeg -y -i %s -filter_complex afade=t=in:ss=%d:d=%d %s"
+ command = String.format(Locale.CHINA, command, srcFile, start, duration, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 音频淡出
+ * @param srcFile 音频源文件
+ * @param targetFile 输出文件
+ * @param start 开始位置(s)
+ * @param duration 持续时间(s)
+ * @return 音频淡出命令行
+ */
+ fun audioFadeOut(srcFile: String?, targetFile: String?, start: Int, duration: Int): Array {
+ var command = "ffmpeg -y -i %s -filter_complex afade=t=out:st=%d:d=%d %s"
+ command = String.format(Locale.CHINA, command, srcFile, start, duration, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频亮度
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param bright 亮度(-1.0 ~ 1.0), 默认0
+ * @return 视频亮度命令行
+ */
+ fun videoBright(srcFile: String?, targetFile: String?, bright: Float): Array {
+ var command = "ffmpeg -y -i %s -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=brightness=%f -f mp4 %s"
+ command = String.format(Locale.CHINA, command, srcFile, bright, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频对比度
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param contrast 对比度(-2.0 ~ 2.0), 默认1.0
+ * @return
+ */
+ fun videoContrast(srcFile: String?, targetFile: String?, contrast: Float): Array {
+ var command = "ffmpeg -y -i %s -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=contrast=%f -f mp4 %s"
+ command = String.format(Locale.CHINA, command, srcFile, contrast, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频旋转
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param transpose
+ * @return 视频旋转命令行
+ */
+ fun videoRotation(srcFile: String?, targetFile: String?, @Transpose transpose: Int): Array {
+ var command = "ffmpeg -y -i %s -vf transpose=%d -b:v 600k %s"
+ command = String.format(Locale.CHINA, command, srcFile, transpose, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 从视频中获取一帧输出图片
+ * @param srcFile 源文件
+ * @param targetFile 目标文件(png 或 jpg)
+ * @param time 一帧的时间:hh:mm:ss.xxx
+ * @return 从视频中获取一帧输出图片命令行
+ */
+ fun frame2Image(srcFile: String?, targetFile: String?, time: String?): Array {
+ var command = "ffmpeg -y -i %s -ss %s -vframes 1 %s"
+ command = String.format(command, srcFile, time, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将音频进行fdk_aac编码
+ * @param srcFile 音频源文件
+ * @param targetFile 音频输出文件(m4a或aac)
+ * @return 将音频进行fdk_aac编码命令
+ */
+ fun audio2Fdkaac(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -c:a libfdk_aac %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将音频进行VBR MP3编码
+ * @param srcFile 音频源文件
+ * @param targetFile 音频输出文件(mp3)
+ * @return 将音频进行VBR MP3编码命令
+ */
+ fun audio2Mp3lame(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -c:a libmp3lame %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将格式视频进行切片,形成m3u8的视频流(m3u8格式一般用于直播或者点播)
+ * @param srcFile 视频路径
+ * @param targetFile 目标路径(以xxx.m3u8为输出)
+ * @param splitTime 切割时间 (单位:秒)
+ * @return 返回以target文件名开头的ts系列文件 如:out0.ts out1.ts ...
+ *
+ * [com.coder.ffmpeg.utils.FFmpegUtils.video2HLS]
+ */
+ @Deprecated("")
+ fun videoHLS(srcFile: String?, targetFile: String?, splitTime: Int): Array {
+ var command = "ffmpeg -y -i %s -c copy -bsf:v h264_mp4toannexb -hls_time %s %s"
+ command = String.format(command, srcFile, splitTime, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将格式视频进行切片,形成m3u8的视频流(m3u8格式一般用于直播或者点播)
+ * @param srcFile 视频路径
+ * @param targetFile 目标路径(以xxx.m3u8为输出)
+ * @param splitTime 切割时间 (单位:秒)
+ * @return 返回以target文件名开头的ts系列文件 如:out0.ts out1.ts ...
+ */
+ fun video2HLS(srcFile: String?, targetFile: String?, splitTime: Int): Array {
+ var command = "ffmpeg -y -i %s -c copy -bsf:v h264_mp4toannexb -hls_time %s %s"
+ command = String.format(command, srcFile, splitTime, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将ts视频流合成视频
+ * @param m3u8Index xx.m3u8视频索引
+ * @param targetFile 目标路径
+ * @return 返回合成视频
+ */
+ fun hls2Video(m3u8Index: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -c copy %s"
+ command = String.format(command, m3u8Index, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将音频转为amr格式
+ * @param srcFile 音频源文件
+ * @param targetFile 目标文件
+ * @return amr格式音频
+ */
+ fun audio2Amr(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -c:a libopencore_amrnb -ar 8000 -ac 1 %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 生成静音音频
+ * @param targetFile 目标文件
+ * @return 静音音频
+ */
+ fun makeMuteAudio(targetFile: String?): Array {
+ var command = "ffmpeg -y -f lavfi -t 10 -i anullsrc %s"
+ command = String.format(command, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/main/jniLibs/arm64-v8a/libffmpeg-invoke.so b/ffmpeg-mini/src/main/jniLibs/arm64-v8a/libffmpeg-invoke.so
index 504190c..8c5db69 100755
Binary files a/ffmpeg-mini/src/main/jniLibs/arm64-v8a/libffmpeg-invoke.so and b/ffmpeg-mini/src/main/jniLibs/arm64-v8a/libffmpeg-invoke.so differ
diff --git a/ffmpeg-mini/src/main/jniLibs/armeabi-v7a/libffmpeg-invoke.so b/ffmpeg-mini/src/main/jniLibs/armeabi-v7a/libffmpeg-invoke.so
index c68c657..d0b23e6 100755
Binary files a/ffmpeg-mini/src/main/jniLibs/armeabi-v7a/libffmpeg-invoke.so and b/ffmpeg-mini/src/main/jniLibs/armeabi-v7a/libffmpeg-invoke.so differ
diff --git a/ffmpeg-mini/src/test/java/com/coder/ffmpeg/ExampleUnitTest.java b/ffmpeg-mini/src/test/java/com/coder/ffmpeg/ExampleUnitTest.java
deleted file mode 100644
index 6d16f66..0000000
--- a/ffmpeg-mini/src/test/java/com/coder/ffmpeg/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.coder.ffmpeg;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/ffmpeg-mini/src/test/java/com/coder/ffmpeg/ExampleUnitTest.kt b/ffmpeg-mini/src/test/java/com/coder/ffmpeg/ExampleUnitTest.kt
new file mode 100644
index 0000000..8d39c16
--- /dev/null
+++ b/ffmpeg-mini/src/test/java/com/coder/ffmpeg/ExampleUnitTest.kt
@@ -0,0 +1,16 @@
+package com.coder.ffmpeg
+
+import org.junit.Assert
+import org.junit.Test
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see [Testing documentation](http://d.android.com/tools/testing)
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ Assert.assertEquals(4, 2 + 2.toLong())
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/bintray.gradle b/ffmpeg/bintray.gradle
index 572cd2f..710ea32 100644
--- a/ffmpeg/bintray.gradle
+++ b/ffmpeg/bintray.gradle
@@ -1,7 +1,7 @@
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'
-version = "1.1.8" //版本号,以后每次更新library都得更改
+version = "1.2.0-beta1" //版本号,以后每次更新library都得更改
def siteUrl = 'https://github.com/AnJoiner/FFmpegCommand' //Homepage URL of the library
def gitUrl = 'https://github.com/AnJoiner/FFmpegCommand.git' //Git repository url
def issueUrl = 'https://github.com/AnJoiner/FFmpegCommand/issues' //issue url of the library
diff --git a/ffmpeg/build.gradle b/ffmpeg/build.gradle
index f673c64..c7f729f 100644
--- a/ffmpeg/build.gradle
+++ b/ffmpeg/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
android {
compileSdkVersion 29
@@ -32,8 +33,10 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
- compile "io.reactivex.rxjava2:rxjava:2.2.16"
- compile 'io.reactivex.rxjava2:rxandroid:2.1.1'
-
apply from: 'bintray.gradle'
+ implementation "androidx.core:core-ktx:+"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
+repositories {
+ mavenCentral()
}
diff --git a/ffmpeg/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.java b/ffmpeg/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.java
deleted file mode 100644
index bfc9401..0000000
--- a/ffmpeg/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.coder.ffmpeg;
-
-import android.content.Context;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- assertEquals("com.coder.ffmpeg.test", appContext.getPackageName());
- }
-}
diff --git a/ffmpeg/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.kt b/ffmpeg/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..714933b
--- /dev/null
+++ b/ffmpeg/src/androidTest/java/com/coder/ffmpeg/ExampleInstrumentedTest.kt
@@ -0,0 +1,22 @@
+package com.coder.ffmpeg
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see [Testing documentation](http://d.android.com/tools/testing)
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ Assert.assertEquals("com.coder.ffmpeg.test", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Attribute.java b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Attribute.java
deleted file mode 100644
index ed9f4fe..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Attribute.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.IntDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 20-4-8
- */
-@IntDef({
- Attribute.DURATION,
- Attribute.WIDTH,
- Attribute.HEIGHT,
- Attribute.VIDEO_BIT_RATE,
- Attribute.FPS,
- Attribute.CHANNELS,
- Attribute.SAMPLE_RATE,
- Attribute.AUDIO_BIT_RATE
-})
-public @interface Attribute {
- int DURATION = 0; // 时长
- int WIDTH = 1; //视频宽(分辨率px)
- int HEIGHT = 2; // 视频高(分辨率px)
- int VIDEO_BIT_RATE = 3; //视频比特率
- int FPS = 4; //帧率
- int CHANNELS = 5; //音频声道数
- int SAMPLE_RATE = 6; //音频采样率
- int AUDIO_BIT_RATE = 7; //音频比特率
-}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Attribute.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Attribute.kt
new file mode 100644
index 0000000..960e2b1
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Attribute.kt
@@ -0,0 +1,22 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 20-4-8
+ */
+@IntDef(Attribute.DURATION, Attribute.WIDTH, Attribute.HEIGHT, Attribute.VIDEO_BIT_RATE, Attribute.FPS, Attribute.CHANNELS, Attribute.SAMPLE_RATE, Attribute.AUDIO_BIT_RATE)
+@Deprecated("")
+annotation class Attribute {
+ companion object {
+ const val DURATION = 0 // 时长
+ const val WIDTH = 1 //视频宽(分辨率px)
+ const val HEIGHT = 2 // 视频高(分辨率px)
+ const val VIDEO_BIT_RATE = 3 //视频比特率
+ const val FPS = 4 //帧率
+ const val CHANNELS = 5 //音频声道数
+ const val SAMPLE_RATE = 6 //音频采样率
+ const val AUDIO_BIT_RATE = 7 //音频比特率
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.java b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.java
deleted file mode 100644
index e65d2ab..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.IntDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 20-9-9
- */
-@IntDef({
- CodecAttribute.ENCODE,
- CodecAttribute.DECODE,
- CodecAttribute.ENCODE_AUDIO,
- CodecAttribute.DECODE_AUDIO,
- CodecAttribute.ENCODE_VIDEO,
- CodecAttribute.DECODE_VIDEO,
- CodecAttribute.ENCODE_OTHER,
- CodecAttribute.DECODE_OTHER
-})
-public @interface CodecAttribute {
- int ENCODE = 1; // 编码格式
- int DECODE = 2; // 解码格式
- int ENCODE_AUDIO = 3; // 音频编码格式
- int DECODE_AUDIO = 4; // 音频解码格式
- int ENCODE_VIDEO = 5; // 视频编码格式
- int DECODE_VIDEO = 6; // 视频解码格式
- int ENCODE_OTHER = 7; // 其他编码格式
- int DECODE_OTHER = 8; // 其他解码格式
-}
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.kt
new file mode 100644
index 0000000..c01b918
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/CodecAttribute.kt
@@ -0,0 +1,28 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 20-9-9
+ */
+@IntDef(CodecAttribute.ENCODE,
+ CodecAttribute.DECODE,
+ CodecAttribute.ENCODE_AUDIO,
+ CodecAttribute.DECODE_AUDIO,
+ CodecAttribute.ENCODE_VIDEO,
+ CodecAttribute.DECODE_VIDEO,
+ CodecAttribute.ENCODE_OTHER,
+ CodecAttribute.DECODE_OTHER)
+annotation class CodecAttribute {
+ companion object {
+ const val ENCODE = 1 // 编码格式
+ const val DECODE = 2 // 解码格式
+ const val ENCODE_AUDIO = 3 // 音频编码格式
+ const val DECODE_AUDIO = 4 // 音频解码格式
+ const val ENCODE_VIDEO = 5 // 视频编码格式
+ const val DECODE_VIDEO = 6 // 视频解码格式
+ const val ENCODE_OTHER = 7 // 其他编码格式
+ const val DECODE_OTHER = 8 // 其他解码格式
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Direction.java b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Direction.java
deleted file mode 100644
index 2b5b536..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Direction.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.IntDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-19
- */
-@IntDef({
- Direction.LAYOUT_HORIZONTAL,
- Direction.LAYOUT_VERTICAL,
-})
-public @interface Direction {
- int LAYOUT_HORIZONTAL = 1;
- int LAYOUT_VERTICAL = 2;
-}
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Direction.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Direction.kt
new file mode 100644
index 0000000..293d7cc
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Direction.kt
@@ -0,0 +1,16 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-19
+ */
+@IntDef(Direction.LAYOUT_HORIZONTAL,
+ Direction.LAYOUT_VERTICAL)
+annotation class Direction {
+ companion object {
+ const val LAYOUT_HORIZONTAL = 1
+ const val LAYOUT_VERTICAL = 2
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.java b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.java
deleted file mode 100644
index 08b3b20..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.IntDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 20-9-9
- */
-@IntDef({
- FormatAttribute.INPUT_FORMAT,
- FormatAttribute.OUTPUT_FORMAT
-})
-public @interface FormatAttribute {
- int INPUT_FORMAT = 1; // 输入格式
- int OUTPUT_FORMAT = 2; // 输出格式
-}
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.kt
new file mode 100644
index 0000000..3aa504d
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/FormatAttribute.kt
@@ -0,0 +1,16 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 20-9-9
+ */
+@IntDef(FormatAttribute.INPUT_FORMAT,
+ FormatAttribute.OUTPUT_FORMAT)
+annotation class FormatAttribute {
+ companion object {
+ const val INPUT_FORMAT = 1 // 输入格式
+ const val OUTPUT_FORMAT = 2 // 输出格式
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.java b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.java
deleted file mode 100644
index 95fe587..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.StringDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-22
- */
-@StringDef({
- ImageFormat.PNG,
- ImageFormat.JPG,
-})
-public @interface ImageFormat {
- String PNG = "png";
- String JPG = "jpg";
-}
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.kt
new file mode 100644
index 0000000..e4d8034
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/ImageFormat.kt
@@ -0,0 +1,16 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.StringDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-22
+ */
+@StringDef(ImageFormat.PNG,
+ ImageFormat.JPG)
+annotation class ImageFormat {
+ companion object {
+ const val PNG = "png"
+ const val JPG = "jpg"
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/MediaAttribute.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/MediaAttribute.kt
new file mode 100644
index 0000000..cd36e0a
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/MediaAttribute.kt
@@ -0,0 +1,29 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+import com.coder.ffmpeg.annotation.MediaAttribute
+
+/**
+ * @author: AnJoiner
+ * @datetime: 20-11-22
+ */
+@IntDef(MediaAttribute.DURATION,
+ MediaAttribute.WIDTH,
+ MediaAttribute.HEIGHT,
+ MediaAttribute.VIDEO_BIT_RATE,
+ MediaAttribute.FPS,
+ MediaAttribute.CHANNELS,
+ MediaAttribute.SAMPLE_RATE,
+ MediaAttribute.AUDIO_BIT_RATE)
+annotation class MediaAttribute {
+ companion object {
+ const val DURATION = 0 // 时长
+ const val WIDTH = 1 //视频宽(分辨率px)
+ const val HEIGHT = 2 // 视频高(分辨率px)
+ const val VIDEO_BIT_RATE = 3 //视频比特率
+ const val FPS = 4
+ const val CHANNELS = 5 //音频声道数
+ const val SAMPLE_RATE = 6 //音频采样率
+ const val AUDIO_BIT_RATE = 7 //音频比特率
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Transpose.java b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Transpose.java
deleted file mode 100644
index ada3536..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Transpose.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.coder.ffmpeg.annotation;
-
-import androidx.annotation.IntDef;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-30
- */
-@IntDef({
- Transpose.CLOCKWISE_ROTATION_90,
- Transpose.ANTICLOCKWISE_ROTATION_90,
- Transpose.CLOCKWISE_ROTATION_90_FLIP,
- Transpose.ANTICLOCKWISE_ROTATION_90_FLIP
-})
-public @interface Transpose { ;
- //顺时针旋转画面90度
- int CLOCKWISE_ROTATION_90 = 1;
- // 逆时针旋转画面90度
- int ANTICLOCKWISE_ROTATION_90 = 2;
- //顺时针旋转画面90度再水平翻转
- int CLOCKWISE_ROTATION_90_FLIP = 3;
- // 逆时针旋转画面90度水平翻转
- int ANTICLOCKWISE_ROTATION_90_FLIP = 0;
-}
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Transpose.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Transpose.kt
new file mode 100644
index 0000000..e25633f
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/annotation/Transpose.kt
@@ -0,0 +1,27 @@
+package com.coder.ffmpeg.annotation
+
+import androidx.annotation.IntDef
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-30
+ */
+@IntDef(Transpose.CLOCKWISE_ROTATION_90,
+ Transpose.ANTICLOCKWISE_ROTATION_90,
+ Transpose.CLOCKWISE_ROTATION_90_FLIP,
+ Transpose.ANTICLOCKWISE_ROTATION_90_FLIP)
+annotation class Transpose {
+ companion object {
+ //顺时针旋转画面90度
+ const val CLOCKWISE_ROTATION_90 = 1
+
+ // 逆时针旋转画面90度
+ const val ANTICLOCKWISE_ROTATION_90 = 2
+
+ //顺时针旋转画面90度再水平翻转
+ const val CLOCKWISE_ROTATION_90_FLIP = 3
+
+ // 逆时针旋转画面90度水平翻转
+ const val ANTICLOCKWISE_ROTATION_90_FLIP = 0
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/call/CommonCallBack.java b/ffmpeg/src/main/java/com/coder/ffmpeg/call/CommonCallBack.java
deleted file mode 100644
index 40682e7..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/call/CommonCallBack.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.coder.ffmpeg.call;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-17
- */
-public abstract class CommonCallBack implements IFFmpegCallBack {
-
- @Override
- public void onError(Throwable t) {
- t.printStackTrace();
- }
-
- @Override
- public void onStart() {
-
- }
-
- @Override
- public void onProgress(int progress) {
-
- }
-
- @Override
- public void onCancel() {
-
- }
-}
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/call/CommonCallBack.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/call/CommonCallBack.kt
new file mode 100644
index 0000000..1cfb5f3
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/call/CommonCallBack.kt
@@ -0,0 +1,13 @@
+package com.coder.ffmpeg.call
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-17
+ */
+open class CommonCallBack : IFFmpegCallBack {
+ override fun onStart() {}
+ override fun onProgress(progress: Int, pts: Long) {}
+ override fun onCancel() {}
+ override fun onComplete() {}
+ override fun onError(errorCode: Int, errorMsg: String?) {}
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/call/ICallBack.java b/ffmpeg/src/main/java/com/coder/ffmpeg/call/ICallBack.java
deleted file mode 100644
index 0d0f475..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/call/ICallBack.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.coder.ffmpeg.call;
-
-public interface ICallBack {
- void onError(Throwable t);
-
- /**
- * 进度回调
- * @param progress
- */
- void onProgress(int progress);
-
- void onComplete();
-
- void onStart();
-
-}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.java b/ffmpeg/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.java
deleted file mode 100644
index 3d6e6fd..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.coder.ffmpeg.call;
-
-/**
- * @author: AnJoiner
- * @datetime: 20-4-22
- */
-public interface IFFmpegCallBack extends ICallBack {
-
- /**
- * 取消回调
- */
- void onCancel();
-}
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.kt
new file mode 100644
index 0000000..64f502c
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/call/IFFmpegCallBack.kt
@@ -0,0 +1,34 @@
+package com.coder.ffmpeg.call
+
+interface IFFmpegCallBack {
+ /**
+ * 开始回调
+ */
+ fun onStart()
+
+ /**
+ * 进度回调
+ *
+ * @param progress 当前执行进度
+ * @param pts 当前执行长度
+ */
+ fun onProgress(progress: Int, pts: Long)
+
+ /**
+ * 取消回调
+ */
+ fun onCancel()
+
+ /**
+ * 完成回调
+ */
+ fun onComplete()
+
+ /**
+ * 错误回调
+ *
+ * @param errorCode 错误码
+ * @param errorMsg 错误内容
+ */
+ fun onError(errorCode: Int, errorMsg: String?)
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.java b/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.java
deleted file mode 100644
index b56c5f1..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.coder.ffmpeg.jni;
-
-import android.util.Log;
-
-import com.coder.ffmpeg.annotation.Attribute;
-import com.coder.ffmpeg.annotation.CodecAttribute;
-import com.coder.ffmpeg.annotation.FormatAttribute;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-17
- */
-class FFmpegCmd {
-
- @Deprecated
- private FFmpegCommand.OnFFmpegProgressListener mCmdListener;
-
- private FFmpegCommand.OnFFmpegCommandListener mCommandListener;
-
- static boolean DEBUG = true;
-
-
- static {
- System.loadLibrary("avdevice");
- System.loadLibrary("avutil");
- System.loadLibrary("avcodec");
- System.loadLibrary("swresample");
- System.loadLibrary("avformat");
- System.loadLibrary("swscale");
- System.loadLibrary("avfilter");
- System.loadLibrary("postproc");
- System.loadLibrary("ffmpeg-invoke");
- }
-
- private native int runSync(int cmdLen,String[] cmd);
-
- private native int runAsync(int cmdLen,String[] cmd);
-
- int runCmdSync(String[] cmd) {
- cmd = command(cmd);
- return runSync(cmd.length, cmd);
- }
-
- @Deprecated
- int runCmdSync(String[] cmd, FFmpegCommand.OnFFmpegProgressListener cmdListener) {
- cmd = command(cmd);
- mCmdListener = cmdListener;
- int result = runSync(cmd.length, cmd);
- if (mCmdListener != null) {
- mCmdListener = null;
- }
- return result;
- }
-
- int runCmdSync(String[] cmd, FFmpegCommand.OnFFmpegCommandListener cmdListener) {
- cmd = command(cmd);
- mCommandListener = cmdListener;
-
- int result = runSync(cmd.length, cmd);
- if (mCommandListener != null) {
- mCommandListener = null;
- }
- return result;
- }
-
- int getInfo(String videoPath, @Attribute int type) {
- return info(videoPath, type);
- }
-
- private native int info(String videoPath, int type);
-
- public String getFormatInfo(@FormatAttribute int format){
- return formatInfo(format);
- }
-
- private native String formatInfo(int format);
-
- public String getCodecInfo(@CodecAttribute int codec){
- return codecInfo(codec);
- }
-
- private native String codecInfo(int codec);
-
- private String[] command(String[] cmd) {
- String[] cmds = new String[cmd.length + 1];
- for (int i = 0; i < cmds.length; i++) {
- if (i < 1) {
- cmds[i] = cmd[i];
- } else if (i == 1) {
- cmds[i] = "-d";
- } else {
- cmds[i] = cmd[i - 1];
- }
- }
- return DEBUG ? cmds : cmd;
- }
-
- native int getProgress();
-
- @Deprecated
- native void exit();
-
- native void cancel();
-
-
- void onStart(){
-
- }
-
- void onProgress(int progress) {
- if (mCmdListener != null) {
- mCmdListener.onProgress(progress);
- }
- if (mCommandListener != null) {
- mCommandListener.onProgress(progress);
- }
- }
-
- void onCancel() {
- if (mCommandListener != null) {
- mCommandListener.onCancel();
- }
- // 移除当前对象,释放内存
- FFmpegCommand.cmds.remove(this);
- }
-
- void onComplete(){
- if (mCommandListener != null) {
- mCommandListener.onComplete();
- }
- // 移除当前对象,释放内存
- FFmpegCommand.cmds.remove(this);
- }
-
- void onError(int errorCode, String errorMsg){
- Log.e("CmdError","errorCode:"+errorCode+", errorMsg:"+errorMsg);
- // 移除当前对象,释放内存
- FFmpegCommand.cmds.remove(this);
- }
-}
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.kt
new file mode 100644
index 0000000..57af771
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCmd.kt
@@ -0,0 +1,223 @@
+package com.coder.ffmpeg.jni
+
+import com.coder.ffmpeg.annotation.CodecAttribute
+import com.coder.ffmpeg.annotation.FormatAttribute
+import com.coder.ffmpeg.annotation.MediaAttribute
+import com.coder.ffmpeg.call.IFFmpegCallBack
+import java.util.*
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-17
+ */
+internal class FFmpegCmd private constructor() {
+ // Program execution callback
+ private val mCallBacks = Collections.synchronizedList(ArrayList())
+ // debugging mode
+ private var ffdebug = true
+
+ companion object {
+ var instance: FFmpegCmd? = null
+ get() {
+ if (field == null) {
+ field = FFmpegCmd()
+ }
+ return field
+ }
+ private set
+
+ init {
+ System.loadLibrary("avdevice")
+ System.loadLibrary("avutil")
+ System.loadLibrary("avcodec")
+ System.loadLibrary("swresample")
+ System.loadLibrary("avformat")
+ System.loadLibrary("swscale")
+ System.loadLibrary("avfilter")
+ System.loadLibrary("postproc")
+ System.loadLibrary("ffmpeg-invoke")
+ }
+ }
+
+
+ /**
+ * Whether to enable debugging mode
+ * @param debug true or false
+ */
+ fun setDebug(debug: Boolean) {
+ this.ffdebug = debug
+ }
+
+ /**
+ * Provide ffmpeg command method to execute
+ * @param command ffmeng command
+ * @return execute status
+ */
+ fun runCmd(command: Array): Int {
+ var cmd = command
+ cmd = buildCommand(cmd)
+ return run(getCommand(cmd))
+ }
+
+ /**
+ * Provide ffmpeg command method to execute
+ * @param command ffmeng command
+ * @param callBack callback result
+ * @return execute status
+ */
+ fun runCmd(command: Array, callBack: IFFmpegCallBack?): Int {
+ var cmd = command
+ mCallBacks.add(callBack)
+ cmd = buildCommand(cmd)
+ return run(getCommand(cmd))
+ }
+
+ /**
+ * Return the complete command
+ * @param cmd ffmeng command
+ * @return ffmeng command
+ */
+ private fun getCommand(cmd: Array): String {
+ val stringBuffer = StringBuilder()
+ for (i in cmd.indices) {
+ stringBuffer.append(cmd[i])
+ if (i < cmd.size - 1) {
+ stringBuffer.append(" ")
+ }
+ }
+ return stringBuffer.toString()
+ }
+
+ /**
+ * Re-survive command according to whether it is debug mode
+ * @param cmd ffmeng command
+ * @return ffmeng command
+ */
+ private fun buildCommand(cmd: Array): Array {
+ val cmds = arrayOfNulls(cmd.size + 1)
+ for (i in cmds.indices) {
+ when {
+ i < 1 -> {
+ cmds[i] = cmd[i]
+ }
+ i == 1 -> {
+ cmds[i] = "-d"
+ }
+ else -> {
+ cmds[i] = cmd[i - 1]
+ }
+ }
+ }
+ return if (ffdebug) cmds else cmd
+ }
+
+ /**
+ * Execute ffmpeg command method
+ * @param command ffmeng command
+ * @return execute status
+ */
+ private external fun run(command: String?): Int
+
+ /**
+ * Get media information
+ * @param videoPath media path
+ * @param type information type
+ * @return media information
+ */
+ fun getMediaInfo(videoPath: String?, @MediaAttribute type: Int): Int? {
+ return info(videoPath, type)
+ }
+
+ /**
+ * Call native to get media information.
+ * @param videoPath media path
+ * @param type information type.
+ */
+ private external fun info(videoPath: String?, type: Int): Int
+
+ /**
+ * Provide method to get format info .
+ * @param format format type.
+ */
+ fun getFormatInfo(@FormatAttribute format: Int): String {
+ return formatInfo(format)
+ }
+
+ /**
+ * Call native to get support format.
+ * @param format format type.
+ *
+ */
+ private external fun formatInfo(format: Int): String
+
+ /**
+ * Provide method to get Codec info .
+ * @param codec codec type.
+ */
+ fun getCodecInfo(@CodecAttribute codec: Int): String {
+ return codecInfo(codec)
+ }
+ /**
+ * Call native to get support codec.
+ * @param codec codec type.
+ *
+ */
+ private external fun codecInfo(codec: Int): String
+
+ @Deprecated("")
+ external fun exit()
+
+ external fun cancel()
+
+ /**
+ * Provide the callback of start execute commands.
+ */
+ fun onStart() {
+ for (callBack in mCallBacks) {
+ callBack.onStart()
+ }
+ }
+
+ /**
+ * Provide the callback of progress
+ * @param progress progress for ffmpeg
+ * @param pts duration of current ffmepg command execution
+ */
+ fun onProgress(progress: Int, pts: Long) {
+ for (callBack in mCallBacks) {
+ callBack.onProgress(progress, pts)
+ }
+ }
+
+ /**
+ * Provide the callback of cancel execute commands.
+ */
+ fun onCancel() {
+ for (callBack in mCallBacks) {
+ callBack.onCancel()
+ mCallBacks.remove(callBack)
+ }
+ }
+
+ /**
+ * Provide the callback of execute commands is completed.
+ */
+ fun onComplete() {
+ for (callBack in mCallBacks) {
+ callBack.onComplete()
+ mCallBacks.remove(callBack)
+ }
+ }
+
+ /**
+ * Provide the callback of error execute commands.
+ * @param errorCode error code of execute commands.
+ * @param errorMsg error message of execute commands.
+ */
+ fun onError(errorCode: Int, errorMsg: String?) {
+ for (callBack in mCallBacks) {
+ callBack.onError(errorCode, errorMsg)
+ mCallBacks.remove(callBack)
+ }
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.java b/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.java
deleted file mode 100644
index aafe8f7..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.java
+++ /dev/null
@@ -1,524 +0,0 @@
-package com.coder.ffmpeg.jni;
-
-import android.annotation.SuppressLint;
-import android.os.Handler;
-import android.os.Message;
-
-import com.coder.ffmpeg.annotation.Attribute;
-import com.coder.ffmpeg.annotation.CodecAttribute;
-import com.coder.ffmpeg.annotation.FormatAttribute;
-import com.coder.ffmpeg.call.ICallBack;
-import com.coder.ffmpeg.call.IFFmpegCallBack;
-
-import org.reactivestreams.Subscriber;
-import org.reactivestreams.Subscription;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import io.reactivex.BackpressureStrategy;
-import io.reactivex.Flowable;
-import io.reactivex.FlowableEmitter;
-import io.reactivex.FlowableOnSubscribe;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.schedulers.Schedulers;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-17
- */
-public class FFmpegCommand {
-
-
- static List cmds;
- static List listeners;
- static int globalProgress = 0;
-
- static {
- cmds = new ArrayList<>();
- listeners = new ArrayList<>();
- }
-
- /**
- * 切换到主线程并返回进度
- */
- @SuppressLint("HandlerLeak")
- private static Handler mHandler = new Handler(){
- @Override
- public void handleMessage(@NonNull Message msg) {
- super.handleMessage(msg);
- if (msg.what == 1){
- IFFmpegCallBack callBack = (IFFmpegCallBack) msg.obj;
- if (callBack!=null){
- int progress = msg.arg1;
- int size = msg.arg2;
- globalAsyncCallbackProgress(callBack,progress,size);
- }
- }else if (msg.what == -1){
- IFFmpegCallBack callBack = (IFFmpegCallBack) msg.obj;
- if (callBack!=null){
- callBack.onCancel();
- listeners.clear();
- }
- }else if (msg.what == 0){
- IFFmpegCallBack callBack = (IFFmpegCallBack) msg.obj;
- if (callBack!=null){
- int size = msg.arg2;
- globalAsyncCallbackComplete(callBack,size);
- }
- }
- }
- };
-
- /**
- * 是否开启debug模式
- *
- * @param debug true:开启 false :不开启
- */
- public static void setDebug(boolean debug) {
- FFmpegCmd.DEBUG = debug;
- }
-
- /**
- * 同步执行 获取媒体信息
- *
- * @param path 媒体地址
- * @param type 属性类型 {@link Attribute}
- * @return 媒体信息
- */
- public static int getInfoSync(String path, @Attribute int type) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- return ffmpegCmd.getInfo(path, type);
- }
-
- /**
- * 获取支持解封装格式
- * @param formatType 格式类型 {@link FormatAttribute}
- * @return 格式信息
- */
- public static String getSupportFormat(@FormatAttribute int formatType){
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- return ffmpegCmd.getFormatInfo(formatType);
- }
-
- /**
- * 获取支持编解码
- * @param codecType 编解码类型 {@link CodecAttribute}
- * @return 编解码信息
- */
- public static String getSupportCodec(@CodecAttribute int codecType){
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- return ffmpegCmd.getCodecInfo(codecType);
- }
-
- /**
- * 同步执行 ffmpeg命令
- *
- * @param cmd ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- */
- public static void runSync(final String[] cmd) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- cmds.add(ffmpegCmd);
- ffmpegCmd.runCmdSync(cmd);
- }
-
- /**
- * 同步执行 ffmpeg命令
- *当前方法已被弃用,请参考{@link FFmpegCommand#runSync(String[], OnFFmpegCommandListener)}
- * @param cmd ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param listener {@link OnFFmpegProgressListener}
- */
- @Deprecated
- public static void runSync(final String[] cmd, OnFFmpegProgressListener listener) {
- if (listener != null) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- cmds.add(ffmpegCmd);
- ffmpegCmd.runCmdSync(cmd, listener);
- }
- }
-
- /**
- * 同步执行 ffmpeg命令
- *
- * @param cmd ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param listener {@link OnFFmpegCommandListener}
- */
- public static void runSync(final String[] cmd, OnFFmpegCommandListener listener) {
- if (listener != null) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- cmds.add(ffmpegCmd);
- ffmpegCmd.runCmdSync(cmd, listener);
- }
- }
-
- /**
- * 同步多命令执行 ffmpeg命令
- *
- * @param cmds 命令集合, ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param listener {@link OnFFmpegCommandListener}
- */
- public static void runMoreSync(final List cmds, final OnFFmpegCommandListener listener) {
- if (listener != null) {
- globalProgress = 0;
- for (String[] cmd : cmds) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- FFmpegCommand.cmds.add(ffmpegCmd);
- OnFFmpegCommandListener commandListener = new OnFFmpegCommandListener() {
- @Override
- public void onProgress(int progress) {
- globalSyncCallbackProgress(listener,progress,cmds.size());
- }
-
- @Override
- public void onCancel() {
- listener.onCancel();
- listeners.clear();
- }
-
- @Override
- public void onComplete() {
- globalSyncCallbackComplete(listener,cmds.size());
- }
- };
- listeners.add(commandListener);
- ffmpegCmd.runCmdSync(cmd, commandListener);
- }
- }
- }
-
- /**
- * 全局进度回调处理
- * @param listener {@link OnFFmpegCommandListener}
- * @param progress 进度
- * @param size 命令数量
- */
- private static void globalSyncCallbackProgress(OnFFmpegCommandListener listener, int progress, int size){
- int temp;
- if (progress == 100){
- globalProgress+=progress;
- temp = globalProgress;
- }else {
- temp = globalProgress+progress;
- }
- if (listener!=null && size>0){
- listener.onProgress(temp/size);
- }
- }
-
-
- /**
- * 全局完成回调处理
- * @param listener listener {@link OnFFmpegCommandListener}
- * @param size 命令数量
- */
- private static void globalSyncCallbackComplete(OnFFmpegCommandListener listener,int size){
- if (listener!=null && size>0 && globalProgress/size == 100){
- listener.onComplete();
- listeners.clear();
- }
- }
-
- /**
- * 当前方法已被弃用,请参考{@link FFmpegCommand#runAsync(String[], IFFmpegCallBack)}
- * 异步执行 ffmpeg命令
- *
- * @param cmd ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param callBack {@link ICallBack}
- */
- @Deprecated
- public static void runAsync(final String[] cmd, final ICallBack callBack) {
- Flowable.create(new FlowableOnSubscribe() {
- @Override
- public void subscribe(final FlowableEmitter emitter) throws Exception {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- cmds.add(ffmpegCmd);
- ffmpegCmd.runCmdSync(cmd, new OnFFmpegProgressListener() {
- @Override
- public void onProgress(int progress) {
- if (callBack != null) {
- callBack.onProgress(progress);
- }
- }
- });
- emitter.onComplete();
- }
- }, BackpressureStrategy.BUFFER)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Subscriber() {
- @Override
- public void onSubscribe(Subscription s) {
- if (callBack != null) {
- callBack.onStart();
- }
- }
-
- @Override
- public void onNext(Integer integer) {
- }
-
- @Override
- public void onError(Throwable t) {
- cmds.clear();
- if (callBack != null) {
- callBack.onError(t);
- }
- }
-
- @Override
- public void onComplete() {
- if (callBack != null) {
- callBack.onComplete();
- }
- }
- });
- }
-
-
- /**
- * 异步执行 ffmpeg命令
- *
- * @param cmd ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param callBack {@link IFFmpegCallBack}
- */
- public static void runAsync(final String[] cmd, final IFFmpegCallBack callBack) {
- Flowable.create(new FlowableOnSubscribe() {
- @Override
- public void subscribe(final FlowableEmitter emitter) throws Exception {
- globalProgress = 0;
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- cmds.add(ffmpegCmd);
- OnFFmpegCommandListener commandListener = new OnFFmpegCommandListener() {
- @Override
- public void onProgress(int progress) {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = 1;
- msg.obj = callBack;
- msg.arg1 = progress;
- msg.arg2 = 1;
- mHandler.sendMessage(msg);
- }
- }
- }
-
- @Override
- public void onCancel() {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = -1;
- msg.obj = callBack;
- msg.arg2 = 1;
- mHandler.sendMessage(msg);
- }
- }
- }
-
- @Override
- public void onComplete() {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = 0;
- msg.obj = callBack;
- msg.arg2 = 1;
- mHandler.sendMessage(msg);
- }
- }
- }
- };
- ffmpegCmd.runCmdSync(cmd,commandListener);
- }
- }, BackpressureStrategy.BUFFER)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Subscriber() {
- @Override
- public void onSubscribe(Subscription s) {
- if (callBack != null) {
- callBack.onStart();
- }
- }
-
- @Override
- public void onNext(Boolean isComplete) {
- // 验证是否是完成
- }
-
- @Override
- public void onError(Throwable t) {
- cmds.clear();
- if (callBack != null) {
- callBack.onError(t);
- }
- }
-
- @Override
- public void onComplete() {
- }
- });
- }
-
-
- /**
- * 异步执行 ffmpeg命令
- *
- * @param cmds 命令集合, ffmpeg 命令 {@link com.coder.ffmpeg.utils.FFmpegUtils}
- * @param callBack {@link IFFmpegCallBack}
- */
- public static void runMoreAsync(final List cmds, final IFFmpegCallBack callBack) {
- Flowable.create(new FlowableOnSubscribe() {
- @Override
- public void subscribe(final FlowableEmitter emitter) throws Exception {
- globalProgress = 0;
- for (String[] cmd : cmds) {
- FFmpegCmd ffmpegCmd = new FFmpegCmd();
- FFmpegCommand.cmds.add(ffmpegCmd);
- OnFFmpegCommandListener commandListener = new OnFFmpegCommandListener() {
- @Override
- public void onProgress(int progress) {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = 1;
- msg.obj = callBack;
- msg.arg1 = progress;
- msg.arg2 = cmds.size();
- mHandler.sendMessage(msg);
- }
- }
- }
-
- @Override
- public void onCancel() {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = -1;
- msg.obj = callBack;
- msg.arg2 = cmds.size();
- mHandler.sendMessage(msg);
- }
- }
- }
-
- @Override
- public void onComplete() {
- if (callBack != null) {
- if (mHandler!=null){
- Message msg = new Message();
- msg.what = 0;
- msg.obj = callBack;
- msg.arg2 = cmds.size();
- mHandler.sendMessage(msg);
- }
- }
- }
- };
- FFmpegCommand.listeners.add(commandListener);
- ffmpegCmd.runCmdSync(cmd,commandListener);
-
- }
- }
- }, BackpressureStrategy.BUFFER)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(new Subscriber() {
- @Override
- public void onSubscribe(Subscription s) {
- if (callBack != null) {
- callBack.onStart();
- }
- }
-
- @Override
- public void onNext(Boolean isComplete) {
- // 验证是否是完成
- }
-
- @Override
- public void onError(Throwable t) {
- cmds.clear();
- if (callBack != null) {
- callBack.onError(t);
- }
- }
-
- @Override
- public void onComplete() {
- }
- });
- }
-
- /**
- * 全局进度回调处理
- * @param callBack {@link IFFmpegCallBack}
- * @param progress 进度
- * @param size 命令数量
- */
- private static void globalAsyncCallbackProgress(IFFmpegCallBack callBack, int progress, int size){
- int temp;
- // 多命令进度回调,当进度100时直接进入下一条命令进度中
- if (progress == 100){
- if (globalProgress < size * 100){
- globalProgress+=progress;
- }
- temp = globalProgress;
- }else {
- temp = globalProgress+progress;
- }
- if (callBack!=null && size>0){
- callBack.onProgress(temp/size);
- }
- }
-
-
- /**
- * 全局完成回调处理
- * @param callBack {@link IFFmpegCallBack}
- * @param size 命令数量
- */
- private static void globalAsyncCallbackComplete(IFFmpegCallBack callBack,int size){
- if (callBack!=null && size>0 && globalProgress/size == 100){
- callBack.onComplete();
- listeners.clear();
- }
- }
-
- /**
- * 当前方法已被弃用,请参考{@link FFmpegCommand#cancel()}}
- * 退出执行
- */
- @Deprecated
- public static void exit(){
- for (FFmpegCmd cmd : cmds) {
- cmd.exit();
- }
- }
-
- /**
- * 退出执行
- */
- public static void cancel(){
- for (FFmpegCmd cmd : cmds) {
- cmd.cancel();
- }
- }
-
- @Deprecated
- public interface OnFFmpegProgressListener {
- void onProgress(int progress);
- }
-
- public interface OnFFmpegCommandListener {
-
- void onProgress(int progress);
-
- void onCancel();
-
- void onComplete();
- }
-}
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.kt
new file mode 100644
index 0000000..eee9850
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/jni/FFmpegCommand.kt
@@ -0,0 +1,77 @@
+package com.coder.ffmpeg.jni
+
+import com.coder.ffmpeg.annotation.CodecAttribute
+import com.coder.ffmpeg.annotation.FormatAttribute
+import com.coder.ffmpeg.annotation.MediaAttribute
+import com.coder.ffmpeg.call.IFFmpegCallBack
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-17
+ */
+object FFmpegCommand {
+ /**
+ * Whether to enable debug mode
+ *
+ * @param debug true:enable false :not enable
+ */
+ fun setDebug(debug: Boolean) {
+ FFmpegCmd.instance?.setDebug(debug)
+ }
+
+ /**
+ * Get media information
+ *
+ * @param path media path
+ * @param type media attribute type [MediaAttribute]
+ * @return media information
+ */
+ fun getMediaInfo(path: String?, @MediaAttribute type: Int): Int? {
+ return FFmpegCmd.instance?.getMediaInfo(path, type)
+ }
+
+ /**
+ * Get support for unpacking format
+ *
+ * @param formatType format attribute type [FormatAttribute]
+ * @return format information
+ */
+ fun getSupportFormat(@FormatAttribute formatType: Int): String? {
+ return FFmpegCmd.instance?.getFormatInfo(formatType)
+ }
+
+ /**
+ * Get support codec
+ *
+ * @param codecType codec attribute type [CodecAttribute]
+ * @return codec info
+ */
+ fun getSupportCodec(@CodecAttribute codecType: Int): String? {
+ return FFmpegCmd.instance?.getCodecInfo(codecType)
+ }
+
+ /**
+ * Execute FFmpeg commands.
+ *
+ * @param cmd ffmpeg commands [com.coder.ffmpeg.utils.FFmpegUtils]
+ */
+ fun runCmd(cmd: Array, callBack: IFFmpegCallBack?): Int? {
+ return FFmpegCmd.instance?.runCmd(cmd, callBack)
+ }
+
+ /**
+ * Execute FFmpeg commands.
+ *
+ * @param cmd ffmpeg commands [com.coder.ffmpeg.utils.FFmpegUtils]
+ */
+ fun runCmd(cmd: Array): Int? {
+ return FFmpegCmd.instance?.runCmd(cmd)
+ }
+
+ /**
+ * Quit execute.
+ */
+ fun cancel() {
+ FFmpegCmd.instance?.cancel()
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.java b/ffmpeg/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.java
deleted file mode 100644
index 230b5e5..0000000
--- a/ffmpeg/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.java
+++ /dev/null
@@ -1,863 +0,0 @@
-package com.coder.ffmpeg.utils;
-
-import android.annotation.SuppressLint;
-import android.util.Log;
-
-import com.coder.ffmpeg.annotation.Direction;
-import com.coder.ffmpeg.annotation.ImageFormat;
-import com.coder.ffmpeg.annotation.Transpose;
-
-import java.util.Locale;
-
-/**
- * @author: AnJoiner
- * @datetime: 19-12-17
- */
-public class FFmpegUtils {
- /**
- * 使用ffmpeg命令行进行音频转码
- *
- * @param srcFile 源文件
- * @param targetFile 目标文件(后缀指定转码格式)
- * @return 转码后的文件
- */
-
- public static String[] transformAudio(String srcFile, String targetFile) {
- String command = "ffmpeg -y -i %s %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行音频剪切
- *
- * @param srcFile 源文件
- * @param startTime 剪切的开始时间(单位为秒)
- * @param duration 剪切时长(单位为秒)
- * @param targetFile 目标文件
- * @return 剪切后的文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] cutAudio(String srcFile, int startTime, int duration,
- String targetFile) {
- String command = "ffmpeg -y -i %s -vn -acodec copy -ss %d -t %d %s";
- command = String.format(command, srcFile, startTime, duration, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * @param srcFile 源文件
- * @param startTime 剪切的开始时间(单位为秒)
- * @param endTime 剪切的结束时间(单位为秒)
- * @param targetFile 目标文件
- * @return 剪切后的文件
- */
- public static String[] cutAudio(String srcFile, String startTime, String endTime,
- String targetFile) {
- String cmd = "ffmpeg -y -i %s -vn -acodec copy -ss %s -t %s %s";
- String command = String.format(cmd, srcFile, startTime, endTime, targetFile);
-
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行音频合并
- *
- * @param srcFile 源文件
- * @param appendFile 待追加的文件
- * @param targetFile 目标文件
- * @return 合并后的文件
- */
- public static String[] concatAudio(String srcFile, String appendFile, String targetFile) {
- //ffmpeg -y -i "concat:123.mp3|124.mp3" -acodec copy output.mp3
- //ffmpeg -i 1.mp3 -i 2.mp3 -filter_complex '[0:0] [1:0] concat=n=2:v=0:a=1 [a]' -map [a] new.mp3
- String command = "ffmpeg -y -i concat:%s|%s -acodec copy %s";
- command = String.format(command, srcFile, appendFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行音频混合
- *
- * @param srcFile 源文件
- * @param mixFile 待混合文件
- * @param targetFile 目标文件
- * @return 混合后的文件
- */
- public static String[] mixAudio(String srcFile, String mixFile, String targetFile) {
- //-filter_complex
- // amix=inputs=2:duration=shortest output.wav
- String command = "ffmpeg -y -i %s -i %s -filter_complex amix=inputs=2:duration=shortest %s";
-
- command = String.format(command, srcFile, mixFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * @param audio 源文件
- * @param reduce 音量(单位dB)
- * @param outPath 输出地址
- * @return 命令
- */
- public static String[] changeVolume(String audio, int reduce, String outPath) {
- String cmd = "ffmpeg -y -i %s -af volume=%sdB %s";
- String command = String.format(cmd, audio, reduce, outPath);
- Log.d("FFMEPG", command);
- return command.split(" ");
- }
-
-
- /**
- * @param audio 源文件
- * @param reduce 音量倍数
- * @param outPath 输出地址
- * @return 命令
- */
- public static String[] changeVolume(String audio, float reduce, String outPath) {
- String cmd = "ffmpeg -y -i %s -af volume=%s %s";
- String command = String.format(cmd, audio, reduce, outPath);
- Log.d("FFMEPG", command);
- return command.split(" ");
- }
-
-
- /**
- * 使用ffmpeg命令行进行音视频合成
- *
- * @param videoFile 视频文件
- * @param audioFile 音频文件
- * @param duration 视频时长
- * @param muxFile 目标文件
- * @return 合成后的文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] mixAudioVideo(String videoFile, String audioFile, int duration,
- String muxFile) {
- //-t:时长 如果忽略音视频时长,则把"-t %d"去掉
- String command = "ffmpeg -y -i %s -i %s -t %d %s";
- command = String.format(command, videoFile, audioFile, duration, muxFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行音视频合成
- *
- * @param videoFile 视频文件
- * @param audioFile 音频文件 (aac 格式)
- * @param muxFile 目标文件
- * @return 合成后的文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] mixAudioVideo(String videoFile, String audioFile,
- String muxFile) {
- //-t:时长 如果忽略音视频时长,则把"-t %d"去掉
- String command = "ffmpeg -y -i %s -i %s -codec copy %s";
- command = String.format(command, videoFile, audioFile, muxFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行抽取音频
- *
- * @param srcFile 原文件
- * @param targetFile 目标文件
- * @return 抽取后的音频文件
- */
- public static String[] extractAudio(String srcFile, String targetFile) {
- //-vn:video not
- String command = "ffmpeg -y -i %s -acodec copy -vn %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行抽取视频
- *
- * @param srcFile 原文件
- * @param targetFile 目标文件
- * @return 抽取后的视频文件
- */
- public static String[] extractVideo(String srcFile, String targetFile) {
- //-an audio not
- String command = "ffmpeg -y -i %s -vcodec copy -an %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行视频转码
- *
- * @param srcFile 源文件
- * @param targetFile 目标文件(后缀指定转码格式)
- * @return 转码后的文件
- */
- public static String[] transformVideo(String srcFile, String targetFile) {
- //指定目标视频的帧率、码率、分辨率
-// String transformVideoCmd = "ffmpeg -i %s -r 25 -b 200 -s 1080x720 %s";
- String command = "ffmpeg -y -i %s -vcodec copy -acodec copy %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行视频剪切(不包含)
- *
- * @param srcFile 源文件
- * @param startTime 剪切的开始时间(单位为秒)
- * @param duration 剪切时长(单位为秒)
- * @param targetFile 目标文件
- * @return 剪切后的文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] cutVideo(String srcFile, int startTime, int duration,
- String targetFile) {
- String command = "ffmpeg -y -i %s -ss %d -t %d -c copy %s";
- command = String.format(command, srcFile, startTime, duration, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行视频剪切(包含关键帧)
- *
- * @param srcFile 源文件
- * @param startTime 剪切的开始时间(单位为秒)
- * @param duration 剪切时长(单位为秒)
- * @param targetFile 目标文件
- * @return 剪切后的文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] cutVideo2(String srcFile, int startTime, int duration,
- String targetFile) {
- String command = "ffmpeg -y -ss %d -t %d -accurate_seek -i %s -codec copy " +
- "-avoid_negative_ts 1 %s";
- command = String.format(command, startTime, duration, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行音频合并
- *
- * @param inputFile 输入文件(.txt格式)
- * @param targetFile 目标文件
- * @return 合并后的文件
- */
- public static String[] concatVideo(String inputFile, String targetFile) {
- // ffmpeg -f concat -i inputs.txt -c copy output.flv
- String command = "ffmpeg -y -f concat -i %s -codec copy %s";
- command = String.format(command, inputFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行视频截图
- *
- * @param srcFile 源文件
- * @param size 图片尺寸大小
- * @param targetFile 目标文件
- * @return 截图后的文件
- */
- public static String[] screenShot(String srcFile, String size, String targetFile) {
- String command = "ffmpeg -y -i %s -f image2 -t 0.001 -s %s %s";
- command = String.format(command, srcFile, size, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行视频截图
- *
- * @param srcFile 源文件
- * @param targetFile 目标文件
- * @return 截图后的文件
- */
- public static String[] screenShot(String srcFile, String targetFile) {
- String command = "ffmpeg -y -i %s -f image2 -t 0.001 %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * @param inputFile 视频源文件
- * @param targetDir 图片生成目录
- * @param format 图片格式
- * @return 图片文件
- */
- public static String[] video2Image(String inputFile, String targetDir,
- @ImageFormat String format) {
- //ffmpeg -i ss.mp4 -r 1 -f image2 image-%3d.jpeg
- String command = "ffmpeg -y -i %s -r 1 -f image2 %s";
- command = String.format(Locale.CHINESE, command, inputFile, targetDir);
- command = command + "/%3d." + format;
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 视频抽帧转成图片
- *
- * @param inputFile 输入文件
- * @param startTime 开始时间
- * @param duration 持续时间
- * @param frameRate 帧率
- * @param targetFile 输出文件
- * @param format 图片格式
- * @return 视频抽帧的命令行
- */
- public static String[] video2Image(String inputFile, int startTime, int duration,
- int frameRate, String targetFile,
- @ImageFormat String format) {
- //-ss:开始时间,单位为秒
- //-t:持续时间,单位为秒
- //-r:帧率,每秒抽多少帧
- String command = "ffmpeg -y -i %s -ss %s -t %s -r %s %s";
- command = String.format(Locale.CHINESE, command, inputFile, startTime, duration,
- frameRate, targetFile);
- command = command + "%3d." + format;
- return command.split(" ");
- }
-
- /**
- * 使用ffmpeg命令行给视频添加水印
- *
- * @param srcFile 源文件
- * @param waterMark 水印文件路径
- * @param targetFile 目标文件
- * @return 添加水印后的文件
- */
- public static String[] addWaterMark(String srcFile, String waterMark, String targetFile) {
- String command = "ffmpeg -y -i %s -i %s -filter_complex overlay=40:40 %s";
- command = String.format(command, srcFile, waterMark, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行视频转成Gif动图
- *
- * @param srcFile 源文件
- * @param startTime 开始时间
- * @param duration 截取时长
- * @param targetFile 目标文件
- * @return Gif文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] video2Gif(String srcFile, int startTime, int duration,
- String targetFile) {
- //String screenShotCmd = "ffmpeg -i %s -vframes %d -s 320x240 -f gif %s";
- String command = "ffmpeg -y -i %s -ss %d -t %d -f gif %s";
- command = String.format(command, srcFile, startTime, duration, targetFile);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 使用ffmpeg命令行进行屏幕录制
- *
- * @param size 视频尺寸大小
- * @param recordTime 录屏时间
- * @param targetFile 目标文件
- * @return 屏幕录制文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] screenRecord(String size, int recordTime, String targetFile) {
- //-vd x11:0,0 指录制所使用的偏移为 x=0 和 y=0
- //String screenRecordCmd = "ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s %s %s";
- String command = "ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s %s -t " +
- "%d %s";
- command = String.format(command, size, recordTime, targetFile);
- Log.i("VideoHandleActivity", "screenRecordCmd=" + command);
- return command.split(" ");//以空格分割为字符串数组
- }
-
- /**
- * 使用ffmpeg命令行进行图片合成视频
- *
- * @param srcFile 源文件
- * @param targetFile 目标文件
- * @return 合成的视频文件
- */
- @SuppressLint("DefaultLocale")
- public static String[] image2Video(String srcFile, String targetFile) {
- //-f image2:代表使用image2格式,需要放在输入文件前面
- String command = "ffmpeg -y -f image2 -r 1 -i %simg#d.jpg -vcodec mpeg4 %s";
- command = String.format(command, srcFile, targetFile);
- command = command.replace("#", "%");
- Log.i("VideoHandleActivity", "combineVideo=" + command);
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- public static String[] image2Video(String srcDir, @ImageFormat String format,
- String targetFile) {
- //-f image2:代表使用image2格式,需要放在输入文件前面
- // ffmpeg -f image2 -i image-%3d.jpeg images.mp4
- String command = "ffmpeg -y -f image2 -r 1 -i %s/#3d.%s -vcodec mpeg4 %s";
- command = String.format(command, srcDir, format, targetFile);
- command = command.replace("#", "%");
- return command.split(" ");//以空格分割为字符串数组
- }
-
-
- /**
- * 音频解码
- *
- * @param srcFile 源文件
- * @param targetFile 目标文件
- * @param sampleRate 采样率
- * @param channel 声道:单声道为1/立体声道为2
- * @return 音频解码的命令行
- */
- public static String[] decodeAudio(String srcFile, String targetFile, int sampleRate,
- int channel) {
- String command = "ffmpeg -y -i %s -acodec pcm_s16le -ar %d -ac %d -f s16le %s";
- command = String.format(command, srcFile, sampleRate, channel, targetFile);
- return command.split(" ");
- }
-
- /**
- * 音频编码
- *
- * @param srcFile 源文件pcm裸流
- * @param targetFile 编码后目标文件
- * @param sampleRate 采样率
- * @param channel 声道:单声道为1/立体声道为2
- * @return 音频编码的命令行
- */
- @SuppressLint("DefaultLocale")
- public static String[] encodeAudio(String srcFile, String targetFile, int sampleRate,
- int channel) {
- String command = "ffmpeg -y -f s16le -ar %d -ac %d -acodec pcm_s16le -i %s %s";
- command = String.format(command, sampleRate, channel, srcFile, targetFile);
- return command.split(" ");
- }
-
- /**
- * 多画面拼接视频
- *
- * @param input1 输入文件1
- * @param input2 输入文件2
- * @param direction 视频布局方向
- * @param targetFile 画面拼接文件
- * @return 画面拼接的命令行
- */
- public static String[] multiVideo(String input1, String input2, String targetFile,
- @Direction int direction) {
-// String multiVideo = "ffmpeg -i %s -i %s -i %s -i %s -filter_complex " +
-// "\"[0:v]pad=iw*2:ih*2[a];[a][1:v]overlay=w[b];[b][2:v]overlay=0:h[c];
-// [c][3:v]overlay=w:h\" %s";
- String command = "ffmpeg -y -i %s -i %s -filter_complex hstack %s";//hstack:水平拼接,默认
- if (direction == Direction.LAYOUT_VERTICAL) {//vstack:垂直拼接
- command = command.replace("hstack", "vstack");
- }
- command = String.format(command, input1, input2, targetFile);
- return command.split(" ");
- }
-
- /**
- * 视频反序倒播
- *
- * @param inputFile 输入文件
- * @param targetFile 反序文件
- * @return 视频反序的命令行
- */
- public static String[] reverseVideo(String inputFile, String targetFile) {
- String command = "ffmpeg -y -i %s -filter_complex [0:v]reverse[v] -map [v] %s";//单纯视频反序
- command = String.format(command, inputFile, targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频反序倒播
- *
- * @param inputFile 输入文件
- * @param targetFile 反序文件
- * @return 视频反序的命令行
- */
- public static String[] reverseAudioVideo(String inputFile, String targetFile) {
- String command = "ffmpeg -y -i %s -filter_complex [0:v]reverse[v];[0:a]reverse[a] -map [v] -map [a] %s";
- command = String.format(command, inputFile, targetFile);
- return command.split(" ");
- }
-
- /**
- * 视频降噪
- *
- * @param inputFile 输入文件
- * @param targetFile 输出文件
- * @return 视频降噪的命令行
- */
- public static String[] denoiseVideo(String inputFile, String targetFile) {
- String command = "ffmpeg -y -i %s -nr 500 %s";
- command = String.format(command, inputFile, targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频叠加成画中画
- *
- * @param inputFile1 输入文件
- * @param inputFile2 输入文件
- * @param targetFile 输出文件
- * @param x 小视频起点x坐标
- * @param y 小视频起点y坐标
- * @return 视频画中画的命令行
- */
- @SuppressLint("DefaultLocale")
- public static String[] picInPicVideo(String inputFile1, String inputFile2, int x, int y,
- String targetFile) {
- String command = "ffmpeg -y -i %s -i %s -filter_complex overlay=%d:%d %s";
- command = String.format(command, inputFile1, inputFile2, x, y, targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频缩小一倍
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 视频缩小一倍 命令行
- */
- public static String[] videoDoubleDown(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -vf scale=iw/2:-1 %s";
- command = String.format(command,srcFile,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频放大一倍
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 视频放大一倍命令行
- */
- @Deprecated
- public static String[] videoDouble(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -vf scale=iw*2:-1 %s";
- command = String.format(command,srcFile,targetFile);
- return command.split(" ");
- }
-
- /**
- * 视频放大一倍
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 视频放大一倍命令行
- */
- public static String[] videoDoubleUp(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -vf scale=iw*2:-1 %s";
- command = String.format(command,srcFile,targetFile);
- return command.split(" ");
- }
-
- /**
- * 视频缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @param height 缩放后高度
- * @return 视频缩放命令行
- */
- public static String[] videoScale(String srcFile, String targetFile, int width, int height){
- return scale(srcFile, targetFile, width, height);
- }
-
- /**
- * 视频缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @return 视频缩放命令行
- */
- public static String[] videoScale(String srcFile, String targetFile, int width){
- return scale(srcFile, targetFile, width);
- }
-
- /**
- * 图片缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @param height 缩放后高度
- * @return 视频缩放命令行
- */
- public static String[] imageScale(String srcFile, String targetFile, int width, int height){
- return scale(srcFile, targetFile, width, height);
- }
-
- /**
- * 图片缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @return 视频缩放命令行
- */
- public static String[] imageScale(String srcFile, String targetFile, int width){
- return scale(srcFile, targetFile, width);
- }
-
- /**
- * 缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @param height 缩放后高度
- * @return 缩放命令行
- */
- public static String[] scale(String srcFile, String targetFile, int width, int height){
- String command = "ffmpeg -y -i %s -vf scale=%d:%d %s";
- command = String.format(Locale.CHINA,command,srcFile,width,height,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 按宽度等比例缩放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 缩放后宽度
- * @return 缩放命令行
- */
- public static String[] scale(String srcFile, String targetFile,int width ){
- String command = "ffmpeg -y -i %s -vf scale=%d:-1 %s";
- command = String.format(Locale.CHINA,command,srcFile,width,targetFile);
- return command.split(" ");
- }
-
- /**
- * 倍速播放
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 倍速播放命令行
- */
- public static String[] videoSpeed2(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -filter_complex [0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a] -map [v] -map [a] %s";
- command = String.format(command,srcFile,targetFile);
- return command.split(" ");
- }
-
- /**
- * 解码成YUV原始数据
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 视频解码命令行
- */
- public static String[] decode2YUV(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s";
- command = String.format(command,srcFile,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * YUV 转 H264
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return YUV 转 H264命令行
- */
- public static String[] yuv2H264(String srcFile, String targetFile){
-// String command = "ffmpeg -y -f rawvideo -pix_fmt yuv420p -s 720x1280 -r 30 -i %s -c:v libx264 -f rawvideo %s";
-// command = String.format(command,srcFile,targetFile);
- return yuv2H264(srcFile, targetFile,720,1280);
- }
-
-
- /**
- * YUV 编码 H264
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param width 输出文件宽
- * @param height 输出文件高
- * @return YUV 转 H264命令行
- */
- public static String[] yuv2H264(String srcFile, String targetFile,int width, int height){
- String command = "ffmpeg -y -f rawvideo -pix_fmt yuv420p -s #wx#h -r 30 -i %s -c:v libx264 -f rawvideo %s";
- command = String.format(Locale.CHINA,command,srcFile,targetFile);
- command = command.replace("#wx#h",width+"x"+height);
- return command.split(" ");
- }
-
-
- /**
- * 音频前5s淡入
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @return 音频前5s淡入命令行
- */
- public static String [] audioFadeIn(String srcFile, String targetFile){
- return audioFadeIn(srcFile, targetFile,0,5);
- }
-
- /**
- * 音频淡入
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param start 开始位置(s)
- * @param duration 持续时间(s)
- * @return 音频淡入命令行
- */
- public static String[] audioFadeIn(String srcFile, String targetFile, int start, int duration){
- String command = "ffmpeg -y -i %s -filter_complex afade=t=in:ss=%d:d=%d %s";
- command = String.format(Locale.CHINA, command,srcFile,start,duration,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 音频淡出
- * @param srcFile 音频源文件
- * @param targetFile 输出文件
- * @param start 开始位置(s)
- * @param duration 持续时间(s)
- * @return 音频淡出命令行
- */
- public static String[] audioFadeOut(String srcFile, String targetFile, int start, int duration){
- String command = "ffmpeg -y -i %s -filter_complex afade=t=out:st=%d:d=%d %s";
- command = String.format(Locale.CHINA, command,srcFile,start,duration,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频亮度
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param bright 亮度(-1.0 ~ 1.0), 默认0
- * @return 视频亮度命令行
- */
- public static String[] videoBright(String srcFile, String targetFile, float bright){
- String command = "ffmpeg -y -i %s -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=brightness=%f -f mp4 %s";
- command = String.format(Locale.CHINA,command,srcFile,bright,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 视频对比度
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param contrast 对比度(-2.0 ~ 2.0), 默认1.0
- * @return
- */
- public static String[] videoContrast(String srcFile, String targetFile, float contrast){
- String command ="ffmpeg -y -i %s -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=contrast=%f -f mp4 %s";
- command = String.format(Locale.CHINA,command,srcFile,contrast,targetFile);
- return command.split(" ");
- }
-
- /**
- * 视频旋转
- * @param srcFile 源文件
- * @param targetFile 输出文件
- * @param transpose
- * @return 视频旋转命令行
- */
- public static String[] videoRotation(String srcFile, String targetFile,@Transpose int transpose){
- String command = "ffmpeg -y -i %s -vf transpose=%d -b:v 600k %s";
- command = String.format(Locale.CHINA,command,srcFile,transpose,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 从视频中获取一帧输出图片
- * @param srcFile 源文件
- * @param targetFile 目标文件(png 或 jpg)
- * @param time 一帧的时间:hh:mm:ss.xxx
- * @return 从视频中获取一帧输出图片命令行
- */
- public static String[] frame2Image(String srcFile, String targetFile, String time){
- String command = "ffmpeg -y -i %s -ss %s -vframes 1 %s";
- command = String.format(command, srcFile,time, targetFile);
- return command.split(" ");
- }
-
- /**
- * 将音频进行fdk_aac编码
- * @param srcFile 音频源文件
- * @param targetFile 音频输出文件(m4a或aac)
- * @return 将音频进行fdk_aac编码命令
- */
- public static String[] audio2Fdkaac(String srcFile,String targetFile){
- String command = "ffmpeg -y -i %s -c:a libfdk_aac %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 将音频进行VBR MP3编码
- * @param srcFile 音频源文件
- * @param targetFile 音频输出文件(mp3)
- * @return 将音频进行VBR MP3编码命令
- */
- public static String[] audio2Mp3lame(String srcFile,String targetFile){
- String command = "ffmpeg -y -i %s -c:a libmp3lame %s";
- command = String.format(command, srcFile, targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 将格式视频进行切片,形成m3u8的视频流(m3u8格式一般用于直播或者点播)
- * @param srcFile 视频路径
- * @param targetFile 目标路径(以xxx.m3u8为输出)
- * @param splitTime 切割时间 (单位:秒)
- * @return 返回以target文件名开头的ts系列文件 如:out0.ts out1.ts ...
- *
- * {@link com.coder.ffmpeg.utils.FFmpegUtils#video2HLS}
- */
- @Deprecated
- public static String[] videoHLS(String srcFile, String targetFile,int splitTime){
- String command = "ffmpeg -y -i %s -c copy -bsf:v h264_mp4toannexb -hls_time %s %s";
- command = String.format(command, srcFile, splitTime ,targetFile);
- return command.split(" ");
- }
-
- /**
- * 将格式视频进行切片,形成m3u8的视频流(m3u8格式一般用于直播或者点播)
- * @param srcFile 视频路径
- * @param targetFile 目标路径(以xxx.m3u8为输出)
- * @param splitTime 切割时间 (单位:秒)
- * @return 返回以target文件名开头的ts系列文件 如:out0.ts out1.ts ...
- */
- public static String[] video2HLS(String srcFile, String targetFile,int splitTime){
- String command = "ffmpeg -y -i %s -c copy -bsf:v h264_mp4toannexb -hls_time %s %s";
- command = String.format(command, srcFile, splitTime ,targetFile);
- return command.split(" ");
- }
-
- /**
- * 将ts视频流合成视频
- * @param m3u8Index xx.m3u8视频索引
- * @param targetFile 目标路径
- * @return 返回合成视频
- */
- public static String[] hls2Video(String m3u8Index, String targetFile){
- String command = "ffmpeg -y -i %s -c copy %s";
- command = String.format(command, m3u8Index ,targetFile);
- return command.split(" ");
- }
-
-
- /**
- * 将音频转为amr格式
- * @param srcFile 音频源文件
- * @param targetFile 目标文件
- * @return amr格式音频
- */
- public static String[] audio2Amr(String srcFile, String targetFile){
- String command = "ffmpeg -y -i %s -c:a libopencore_amrnb -ar 8000 -ac 1 %s";
- command = String.format(command, srcFile ,targetFile);
- return command.split(" ");
- }
-}
diff --git a/ffmpeg/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.kt b/ffmpeg/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.kt
new file mode 100644
index 0000000..45e37bd
--- /dev/null
+++ b/ffmpeg/src/main/java/com/coder/ffmpeg/utils/FFmpegUtils.kt
@@ -0,0 +1,849 @@
+package com.coder.ffmpeg.utils
+
+import android.annotation.SuppressLint
+import android.util.Log
+import com.coder.ffmpeg.annotation.Direction
+import com.coder.ffmpeg.annotation.ImageFormat
+import com.coder.ffmpeg.annotation.Transpose
+import java.util.*
+
+/**
+ * @author: AnJoiner
+ * @datetime: 19-12-17
+ */
+object FFmpegUtils {
+ /**
+ * 使用ffmpeg命令行进行音频转码
+ *
+ * @param srcFile 源文件
+ * @param targetFile 目标文件(后缀指定转码格式)
+ * @return 转码后的文件
+ */
+ fun transformAudio(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音频剪切
+ *
+ * @param srcFile 源文件
+ * @param startTime 剪切的开始时间(单位为秒)
+ * @param duration 剪切时长(单位为秒)
+ * @param targetFile 目标文件
+ * @return 剪切后的文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun cutAudio(srcFile: String?, startTime: Int, duration: Int,
+ targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -vn -acodec copy -ss %d -t %d %s"
+ command = String.format(command, srcFile, startTime, duration, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * @param srcFile 源文件
+ * @param startTime 剪切的开始时间(单位为秒)
+ * @param endTime 剪切的结束时间(单位为秒)
+ * @param targetFile 目标文件
+ * @return 剪切后的文件
+ */
+ fun cutAudio(srcFile: String?, startTime: String?, endTime: String?,
+ targetFile: String?): Array {
+ val cmd = "ffmpeg -y -i %s -vn -acodec copy -ss %s -t %s %s"
+ val command = String.format(cmd, srcFile, startTime, endTime, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音频合并
+ *
+ * @param srcFile 源文件
+ * @param appendFile 待追加的文件
+ * @param targetFile 目标文件
+ * @return 合并后的文件
+ */
+ fun concatAudio(srcFile: String?, appendFile: String?, targetFile: String?): Array {
+ //ffmpeg -y -i "concat:123.mp3|124.mp3" -acodec copy output.mp3
+ //ffmpeg -i 1.mp3 -i 2.mp3 -filter_complex '[0:0] [1:0] concat=n=2:v=0:a=1 [a]' -map [a] new.mp3
+ var command = "ffmpeg -y -i concat:%s|%s -acodec copy %s"
+ command = String.format(command, srcFile, appendFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音频混合
+ *
+ * @param srcFile 源文件
+ * @param mixFile 待混合文件
+ * @param targetFile 目标文件
+ * @return 混合后的文件
+ */
+ fun mixAudio(srcFile: String?, mixFile: String?, targetFile: String?): Array {
+ //-filter_complex
+ // amix=inputs=2:duration=shortest output.wav
+ var command = "ffmpeg -y -i %s -i %s -filter_complex amix=inputs=2:duration=shortest %s"
+ command = String.format(command, srcFile, mixFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * @param audio 源文件
+ * @param reduce 音量(单位dB)
+ * @param outPath 输出地址
+ * @return 命令
+ */
+ fun changeVolume(audio: String?, reduce: Int, outPath: String?): Array {
+ val cmd = "ffmpeg -y -i %s -af volume=%sdB %s"
+ val command = String.format(cmd, audio, reduce, outPath)
+ Log.d("FFMEPG", command)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * @param audio 源文件
+ * @param reduce 音量倍数
+ * @param outPath 输出地址
+ * @return 命令
+ */
+ fun changeVolume(audio: String?, reduce: Float, outPath: String?): Array {
+ val cmd = "ffmpeg -y -i %s -af volume=%s %s"
+ val command = String.format(cmd, audio, reduce, outPath)
+ Log.d("FFMEPG", command)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音视频合成
+ *
+ * @param videoFile 视频文件
+ * @param audioFile 音频文件
+ * @param duration 视频时长
+ * @param muxFile 目标文件
+ * @return 合成后的文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun mixAudioVideo(videoFile: String?, audioFile: String?, duration: Int,
+ muxFile: String?): Array {
+ //-t:时长 如果忽略音视频时长,则把"-t %d"去掉
+ var command = "ffmpeg -y -i %s -i %s -t %d %s"
+ command = String.format(command, videoFile, audioFile, duration, muxFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音视频合成
+ *
+ * @param videoFile 视频文件
+ * @param audioFile 音频文件 (aac 格式)
+ * @param muxFile 目标文件
+ * @return 合成后的文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun mixAudioVideo(videoFile: String?, audioFile: String?,
+ muxFile: String?): Array {
+ //-t:时长 如果忽略音视频时长,则把"-t %d"去掉
+ var command = "ffmpeg -y -i %s -i %s -codec copy %s"
+ command = String.format(command, videoFile, audioFile, muxFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行抽取音频
+ *
+ * @param srcFile 原文件
+ * @param targetFile 目标文件
+ * @return 抽取后的音频文件
+ */
+ fun extractAudio(srcFile: String?, targetFile: String?): Array {
+ //-vn:video not
+ var command = "ffmpeg -y -i %s -acodec copy -vn %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行抽取视频
+ *
+ * @param srcFile 原文件
+ * @param targetFile 目标文件
+ * @return 抽取后的视频文件
+ */
+ fun extractVideo(srcFile: String?, targetFile: String?): Array {
+ //-an audio not
+ var command = "ffmpeg -y -i %s -vcodec copy -an %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频转码
+ *
+ * @param srcFile 源文件
+ * @param targetFile 目标文件(后缀指定转码格式)
+ * @return 转码后的文件
+ */
+ fun transformVideo(srcFile: String?, targetFile: String?): Array {
+ //指定目标视频的帧率、码率、分辨率
+// String transformVideoCmd = "ffmpeg -i %s -r 25 -b 200 -s 1080x720 %s";
+ var command = "ffmpeg -y -i %s -vcodec copy -acodec copy %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频剪切(不包含)
+ *
+ * @param srcFile 源文件
+ * @param startTime 剪切的开始时间(单位为秒)
+ * @param duration 剪切时长(单位为秒)
+ * @param targetFile 目标文件
+ * @return 剪切后的文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun cutVideo(srcFile: String?, startTime: Int, duration: Int,
+ targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -ss %d -t %d -c copy %s"
+ command = String.format(command, srcFile, startTime, duration, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频剪切(包含关键帧)
+ *
+ * @param srcFile 源文件
+ * @param startTime 剪切的开始时间(单位为秒)
+ * @param duration 剪切时长(单位为秒)
+ * @param targetFile 目标文件
+ * @return 剪切后的文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun cutVideo2(srcFile: String?, startTime: Int, duration: Int,
+ targetFile: String?): Array {
+ var command = "ffmpeg -y -ss %d -t %d -accurate_seek -i %s -codec copy " +
+ "-avoid_negative_ts 1 %s"
+ command = String.format(command, startTime, duration, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行音频合并
+ *
+ * @param inputFile 输入文件(.txt格式)
+ * @param targetFile 目标文件
+ * @return 合并后的文件
+ */
+ fun concatVideo(inputFile: String?, targetFile: String?): Array {
+ // ffmpeg -f concat -i inputs.txt -c copy output.flv
+ var command = "ffmpeg -y -f concat -i %s -codec copy %s"
+ command = String.format(command, inputFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频截图
+ *
+ * @param srcFile 源文件
+ * @param size 图片尺寸大小
+ * @param targetFile 目标文件
+ * @return 截图后的文件
+ */
+ fun screenShot(srcFile: String?, size: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -f image2 -t 0.001 -s %s %s"
+ command = String.format(command, srcFile, size, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频截图
+ *
+ * @param srcFile 源文件
+ * @param targetFile 目标文件
+ * @return 截图后的文件
+ */
+ fun screenShot(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -f image2 -t 0.001 %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * @param inputFile 视频源文件
+ * @param targetDir 图片生成目录
+ * @param format 图片格式
+ * @return 图片文件
+ */
+ fun video2Image(inputFile: String?, targetDir: String?,
+ @ImageFormat format: String): Array {
+ //ffmpeg -i ss.mp4 -r 1 -f image2 image-%3d.jpeg
+ var command = "ffmpeg -y -i %s -r 1 -f image2 %s"
+ command = String.format(Locale.CHINESE, command, inputFile, targetDir)
+ command = "$command/%3d.$format"
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 视频抽帧转成图片
+ *
+ * @param inputFile 输入文件
+ * @param startTime 开始时间
+ * @param duration 持续时间
+ * @param frameRate 帧率
+ * @param targetFile 输出文件
+ * @param format 图片格式
+ * @return 视频抽帧的命令行
+ */
+ fun video2Image(inputFile: String?, startTime: Int, duration: Int,
+ frameRate: Int, targetFile: String?,
+ @ImageFormat format: String): Array {
+ //-ss:开始时间,单位为秒
+ //-t:持续时间,单位为秒
+ //-r:帧率,每秒抽多少帧
+ var command = "ffmpeg -y -i %s -ss %s -t %s -r %s %s"
+ command = String.format(Locale.CHINESE, command, inputFile, startTime, duration,
+ frameRate, targetFile)
+ command = "$command%3d.$format"
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行给视频添加水印
+ *
+ * @param srcFile 源文件
+ * @param waterMark 水印文件路径
+ * @param targetFile 目标文件
+ * @return 添加水印后的文件
+ */
+ fun addWaterMark(srcFile: String?, waterMark: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -i %s -filter_complex overlay=40:40 %s"
+ command = String.format(command, srcFile, waterMark, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行视频转成Gif动图
+ *
+ * @param srcFile 源文件
+ * @param startTime 开始时间
+ * @param duration 截取时长
+ * @param targetFile 目标文件
+ * @return Gif文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun video2Gif(srcFile: String?, startTime: Int, duration: Int,
+ targetFile: String?): Array {
+ //String screenShotCmd = "ffmpeg -i %s -vframes %d -s 320x240 -f gif %s";
+ var command = "ffmpeg -y -i %s -ss %d -t %d -f gif %s"
+ command = String.format(command, srcFile, startTime, duration, targetFile)
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行屏幕录制
+ *
+ * @param size 视频尺寸大小
+ * @param recordTime 录屏时间
+ * @param targetFile 目标文件
+ * @return 屏幕录制文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun screenRecord(size: String?, recordTime: Int, targetFile: String?): Array {
+ //-vd x11:0,0 指录制所使用的偏移为 x=0 和 y=0
+ //String screenRecordCmd = "ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s %s %s";
+ var command = "ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s %s -t " +
+ "%d %s"
+ command = String.format(command, size, recordTime, targetFile)
+ Log.i("VideoHandleActivity", "screenRecordCmd=$command")
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 使用ffmpeg命令行进行图片合成视频
+ *
+ * @param srcFile 源文件
+ * @param targetFile 目标文件
+ * @return 合成的视频文件
+ */
+ @SuppressLint("DefaultLocale")
+ fun image2Video(srcFile: String?, targetFile: String?): Array {
+ //-f image2:代表使用image2格式,需要放在输入文件前面
+ var command = "ffmpeg -y -f image2 -r 1 -i %simg#d.jpg -vcodec mpeg4 %s"
+ command = String.format(command, srcFile, targetFile)
+ command = command.replace("#", "%")
+ Log.i("VideoHandleActivity", "combineVideo=$command")
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ fun image2Video(srcDir: String?, @ImageFormat format: String?,
+ targetFile: String?): Array {
+ //-f image2:代表使用image2格式,需要放在输入文件前面
+ // ffmpeg -f image2 -i image-%3d.jpeg images.mp4
+ var command = "ffmpeg -y -f image2 -r 1 -i %s/#3d.%s -vcodec mpeg4 %s"
+ command = String.format(command, srcDir, format, targetFile)
+ command = command.replace("#", "%")
+ return command.split(" ") //以空格分割为字符串数组
+ .toTypedArray()
+ }
+
+ /**
+ * 音频解码
+ *
+ * @param srcFile 源文件
+ * @param targetFile 目标文件
+ * @param sampleRate 采样率
+ * @param channel 声道:单声道为1/立体声道为2
+ * @return 音频解码的命令行
+ */
+ fun decodeAudio(srcFile: String?, targetFile: String?, sampleRate: Int,
+ channel: Int): Array {
+ var command = "ffmpeg -y -i %s -acodec pcm_s16le -ar %d -ac %d -f s16le %s"
+ command = String.format(command, srcFile, sampleRate, channel, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 音频编码
+ *
+ * @param srcFile 源文件pcm裸流
+ * @param targetFile 编码后目标文件
+ * @param sampleRate 采样率
+ * @param channel 声道:单声道为1/立体声道为2
+ * @return 音频编码的命令行
+ */
+ @SuppressLint("DefaultLocale")
+ fun encodeAudio(srcFile: String?, targetFile: String?, sampleRate: Int,
+ channel: Int): Array {
+ var command = "ffmpeg -y -f s16le -ar %d -ac %d -acodec pcm_s16le -i %s %s"
+ command = String.format(command, sampleRate, channel, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 多画面拼接视频
+ *
+ * @param input1 输入文件1
+ * @param input2 输入文件2
+ * @param direction 视频布局方向
+ * @param targetFile 画面拼接文件
+ * @return 画面拼接的命令行
+ */
+ fun multiVideo(input1: String?, input2: String?, targetFile: String?,
+ @Direction direction: Int): Array {
+// String multiVideo = "ffmpeg -i %s -i %s -i %s -i %s -filter_complex " +
+// "\"[0:v]pad=iw*2:ih*2[a];[a][1:v]overlay=w[b];[b][2:v]overlay=0:h[c];
+// [c][3:v]overlay=w:h\" %s";
+ var command = "ffmpeg -y -i %s -i %s -filter_complex hstack %s" //hstack:水平拼接,默认
+ if (direction == Direction.LAYOUT_VERTICAL) { //vstack:垂直拼接
+ command = command.replace("hstack", "vstack")
+ }
+ command = String.format(command, input1, input2, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频反序倒播
+ *
+ * @param inputFile 输入文件
+ * @param targetFile 反序文件
+ * @return 视频反序的命令行
+ */
+ fun reverseVideo(inputFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -filter_complex [0:v]reverse[v] -map [v] %s" //单纯视频反序
+ command = String.format(command, inputFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频反序倒播
+ *
+ * @param inputFile 输入文件
+ * @param targetFile 反序文件
+ * @return 视频反序的命令行
+ */
+ fun reverseAudioVideo(inputFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -filter_complex [0:v]reverse[v];[0:a]reverse[a] -map [v] -map [a] %s"
+ command = String.format(command, inputFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频降噪
+ *
+ * @param inputFile 输入文件
+ * @param targetFile 输出文件
+ * @return 视频降噪的命令行
+ */
+ fun denoiseVideo(inputFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -nr 500 %s"
+ command = String.format(command, inputFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频叠加成画中画
+ *
+ * @param inputFile1 输入文件
+ * @param inputFile2 输入文件
+ * @param targetFile 输出文件
+ * @param x 小视频起点x坐标
+ * @param y 小视频起点y坐标
+ * @return 视频画中画的命令行
+ */
+ @SuppressLint("DefaultLocale")
+ fun picInPicVideo(inputFile1: String?, inputFile2: String?, x: Int, y: Int,
+ targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -i %s -filter_complex overlay=%d:%d %s"
+ command = String.format(command, inputFile1, inputFile2, x, y, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频缩小一倍
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 视频缩小一倍 命令行
+ */
+ fun videoDoubleDown(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -vf scale=iw/2:-1 %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频放大一倍
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 视频放大一倍命令行
+ */
+ @Deprecated("")
+ fun videoDouble(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -vf scale=iw*2:-1 %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频放大一倍
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 视频放大一倍命令行
+ */
+ fun videoDoubleUp(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -vf scale=iw*2:-1 %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @param height 缩放后高度
+ * @return 视频缩放命令行
+ */
+ fun videoScale(srcFile: String?, targetFile: String?, width: Int, height: Int): Array {
+ return scale(srcFile, targetFile, width, height)
+ }
+
+ /**
+ * 视频缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @return 视频缩放命令行
+ */
+ fun videoScale(srcFile: String?, targetFile: String?, width: Int): Array {
+ return scale(srcFile, targetFile, width)
+ }
+
+ /**
+ * 图片缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @param height 缩放后高度
+ * @return 视频缩放命令行
+ */
+ fun imageScale(srcFile: String?, targetFile: String?, width: Int, height: Int): Array {
+ return scale(srcFile, targetFile, width, height)
+ }
+
+ /**
+ * 图片缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @return 视频缩放命令行
+ */
+ fun imageScale(srcFile: String?, targetFile: String?, width: Int): Array {
+ return scale(srcFile, targetFile, width)
+ }
+
+ /**
+ * 缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @param height 缩放后高度
+ * @return 缩放命令行
+ */
+ fun scale(srcFile: String?, targetFile: String?, width: Int, height: Int): Array {
+ var command = "ffmpeg -y -i %s -vf scale=%d:%d %s"
+ command = String.format(Locale.CHINA, command, srcFile, width, height, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 按宽度等比例缩放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 缩放后宽度
+ * @return 缩放命令行
+ */
+ fun scale(srcFile: String?, targetFile: String?, width: Int): Array {
+ var command = "ffmpeg -y -i %s -vf scale=%d:-1 %s"
+ command = String.format(Locale.CHINA, command, srcFile, width, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 倍速播放
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 倍速播放命令行
+ */
+ fun videoSpeed2(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -filter_complex [0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a] -map [v] -map [a] %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 解码成YUV原始数据
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 视频解码命令行
+ */
+ fun decode2YUV(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -an -c:v rawvideo -pixel_format yuv420p %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+ /**
+ * YUV 编码 H264
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param width 输出文件宽
+ * @param height 输出文件高
+ * @return YUV 转 H264命令行
+ */
+ /**
+ * YUV 转 H264
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return YUV 转 H264命令行
+ */
+ @JvmOverloads
+ fun yuv2H264(srcFile: String?, targetFile: String?, width: Int = 720, height: Int = 1280): Array {
+ var command = "ffmpeg -y -f rawvideo -pix_fmt yuv420p -s #wx#h -r 30 -i %s -c:v libx264 -f rawvideo %s"
+ command = String.format(Locale.CHINA, command, srcFile, targetFile)
+ command = command.replace("#wx#h", width.toString() + "x" + height)
+ return command.split(" ").toTypedArray()
+ }
+ /**
+ * 音频淡入
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param start 开始位置(s)
+ * @param duration 持续时间(s)
+ * @return 音频淡入命令行
+ */
+ /**
+ * 音频前5s淡入
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @return 音频前5s淡入命令行
+ */
+ @JvmOverloads
+ fun audioFadeIn(srcFile: String?, targetFile: String?, start: Int = 0, duration: Int = 5): Array {
+ var command = "ffmpeg -y -i %s -filter_complex afade=t=in:ss=%d:d=%d %s"
+ command = String.format(Locale.CHINA, command, srcFile, start, duration, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 音频淡出
+ * @param srcFile 音频源文件
+ * @param targetFile 输出文件
+ * @param start 开始位置(s)
+ * @param duration 持续时间(s)
+ * @return 音频淡出命令行
+ */
+ fun audioFadeOut(srcFile: String?, targetFile: String?, start: Int, duration: Int): Array {
+ var command = "ffmpeg -y -i %s -filter_complex afade=t=out:st=%d:d=%d %s"
+ command = String.format(Locale.CHINA, command, srcFile, start, duration, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频亮度
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param bright 亮度(-1.0 ~ 1.0), 默认0
+ * @return 视频亮度命令行
+ */
+ fun videoBright(srcFile: String?, targetFile: String?, bright: Float): Array {
+ var command = "ffmpeg -y -i %s -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=brightness=%f -f mp4 %s"
+ command = String.format(Locale.CHINA, command, srcFile, bright, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频对比度
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param contrast 对比度(-2.0 ~ 2.0), 默认1.0
+ * @return
+ */
+ fun videoContrast(srcFile: String?, targetFile: String?, contrast: Float): Array {
+ var command = "ffmpeg -y -i %s -c:v libx264 -b:v 800k -c:a libfdk_aac -vf eq=contrast=%f -f mp4 %s"
+ command = String.format(Locale.CHINA, command, srcFile, contrast, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 视频旋转
+ * @param srcFile 源文件
+ * @param targetFile 输出文件
+ * @param transpose
+ * @return 视频旋转命令行
+ */
+ fun videoRotation(srcFile: String?, targetFile: String?, @Transpose transpose: Int): Array {
+ var command = "ffmpeg -y -i %s -vf transpose=%d -b:v 600k %s"
+ command = String.format(Locale.CHINA, command, srcFile, transpose, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 从视频中获取一帧输出图片
+ * @param srcFile 源文件
+ * @param targetFile 目标文件(png 或 jpg)
+ * @param time 一帧的时间:hh:mm:ss.xxx
+ * @return 从视频中获取一帧输出图片命令行
+ */
+ fun frame2Image(srcFile: String?, targetFile: String?, time: String?): Array {
+ var command = "ffmpeg -y -i %s -ss %s -vframes 1 %s"
+ command = String.format(command, srcFile, time, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将音频进行fdk_aac编码
+ * @param srcFile 音频源文件
+ * @param targetFile 音频输出文件(m4a或aac)
+ * @return 将音频进行fdk_aac编码命令
+ */
+ fun audio2Fdkaac(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -c:a libfdk_aac %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将音频进行VBR MP3编码
+ * @param srcFile 音频源文件
+ * @param targetFile 音频输出文件(mp3)
+ * @return 将音频进行VBR MP3编码命令
+ */
+ fun audio2Mp3lame(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -c:a libmp3lame %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将格式视频进行切片,形成m3u8的视频流(m3u8格式一般用于直播或者点播)
+ * @param srcFile 视频路径
+ * @param targetFile 目标路径(以xxx.m3u8为输出)
+ * @param splitTime 切割时间 (单位:秒)
+ * @return 返回以target文件名开头的ts系列文件 如:out0.ts out1.ts ...
+ *
+ * [com.coder.ffmpeg.utils.FFmpegUtils.video2HLS]
+ */
+ @Deprecated("")
+ fun videoHLS(srcFile: String?, targetFile: String?, splitTime: Int): Array {
+ var command = "ffmpeg -y -i %s -c copy -bsf:v h264_mp4toannexb -hls_time %s %s"
+ command = String.format(command, srcFile, splitTime, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将格式视频进行切片,形成m3u8的视频流(m3u8格式一般用于直播或者点播)
+ * @param srcFile 视频路径
+ * @param targetFile 目标路径(以xxx.m3u8为输出)
+ * @param splitTime 切割时间 (单位:秒)
+ * @return 返回以target文件名开头的ts系列文件 如:out0.ts out1.ts ...
+ */
+ fun video2HLS(srcFile: String?, targetFile: String?, splitTime: Int): Array {
+ var command = "ffmpeg -y -i %s -c copy -bsf:v h264_mp4toannexb -hls_time %s %s"
+ command = String.format(command, srcFile, splitTime, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将ts视频流合成视频
+ * @param m3u8Index xx.m3u8视频索引
+ * @param targetFile 目标路径
+ * @return 返回合成视频
+ */
+ fun hls2Video(m3u8Index: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -c copy %s"
+ command = String.format(command, m3u8Index, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 将音频转为amr格式
+ * @param srcFile 音频源文件
+ * @param targetFile 目标文件
+ * @return amr格式音频
+ */
+ fun audio2Amr(srcFile: String?, targetFile: String?): Array {
+ var command = "ffmpeg -y -i %s -c:a libopencore_amrnb -ar 8000 -ac 1 %s"
+ command = String.format(command, srcFile, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+
+ /**
+ * 生成静音音频
+ * @param targetFile 目标文件
+ * @return 静音音频
+ */
+ fun makeMuteAudio(targetFile: String?): Array {
+ var command = "ffmpeg -y -f lavfi -t 10 -i anullsrc %s"
+ command = String.format(command, targetFile)
+ return command.split(" ").toTypedArray()
+ }
+}
\ No newline at end of file
diff --git a/ffmpeg/src/main/jniLibs/arm64-v8a/libffmpeg-invoke.so b/ffmpeg/src/main/jniLibs/arm64-v8a/libffmpeg-invoke.so
index 504190c..8c5db69 100755
Binary files a/ffmpeg/src/main/jniLibs/arm64-v8a/libffmpeg-invoke.so and b/ffmpeg/src/main/jniLibs/arm64-v8a/libffmpeg-invoke.so differ
diff --git a/ffmpeg/src/main/jniLibs/armeabi-v7a/libffmpeg-invoke.so b/ffmpeg/src/main/jniLibs/armeabi-v7a/libffmpeg-invoke.so
index 694f923..d0b23e6 100755
Binary files a/ffmpeg/src/main/jniLibs/armeabi-v7a/libffmpeg-invoke.so and b/ffmpeg/src/main/jniLibs/armeabi-v7a/libffmpeg-invoke.so differ
diff --git a/ffmpeg/src/test/java/com/coder/ffmpeg/ExampleUnitTest.java b/ffmpeg/src/test/java/com/coder/ffmpeg/ExampleUnitTest.java
deleted file mode 100644
index 6d16f66..0000000
--- a/ffmpeg/src/test/java/com/coder/ffmpeg/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.coder.ffmpeg;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/ffmpeg/src/test/java/com/coder/ffmpeg/ExampleUnitTest.kt b/ffmpeg/src/test/java/com/coder/ffmpeg/ExampleUnitTest.kt
new file mode 100644
index 0000000..8d39c16
--- /dev/null
+++ b/ffmpeg/src/test/java/com/coder/ffmpeg/ExampleUnitTest.kt
@@ -0,0 +1,16 @@
+package com.coder.ffmpeg
+
+import org.junit.Assert
+import org.junit.Test
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see [Testing documentation](http://d.android.com/tools/testing)
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ Assert.assertEquals(4, 2 + 2.toLong())
+ }
+}
\ No newline at end of file
diff --git a/images/zan.png b/images/zan.png
new file mode 100644
index 0000000..f812448
Binary files /dev/null and b/images/zan.png differ