1. 程式人生 > 實用技巧 >關於ffmpeg解決主流瀏覽器無法播放wmv、avi等格式視訊問題

關於ffmpeg解決主流瀏覽器無法播放wmv、avi等格式視訊問題

最近接到一個老專案,由於老專案之前適配的是ie瀏覽器。該老專案中有很多wmv和avi格式的視訊。最近需要更換視訊其他瀏覽器訪問,需要對除ie瀏覽器的其他瀏覽器進行適配。ie瀏覽器播放視訊沒有任何問題,但是在主流瀏覽器中,無法識別<embed>標籤,只支援<video>、<audio>標籤,然而這些標籤支援的視訊格式為主流的mp4格式的視訊。導致相容性問題,無法播放,以及主流瀏覽器無法播放非mp4格式的視訊。嘗試了很多,查閱了很多資料,前端無法解決該問題,最後嘗試使用後端來解決該問題。通過java呼叫ffmpeg來對已經儲存在系統中的視訊進行自主的格式轉換為mp4,前端通過判斷是否為ie瀏覽器,而執行對應的js指令碼。如果發現為非ie瀏覽器,並且視訊為非mp4格式的視訊,則主動呼叫後臺介面,對該視訊進行格式轉換,同時更新資料庫儲存視訊的目標位置,之後再訪問該視訊時,就查詢到的是mp4格式的視訊,就無需再做格式轉換操作了。

文末附相關安裝包、ffmpeg相關命令介紹


ffmpeg

ffmpeg是一個支援多個格式視訊轉換(包括asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv格式)的軟體。在windows和linux環境下使用的方式不同。

首先需要下載ffmpeg軟體,解壓之後將ffmpeg.exe放入到專案目錄中。通過java去呼叫該程式,執行命令對目標視訊進行格式轉換。


相關java程式碼

ConvertVideo 是接收需要轉換的視訊的類。需要注意的是,其中window環境下獲取的ffmpeg目錄與linux環境下獲取ffmpeg目錄是不同的。

public class ConvertVideo {
    public static Boolean convertVedio(String inputPath) throws FFmpegException {
        if (inputPath.substring(inputPath.lastIndexOf(".")).equals(".mp4") || inputPath.substring(inputPath.lastIndexOf(".")).equals(".MP4")){
            return true;
        }
        String ffmpegPath = getFfmpegPath();
        String outputPath = getOutputPath(inputPath);
        if (new File(outputPath).exists()){
            return true;
        }
        return FFmpegUtil.ffmpeg(ffmpegPath, inputPath, outputPath);
    }

    /**
     * 獲取ffmpeg執行檔案的路徑
     *
     * @return
     */
    private static String getFfmpegPath() {
        // windows環境
        return new Object(){
            public String getPath(){
                return this.getClass().getResource("/").getPath().replaceAll("WEB-INF/classes/", "")+"ffmpeg/";
            }
        }.getPath();

        // linux環境
        //return "/opt/ffmpeg/ffmpeg-release.4.1/";
    }

    /**
     * 獲取輸出檔名
     *
     * @param inputPath
     * @return
     */
    private static String getOutputPath(String inputPath) {
        return inputPath.substring(0, inputPath.lastIndexOf(".")) + ".mp4";
    }

    public static String getNewFileUrl(String inputPath) {
        return inputPath.substring(0, inputPath.lastIndexOf(".")) + ".mp4";
    }
}

FFmpegException 這是定義ffmpeg異常類

public class FFmpegException extends Exception {
    private static final long serialVersionUID = 1L;

    public FFmpegException() {
        super();
    }

    public FFmpegException(String message) {
        super(message);
    }

    public FFmpegException(Throwable cause) {
        super(cause);
    }

    public FFmpegException(String message, Throwable cause) {
        super(message, cause);
    }
}

FFmpegUtil 主要對視訊進行格式轉換的類

