前言
最近在学习FFmpeg和音视频的相关知识,为了加强对FFmpeg的认识和了解,于是撸了一个短视频编辑软件Cut。
效果图先行:
技术点
启动页优化
但启动app的时候会有一个短暂的黑屏或者白屏。为什么呢? 是因为在App启动时,系统会执行3个Task:
1、 加载并启动app
2、在app启动后,立即展示空白的window
3、创建app进程
一旦app进程完成了第一次绘制,系统进程就会用main activity替换已经展示的background window。之后用户才可以使用app。
这个空白的window就是导致白屏或者黑屏的罪魁祸首。怎么解决呢? 1.定义透明的主题,parent中的AppTheme为APP的主题
<style name="Theme.AppStartLoadTranslucent" parent="AppTheme"> <item name="android:windowIsTranslucent">true</item> <item name="android:windowNoTitle">true</item> </style>
<!-- 启动界面 --> <activity android:name=".ui.LaunchActivity" android:launchMode="singleTask" android:theme="@style/Theme.AppStartLoadTranslucent"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
启动页优化原理
增量更新和全量更新
在App用了增量更新。
增量更新:增量更新是指在进行更新操作时,只更新需要改变的地方,不需要更新或者已经更新过的地方则不会重复更新,增量更新与全量更新相对。
使用的是bsdiff、 在bspatch中还会用到bzip2.
增量更新的流程:下载差分包,手机上的apk和差很包合并形成新的apk,然后再次安装。
DownloadUtil.get().download(appPath, savePath, saveName,new DownloadUtil.OnDownloadListener() {@Overridepublic void onDownloadSuccess(File file) {if(file != null){mProgressDialog.dismiss();LogUtil.e("tag", "---path = " + file.getAbsolutePath());if(update_type == 1){//获取当前应用的apk文件/data/app/appString oldFile = Utils.getSourceApkPath(LaunchActivity.this, getPackageName());//2.合并得到最新版本的APK文件String newApkPath = MApplication.VIDEO_PATH+"meger.apk";//下载差分包的地址String patchFileAbsolutePath = file.getAbsolutePath();LogUtil.e(TAG, "oldfile:"+oldFile);LogUtil.e(TAG, "newfile:"+newApkPath);LogUtil.e(TAG, "patchfile:"+patchFileAbsolutePath);//jni调用baspatch old.APK 和 差分包 合成新的apkBspatchNDK.bspatch(oldFile, newApkPath, patchFileAbsolutePath);//再次安装Utils.installApk(LaunchActivity.this,newApkPath);}else if(update_type == 2){Utils.installApk(LaunchActivity.this,file);}}}@Overridepublic void onDownloading(int progress) {mProgressDialog.setProgress(progress);}@Overridepublic void onDownloadFailed() {mProgressDialog.dismiss();}});
这里会有一个问题?这个差分包,是什么版本和新版本的差分包?我这里是这样处理的:假如市场发布了1.0.0
、1.0.1
、1.0.2
,最新版本为1.0.3
.
差分包patch是:1.0.2
和1.0.3
生成的差分包。
因此:当且仅有版本为1.0.2
(前一个版本),才能进行增量更新,1.0.2
之前的(前一个版本之前的)都需要全量更新。所以在代码中有这样的一段判断:
if (MApplication.getUpgradeinfo().versionCode - Utils.getVerCode(this) == 1) {//前一个版本//增量更新//有新版本hasNewVersion = true;update_type = 1;apkUrl = MApplication.QINIU_ADDRESS + "diff-"+MApplication.getUpgradeinfo().versionCode+".patch";}else if(MApplication.getUpgradeinfo().versionCode - Utils.getVerCode(this) > 1){//全量更新hasNewVersion = true;apkUrl = MApplication.getUpgradeinfo().apkUrl;update_type = 2;} else {update_type = 0;hasNewVersion = false;toHome3Second();}
差分包怎么生成?下载了bsdiff,调用命令即可:(我这里是 Mac OS下执行的)
bsdiff old.apk new.apk diff.patch
然后然后就是差分包和旧的apk在Android如何合成的问题了。因为如何在Android使用bspacth,还得需要如何把bapacth引入Android Studio。 所以新开了一篇文章介绍,可以看这里。
ffmpeg命令行使用
FFmpeg的使用整个项目的重点,大部分的功能都需要它。而在之前的一篇文章中有介绍如何编译FFmpeg并且引入Android Studio 使用, 如何在Android 中使用FFmpeg命令。
ffmpeg命令
在项目中,使用的命令有:改变视频的速度,改变视频的分辨率,视频和视频的连接,视频和图片的合成,视频的剪辑。
【免费分享】音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击788280672加群免费领取~
改变视频的速度
点击分镜,会弹出一个popup可以选择分镜播放的速度
/*** 改变视频的速度的ffmpeg命令 atempo【0.5,2】* ffmpeg -i input.mkv -filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]" -map "[v]" -map "[a]" output.mkv* @param videoPath 输入录像* @param outPath 输出路径* @param speed 速度* @return*/private String getSpeedCommandStr(String videoPath, float speed, String outPath) {if (TextUtils.isEmpty(videoPath) || TextUtils.isEmpty(outPath)) {return null;}String filter = String.format(Locale.getDefault(), "[0:v]setpts=%f*PTS[v];[0:a]atempo=%f[a]", 1/speed, speed);StringBuilder sb = new StringBuilder("ffmpeg");sb.append(" -i");sb.append(" "+videoPath);sb.append(" -filter_complex");sb.append(" "+filter);sb.append(" -map");sb.append(" [v]");sb.append(" -map");sb.append(" [a]");sb.append(" -b:v 3000k -g 25");sb.append(" -y");sb.append(" "+outPath);LogUtil.d(TAG,"------- cmd = " + sb.toString());return sb.toString();}
视频和视频的连接
在项目中共有3个分镜头,最后需要把这三个分镜合成一个完整的视频:
/*** 合成视频命令* ffmpeg -f concat -i filelist.txt -c copy output.mkv* @param path* @return-vcodec libx264*/private String getComplexVideoCmd(String fileList,String path) {StringBuilder builder = new StringBuilder();builder.append("ffmpeg -f concat -safe 0 -i ");builder.append(fileList);
// builder.append(" -b:v 4000K -b:a 96K ");
// builder.append("-profile:v baseline -preset ultrafast ");
// builder.append(" -b:v 1500K -b:a 48K -f mp4 ");builder.append(" ");builder.append(path);LogUtil.d(TAG,"----- 合成视频命令 = " + builder.toString());return builder.toString();}
在视频连接的时候会有一个坑,因为在fileList.txt里面写入的路径是绝对路径,在使用ffmpeg 命令连接视频的时候,会报 Operation not permitted
的错误。加上-safe 0
就可以解决了。
改变视频的分辨率
每一分镜的视频来源有可能是录制的,也可能是选择本地视频剪辑一部分的,因此分辨率和码率都会各部相同,就对每一分镜统一成相同分辨率和码率,如果不统一,不然会在视频连接的生成的视频会丢帧的厉害。
/*** 改变视频分辨率的命令* @param videoPath* @return*/private String getChangeVideoSizeCmd(String videoPath,String outPath) {if (TextUtils.isEmpty(videoPath) || TextUtils.isEmpty(outPath)) {return null;}StringBuilder builder = new StringBuilder();builder.append("ffmpeg -y -i ");builder.append(videoPath);builder.append(" -vf scale=1080:1920 -r 25 ");builder.append(outPath);LogUtil.e(TAG, "----- 改变视频size 命令 = " + builder.toString());return builder.toString();}
视频的剪辑
对本地的视频剪辑出其中的一部分 ,现在固定3s。
StringBuilder builder = new StringBuilder();builder.append("ffmpeg -ss ");builder.append(start);builder.append(" -t ");builder.append(duration);builder.append(" -i ");builder.append(inputFile);
// builder.append(" -vcodec copy -acodec copy -b:v 4000K -b:a 96K -f mp4 ");builder.append(" -vcodec copy -acodec copy ");builder.append(MApplication.VIDEO_PATH);builder.append(outputName);LogUtil.e(TAG,"------------ 剪辑视频ffmpeg 命令 = " +builder.toString());final String[] command = builder.toString().split(" ");
视频和图片的合成
把三个分镜头合成一个视频后,可以对视频进行涂鸦,帖子,添加文本等操作。
StringBuilder sb = new StringBuilder();sb.append("ffmpeg");sb.append(" -y -i");sb.append(" "+path);sb.append(" -i");sb.append(" "+imagePath);sb.append(" -filter_complex overlay ");sb.append(mergeVideo);String[] cmds = sb.toString().split(" ");LogUtil.d(TAG, "----- overlay 命令 " + sb.toString());
不足
在这个项目中,完成初期的预想,加深对FFmpeg认识和了解。但是1.0
版本存在很多的不足,比如:
- 速度的变换范围少
- 合成视频画质差
- FFmpeg对一些功能,比如:在overlay做叠加,用scale缩放,改变速度功能较慢 。
- 不能添加滤镜
后记
这个项目将会一直会维护下去,完善所能知道的一些不足的地方,还请大家多多指导和多提意见,互相学习,感谢。
Thanks
FFmpeg
glide
butterknife
BaseRecyclerViewAdapterHelper
okhttp
bspatchlibrary
ffmpeglibrary
circular-progress-button
material-dialogs
Zhaoss
视频裁剪
作者:maimingliang
原文 基于FFmpeg的短视频编辑工具Cut - 掘金