前言
本文討論的不是類似秒拍的短視頻錄制,而是用戶選擇本地一個現有視頻,壓縮后上傳。秒拍的實現其實是自定義視頻錄制功能,從而控制錄制時長,分辨率,碼率等,生成體積很小的視頻再上傳。而我們則沒辦法控制原視頻的參數,可能是一個很大的視頻需要壓縮處理。
思路
利用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.壓縮進度反饋
執行轉碼命令后,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端用 格式工廠壓縮轉碼視頻也不是很快。
壓縮質量還可以,基本能保持和原視頻一樣的清晰度。下面是測試數據:

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。
新聞熱點
疑難解答