public class FFmpegUtil {
    /**
     * 將視訊轉換為mp4
     *
     * @param ffmpegPath ffmpegPath bin路徑
     * @param inputPath 原始檔路徑
     * @param outputPath 輸出檔案路徑
     * @return
     */
    public static Boolean ffmpeg(String ffmpegPath, String inputPath, String outputPath) throws FFmpegException{

        if (!checkfile(inputPath)) {
            throw new FFmpegException("檔案格式不合法");
        }

        int type = checkContentType(inputPath);
        List<String> command = getFfmpegCommand(type, ffmpegPath, inputPath, outputPath);
        if (null != command && command.size() > 0) {
            return process(command);
        }
        return false;
    }

    /**
     * 檢查視訊的格式
     *
     * @param inputPath 原始檔
     * @return
     */
    private static int checkContentType(String inputPath) {
        String type = inputPath.substring(inputPath.lastIndexOf(".") + 1, inputPath.length()).toLowerCase();
        // ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
        if (type.equals("avi")) {
            return 0;
        } else if (type.equals("mpg")) {
            return 0;
        } else if (type.equals("wmv")) {
            return 0;
        } else if (type.equals("3gp")) {
            return 0;
        } else if (type.equals("mov")) {
            return 0;
        } else if (type.equals("mp4")) {
            return 0;
        } else if (type.equals("asf")) {
            return 0;
        } else if (type.equals("asx")) {
            return 0;
        } else if (type.equals("flv")) {
            return 0;
        }
        // 對ffmpeg無法解析的檔案格式(wmv9,rm,rmvb等)
        else if (type.equals("wmv9")) {
            return 1;
        } else if (type.equals("rm")) {
            return 1;
        } else if (type.equals("rmvb")) {
            return 1;
        }
        return 9;
    }

    /**
     * 檢查檔案的合法性
     *
     * @param path 檔案路徑
     * @return
     */
    private static boolean checkfile(String path) {
        File file = new File(path);
        return file.isFile();
    }

    /**
     * ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
     *
     * @param command ffmpeg的命令
     * @throws FFmpegException
     */
    private static boolean process(List<String> command) throws FFmpegException{

        try {

            if (null == command || command.size() == 0) {
                return false;
            }
            Process videoProcess = new ProcessBuilder(command).redirectErrorStream(true).start();

            new PrintStream(videoProcess.getErrorStream()).start();

            new PrintStream(videoProcess.getInputStream()).start();

            int exitcode = videoProcess.waitFor();

            return exitcode != 1;
        } catch (Exception e) {
            throw new FFmpegException("file upload failed",e);
        }

    }

    /**
     * 根據檔案型別設定ffmpeg命令
     *
     * @param type 該視訊對應的型別
     * @param ffmpegPath ffmpeg執行檔案的路徑
     * @param oldfilepath 需要被轉換的視訊路徑
     * @param outputPath 轉換後輸出的路徑
     * @return
     * @throws FFmpegException
     */
    private static List<String> getFfmpegCommand(int type, String ffmpegPath, String oldfilepath, String outputPath) throws FFmpegException {
        List<String> command = new ArrayList<String>();
        if (type == 0) {
            command.add(ffmpegPath+"ffmpeg");
            command.add("-y");
            command.add("-i");
            command.add(oldfilepath);
            command.add("-c:v");
            command.add("libx264");
            command.add("-mbd");
            command.add("0");
            command.add("-c:a");
            command.add("aac");
            command.add("-strict");
            command.add("-2");
            command.add("-pix_fmt");
            command.add("yuv420p");
            command.add("-movflags");
            command.add("faststart");
            command.add(outputPath);
        } else{
            throw new FFmpegException("不支援當前上傳的檔案格式");
        }
        return command;
    }
}

// 開啟新的執行緒對視訊進行格式轉換
class PrintStream extends Thread {
    private static final Logger log = LoggerFactory.getLogger(PrintStream.class);
    java.io.InputStream inputStream = null;

    public PrintStream(java.io.InputStream is) {
        inputStream = is;
    }

    @Override
    public void run() {
        try {
            while (this != null) {
                int ch = inputStream.read();
                if (ch == -1) {
                    break;
                } else {
                    System.out.print((char) ch);
                }

            }
        } catch (Exception e) {
            log.error("convert media error!", e);
        }
    }
}

linux環境下使用ffmpeg

安裝GCC

yum install gcc
檢視安裝結果gcc --version

