=======结尾重要绿色链接,不看悔恨,和上面关联==========
JAVA无需本地下载Ffmpeg,实现FfmpegCMD
本文不须要本地下载Ffmpeg,只需引入maven依赖即可
1、增加maven依赖
<!--m3u8视频转码须要FFmpeg依赖--> <dependency> <groupId>ws.schild</groupId> <artifactId>jave-all-deps</artifactId> <version>3.3.1</version> </dependency> <dependency> <groupId>ws.schild</groupId> <artifactId>jave-core</artifactId> <version>3.3.1</version> </dependency> <dependency> <groupId>ws.schild</groupId> <artifactId>jave-nativebin-win64</artifactId> <version>3.3.1</version> </dependency>
2、编写代码
package com.gxw.util;import com.alibaba.fastjson.JSON;import ws.schild.jave.MultimediaObject;import ws.schild.jave.info.MultimediaInfo;import java.io.*;import java.math.BigDecimal;import java.math.RoundingMode;import java.nio.channels.FileChannel;import java.util.HashMap;import java.util.Map;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * @Description:(MP4 转码 HLS m3u8 AES128 加密) * @author: GuoXiangWen * @date: 2022年04月16日 上午08:08:08 */public class EncodeVideoUtils { private static String PREIX = "gxwAwdlpgyn"; //加密文件门路,如:enc.key、enc.keyinfo、m3u8文件、ts文件等 private static String ENC_DIRECTORY = "D:/test-ffmpeg/"+PREIX; //enc.key地址 private static String ENC_KEY_FILE_PATH = null; //enc.keyinfo地址 private static String ENC_KEY_INFO_FILE_PATH = null; //密钥http地址 private static String ENC_HTTP_PATH = "http://127.0.0.1:8848/Student"; //执行胜利0,失败1 private static int CODE_SUCCESS = 0; private static int CODE_FAIL = 1; //$encInfoPath、$encPath是须要替换的ENC_DIRECTORY文件门路 private static String cmd_enc = " -y -i {videoPath} -hls_time 12 -hls_key_info_file $encInfoPath -hls_playlist_type vod -hls_segment_filename $encPath\\{tsName}_%3d.ts $encPath\\{saveM3u8Name} "; public static void main(String[] args) { //视频门路 String videoPath = "D:/test-ffmpeg/test.mp4"; System.out.println(JSON.toJSONString(encodeVideo(videoPath, "test", "1"))); } /** * 第一步:创立enc.keyinfo文件 * * 第二步:HLS m3u8 AES128 加密 * @param videoPath 视频门路 * @param videoType 视频所属类型, * @param index 视频角标,传来递增的 * @return */ public synchronized static Map<String, String> encodeVideo(String videoPath, String videoType, String index) { String videoTypeBP = videoType; String mp4Name = videoPath.substring(videoPath.lastIndexOf('/') + 1, videoPath.lastIndexOf(".mp4")); String saveM3u8Name = PREIX + RandomUtils.generateRandomString(32); //设置tsName cmd_enc = cmd_enc.replace("{tsName}", saveM3u8Name); //设置视频地址 cmd_enc = cmd_enc.replace("{videoPath}", videoPath); //设置视频保留名称地址 cmd_enc = cmd_enc.replace("{saveM3u8Name}", saveM3u8Name + ".m3u8"); if(videoType.equals("") || videoType == null){ videoType = ""; }else { videoType = "/" + Md5Utils.md5Password(videoType, 8); } ENC_DIRECTORY = ENC_DIRECTORY + videoType + "/" + index; ENC_KEY_FILE_PATH = ENC_DIRECTORY + "/" + saveM3u8Name + ".key"; ENC_KEY_INFO_FILE_PATH = ENC_DIRECTORY + "/" + saveM3u8Name + ".keyinfo"; ENC_HTTP_PATH = ENC_HTTP_PATH + "/" + PREIX + videoType + "/" + index + "/" + saveM3u8Name + ".key"; // 先创立两个流动线程的线程池 ExecutorService executor = Executors.newFixedThreadPool(10); //异步执行 //第一步:创立enc.keyinfo文件等 CompletableFuture<String> completableFutureTask = CompletableFuture.supplyAsync(() ->{ //保留加密视频目录enc File encFilePathDir = new File(ENC_DIRECTORY); if (!encFilePathDir.exists()) {// 判断目录是否存在 encFilePathDir.mkdirs(); } //写入文件内容enc.key BufferedWriter bwkey = null; //写入文件内容enc.keyinfo BufferedWriter bwkeyInfo = null; try{//文件 File fileKey = new File(ENC_KEY_FILE_PATH); File fileKeyInfo = new File(ENC_KEY_INFO_FILE_PATH); //初始化存在删除 if(fileKey.exists()) { fileKey.delete(); } if(fileKeyInfo.exists()) { fileKeyInfo.delete(); } bwkey = new BufferedWriter(new FileWriter(fileKey)); bwkeyInfo = new BufferedWriter(new FileWriter(fileKeyInfo)); //写入key--自定义的AES128加密的密匙 bwkey.write("********换成本人加密钥匙即可*******"); //写入keyInfo //密匙URL地址,能够对该URL鉴权 bwkeyInfo.write(ENC_HTTP_PATH); bwkeyInfo.newLine(); //全门路,绝对路径 bwkeyInfo.write(ENC_KEY_FILE_PATH); bwkey.flush(); bwkeyInfo.flush(); }catch(IOException e){ e.printStackTrace(); //复原默认 ENC_KEY_INFO_FILE_PATH = null; } finally{ try { //肯定要敞开文件 bwkey.close(); bwkeyInfo.close(); } catch (IOException e) { e.printStackTrace(); } } return ENC_KEY_INFO_FILE_PATH; }, executor).exceptionally(e -> { System.out.println(e); return "false"; }); //异步执行 //第二步:HLS m3u8 AES128 加密 CompletableFuture<Integer> completableFutureTaskHls = completableFutureTask.thenApplyAsync((String encKeyInfoFilePath)->{ if(encKeyInfoFilePath == null || encKeyInfoFilePath.length() == 0) {return CODE_FAIL;} Integer codeTmp = cmdExecut(cmd_enc.replace("$encInfoPath", encKeyInfoFilePath).replace("$encPath", ENC_DIRECTORY)); if(CODE_SUCCESS != codeTmp) {return CODE_FAIL;} System.out.println("类型:"+videoTypeBP+" 视频:" + mp4Name + " 加密,胜利!"); return codeTmp; }, executor).exceptionally(e -> { System.out.println(e); return null; }); //获取执行后果 //code=0示意失常 try { System.out.println(String.format("获取最终执行后果:%s", completableFutureTaskHls.get() == CODE_SUCCESS ? "胜利!" : "失败!")); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } //返回后果 Map<String, String> resultMap = new HashMap<>(); //视频名称 resultMap.put("videoName", mp4Name); //视频大小 resultMap.put("videoSize", getVideoSize(new File(videoPath))); //视频时长 resultMap.put("videoTime", getVideoTime(videoPath)); //保留m3u8加密地址 resultMap.put("saveM3u8Path", ENC_DIRECTORY + "/" + saveM3u8Name + ".m3u8"); return resultMap; } /** * * @Description: (执行ffmpeg自定义命令) * @param: @param cmdStr * @param: @return * @return: Integer * @throws */ public static Integer cmdExecut(String cmdStr) { //code=0示意失常 Integer code = null; FfmpegCmd ffmpegCmd = new FfmpegCmd(); /** * 谬误流 */ InputStream errorStream = null; try { ffmpegCmd.execute(false, true, cmdStr); errorStream = ffmpegCmd.getErrorStream(); //打印过程 int len = 0; while ((len=errorStream.read())!=-1){ System.out.print((char)len); } //code=0示意失常 code = ffmpegCmd.getProcessExitCode(); } catch (IOException e) { e.printStackTrace(); } finally { //敞开资源 ffmpegCmd.close(); } //返回 return code; } /** * 获取视频时长 * * @param FileUrl * @return */ public static String getVideoTime(String FileUrl) { File source = new File(FileUrl); String length = ""; try { MultimediaObject instance = new MultimediaObject(source); MultimediaInfo result = instance.getInfo(); long ls = result.getDuration() / 1000; Integer hour = (int) (ls / 3600); Integer minute = (int) (ls % 3600) / 60; Integer second = (int) (ls - hour * 3600 - minute * 60); String hr = hour.toString(); String mi = minute.toString(); String se = second.toString(); if (hr.length() < 2) { hr = "0" + hr; } if (mi.length() < 2) { mi = "0" + mi; } if (se.length() < 2) { se = "0" + se; } length = hr + ":" + mi + ":" + se; } catch (Exception e) { e.printStackTrace(); } return length; } /** * 获取视频大小 * * @param source * @return */ public static String getVideoSize(File source) { FileChannel fc = null; String size = ""; try { FileInputStream fis = new FileInputStream(source); fc = fis.getChannel(); BigDecimal fileSize = new BigDecimal(fc.size()); size = fileSize.divide(new BigDecimal(1024 * 1024), 2, RoundingMode.HALF_UP) + "MB"; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (null != fc) { try { fc.close(); } catch (IOException e) { e.printStackTrace(); } } } return size; }}
3、运行测试
取得以下后果,即胜利: