From 0aa78d876fe4819fb9b7ffc24b9a01e2c9869414 Mon Sep 17 00:00:00 2001
From: shenrongliang <1328040932@qq.com>
Date: 星期一, 21 四月 2025 14:42:03 +0800
Subject: [PATCH] 处理合成片头片尾

---
 yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceImpl.java |  251 +++++++++++++++++++++++++++++++++++++-------------
 yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java |   12 +-
 2 files changed, 192 insertions(+), 71 deletions(-)

diff --git a/yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceImpl.java b/yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceImpl.java
index c52a960..8935f32 100644
--- a/yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceImpl.java
+++ b/yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceImpl.java
@@ -256,94 +256,215 @@
      */
     @Override
     public CommonResult createCompositeVideo(CourseMediaSubtitlesReqVO courseMediaSubtitlesReqVO) {
-        // 鐢熸垚鏃堕棿鎴�
+        //鐢熸垚鏃堕棿鎴�
         String timestamp = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
-        //鐗囧ご鍦板潃
+        // 鐗囧ご鍦板潃
         String titles = courseMediaSubtitlesReqVO.getTitles();
         titles = configApi.getConfigValueByKey("easegen.url") + titles.substring(titles.lastIndexOf("/"));
-        //鐗囧熬鍦板潃
+
+        // 鐗囧熬鍦板潃
         String trailer = courseMediaSubtitlesReqVO.getTrailer();
         trailer = configApi.getConfigValueByKey("easegen.url") + trailer.substring(trailer.lastIndexOf("/"));
+
         String videoUrl = courseMediaSubtitlesReqVO.getVideoUrl();
         String previewUrl = courseMediaSubtitlesReqVO.getPreviewUrl();
+
         List<String> videoUrls = new ArrayList<>();
         videoUrls.add(titles);
-        if (videoUrl != null){
-            videoUrl = configApi.getConfigValueByKey("easegen.url") + videoUrl.substring(videoUrl.lastIndexOf("/"));
-            videoUrls.add(videoUrl);
-            videoUrls.add(trailer);
+        String mainVideoPath = "";
+        if (videoUrl != null) {
+            mainVideoPath = configApi.getConfigValueByKey("easegen.url") + videoUrl.substring(videoUrl.lastIndexOf("/"));
+            videoUrls.add(mainVideoPath);
+            videoUrls.add(mainVideoPath);
         } else if (previewUrl != null) {
-            previewUrl = configApi.getConfigValueByKey("easegen.url") + previewUrl.substring(previewUrl.lastIndexOf("/"));
-            videoUrls.add(previewUrl);
-            videoUrls.add(trailer);
+            mainVideoPath = configApi.getConfigValueByKey("easegen.url") + previewUrl.substring(previewUrl.lastIndexOf("/"));
+            videoUrls.add(mainVideoPath);
+            videoUrls.add(mainVideoPath);
         }
-        //鍒ゆ柇鏂囦欢澶规槸鍚﹀瓨鍦紝濡傛灉涓嶅瓨鍦ㄥ氨鍒涘缓
-        String filePath = configApi.getConfigValueByKey(HEYGEM_FACE2FACE) +"/compositeVideo/";
+        // 鎻愬彇涓昏棰戠殑鍙傛暟
+        List<String> mainVideoParams = extractMainVideoParams(mainVideoPath);
+        if (mainVideoParams.isEmpty()) {
+            System.err.println("Failed to extract parameters from main video.");
+            return CommonResult.error(BAD_REQUEST.getCode(), "鍚堟垚澶辫触");
+        }
+        // 鍒ゆ柇鏂囦欢澶规槸鍚﹀瓨鍦紝濡傛灉涓嶅瓨鍦ㄥ氨鍒涘缓
+        String filePath = configApi.getConfigValueByKey(HEYGEM_FACE2FACE) + "/compositeVideo/";
         File file = new File(filePath);
         if (!file.exists()) {
             file.mkdirs();
         }
-        String fileListPath = configApi.getConfigValueByKey(HEYGEM_FACE2FACE) +"/compositeVideo/"+timestamp+".txt";
-        try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileListPath))) {
-            for (String path : videoUrls) {
-                writer.write("file '" + path + "'\n");
-            }
-            System.out.println("鏂囦欢鍒楄〃宸茬敓鎴愶細" + fileListPath);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        //鍘绘帀updateReqVO.getName()涓殑绌烘牸鍜岀壒娈婂瓧绗�
-        String newFileName = courseMediaSubtitlesReqVO.getCourseName().replaceAll("[\\s\\p{Punct}]", "");
-        ProcessBuilder builder = new ProcessBuilder(
-                "ffmpeg", "-f", "concat", "-safe", "0", "-i", fileListPath, "-c", "copy",
-                configApi.getConfigValueByKey(HEYGEM_FACE2FACE) + "/compositeVideo/" + timestamp + ".mp4"
-        );
-        builder.redirectErrorStream(true);
-        Process process = null;
+        // 鍒涘缓txt 鏂囦欢
         try {
-            process = builder.start();
-
-            // 浣跨敤 try-with-resources 纭繚娴佸叧闂�
-            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
-                String line;
-                while ((line = reader.readLine()) != null) {
-                    System.out.println(line);
-                }
-            }
-
-            // 绛夊緟 FFmpeg 杩涚▼瀹屾垚
-            int exitCode = process.waitFor();
-            if (exitCode != 0) {
-                throw new RuntimeException("FFmpeg 鎵ц澶辫触锛岄��鍑虹爜锛�" + exitCode);
-            }
-
-            System.out.println("鏈�缁堣棰戝凡鐢熸垚");
-        } catch (IOException | InterruptedException e) {
-            throw new RuntimeException("FFmpeg 鎵ц寮傚父", e);
-        } finally {
-            // 纭繚 Process 鐨勮緭鍏�/閿欒娴佽鍏抽棴
-            if (process != null) {
-                try {
-                    process.getInputStream().close();
-                    process.getErrorStream().close();
-                    process.getOutputStream().close();
-                } catch (IOException e) {
-                    e.printStackTrace();
-                }
-            }
+            createVideosFile(titles, mainVideoPath, trailer);
+        } catch (IOException e) {
+            System.err.println("Error creating videos.txt file: " + e.getMessage());
+            return CommonResult.error(BAD_REQUEST.getCode(), "鍚堟垚澶辫触");
         }
