Android本地视频压缩方案的示例代码

前言

本文讨论的不是类似秒拍的短视频录制,而是用户选择本地一个现有视频,压缩后上传。秒拍的实现其实是自定义视频录制功能,从而控制录制时长,分辨率,码率等,生成体积很小的视频再上传。而我们则没办法控制原视频的参数,可能是一个很大的视频需要压缩处理。

思路

利用ffmpeg对视频转码,通过设定参数生成分辨率和码率更小的视频,实现压缩。当然,ffmpeg的功能远不止如此,这是一个很大的专题。

用到的开源库:https://github.com/WritingMinds/ffmpeg-android-java

使用方法

基本原理:将android环境下可执行文件ffmpeg存放在本地,代码执行ffmpeg的压缩命令。

//将开源库中asset目录的ffmpeg可执行文件,拷贝到 app的data/data/files目录

FFmpeg.getInstance(this).loadBinary(null);

这个方法是异步执行,所以最好在Application中执行。方法有执行成功与否的回调,这里我传入null不关心结果。执行完看下手机中的目录:

既然是可执行文件,那么在android shell环境下肯定可以执行了。adb shell进入手机看下(前提是手机已经获取root权限):

执行ffmpeg的一个命令:比如查看ffmpeg的当前版本:./ffmpeg -version

接着就可以在代码中,使用ffmpeg的各种命令了:把命令写入String[],然后调用fFmpeg.execute 即可

获取视频文件的信息

String[] command = new String[]{"-i", arg.filePath};
try {
      fFmpeg.execute(commands, new ExecuteBinaryResponseHandler(){
        @Override
        public void onStart() {}

        @Override
        public void onProgress(String message) {
          Log.e("dml", "onProgress: message is " + message);
        }

        @Override
        public void onFailure(String message) {
          Log.e("dml", "onFailure: message is " + message);
        }

        @Override
        public void onSuccess(String message) {
          Log.e("dml", "onSuccess: message is " + message);
        }

        @Override
        public void onFinish() {
          Log.e("dml", "onFinish: ");
        }
      });
    } catch (FFmpegCommandAlreadyRunningException e) {
      e.printStackTrace();
    }

压缩视频:

String[] commands = new String[]{"-threads","1","-i", arg.filePath, "-c:v", "libx264","-crf","30","-preset", "superfast" ,"-y", "-acodec","libmp3lame",arg.thumbVideoPath};
fFmpeg.execute(commands, new ExecuteBinaryResponseHandler(){});

参数解释:

  1. -threads: 执行线程数,传入1 单线程压缩
  2. -i:input路径,传入视频文件的路径
  3. -c:v:编码格式,一般都是指定libx264
  4. -crf: 编码质量,取值范围是0-51,默认值为23,数字越小输出视频的质量越高。这里的30是我们经过测试得到的经验值
  5. -preset:转码速度,ultrafast,superfast,veryfast,faster,fast,medium,slow,slower,veryslow和placebo。ultrafast编码速度最快,但压缩率低,生成的文件更大,placebo则正好相反。x264所取的默认值为medium。需要说明的是,preset主要是影响编码的速度,并不会很大的影响编码出来的结果的质量。
  6. -acodec:音频编码,一般采用libmp3lame
  7. arg.thumbVideoPath:最后传入的是视频压缩后保存的路径
  8. -y:输出时覆盖输出目录已存在的同名文件(如果不加此参数,就不会覆盖)

问题解决

此开源库用于视频压缩在实际开发中存在不少问题,下面一一解决

1.压缩进度反馈

执行转码命令后,onProgress只是不停输出字符串,而且文本很长 需要正则表达式从中截取转码进度反馈:

        @Override
        public void onProgress(String s) {
          Pattern timePattern = Pattern.compile("(?<=time=)[\\d:.]*");
          Scanner sc = new Scanner(s);
          String match = sc.findWithinHorizon(timePattern, 0);
          if (match != null) {
            String[] matchSplit = match.split(":");
            if (duration!= 0) {
              float progress = (Integer.parseInt(matchSplit[0]) * 3600 +
                  Integer.parseInt(matchSplit[1]) * 60 +
                  Float.parseFloat(matchSplit[2])) / duration;
              int showProgress = (int) (progress * 100);
              if(showProgress>100){
                showProgress = 100;
              }
              notify.compressProgress(getTag(),showProgress);
            }
          }
        }

