=======开头重要绿色链接,不看后悔,和下面关联==========
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、运行测试
获得以下结果,即成功:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。