wenhongquan 2 miesięcy temu
rodzic
commit
8c3d8c54c0

+ 4 - 0
ruoyi-admin/src/main/resources/application.yml

@@ -49,6 +49,10 @@ user:
   difykey: ${difykey:app-bam2jWvQRd16MnfFaaGWK0Lj}
   difykey1: ${difykey1:app-bam2jWvQRd16MnfFaaGWK0Lj}
   ffmpegpath: ${ffmpegpath:/Users/wenhongquan/miniforge3/bin/ffmpeg}
+  mediaserver:
+      url: ${mediaserver:http://127.0.0.1:80}
+      defaultVhost: ${vhost:jtjai}
+      secret : ${secret:EJiP6vrUP4wgn5OS1FJr2jtIt2cxp7EC}
   hls:
     # 直播流保存路径
     path: ${hlspath:./ruoyi/hls/}

+ 32 - 21
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/TblDeviceController.java

@@ -7,6 +7,8 @@ import java.util.Map;
 
 import cn.hutool.core.codec.Base64;
 import cn.hutool.core.codec.Base64Decoder;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONUtil;
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
@@ -118,31 +120,37 @@ public class TblDeviceController extends BaseController {
     @Value("${user.ffmpegpath}")
     private String ffmpegFilePath = "/usr/local/bin/ffmpeg";
 
+
+
+
+    @Value(value = "${user.mediaserver.url}")
+    private String mediaUrl;
+
+    @Value(value = "${user.mediaserver.secret}")
+    private String mediaSecret;
+
+    @Value(value = "${user.mediaserver.defaultVhost}")
+    private String mediavHost;
+
+
     @GetMapping("/rtmp/{from}")
     public R<Object> getHlsCode(@PathVariable("from") String from) {
         Object data = "";
         try {
-
             from = Base64Decoder.decodeStr(from);
             String p[] = from.split("/");
-            if (rtmptohlsmap.get(from) != null && rtmptohlsmap.get(from).isAlive()) {
-
-            } else {
-                File pc = new File(path + p[p.length - 1] + "");
-                if (!pc.exists()) {
-                    pc.mkdir();
-                    pc.setWritable(true, false);
-                    File pc1 = new File(path + p[p.length - 1] + "/playlist.m3u8");
-                    pc1.createNewFile();
-                    pc1.setWritable(true, false);
-
-                }
-                rtmptohlsmap.put(from, Utils.push(ffmpegFilePath, from, path + p[p.length - 1] + "/playlist.m3u8"));
+            String key = p[p.length - 1];
+            //添加
+            String res = HttpUtil.get(mediaUrl + "/index/api/addStreamProxy?secret=" + mediaSecret + "&vhost=" + mediavHost + "&app=hls&stream=" + key + "&url=" + from);
+            if (JSONUtil.parseObj(res).getInt("code") == 0||res.contains("exists")) {
+                data = url+key+"/hls.m3u8";
+                return R.ok(data);
             }
-            data = url + p[p.length - 1] + "/playlist.m3u8";
-        } catch (Exception e) {
-
+        }catch (Exception e){
+            return R.fail("转码失败");
         }
+
+
         return R.ok(data);
     }
 
@@ -153,12 +161,15 @@ public class TblDeviceController extends BaseController {
 
             from = Base64Decoder.decodeStr(from);
             String p[] = from.split("/");
-            if (rtmptohlsmap.get(from) != null && rtmptohlsmap.get(from).isAlive()) {
-                rtmptohlsmap.get(from).interrupt();
-                rtmptohlsmap.remove(from);
+            String key = p[p.length - 1];
+            //添加
+            String res = HttpUtil.get(mediaUrl + "/index/api/delStreamProxy?secret=" + mediaSecret + "&key=" + (mediavHost+"hls/"+key) );
+            if (JSONUtil.parseObj(res).getInt("code") == 0) {
+                data = "success";
+                return R.ok(data);
             }
         } catch (Exception e) {
-
+            return R.fail("关闭转码失败");
         }
         return R.ok(data);
     }

+ 54 - 4
ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/utils/Utils.java

@@ -3,11 +3,14 @@ package org.dromara.system.utils;
 
 import javax.imageio.ImageIO;
 import java.awt.image.BufferedImage;
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 public class Utils {
 
@@ -105,10 +108,14 @@ public class Utils {
         convert.add("copy");
         convert.add("-acodec");
         convert.add("copy");
-        convert.add("-c:a");
-        convert.add("aac");
-        convert.add("-vbsf");
-        convert.add("h264_mp4toannexb");
+        String codecName = getCodecName(input); // 用 ffprobe 获取
+        if (codecName!=null && codecName.contains("h264")) {
+            convert.add("-bsf:v");
+            convert.add("h264_mp4toannexb");
+        } else if (codecName!=null && codecName.contains("hevc")) {
+            convert.add("-bsf:v");
+            convert.add("hevc_mp4toannexb");
+        }
         convert.add("-f");
         convert.add("hls");
         convert.add("-hls_list_size");
@@ -140,6 +147,49 @@ public class Utils {
         return null;
     }
 
+    public static String getCodecName(String input) {
+        String codecName = null;
+        Process process = null;
+        try {
+            ProcessBuilder pb = new ProcessBuilder("ffprobe", "-v", "error", "-select_streams", "v:0",
+                "-read_intervals", "%+#1",
+                "-analyzeduration", "1M",
+                "-probesize", "32k",
+                "-show_entries", "stream=codec_name", "-of", "default=nw=1:nk=1", input);
+            pb.redirectErrorStream(true);
+
+            process = pb.start();
+
+            // 采用多线程读取避免死锁,加入超时机制
+            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+            StringBuilder sb = new StringBuilder();
+            Thread t = new Thread(() -> {
+                try {
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        sb.append(line);
+                        break; // 只读一行
+                    }
+                } catch (Exception ignore) {}
+            });
+            t.start();
+
+            boolean done = process.waitFor(3, java.util.concurrent.TimeUnit.SECONDS); // 最多3秒
+            if (!done) {
+                process.destroyForcibly();
+            }
+
+            t.join(1000); // 最多再等1秒
+            if (sb.length() > 0) {
+                codecName = sb.toString().trim();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (process != null) process.destroyForcibly();
+        }
+        return codecName;
+    }
 
 
 //    public static void main(String[] args) {