浏览代码

新建苹果手机看监控视频项目

zhuoning 3 年之前
父节点
当前提交
d1a1b8b814
共有 80 个文件被更改,包括 2677 次插入0 次删除
  1. 2 0
      rtsphls/.idea/.gitignore
  2. 18 0
      rtsphls/.idea/compiler.xml
  3. 6 0
      rtsphls/.idea/encodings.xml
  4. 36 0
      rtsphls/.idea/inspectionProfiles/Project_Default.xml
  5. 14 0
      rtsphls/.idea/misc.xml
  6. 6 0
      rtsphls/.idea/vcs.xml
  7. 50 0
      rtsphls/pom.xml
  8. 2 0
      rtsphls/rtsphls.iml
  9. 25 0
      rtsphls/src/main/java/com/my/core/CoreController.java
  10. 116 0
      rtsphls/src/main/java/com/my/core/commandManager/CommandManager.java
  11. 327 0
      rtsphls/src/main/java/com/my/core/commandManager/CommandManagerImpl.java
  12. 19 0
      rtsphls/src/main/java/com/my/core/commandManager/callback/EventCallBack.java
  13. 11 0
      rtsphls/src/main/java/com/my/core/commandManager/callback/EventCallBackType.java
  14. 87 0
      rtsphls/src/main/java/com/my/core/commandManager/callback/worker/EventMsgNetWorker.java
  15. 22 0
      rtsphls/src/main/java/com/my/core/commandManager/commandbuidler/CommandAssembly.java
  16. 73 0
      rtsphls/src/main/java/com/my/core/commandManager/commandbuidler/CommandAssemblyImpl.java
  17. 55 0
      rtsphls/src/main/java/com/my/core/commandManager/commandbuidler/CommandBuidler.java
  18. 21 0
      rtsphls/src/main/java/com/my/core/commandManager/commandbuidler/CommandBuidlerFactory.java
  19. 68 0
      rtsphls/src/main/java/com/my/core/commandManager/commandbuidler/DefaultCommandBuidler.java
  20. 29 0
      rtsphls/src/main/java/com/my/core/commandManager/config/ProgramConfig.java
  21. 178 0
      rtsphls/src/main/java/com/my/core/commandManager/contrller/TaskController.java
  22. 42 0
      rtsphls/src/main/java/com/my/core/commandManager/data/CommandTasker.java
  23. 58 0
      rtsphls/src/main/java/com/my/core/commandManager/data/TaskDao.java
  24. 71 0
      rtsphls/src/main/java/com/my/core/commandManager/data/TaskDaoImpl.java
  25. 41 0
      rtsphls/src/main/java/com/my/core/commandManager/data/TaskerEventMsg.java
  26. 35 0
      rtsphls/src/main/java/com/my/core/commandManager/handler/DefaultOutHandlerMethod.java
  27. 81 0
      rtsphls/src/main/java/com/my/core/commandManager/handler/KeepAliveHandler.java
  28. 122 0
      rtsphls/src/main/java/com/my/core/commandManager/handler/OutHandler.java
  29. 21 0
      rtsphls/src/main/java/com/my/core/commandManager/handler/OutHandlerMethod.java
  30. 42 0
      rtsphls/src/main/java/com/my/core/commandManager/handler/TaskHandler.java
  31. 62 0
      rtsphls/src/main/java/com/my/core/commandManager/handler/TaskHandlerImpl.java
  32. 205 0
      rtsphls/src/main/java/com/my/core/commandManager/test/Test.java
  33. 79 0
      rtsphls/src/main/java/com/my/core/commandManager/util/CommonUtil.java
  34. 182 0
      rtsphls/src/main/java/com/my/core/commandManager/util/ExecUtil.java
  35. 139 0
      rtsphls/src/main/java/com/my/core/commandManager/util/PropertiesUtil.java
  36. 163 0
      rtsphls/src/main/java/com/my/core/commandManager/util/ReflectUtil.java
  37. 21 0
      rtsphls/src/main/java/com/my/core/config/CommonConfig.java
  38. 22 0
      rtsphls/src/main/java/com/my/core/entity/StartParam.java
  39. 30 0
      rtsphls/src/main/java/com/my/core/task/StopTask.java
  40. 2 0
      rtsphls/src/main/resources/application.yml
  41. 13 0
      rtsphls/src/main/resources/defaultFFmpegConfig.properties
  42. 2 0
      rtsphls/target/classes/application.yml
  43. 二进制
      rtsphls/target/classes/com/my/core/CoreController.class
  44. 二进制
      rtsphls/target/classes/com/my/core/commandManager/CommandManager.class
  45. 二进制
      rtsphls/target/classes/com/my/core/commandManager/CommandManagerImpl.class
  46. 二进制
      rtsphls/target/classes/com/my/core/commandManager/callback/EventCallBack.class
  47. 二进制
      rtsphls/target/classes/com/my/core/commandManager/callback/EventCallBackType.class
  48. 二进制
      rtsphls/target/classes/com/my/core/commandManager/callback/worker/EventMsgNetWorker.class
  49. 二进制
      rtsphls/target/classes/com/my/core/commandManager/commandbuidler/CommandAssembly.class
  50. 二进制
      rtsphls/target/classes/com/my/core/commandManager/commandbuidler/CommandAssemblyImpl.class
  51. 二进制
      rtsphls/target/classes/com/my/core/commandManager/commandbuidler/CommandBuidler.class
  52. 二进制
      rtsphls/target/classes/com/my/core/commandManager/commandbuidler/CommandBuidlerFactory.class
  53. 二进制
      rtsphls/target/classes/com/my/core/commandManager/commandbuidler/DefaultCommandBuidler.class
  54. 二进制
      rtsphls/target/classes/com/my/core/commandManager/config/ProgramConfig.class
  55. 二进制
      rtsphls/target/classes/com/my/core/commandManager/contrller/TaskController.class
  56. 二进制
      rtsphls/target/classes/com/my/core/commandManager/data/CommandTasker.class
  57. 二进制
      rtsphls/target/classes/com/my/core/commandManager/data/TaskDao.class
  58. 二进制
      rtsphls/target/classes/com/my/core/commandManager/data/TaskDaoImpl.class
  59. 二进制
      rtsphls/target/classes/com/my/core/commandManager/data/TaskerEventMsg.class
  60. 二进制
      rtsphls/target/classes/com/my/core/commandManager/handler/DefaultOutHandlerMethod.class
  61. 二进制
      rtsphls/target/classes/com/my/core/commandManager/handler/KeepAliveHandler.class
  62. 二进制
      rtsphls/target/classes/com/my/core/commandManager/handler/OutHandler.class
  63. 二进制
      rtsphls/target/classes/com/my/core/commandManager/handler/OutHandlerMethod.class
  64. 二进制
      rtsphls/target/classes/com/my/core/commandManager/handler/TaskHandler.class
  65. 二进制
      rtsphls/target/classes/com/my/core/commandManager/handler/TaskHandlerImpl.class
  66. 二进制
      rtsphls/target/classes/com/my/core/commandManager/test/Test.class
  67. 二进制
      rtsphls/target/classes/com/my/core/commandManager/util/CommonUtil.class
  68. 二进制
      rtsphls/target/classes/com/my/core/commandManager/util/ExecUtil.class
  69. 二进制
      rtsphls/target/classes/com/my/core/commandManager/util/PropertiesUtil.class
  70. 二进制
      rtsphls/target/classes/com/my/core/commandManager/util/ReflectUtil.class
  71. 二进制
      rtsphls/target/classes/com/my/core/config/CommonConfig.class
  72. 二进制
      rtsphls/target/classes/com/my/core/entity/StartParam.class
  73. 二进制
      rtsphls/target/classes/com/my/core/task/StopTask.class
  74. 13 0
      rtsphls/target/classes/defaultFFmpegConfig.properties
  75. 4 0
      rtsphls/target/maven-archiver/pom.properties
  76. 31 0
      rtsphls/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
  77. 31 0
      rtsphls/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
  78. 0 0
      rtsphls/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
  79. 二进制
      rtsphls/target/rtsp.jar
  80. 二进制
      rtsphls/target/rtsp.jar.original

+ 2 - 0
rtsphls/.idea/.gitignore

@@ -0,0 +1,2 @@
+# Default ignored files
+/workspace.xml

+ 18 - 0
rtsphls/.idea/compiler.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <annotationProcessing>
+      <profile name="Maven default annotation processors profile" enabled="true">
+        <sourceOutputDir name="target/generated-sources/annotations" />
+        <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
+        <outputRelativeToContentRoot value="true" />
+        <module name="rtsphls" />
+      </profile>
+    </annotationProcessing>
+  </component>
+  <component name="JavacSettings">
+    <option name="ADDITIONAL_OPTIONS_OVERRIDE">
+      <module name="rtsphls" options="-parameters" />
+    </option>
+  </component>
+</project>

+ 6 - 0
rtsphls/.idea/encodings.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding">
+    <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
+  </component>
+</project>

+ 36 - 0
rtsphls/.idea/inspectionProfiles/Project_Default.xml

@@ -0,0 +1,36 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="TOP_LEVEL_CLASS_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="" />
+        </value>
+      </option>
+      <option name="INNER_CLASS_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="" />
+        </value>
+      </option>
+      <option name="METHOD_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
+        </value>
+      </option>
+      <option name="FIELD_OPTIONS">
+        <value>
+          <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+          <option name="REQUIRED_TAGS" value="" />
+        </value>
+      </option>
+      <option name="IGNORE_DEPRECATED" value="false" />
+      <option name="IGNORE_JAVADOC_PERIOD" value="true" />
+      <option name="IGNORE_DUPLICATED_THROWS" value="false" />
+      <option name="IGNORE_POINT_TO_ITSELF" value="false" />
+      <option name="myAdditionalJavadocTags" value="date" />
+    </inspection_tool>
+  </profile>
+</component>

+ 14 - 0
rtsphls/.idea/misc.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="MavenProjectsManager">
+    <option name="originalFiles">
+      <list>
+        <option value="$PROJECT_DIR$/pom.xml" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>

+ 6 - 0
rtsphls/.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
+  </component>
+</project>

+ 50 - 0
rtsphls/pom.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.example</groupId>
+    <artifactId>RtspToHls</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.1.0.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.12</version>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.76</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>rtsp</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.sonarsource.scanner.maven</groupId>
+                <artifactId>sonar-maven-plugin</artifactId>
+                <version>3.8.0.2131</version>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 2 - 0
rtsphls/rtsphls.iml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4" />

+ 25 - 0
rtsphls/src/main/java/com/my/core/CoreController.java

@@ -0,0 +1,25 @@
+package com.my.core;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 〈一句话功能简述〉<br>
+ * 〈〉
+ *
+ * @author M.Y
+ * @date 2021/4/2
+ * @since 1.0.0
+ */
+@SpringBootApplication
+@RestController
+@EnableAsync
+public class CoreController {
+
+    public static void main(String[] args) {
+        SpringApplication.run(CoreController.class, args);
+    }
+
+}

+ 116 - 0
rtsphls/src/main/java/com/my/core/commandManager/CommandManager.java

@@ -0,0 +1,116 @@
+package com.my.core.commandManager;
+
+
+import com.my.core.commandManager.commandbuidler.CommandAssembly;
+import com.my.core.commandManager.commandbuidler.CommandBuidler;
+import com.my.core.commandManager.config.ProgramConfig;
+import com.my.core.commandManager.data.CommandTasker;
+import com.my.core.commandManager.data.TaskDao;
+import com.my.core.commandManager.handler.TaskHandler;
+
+import java.util.Collection;
+import java.util.Map;
+
+import static com.my.core.commandManager.util.PropertiesUtil.load;
+
+
+/**
+ * FFmpeg命令操作管理器,可执行FFmpeg命令/停止/查询任务信息
+ *
+ * @author eguid
+ * @version 2016年10月29日
+ * @since jdk1.7
+ */
+public interface CommandManager {
+
+    ProgramConfig config = load(ProgramConfig.class);
+
+    /**
+     * 注入自己实现的持久层
+     *
+     * @param taskDao
+     */
+    void setTaskDao(TaskDao taskDao);
+
+    /**
+     * 注入ffmpeg命令处理器
+     *
+     * @param taskHandler
+     */
+    void setTaskHandler(TaskHandler taskHandler);
+
+    /**
+     * 注入ffmpeg命令组装器
+     *
+     * @param commandAssembly
+     */
+    void setCommandAssembly(CommandAssembly commandAssembly);
+
+    /**
+     * 通过命令发布任务(默认命令前不加FFmpeg路径)
+     *
+     * @param id      - 任务标识
+     * @param command - FFmpeg命令
+     * @return
+     */
+    String start(String id, String command, String rtsp, String media);
+
+    /**
+     * 通过命令发布任务
+     *
+     * @param id      - 任务标识
+     * @param commond - FFmpeg命令
+     * @param hasPath - 命令中是否包含FFmpeg执行文件的绝对路径
+     * @return
+     */
+    String start(String id, String commond, boolean hasPath, String rtsp, String media);
+
+    /**
+     * 通过流式命令构建器发布任务
+     *
+     * @param commandBuidler
+     * @return
+     */
+    String start(String id, CommandBuidler commandBuidler, String rtsp, String media);
+
+    /**
+     * 通过组装命令发布任务
+     *
+     * @param assembly -组装命令(详细请参照readme文档说明)
+     * @return
+     */
+    String start(Map<String, String> assembly);
+
+    /**
+     * 停止任务
+     *
+     * @param id
+     * @return
+     */
+    boolean stop(String id);
+
+    /**
+     * 停止全部任务
+     *
+     * @return
+     */
+    int stopAll();
+
+    /**
+     * 通过id查询任务信息
+     *
+     * @param id
+     */
+    CommandTasker query(String id);
+
+    /**
+     * 查询全部任务信息
+     */
+    Collection<CommandTasker> queryAll();
+
+    /**
+     * 销毁一些后台资源和保活线程
+     */
+    void destory();
+
+}

+ 327 - 0
rtsphls/src/main/java/com/my/core/commandManager/CommandManagerImpl.java

