FFmpeg在JAVA中的使用以及Process.waitFor()引起的阻塞問題

此文已由做者葉海嘯受權網易雲社區發佈。
html

歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。java


FFmpeg是一個開源免費跨平臺的視頻和音頻流方案,能夠快速對音視頻流進行多方面的處理,本文主要介紹FFmpeg經常使用的命令與參數講解,如何在JAVA中使用FFmpeg以及遇到的一些問題。linux

背景

項目需求中涉及到有關於視頻、音頻的一系列處理,包含視頻中音頻提取、視頻首幀提取、音頻重採樣、字幕壓縮的功能,一直在研究ffmpeg,僅僅幾個功能,卻深受ffmpeg的折磨。git

今天談談ffmpeg在java中的簡單使用,首先下載ffmpeg包,官方地址:http://ffmpeg.org/download.html, 這裏建議下載Linux Static Builds版本的,輕小並且解壓後能夠直接使用,本人使用的版本是ffmpeg-git-20170922-64bit-static.tar.xz。web

解壓以後,文件夾中有一個可執行文件ffmpeg,在linux上能夠直接運行./ffmpeg -version,能夠查看ffmpeg的版本信息,以及configuration配置信息。安全

基本命令

  • 視頻中音頻提取:ffmpeg -i [videofile] -vn -acodec copy [targetaudiofile]網絡

  • 視頻首幀提取:ffmpeg -i [videofile] -vframes 1 -q:v 2 -f image2  [imagefile]jvm

  • 音頻重採樣:ffmpeg -i [audiofile] -ar [samplingrate]  [targetaudiofile]ide

  • 字幕壓縮:ffmpeg -i [videofile] -vf subtitles=[subtitle.srt] [targetvideofile]工具

命令說明

  • audiofile、videofile是音視頻源文件,能夠是本地文件,也能夠是網絡文件URL;

  • 提取音頻流時,-vn 忽略視頻流 -acodec 設定聲音編解碼器,未設定時則使用與輸入流相同的編解碼器,若是須要提取視頻流,則參數變爲-an -vcodec;

  • -vframes 表示提取的第幾幀,獲取第一楨則後面的值爲1,若是後面的值大於1,那麼最後的[imagefile]不能指定一個文件,否則會報錯,以下 Alt pic

  • 指定了輸出的文件名爲「1.jpeg」,報錯:不能從1.jpeg文件中獲取第二幀的文件名,由於-vframes只要大於1,則會提取出每一幀的圖片,建議使用如%03d.jpeg來做爲文件名,那麼它解析的結果即是001.jpeg,002.jpeg,...依次編號日後;

  • -q:v 2 q表明質量quality, v表明視頻流,2是控制質量的參數;-f指定輸出的格式是image2;

  • 除了使用-vframes來獲取視頻幀,還有使用-ss參數來獲取,-ss後的時間參數是自行設定,而且在視頻的有效時間內(格式爲00:00:00),使用-ss時,若是沒有使用%03d.jpeg來做爲文件名,則獲取的是-ss參數指定的那個時間的幀;

  • -ar表示使用新的採樣率,經常使用的有8,000Hz、16,000Hz、22,050Hz、32,000Hz、44,100Hz;

  • subtitle.srt是字幕文件(中文字幕即把英文變爲中文,其它格式一致),這邊就使用最簡單的srt標準格式,srt文件寫入的字符編碼須要是UTF-8,不然壓縮的時候會報沒法讀取srt文件;

  • 若想壓縮中文字幕,須要系統中有中文字體,使用fc-list查詢系統支持的字體,fc-list :lang=zh查詢支持的中文字體

JAVA使用遇到的問題

通常須要調用系統命令時,大部分人第一反應確定是使用Runtime.getRuntime().exec(command)返回一個process對象,再調用process.waitFor()來等待命令執行結束,獲取執行結果。

產品剛上線,運行很穩定,可是沒過多久,產品同窗說從某個時間點開始添加的視頻都不出來了!由於這個視頻必需要通過一系列的處理,纔會展現出來,因此中途某個環節出錯了。

首先查看了日誌,沒有發現任何的報錯,可是幸虧開發的時候加了debug日誌,每一句命令exec先後都會打一句log,因而看下是否「開始執行」和「執行成功」兩句log都打印了,結果發如今截取首幀的時候,只打印了「開始執行」,一直沒有結束,那麼猜想進程堵塞了。

可是,我把產品同窗的視頻拿過來,直接執行提取視頻第一幀的命令,提示圖片未提取成功,後來發現該視頻是產品同窗經過某個壓縮工具壓縮過的,點開視頻能夠看見黑屏,看不到任何東西,確定是壓縮時把視頻壓縮出錯了,可是截取首幀命令既然執行結束了,按道理不該該一直堵塞啊?    Alt pic

因而經過dump下了內存鏡像文件,命令jmap -dump:live,format=b,file=heap.dmp PID,經過jvisualvm工具查看,發現有不少以下的堆棧:     Alt pic

所以能夠判斷,確實是在截取首幀的時候,進程阻塞了,可是爲何會阻塞???

解決方案

查看waitFor()源碼能夠發現,其實調用的是Object類中的,wait()方法,而且未指定等待時間,那麼若是一直不返回,則會一直阻塞。

而且查看了JDK的幫助文檔,以下    Alt pic

所以,能夠得出結論:若是外部程序不斷在向標準輸出流(對於jvm來講就是輸入流)和標準錯誤流寫數據,而JVM不讀取的話,當緩衝區滿以後將沒法繼續寫入數據,最終形成阻塞在waitFor()這裏。

解決方法:在waitFor()以前,利用單獨兩個線程,分別處理process的getInputStream()和getErrorSteam(),防止緩衝區被撐滿,致使阻塞;

 /**
 * 處理process輸出流和錯誤流,防止進程阻塞
 * 在process.waitFor();前調用
 * @param process
 */
private static void dealStream(Process process) {
    if (process == null) {
        return;
    }
    // 處理InputStream的線程
    new Thread() {
        @Override
        public void run() {
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line = null;
            try {
                while ((line = in.readLine()) != null) {
                    logger.info("output: " + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
    // 處理ErrorStream的線程
    new Thread() {
        @Override
        public void run() {
            BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String line = null;
            try {
                while ((line = err.readLine()) != null) {
                    logger.info("err: " + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    err.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
}


免費體驗雲安全(易盾)內容安全、驗證碼等服務

更多網易技術、產品、運營經驗分享請點擊


相關文章:
【推薦】 Kubernetes 在網易雲中的落地優化實踐
【推薦】 寓教於樂——玩轉角色互換遊戲

相關文章
相關標籤/搜索