瀏覽代碼

添加压缩视频功能;
屏蔽x86框架

zengjiebin 7 年之前
父節點
當前提交
a43d244cc5
共有 47 個文件被更改,包括 1492 次插入4 次删除
  1. 1 0
      .idea/gradle.xml
  2. 3 2
      app/build.gradle
  3. 2 1
      app/src/main/java/com/sheep/gamegroup/view/activity/ActPublishArticle.java
  4. 1 0
      joevideolib/.gitignore
  5. 36 0
      joevideolib/build.gradle
  6. 二進制
      joevideolib/libs/arm64-v8a/libavcodec.so
  7. 二進制
      joevideolib/libs/arm64-v8a/libavdevice.so
  8. 二進制
      joevideolib/libs/arm64-v8a/libavfilter.so
  9. 二進制
      joevideolib/libs/arm64-v8a/libavformat.so
  10. 二進制
      joevideolib/libs/arm64-v8a/libavutil.so
  11. 二進制
      joevideolib/libs/arm64-v8a/libjoe_ffmpeg.so
  12. 二進制
      joevideolib/libs/arm64-v8a/libpostproc.so
  13. 二進制
      joevideolib/libs/arm64-v8a/libswresample.so
  14. 二進制
      joevideolib/libs/arm64-v8a/libswscale.so
  15. 二進制
      joevideolib/libs/armeabi-v7a/libavcodec.so
  16. 二進制
      joevideolib/libs/armeabi-v7a/libavdevice.so
  17. 二進制
      joevideolib/libs/armeabi-v7a/libavfilter.so
  18. 二進制
      joevideolib/libs/armeabi-v7a/libavformat.so
  19. 二進制
      joevideolib/libs/armeabi-v7a/libavutil.so
  20. 二進制
      joevideolib/libs/armeabi-v7a/libjoe_ffmpeg.so
  21. 二進制
      joevideolib/libs/armeabi-v7a/libpostproc.so
  22. 二進制
      joevideolib/libs/armeabi-v7a/libswresample.so
  23. 二進制
      joevideolib/libs/armeabi-v7a/libswscale.so
  24. 二進制
      joevideolib/libs/armeabi/libavcodec.so
  25. 二進制
      joevideolib/libs/armeabi/libavdevice.so
  26. 二進制
      joevideolib/libs/armeabi/libavfilter.so
  27. 二進制
      joevideolib/libs/armeabi/libavformat.so
  28. 二進制
      joevideolib/libs/armeabi/libavutil.so
  29. 二進制
      joevideolib/libs/armeabi/libjoe_ffmpeg.so
  30. 二進制
      joevideolib/libs/armeabi/libpostproc.so
  31. 二進制
      joevideolib/libs/armeabi/libswresample.so
  32. 二進制
      joevideolib/libs/armeabi/libswscale.so
  33. 26 0
      joevideolib/proguard-rules.pro
  34. 16 0
      joevideolib/src/main/AndroidManifest.xml
  35. 18 0
      joevideolib/src/main/java/Jni/ColorUtils.java
  36. 77 0
      joevideolib/src/main/java/Jni/FFmpegCmd.java
  37. 75 0
      joevideolib/src/main/java/Jni/FileUtils.java
  38. 50 0
      joevideolib/src/main/java/Jni/TrackUtils.java
  39. 41 0
      joevideolib/src/main/java/Jni/VideoUitls.java
  40. 49 0
      joevideolib/src/main/java/VideoHandle/CmdList.java
  41. 75 0
      joevideolib/src/main/java/VideoHandle/EpDraw.java
  42. 653 0
      joevideolib/src/main/java/VideoHandle/EpEditor.java
  43. 59 0
      joevideolib/src/main/java/VideoHandle/EpText.java
  44. 290 0
      joevideolib/src/main/java/VideoHandle/EpVideo.java
  45. 16 0
      joevideolib/src/main/java/VideoHandle/OnEditorListener.java
  46. 3 0
      joevideolib/src/main/res/values/strings.xml
  47. 1 1
      settings.gradle

+ 1 - 0
.idea/gradle.xml

@@ -10,6 +10,7 @@
             <option value="$PROJECT_DIR$" />
             <option value="$PROJECT_DIR$/WaterWaveProgress" />
             <option value="$PROJECT_DIR$/app" />
+            <option value="$PROJECT_DIR$/joevideolib" />
             <option value="$PROJECT_DIR$/media/share_library" />
             <option value="$PROJECT_DIR$/ucrop" />
             <option value="$PROJECT_DIR$/view" />

+ 3 - 2
app/build.gradle

