關於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