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不关心结果。执行完看下手机中的目录:

image.png

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

image.png

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

image.png

接着就可以在代码中,使用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(){});

参数解释:

  • -threads: 执行线程数,传入1 单线程压缩

  • -i:input路径,传入视频文件的路径

  • -c:v:编码格式,一般都是指定libx264

  • -crf: 编码质量,取值范围是0-51,默认值为23,数字越小输出视频的质量越高。这里的30是我们经过测试得到的经验值

  • -preset:转码速度,ultrafast,superfast,veryfast,faster,fast,medium,slow,slower,veryslow和placebo。ultrafast编码速度最快,但压缩率低,生成的文件更大,placebo则正好相反。x264所取的默认值为medium。需要说明的是,preset主要是影响编码的速度,并不会很大的影响编码出来的结果的质量。

  • -acodec:音频编码,一般采用libmp3lame

  • arg.thumbVideoPath:最后传入的是视频压缩后保存的路径

  • -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端用 格式工厂压缩转码视频也不是很快。
压缩质量还可以,基本能保持和原视频一样的清晰度。下面是测试数据:


image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,848评论 25 707
  • 前几天项目需要压缩视频,Github上找了许多库,要么就是太大,要么就是质量不高,其实我只需要压缩视频,最好的方案...
    voiddog阅读 26,247评论 59 82
  • 任之行于英彬日精进分享 显现-练习——使用 我行,一切行,我行任之行 选好人,做对事 懂人事 体验价值从满足身...
    于英彬阅读 159评论 0 0
  • 第一次接触简书,就爱上了这个地方,简单干净,只有单纯的文字音符,却每一次都会演绎灵魂的序曲。有鸡汤却朴实,听听别人...
    Mango芒芒阅读 388评论 1 1
  • 使用的东西我自己知道好不好。最令人感慨的就是明明我用的是好东西,但是消费者不买账,最后企业亏损,这些好东西都销声匿...
    评综侠影阅读 586评论 0 51