java 获取 ffmpeg 转码进度
java
获取 ffmpeg
转码进度
说明:由于需要用到转码,需要获取进度,找很多资料都不是很理想。下面是我自己参考资料改善的。废话不多说。直接上代码,代码如下
-
定义一个实体,主要用于获取视频信息
import com.alibaba.fastjson.annotation.JSONField; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; /** * @author: pinlin * @date: 2021/8/18 14:07 * @description: 视频信息输出实体 */ @NoArgsConstructor @Data public class VideoInfoVO { @JSONField(name = "streams") private List<StreamsVO> streams; @NoArgsConstructor @Data public static class StreamsVO { @JSONField(name = "duration_ts") private String durationTs; @JSONField(name = "duration") private String duration; @JSONField(name = "height") private String height; @JSONField(name = "level") private String level; @JSONField(name = "profile") private String profile; @JSONField(name = "width") private String width; @JSONField(name = "bit_rate") private String bitRate; } }
-
转码工具类代码如下
import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Lists; import com.sf.yh.sys.test.VideoInfoVO; import lombok.extern.slf4j.Slf4j; import java.io.*; import java.util.List; import java.util.Objects; /** * @author: pinlin * @date: 2021/8/16 9:14 * @description: */ @Slf4j public class TranscodeUtil { private static final String X = "x"; private static final String STR_TIME = "time="; private static final String STR_BITRATE = " bitrate="; private static final String DEFAULT_WIDTH = "1280"; private static final String DEFAULT_HEIGHT = "720"; private TranscodeUtil() { throw new IllegalStateException("Utility class"); } /** * 执行视频转码 * * @param sourcePath 目标文件地址,需要加上文件名 * @param targetPath 目标文件地址(转码后存放的文件地址,需要带上文件名) * @author zpl * @date 2021/8/16 16:17 */ public static Boolean transVideo(String sourcePath, String targetPath) { try { File f0 = new File(sourcePath); // 如果传入的命名不是文件,直接结束执行 if (!f0.isFile()) { log.info("传入路径不是文件,请检查 传入的路径为 sourcePath : {}", sourcePath); return Boolean.FALSE; } long startTime = System.currentTimeMillis(); VideoInfoVO videoInfoVO = getFileInfo(sourcePath); log.info("获取视频总耗时 {}", (System.currentTimeMillis() - startTime)); if (Objects.isNull(videoInfoVO)) { return Boolean.FALSE; } VideoInfoVO.StreamsVO streamsVO = videoInfoVO.getStreams().get(0); String width = streamsVO.getWidth() == null ? DEFAULT_WIDTH : streamsVO.getWidth(); String height = streamsVO.getHeight() == null ? DEFAULT_HEIGHT : streamsVO.getHeight(); // 封装命令参数 List<String> command = Lists.newArrayList(); command.add("ffmpeg"); // 把转码过程过程输出到文件中,主要用于进度展示 //command.add("-progress"); //command.add("F:\\公司资料\\video\\视频\\tr\\progress.txt"); command.add("-i"); command.add(sourcePath); command.add("-s"); command.add(width + X + height); command.add("-threads"); command.add("8"); command.add("-pix_fmt"); command.add("yuv420p"); command.add("-vcodec"); command.add("libx264"); command.add("-preset"); command.add("veryslow"); command.add("-profile:v"); command.add("main"); command.add("-level:v"); command.add("4.1"); command.add("-crf"); command.add("30"); command.add("-acodec"); command.add("aac"); // 设置音频采样率 //command.add("-ar"); //command.add("44100"); // 音频声道数 //command.add("-ac"); //command.add("2"); // 设置音频比特率 //command.add("-b:a"); // command.add(60k); // 设置视频比特率 command.add("-b:v"); command.add("90k"); // 禁用音频 command.add("-an"); command.add(targetPath); ProcessBuilder builder = new ProcessBuilder(); builder.command(command); // 临时变量,用于记录是否转码完成 boolean temp = false; Process process = builder.start(); try ( BufferedInputStream in = new BufferedInputStream(process.getErrorStream()); BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { String lineStr; String totalDuration = streamsVO.getDuration(); while ((lineStr = reader.readLine()) != null) { // 转码开始时间索引 int strTimeStartIndex = lineStr.indexOf(STR_TIME); // 判断是否存在,存在则获取转码过程输出的时间 if (strTimeStartIndex > 0 && lineStr.indexOf(STR_BITRATE) > 0) { // 转码过程时间结束索引 int strTimeEndIndex = lineStr.indexOf(STR_BITRATE, strTimeStartIndex); if (strTimeEndIndex > strTimeStartIndex) { // 编码过程时间 String strProcessTime = lineStr.substring(strTimeStartIndex + 5, strTimeEndIndex); log.info("转码进度时间为 {}", strProcessTime); String strParseTime = parseStrTime(strProcessTime); // 判断下,防止索引越界异常 if (strParseTime.length() > 3) { strParseTime = strParseTime.substring(0, 3); } String strTotalTime = totalDuration.split("\\.")[0]; if (strTotalTime.equals(strParseTime)) { temp = true; } } } log.info("进度信息 {}", lineStr); } } // 0 表示正常终止 int status = process.waitFor(); log.info("status {}", status); // 0 表示正常终止 int exitValue = process.exitValue(); log.info("exitValue {}", status); if (status != 0 && exitValue != 0) { log.error("》》》》》》》》》》命令执行失败》》》》》》》》》》》》》》"); } // 终止子进程执行 process.destroy(); return temp; } catch (Exception e) { log.error("创建转码进程异常", e); Thread.currentThread().interrupt(); } log.info("进程结束"); return Boolean.FALSE; } /** * 获取视频的所有信息 * * @param sourcePath 文件路径,带上文件名 * @return {@link VideoInfoVO} * @author zpl * @date 2021/8/16 16:15 */ public static VideoInfoVO getFileInfo(String sourcePath) throws IOException { List<String> command = Lists.newArrayList(); command.add("ffprobe"); command.add("-v"); command.add("quiet"); command.add("-show_streams"); command.add("-print_format"); command.add("json"); command.add(sourcePath); ProcessBuilder builder = new ProcessBuilder(command); Process process = builder.start(); VideoInfoVO videoInfoVO = null; InputStream in = process.getInputStream(); try ( // 使用缓冲流读取 BufferedInputStream buff = new BufferedInputStream(in)) { byte[] bytes = new byte[1024]; // 用来接收读取的字节数组 StringBuilder sb = new StringBuilder(); // 读取到的字节数组长度,为-1时表示没有数据 int length; // 循环取数据 while ((length = buff.read(bytes)) != -1) { // 将读取的内容转换成字符串 sb.append(new String(bytes, 0, length)); } if (sb.length() > 0) { String result = sb.toString(); videoInfoVO = JSON.parseObject(result, VideoInfoVO.class); } } in.close(); process.destroy(); return videoInfoVO; } /** * 解析时间字符串 * * @param strTime 时间字符串 * @return {@link String} * @author zpl * @date 2021/8/18 17:25 */ private static String parseStrTime(String strTime) { if (StrUtil.isBlank(strTime)) { return ""; } String strHour = strTime.substring(0, 1 + 1); String strMinute = strTime.substring(3, 4 + 1); String strSecond = strTime.substring(6, 7 + 1); String strMilliSecond = strTime.substring(9); int totalDuration = (Integer.parseInt(strHour) * 3600 + Integer.parseInt(strMinute) * 60 + Integer.parseInt(strSecond)) * 1000 + Integer.parseInt(strMilliSecond); return String.valueOf(totalDuration); } public static void main(String[] args) { parseStrTime("00:04:13.79"); } }
要在界面实时展示,可以把获取的进度存入缓存,然后前端定时查询即可,既可以通过
WebSocket
来实现,如有描述不对还请大家指出。谢谢。