diff --git a/src/main/java/com/tuoheng/steam/dos/StreamProcess.java b/src/main/java/com/tuoheng/steam/dos/StreamProcess.java index 6e2efcf..265cabc 100644 --- a/src/main/java/com/tuoheng/steam/dos/StreamProcess.java +++ b/src/main/java/com/tuoheng/steam/dos/StreamProcess.java @@ -15,7 +15,9 @@ public class StreamProcess implements Serializable { ProcessType processType; String fileName; - + public Process getProcess() { + return process; + } public String startTime(){ return TimeUtils.formatDateToString(createTime); diff --git a/src/main/java/com/tuoheng/steam/service/TaskService.java b/src/main/java/com/tuoheng/steam/service/TaskService.java index 5c13eec..5259667 100644 --- a/src/main/java/com/tuoheng/steam/service/TaskService.java +++ b/src/main/java/com/tuoheng/steam/service/TaskService.java @@ -184,13 +184,32 @@ public class TaskService implements ITaskService{ if (!CollectionUtils.isEmpty(currentStreamTask.getStreamProcesses())) { for (StreamProcess streamProcess : currentStreamTask.getStreamProcesses()) { - if(!new File(streamProcess.getFileName()).exists()){ - recordSuccess = false; - logger.error("流录制失败: streamUrl {} taskId {} destroy Process {}", streamUrl,currentStreamTask.getTaskId() ,streamProcess.getInnerProcessId()); - }else { - logger.info("streamUrl {} taskId {} destroy Process {}", streamUrl,currentStreamTask.getTaskId() ,streamProcess.getInnerProcessId()); - } + streamProcess.destroy(); + + try { + if (streamProcess.getProcess() != null) { + boolean finished = streamProcess.getProcess().waitFor(10, TimeUnit.SECONDS); + if (finished) { + logger.info("进程 {} 已完全结束", streamProcess.getInnerProcessId()); + } else { + logger.warn("进程 {} 超时,强制终止", streamProcess.getInnerProcessId()); + streamProcess.getProcess().destroyForcibly(); + } + } + } catch (InterruptedException e) { + logger.warn("等待进程结束被中断: {}", streamProcess.getInnerProcessId()); + Thread.currentThread().interrupt(); + } + + File file = new File(streamProcess.getFileName()); + if(!file.exists() || file.length() == 0){ + recordSuccess = false; + logger.error("流录制失败: streamUrl {} taskId {} Process {} 文件不存在或为空", streamUrl,currentStreamTask.getTaskId() ,streamProcess.getInnerProcessId()); + }else { + logger.info("streamUrl {} taskId {} Process {} 文件有效,大小: {} bytes", streamUrl,currentStreamTask.getTaskId() ,streamProcess.getInnerProcessId(), file.length()); + } + } } diff --git a/src/main/java/com/tuoheng/steam/service/innerService/ProcessService.java b/src/main/java/com/tuoheng/steam/service/innerService/ProcessService.java index 9514be3..17c9ff1 100644 --- a/src/main/java/com/tuoheng/steam/service/innerService/ProcessService.java +++ b/src/main/java/com/tuoheng/steam/service/innerService/ProcessService.java @@ -13,8 +13,10 @@ import org.springframework.stereotype.Service; import java.io.BufferedReader; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; +import java.io.PrintWriter; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -37,6 +39,9 @@ public class ProcessService { private String recordPath; private static final Logger logger = LoggerFactory.getLogger(ProcessService.class); + + // 缓存 FFmpeg 功能支持状态 + private Boolean ffmpegSupportsConcat = null; /** * 任务池 @@ -240,10 +245,10 @@ public class ProcessService { List fileList = new ArrayList<>(); for(StreamProcess streamProcess : streamProcesses){ - File newFile = new File(streamProcess.getFileName()); - if(newFile.exists()){ +// File newFile = new File(streamProcess.getFileName()); +// if(newFile.exists()){ fileList.add(streamProcess.getFileName()); - } +// } } @@ -257,10 +262,28 @@ public class ProcessService { ffmpeg+ " -i %s -c copy %s", fileList.get(0), outFileName); }else { - String filePaths = String.join("|", fileList); + // 使用 concat demuxer 来更好地处理多个 ts 文件 + String concatFile = targetPath + "/concat_" + UUID.randomUUID().toString() + ".txt"; + try (PrintWriter writer = new PrintWriter(new FileWriter(concatFile))) { + for (String fileName : fileList) { + writer.println("file '" + fileName + "'"); + } + } + command = String.format( - ffmpeg+ " -i \"concat:%s\" -c copy %s", - filePaths, outFileName); + ffmpeg+ " -f concat -safe 0 -i %s -c copy %s", + concatFile, outFileName); + + // 合并完成后删除临时 concat 文件 + final String finalConcatFile = concatFile; + loggingService.execute(() -> { + try { + Thread.sleep(5000); // 等待5秒确保合并完成 + new File(finalConcatFile).delete(); + } catch (Exception e) { + logger.warn("删除临时 concat 文件失败: {}", finalConcatFile); + } + }); } logger.info("mergeStream {}", command); ProcessBuilder pb = new ProcessBuilder(command.split(" "));