基于FFmpeg的短视频编辑工具Cut

news2024/11/6 9:30:14

前言

最近在学习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() {
            @Override
            public void onDownloadSuccess(File file) {

                if(file != null){
                    mProgressDialog.dismiss();
                    LogUtil.e("tag", "---path = " + file.getAbsolutePath());

                    if(update_type == 1){
                        //获取当前应用的apk文件/data/app/app
                        String 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 和 差分包 合成新的apk
                        BspatchNDK.bspatch(oldFile, newApkPath, patchFileAbsolutePath);
                        
                        //再次安装
                        Utils.installApk(LaunchActivity.this,newApkPath);
                    }else if(update_type == 2){
                        Utils.installApk(LaunchActivity.this,file);
                    }


                 }

            }

            @Override
            public void onDownloading(int progress) {
                mProgressDialog.setProgress(progress);

            }

            @Override
            public void onDownloadFailed() {
                mProgressDialog.dismiss();

            }
        });

这里会有一个问题?这个差分包,是什么版本和新版本的差分包?我这里是这样处理的:假如市场发布了1.0.01.0.11.0.2,最新版本为1.0.3.
差分包patch是:1.0.21.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版本存在很多的不足,比如:

  1. 速度的变换范围少
  2. 合成视频画质差
  3. FFmpeg对一些功能,比如:在overlay做叠加,用scale缩放,改变速度功能较慢 。
  4. 不能添加滤镜

后记

这个项目将会一直会维护下去,完善所能知道的一些不足的地方,还请大家多多指导和多提意见,互相学习,感谢。

Thanks
FFmpeg
glide
butterknife
BaseRecyclerViewAdapterHelper
okhttp
bspatchlibrary
ffmpeglibrary
circular-progress-button
material-dialogs
Zhaoss
视频裁剪

作者:maimingliang
原文 基于FFmpeg的短视频编辑工具Cut - 掘金

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1362691.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

497 蓝桥杯 成绩分析 简单

497 蓝桥杯 成绩分析 简单 //C风格解法1&#xff0c;*max_element&#xff08;&#xff09;与*min_element&#xff08;&#xff09;求最值 //时间复杂度O(n)&#xff0c;通过率100% #include <bits/stdc.h> using namespace std;using ll long long; const int N 1e4 …

【Spring Cloud】组件概念详解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Spring Cloud》。&#x1f3af;&#x1f3af; &am…

CentOS中开启mysql挂载

挂载的作用其实说白了就是备份。防止数据库文件损害或者数据库被误删导致数据丢失。 创建一个文件名为my.cnf内容如下 # Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. # # This program is free software; you can redistribute it and/or modif…

清理windows中git凭证

清理windows中git凭证 控制面板——>用户账户——>凭据管理器——>管理Windows凭据 点开后如下&#xff1a;

面试之线程状态

1.线程有哪些状态 1.1Java线程的六种状态 Java 线程六种状态 新建 当一个线程对象被创建&#xff0c;但还未调用 start 方法时处于新建状态 此时未与操作系统底层线程关联 可运行 调用了 start 方法&#xff0c;就会由新建进入可运行 此时与底层线程关联&#xff0c;由操作…

Hadolint:Lint Dockerfile 的完整指南

想学习如何使用 Hadolint 对 Dockerfile 进行 lint 处理吗&#xff1f;这篇博文将向您展示如何操作。这是关于 Dockerfile linting 的完整指南。 通过对 Dockerfile 进行 lint 检查&#xff0c;您可以及早发现错误和问题&#xff0c;并确保它们遵循最佳实践。 什么是Hadolint…

windows 查看所有端口占用情况

winR&#xff0c;调出cmd窗口&#xff1a; 输入命令 netstat -ano 内容太多&#xff0c;显示不全&#xff0c;怎么办? 输入下面命令 netstat -ano > d:\1.log 在d盘根目录下就产生了 输出文件 打开可以看到如下内容 活动连接协议 本地地址 外部地址 状…

2.3_7 生产者-消费者问题

2.3_7 生产者-消费者问题 系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。(注:这里的“产品”理解为某种数据) 生产者、消费者共享一个初始为空、大小为n的缓冲区。 只有缓冲区没满时,生产者才…

Kotlin协程学习之-01