-        byte[] bytes = FileUtil.readBytes(FileUtil.file(configApi.getConfigValueByKey(HEYGEM_FACE2FACE) +"/compositeVideo/"+timestamp+".mp4"));
+        // 杈撳嚭鏂囦欢璺緞
+        String outputFilePath = configApi.getConfigValueByKey(HEYGEM_FACE2FACE) + "/compositeVideo/" + timestamp + ".mp4";
+        // 浣跨敤 FFmpeg 鍚堝苟瑙嗛骞跺簲鐢ㄤ富瑙嗛鐨勫弬鏁�
+        mergeVideos(outputFilePath, mainVideoParams,titles,mainVideoPath,trailer);
+
+        System.out.println("Video merging completed.");
+
+        byte[] bytes = FileUtil.readBytes(FileUtil.file(outputFilePath));
         String compositeVideo = fileApi.createFile(bytes);
+
+        // 鏇存柊鏁版嵁搴撹褰�
         CourseMediaDO courseMediaDO = new CourseMediaDO();
         courseMediaDO.setId(courseMediaSubtitlesReqVO.getId());
         courseMediaDO.setCompositeVideo(compositeVideo);
         int i = courseMediaMapper.updateById(courseMediaDO);
-        FileUtil.del(configApi.getConfigValueByKey(HEYGEM_FACE2FACE) +"/compositeVideo/"+timestamp+".mp4");
-        FileUtil.del(configApi.getConfigValueByKey(HEYGEM_FACE2FACE) +"/compositeVideo/"+timestamp+".txt");
-        System.out.println();
-        if (i>0){
+
+        // 鍒犻櫎涓存椂鏂囦欢
+        FileUtil.del(outputFilePath);
+        System.out.println("涓存椂鏂囦欢宸插垹闄�");
+
+        if (i > 0) {
             return CommonResult.success("鍚堟垚鎴愬姛");
         }
-        return CommonResult.error(BAD_REQUEST.getCode(),"鍚堟垚澶辫触");
+        return CommonResult.error(BAD_REQUEST.getCode(), "鍚堟垚澶辫触");
+    }
+
+    private static void createVideosFile(String introVideoPath, String mainVideoPath, String outroVideoPath) throws IOException {
+        File videosFile = new File("videos.txt");
+        StringBuilder content = new StringBuilder();
+        content.append("file '").append(introVideoPath).append("'\n");
+        content.append("file '").append(mainVideoPath).append("'\n");
+        content.append("file '").append(outroVideoPath).append("'\n");
+
+        java.nio.file.Files.write(videosFile.toPath(), content.toString().getBytes());
+    }
+
+    private static List<String> extractMainVideoParams(String mainVideoPath) {
+        List<String> params = new ArrayList<>();
+        ProcessBuilder processBuilder = new ProcessBuilder(
+                "ffmpeg",
+                "-i", mainVideoPath
+        );
+
+        try {
+            Process process = processBuilder.start();
+
+            BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+            String line;
+            while ((line = errorReader.readLine()) != null) {
+                if (line.contains("Video:")) {
+                    String videoInfo = line.split(":")[1].trim();
+                    String codec = videoInfo.split(", ")[0];
+                    params.add("-c:v");
+                    params.add(codec);
+                } else if (line.contains("Audio:")) {
+                    String audioInfo = line.split(":")[1].trim();
+                    String codec = audioInfo.split(", ")[0];
+                    String bitrate = audioInfo.split(", ")[1].split("\\s+")[0];
+                    params.add("-c:a");
+                    params.add(codec);
+                    params.add("-b:a");
+                    params.add(bitrate);
+                }
+            }
+
+            int exitCode = process.waitFor();
+            if (exitCode == 0) {
+                System.out.println("Parameters extracted successfully.");
+            } else {
+                System.err.println("Parameter extraction failed with exit code: " + exitCode);
+            }
+        } catch (IOException | InterruptedException e) {
+            System.err.println("Error during parameter extraction: " + e.getMessage());
+        }
+
+        return params;
+    }
+
+    private static void mergeVideos(String outputPath, List<String> mainVideoParams, String introVideoPath, String mainVideoPath, String outroVideoPath) {
+        List<String> command = new ArrayList<>();
+        command.add("ffmpeg");
+        command.add("-f");
+        command.add("concat");
+        command.add("-safe");
+        command.add("0");
+        command.add("-i");
+        command.add("videos.txt");
+
+        // 娣诲姞鏄犲皠閫夐」浠ョ‘淇濋煶棰戞祦鐨勪竴鑷存��
+        command.add("-map");
+        command.add("[v]");
+        command.add("-map");
+        command.add("[a]");
+
+        // 娣诲姞杩囨护鍣ㄩ�夐」浠ュ鐞嗘棤澹伴煶鎯呭喌
+        double introDuration = getDurationInSeconds(introVideoPath);
+        double mainDuration = getDurationInSeconds(mainVideoPath);
+        double outroDuration = getDurationInSeconds(outroVideoPath);
+
+        StringBuilder filterComplex = new StringBuilder();
+        filterComplex.append("[0:v][0:a?]overlay=enable='between(t,0,");
+        filterComplex.append(introDuration);
+        filterComplex.append(")'[v0];");
+        filterComplex.append("[1:v][1:a?]overlay=enable='between(t,");
+        filterComplex.append(introDuration);
+        filterComplex.append(",");
+        filterComplex.append(introDuration + mainDuration);
+        filterComplex.append(")'[v1];");
+        filterComplex.append("[2:v][2:a?]overlay=enable='gt(t,");
+        filterComplex.append(introDuration + mainDuration);
+        filterComplex.append(")'[v2];");
+        filterComplex.append("[v0][v1][v2]concat=n=3:v=1:a=0[v];");
+        filterComplex.append("[0:a]aresample=async=1:first_pts=0[a0];");
+        filterComplex.append("[1:a]aresample=async=1:first_pts=0[a1];");
+        filterComplex.append("[2:a]aresample=async=1:first_pts=0[a2];");
+        filterComplex.append("[a0][a1][a2]amerge=inputs=3[a]");
+
+        command.add("-filter_complex");
+        command.add(filterComplex.toString());
+
+        // 娣诲姞涓昏棰戠殑鍙傛暟
+        command.addAll(mainVideoParams);
+        command.add(outputPath);
+
+        ProcessBuilder processBuilder = new ProcessBuilder(command);
+
+        try {
+            Process process = processBuilder.start();
+
+            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+            String line;
+            while ((line = reader.readLine()) != null) {
+                System.out.println(line);
+            }
+
+            int exitCode = process.waitFor();
+            if (exitCode == 0) {
+                System.out.println("Video merged successfully.");
+            } else {
+                System.err.println("Video merging failed with exit code: " + exitCode);
+            }
+        } catch (IOException | InterruptedException e) {
+            System.err.println("Error during video merging: " + e.getMessage());
+        }
+    }
+
+    private static double getDurationInSeconds(String videoPath) {
+        ProcessBuilder processBuilder = new ProcessBuilder(
+                "ffprobe",
+                "-v", "error",
+                "-show_entries", "format=duration",
+                "-of", "default=noprint_wrappers=1:nokey=1",
+                videoPath
+        );
+
+        try {
+            Process process = processBuilder.start();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+            String durationStr = reader.readLine();
+            double duration = Double.parseDouble(durationStr);
+            process.waitFor();
+            return duration;
+        } catch (IOException | InterruptedException | NumberFormatException e) {
+            System.err.println("Error getting duration of video: " + e.getMessage());
+            return 0.0;
+        }
     }
 }
