1. 程式人生 > >使用java連線ffmpeg轉碼視訊

使用java連線ffmpeg轉碼視訊

最近用到了ffmpeg,寫一篇東西壓壓驚

借鑑了一些大爺的部落格

重構了整個程式碼,解耦,新增若干註釋

本類:

package videoformat;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

import videoformat.utils.getProperties;

/**
 * 把各種視訊檔案轉換為flv 有參構造,傳入檔案源路徑,檔案輸出路徑,檔名 無參構造通過配置檔案讀取,但需要檔名,並且存在檔案路徑中。
 * 
 * @author woshihuzios123
 * @version 0.3
 */

public class ConV2 {
	private Properties prop = getProperties.getPropertie();// 獲取配置檔案裡儲存的值
	private String filePath;// 這裡傳入檔案路徑
	private String outFilePath;// 輸出檔案存放路徑
	private String fileName;// 檔名
	private String filePathandName;// 檔案路徑+檔名

	/**
	 * 無參構造讀取配置檔案中的配置,但fileName還需要設定,不然無法轉碼
	 */
	public ConV2() {
		super();
		filePath = prop.getProperty("inputFilePath");
		outFilePath = prop.getProperty("outputFilePath");
	}

	/**
	 * 
	 * @param filePath
	 *            檔案所在路徑
	 * @param outFilePath
	 *            檔案轉碼後輸出的路徑
	 * @param fileName
	 *            當前檔名
	 */
	public ConV2(String filePath, String outFilePath, String fileName) {
		super();
		this.filePath = filePath;
		this.outFilePath = outFilePath;
		this.fileName = fileName;
	}