@@ -15,7 +15,7 @@ android {
         multiDexEnabled true
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
         ndk {
-            abiFilters "armeabi-v7a", 'x86'//, 'armeabi-v7a', 'x86_64', 'arm64-v8a'
+            abiFilters "armeabi-v7a"//, 'x86'//, 'armeabi-v7a', 'x86_64', 'arm64-v8a'
         }
 //        jackOptions {
 //            enabled true
@@ -427,7 +427,8 @@ dependencies {
     //fast json
     implementation 'com.alibaba:fastjson:1.2.52'
     implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.34'
-    implementation 'com.github.yangjie10930:EpMedia:v0.9.5'
+//    implementation 'com.github.yangjie10930:EpMedia:v0.9.5'
+    implementation project(':joevideolib')
 }
 
 static def releaseTime() {

+ 2 - 1
app/src/main/java/com/sheep/gamegroup/view/activity/ActPublishArticle.java

@@ -211,6 +211,7 @@ public class ActPublishArticle extends BaseActivity {
                     if(video.getOrientation() > 0) {
                         video.setOrientation(0);
                     }
+                    video.setFilePath(outVideoPath);
                     emitter.onComplete();
                 }
 
@@ -246,7 +247,7 @@ public class ActPublishArticle extends BaseActivity {
                 .subscribe(new AbsObserver<Float>() {
                     @Override
                     public void onNext(Float percent) {
-                        ViewUtil.setText(dialogProgress.getTextView(), String.format(Locale.CHINA, "%d%%", (int) (percent * 100)));
+//                        ViewUtil.setText(dialogProgress.getTextView(), String.format(Locale.CHINA, "%d%%", (int) (percent * 100)));
                     }
 
                     @Override

+ 1 - 0
joevideolib/.gitignore

@@ -0,0 +1 @@
+/build

+ 36 - 0
joevideolib/build.gradle

@@ -0,0 +1,36 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion ANDROID_COMPILE_SDK_VERSION as int
+    buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
+
+    defaultConfig {
+        minSdkVersion ANDROID_MIN_SDK_VERSION as int
+        targetSdkVersion ANDORID_TARGET_SDK_VERSION as int
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+        ndk {
+            abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
+        }
+
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    sourceSets {
+        main {
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation "com.android.support:support-annotations:$supportLibVersion"
+}

二進制
joevideolib/libs/arm64-v8a/libavcodec.so


二進制
joevideolib/libs/arm64-v8a/libavdevice.so


二進制
joevideolib/libs/arm64-v8a/libavfilter.so


二進制
joevideolib/libs/arm64-v8a/libavformat.so


二進制
joevideolib/libs/arm64-v8a/libavutil.so


二進制
joevideolib/libs/arm64-v8a/libjoe_ffmpeg.so


二進制
joevideolib/libs/arm64-v8a/libpostproc.so


二進制
joevideolib/libs/arm64-v8a/libswresample.so


二進制
joevideolib/libs/arm64-v8a/libswscale.so


二進制
joevideolib/libs/armeabi-v7a/libavcodec.so


二進制
joevideolib/libs/armeabi-v7a/libavdevice.so


二進制
joevideolib/libs/armeabi-v7a/libavfilter.so


二進制
joevideolib/libs/armeabi-v7a/libavformat.so


二進制
joevideolib/libs/armeabi-v7a/libavutil.so


二進制
joevideolib/libs/armeabi-v7a/libjoe_ffmpeg.so


二進制
joevideolib/libs/armeabi-v7a/libpostproc.so


二進制
joevideolib/libs/armeabi-v7a/libswresample.so


二進制
joevideolib/libs/armeabi-v7a/libswscale.so


二進制
joevideolib/libs/armeabi/libavcodec.so


二進制
joevideolib/libs/armeabi/libavdevice.so


二進制
joevideolib/libs/armeabi/libavfilter.so


二進制
joevideolib/libs/armeabi/libavformat.so


二進制
joevideolib/libs/armeabi/libavutil.so


二進制
joevideolib/libs/armeabi/libjoe_ffmpeg.so


二進制
joevideolib/libs/armeabi/libpostproc.so


二進制
joevideolib/libs/armeabi/libswresample.so


二進制
joevideolib/libs/armeabi/libswscale.so


+ 26 - 0
joevideolib/proguard-rules.pro

@@ -0,0 +1,26 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\Users\Administrator\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+#打印混淆信息
+-verbose
+#代码优化选项,不加该行会将没有用到的类删除,这里为了验证时间结果而使用,在实际生产环境中可根据实际需要选择是否使用
+-dontshrink
+-dontwarn android.support.annotation.Keep
+#保留注解,如果不添加改行会导致我们的@Keep注解失效
+-keepattributes *Annotation*
+-keep @android.support.annotation.Keep class **

+ 16 - 0
joevideolib/src/main/AndroidManifest.xml

@@ -0,0 +1,16 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.joe.joevideolib">
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+
+    <application android:allowBackup="true"
+                 android:label="@string/app_name"
+                 android:supportsRtl="true"
+    >
+
+    </application>
+
+</manifest>

+ 18 - 0
joevideolib/src/main/java/Jni/ColorUtils.java

@@ -0,0 +1,18 @@
+package Jni;
+
+/**
+ * Created by 杨杰 on 2017/4/19.
+ * 颜色格式转换的类
+ */
+
+public class ColorUtils {
+
+	/**
+	 * 加载所有相关链接库
+	 */
+	static {
+		System.loadLibrary("colorutils");
+	}
+
+	public static native byte[] rgb2yuvfloat(byte[] rgbs, int size, int width, int height);
+}

+ 77 - 0
joevideolib/src/main/java/Jni/FFmpegCmd.java

@@ -0,0 +1,77 @@
+package Jni;
+
+import android.support.annotation.Keep;
+
+import VideoHandle.OnEditorListener;
+
+/**
+ * Created by 杨杰 on 2017/2/14.
+ */
+
+@Keep
+public class FFmpegCmd {
+	/**
+	 * 加载所有相关链接库
+	 */
+	static {
+		System.loadLibrary("avutil");
+		System.loadLibrary("avcodec");
+		System.loadLibrary("swresample");
+		System.loadLibrary("avformat");
+		System.loadLibrary("swscale");
+		System.loadLibrary("avfilter");
+		System.loadLibrary("avdevice");
+		System.loadLibrary("joe_ffmpeg");
+	}
+
+	private static OnEditorListener listener;
+	private static long duration;
+
+	/**
+	 * 调用底层执行
+	 *
+	 * @param argc
+	 * @param argv
+	 * @return
+	 */
+	@Keep
+	public static native int exec(int argc, String[] argv);
+
+	@Keep
+	public static native void exit();
+
+	@Keep
+	public static void onExecuted(int ret) {
+		if (listener != null) {
+			if (ret == 0) {
+				listener.onProgress(1);
+				listener.onSuccess();
+			} else {
+				listener.onFailure();
+			}
+		}
+	}
+
+	@Keep
+	public static void onProgress(float progress) {
+		if (listener != null) {
+			if (duration != 0) {
+				listener.onProgress(progress / (duration / 1000000) * 0.95f);
+			}
+		}
+	}
+
+
+	/**
+	 * 执行ffmoeg命令
+	 *
+	 * @param cmds
+	 * @param listener
+	 */
+	@Keep
+	public static void exec(String[] cmds, long duration, OnEditorListener listener) {
+		FFmpegCmd.listener = listener;
+		FFmpegCmd.duration = duration;
+		exec(cmds.length, cmds);
+	}
+}

+ 75 - 0
joevideolib/src/main/java/Jni/FileUtils.java

@@ -0,0 +1,75 @@
+package Jni;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.util.List;
+
+/**
+ * Created by 杨杰 on 2017/2/20.
+ */
+
+public class FileUtils {
+
+	/**
+	 * 此类用于生成合并视频所需要的文档
+	 * @param strcontent 视频路径集合
+	 * @param filePath 生成的地址
+	 * @param fileName 生成的文件名
+	 */
+	public static void writeTxtToFile(List<String> strcontent, String filePath, String fileName) {
+		//生成文件夹之后,再生成文件,不然会出错
+		makeFilePath(filePath, fileName);
+		String strFilePath = filePath + fileName;
+		// 每次写入时,都换行写
+		String strContent = "";
+		for (int i = 0; i < strcontent.size(); i++) {
+			strContent += "file " + strcontent.get(i) + "\r\n";
+		}
+		try {
+			File file = new File(strFilePath);
+			//检查文件是否存在,存在则删除
+			if (file.isFile() && file.exists()) {
+				file.delete();
+			}
+			file.getParentFile().mkdirs();
+			file.createNewFile();
+			RandomAccessFile raf = new RandomAccessFile(file, "rwd");
+			raf.seek(file.length());
+			raf.write(strContent.getBytes());
+			raf.close();
+			Log.e("TestFile", "写入成功:" + strFilePath);
+		} catch (Exception e) {
+			Log.e("TestFile", "Error on write File:" + e);
+		}
+	}
+
+	//创建路径
+	public static File makeFilePath(String filePath, String fileName) {
+		File file = null;
+		makeRootDirectory(filePath);
+		try {
+			file = new File(filePath + fileName);
+			if (!file.exists()) {
+				file.createNewFile();
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return file;
+	}
+
+	//创建文件夹
+	public static void makeRootDirectory(String filePath) {
+		File file = null;
+		try {
+			file = new File(filePath);
+			if (!file.exists()) {
+				file.mkdir();
+			}
+		} catch (Exception e) {
+			Log.i("error:", e + "");
+		}
+	}
+}

+ 50 - 0
joevideolib/src/main/java/Jni/TrackUtils.java

@@ -0,0 +1,50 @@
+package Jni;
+
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.util.Log;
+
+/**
+ * Created by Yj on 2017/3/29.
+ */
+
+public class TrackUtils {
+
+	private static final String TAG = "TrackUtils";
+
+	/**
+	 * 查找视频轨道
+	 * @param extractor
+	 * @return
+	 */
+	public static int selectVideoTrack(MediaExtractor extractor) {
+		int numTracks = extractor.getTrackCount();
+		for (int i = 0; i < numTracks; i++) {
+			MediaFormat format = extractor.getTrackFormat(i);
+			String mime = format.getString(MediaFormat.KEY_MIME);
+			if (mime.startsWith("video/")) {
+				Log.d(TAG, "Extractor selected track " + i + " (" + mime + "): " + format);
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/**
+	 * 查找音频轨道
+	 * @param extractor
+	 * @return
+	 */
+	public static int selectAudioTrack(MediaExtractor extractor) {
+		int numTracks = extractor.getTrackCount();
+		for (int i = 0; i < numTracks; i++) {
+			MediaFormat format = extractor.getTrackFormat(i);
+			String mime = format.getString(MediaFormat.KEY_MIME);
+			if (mime.startsWith("audio/")) {
+				Log.d(TAG, "Extractor selected track " + i + " (" + mime + "): " + format);
+				return i;
+			}
+		}
+		return -1;
+	}
+}

+ 41 - 0
joevideolib/src/main/java/Jni/VideoUitls.java

@@ -0,0 +1,41 @@
+package Jni;
+
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+
+/**
+ * Created by 杨杰 on 2017/3/21.
+ * 获取音频或视频的信息
+ */
+
+public class VideoUitls {
+
+	private VideoUitls() {
+	}
+
+	/**
+	 * 获取视频信息
+	 *
+	 * @param url
+	 * @return	视频时长(单位微秒)
+	 */
+	public static long getDuration(String url) {
+		try {
+			MediaExtractor mediaExtractor = new MediaExtractor();
+			mediaExtractor.setDataSource(url);
+			int videoExt = TrackUtils.selectVideoTrack(mediaExtractor);
+			if(videoExt == -1){
+				videoExt = TrackUtils.selectAudioTrack(mediaExtractor);
+				if(videoExt == -1){
+					return 0;
+				}
+			}
+			MediaFormat mediaFormat = mediaExtractor.getTrackFormat(videoExt);
+			long res = mediaFormat.containsKey(MediaFormat.KEY_DURATION) ? mediaFormat.getLong(MediaFormat.KEY_DURATION) : 0;//时长
+			mediaExtractor.release();
+			return res;
+		} catch (Exception e) {
+			return 0;
+		}
+	}
+}

+ 49 - 0
joevideolib/src/main/java/VideoHandle/CmdList.java

@@ -0,0 +1,49 @@
+package VideoHandle;
+
+import java.util.ArrayList;
+
+/**
+ * Created by Yj on 2017/11/13.
+ * 命令集合
+ */
+
+public class CmdList extends ArrayList<String> {
+
+	public CmdList append(String s) {
+		this.add(s);
+		return this;
+	}
+
+	public CmdList append(int i) {
+		this.add(i + "");
+		return this;
+	}
+
+	public CmdList append(float f) {
+		this.add(f + "");
+		return this;
+	}
+
+	public CmdList append(StringBuilder sb) {
+		this.add(sb.toString());
+		return this;
+	}
+
+	public CmdList append(String[] ss) {
+		for (String s:ss) {
+			if(!s.replace(" ","").equals("")) {
+				this.add(s);
+			}
+		}
+		return this;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		for (String s : this) {
+			sb.append(" ").append(s);
+		}
+		return sb.toString();
+	}
+}

+ 75 - 0
joevideolib/src/main/java/VideoHandle/EpDraw.java

@@ -0,0 +1,75 @@
+package VideoHandle;
+
+/**
+ * 添加特效类
+ * Created by YangJie on 2017/5/23.
+ */
+
+public class EpDraw {
+
+	private String picPath;//图片路径
+	private int picX;//图片x的位置
+	private int picY;//图片y的位置
+	private float picWidth;//图片的宽
+	private float picHeight;//图片的高
+	private boolean isAnimation;//是否是动图
+
+	private String time = "";//起始结束时间
+
+	private String picFilter;//图片滤镜
+
+	public EpDraw(String picPath, int picX, int picY, float picWidth, float picHeight, boolean isAnimation) {
+		this.picPath = picPath;
+		this.picX = picX;
+		this.picY = picY;
+		this.picWidth = picWidth;
+		this.picHeight = picHeight;
+		this.isAnimation = isAnimation;
+	}
+
+	public EpDraw(String picPath, int picX, int picY, float picWidth, float picHeight, boolean isAnimation,int start,int end) {
+		this.picPath = picPath;
+		this.picX = picX;
+		this.picY = picY;
+		this.picWidth = picWidth;
+		this.picHeight = picHeight;
+		this.isAnimation = isAnimation;
+		time = ":enable=between(t\\," + start + "\\," + end + ")";
+	}
+
+	public String getPicPath() {
+		return picPath;
+	}
+
+	public int getPicX() {
+		return picX;
+	}
+
+	public int getPicY() {
+		return picY;
+	}
+
+	public float getPicWidth() {
+		return picWidth;
+	}
+
+	public float getPicHeight() {
+		return picHeight;
+	}
+
+	public boolean isAnimation() {
+		return isAnimation;
+	}
+
+	public String getPicFilter() {
+		return picFilter == null ? "" : (picFilter + ",");
+	}
+
+	public String getTime() {
+		return time;
+	}
+
+	public void setPicFilter(String picFilter) {
+		this.picFilter = picFilter;
+	}
+}

+ 653 - 0
joevideolib/src/main/java/VideoHandle/EpEditor.java

@@ -0,0 +1,653 @@
+package VideoHandle;
+
+import android.content.Context;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import Jni.FFmpegCmd;
+import Jni.FileUtils;
+import Jni.TrackUtils;
+import Jni.VideoUitls;
+
+/**
+ * 视频编辑器
+ * Created by YangJie on 2017/5/18.
+ */
+
+public class EpEditor {
+
+	private static final int DEFAULT_WIDTH = 480;//默认输出宽度
+	private static final int DEFAULT_HEIGHT = 360;//默认输出高度
+
+	public enum Format {
+		MP3, MP4
+	}
+
+	public enum PTS {
+		VIDEO, AUDIO, ALL
+	}
+
+	private EpEditor() {
+	}
+
+	/**
+	 * 处理单个视频
+	 *
+	 * @param epVideo      需要处理的视频
+	 * @param outputOption 输出选项配置
+	 */
+	public static void exec(EpVideo epVideo, OutputOption outputOption, OnEditorListener onEditorListener) {
+		boolean isFilter = false;
+		ArrayList<EpDraw> epDraws = epVideo.getEpDraws();
+		//开始处理
+		CmdList cmd = new CmdList();
+		cmd.append("ffmpeg");
+		cmd.append("-y");
+		if (epVideo.getVideoClip()) {
+			cmd.append("-ss").append(epVideo.getClipStart()).append("-t").append(epVideo.getClipDuration()).append("-accurate_seek");
+		}
+		cmd.append("-i").append(epVideo.getVideoPath());
+		//添加图片或者动图
+		if (epDraws.size() > 0) {
+			for (int i = 0; i < epDraws.size(); i++) {
+				if (epDraws.get(i).isAnimation()) {
+					cmd.append("-ignore_loop");
+					cmd.append(0);
+				}
+				cmd.append("-i").append(epDraws.get(i).getPicPath());
+			}
+			cmd.append("-filter_complex");
+			StringBuilder filter_complex = new StringBuilder();
+			filter_complex.append("[0:v]").append(epVideo.getFilters() != null ? epVideo.getFilters() + "," : "")
+					.append("scale=").append(outputOption.width == 0 ? "iw" : outputOption.width).append(":")
+					.append(outputOption.height == 0 ? "ih" : outputOption.height)
+					.append(outputOption.width == 0 ? "" : ",setdar=" + outputOption.getSar()).append("[outv0];");
+			for (int i = 0; i < epDraws.size(); i++) {
+				filter_complex.append("[").append(i + 1).append(":0]").append(epDraws.get(i).getPicFilter()).append("scale=").append(epDraws.get(i).getPicWidth()).append(":")
+						.append(epDraws.get(i).getPicHeight()).append("[outv").append(i + 1).append("];");
+			}
+			for (int i = 0; i < epDraws.size(); i++) {
+				if (i == 0) {
+					filter_complex.append("[outv").append(i).append("]").append("[outv").append(i + 1).append("]");
+				} else {
+					filter_complex.append("[outo").append(i - 1).append("]").append("[outv").append(i + 1).append("]");
+				}
+				filter_complex.append("overlay=").append(epDraws.get(i).getPicX()).append(":").append(epDraws.get(i).getPicY())
+						.append(epDraws.get(i).getTime());
+				if (epDraws.get(i).isAnimation()) {
+					filter_complex.append(":shortest=1");
+				}
+				if (i < epDraws.size() - 1) {
+					filter_complex.append("[outo").append(i).append("];");
+				}
+			}
+			cmd.append(filter_complex.toString());
+			isFilter = true;
+		} else {
+			StringBuilder filter_complex = new StringBuilder();
+			if (epVideo.getFilters() != null) {
+				cmd.append("-filter_complex");
+				filter_complex.append(epVideo.getFilters());
+				isFilter = true;
+			}
+			//设置输出分辨率
+			if (outputOption.width != 0) {
+				if (epVideo.getFilters() != null) {
+					filter_complex.append(",scale=").append(outputOption.width).append(":").append(outputOption.height)
+							.append(",setdar=").append(outputOption.getSar());
+				} else {
+					cmd.append("-filter_complex");
+					filter_complex.append("scale=").append(outputOption.width).append(":").append(outputOption.height)
+							.append(",setdar=").append(outputOption.getSar());
+					isFilter = true;
+				}
+			}
+			if (!filter_complex.toString().equals("")) {
+				cmd.append(filter_complex.toString());
+			}
+		}
+
+		//输出选项
+		cmd.append(outputOption.getOutputInfo().split(" "));
+		if (!isFilter && outputOption.getOutputInfo().isEmpty()) {
+			cmd.append("-vcodec");
+			cmd.append("copy");
+			cmd.append("-acodec");
+			cmd.append("copy");
+		} else {
+			cmd.append("-preset");
+			cmd.append("superfast");
+		}
+		cmd.append(outputOption.outPath);
+		long duration = VideoUitls.getDuration(epVideo.getVideoPath());
+		if (epVideo.getVideoClip()) {
+			long clipTime = (long) ((epVideo.getClipDuration() - epVideo.getClipStart()) * 1000000);
+			duration = clipTime < duration ? clipTime : duration;
+		}
+		//执行命令
+		execCmd(cmd, duration, onEditorListener);
+	}
+
+	/**
+	 * 合并多个视频
+	 *
+	 * @param epVideos     需要合并的视频集合
+	 * @param outputOption 输出选项配置
+	 */
+	public static void merge(List<EpVideo> epVideos, OutputOption outputOption, OnEditorListener onEditorListener) {
+		//检测是否有无音轨视频
+		boolean isNoAudioTrack = false;
+		for (EpVideo epVideo : epVideos) {
+			MediaExtractor mediaExtractor = new MediaExtractor();
+			try {
+				mediaExtractor.setDataSource(epVideo.getVideoPath());
+			} catch (IOException e) {
+				e.printStackTrace();
+				return;
+			}
+			int at = TrackUtils.selectAudioTrack(mediaExtractor);
+			if (at == -1) {
+				isNoAudioTrack = true;
+				mediaExtractor.release();
+				break;
+			}
+			mediaExtractor.release();
+		}
+		//设置默认宽高
+		outputOption.width = outputOption.width == 0 ? DEFAULT_WIDTH : outputOption.width;
+		outputOption.height = outputOption.height == 0 ? DEFAULT_HEIGHT : outputOption.height;
+		//判断数量
+		if (epVideos.size() > 1) {
+			CmdList cmd = new CmdList();
+			cmd.append("ffmpeg");
+			cmd.append("-y");
+			//添加输入标示
+			for (EpVideo e : epVideos) {
+				if (e.getVideoClip()) {
+					cmd.append("-ss").append(e.getClipStart()).append("-t").append(e.getClipDuration()).append("-accurate_seek");
+				}
+				cmd.append("-i").append(e.getVideoPath());
+			}
+			for (EpVideo e : epVideos) {
+				ArrayList<EpDraw> epDraws = e.getEpDraws();
+				if (epDraws.size() > 0) {
+					for (EpDraw ep : epDraws) {
+						if (ep.isAnimation()) cmd.append("-ignore_loop").append(0);
+						cmd.append("-i").append(ep.getPicPath());
+					}
+				}
+			}
+			//添加滤镜标识
+			cmd.append("-filter_complex");
+			StringBuilder filter_complex = new StringBuilder();
+			for (int i = 0; i < epVideos.size(); i++) {
+				StringBuilder filter = epVideos.get(i).getFilters() == null ? new StringBuilder("") : epVideos.get(i).getFilters().append(",");
+				filter_complex.append("[").append(i).append(":v]").append(filter).append("scale=").append(outputOption.width).append(":").append(outputOption.height)
+						.append(",setdar=").append(outputOption.getSar()).append("[outv").append(i).append("];");
+			}
+			//添加标记和处理宽高
+			int drawNum = epVideos.size();//图标计数器
+			for (int i = 0; i < epVideos.size(); i++) {
+				for (int j = 0; j < epVideos.get(i).getEpDraws().size(); j++) {
+					filter_complex.append("[").append(drawNum++).append(":0]").append(epVideos.get(i).getEpDraws().get(j).getPicFilter()).append("scale=")
+							.append(epVideos.get(i).getEpDraws().get(j).getPicWidth()).append(":").append(epVideos.get(i).getEpDraws().get(j)
+							.getPicHeight()).append("[p").append(i).append("a").append(j).append("];");
+				}
+			}
+			//添加图标操作
+			for (int i = 0; i < epVideos.size(); i++) {
+				for (int j = 0; j < epVideos.get(i).getEpDraws().size(); j++) {
+					filter_complex.append("[outv").append(i).append("][p").append(i).append("a").append(j).append("]overlay=")
+							.append(epVideos.get(i).getEpDraws().get(j).getPicX()).append(":")
+							.append(epVideos.get(i).getEpDraws().get(j).getPicY())
+							.append(epVideos.get(i).getEpDraws().get(j).getTime());
+					if (epVideos.get(i).getEpDraws().get(j).isAnimation()) {
+						filter_complex.append(":shortest=1");
+					}
+					filter_complex.append("[outv").append(i).append("];");
+				}
+			}
+			//开始合成视频
+			for (int i = 0; i < epVideos.size(); i++) {
+				filter_complex.append("[outv").append(i).append("]");
+			}
+			filter_complex.append("concat=n=").append(epVideos.size()).append(":v=1:a=0[outv]");
+			//是否添加音轨
+			if (!isNoAudioTrack) {
+				filter_complex.append(";");
+				for (int i = 0; i < epVideos.size(); i++) {
+					filter_complex.append("[").append(i).append(":a]");
+				}
+				filter_complex.append("concat=n=").append(epVideos.size()).append(":v=0:a=1[outa]");
+			}
+			if (!filter_complex.toString().equals("")) {
+				cmd.append(filter_complex.toString());
+			}
+			cmd.append("-map").append("[outv]");
+			if (!isNoAudioTrack) {
+				cmd.append("-map").append("[outa]");
+			}
+			cmd.append(outputOption.getOutputInfo().split(" "));
+			cmd.append("-preset").append("superfast").append(outputOption.outPath);
+			long duration = 0;
+			for (EpVideo ep : epVideos) {
+				long d = VideoUitls.getDuration(ep.getVideoPath());
+				if (ep.getVideoClip()) {
+					long clipTime = (long) ((ep.getClipDuration() - ep.getClipStart()) * 1000000);
+					d = clipTime < d ? clipTime : d;
+				}
+				if (d != 0) {
+					duration += d;
+				} else {
+					break;
+				}
+			}
+			//执行命令
+			execCmd(cmd, duration, onEditorListener);
+		} else {
+			throw new RuntimeException("Need more than one video");
+		}
+	}
+
+	/**
+	 * 无损合并多个视频
+	 * <p>
+	 * 注意:此方法要求视频格式非常严格,需要合并的视频必须分辨率相同,帧率和码率也得相同
+	 *
+	 * @param context          Context
+	 * @param epVideos         需要合并的视频的集合
+	 * @param outputOption     输出选项
+	 * @param onEditorListener 回调监听
+	 */
+	public static void mergeByLc(Context context, List<EpVideo> epVideos, OutputOption outputOption, final OnEditorListener onEditorListener) {
+		String appDir = context.getFilesDir().getAbsolutePath() + "/EpVideos/";
+		String fileName = "ffmpeg_concat.txt";
+		List<String> videos = new ArrayList<>();
+		for (EpVideo e : epVideos) {
+			videos.add(e.getVideoPath());
+		}
+		FileUtils.writeTxtToFile(videos, appDir, fileName);
+		CmdList cmd = new CmdList();
+		cmd.append("ffmpeg").append("-y").append("-f").append("concat").append("-safe")
+				.append("0").append("-i").append(appDir + fileName)
+				.append("-c").append("copy").append(outputOption.outPath);
+		long duration = 0;
+		for (EpVideo ep : epVideos) {
+			long d = VideoUitls.getDuration(ep.getVideoPath());
+			if (d != 0) {
+				duration += d;
+			} else {
+				break;
+			}
+		}
+		execCmd(cmd, duration, onEditorListener);
+	}
+
+	/**
+	 * 添加背景音乐
+	 *
+	 * @param videoin          视频文件
+	 * @param audioin          音频文件
+	 * @param output           输出路径
+	 * @param videoVolume      视频原声音音量(例:0.7为70%)
+	 * @param audioVolume      背景音乐音量(例:1.5为150%)
+	 * @param onEditorListener 回调监听
+	 */
+	public static void music(String videoin, String audioin, String output, float videoVolume, float audioVolume, OnEditorListener onEditorListener) {
+		MediaExtractor mediaExtractor = new MediaExtractor();
+		try {
+			mediaExtractor.setDataSource(videoin);
+		} catch (IOException e) {
+			e.printStackTrace();
+			return;
+		}
+		int at = TrackUtils.selectAudioTrack(mediaExtractor);
+		CmdList cmd = new CmdList();
+		cmd.append("ffmpeg").append("-y").append("-i").append(videoin);
+		if (at == -1) {
+			int vt = TrackUtils.selectVideoTrack(mediaExtractor);
+			float duration = (float) mediaExtractor.getTrackFormat(vt).getLong(MediaFormat.KEY_DURATION) / 1000 / 1000;
+			cmd.append("-ss").append("0").append("-t").append(duration).append("-i").append(audioin).append("-acodec").append("copy").append("-vcodec").append("copy");
+		} else {
+			cmd.append("-i").append(audioin).append("-filter_complex")
+					.append("[0:a]aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo,volume=" + videoVolume + "[a0];[1:a]aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo,volume=" + audioVolume + "[a1];[a0][a1]amix=inputs=2:duration=first[aout]")
+					.append("-map").append("[aout]").append("-ac").append("2").append("-c:v")
+					.append("copy").append("-map").append("0:v:0");
+		}
+		cmd.append(output);
+		mediaExtractor.release();
+		long d = VideoUitls.getDuration(videoin);
+		execCmd(cmd, d, onEditorListener);
+	}
+
+	/**
+	 * 音视频分离
+	 *
+	 * @param videoin          视频文件
+	 * @param out              输出文件路径
+	 * @param format           输出类型
+	 * @param onEditorListener 回调监听
+	 */
+	public static void demuxer(String videoin, String out, Format format, OnEditorListener onEditorListener) {
+		CmdList cmd = new CmdList();
+		cmd.append("ffmpeg").append("-y").append("-i").append(videoin);
+		switch (format) {
+			case MP3:
+				cmd.append("-vn").append("-acodec").append("libmp3lame");
+				break;
+			case MP4:
+				cmd.append("-vcodec").append("copy").append("-an");
+				break;
+		}
+		cmd.append(out);
+		long d = VideoUitls.getDuration(videoin);
+		execCmd(cmd, d, onEditorListener);
+	}
+
+	/**
+	 * 音视频倒放
+	 *
+	 * @param videoin          视频文件
+	 * @param out              输出文件路径
+	 * @param vr               是否视频倒放
+	 * @param ar               是否音频倒放
+	 * @param onEditorListener 回调监听
+	 */
+	public static void reverse(String videoin, String out, boolean vr, boolean ar, OnEditorListener onEditorListener) {
+		if (!vr && !ar) {
+			Log.e("ffmpeg", "parameter error");
+			onEditorListener.onFailure();
+			return;
+		}
+		CmdList cmd = new CmdList();
+		cmd.append("ffmpeg").append("-y").append("-i").append(videoin).append("-filter_complex");
+		String filter = "";
+		if (vr) {
+			filter += "[0:v]reverse[v];";
+		}
+		if (ar) {
+			filter += "[0:a]areverse[a];";
+		}
+		cmd.append(filter.substring(0, filter.length() - 1));
+		if (vr) {
+			cmd.append("-map").append("[v]");
+		}
+		if (ar) {
+			cmd.append("-map").append("[a]");
+		}
+		if (ar && !vr) {
+			cmd.append("-acodec").append("libmp3lame");
+		}
+		cmd.append("-preset").append("superfast").append(out);
+		long d = VideoUitls.getDuration(videoin);
+		execCmd(cmd, d, onEditorListener);
+	}
+
+	/**
+	 * 音视频变速
+	 *
+	 * @param videoin          音视频文件
+	 * @param out              输出路径
+	 * @param times            倍率(调整范围0.25-4)
+	 * @param pts              加速类型
+	 * @param onEditorListener 回调接口
+	 */
+	public static void changePTS(String videoin, String out, float times, PTS pts, OnEditorListener onEditorListener) {
+		if (times < 0.25f || times > 4.0f) {
+			Log.e("ffmpeg", "times can only be 0.25 to 4");
+			onEditorListener.onFailure();
+			return;
+		}
+		CmdList cmd = new CmdList();
+		cmd.append("ffmpeg").append("-y").append("-i").append(videoin);
+		String t = "atempo=" + times;
+		if (times < 0.5f) {
+			t = "atempo=0.5,atempo=" + (times / 0.5f);
+		} else if (times > 2.0f) {
+			t = "atempo=2.0,atempo=" + (times / 2.0f);
+		}
+		Log.v("ffmpeg", "atempo:" + t);
+		switch (pts) {
+			case VIDEO:
+				cmd.append("-filter_complex").append("[0:v]setpts=" + (1 / times) + "*PTS").append("-an");
+				break;
+			case AUDIO:
+				cmd.append("-filter:a").append(t);
+				break;
+			case ALL:
+				cmd.append("-filter_complex").append("[0:v]setpts=" + (1 / times) + "*PTS[v];[0:a]" + t + "[a]")
+						.append("-map").append("[v]").append("-map").append("[a]");
+				break;
+		}
+		cmd.append("-preset").append("superfast").append(out);
+		long d = VideoUitls.getDuration(videoin);
+		double dd = d / times;
+		long ddd = (long) dd;
+		execCmd(cmd, ddd, onEditorListener);
+	}
+
+	/**
+	 * 视频转图片
+	 *
+	 * @param videoin			音视频文件
+	 * @param out				输出路径
+	 * @param w					输出图片宽度
+	 * @param h					输出图片高度
+	 * @param rate				每秒视频生成图片数
+	 * @param onEditorListener	回调接口
+	 */
+	public static void video2pic(String videoin, String out, int w, int h, float rate, OnEditorListener onEditorListener) {
+		if (w <= 0 || h <= 0) {
+			Log.e("ffmpeg", "width and height must greater than 0");
+			onEditorListener.onFailure();
+			return;
+		}
+		if(rate <= 0){
+			Log.e("ffmpeg", "rate must greater than 0");
+			onEditorListener.onFailure();
+			return;
+		}
+		CmdList cmd = new CmdList();
+		cmd.append("ffmpeg").append("-y").append("-i").append(videoin)
+				.append("-r").append(rate).append("-s").append(w+"x"+h).append("-q:v").append(2)
+				.append("-f").append("image2").append("-preset").append("superfast").append(out);
+		long d = VideoUitls.getDuration(videoin);
+		execCmd(cmd, d, onEditorListener);
+	}
+
+	/**
+	 * 图片转视频
+	 *
+	 * @param videoin			视频文件
+	 * @param out				输出路径
+	 * @param w					输出视频宽度
+	 * @param h					输出视频高度
+	 * @param rate				输出视频帧率
+	 * @param onEditorListener	回调接口
+	 */
+	public static void pic2video(String videoin, String out, int w, int h, float rate, OnEditorListener onEditorListener) {
+		if (w < 0 || h < 0) {
+			Log.e("ffmpeg", "width and height must greater than 0");
+			onEditorListener.onFailure();
+			return;
+		}
+		if(rate <= 0){
+			Log.e("ffmpeg", "rate must greater than 0");
+			onEditorListener.onFailure();
+			return;
+		}
+		CmdList cmd = new CmdList();
+		cmd.append("ffmpeg").append("-y").append("-f").append("image2").append("-i").append(videoin)
+				.append("-vcodec").append("libx264")
+				.append("-r").append(rate);
+//				.append("-b").append("10M");
+				if(w > 0 && h > 0) {
+					cmd.append("-s").append(w + "x" + h);
+				}
+		cmd.append(out);
+		long d = VideoUitls.getDuration(videoin);
+		execCmd(cmd, d, onEditorListener);
+	}
+
+
+	/**
+	 * 输出选项设置
+	 */
+	public static class OutputOption {
+		static final int ONE_TO_ONE = 1;// 1:1
+		static final int FOUR_TO_THREE = 2;// 4:3
+		static final int SIXTEEN_TO_NINE = 3;// 16:9
+		static final int NINE_TO_SIXTEEN = 4;// 9:16
+		static final int THREE_TO_FOUR = 5;// 3:4
+
+		String outPath;//输出路径
+		public int frameRate = 0;//帧率
+		public int bitRate = 0;//比特率(一般设置10M)
+		public String outFormat = "";//输出格式(目前暂时只支持mp4,x264,mp3,gif)
+		private int width = 0;//输出宽度
+		private int height = 0;//输出高度
+		private int sar = 6;//输出宽高比
+
+		public OutputOption(String outPath) {
+			this.outPath = outPath;
+		}
+
+		/**
+		 * 获取宽高比
+		 *
+		 * @return 1
+		 */
+		public String getSar() {
+			String res;
+			switch (sar) {
+				case ONE_TO_ONE:
+					res = "1/1";
+					break;
+				case FOUR_TO_THREE:
+					res = "4/3";
+					break;
+				case THREE_TO_FOUR:
+					res = "3/4";
+					break;
+				case SIXTEEN_TO_NINE:
+					res = "16/9";
+					break;
+				case NINE_TO_SIXTEEN:
+					res = "9/16";
+					break;
+				default:
+					res = width + "/" + height;
+					break;
+			}
+			return res;
+		}
+
+		public void setSar(int sar) {
+			this.sar = sar;
+		}
+
+		/**
+		 * 获取输出信息
+		 *
+		 * @return 1
+		 */
+		String getOutputInfo() {
+			StringBuilder res = new StringBuilder();
+			if (frameRate != 0) {
+				res.append(" -r ").append(frameRate);
+			}
+			if (bitRate != 0) {
+				res.append(" -b ").append(bitRate).append("M");
+			}
+			if (!outFormat.isEmpty()) {
+				res.append(" -f ").append(outFormat);
+			}
+			return res.toString();
+		}
+
+		/**
+		 * 设置宽度
+		 *
+		 * @param width 宽
+		 */
+		public void setWidth(int width) {
+			if (width % 2 != 0) width -= 1;
+			this.width = width;
+		}
+
+		/**
+		 * 设置高度
+		 *
+		 * @param height 高
+		 */
+		public void setHeight(int height) {
+			if (height % 2 != 0) height -= 1;
+			this.height = height;
+		}
+	}
+
+	/**
+	 * 开始处理
+	 *
+	 * @param cmd              命令
+	 * @param duration         视频时长(单位微秒)
+	 * @param onEditorListener 回调接口
+	 */
+	public static void execCmd(String cmd, long duration, final OnEditorListener onEditorListener) {
+		cmd = "ffmpeg " + cmd;
+		String[] cmds = cmd.split(" ");
+		FFmpegCmd.exec(cmds, duration, new OnEditorListener() {
+			@Override
+			public void onSuccess() {
+				onEditorListener.onSuccess();
+			}
+
+			@Override
+			public void onFailure() {
+				onEditorListener.onFailure();
+			}
+
+			@Override
+			public void onProgress(final float progress) {
+				onEditorListener.onProgress(progress);
+			}
+		});
+	}
+
+	/**
+	 * 开始处理
+	 *
+	 * @param cmd              命令
+	 * @param duration         视频时长(单位微秒)
+	 * @param onEditorListener 回调接口
+	 */
+	private static void execCmd(CmdList cmd, long duration, final OnEditorListener onEditorListener) {
+		String[] cmds = cmd.toArray(new String[cmd.size()]);
+		String cmdLog = "";
+		for (String ss : cmds) {
+			cmdLog += cmds;
+		}
+		Log.v("EpMediaF", "cmd:" + cmdLog);
+		FFmpegCmd.exec(cmds, duration, new OnEditorListener() {
+			@Override
+			public void onSuccess() {
+				onEditorListener.onSuccess();
+			}
+
+			@Override
+			public void onFailure() {
+				onEditorListener.onFailure();
+			}
+
+			@Override
+			public void onProgress(final float progress) {
+				onEditorListener.onProgress(progress);
+			}
+		});
+	}
+}

+ 59 - 0
joevideolib/src/main/java/VideoHandle/EpText.java

@@ -0,0 +1,59 @@
+package VideoHandle;
+
+/**
+ * Created by Administrator on 2017/11/8.
+ */
+
+public class EpText {
+
+	private String textFitler;
+
+	/**
+	 * @param x     文字起始位置X
+	 * @param y     文字起始位置Y
+	 * @param size  文字的大小
+	 * @param color 文字的颜色
+	 * @param ttf   文字的字体文件路径
+	 * @param text  添加文字的内容
+	 * @param time  起始结束时间(传null的时候为一直显示)
+	 */
+	public EpText(int x, int y, float size, Color color, String ttf, String text, Time time) {
+		this.textFitler = "drawtext=fontfile=" + ttf + ":fontsize=" + size + ":fontcolor=" + color.getColor() + ":x=" + x + ":y=" + y + ":text='" + text + "'" + (time == null ? "" : time.getTime());
+	}
+
+	public String getTextFitler() {
+		return textFitler;
+	}
+
+	/**
+	 * 起始结束时间的类
+	 */
+	public static class Time {
+		private String time;
+
+		public Time(int start, int end) {
+			this.time = ":enable=between(t\\," + start + "\\," + end + ")";
+		}
+
+		public String getTime() {
+			return time;
+		}
+	}
+
+	/**
+	 * 颜色
+	 */
+	public enum Color {
+		Red("Red"), Blue("Blue"), Yellow("Yellow"), Black("Black"), DarkBlue("DarkBlue"),
+		Green("Green"), SkyBlue("SkyBlue"), Orange("Orange"), White("White"), Cyan("Cyan");
+		private String color;
+
+		Color(String color) {
+			this.color = color;
+		}
+
+		public String getColor() {
+			return color;
+		}
+	}
+}

+ 290 - 0
joevideolib/src/main/java/VideoHandle/EpVideo.java

@@ -0,0 +1,290 @@
+package VideoHandle;
+
+import java.util.ArrayList;
+
+/**
+ * 视频处理类
+ * Created by YangJie on 2017/5/18.
+ */
+
+public class EpVideo {
+
+	private String videoPath;  //视频地址
+
+	//剪辑
+	private boolean isClip = false;//是否剪辑
+	private float clipStart;//剪辑开始时间
+	private float clipDuration;//剪辑时间
+
+	//滤镜
+	private StringBuilder filter;
+
+	//特效
+	private ArrayList<EpDraw> epPics;
+
+	//裁剪
+	private Crop mCrop;
+
+
+	public EpVideo(String videoPath) {
+		this.videoPath = videoPath;
+		epPics = new ArrayList<>();
+	}
+
+	private StringBuilder getFilter() {
+		if (filter == null || filter.toString().equals("")) {
+			filter = new StringBuilder();
+		} else {
+			filter.append(",");
+		}
+		return filter;
+	}
+
+	/**
+	 * 获取滤镜效果
+	 *
+	 * @return
+	 */
+	public StringBuilder getFilters() {
+		return filter;
+	}
+
+	/**
+	 * 获取视频路径
+	 *
+	 * @return
+	 */
+	public String getVideoPath() {
+		return videoPath;
+	}
+
+	/**
+	 * 获取剪辑信息
+	 *
+	 * @return
+	 */
+	public boolean getVideoClip() {
+		return isClip;
+	}
+
+	/**
+	 * 获取剪辑起始时间
+	 *
+	 * @return
+	 */
+	public float getClipStart() {
+		return clipStart;
+	}
+
+	/**
+	 * 获取剪辑持续时间
+	 *
+	 * @return
+	 */
+	public float getClipDuration() {
+		return clipDuration;
+	}
+
+	/**
+	 * 设置视频剪辑
+	 *
+	 * @param start    起始时间,单位秒
+	 * @param duration 持续时间,单位秒
+	 * @return
+	 */
+	public EpVideo clip(float start, float duration) {
+		isClip = true;
+		this.clipStart = start;
+		this.clipDuration = duration;
+		return this;
+	}
+
+
+	/**
+	 * 设置旋转和镜像
+	 *
+	 * @param rotation 旋转角度(仅支持90,180,270度旋转)
+	 * @param isFlip   是否镜像
+	 * @return
+	 */
+	public EpVideo rotation(int rotation, boolean isFlip) {
+		filter = getFilter();
+		if (isFlip) {
+			switch (rotation) {
+				case 0:
+					filter.append("hflip");
+					break;
+				case 90:
+					filter.append("transpose=3");
+					break;
+				case 180:
+					filter.append("vflip");
+					break;
+				case 270:
+					filter.append("transpose=0");
+					break;
+			}
+		} else {
+			switch (rotation) {
+				case 90:
+					filter.append("transpose=2");
+					break;
+				case 180:
+					filter.append("vflip,hflip");
+					break;
+				case 270:
+					filter.append("transpose=1");
+					break;
+			}
+		}
+		return this;
+	}
+
+	/**
+	 * 设置裁剪
+	 *
+	 * @param width  裁剪宽度
+	 * @param height 裁剪高度
+	 * @param x      起始位置X
+	 * @param y      起始位置Y
+	 * @return
+	 */
+	public EpVideo crop(float width, float height, float x, float y) {
+		filter = getFilter();
+		mCrop = new Crop(width,height,x,y);
+		filter.append("crop=" + width + ":" + height + ":" + x + ":" + y);
+		return this;
+	}
+
+	/**
+	 * 获取裁剪信息
+	 * @return
+	 */
+	public Crop getCrop(){
+		return mCrop;
+	}
+
+	/**
+	 * 为视频添加文字
+	 *
+	 * @param size  文字大小
+	 * @param color 文字颜色(white,black,blue,red...)
+	 * @param x     文字的x坐标
+	 * @param y     文字的y坐标
+	 * @param ttf   文字字体的路径
+	 * @param text  添加的文字
+	 * @deprecated 废弃,采用EpText参数
+	 */
+	@Deprecated
+	public EpVideo addText(int x, int y, float size, String color, String ttf, String text) {
+		filter = getFilter();
+		filter.append("drawtext=fontfile=" + ttf + ":fontsize=" + size + ":fontcolor=" + color + ":x=" + x + ":y=" + y + ":text='" + text + "'");
+		return this;
+	}
+
+	/**
+	 * 为视频添加文字(新增可以控制显示周期)
+	 *
+	 * @param epText  添加文字类
+	 */
+	public EpVideo addText(EpText epText) {
+		filter = getFilter();
+		filter.append(epText.getTextFitler());
+		return this;
+	}
+
+	/**
+	 * 为视频添加时间
+	 *
+	 * @param size  文字大小
+	 * @param color 文字颜色(white,black,blue,red...)
+	 * @param x     文字的x坐标
+	 * @param y     文字的y坐标
+	 * @param ttf   文字字体的路径
+	 * @param type  时间类型(1==>hh:mm:ss,2==>yyyy-MM-dd hh:mm:ss,3==>yyyy年MM月dd日 hh时mm分ss秒)
+	 */
+	public EpVideo addTime(int x, int y, float size, String color, String ttf,int type){
+		long time=System.currentTimeMillis()/1000;
+		String  str=String.valueOf(time);
+		filter = getFilter();
+		String ts = "";
+		switch (type){
+			case 1:
+				ts = "%{pts\\:localtime\\:" + str + "\\:%H\\\\\\:%M\\\\\\:%S}";
+				break;
+			case 2:
+				ts = "%{pts\\:localtime\\:" + str + "}";
+				break;
+			case 3:
+				ts = "%{pts\\:localtime\\:" + str + "\\:%Y\\\\年%m\\\\月%d\\\\日\n%H\\\\\\时%M\\\\\\分%S秒}";
+				break;
+		}
+		filter.append("drawtext=fontfile=" + ttf + ":fontsize=" + size + ":fontcolor=" + color + ":x=" + x + ":y=" + y + ":text='"+ts+"'");
+		return this;
+	}
+
+	/**
+	 * 添加自定义滤镜效果
+	 *
+	 * @param ofi 命令符
+	 * @return
+	 */
+	public EpVideo addFilter(String ofi) {
+		filter = getFilter();
+		filter.append(ofi);
+		return this;
+	}
+
+	/**
+	 * 为视频添加图片
+	 *
+	 * @param epDraw 添加的图片类
+	 * @return
+	 */
+	public EpVideo addDraw(EpDraw epDraw) {
+		epPics.add(epDraw);
+		return this;
+	}
+
+	/**
+	 * 获取添加的图片类
+	 *
+	 * @return
+	 */
+	public ArrayList<EpDraw> getEpDraws() {
+		return epPics;
+	}
+
+	/**
+	 * 裁剪信息类
+	 */
+	public class Crop {
+		float width;
+		float height;
+		float x;
+		float y;
+
+		public Crop(float width, float height, float x, float y) {
+			this.width = width;
+			this.height = height;
+			this.x = x;
+			this.y = y;
+		}
+
+		public float getWidth() {
+			return width;
+		}
+
+		public float getHeight() {
+			return height;
+		}
+
+		public float getX() {
+			return x;
+		}
+
+		public float getY() {
+			return y;
+		}
+	}
+}

+ 16 - 0
joevideolib/src/main/java/VideoHandle/OnEditorListener.java

@@ -0,0 +1,16 @@
+package VideoHandle;
+
+/**
+ * Created by YangJie on 2017/5/18.
+ */
+
+/**
+ * 执行完成/错误 时的回调接口
+ */
+public interface OnEditorListener {
+	void onSuccess();
+
+	void onFailure();
+
+	void onProgress(float progress);
+}

+ 3 - 0
joevideolib/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">JoeVideoLib</string>
+</resources>

+ 1 - 1
settings.gradle

@@ -1,3 +1,3 @@
-include ':app', ':view', ':ucrop', ':WaterWaveProgress', ':media_share_lib'//, ':RxGalleryFinal', ':Aria', ':datashare', ':AriaAnnotations'
+include ':app', ':view', ':ucrop', ':WaterWaveProgress', ':media_share_lib', ':joevideolib'//, ':RxGalleryFinal', ':Aria', ':datashare', ':AriaAnnotations'
 
 project(':media_share_lib').projectDir = new File('media/share_library')