|
|
@@ -19,8 +19,9 @@ import java.util.regex.Pattern;
|
|
|
*/
|
|
|
@Slf4j
|
|
|
public class VideoFileProcessor {
|
|
|
- static final ObjectMapper mapper = new ObjectMapper();
|
|
|
-
|
|
|
+ private final static String ffprobe = "/usr/bin/ffprobe";
|
|
|
+ private final static String ffmpeg = "/usr/bin/ffmpeg";
|
|
|
+ private static final ObjectMapper mapper = new ObjectMapper();
|
|
|
|
|
|
/**
|
|
|
* 获取视频最详尽的信息:格式、所有流、程序、章节、元数据
|
|
|
@@ -28,7 +29,7 @@ public class VideoFileProcessor {
|
|
|
public static JsonNode getVideoMetadata(String filePath) {
|
|
|
// 构建全量探测命令
|
|
|
List<String> cmd = Arrays.asList(
|
|
|
- "ffprobe",
|
|
|
+ ffprobe,
|
|
|
"-v", "quiet", // 不打印日志头
|
|
|
"-print_format", "json", // 输出 JSON
|
|
|
"-show_format", // 容器格式信息 (bitrate, size, duration, tags)
|
|
|
@@ -77,16 +78,18 @@ public class VideoFileProcessor {
|
|
|
|
|
|
public static void checkVideo(File inputFile) {
|
|
|
List<String> command = Arrays.asList(
|
|
|
- "ffmpeg", "-v", "error",
|
|
|
+ ffmpeg, "-v", "error",
|
|
|
"-i", inputFile.getAbsolutePath(),
|
|
|
"-f", "null", "-"
|
|
|
);
|
|
|
|
|
|
try {
|
|
|
- OutputHandler outputHandler = new EmptyHandler();
|
|
|
- int exitCode = ShellWrapper.executeFFmpeg(command, outputHandler, outputHandler);
|
|
|
- if (exitCode != 0) {
|
|
|
- String errorMsg = "video invalid";
|
|
|
+ ShellResult shellResult = ShellWrapper.executeWithResult(command);
|
|
|
+ if (shellResult.getExitCode() != 0) {
|
|
|
+ String errorMsg = String.format("exec failed");
|
|
|
+ throw new RuntimeException(errorMsg);
|
|
|
+ } else if (!shellResult.getStdout().isEmpty() || !shellResult.getStderr().isEmpty()) {
|
|
|
+ String errorMsg = String.format("video %s invalid", inputFile.getAbsolutePath());
|
|
|
throw new RuntimeException(errorMsg);
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
@@ -96,7 +99,7 @@ public class VideoFileProcessor {
|
|
|
|
|
|
public static void convertToWebVideo(File inputFile, File outputFile, double duration) {
|
|
|
List<String> command = Arrays.asList(
|
|
|
- "ffmpeg", "-y", "-hide_banner",
|
|
|
+ ffmpeg, "-y", "-hide_banner",
|
|
|
"-i", inputFile.getAbsolutePath(),
|
|
|
"-c:v", "h264_nvenc",
|
|
|
// 1. 替换 -crf 为 NVENC 的质量控制模式
|
|
|
@@ -112,7 +115,7 @@ public class VideoFileProcessor {
|
|
|
outputFile.getAbsolutePath()
|
|
|
);
|
|
|
List<String> command1 = Arrays.asList(
|
|
|
- "ffmpeg", "-y", "-hide_banner",
|
|
|
+ ffmpeg, "-y", "-hide_banner",
|
|
|
"-i", inputFile.getAbsolutePath(),
|
|
|
// CPU 上处理滤镜后再交给 GPU 处理
|
|
|
"-vf", "scale=-2:480",
|
|
|
@@ -131,7 +134,7 @@ public class VideoFileProcessor {
|
|
|
);
|
|
|
|
|
|
List<String> command2 = Arrays.asList(
|
|
|
- "ffmpeg", "-y", "-hide_banner",
|
|
|
+ ffmpeg, "-y", "-hide_banner",
|
|
|
"-i", inputFile.getAbsolutePath(),
|
|
|
// 裁剪滤镜:保留中间 1/3
|
|
|
"-vf", "crop=iw/3:ih:iw/3:0",
|
|
|
@@ -151,7 +154,7 @@ public class VideoFileProcessor {
|
|
|
try {
|
|
|
OutputHandler stdoutHandler = new EmptyHandler();
|
|
|
OutputHandler stderrHandler = new ConvertVideoOutputHandler(duration);
|
|
|
- int exitCode = ShellWrapper.executeFFmpeg(command1, stdoutHandler, stderrHandler);
|
|
|
+ int exitCode = ShellWrapper.executeFFmpeg(command, stdoutHandler, stderrHandler);
|
|
|
if (exitCode != 0) {
|
|
|
String errorMsg = "convert video failed";
|
|
|
throw new RuntimeException(errorMsg);
|
|
|
@@ -173,13 +176,13 @@ public class VideoFileProcessor {
|
|
|
|
|
|
public static void volumeDetect(File videoFile) {
|
|
|
List<String> command = Arrays.asList(
|
|
|
- "ffmpeg", "-hide_banner",
|
|
|
+ ffmpeg, "-hide_banner",
|
|
|
"-i", videoFile.getAbsolutePath(),
|
|
|
"-af", "volumedetect", "-vn", "-sn", "-f", "null", "/dev/null"
|
|
|
);
|
|
|
|
|
|
List<String> command1 = Arrays.asList(
|
|
|
- "ffmpeg", "-hide_banner",
|
|
|
+ ffmpeg, "-hide_banner",
|
|
|
"-i", videoFile.getAbsolutePath(),
|
|
|
"-af", "loudnorm=print_format=summary", "-vn", "-sn", "-f", "null", "/dev/null"
|
|
|
);
|
|
|
@@ -278,48 +281,4 @@ public class VideoFileProcessor {
|
|
|
convertToWebVideo(videoFile, outputFile, duration);
|
|
|
log.info("convert video cost {}s", (System.currentTimeMillis()-start)/1000);*/
|
|
|
}
|
|
|
-
|
|
|
- public static void main(String[] args) {
|
|
|
- String baseDir = "/home/reghao/Downloads/0/2";
|
|
|
- //baseDir = "/home/reghao/disk/2/porn/zzz/flv/";
|
|
|
- for (File videoFile : Objects.requireNonNull(new File(baseDir).listFiles())) {
|
|
|
- //String videoPath = "/home/reghao/Downloads/0/123.flv.mp4";
|
|
|
- //File videoFile = new File(videoPath);
|
|
|
- log.info("process video {}", videoFile.getAbsolutePath());
|
|
|
- //process(videoFile);
|
|
|
- //volumeDetect(videoFile);
|
|
|
-
|
|
|
- /*Map<String, Double> metrics = analyzeLoudness(videoFile.getAbsolutePath());
|
|
|
- if (!metrics.isEmpty()) {
|
|
|
- System.out.println("--- 响度分析报告 ---");
|
|
|
- System.out.printf("整体响度 (I): %.2f LUFS\n", metrics.get("input_i"));
|
|
|
- System.out.printf("真实峰值 (TP): %.2f dB\n", metrics.get("input_tp"));
|
|
|
- System.out.printf("动态范围 (LRA): %.2f LU\n", metrics.get("input_lra"));
|
|
|
- // 决策逻辑
|
|
|
- if (metrics.get("input_i") < -20) {
|
|
|
- System.out.println("建议:该视频音量太小,需要增强。");
|
|
|
- }
|
|
|
- }*/
|
|
|
- }
|
|
|
-
|
|
|
- File inputFile = new File("/home/reghao/Downloads/rtmp.mp4");
|
|
|
- File outputFile = new File("/home/reghao/Downloads/rtmp.mp4.mp4");
|
|
|
-
|
|
|
- String filePath = "/home/reghao/Downloads/123abc.mp4";
|
|
|
- JsonNode meta = getVideoMetadata(inputFile.getAbsolutePath());
|
|
|
- String durationStr = meta.path("format").path("duration").asText();
|
|
|
- double duration = durationStr.isEmpty() ? 0.0 : Double.parseDouble(durationStr);
|
|
|
- convertToWebVideo(inputFile, outputFile, duration);
|
|
|
-
|
|
|
- String listFile = "list.txt";
|
|
|
- String outputPath = "output.mp4";
|
|
|
- List<String> command = Arrays.asList(
|
|
|
- "ffmpeg", "-y", "-hide_banner",
|
|
|
- "-f", "concat", "-safe", "0", "-i", listFile,
|
|
|
- "-c:v", "copy", // 视频流直接拷贝,速度极快
|
|
|
- "-c:a", "aac", // 音频流重新编码,解决时间戳不连续
|
|
|
- "-b:a", "128k",
|
|
|
- outputPath
|
|
|
- );
|
|
|
- }
|
|
|
}
|