@@ -0,0 +1,327 @@
+package com.my.core.commandManager;
+
+import com.my.core.commandManager.commandbuidler.CommandAssembly;
+import com.my.core.commandManager.commandbuidler.CommandAssemblyImpl;
+import com.my.core.commandManager.commandbuidler.CommandBuidler;
+import com.my.core.commandManager.data.CommandTasker;
+import com.my.core.commandManager.data.TaskDao;
+import com.my.core.commandManager.data.TaskDaoImpl;
+import com.my.core.commandManager.handler.*;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * FFmpeg命令操作管理器
+ */
+@Service
+public class CommandManagerImpl implements CommandManager {
+    /**
+     * 任务持久化器
+     */
+    private TaskDao taskDao = null;
+    /**
+     * 任务执行处理器
+     */
+    private TaskHandler taskHandler = null;
+    /**
+     * 命令组装器
+     */
+    private CommandAssembly commandAssembly = null;
+    /**
+     * 任务消息处理器
+     */
+    private OutHandlerMethod ohm = null;
+
+    /**
+     * 保活处理器
+     */
+    private KeepAliveHandler keepAliveHandler = null;
+
+    /**
+     * 全部默认初始化,自动查找配置文件
+     */
+    public CommandManagerImpl() {
+        this(null);
+    }
+
+    /**
+     * 指定任务池大小的初始化,其他使用默认
+     *
+     * @param size
+     */
+    public CommandManagerImpl(Integer size) {
+        init(size);
+    }
+
+    /**
+     * 初始化
+     *
+     * @param taskDao
+     * @param taskHandler
+     * @param commandAssembly
+     * @param ohm
+     * @param size
+     */
+    public CommandManagerImpl(TaskDao taskDao, TaskHandler taskHandler, CommandAssembly commandAssembly, OutHandlerMethod ohm, Integer size) {
+        super();
+        this.taskDao = taskDao;
+        this.taskHandler = taskHandler;
+        this.commandAssembly = commandAssembly;
+        this.ohm = ohm;
+        init(size);
+    }
+
+    /**
+     * 初始化,如果几个处理器未注入,则使用默认处理器
+     *
+     * @param size
+     */
+    public void init(Integer size) {
+        if (config == null) {
+            System.err.println("配置文件加载失败!配置文件不存在或配置错误");
+            return;
+        }
+        boolean iskeepalive = false;
+        if (size == null) {
+            size = config.getSize() == null ? 10 : config.getSize();
+            iskeepalive = config.isKeepalive();
+        }
+
+        if (this.ohm == null) {
+            this.ohm = new DefaultOutHandlerMethod();
+        }
+
+        if (this.taskDao == null) {
+            this.taskDao = new TaskDaoImpl(size);
+            //初始化保活线程
+            if (iskeepalive) {
+                keepAliveHandler = new KeepAliveHandler(taskDao);
+                keepAliveHandler.start();
+            }
+        }
+
+        if (this.taskHandler == null) {
+            this.taskHandler = new TaskHandlerImpl(this.ohm);
+        }
+
+        if (this.commandAssembly == null) {
+            this.commandAssembly = new CommandAssemblyImpl();
+        }
+
+    }
+
+    @Override
+    public void setTaskDao(TaskDao taskDao) {
+        this.taskDao = taskDao;
+    }
+
+    @Override
+    public void setTaskHandler(TaskHandler taskHandler) {
+        this.taskHandler = taskHandler;
+    }
+
+    @Override
+    public void setCommandAssembly(CommandAssembly commandAssembly) {
+        this.commandAssembly = commandAssembly;
+    }
+
+    public void setOhm(OutHandlerMethod ohm) {
+        this.ohm = ohm;
+    }
+
+    /**
+     * 是否已经初始化
+     *
+     * @param b 如果未初始化时是否初始化
+     * @return
+     */
+    public boolean isInit(boolean b) {
+        boolean ret = this.ohm == null || this.taskDao == null || this.taskHandler == null || this.commandAssembly == null;
+        if (ret && b) {
+            init(null);
+        }
+        return ret;
+    }
+
+    @Override
+    public String start(String id, String command,String rtsp,String media) {
+        return start(id, command, false,rtsp,media);
+    }
+
+    @Override
+    public String start(String id, String command, boolean hasPath,String rtsp,String media) {
+        if (isInit(true)) {
+            System.err.println("执行失败,未进行初始化或初始化失败!");
+            return null;
+        }
+        System.out.println("id=" + id + ", command=" + command);
+        if (id != null && command != null) {
+            //构建删除旧任务命令
+            CommandTasker oldTask = taskDao.get(id);
+            System.out.println("=================> oldTask>>"+oldTask);
+            if (oldTask != null) {
+                boolean stopRes = taskHandler.stop(oldTask.getProcess(), oldTask.getThread());
+                System.out.println("停止结果(stopRes)=" + stopRes);
+                int removeRes = taskDao.remove(id);
+                System.out.println("停止结果(removeRes)=" + removeRes);
+            }
+            //构建任务命令
+            CommandTasker tasker = taskHandler.process(id, hasPath ? command : config.getPath() + command,rtsp,media);
+            if (tasker != null) {
+                int ret = taskDao.add(tasker);
+                if (ret > 0) {
+                    return tasker.getId();
+                } else {
+                    // 持久化信息失败,停止处理
+                    taskHandler.stop(tasker.getProcess(), tasker.getThread());
+                    if (config.isDebug()) {
+                        System.err.println("持久化失败,停止任务!");
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String start(Map<String, String> assembly) {
+        // ffmpeg环境是否配置正确
+        if (checkConfig()) {
+            System.err.println("配置未正确加载,无法执行");
+            return null;
+        }
+        // 参数是否符合要求
+        if (assembly == null || assembly.isEmpty() || !assembly.containsKey("appName")) {
+            System.err.println("参数不正确,无法执行");
+            return null;
+        }
+        String appName = (String) assembly.get("appName");
+        if (appName != null && "".equals(appName.trim())) {
+            System.err.println("appName不能为空");
+            return null;
+        }
+        assembly.put("ffmpegPath", config.getPath() + "ffmpeg");
+        String command = commandAssembly.assembly(assembly);
+        //rtsp
+        String rtsp = (String) assembly.get("rtsp");
+        if (rtsp != null && "".equals(rtsp.trim())) {
+            System.err.println("rtsp不能为空");
+            return null;
+        }
+        //media
+        String media = (String) assembly.get("media");
+        if (media != null && "".equals(media.trim())) {
+            System.err.println("media不能为空");
+            return null;
+        }
+        if (command != null) {
+            return start(appName, command, true,rtsp,media);
+        }
+        return null;
+    }
+
+    @Override
+//    @Async("taskExecutor")
+    public String start(String id, CommandBuidler commandBuidler, String rtsp, String media) {
+        // ffmpeg环境是否配置正确
+        if (checkConfig()) {
+            System.err.println("配置未正确加载,无法执行");
+            return null;
+        }
+        //构建Ffmpeg命令
+        String command = commandBuidler.get();
+        if (command != null) {
+            //执行ffmpeg命令
+            return start(id, command, true,rtsp,media);
+        }
+        return null;
+    }
+
+    /**
+     * @Method      : start1
+     * @Description : 异步
+     * @Params      : [id, commandBuidler]
+     * @Return      : java.lang.String
+     *
+     * @Author      : ZhuoNing
+     * @Date        : 2022/4/22
+     * @Time        : 10:57
+     */
+//    @Override
+//    @Async("taskExecutor")
+//    public String start1(String id, CommandBuidler commandBuidler) {
+//        System.out.println("同步执行 start...---------------------------->");
+//        // ffmpeg环境是否配置正确
+//        if (checkConfig()) {
+//            System.err.println("配置未正确加载,无法执行");
+//            return null;
+//        }
+//        //构建Ffmpeg命令
+//        String command = commandBuidler.get();
+//        if (command != null) {
+//            //执行ffmpeg命令
+//            return start(id, command, true);
+//        }
+//        return null;
+//    }
+
+    private boolean checkConfig() {
+        return config == null;
+    }
+
+    @Override
+    public boolean stop(String id) {
+        if (id != null && taskDao.isHave(id)) {
+            if (config.isDebug()) {
+                System.out.println("正在停止任务:" + id);
+            }
+            CommandTasker tasker = taskDao.get(id);
+            if (taskHandler.stop(tasker.getProcess(), tasker.getThread())) {
+                taskDao.remove(id);
+                return true;
+            }
+        }
+        System.err.println("停止任务失败!id=" + id);
+        return false;
+    }
+
+    @Override
+    public int stopAll() {
+        Collection<CommandTasker> list = taskDao.getAll();
+        Iterator<CommandTasker> iter = list.iterator();
+        CommandTasker tasker = null;
+        int index = 0;
+        while (iter.hasNext()) {
+            tasker = iter.next();
+            if (taskHandler.stop(tasker.getProcess(), tasker.getThread())) {
+                taskDao.remove(tasker.getId());
+                index++;
+            }
+        }
+        if (config.isDebug()) {
+            System.out.println("停止了" + index + "个任务!");
+        }
+        return index;
+    }
+
+    @Override
+    public CommandTasker query(String id) {
+        return taskDao.get(id);
+    }
+
+    @Override
+    public Collection<CommandTasker> queryAll() {
+        return taskDao.getAll();
+    }
+
+    @Override
+    public void destory() {
+        if (keepAliveHandler != null) {
+            //安全停止保活线程
+            keepAliveHandler.interrupt();
+        }
+    }
+}

+ 19 - 0
rtsphls/src/main/java/com/my/core/commandManager/callback/EventCallBack.java

@@ -0,0 +1,19 @@
+package com.my.core.commandManager.callback;
+
+import com.my.core.commandManager.data.CommandTasker;
+
+/**
+ * 事件回调
+ *
+ * @author eguid
+ */
+public interface EventCallBack {
+
+    /**
+     * 命令行执行开始事件
+     *
+     * @param t      -事件类型
+     * @param tasker -任务信息
+     */
+    boolean callback(EventCallBackType t, CommandTasker tasker);
+}

+ 11 - 0
rtsphls/src/main/java/com/my/core/commandManager/callback/EventCallBackType.java

@@ -0,0 +1,11 @@
+package com.my.core.commandManager.callback;
+
+/**
+ * 事件回调类型
+ */
+public enum EventCallBackType {
+    exec,//执行命令后通知
+    stop,//停止命令后通知
+    interrupt,//进程中断后通知
+    heartbeat,//主进程存活心跳
+}

+ 87 - 0
rtsphls/src/main/java/com/my/core/commandManager/callback/worker/EventMsgNetWorker.java

@@ -0,0 +1,87 @@
+package com.my.core.commandManager.callback.worker;
+
+import com.my.core.commandManager.CommandManager;
+import com.my.core.commandManager.callback.EventCallBack;
+import com.my.core.commandManager.callback.EventCallBackType;
+import com.my.core.commandManager.data.CommandTasker;
+import com.my.core.commandManager.data.TaskerEventMsg;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * 事件消息独立发送线程
+ *
+ * @author M.Y
+ */
+public class EventMsgNetWorker extends Thread implements EventCallBack {
+
+    // 一个事件消息队列,发送失败的事件消息将会进入队列队尾等待下次再次发送
+    protected static Queue<TaskerEventMsg> queue = null;
+
+    // 一个网络库,用于快速发送http消息
+    private int timeout = 300;
+
+    public EventMsgNetWorker(int timeout) {
+        super();
+        this.timeout = timeout;
+        queue = new ConcurrentLinkedQueue<>();
+    }
+
+    @Override
+    public void run() {
+        for (; ; ) {
+            try {
+                while (queue.peek() != null) {
+                    TaskerEventMsg t = queue.poll();
+                    // 借助网络库发送该消息
+                    String url = CommandManager.config.getCallback();
+                    if (reqGET(url)) {
+                        System.err.println("发送成功");
+                    } else {
+                        System.err.println("发送失败");
+                        // 发送失败的事件消息将会进入队列队尾等待下次再次发送
+                        queue.offer(t);
+                    }
+                }
+            } catch (Exception e) {
+
+            }
+        }
+
+    }
+
+    /**
+     * 发送get请求
+     */
+    private boolean reqGET(String url) {
+        URL realUrl;
+//		PrintWriter out = null;
+        try {
+            realUrl = new URL(url);
+            // 打开和URL之间的连接
+            URLConnection connection = realUrl.openConnection();
+            // 设置通用的请求属性
+            connection.setUseCaches(false);
+            connection.setConnectTimeout(timeout);
+            connection.setRequestProperty("accept", "*/*");
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            connection.setDoOutput(false);
+            connection.setDoInput(false);
+            connection.connect();
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean callback(EventCallBackType ecbt, CommandTasker tasker) {
+        return queue.add(new TaskerEventMsg(ecbt, tasker));
+    }
+
+}

+ 22 - 0
rtsphls/src/main/java/com/my/core/commandManager/commandbuidler/CommandAssembly.java

@@ -0,0 +1,22 @@
+package com.my.core.commandManager.commandbuidler;
+
+import java.util.Map;
+
+/**
+ * 命令组装器接口
+ *
+ * @author eguid
+ * @version 2016年10月29日
+ * @since jdk1.7
+ */
+public interface CommandAssembly {
+    /**
+     * 将参数转为ffmpeg命令
+     *
+     * @param paramMap
+     * @return
+     */
+    public String assembly(Map<String, String> paramMap);
+
+    public String assembly();
+}

+ 73 - 0
rtsphls/src/main/java/com/my/core/commandManager/commandbuidler/CommandAssemblyImpl.java

@@ -0,0 +1,73 @@
+package com.my.core.commandManager.commandbuidler;
+
+import java.util.Map;
+
+/**
+ * 默认命令组装器实现
+ */
+public class CommandAssemblyImpl implements CommandAssembly {
+    /**
+     * @param paramMap 要组装的map
+     */
+    @Override
+    public String assembly(Map<String, String> paramMap) {
+        try {
+            if (paramMap.containsKey("ffmpegPath")) {
+                String ffmpegPath = paramMap.get("ffmpegPath");
+                // -i:输入流地址或者文件绝对地址
+                StringBuilder comm = new StringBuilder(ffmpegPath + " -i ");
+                // 是否有必输项:输入地址,输出地址,应用名,twoPart:0-推一个元码流;1-推一个自定义推流;2-推两个流(一个是自定义,一个是元码)
+                if (paramMap.containsKey("input") && paramMap.containsKey("output") && paramMap.containsKey("appName")
+                        && paramMap.containsKey("twoPart")) {
+                    String input = paramMap.get("input");
+                    String output = paramMap.get("output");
+                    String appName = paramMap.get("appName");
+                    String twoPart = paramMap.get("twoPart");
+                    String codec = paramMap.get("codec");
+                    // 默认h264解码
+                    codec = (codec == null ? "h264" : (String) paramMap.get("codec"));
+                    // 输入地址
+                    comm.append(input);
+                    // 当twoPart为0时,只推一个元码流
+                    if ("0".equals(twoPart)) {
+                        comm.append(" -vcodec " + codec + " -f flv -an " + output + appName);
+                    } else {
+                        // -f :转换格式,默认flv
+                        if (paramMap.containsKey("fmt")) {
+                            String fmt = (String) paramMap.get("fmt");
+                            comm.append(" -f " + fmt);
+                        }
+                        // -r :帧率,默认25;-g :帧间隔
+                        if (paramMap.containsKey("fps")) {
+                            String fps = (String) paramMap.get("fps");
+                            comm.append(" -r " + fps);
+                            comm.append(" -g " + fps);
+                        }
+                        // -s 分辨率 默认是原分辨率
+                        if (paramMap.containsKey("rs")) {
+                            String rs = (String) paramMap.get("rs");
+                            comm.append(" -s " + rs);
+                        }
+                        // 输出地址+发布的应用名
+                        comm.append(" -an " + output + appName);
+                        // 当twoPart为2时推两个流,一个自定义流,一个元码流
+                        if ("2".equals(twoPart)) {
+                            // 一个视频源,可以有多个输出,第二个输出为拷贝源视频输出,不改变视频的各项参数并且命名为应用名+HD
+                            comm.append(" -vcodec copy  -f flv -an ").append(output + appName + "HD");
+                        }
+                    }
+                    return comm.toString();
+                }
+            }
+        } catch (Exception e) {
+            return null;
+        }
+        return null;
+    }
+
+    @Override
+    public String assembly() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}

+ 55 - 0
rtsphls/src/main/java/com/my/core/commandManager/commandbuidler/CommandBuidler.java

@@ -0,0 +1,55 @@
+package com.my.core.commandManager.commandbuidler;
+
+/**
+ * 流式命令行构建器
+ *
+ * @author eguid
+ */
+public interface CommandBuidler {
+
+    /**
+     * 创建命令行
+     *
+     * @param root -命令行运行根目录或FFmpeg可执行文件安装目录
+     * @return
+     */
+    CommandBuidler create(String root);
+
+    /**
+     * 创建默认根目录或默认FFmpeg可执行文件安装目录
+     *
+     * @return
+     */
+    CommandBuidler create();
+
+    /**
+     * 累加键-值命令
+     *
+     * @param key
+     * @param val
+     * @return
+     */
+    CommandBuidler add(String key, String val);
+
+    /**
+     * 累加命令
+     *
+     * @param val
+     * @return
+     */
+    CommandBuidler add(String val);
+
+    /**
+     * 生成完整命令行
+     *
+     * @return
+     */
+    CommandBuidler build();
+
+    /**
+     * 获取已经构建好的命令行
+     *
+     * @return
+     */
+    String get();
+}

+ 21 - 0
rtsphls/src/main/java/com/my/core/commandManager/commandbuidler/CommandBuidlerFactory.java

@@ -0,0 +1,21 @@
+package com.my.core.commandManager.commandbuidler;
+
+/**
+ * 默认流式命令构建器工厂类
+ *
+ * @author eguid
+ */
+public class CommandBuidlerFactory {
+
+    public static CommandBuidler createBuidler() {
+        return new DefaultCommandBuidler();
+    }
+
+    ;
+
+    public static CommandBuidler createBuidler(String rootpath) {
+        return new DefaultCommandBuidler(rootpath);
+    }
+
+    ;
+}

+ 68 - 0
rtsphls/src/main/java/com/my/core/commandManager/commandbuidler/DefaultCommandBuidler.java

@@ -0,0 +1,68 @@
+package com.my.core.commandManager.commandbuidler;
+
+import com.my.core.commandManager.CommandManager;
+
+/**
+ * 默认流式命令行构建器(非线程安全)
+ */
+public class DefaultCommandBuidler implements CommandBuidler {
+
+    StringBuilder buidler = null;
+    String command = null;
+
+    public DefaultCommandBuidler() {
+        create();
+    }
+
+
+    public DefaultCommandBuidler(String rootpath) {
+        create(rootpath);
+    }
+
+
+    @Override
+    public CommandBuidler create(String rootpath) {
+        buidler = new StringBuilder(rootpath);
+        return this;
+    }
+
+    @Override
+    public CommandBuidler create() {
+        return create(CommandManager.config.getPath());
+    }
+
+    @Override
+    public CommandBuidler add(String key, String val) {
+        return add(key).add(val);
+    }
+
+    @Override
+    public CommandBuidler add(String val) {
+        if (buidler != null) {
+            buidler.append(val);
+            addBlankspace();
+        }
+        return this;
+    }
+
+    @Override
+    public CommandBuidler build() {
+        if (buidler != null) {
+            command = buidler.toString();
+        }
+        return this;
+    }
+
+    private void addBlankspace() {
+        buidler.append(" ");
+    }
+
+    @Override
+    public String get() {
+        if (command == null) {
+            build();
+        }
+        return command;
+    }
+
+}

+ 29 - 0
rtsphls/src/main/java/com/my/core/commandManager/config/ProgramConfig.java

@@ -0,0 +1,29 @@
+package com.my.core.commandManager.config;
+
+import lombok.Data;
+
+/**
+ * 程序基础配置
+ *
+ * @author eguid
+ */
+@Data
+public class ProgramConfig {
+
+    // 默认命令行执行根路径
+    private String path;
+    //是否开启debug模式
+    private boolean debug;
+    //任务池大小
+    private Integer size;
+    //回调通知地址
+    private String callback;
+    //是否开启保活
+    private boolean keepalive;
+
+    @Override
+    public String toString() {
+        return "ProgramConfig [path=" + path + ", debug=" + debug + ", size=" + size + ", callback=" + callback
+                + ", keepalive=" + keepalive + "]";
+    }
+}

+ 178 - 0
rtsphls/src/main/java/com/my/core/commandManager/contrller/TaskController.java

@@ -0,0 +1,178 @@
+package com.my.core.commandManager.contrller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.my.core.commandManager.CommandManager;
+import com.my.core.commandManager.commandbuidler.CommandBuidlerFactory;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+import java.util.Random;
+
+/**
+ * @Project : huimv.shiwan
+ * @Package : com.huimv.biosafety.uface.controller
+ * @Description : TODO
+ * @Version : 1.0
+ * @Author : ZhuoNing
+ * @Create : 2020-12-25
+ **/
+@CrossOrigin
+@RestController
+@RequestMapping("/appleVideo")
+public class TaskController {
+    @Resource
+    CommandManager manager;
+
+    @GetMapping("/play")
+    public String play(@RequestParam(value = "url", required = true) String url,
+                       @RequestParam(value = "name", required = true) String name,
+                       @RequestParam(value = "flags", required = true) String flags) throws InterruptedException {
+        url = url.replace("rtsp://", "");
+        String mediaName = getMedia(url);
+        //TEST
+        name = mediaName;
+        //{启动}
+        String media = manager.start(name, CommandBuidlerFactory.createBuidler()
+                .add("ffmpeg")
+                .add("-f", "rtsp")
+                .add("-rtsp_transport", "tcp")
+                .add("-i", "rtsp://" + url)
+                .add("-c", "copy")
+                .add("-f", "hls")
+                .add("-hls_time", "2.0")
+//                .add("-hls_list_size", "1")
+//                .add("-hls_wrap", "15")
+                .add("-hls_list_size", "2")
+                .add(" -hls_flags", flags)
+//                .add("/usr/local/nginx/html/hls/" + mediaName + ".m3u8"),url,mediaName);
+                .add("D:\\nginx-1.20.2\\html\\hls\\"+mediaName+".m3u8"),url,mediaName);
+
+        Thread.sleep(10000);
+        System.out.println("C1 media="+mediaName);
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("url", "/hls/" + mediaName + ".m3u8");
+        jsonObject.put("name", name);
+        jsonObject.put("code", 8200);
+        jsonObject.put("message", "请求成功。");
+        return jsonObject.toJSONString();
+    }
+
+//    @PostMapping("/start2")
+//    public String start2(@RequestBody @Validated StartParam param) {
+//        String name;
+//        if (param.getName() == null || "".equals(param.getName())) {
+//            name = getRandomString(10);
+//        } else {
+//            name = param.getName();
+//        }
+//
+//        CommandTasker query = manager.query(name);
+//        if (query == null) {
+//            manager.start(name, CommandBuidlerFactory.createBuidler()
+//                    .add("ffmpeg").add("-f", "rtsp")
+//                    .add("-rtsp_transport", "tcp")
+//                    .add("-i", param.getUrl())
+//                    .add("-c", "copy")
+//                    .add("-f", "hls")
+//                    .add("-hls_time", "2.0")
+//                    .add("-hls_list_size", "1")
+//                    .add("-hls_wrap", "15")
+//                    .add("/tmp/hls/" + name + ".m3u8"));
+//        } else {
+//            query.setCreateTime(LocalDateTime.now());
+//        }
+//        JSONObject jsonObject = new JSONObject();
+//        jsonObject.put("url", "/hls/" + name + ".m3u8");
+//        jsonObject.put("name", name);
+//        jsonObject.put("code", 8200);
+//        jsonObject.put("message", "请求成功。");
+//        return jsonObject.toJSONString();
+//    }
+
+//    @GetMapping("/start_old1")
+//    public String start_old1(@RequestParam(value = "url", required = true) String url,
+//                       @RequestParam(value = "name", required = true) String name) {
+//        url = url.replace("rtsp://", "");
+//        String mediaName = getMedia(url);
+//
+////        CommandTasker query = manager.query(name);
+////        if (query == null) {
+//        manager.start(name, CommandBuidlerFactory.createBuidler()
+//                .add("ffmpeg").add("-f", "rtsp")
+//                .add("-rtsp_transport", "tcp")
+//                .add("-i", "rtsp://" + url)
+//                .add("-c", "copy")
+//                .add("-f", "hls")
+//                .add("-hls_time", "2.0")
+//                .add("-hls_list_size", "1")
+//                .add("-hls_wrap", "15")
+//                .add("/usr/local/nginx/html/hls/" + mediaName + ".m3u8"));
+////        }else{
+////            query.setCreateTime(LocalDateTime.now());
+////        }
+//        JSONObject jsonObject = new JSONObject();
+//        jsonObject.put("url", "/hls/" + mediaName + ".m3u8");
+//        jsonObject.put("name", name);
+//        jsonObject.put("code", 8200);
+//        jsonObject.put("message", "请求成功。");
+//        return jsonObject.toJSONString();
+//    }
+
+    //    @GetMapping("/start")
+//    public String test(@RequestParam(value = "url", required = true) String url,
+//                       @RequestParam(value = "name", required = true) String name) {
+//        url = url.replace("rtsp://", "");
+//        String mediaName = getMedia(url);
+//
+////        CommandTasker query = manager.query(name);
+////        if (query == null) {
+//        manager.start(name, CommandBuidlerFactory.createBuidler()
+//                .add("ffmpeg").add("-f", "rtsp")
+//                .add("-rtsp_transport", "tcp")
+//                .add("-i", "rtsp://" + url)
+//                .add("-c", "copy")
+//                .add("-f", "hls")
+//                .add("-hls_time", "2.0")
+//                .add("-hls_list_size", "1")
+//                .add("-hls_wrap", "15")
+//                .add("/usr/local/nginx/html/hls/" + mediaName + ".m3u8"));
+////        }else{
+////            query.setCreateTime(LocalDateTime.now());
+////        }
+//        JSONObject jsonObject = new JSONObject();
+//        jsonObject.put("url", "/hls/" + mediaName + ".m3u8");
+//        jsonObject.put("name", name);
+//        jsonObject.put("code", 8200);
+//        jsonObject.put("message", "请求成功。");
+//        return jsonObject.toJSONString();
+//    }
+
+    public String getMedia(String rtsp) {
+        String ipc = rtsp.substring(rtsp.lastIndexOf(".") + 1, rtsp.length());
+        return "m" + ipc;
+    }
+
+    @GetMapping("/stop")
+    public String stop(String name) {
+        manager.stop(name);
+        return "停止成功";
+    }
+
+    @PreDestroy
+    public void destory() {
+        manager.stopAll();
+    }
+
+    private static final Random RANDOM = new Random();
+
+    private static String getRandomString(int length) {
+        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < length; i++) {
+            int number = RANDOM.nextInt(62);
+            sb.append(str.charAt(number));
+        }
+        return sb.toString();
+    }
+}

+ 42 - 0
rtsphls/src/main/java/com/my/core/commandManager/data/CommandTasker.java

@@ -0,0 +1,42 @@
+package com.my.core.commandManager.data;
+
+import com.my.core.commandManager.handler.OutHandler;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 用于存放任务id,任务主进程,任务输出线程
+ */
+@Data
+public class CommandTasker {
+    // 任务id
+    private final String id;
+    //命令行
+    private final String command;
+    // 命令行运行主进程
+    private Process process;
+    // 命令行消息输出子线程
+    private OutHandler thread;
+    //
+    private String rtsp;
+    //
+    private String media;
+
+    private LocalDateTime createTime;
+
+    public CommandTasker(String id, String command) {
+        this.id = id;
+        this.command = command;
+        this.createTime = LocalDateTime.now();
+    }
+
+    public CommandTasker(String id, String command, Process process, OutHandler thread) {
+        this.id = id;
+        this.command = command;
+        this.process = process;
+        this.thread = thread;
+        this.createTime = LocalDateTime.now();
+    }
+
+}

+ 58 - 0
rtsphls/src/main/java/com/my/core/commandManager/data/TaskDao.java

@@ -0,0 +1,58 @@
+package com.my.core.commandManager.data;
+
+import java.util.Collection;
+
+/**
+ * 任务信息持久层接口
+ *
+ * @author eguid
+ * @version 2016年10月29日
+ * @since jdk1.7
+ */
+public interface TaskDao {
+    /**
+     * 通过id查询任务信息
+     *
+     * @param id - 任务ID
+     * @return CommandTasker -任务实体
+     */
+    CommandTasker get(String id);
+
+    /**
+     * 查询全部任务信息
+     *
+     * @return Collection<CommandTasker>
+     */
+    Collection<CommandTasker> getAll();
+
+    /**
+     * 增加任务信息
+     *
+     * @param commandTasker -任务信息实体
+     * @return 增加数量:<1-增加失败,>=1-增加成功
+     */
+    int add(CommandTasker commandTasker);
+
+    /**
+     * 删除id对应的任务信息
+     *
+     * @param id 任务ID
+     * @return 数量:<1-操作失败,>=1-操作成功
+     */
+    int remove(String id);
+
+    /**
+     * 删除全部任务信息
+     *
+     * @return 数量:<1-操作失败,>=1-操作成功
+     */
+    int removeAll();
+
+    /**
+     * 是否存在某个ID
+     *
+     * @param id - 任务ID
+     * @return true:存在,false:不存在
+     */
+    boolean isHave(String id);
+}

+ 71 - 0
rtsphls/src/main/java/com/my/core/commandManager/data/TaskDaoImpl.java

@@ -0,0 +1,71 @@
+package com.my.core.commandManager.data;
+
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * 任务信息持久层实现
+ *
+ * @author M.Y
+ */
+
+public class TaskDaoImpl implements TaskDao {
+    /**
+     * 存放任务信息
+     */
+    private final ConcurrentMap<String, CommandTasker> map;
+
+    public TaskDaoImpl(int size) {
+        map = new ConcurrentHashMap<>(size);
+    }
+
+    @Override
+    public CommandTasker get(String id) {
+        return map.get(id);
+    }
+
+    @Override
+    public Collection<CommandTasker> getAll() {
+        return map.values();
+    }
+
+    @Override
+    public int add(CommandTasker commandTasker) {
+        String id = commandTasker.getId();
+        if (id != null && !map.containsKey(id)) {
+            map.put(commandTasker.getId(), commandTasker);
+            if (map.get(id) != null) {
+                //添加成功返回1
+                return 1;
+            }
+        }
+        //添加失败返回0
+        return 0;
+    }
+
+    @Override
+    public int remove(String id) {
+        if (map.remove(id) != null) {
+            return 1;
+        }
+        return 0;
+    }
+
+    @Override
+    public int removeAll() {
+        int size = map.size();
+        try {
+            map.clear();
+        } catch (Exception e) {
+            return 0;
+        }
+        return size;
+    }
+
+    @Override
+    public boolean isHave(String id) {
+        return map.containsKey(id);
+    }
+
+}

+ 41 - 0
rtsphls/src/main/java/com/my/core/commandManager/data/TaskerEventMsg.java

@@ -0,0 +1,41 @@
+package com.my.core.commandManager.data;
+
+import com.my.core.commandManager.callback.EventCallBackType;
+
+/**
+ * 命令行事件消息
+ *
+ * @author M.Y
+ */
+public class TaskerEventMsg {
+    EventCallBackType ecbt;
+    CommandTasker tasker;
+
+    public TaskerEventMsg(EventCallBackType ecbt, CommandTasker tasker) {
+        super();
+        this.ecbt = ecbt;
+        this.tasker = tasker;
+    }
+
+    public EventCallBackType getEcbt() {
+        return ecbt;
+    }
+
+    public void setEcbt(EventCallBackType ecbt) {
+        this.ecbt = ecbt;
+    }
+
+    public CommandTasker getTasker() {
+        return tasker;
+    }
+
+    public void setTasker(CommandTasker tasker) {
+        this.tasker = tasker;
+    }
+
+    @Override
+    public String toString() {
+        return "CommandEventMsg [ecbt=" + ecbt + ", tasker=" + tasker + "]";
+    }
+
+}

+ 35 - 0
rtsphls/src/main/java/com/my/core/commandManager/handler/DefaultOutHandlerMethod.java

@@ -0,0 +1,35 @@
+package com.my.core.commandManager.handler;
+
+/**
+ * 默认任务消息输出处理
+ */
+public class DefaultOutHandlerMethod implements OutHandlerMethod {
+
+    /**
+     * 任务是否异常中断,如果
+     */
+    public boolean isBroken = false;
+
+    @Override
+    public void parse(String id, String msg) {
+        //过滤消息
+        if (msg.indexOf("fail") != -1) {
+            System.err.println(id + "任务可能发生故障:" + msg);
+            System.err.println("失败,设置中断状态");
+            isBroken = true;
+        } else if (msg.indexOf("miss") != -1) {
+            System.err.println(id + "任务可能发生丢包:" + msg);
+            System.err.println("失败,设置中断状态");
+            isBroken = true;
+        } else {
+            isBroken = false;
+        }
+
+    }
+
+    @Override
+    public boolean isbroken() {
+        return isBroken;
+    }
+
+}

+ 81 - 0
rtsphls/src/main/java/com/my/core/commandManager/handler/KeepAliveHandler.java

@@ -0,0 +1,81 @@
+package com.my.core.commandManager.handler;
+
+import com.my.core.commandManager.data.CommandTasker;
+import com.my.core.commandManager.data.TaskDao;
+import com.my.core.commandManager.util.ExecUtil;
+
+import java.io.IOException;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * 任务保活处理器(一个后台保活线程,用于处理异常中断的持久任务)
+ */
+public class KeepAliveHandler extends Thread {
+    /**
+     * 待处理队列
+     */
+    private static Queue<String> queue = null;
+
+    public int err_index = 0;//错误计数
+
+    public volatile int stop_index = 0;//安全停止线程标记
+
+    /**
+     * 任务持久化器
+     */
+    private TaskDao taskDao = null;
+
+    public KeepAliveHandler(TaskDao taskDao) {
+        super();
+        this.taskDao = taskDao;
+        queue = new ConcurrentLinkedQueue<>();
+    }
+
+    public static void add(String id) {
+        if (queue != null) {
+            queue.offer(id);
+        }
+    }
+
+    public boolean stop(Process process) {
+        if (process != null) {
+            process.destroy();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void run() {
+        for (; stop_index == 0; ) {
+            if (queue == null) {
+                continue;
+            }
+            String id = null;
+            CommandTasker task = null;
+
+            try {
+                while (queue.peek() != null) {
+                    System.err.println("准备重启任务:" + queue);
+                    id = queue.poll();
+                    task = taskDao.get(id);
+                    //重启任务
+                    ExecUtil.restart(task);
+                }
+            } catch (IOException e) {
+                System.err.println(id + " 任务重启失败,详情:" + task);
+                //重启任务失败
+                err_index++;
+            } catch (Exception e) {
+
+            }
+        }
+    }
+
+    @Override
+    public void interrupt() {
+        stop_index = 1;
+    }
+
+}

+ 122 - 0
rtsphls/src/main/java/com/my/core/commandManager/handler/OutHandler.java

@@ -0,0 +1,122 @@
+package com.my.core.commandManager.handler;
+
+import com.my.core.commandManager.CommandManager;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/**
+ * 任务消息输出处理器
+ */
+public class OutHandler extends Thread {
+    /**
+     * 控制状态
+     */
+    private volatile boolean desstatus = true;
+
+    /**
+     * 读取输出流
+     */
+    private BufferedReader br = null;
+
+    /**
+     * 任务ID
+     */
+    private String id = null;
+
+    /**
+     * 消息处理方法
+     */
+    private OutHandlerMethod ohm;
+
+    /**
+     * 创建输出线程(默认立即开启线程)
+     *
+     * @param is
+     * @param id
+     * @param ohm
+     * @return
+     */
+    public static OutHandler create(InputStream is, String id, OutHandlerMethod ohm) {
+        return create(is, id, ohm, true);
+    }
+
+    /**
+     * 创建输出线程
+     *
+     * @param is
+     * @param id
+     * @param ohm
+     * @param start-是否立即开启线程
+     * @return
+     */
+    public static OutHandler create(InputStream is, String id, OutHandlerMethod ohm, boolean start) {
+        OutHandler out = new OutHandler(is, id, ohm);
+        if (start) {
+            out.start();
+        }
+        return out;
+    }
+
+    public void setOhm(OutHandlerMethod ohm) {
+        this.ohm = ohm;
+    }
+
+    public void setDesStatus(boolean desStatus) {
+        this.desstatus = desStatus;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public OutHandlerMethod getOhm() {
+        return ohm;
+    }
+
+    public OutHandler(InputStream is, String id, OutHandlerMethod ohm) {
+        br = new BufferedReader(new InputStreamReader(is));
+        this.id = id;
+        this.ohm = ohm;
+    }
+
+    /**
+     * 重写线程销毁方法,安全的关闭线程
+     */
+    @Override
+    public void destroy() {
+        setDesStatus(false);
+    }
+
+    /**
+     * 执行输出线程
+     */
+    @Override
+    public void run() {
+        String msg = null;
+        try {
+            if (CommandManager.config.isDebug()) {
+                System.out.println(id + "开始推流!");
+            }
+            while (desstatus && (msg = br.readLine()) != null) {
+                ohm.parse(id, msg);
+                if (ohm.isbroken()) {
+                    System.err.println("检测到中断,提交重启任务给保活处理器");
+                    //如果发生异常中断,立即进行保活
+                    //把中断的任务交给保活处理器进行进一步处理
+                    KeepAliveHandler.add(id);
+                }
+            }
+        } catch (IOException e) {
+            System.out.println("发生内部异常错误,自动关闭[" + this.getId() + "]线程");
+            destroy();
+        } finally {
+            if (this.isAlive()) {
+                destroy();
+            }
+        }
+    }
+
+}

+ 21 - 0
rtsphls/src/main/java/com/my/core/commandManager/handler/OutHandlerMethod.java

@@ -0,0 +1,21 @@
+package com.my.core.commandManager.handler;
+
+/**
+ * 输出消息处理
+ */
+public interface OutHandlerMethod {
+    /**
+     * 解析消息
+     *
+     * @param id-任务ID
+     * @param msg     -消息
+     */
+    void parse(String id, String msg);
+
+    /**
+     * 任务是否异常中断
+     *
+     * @return
+     */
+    boolean isbroken();
+}

+ 42 - 0
rtsphls/src/main/java/com/my/core/commandManager/handler/TaskHandler.java

@@ -0,0 +1,42 @@
+package com.my.core.commandManager.handler;
+
+import com.my.core.commandManager.data.CommandTasker;
+
+/**
+ * 任务执行接口
+ */
+public interface TaskHandler {
+    /**
+     * 按照命令执行主进程和输出线程
+     *
+     * @param id
+     * @param command
+     * @return
+     */
+    CommandTasker process(String id, String command, String rtsp, String media);
+
+    /**
+     * 停止主进程(停止主进程需要保证输出线程已经关闭,否则输出线程会出错)
+     *
+     * @param process
+     * @return
+     */
+    boolean stop(Process process);
+
+    /**
+     * 停止输出线程
+     *
+     * @param thread
+     * @return
+     */
+    boolean stop(Thread thread);
+
+    /**
+     * 正确的停止输出线程和主进程
+     *
+     * @param process
+     * @param thread
+     * @return
+     */
+    boolean stop(Process process, Thread thread);
+}

+ 62 - 0
rtsphls/src/main/java/com/my/core/commandManager/handler/TaskHandlerImpl.java

@@ -0,0 +1,62 @@
+package com.my.core.commandManager.handler;
+
+import com.my.core.commandManager.CommandManager;
+import com.my.core.commandManager.data.CommandTasker;
+import com.my.core.commandManager.util.ExecUtil;
+
+import java.io.IOException;
+
+/**
+ * 任务处理实现
+ */
+public class TaskHandlerImpl implements TaskHandler {
+
+    private OutHandlerMethod ohm = null;
+
+    public TaskHandlerImpl(OutHandlerMethod ohm) {
+        this.ohm = ohm;
+    }
+
+    public void setOhm(OutHandlerMethod ohm) {
+        this.ohm = ohm;
+    }
+
+    @Override
+    public CommandTasker process(String id, String command, String rtsp, String media) {
+        CommandTasker tasker = null;
+        try {
+            tasker = ExecUtil.createTasker(id, command, ohm,rtsp,media);
+            if (CommandManager.config.isDebug()) {
+                System.out.println(id + " 执行命令行:" + command);
+            }
+            return tasker;
+        } catch (IOException e) {
+            //运行失败,停止任务
+            ExecUtil.stop(tasker);
+            e.printStackTrace();
+            if (CommandManager.config.isDebug()) {
+                System.err.println(id + " 执行命令失败!进程和输出线程已停止");
+            }
+            // 出现异常说明开启失败,返回null
+            return null;
+        }
+    }
+
+    @Override
+    public boolean stop(Process process) {
+        return ExecUtil.stop(process);
+    }
+
+    @Override
+    public boolean stop(Thread outHandler) {
+        return ExecUtil.stop(outHandler);
+    }
+
+    @Override
+    public boolean stop(Process process, Thread thread) {
+        boolean ret;
+        ret = stop(thread);
+        ret = stop(process);
+        return ret;
+    }
+}

+ 205 - 0
rtsphls/src/main/java/com/my/core/commandManager/test/Test.java

@@ -0,0 +1,205 @@
+package com.my.core.commandManager.test;
+
+import com.my.core.commandManager.CommandManager;
+import com.my.core.commandManager.CommandManagerImpl;
+import com.my.core.commandManager.data.CommandTasker;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 测试
+ *
+ * @author eguid
+ * @version 2017年10月13日
+ * @since jdk1.7
+ */
+public class Test {
+    /**
+     * 命令组装器测试
+     *
+     * @throws InterruptedException
+     */
+    public static void test1() throws InterruptedException {
+        CommandManager manager = new CommandManagerImpl();
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("appName", "test123");
+        map.put("input", "rtsp://admin:admin@192.168.2.236:37779/cam/realmonitor?channel=1&subtype=0");
+        map.put("output", "rtmp://192.168.30.21/live/");
+        map.put("codec", "h264");
+        map.put("fmt", "flv");
+        map.put("fps", "25");
+        map.put("rs", "640x360");
+        map.put("twoPart", "2");
+        // 执行任务,id就是appName,如果执行失败返回为null
+        String id = manager.start(map);
+        System.out.println(id);
+        // 通过id查询
+        CommandTasker info = manager.query(id);
+        System.out.println(info);
+        // 查询全部
+        Collection<CommandTasker> infoList = manager.queryAll();
+        System.out.println(infoList);
+        Thread.sleep(30000);
+        // 停止id对应的任务
+        manager.stop(id);
+    }
+
+    /**
+     * 默认方式,rtsp->rtmp转流单个命令测试
+     *
+     * @throws InterruptedException
+     */
+//    public static void test2() throws InterruptedException {
+//        CommandManager manager = new CommandManagerImpl();
+//        // -rtsp_transport tcp
+//        //测试多个任何同时执行和停止情况
+//        //默认方式发布任务
+//        manager.start("tomcat", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat");
+//
+//        Thread.sleep(30000);
+//        // 停止全部任务
+//        manager.stopAll();
+//    }
+
+    /**
+     * 完整ffmpeg路径测试
+     *
+     * @throws InterruptedException
+     */
+//    public static void test4() throws InterruptedException {
+//        CommandManager manager = new CommandManagerImpl();
+//        // -rtsp_transport tcp
+//        //测试多个任何同时执行和停止情况
+//        //默认方式发布任务
+//        manager.start("tomcat", "D:/TestWorkspaces/FFmpegCommandHandler/src/cc/eguid/FFmpegCommandManager/ffmpeg/ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat", true);
+//
+//        Thread.sleep(30000);
+//        // 停止全部任务
+//        manager.stopAll();
+//    }
+
+    /**
+     * rtsp-rtmp转流多任务测试
+     *
+     * @throws InterruptedException
+     */
+//    public static void test3() throws InterruptedException {
+//        CommandManager manager = new CommandManagerImpl();
+//        // -rtsp_transport tcp
+//        //测试多个任何同时执行和停止情况
+//        //false表示使用配置文件中的ffmpeg路径,true表示本条命令已经包含ffmpeg所在的完整路径
+//        manager.start("tomcat", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat", false);
+//        manager.start("tomcat1", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat1", false);
+//        manager.start("tomcat2", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat2", false);
+//        manager.start("tomcat3", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat3", false);
+//        manager.start("tomcat4", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat4", false);
+//        manager.start("tomcat5", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat5", false);
+//        manager.start("tomcat6", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat6", false);
+//        manager.start("tomcat7", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat7", false);
+//        manager.start("tomcat8", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat8", false);
+//        manager.start("tomcat9", "ffmpeg -i rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov -vcodec copy -acodec copy -f flv -y rtmp://106.14.182.20:1935/rtmp/tomcat9", false);
+//
+//        Thread.sleep(30000);
+//        // 停止全部任务
+//        manager.stopAll();
+//    }
+
+    /**
+     * 测试流式命令行构建器
+     *
+     * @throws InterruptedException
+     */
+//    public static void testStreamCommandAssmbly() throws InterruptedException {
+//        CommandManager manager = new CommandManagerImpl();
+//        manager.start("test1", CommandBuidlerFactory.createBuidler()
+//                .add("ffmpeg").add("-i", "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov")
+//                .add("-rtsp_transport", "tcp")
+//                .add("-vcodec", "copy")
+//                .add("-acodec", "copy")
+//                .add("-f", "flv")
+//                .add("-y").add("rtmp://106.14.182.20:1935/rtmp/test1"));
+//        Thread.sleep(30000);
+//        // 停止全部任务
+//        manager.stopAll();
+//    }
+
+    /**
+     * 测试任务中断自动重启任务
+     *
+     * @throws InterruptedException
+     */
+//    public static void testBroken() throws InterruptedException {
+//        CommandManager manager = new CommandManagerImpl();
+//        manager.start("test1", CommandBuidlerFactory.createBuidler()
+//                .add("ffmpeg").add("-i", "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov")
+//                .add("-rtsp_transport", "tcp")
+//                .add("-vcodec", "copy")
+//                .add("-acodec", "copy")
+//                .add("-f", "flv")
+//                .add("-y").add("rtmp://106.14.182.20:1935/rtmp/test1"));
+//        Thread.sleep(30000);
+//        // 停止全部任务
+//        manager.stopAll();
+//        manager.destory();
+//    }
+
+    /**
+     * 批量测试任务中断自动重启任务
+     *
+     * @throws InterruptedException
+     */
+//    public static void testBrokenMuti() throws InterruptedException {
+//        CommandManager manager = new CommandManagerImpl();
+//        manager.start("test1", CommandBuidlerFactory.createBuidler()
+//                .add("ffmpeg").add("-i", "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov")
+//                .add("-rtsp_transport", "tcp")
+//                .add("-vcodec", "copy")
+//                .add("-acodec", "copy")
+//                .add("-f", "flv")
+//                .add("-y").add("rtmp://106.14.182.20:1935/rtmp/test1"));
+//        manager.start("test2", CommandBuidlerFactory.createBuidler()
+//                .add("ffmpeg").add("-i", "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov")
+//                .add("-rtsp_transport", "tcp")
+//                .add("-vcodec", "copy")
+//                .add("-acodec", "copy")
+//                .add("-f", "flv")
+//                .add("-y").add("rtmp://106.14.182.20:1935/rtmp/test2"));
+//        manager.start("test3", CommandBuidlerFactory.createBuidler()
+//                .add("ffmpeg").add("-i", "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov")
+//                .add("-rtsp_transport", "tcp")
+//                .add("-vcodec", "copy")
+//                .add("-acodec", "copy")
+//                .add("-f", "flv")
+//                .add("-y").add("rtmp://106.14.182.20:1935/rtmp/test3"));
+//        manager.start("test4", CommandBuidlerFactory.createBuidler()
+//                .add("ffmpeg").add("-i", "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov")
+//                .add("-rtsp_transport", "tcp")
+//                .add("-vcodec", "copy")
+//                .add("-acodec", "copy")
+//                .add("-f", "flv")
+//                .add("-y").add("rtmp://106.14.182.20:1935/rtmp/test4"));
+//        manager.start("test5", CommandBuidlerFactory.createBuidler()
+//                .add("ffmpeg").add("-i", "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov")
+////				.add("-rtsp_transport","tcp")
+//                .add("-vcodec", "copy")
+//                .add("-acodec", "copy")
+//                .add("-f", "flv")
+//                .add("-y").add("rtmp://106.14.182.20:1935/rtmp/test5"));
+//        Thread.sleep(30000);
+//        // 停止全部任务
+//        manager.stopAll();
+//        manager.destory();
+//    }
+
+    public static void main(String[] args) throws InterruptedException {
+//		test1();
+//		test2();
+//		test3();
+//		test4();
+//		testStreamCommandAssmbly();
+//		testBroken();
+//        testBrokenMuti();
+    }
+}

+ 79 - 0
rtsphls/src/main/java/com/my/core/commandManager/util/CommonUtil.java

@@ -0,0 +1,79 @@
+package com.my.core.commandManager.util;
+
+import java.io.File;
+import java.util.UUID;
+
+/**
+ * 公共常用方法工具
+ *
+ * @author eguid
+ */
+public class CommonUtil {
+    /**
+     * 当前项目根路径
+     */
+    public static final String rootPath = getProjectRootPath();
+    public static final String TRUE = "true";
+    public static final String NULL_STRING = "";
+    public static final String H_LINE = "-";
+
+    public static String getUUID() {
+        return UUID.randomUUID().toString().trim().replaceAll(H_LINE, NULL_STRING);
+    }
+
+    /**
+     * 是否为空
+     *
+     * @param str
+     * @return boolean true:为空,false:不为空
+     */
+    public static boolean isNull(String str) {
+        return str == null || NULL_STRING.equals(str.trim());
+    }
+
+    /**
+     * 字符串是否是"true"
+     *
+     * @param str
+     * @return
+     */
+    public static boolean isTrue(String str) {
+        return TRUE.equals(str) ? true : false;
+    }
+
+    /**
+     * 获取项目根目录(静态)
+     *
+     * @return
+     */
+    public static String getRootPath() {
+        return rootPath;
+    }
+
+    /**
+     * 获取项目根目录(动态)
+     *
+     * @return
+     */
+    public static String getProjectRootPath() {
+        String path = null;
+        try {
+            path = CommonUtil.class.getResource("/").getPath();
+        } catch (Exception e) {
+            File directory = new File(NULL_STRING);
+            path = directory.getAbsolutePath() + File.separator;
+        }
+        return path;
+    }
+
+    /**
+     * 获取类路径
+     *
+     * @param cla
+     * @return
+     */
+    public static String getClassPath(Class<?> cla) {
+        return cla.getResource("").getPath();
+    }
+
+}

+ 182 - 0
rtsphls/src/main/java/com/my/core/commandManager/util/ExecUtil.java

@@ -0,0 +1,182 @@
+package com.my.core.commandManager.util;
+
+import com.my.core.commandManager.data.CommandTasker;
+import com.my.core.commandManager.handler.OutHandler;
+import com.my.core.commandManager.handler.OutHandlerMethod;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+
+/**
+ * 命令行操作工具类
+ *
+ * @author eguid
+ */
+public class ExecUtil {
+
+    /**
+     * 执行命令行并获取进程
+     *
+     * @param cmd
+     * @return
+     * @throws IOException
+     */
+    public static Process exec(String cmd,String rtsp,String media) throws IOException {
+        Runtime runtime = Runtime.getRuntime();
+        Process process = runtime.exec(cmd);// 执行命令获取主进程
+        return process;
+    }
+
+//    public static Process exec(String cmd,String rtsp,String media) throws IOException {
+////        Runtime runtime = Runtime.getRuntime();
+////        Process process = runtime.exec(cmd);// 执行命令获取主进程
+//        String allPath = "/usr/local/nginx/html/hls/"+media+".m3u8";
+//        //设置第三方应用命令
+//        List<String> list = new ArrayList<>();
+//        list.add("ffmpeg");
+//        list.add("-i");
+////        list.add("rtsp://admin:hmkj6688@192.168.1.109");
+//        list.add("rtsp://"+rtsp);
+//        list.add("-c");
+//        list.add("copy");
+//        list.add("-f");
+//        list.add("hls");
+//        list.add("-hls_time");
+//        list.add("2.0");
+//        list.add("-hls_list_size");
+//        list.add("1");
+//        list.add("-hls_wrap");
+//        list.add("15");
+////        list.add("/usr/local/nginx/html/hls/test.m3u8");
+//        list.add(allPath);
+//        System.out.println("## list="+list.toString());
+//        //
+//        Process process = run(list);
+//        return process;
+//    }
+
+    public static Process run(List cmdList) throws IOException {
+        //创建processBuilder对象
+        ProcessBuilder processBuilder = new ProcessBuilder();
+        processBuilder.command(cmdList);
+        //将标准输入流和错误流合并
+        processBuilder.redirectErrorStream(true);
+        //启动一个进程
+        Process process = processBuilder.start();
+        List<String> listOut = processBuilder.command();
+        System.out.println(">>"+listOut.toString());
+
+        //判断子进程是否存在
+//        process.isAlive();
+        //杀死子进程
+//        process.destroy();
+
+        //===================================================================================
+        //下面没有输出也能正常输出
+
+        //通过标准输入流拿到正常错误的信息
+        InputStream inputStream = process.getInputStream();
+        //转成字符流输出
+        InputStreamReader reader = new InputStreamReader(inputStream, "GBK");
+        //缓冲
+        char[] chars = new char[1024];
+        int len = -1;
+        while ((len = reader.read(chars)) != -1) {
+            String string = new String(chars, 0, len);
+            System.out.println(string);
+        }
+        //关流
+        reader.close();
+        inputStream.close();
+
+        return process;
+    }
+
+    /**
+     * 销毁进程
+     *
+     * @param process
+     * @return
+     */
+    public static boolean stop(Process process) {
+        if (process != null) {
+            process.destroy();
+//            process.exitValue();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 销毁输出线程
+     *
+     * @param outHandler
+     * @return
+     */
+    public static boolean stop(Thread outHandler) {
+        if (outHandler != null && outHandler.isAlive()) {
+            outHandler.stop();
+            outHandler.destroy();
+            return true;
+        }
+        return false;
+    }
+
+    public static void stop(CommandTasker tasker) {
+        if (tasker != null) {
+            stop(tasker.getThread());
+            stop(tasker.getProcess());
+        }
+    }
+
+    /**
+     * 创建命令行任务
+     *
+     * @param id
+     * @param command
+     * @return
+     * @throws IOException
+     */
+    public static CommandTasker createTasker(String id, String command, OutHandlerMethod ohm, String rtsp, String media) throws IOException {
+        // 执行本地命令获取任务主进程
+        Process process = exec(command,rtsp,media);
+        System.out.println("执行------------------------------------->");
+        // 创建输出线程
+        OutHandler outHandler = OutHandler.create(process.getErrorStream(), id, ohm);
+
+        CommandTasker tasker = new CommandTasker(id, command, process, outHandler);
+
+        return tasker;
+    }
+
+    /**
+     * 中断故障缘故重启
+     *
+     * @param tasker
+     * @return
+     * @throws IOException
+     */
+    public static CommandTasker restart(CommandTasker tasker) throws IOException {
+        if (tasker != null) {
+            String id = tasker.getId(), command = tasker.getCommand();
+            OutHandlerMethod ohm = null;
+            if (tasker.getThread() != null) {
+                ohm = tasker.getThread().getOhm();
+            }
+            String rtsp = tasker.getRtsp();
+            String media = tasker.getMedia();
+
+            //安全销毁命令行进程和输出子线程
+            stop(tasker);
+            // 执行本地命令获取任务主进程
+            Process process = exec(command,rtsp,media);
+            tasker.setProcess(process);
+            // 创建输出线程
+            OutHandler outHandler = OutHandler.create(process.getErrorStream(), id, ohm);
+            tasker.setThread(outHandler);
+        }
+        return tasker;
+    }
+}

+ 139 - 0
rtsphls/src/main/java/com/my/core/commandManager/util/PropertiesUtil.java

@@ -0,0 +1,139 @@
+package com.my.core.commandManager.util;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+/**
+ * properties配置文件读取
+ *
+ * @author eguid
+ */
+public class PropertiesUtil {
+    /**
+     * 加载properties配置文件并读取配置项
+     *
+     * @param cl
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T load(Class<T> cl) {
+        InputStream is;
+
+        //尝试从jar包中读取默认配置文件
+        ClassLoader classloader = Thread.currentThread().getContextClassLoader();
+        try {
+            is = classloader.getResourceAsStream("defaultFFmpegConfig.properties");
+            System.err.println("读取默认配置文件:defaultFFmpegConfig.properties");
+        } catch (Exception e2) {
+            System.err.println("没找到默认配置文件:defaultFFmpegConfig.properties");
+            return null;
+        }
+        if (is != null) {
+            Properties pro = new Properties();
+            try {
+                System.err.println("加载配置文件...");
+                pro.load(is);
+                System.err.println("加载配置文件完毕");
+                return (T) load(pro, cl);
+            } catch (IOException e) {
+                System.err.println("加载配置文件失败");
+                return null;
+            }
+
+        }
+        return null;
+    }
+
+    /**
+     * 读取配置项并转换为对应对象
+     *
+     * @param pro
+     * @param cl
+     * @return
+     */
+    public static Object load(Properties pro, Class<?> cl) {
+        try {
+            Map<String, Object> map = getMap(pro);
+            System.err.println("读取的配置项:" + map);
+            Object obj = ReflectUtil.mapToObj(map, cl);
+            System.err.println("转换后的对象:" + obj);
+            return obj;
+        } catch (InstantiationException e) {
+            System.err.println("加载配置文件失败");
+            return null;
+        } catch (IllegalAccessException e) {
+            System.err.println("加载配置文件失败");
+            return null;
+        } catch (IllegalArgumentException e) {
+            System.err.println("加载配置文件失败");
+            return null;
+        } catch (InvocationTargetException e) {
+            System.err.println("加载配置文件失败");
+            return null;
+        }
+    }
+
+    /**
+     * 获取对应文件路径下的文件流
+     *
+     * @param path
+     * @return
+     * @throws FileNotFoundException
+     */
+    public static InputStream getInputStream(String path) throws FileNotFoundException {
+        return new FileInputStream(path);
+    }
+
+    /**
+     * 根据路径获取properties的Map格式内容
+     *
+     * @param path
+     * @return
+     */
+    public static Map<String, Object> getMap(String path) {
+        Properties pro = new Properties();
+        try {
+            pro.load(getInputStream(path));
+            return getMap(pro);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * 根据路径获取properties的Map格式内容
+     *
+     * @param path
+     * @param isRootPath -是否在项目根目录中
+     * @return
+     */
+    public static Map<String, Object> getMap(String path, boolean isRootPath) {
+        return getMap(isRootPath ? CommonUtil.getProjectRootPath() + path : path);
+    }
+
+    /**
+     * Properties配置项转为Map<String, Object>
+     *
+     * @param pro
+     * @return
+     */
+    public static Map<String, Object> getMap(Properties pro) {
+        if (pro == null || pro.isEmpty() || pro.size() < 1) {
+            return null;
+        }
+        Map<String, Object> map = new HashMap<String, Object>();
+        for (Entry<Object, Object> en : pro.entrySet()) {
+            String key = (String) en.getKey();
+            Object value = en.getValue();
+            map.put(key, value);
+        }
+        return map;
+    }
+}

+ 163 - 0
rtsphls/src/main/java/com/my/core/commandManager/util/ReflectUtil.java

@@ -0,0 +1,163 @@
+package com.my.core.commandManager.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * 反射操作工具
+ *
+ * @author eguid
+ */
+public class ReflectUtil {
+
+    public static final String SET = "set";
+    public static final String GET = "get";
+
+    public static Object mapToObj(Map<String, Object> map, Class<?> oc)
+            throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+        Method[] ms = oc.getDeclaredMethods();
+        if (ms == null || ms.length < 1) {
+            return null;
+        }
+        Object obj = getObject(oc);
+        for (Method m : ms) {
+            String methodName = m.getName();
+            String fieldName = getMethodField(methodName, SET);
+            Object value = map.get(fieldName);
+            if (value != null) {
+                setMethodValue(m, obj, typeConvert(value, m));
+            }
+        }
+        return obj;
+    }
+
+    public static Object typeConvert(Object obj, Method m) {
+        return typeConvert(obj, m.getParameterTypes()[0].getName());
+    }
+
+    public static Object typeConvert(Object obj, Field f) {
+        return typeConvert(obj, f.getType().getName());
+    }
+
+    /**
+     * 基础数据转换
+     *
+     * @param obj
+     * @param typeName
+     * @return
+     */
+    public static Object typeConvert(Object obj, String typeName) {
+        // 基础数据都可以转为String
+        String str = String.valueOf(obj);
+        if ("int".equals(typeName) || "java.lang.Integer".equals(typeName)) {
+            return Integer.valueOf(str.trim());
+        } else if ("long".equals(typeName) || "java.lang.Long".equals(typeName)) {
+            return Long.valueOf(str.trim());
+        } else if ("byte".equals(typeName) || "java.lang.Byte".equals(typeName)) {
+            return Byte.valueOf(str.trim());
+        } else if ("short".equals(typeName) || "java.lang.Short".equals(typeName)) {
+            return Short.valueOf(str.trim());
+        } else if ("float".equals(typeName) || "java.lang.Float".equals(typeName)) {
+            return Float.valueOf(str.trim());
+        } else if ("double".equals(typeName) || "java.lang.Double".equals(typeName)) {
+            return Double.valueOf(str.trim());
+        } else if ("boolean".equals(typeName) || "java.lang.Boolean".equals(typeName)) {
+            return CommonUtil.TRUE.equals(str) ? true : false;
+        } else if ("char".equals(typeName) || "java.lang.Character".equals(typeName)) {
+            return Character.valueOf(str.trim().charAt(0));
+        } else if ("java.lang.String".equals(typeName)) {
+            return str;
+        }
+        return null;
+    }
+
+    public static Class<?> getFieldType(Class<?> cl, String fieldName) throws NoSuchFieldException, SecurityException {
+        Field f = cl.getDeclaredField(fieldName);
+        return f.getType();
+    }
+
+    public static Field findField(Class<?> cl, String fieldName) throws NoSuchFieldException, SecurityException {
+        return cl.getDeclaredField(fieldName);
+    }
+
+    /**
+     * 执行方法
+     *
+     * @param m     - 方法
+     * @param obj   - 对象
+     * @param value - 参数
+     * @throws IllegalAccessException
+     * @throws IllegalArgumentException
+     * @throws InvocationTargetException
+     */
+    public static Object setMethodValue(Method m, Object obj, Object... value)
+            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+        m.getParameterTypes();
+        return m.invoke(obj, value);
+    }
+
+    public static Object getFieldValue(Class<?> obj, String FieldName) throws NoSuchFieldException, SecurityException {
+        return obj.getDeclaredField(FieldName);
+    }
+
+    /**
+     * 通过class实例化
+     *
+     * @param oc
+     * @return
+     * @throws InstantiationException
+     * @throws IllegalAccessException
+     */
+    public static Object getObject(Class<?> oc) throws InstantiationException, IllegalAccessException {
+        return oc.newInstance();
+    }
+
+    /**
+     * 获取方法字段
+     *
+     * @param methodName
+     * @param prefix
+     * @return
+     */
+    public static String getMethodField(String methodName, String prefix) {
+        String m = null;
+        if (prefix != null) {
+            if (methodName.indexOf(prefix) >= 0) {
+                m = methodName.substring(prefix.length());
+                return stringFirstLower(m);
+            }
+        }
+        return m;
+    }
+
+    /**
+     * 首字母大写
+     *
+     * @param str
+     * @return
+     */
+    public static String stringFirstUpper(String str) {
+        char[] ch = str.toCharArray();
+        if (ch[0] >= 'a' && ch[0] <= 'z') {
+            ch[0] = (char) (ch[0] - 32);
+        }
+        return new String(ch);
+    }
+
+    /**
+     * 首字母小写
+     *
+     * @param str
+     * @return
+     */
+    public static String stringFirstLower(String str) {
+        char[] ch = str.toCharArray();
+        if (ch[0] >= 'A' && ch[0] <= 'Z') {
+            ch[0] = (char) (ch[0] + 32);
+        }
+        return new String(ch);
+    }
+
+}

+ 21 - 0
rtsphls/src/main/java/com/my/core/config/CommonConfig.java

@@ -0,0 +1,21 @@
+package com.my.core.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+/**
+ *
+ */
+@Configuration
+public class CommonConfig {
+    @Bean
+    public ThreadPoolTaskExecutor taskExecutor() {
+        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
+        threadPoolTaskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
+        threadPoolTaskExecutor.setKeepAliveSeconds(300);
+        threadPoolTaskExecutor.setMaxPoolSize(100);
+        threadPoolTaskExecutor.setQueueCapacity(50);
+        return threadPoolTaskExecutor;
+    }
+}

+ 22 - 0
rtsphls/src/main/java/com/my/core/entity/StartParam.java

@@ -0,0 +1,22 @@
+package com.my.core.entity;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 〈一句话功能简述〉<br>
+ * 〈〉
+ *
+ * @author M.Y
+ * @date 2021/4/14
+ * @since 1.0.0
+ */
+@Data
+public class StartParam {
+
+    @NotBlank(message = "url不能为空")
+    private String url;
+
+    private String name;
+}

+ 30 - 0
rtsphls/src/main/java/com/my/core/task/StopTask.java

@@ -0,0 +1,30 @@
+package com.my.core.task;
+
+import com.my.core.commandManager.CommandManager;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+
+/**
+ * 〈一句话功能简述〉<br>
+ * 〈定时关闭推流,防止前端无法主动停止导致资源堆积〉
+ *
+ * @author M.Y
+ * @date 2021/4/14
+ * @since 1.0.0
+ */
+@EnableScheduling
+@Configuration
+public class StopTask {
+    @Resource
+    CommandManager manager;
+
+    @Scheduled(cron = "* */10 * * * ?")
+    public void stopTask() {
+        manager.queryAll().stream().filter(c -> c.getCreateTime().isBefore(LocalDateTime.now().plusMinutes(-10)))
+                .forEach(c -> manager.stop(c.getId()));
+    }
+}

+ 2 - 0
rtsphls/src/main/resources/application.yml

@@ -0,0 +1,2 @@
+server:
+  port: 8080

+ 13 - 0
rtsphls/src/main/resources/defaultFFmpegConfig.properties

@@ -0,0 +1,13 @@
+#ffmpeg执行路径,一般为ffmpeg的安装目录,该路径只能是目录,不能为具体文件路径,否则会报错
+path=D:\\ffmpeg-5.0.1-essentials_build\\bin\\
+#存放任务的默认Map的初始化大小
+size=20
+#事件回调通知接口地址
+callback=http://127.0.0.1/callback
+#网络超时设置(毫秒)
+timeout=300
+#开启保活线程
+keepalive=true
+#是否输出debug消息
+debug=false
+

+ 2 - 0
rtsphls/target/classes/application.yml

@@ -0,0 +1,2 @@
+server:
+  port: 8080

二进制
rtsphls/target/classes/com/my/core/CoreController.class


二进制
rtsphls/target/classes/com/my/core/commandManager/CommandManager.class


二进制
rtsphls/target/classes/com/my/core/commandManager/CommandManagerImpl.class


二进制
rtsphls/target/classes/com/my/core/commandManager/callback/EventCallBack.class


二进制
rtsphls/target/classes/com/my/core/commandManager/callback/EventCallBackType.class


二进制
rtsphls/target/classes/com/my/core/commandManager/callback/worker/EventMsgNetWorker.class


二进制
rtsphls/target/classes/com/my/core/commandManager/commandbuidler/CommandAssembly.class


二进制
rtsphls/target/classes/com/my/core/commandManager/commandbuidler/CommandAssemblyImpl.class


二进制
rtsphls/target/classes/com/my/core/commandManager/commandbuidler/CommandBuidler.class


二进制
rtsphls/target/classes/com/my/core/commandManager/commandbuidler/CommandBuidlerFactory.class


二进制
rtsphls/target/classes/com/my/core/commandManager/commandbuidler/DefaultCommandBuidler.class


二进制
rtsphls/target/classes/com/my/core/commandManager/config/ProgramConfig.class


二进制
rtsphls/target/classes/com/my/core/commandManager/contrller/TaskController.class


二进制
rtsphls/target/classes/com/my/core/commandManager/data/CommandTasker.class


二进制
rtsphls/target/classes/com/my/core/commandManager/data/TaskDao.class


二进制
rtsphls/target/classes/com/my/core/commandManager/data/TaskDaoImpl.class


二进制
rtsphls/target/classes/com/my/core/commandManager/data/TaskerEventMsg.class


二进制
rtsphls/target/classes/com/my/core/commandManager/handler/DefaultOutHandlerMethod.class


二进制
rtsphls/target/classes/com/my/core/commandManager/handler/KeepAliveHandler.class


二进制
rtsphls/target/classes/com/my/core/commandManager/handler/OutHandler.class


二进制
rtsphls/target/classes/com/my/core/commandManager/handler/OutHandlerMethod.class


二进制
rtsphls/target/classes/com/my/core/commandManager/handler/TaskHandler.class


二进制
rtsphls/target/classes/com/my/core/commandManager/handler/TaskHandlerImpl.class


二进制
rtsphls/target/classes/com/my/core/commandManager/test/Test.class


二进制
rtsphls/target/classes/com/my/core/commandManager/util/CommonUtil.class


二进制
rtsphls/target/classes/com/my/core/commandManager/util/ExecUtil.class


二进制
rtsphls/target/classes/com/my/core/commandManager/util/PropertiesUtil.class


二进制
rtsphls/target/classes/com/my/core/commandManager/util/ReflectUtil.class


二进制
rtsphls/target/classes/com/my/core/config/CommonConfig.class


二进制
rtsphls/target/classes/com/my/core/entity/StartParam.class


二进制
rtsphls/target/classes/com/my/core/task/StopTask.class


+ 13 - 0
rtsphls/target/classes/defaultFFmpegConfig.properties

@@ -0,0 +1,13 @@
+#ffmpeg执行路径,一般为ffmpeg的安装目录,该路径只能是目录,不能为具体文件路径,否则会报错
+path=D:\\ffmpeg-5.0.1-essentials_build\\bin\\
+#存放任务的默认Map的初始化大小
+size=20
+#事件回调通知接口地址
+callback=http://127.0.0.1/callback
+#网络超时设置(毫秒)
+timeout=300
+#开启保活线程
+keepalive=true
+#是否输出debug消息
+debug=false
+

+ 4 - 0
rtsphls/target/maven-archiver/pom.properties

@@ -0,0 +1,4 @@
+#Created by Apache Maven 3.6.3
+version=1.0-SNAPSHOT
+groupId=org.example
+artifactId=RtspToHls

+ 31 - 0
rtsphls/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst

@@ -0,0 +1,31 @@
+com\my\core\entity\StartParam.class
+com\my\core\commandManager\util\ExecUtil.class
+com\my\core\commandManager\commandbuidler\CommandBuidlerFactory.class
+com\my\core\commandManager\CommandManagerImpl.class
+com\my\core\config\CommonConfig.class
+com\my\core\commandManager\config\ProgramConfig.class
+com\my\core\commandManager\handler\DefaultOutHandlerMethod.class
+com\my\core\commandManager\util\CommonUtil.class
+com\my\core\commandManager\commandbuidler\CommandAssembly.class
+com\my\core\commandManager\handler\OutHandlerMethod.class
+com\my\core\commandManager\data\TaskerEventMsg.class
+com\my\core\CoreController.class
+com\my\core\commandManager\commandbuidler\CommandAssemblyImpl.class
+com\my\core\commandManager\contrller\TaskController.class
+com\my\core\commandManager\data\CommandTasker.class
+com\my\core\commandManager\test\Test.class
+com\my\core\commandManager\handler\TaskHandlerImpl.class
+com\my\core\commandManager\handler\TaskHandler.class
+com\my\core\commandManager\commandbuidler\DefaultCommandBuidler.class
+com\my\core\commandManager\CommandManager.class
+com\my\core\commandManager\handler\KeepAliveHandler.class
+com\my\core\commandManager\callback\EventCallBackType.class
+com\my\core\commandManager\data\TaskDao.class
+com\my\core\commandManager\data\TaskDaoImpl.class
+com\my\core\commandManager\util\ReflectUtil.class
+com\my\core\commandManager\callback\EventCallBack.class
+com\my\core\task\StopTask.class
+com\my\core\commandManager\handler\OutHandler.class
+com\my\core\commandManager\callback\worker\EventMsgNetWorker.class
+com\my\core\commandManager\commandbuidler\CommandBuidler.class
+com\my\core\commandManager\util\PropertiesUtil.class

+ 31 - 0
rtsphls/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst

@@ -0,0 +1,31 @@
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\CommandManager.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\handler\OutHandler.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\test\Test.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\util\ExecUtil.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\handler\DefaultOutHandlerMethod.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\data\TaskDao.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\CoreController.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\handler\OutHandlerMethod.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\CommandManagerImpl.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\callback\worker\EventMsgNetWorker.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\config\ProgramConfig.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\data\CommandTasker.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\commandbuidler\CommandAssemblyImpl.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\handler\TaskHandler.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\handler\TaskHandlerImpl.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\util\PropertiesUtil.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\data\TaskDaoImpl.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\task\StopTask.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\commandbuidler\DefaultCommandBuidler.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\util\CommonUtil.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\commandbuidler\CommandAssembly.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\entity\StartParam.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\commandbuidler\CommandBuidler.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\callback\EventCallBackType.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\commandbuidler\CommandBuidlerFactory.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\contrller\TaskController.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\data\TaskerEventMsg.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\util\ReflectUtil.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\callback\EventCallBack.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\commandManager\handler\KeepAliveHandler.java
+D:\idea4\rtsphls\rtsphls\src\main\java\com\my\core\config\CommonConfig.java

+ 0 - 0
rtsphls/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst


二进制
rtsphls/target/rtsp.jar


二进制
rtsphls/target/rtsp.jar.original