安裝yasm

1.下載yasm
wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
2.解壓yasm
執行:tar -zxvf yasm-1.3.0.tar.gz
3.進入解壓目錄
執行: cd yasm-1.3.0
4.編譯和安裝
執行1:./configure
執行2:make
執行3:make install
5.檢視安裝結果
執行yasm --version

安裝nasm

1.下載nasm
wget http://www.nasm.us/pub/nasm/releasebuilds/2.13/nasm-2.13.tar.gz
2.解壓nasm
執行 tar xzvf nasm-2.13.tar.gz
3.進入目錄
執行:cd nasm-2.13
4.編譯和安裝
執行1:./configure
執行2:make
執行3:make install
5.檢視安裝結果
執行nasm --version

安裝x264

1,下載x264 或解壓x264
git clone https://code.videolan.org/videolan/x264.git
2.進入x264目錄
cd x264
3.編譯和安裝
執行1:./configure --enable-shared
執行2:make
執行3:make install
4.檢視安裝結果
執行x264 --version

安裝ffmpeg

1.下載ffmpeg4.1
wget https://git.ffmpeg.org/gitweb/ffmpeg.git/snapshot/refs/heads/release/4.1.tar.gz

2.解壓ffmpeg4.1
執行: tar xzfv 4.1.tar.gz

3.進入解壓目錄

解壓之後目錄名為ffmpeg-release.4.1-d44da66,請將目錄名修改為ffmpeg-release.4.1

執行: cd ffmpeg-release.4.1/

4.編譯和安裝
執行1:./configure --enable-gpl --enable-libx264

如果出現類似error如下:

WARNING: using libfdk without pkg-config
WARNING: using libx264 without pkg-config
ERROR: x265 not found using pkg-config
If you think configure made a mistake, make sure you are using the latest
version from Git.  If the latest version fails, report the problem to the
[email protected] mailing list or IRC #ffmpeg on irc.freenode.net.
Include the log file "ffbuild/config.log" produced by configure as this will help
solve the problem.

原因是需要設定PKG_CONFIG_PATH,通過pkg-config去指定路徑自動尋找需要連結的依賴庫,解決方法如下,執行命令:

export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
echo $PKG_CONFIG_PATH
檢視結果為 /usr/local/lib/pkgconfig

解釋:PKG_CONFIG_PATH (此路徑為.pc檔案所在路徑,裡面有x264.cp)

然後重新編譯就ok了!

執行2:make
執行3:make install(大概30分鐘左右)

5.檢視安裝結果 ffmpeg -version

如果出現如下:

6.編輯id.so.conf檔案
執行vim /etc/ld.so.conf
在include ld.so.conf.d/*.conf後換行新增
/usr/local/lib (如果有這句話,直接儲存退出,沒有就新增這句話)
:x 儲存退出
在ffmpeg目錄下,執行 ldconfig
然後再執行 ffmpeg -version


測試

2.wmv轉換成2.mp4

本人安裝ffmpeg時,是將ffmpeg安裝在/home/herocheung/ffmpeg/目錄下的,因此有了一下測試命令:

/home/herocheung/ffmpeg/ffmpeg-release.4.1/ffmpeg -i ./2.wmv -c:v libx264 -mbd 0 -c:a aac -strict -2 -pix_fmt yuv420p -movflags faststart ./2.mp4


-- 命令解釋:
(指定ffmpeg安裝目錄下的ffmpeg) -i (需要轉換的視訊位置) -c:v libx264 -mbd 0 -c:a aac -strict -2 -pix_fmt yuv420p -movflags faststart (轉換後視訊存放位置)

安裝完成之後,把java中相關的程式碼切換到linux環境下能夠正確識別的路徑即可實現java呼叫ffmpeg程式,實現視訊格式轉。

附件

相關安裝包(windows ffmpeg、linux ffmpeg相關包):

ffmpeg相關命令:http://linux.51yip.com/search/ffmpeg


相關部落格:
【linux安裝ffmpeg】https://blog.csdn.net/chaos1/article/details/110440426?utm_medium=distribute.pc_relevant_download.none-task-blog-baidujs-4.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-baidujs-4.nonecase