您好,登錄后才能下訂單哦!
如何在JAVA中使用ffmepg處理視頻?很多新手對此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
路徑:
然后在使用的類中生命一個(gè)全局變量就好
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(); } // 使用這種方式會(huì)在瞬間大量消耗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合并多個(gè)視頻文件 * * @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 h364_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)城的錯(cuò)誤流 final InputStream is2 = process.getErrorStream(); //啟動(dòng)兩個(gè)線程,一個(gè)線程負(fù)責(zé)讀標(biāo)準(zhǔn)輸出流,另一個(gè)負(fù)責(zé)讀標(biāo)準(zhǔn)錯(cuò)誤流 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 輸出文件路徑,會(huì)在該路徑下根據(jù)系統(tǒng)時(shí)間創(chuàng)建目錄,并在此目錄下輸出段視頻 * @param videoTime 總時(shí)間,單位 分鐘 * @param periodTime 小段視頻時(shí)長 單位 分鐘 * @param merge true合并,false單獨(dú)分割 說明:是否將整個(gè)視頻結(jié)尾部分不足一次分割時(shí)間的部分,合并到最后一次分割的視頻中,即false會(huì)比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)時(shí)間創(chuàng)建目錄 File file = createFileBySysTime(outputPath); if (file == null) { System.out.println("分割視頻失敗,創(chuàng)建目錄失敗"); return null; } outputPath = file.getPath() + File.separator; // 更新視頻輸出目錄 // 計(jì)算視頻分割的個(gè)數(shù) int count;// 分割為幾段 float remain = 0; // 不足一次剪輯的剩余時(shí)間 if (merge) { count = (int) (videoTime / periodTime); remain = videoTime % periodTime; // 不足一次剪輯的剩余時(shí)間 } else { count = (int) (videoTime / periodTime) + 1; } System.out.println("將視頻分割為" + count + "段,每段約" + periodTime + "分鐘"); String indexName; // 第 i 個(gè)視頻,打印日志用 final String FFMPEG = ffmpegPath; String startTime; // 每段視頻的開始時(shí)間 String periodVideoName; // 每段視頻的名字,名字規(guī)則:視頻i_時(shí)間段xx_yy float duration; // 每次分割的時(shí)長 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) + "個(gè)視頻"; // 決定是否將整個(gè)視頻結(jié)尾部分不足一次的時(shí)間,合并到最后一次分割的視頻中 if (merge) { if (i == count - 1) { duration = periodTime * 60 + remain * 60;// 將整個(gè)視頻不足一次剪輯的時(shí)間,拼接在最后一次剪裁中 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)時(shí)間創(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)時(shí)間 * 2. 格式化系統(tǒng)時(shí)間 * 3. 創(chuàng)建文件夾 * <p> * 參考:http://www.bubuko.com/infodetail-1685972.html */ public static File createFileBySysTime(String path) { // 1. 讀取系統(tǒng)時(shí)間 Calendar calendar = Calendar.getInstance(); Date time = calendar.getTime(); // 2. 格式化系統(tǒng)時(shí)間 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); String fileName = format.format(time); //獲取系統(tǒng)當(dāng)前時(shí)間并將其轉(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; }
/** * 獲取視頻時(shí)長 單位/秒 * @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: 截取的開始時(shí)間 格式為 00:00:00(時(shí)分秒) endTime: 截取的結(jié)束時(shí)間 格式為00:03:00(時(shí)分秒) devIp: 通道號(hào) 業(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 *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)色板出錯(cuò)"); } 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出錯(cuò)"); } } 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出錯(cuò)"); // } } } catch (Exception e) { System.out.println("生成gif出錯(cuò)"); } } return gifFile; }
Java的基本數(shù)據(jù)類型分為:1、整數(shù)類型,用來表示整數(shù)的數(shù)據(jù)類型。2、浮點(diǎn)類型,用來表示小數(shù)的數(shù)據(jù)類型。3、字符類型,字符類型的關(guān)鍵字是“char”。4、布爾類型,是表示邏輯值的基本數(shù)據(jù)類型。
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。