2.低码率视频压缩会变大

实际中发现有些原质量较差的视频压缩后,体积反而变大。

处理方法:压缩前先执行对视频提取信息的命令,小于1024kb/s的视频 不压缩:

        @Override
        public void onProgress(String s) {
          //Log.d("dml","pre onProgress = " + s);
          if(s.contains("Stream #0:0")){
            String tem = s.substring(0, s.indexOf("kb/s"));
            String type ;
            int pos = tem.lastIndexOf(",");
            if (pos != -1) {
              type = tem.substring(pos + 1,tem.length()).trim();
              try {
                Integer integer = Integer.parseInt(type);
                if(integer > 1024){
                  pressV(fFmpeg);//执行压缩
                }else {
                  //放弃压缩,直接使用原文件
                }
              }catch (Exception e){
              }
            }
          }
        }

并且在压缩成功后,检查压缩后的文件和原文件大小,如果变大了,直接使用原文件。

3.多线程压缩多个视频

开源库中执行ffmpeg的命令是在AsycTask执行的:

ffmpegExecuteAsyncTask = new FFmpegExecuteAsyncTask(command , timeout, ffmpegExecuteResponseHandler);
ffmpegExecuteAsyncTask.execute();

execute 方法在api 11之后是串行方法,就是说开源库已经限制为单线程。

改为:ffmpegExecuteAsyncTask.executeOnExecutor(Executors.newCachedThreadPool()); 可以使用多线程

测试中发现多个视频同时压缩,手机会严重发热,强烈建议采用原设计 。

4.压缩速度和质量

手机性能有限,压缩视频速度不太理想,即使在PC端用 格式工厂压缩转码视频也不是很快。

压缩质量还可以,基本能保持和原视频一样的清晰度。下面是测试数据:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • Android视频压缩的示例代码
(0)

