JAVA使用ffmepg處理視頻的方法(壓縮,分片,合并)
FFmepg安裝
路徑:
然后在使用的類中生命一個全局變量就好
private static String ffmpegPath = "C:\\hk\\ffmpeg\\bin\\ffmpeg.exe"; //ffmepg的絕對路徑
視頻壓縮
注意:此壓縮視頻涉及轉(zhuǎn)碼,對cpu的占用比較大(能不壓縮盡量不壓縮)
/**
* 壓縮視頻
* @param convertFile 待轉(zhuǎn)換的文件
* @param targetFile 轉(zhuǎn)換后的目標(biāo)文件
*/
public static void toCompressFile(String convertFile,String targetFile) throws IOException {
List<String> command = new ArrayList<String>();
/**將視頻壓縮為 每秒15幀 平均碼率600k 畫面的寬與高 為1280*720*/
command.add(ffmpegPath);
command.add("-i");
command.add(convertFile);
command.add("-r");
command.add("15");
command.add("-b:v");
command.add("600k");
command.add("-s");
command.add("1280x720");
command.add(targetFile);
ProcessBuilder builder = new ProcessBuilder(command);
Process process = null;
try {
process = builder.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 使用這種方式會在瞬間大量消耗CPU和內(nèi)存等系統(tǒng)資源,所以這里我們需要對流進(jìn)行處理
InputStream errorStream = process.getErrorStream();
InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
BufferedReader br = new BufferedReader(inputStreamReader);
String line = "";
while ((line = br.readLine()) != null) {
}
if (br != null) {
br.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (errorStream != null) {
errorStream.close();
}
logger.info("-------------------壓縮完成---轉(zhuǎn)存文件--"+targetFile+"-------------");
}
獲取視頻合并
/**
* ffmpeg合并多個視頻文件
*
* @param list 需要合并的多視頻url地址以List存放
* @param outputDir 此處是ffmpeg 配置地址,可寫死如“E:/ffmpeg/bin/ffmpeg.exe”
* @param outputFile 合并后的視頻存放地址,如:E:/mergevideo.mp4
* @date: 2021/4/17 9:31
* @return: void
*/
public static String mergeVideo(List<String> list, String outputDir, String outputFile) {
try {
String format1 = "%s -i %s -c copy -bsf:v h264_mp4toannexb -f mpegts %s";
List<String> commandList = new ArrayList<>(6);
List<String> inputList = new ArrayList<>(6);
for (int i = 0; i < list.size(); i++) {
String input = String.format("input%d.ts", i + 1);
String command = String.format(format1, ffmpegPath, list.get(i), outputDir + input);
commandList.add(command);
inputList.add(input);
}
String command = getCommand(outputDir,outputFile, inputList);
commandList.add(command);
Boolean falg = Boolean.FALSE;
for (int i = 0; i < commandList.size(); i++) {
if (execCommand(commandList.get(i)) > 0) falg = true;
}
if (falg) {
for (int i = 0; i < inputList.size(); i++) {
if (i != commandList.size() - 1) {
File file = new File(outputDir + inputList.get(i));
file.delete();
}
}
// //刪除壓縮的文件
// for (String s:list
// ) {
// new File(s).delete();
// }
return outputFile;
} else {
return "fail";
}
} catch (Exception e) {
e.printStackTrace();
logger.error("-----合并失敗!!!!!!" + outputFile);
return "fail";
}
}
private static Integer execCommand(String command) {
logger.info("execCommand.exec command={}",command);
try {
Process process = Runtime.getRuntime().exec(command);
//獲取進(jìn)程的標(biāo)準(zhǔn)輸入流
final InputStream is1 = process.getInputStream();
//獲取進(jìn)城的錯誤流
final InputStream is2 = process.getErrorStream();
//啟動兩個線程,一個線程負(fù)責(zé)讀標(biāo)準(zhǔn)輸出流,另一個負(fù)責(zé)讀標(biāo)準(zhǔn)錯誤流
readInputStream(is1);
readInputStream(is2);
process.waitFor();
process.destroy();
logger.info("-----操作成功" + command + " " + sdf.format(new Date()));
return 1;
} catch (Exception e) {
e.printStackTrace();
System.out.println("-----操作失敗" + command);
return -1;
}
}
private static void readInputStream(InputStream inputStream) {
new Thread(() -> {
BufferedReader br1 = new BufferedReader(new InputStreamReader(inputStream));
try {
String line1;
while ((line1 = br1.readLine()) != null) {
if (line1 != null) {
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
視頻分片(分割)
/**
* 將視頻分割為小段
*
* @param fileName 源文件名字(帶路徑)
* @param outputPath 輸出文件路徑,會在該路徑下根據(jù)系統(tǒng)時間創(chuàng)建目錄,并在此目錄下輸出段視頻
* @param videoTime 總時間,單位 分鐘
* @param periodTime 小段視頻時長 單位 分鐘
* @param merge true合并,false單獨(dú)分割 說明:是否將整個視頻結(jié)尾部分不足一次分割時間的部分,合并到最后一次分割的視頻中,即false會比true多生成一段視頻
*/
public static List<Map<String,Object>> splitVideoFile(String fileName, String outputPath, float videoTime, int periodTime, boolean merge) {
final String TAG = "----------------------------";
// 在outputPath路徑下根據(jù)系統(tǒng)時間創(chuàng)建目錄
File file = createFileBySysTime(outputPath);
if (file == null) {
System.out.println("分割視頻失敗,創(chuàng)建目錄失敗");
return null;
}
outputPath = file.getPath() + File.separator; // 更新視頻輸出目錄
// 計算視頻分割的個數(shù)
int count;// 分割為幾段
float remain = 0; // 不足一次剪輯的剩余時間
if (merge) {
count = (int) (videoTime / periodTime);
remain = videoTime % periodTime; // 不足一次剪輯的剩余時間
} else {
count = (int) (videoTime / periodTime) + 1;
}
System.out.println("將視頻分割為" + count + "段,每段約" + periodTime + "分鐘");
String indexName; // 第 i 個視頻,打印日志用
final String FFMPEG = ffmpegPath;
String startTime; // 每段視頻的開始時間
String periodVideoName; // 每段視頻的名字,名字規(guī)則:視頻i_時間段xx_yy
float duration; // 每次分割的時長
String command;// 執(zhí)行的命令
// 得到視頻后綴 如.mp4
String videoSuffix = fileName.substring(fileName.lastIndexOf("."));//得到點(diǎn)后面的后綴,包括點(diǎn)
Runtime runtime = Runtime.getRuntime(); // 執(zhí)行命令者
List<Map<String,Object>> list =new ArrayList<>();
// 將視頻分割為count段
for (int i = 0; i < count; i++) {
Map<String,Object> map =new HashMap<>();
indexName = "第" + (i+1) + "個視頻";
// 決定是否將整個視頻結(jié)尾部分不足一次的時間,合并到最后一次分割的視頻中
if (merge) {
if (i == count - 1) {
duration = periodTime * 60 + remain * 60;// 將整個視頻不足一次剪輯的時間,拼接在最后一次剪裁中
if(periodTime * i /60 >= 1){
startTime = "0"+periodTime * i /60+ ":00:00";
}else{
startTime = periodTime * i + ":00";
}
periodVideoName = "video" + (i+1) + "_" + periodTime * i + "_end" + videoSuffix;
} else {
duration = periodTime * 60;
if(periodTime * i /60 >= 1){
startTime = "0"+periodTime * i /60+ ":00:00";
}else{
startTime = periodTime * i + ":00";
}
periodVideoName = "video" +(i+1) + "_" + periodTime * i + "_" + periodTime * (i + 1) + videoSuffix;
}
} else {
duration = periodTime * 60;
if(periodTime * i /60 >= 1){
startTime = "0"+periodTime * i /60+ ":00:00";
}else{
startTime = periodTime * i + ":00";
}
periodVideoName = "video" + (i+1) + "_" + periodTime * i + "_" + periodTime * (i + 1) + videoSuffix;
}
// 執(zhí)行分割命令
try {
// 創(chuàng)建命令
command = FFMPEG + " -ss " + startTime +" -accurate_seek "+ " -i " + fileName + " -c copy -t " + duration + " " + outputPath + periodVideoName;
System.out.println(TAG);
System.out.println(indexName);
System.out.println("執(zhí)行命令:" + command);
runtime.exec(command);
System.out.println(indexName + "分割成功");
map.put("videoPath",(outputPath + periodVideoName).replace("\\","/"));
map.put("count",i);
list.add(map);
} catch (Exception e) {
e.printStackTrace();
System.out.println(indexName + "分割失敗!!!!!!");
}
}
//刪除原來的大視頻
new File(fileName).delete();
return list;
}
/**
* 在指定目錄下根據(jù)系統(tǒng)時間創(chuàng)建文件夾
* 文件名字eg:2019-07-02-23-56-31
*
* @param path 路徑:eg: "/Users/amarao/業(yè)余/剪輯/output/";
* 結(jié)果:創(chuàng)建成功/Users/amarao/業(yè)余/剪輯/output/2019-07-03-10-28-05
* <p>
* 步驟:
* 1. 讀取系統(tǒng)時間
* 2. 格式化系統(tǒng)時間
* 3. 創(chuàng)建文件夾
* <p>
* 參考:http://www.bubuko.com/infodetail-1685972.html
*/
public static File createFileBySysTime(String path) {
// 1. 讀取系統(tǒng)時間
Calendar calendar = Calendar.getInstance();
Date time = calendar.getTime();
// 2. 格式化系統(tǒng)時間
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
String fileName = format.format(time); //獲取系統(tǒng)當(dāng)前時間并將其轉(zhuǎn)換為string類型,fileName即文件名
// 3. 創(chuàng)建文件夾
String newPath = path + fileName;
File file = new File(newPath);
//如果文件目錄不存在則創(chuàng)建目錄
if (!file.exists()) {
if (!file.mkdir()) {
System.out.println("當(dāng)前路徑不存在,創(chuàng)建失敗");
return null;
}
}
System.out.println("創(chuàng)建成功" + newPath);
return file;
}
獲取視頻的時長
/**
* 獲取視頻時長 單位/秒
* @param video
* @return
*/
public static long getVideoDuration(File video) {
long duration = 0L;
FFmpegFrameGrabber ff = new FFmpegFrameGrabber(video);
try {
ff.start();
duration = ff.getLengthInTime() / (1000 * 1000 * 60);
ff.stop();
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
}
return duration;
}
視頻剪切
/**
*
*剪切視頻
videoInputPath 需要處理的視頻路徑
startTime: 截取的開始時間 格式為 00:00:00(時分秒)
endTime: 截取的結(jié)束時間 格式為00:03:00(時分秒)
devIp: 通道號 業(yè)務(wù)存在 ,可自行刪除
* */
public static String videoClip(String videoInputPath,String startTime,String endTime,String devIp) throws IOException {
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat dtf=new SimpleDateFormat("yyyyMMddHHmmss");
//判斷轉(zhuǎn)碼文件是否存在
if(!new File(videoInputPath).exists()){
System.out.println("需要處理的視頻不存在");
return null;
}
StringBuffer videoOutPath = new StringBuffer();
videoOutPath.append("C:/video/playBack/"+devIp+"/"+sdf1.format(new Date())+"/clip/");
File file = new File(videoOutPath.toString());
if (!file.exists()){
file.mkdirs();
}
videoOutPath.append(dtf.format(new Date())+".mp4");
List<String> command = new ArrayList<String>();
command.add(ffmpegPath);
command.add("-ss");
command.add(startTime);
command.add("-t");
command.add(endTime);
command.add("-i");
command.add(videoInputPath);
command.add("-c");
command.add("copy");
command.add(videoOutPath.toString());
command.add("-y");
ProcessBuilder builder = new ProcessBuilder(command);
Process process = null;
try {
process = builder.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
InputStream errorStream = process.getErrorStream();
InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
BufferedReader br = new BufferedReader(inputStreamReader);
String line = "";
while ((line = br.readLine()) != null) {
}
if (br != null) {
br.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (errorStream != null) {
errorStream.close();
}
return videoOutPath.toString();
}
視頻轉(zhuǎn)GIF(MP4)
/*
* 視頻轉(zhuǎn)gif
*inputVideoPath 需要轉(zhuǎn)換的視頻
* createGif gif保存的位置
* */
public static String mp4ToGif(String inputVideoPath,String createGif) {
String name = UUID.randomUUID().toString().replaceAll("-", "");
String paletteFile = createGif +name + ".png";
String gifFile = createGif+name + ".gif";
boolean isComplete = false;
//操作ffmpeg生成gif圖片
for (int i = 0; i < 5; i++) {
try {
//生成調(diào)色板
Process p = new ProcessBuilder()
.command(ffmpegPath,
"-v", "warning",
"-ss", "2",
"-t", "10",
"-i", inputVideoPath,
"-vf", "fps=5,scale=400:-1:flags=lanczos,palettegen",
"-y", paletteFile, "-vn")
.redirectError(new File("stderr.txt"))
.start();
isComplete = p.waitFor(10, TimeUnit.SECONDS);
if (!isComplete) {
System.out.println("生成調(diào)色板出錯");
} else {
List<String> command = new ArrayList<String>();
/**將視頻壓縮為 每秒15幀 平均碼率600k 畫面的寬與高 為1280*720*/
command.add(ffmpegPath);
command.add("-v");
command.add("warning");
command.add("-ss");
command.add("2");
command.add("-t");
command.add("10");
command.add("-i");
command.add(inputVideoPath);
command.add("-i");
command.add(paletteFile);
command.add("-lavfi");
command.add("fps=5,scale=400:-1:flags=lanczos [x]; [x][1:v] paletteuse");
command.add("-y");
command.add(gifFile);
command.add("-vn");
ProcessBuilder builder = new ProcessBuilder(command);
Process process = null;
try {
process = builder.start();
isComplete = process.waitFor(10, TimeUnit.SECONDS);
if (isComplete) {
new File(paletteFile).delete();
System.out.println("生成gif成功");
break;
} else {
System.out.println("生成gif出錯");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// process = new ProcessBuilder()
// .command(ffmpegPath,
// "-v", "warning",
// "-ss", "2",
// "-t", "10",
// "-i", "E:\\Video_2021-05-14_113013.mp4",
// "-i", paletteFile,
// "-lavfi", "fps=5,scale=400:-1:flags=lanczos [x]; [x][1:v] paletteuse",
// "-y", gifFile, "-vn")
// .redirectError(new File("stderr.txt"))
// .start();
// isComplete = process.waitFor(10, TimeUnit.SECONDS);
// if (isComplete) {
// System.out.println("生成gif成功");
// break;
// } else {
// System.out.println("生成gif出錯");
// }
}
} catch (Exception e) {
System.out.println("生成gif出錯");
}
}
return gifFile;
}
以上就是JAVA使用ffmepg處理視頻的方法(壓縮,分片,合并)的詳細(xì)內(nèi)容,更多關(guān)于java ffmepg處理視頻的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot之整合MyBatis實(shí)現(xiàn)CRUD方式
這篇文章主要介紹了SpringBoot之整合MyBatis實(shí)現(xiàn)CRUD方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
Servlet連接數(shù)據(jù)庫實(shí)現(xiàn)用戶登錄的實(shí)現(xiàn)示例
本文主要介紹了Servlet連接數(shù)據(jù)庫實(shí)現(xiàn)用戶登錄的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
IntelliJ?IDEA?2023.2最新版激活方法及驗(yàn)證ja-netfilter配置是否成功
隨著2023.2版本的發(fā)布,用戶們渴望了解如何激活這個最新版的IDE,本文將介紹三種可行的激活方案,包括許可證服務(wù)器、許可證代碼和idea?vmoptions配置,幫助讀者成功激活并充分利用IDEA的功能,感興趣的朋友參考下吧2023-08-08
Java語言實(shí)現(xiàn)簡單FTP軟件 FTP軟件效果圖預(yù)覽之上傳功能(3)
這篇文章主要為大家詳細(xì)介紹了Java語言實(shí)現(xiàn)簡單FTP軟件,F(xiàn)TP軟件效果圖預(yù)覽之上傳功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03
SpringBoot集成Swagger使用SpringSecurity控制訪問權(quán)限問題
這篇文章主要介紹了SpringBoot集成Swagger使用SpringSecurity控制訪問權(quán)限問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05
Spring Jpa多數(shù)據(jù)源工程配置過程解析
這篇文章主要介紹了Spring Jpa多數(shù)據(jù)源工程配置過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08
SpringBoot如何返回Json數(shù)據(jù)格式
這篇文章主要介紹了SpringBoot如何返回Json數(shù)據(jù)格式問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03