由于协程需要支持挂起、恢复、因此对于挂起点的状态保存就显得机器关键。类似的&#xff0c;线程会因为CPU调度权的切换而被中断&#xff0c;它的中断状态会保存在调用栈当中&#xff0c;因而协程的实现也按照是否开辟相应的调用栈存在以下两种类型&#xff1a; 有栈协程&…

weak_ptr如何能做到解决循环引用又能传递参数呢?

引子&#xff1a;今天在看CLR via C#的时候看到C#的垃圾回收算法--引用跟踪算法的时候想到以下几个问题。 一、引用计数法存在的问题 一般引用计数法存在的问题就是不好处理循环引用的问题&#xff0c;但是C不是有weak_ptr吗&#xff1f; 这个引用跟踪的垃圾回收算法看起来还…

vivado xsim 终端 模拟

只模拟的话直接终端运行会快很多 计数器举例 mkdir srccounter.v module counter(input wire clk,input wire rst_n,output reg[31:0] cnt ); always (posedge clk or negedge rst_n)if(!rst_n)cnt < 31h0;elsecnt < cnt1;endmodule tb.v module tb; wire[31:0] out…

C语言编译器(C语言编程软件)完全攻略

介绍常用C语言编译器的安装、配置和使用。 常用的C语言编译器&#xff08;编程软件&#xff09;介绍&#xff0c;同时附带下载地址、详细的安装教程和使用教程。我们还对比了不同C语言编译器&#xff08;C语言编程软件&#xff09;的优缺点&#xff0c;让初学者知道该如何选择…

WPF 使用矢量字体图标

矢量字体图标 在WPF项目中经常需要显示图标&#xff0c;但是项目改动后&#xff0c;有时候需要替换和修改图标&#xff0c;这样非常麻烦且消耗开发和美工的时间。为了快速开发项目&#xff0c;节省项目时间&#xff0c;使用图标矢量字体图标是一个非常不错的选择。 矢量字体图标…

Java Swing手搓童年坦克大战游戏(I)

前言 业余偶尔对游戏有些兴趣&#xff0c;不过这样的时代&#xff0c;硬件软件飞速进步&#xff0c;2D游戏画面都无比精美&#xff0c;之前的8bit像素游戏时代早就过去了&#xff0c;不过那时候有许多让人印象深刻的游戏比如魂斗罗、超级玛丽、坦克大战(Battle City)等等。 学…

案例102:基于微信小程序的旅游社交管理系统设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

Robot Operating System 2: Design, Architecture, and Uses In The Wild

Robot Operating System 2: Design, Architecture, and Uses In The Wild (机器人操作系统 2&#xff1a;设计、架构和实际应用) 摘要&#xff1a;随着机器人在广泛的商业用例中的部署&#xff0c;机器人革命的下一章正在顺利进行。即使在无数的应用程序和环境中&#xff0c;也…

C语言基础知识(5):TCP网络编程

TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构&#xff0c;当应用程序采用 TCP 发送消息时&#xff0c;虽然可以保证发送的顺序&#xff0c;但还是犹如没有任何间隔的数据流发送给接收端。TCP 为提供可靠性传输&#xff0c;实行“顺序控制”或“重发控制”机制。此…

学习笔记——C++中的循环结构 while语句

while循环语句 作用&#xff1a;满足循环条件&#xff0c;执行循环语句 语法&#xff1a;while&#xff08;循环条件&#xff09;{循环语句} 解释&#xff1a;只要循环条件的结果为真&#xff0c;就执行循环语句 以打印0-9这十个数字为例&#xff0c;特别需要注意的是&…

MySQL之视图索引执行计划

目录 一.视图 二.执行计划 2.1.什么是执行计划 2.2.执行计划的作用 三.使用外连接、内连接和子查询进行举例 四.思维导图 好啦今天就到这里了哦&#xff01;&#xff01;&#xff01;希望能帮到你哦&#xff01;&#xff01;&#xff01; 一.视图 含义 &#xff1a;在数…

Nginx快速入门:Nginx实现高可用|结合keepalived实现主备节点(九)

0. 引言 在生产中我们要尽可能避免单点故障&#xff0c;nginx也不例外&#xff0c;因此搭建主备节点必不可少&#xff0c;今天我们来学习下如何利用keepalived实现主备 1. keepalived简介 keepalived 是一个LINUX系统中开源的负载均衡和故障转移软件&#xff0c;它主要用于高…