Android本地視頻壓縮方案

本文討論的不是相似秒拍的短視頻錄製,而是用戶選擇本地一個現有視頻,壓縮後上傳。秒拍的實現實際上是自定義視頻錄製功能,從而控制錄製時長,分辨率,碼率等,生成體積很小的視頻再上傳。而咱們則沒辦法控制原視頻的參數,多是一個很大的視頻須要壓縮處理。java

思路

利用ffmpeg對視頻轉碼,經過設定參數生成分辨率和碼率更小的視頻,實現壓縮。固然,ffmpeg的功能遠不止如此,這是一個很大的專題。
用到的開源庫:https://github.com/WritingMinds/ffmpeg-android-javaandroid

使用方法

基本原理:將android環境下可執行文件ffmpeg存放在本地,代碼執行ffmpeg的壓縮命令。git

//將開源庫中asset目錄的ffmpeg可執行文件,拷貝到 app的data/data/files目錄

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

這個方法是異步執行,因此最好在Application中執行。方法有執行成功與否的回調,這裏我傳入null不關心結果。執行完看下手機中的目錄:github

既然是可執行文件,那麼在android shell環境下確定能夠執行了。adb shell進入手機看下(前提是手機已經獲取root權限):正則表達式

 
 
image.png

執行ffmpeg的一個命令:好比查看ffmpeg的當前版本:./ffmpeg -versionshell

 
 
image.png

接着就能夠在代碼中,使用ffmpeg的各類命令了:把命令寫入String[],而後調用fFmpeg.execute 便可api

獲取視頻文件的信息多線程

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();

        }

壓縮視頻:app

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端用 格式工廠壓縮轉碼視頻也不是很快。
壓縮質量還能夠,基本能保持和原視頻同樣的清晰度。下面是測試數據:

 
 
相關文章
相關標籤/搜索