	public String getFileName() {
		return fileName;
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	public String getFilePath() {
		return filePath;
	}

	public String getOutFilePath() {
		return outFilePath;
	}

	public String getFilePathandName() {
		return filePathandName;
	}

	/**
	 * 如果encoderForFlv輸出為false,則表示檔名可能輸錯,轉碼引數錯誤,檔案型別不支援等
	 * 
	 * @return
	 */
	public boolean encoderForFLV() {
		// 呼叫這個唯一的方法後才會拼接字串,防止出現問題
		filePathandName = filePath + fileName;
		// 判斷檔案是否是檔案
		if (!checkfile(filePathandName)) {
			return false;
		}
		if (proccess()) {
			return true;
		}
		return false;

	}

	private boolean checkfile(String path) {
		// 沒有許可權讀取或者不是一個正常的檔案會返回false
		File file = new File(path);
		if (!file.isFile()) {
			return false;
		}
		return true;
	}

	private boolean proccess() {
		int type = checkContentType();
		boolean status = false;
		if (type == 0) {
			status = processFLV();
		}
		return status;
	}

	private int checkContentType() {
		// 擷取字尾
		String type = filePathandName.substring(
				filePathandName.lastIndexOf(".") + 1, filePathandName.length())
				.toLowerCase();
		// 後臺判斷能否轉換,前臺需要確定上傳檔案型別。
		if (type.equals(prop.getProperty("avi"))
				|| type.equals(prop.getProperty("mpg"))
				|| type.equals(prop.getProperty("wmv"))
				|| type.equals(prop.getProperty("3gp"))
				|| type.equals(prop.getProperty("mov"))
				|| type.equals(prop.getProperty("mp4"))
				|| type.equals(prop.getProperty("asf"))
				|| type.equals(prop.getProperty("asx"))
				|| type.equals(prop.getProperty("flv"))
				|| type.equals(prop.getProperty("vob"))) {
			return 0;
		}
		System.out.println(999);
		return 9;
	}

	private boolean processFLV() {
		if (!checkfile(filePathandName)) {
			return false;
		}
		fileName = fileNameHexToFLV(fileName);// 在這裡包裝 fileName,增加UUID,使用.分割
		List<String> commend = new ArrayList<String>();
		commend.add(prop.getProperty("encoderPath2"));
		commend.add(prop.getProperty("y"));// 覆蓋輸出檔案,即如果已存在就覆蓋而不提示
		commend.add(prop.getProperty("i"));// 指定輸入目錄
		commend.add(filePathandName);// 從這個目錄讀取檔案
		commend.add(prop.getProperty("ab"));// 音訊資料流 ,64kb
		commend.add(prop.getProperty("ar"));// 音訊取樣率 24000hz
		commend.add(prop.getProperty("b"));// 動態位元速率,可以改成例如"-b 1500" 來指定位元速率
		commend.add(prop.getProperty("r"));// 幀數 24
		commend.add(prop.getProperty("s"));// 輸出解析度 480*272
		commend.add(prop.getProperty("threads"));// 多執行緒, 可選項,指定2~4
		commend.add(outFilePath + fileName);// 輸出路徑+修改後的檔名
		// 使用圖片生成的返回值來判斷是否繼續
		processJPG();
		String s = commandTo(commend);
		boolean b = getRuntime(s);// 封裝,在命令列下執行
		return b;
	}

	private String fileNameHexToFLV(String filename) {
		// 擷取字尾,然後新增flv
		filename = "flv";
		// 檔案頭新增uuid
		String u = UUID.randomUUID().toString().replace("-", "")
				+ prop.getProperty("fenge") + filename;
		// 返回u
		return u;
	}

	private boolean processJPG() {

		List<String> list = new ArrayList<String>();
		list.add(prop.getProperty("encoderPath2"));// 轉碼器路徑
		list.add(prop.getProperty("i"));// 指定輸入目錄
		list.add(filePathandName);// 輸入目錄的路徑和檔案
		list.add(prop.getProperty("y"));// 覆蓋輸出檔案,即如果已存在就覆蓋而不提示
		list.add(prop.getProperty("f"));// 強制採用格式
		list.add(prop.getProperty("image2"));// image2

		list.add(prop.getProperty("s"));// 輸出解析度 480*272
		list.add(prop.getProperty("t"));// 設定記錄時間
		list.add(outFilePath + fileName + prop.getProperty("jpg"));// 新增檔案路徑加檔名再追加.jpg

		String s = commandTo(list);

		boolean b = getRuntime(s);
		return b;

	}

	private String commandTo(List<String> list) {
		StringBuilder sb = new StringBuilder();
		ArrayList<String> arrayList = (ArrayList<String>) list;
		Iterator<String> its = arrayList.iterator();
		while (its.hasNext()) {

			sb.append(its.next());
			sb.append(" ");
		}
		System.out.println(sb.toString().replace(",", ""));
		return sb.toString().replace(",", "");
	}

	// 執行程式
	private boolean getRuntime(String arguments) {
		try {
			Process p = Runtime.getRuntime().exec(arguments);
			// 輸出流,防止出現流堵塞導致死鎖
			outStream(p.getErrorStream());
			outStream(p.getInputStream());
			return true;
		} catch (IOException e) {
			e.printStackTrace();
		}

		return false;
	}

	// 可能出現執行緒異常,列印到控制檯,ffmpeg日誌,可以使用log4j列印成日誌
	private void outStream(InputStream p) {
		// 用一個讀輸出流類去讀
		BufferedReader br = new BufferedReader(new InputStreamReader(p));
		String line = null;
		// 逐行讀取輸出到控制檯
		try {
			while ((line = br.readLine()) != null) {
				System.out.println(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

配置檔案:
inputFilePath=c\:\\\\ffmpeg\\\\input\\\\
outputFilePath=c\:\\\\ffmpeg\\\\output\\\\
encoderPath2=c\:\\ffmpeg\\ffmpeg.exe
mpg=mpg
wmv=wmv
3gp=3gp
avi=avi
asf=asf
asx=asx
mp4=mp4
flv=flv
mov=mov
y=-y
qscale=-qscale 4
r=-r 24
s=-s 1600x900
b=-b 1500
ar=-ar 22050
i=-i
ab=-ab 56
image2=image2
ss=-ss 8
t=-t 0.001
jpg=.jpg
f=-f
fenge=.
threads=-threads 2

工具類:
package videoformat.utils;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

//獲取配置檔案
public class getProperties {
	static InputStream is = Object.class
			.getResourceAsStream("/decoder.properties");

	// 相對路徑

	public static Properties getPropertie() {
		Properties prop = new Properties();
		try {
			prop.load(is);
			return prop;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
}
單元測試:
package videoformat.test;


import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;


import org.junit.Test;


import videoformat.ConV2;


public class junitTest {
<span style="white-space:pre">	</span>// 能使用多執行緒建立。


<span style="white-space:pre">	</span>@Test
<span style="white-space:pre">	</span>public void fun1() {
<span style="white-space:pre">		</span>String s = "C:\\ffmpeg\\input\\";
<span style="white-space:pre">		</span>String s2 = "C:\\ffmpeg\\output\\";
<span style="white-space:pre">		</span>String s3 = "a.avi";
<span style="white-space:pre">		</span>ConV2 conV2 = new ConV2(s, s2, s3);


<span style="white-space:pre">		</span>boolean b = conV2.encoderForFLV();
<span style="white-space:pre">		</span>System.out.println(b);
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>@Test
<span style="white-space:pre">	</span>public void fun2() {
<span style="white-space:pre">		</span>// 生成所有ffmpeg的引數
<span style="white-space:pre">		</span>try {
<span style="white-space:pre">			</span>Process p = Runtime.getRuntime().exec(
<span style="white-space:pre">					</span>"c:\\ffmpeg\\ffmpeg.exe -h full");
<span style="white-space:pre">			</span>// outStream(p.getErrorStream());
<span style="white-space:pre">			</span>outStream(p.getInputStream());


<span style="white-space:pre">		</span>} catch (IOException e) {
<span style="white-space:pre">			</span>e.printStackTrace();
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>// 把流中內容寫入到檔案
<span style="white-space:pre">	</span>private void outStream(InputStream p) {
<span style="white-space:pre">		</span>// 用一個讀輸出流類去讀
<span style="white-space:pre">		</span>BufferedReader br = new BufferedReader(new InputStreamReader(p));
<span style="white-space:pre">		</span>String line = null;
<span style="white-space:pre">		</span>// 逐行讀取輸出到控制檯
<span style="white-space:pre">		</span>try {
<span style="white-space:pre">			</span>String file = "f:/1.txt";
<span style="white-space:pre">			</span>OutputStreamWriter pw = new OutputStreamWriter(
<span style="white-space:pre">					</span>new FileOutputStream(file));
<span style="white-space:pre">			</span>while ((line = br.readLine()) != null) {
<span style="white-space:pre">				</span>System.out.println(line);
<span style="white-space:pre">				</span>// 寫入到檔案
<span style="white-space:pre">				</span>pw.write(line);
<span style="white-space:pre">				</span>pw.write("\r\n");
<span style="white-space:pre">				</span>pw.flush();
<span style="white-space:pre">			</span>}
<span style="white-space:pre">			</span>pw.close();
<span style="white-space:pre">			</span>br.close();
<span style="white-space:pre">		</span>} catch (IOException e) {
<span style="white-space:pre">			</span>e.printStackTrace();
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>}
}