相关推荐

  • Android视频压缩的示例代码

    最近由于项目需求,需要对自己录制的视频进行压缩并发送.本篇主要说的是视频的压缩,所以关于视频的录制和上传.发送就不在这里做过多表述了. 背景: 以前从来没有做过关于视频方面的项目,所以在刚接受这个项目的时候,有点头大,当然了,迎难而上是我们攻城狮必备的品质.所以我就在网上搜索关于视频编解码的知识,搜到最多的就是ffmpeg,那么什么是ffmpeg呢?简单说FFmpeg 是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序.采用LGPL或GPL许可证.它提供了录制.转换以及流化

  • Android本地视频压缩方案的示例代码

    前言 本文讨论的不是类似秒拍的短视频录制,而是用户选择本地一个现有视频,压缩后上传.秒拍的实现其实是自定义视频录制功能,从而控制录制时长,分辨率,码率等,生成体积很小的视频再上传.而我们则没办法控制原视频的参数,可能是一个很大的视频需要压缩处理. 思路 利用ffmpeg对视频转码,通过设定参数生成分辨率和码率更小的视频,实现压缩.当然,ffmpeg的功能远不止如此,这是一个很大的专题. 用到的开源库:https://github.com/WritingMinds/ffmpeg-android-j

  • C# 获得本地通用网卡信息的示例代码

    可以通过使用命名空间下的ManagementObjectSearcher类及其方法Get来获得通用网卡信息. 其中,最重要的是ManagementObjectSearcher构造函数的输入参数,可以传入以下的QueryString. SELECT * FROM Win32_NetworkAdapterConfiguration 以下的例子为将本地网卡的配置信息写入一个文件并保存,代码如下: using System.IO; using System.Linq; using System.Mana

  • SpringBoot中默认缓存实现方案的示例代码

    在上一节中,我带大家学习了在Spring Boot中对缓存的实现方案,尤其是结合Spring Cache的注解的实现方案,接下来在本章节中,我带大家通过代码来实现. 一. Spring Boot实现默认缓存 1. 创建web项目 我们按照之前的经验,创建一个web程序,并将之改造成Spring Boot项目,具体过程略. 2. 添加依赖包 <dependency> <groupId>org.springframework.boot</groupId> <artif

  • SpringBoot 签到奖励实现方案的示例代码

    前言 最近在做社交业务,用户进入APP后有签到功能,签到成功后获取相应的奖励: 项目状况:前期尝试业务阶段: 特点: 快速实现(不需要做太重,满足初期推广运营即可) 快速投入市场去运营 用户签到: 用户在每次启动时查询签到记录(规则:连续7日签到从0开始,签到过程中有断签从0开始) 如果今日未签到则提示用户可以进行签到 用户签到获取相应的奖励 提到签到,脑海中首先浮现特点: 需要记录每位用户每天的签到情况 查询时根据规则进行签到记录情况 需求&流程设计&技术实现方案 需求原型图 查询签到记

  • SpringBoot+Mybatis-Plus实现mysql读写分离方案的示例代码

    1. 引入mybatis-plus相关包,pom.xml文件 2. 配置文件application.property增加多库配置 mysql 数据源配置 spring.datasource.primary.jdbc-url=jdbc:mysql://xx.xx.xx.xx:3306/portal?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=

  • Android实现微信登录的示例代码

    目录 一.布局界面 二.MainActivity.java 微信登录的实现与qq登录类似.不过微信登录比较麻烦,需要拿到开发者资质认证,花300块钱,然后应用的话还得有官网之类的,就是比较繁琐的前期准备工作,如果在公司里,这些应该都不是事,会有相关人提前准备好.在这里我们已经拿到了开发者认证,并且申请到了微信登录的授权. 现在直接介绍mob来实现微信登录的代码,并获取微信的相关数据,比较简单. 一.布局界面 布局界面只需要一个button来触发授权就可以 <Button android:id=&qu

  • Android绘制平移动画的示例代码

    目录 1.具体操作步骤 2.具体实施 创建ImageView 创建ObjectAnimator对象 3.具体实例 activity_main.xml MainActivity.java 1.具体操作步骤 创建ImageView对象 创建ObjectAnimator对象 通过ofFloat方法实现平移 2.具体实施 创建ImageView <ImageView android:id="@+id/car" android:layout_width="wrap_content

  • 在Android上实现HttpServer的示例代码

    在最近的项目中因为要用Android作为一个服务器去做一个实时接收数据的功能,所以这个时候就要去做一个Android本地的微型服务器. 那么此时我首先想到了spring boot,因为他是一个服务器的框架.但是实际上我们根本用不到这么大型的服务器框架,配置这些都太麻烦.所以,我又找到了Ijetty.NanoHttpd和AndroidAsync这三个框架,都是比较微型的,适用于Android的. 经过对比,Ijetty使用起来过于复杂,而且会莫名其妙的报一些不太容易解决的问题,所以,舍弃掉了. 因

  • Android实现动态高斯模糊效果示例代码

     写在前面 现在,越来越多的App里面使用了模糊效果,这种模糊效果称之为高斯模糊.大家都知道,在Android平台上进行模糊渲染是一个相当耗CPU也相当耗时的操作,一旦处理不好,卡顿是在所难免的.一般来说,考虑到效率,渲染一张图片最好的方法是使用OpenGL,其次是使用C++/C,使用Java代码是效率是最低,速度也是最慢的.但是Android推出RenderScript之后,我们就有了选择,测试表明,使用RederScript的渲染效率和使用C++/C不相上下,但是使用RenderScript

  • android开发权限询问的示例代码

    现在基于信息安全问题,特别是版本是23以上权限越严格. 特别是拍照,读,写权限 一般权限允许过,下次就不用询问了的,所以很多应用都喜欢在首页或者启动页直接询问,不允许的就用不了1.下面给出封装好的类,至于什么时候调看项目需要 public class EasyPermissions { private static final String TAG = "EasyPermissions"; public interface PermissionCallbacks extends Act

随机推荐