diff --git a/yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java b/yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java
index 71be55f..9721587 100644
--- a/yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java
+++ b/yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java
@@ -167,7 +167,7 @@
                         newFileName2 // 杈撳嚭鏂囦欢鍚�
                 );
             } else if ("2".equals(scene.getHasPerson())) {
-                // 褰撴病鏈変汉鍍忔椂锛宻ubstring1鏀惧湪 cover1 鐨勪笅灞�
+                //cover1鏀惧湪substring1鐨勶紝璁ヽover1鎸′綇substring1,鑳屾櫙鍥緋pt鍐呭鍜屼汉鍍忚棰戜笉鍙橈紝鍙槸灞傜骇鍏崇郴淇敼涓�涓�
                 builder = new ProcessBuilder(
                         "ffmpeg",
                         "-i", cover1, // 鑳屾櫙鍥�
@@ -175,11 +175,11 @@
                         "-i", substring1, // 浜哄儚瑙嗛
                         "-filter_complex",
                         "[0:v]scale=" + Math.round(scene.getBackground().getWidth()) + ":" + Math.round(scene.getBackground().getHeight()) + "[bg];" +
-                                "[1:v]scale=" + Math.round(scene.getComponents().get(1).getWidth()) + ":" + Math.round(scene.getComponents().get(1).getHeight()) + "[v1];" +
-                                "[bg][v1]overlay=x=" + Math.round(scene.getComponents().get(1).getMarginLeft()) + ":y=" + Math.round(scene.getComponents().get(1).getTop()) + "[img];" +
-                                "[2:v]scale=" + Math.round(scene.getComponents().get(0).getWidth()) + ":" + Math.round(scene.getComponents().get(0).getHeight()) + "[v2];" +
-                                "[img][v2]overlay=x=" + Math.round(scene.getComponents().get(0).getMarginLeft()) + ":y=" + Math.round(scene.getComponents().get(0).getTop()),
-                        newFileName2 // 杈撳嚭鏂囦欢鍚�
+                        "[1:v]scale=" + Math.round(scene.getComponents().get(1).getWidth()) + ":" + Math.round(scene.getComponents().get(1).getHeight()) + "[v1];" +
+                        "[bg][v1]overlay=x=" + Math.round(scene.getComponents().get(1).getMarginLeft()) + ":y=" + Math.round(scene.getComponents().get(1).getTop()) + "[img];" +
+                        "[2:v]scale=" + Math.round(scene.getComponents().get(0).getWidth()) + ":" + Math.round(scene.getComponents().get(0).getHeight()) + "[v2];" +
+                        "[v2][img]overlay=x=" + Math.round(scene.getComponents().get(0).getMarginLeft()) + ":y=" + Math.round(scene.getComponents().get(0).getTop()),
+                        newFileName2
                 );
             }
             System.out.println(newFileName2);

--
Gitblit v1.9.3