[*] 服务端录制

This commit is contained in:
acgist
2023-06-01 07:32:35 +08:00
parent 441e99483b
commit a06f6a251f
11 changed files with 521 additions and 167 deletions

View File

@@ -109,6 +109,14 @@ public interface Constant {
* 密码
*/
String PASSWORD = "password";
/**
* 端口
*/
String PORT = "port";
/**
* 地址
*/
String HOST = "host";
/**
* 数据
*/
@@ -233,6 +241,10 @@ public interface Constant {
* 状态
*/
String ENABLED = "enabled";
/**
* 文件路径
*/
String FILEPATH = "filepath";
/**
* 是否是消费者
*/

View File

@@ -31,6 +31,8 @@ public class FfmpegProperties {
private String storageImagePath;
@Schema(title = "视频存储目录", description = "视频存储目录")
private String storageVideoPath;
@Schema(title = "录像录像地址", description = "录像录像地址")
private String host;
@Schema(title = "录像最小端口", description = "录像最小端口")
private Integer minPort;
@Schema(title = "录像最大端口", description = "录像最大端口")

View File

@@ -1,7 +1,9 @@
package com.acgist.taoyao.boot.utils;
import java.math.BigInteger;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.BitSet;
@@ -209,5 +211,25 @@ public final class NetUtils {
// 本地地址A/B/C类本地地址
inetAddress.isSiteLocalAddress();
}
/**
* 扫描端口
*
* @param min 最小端口
* @param max 最大端口
*
* @return 端口
*/
public static final int scanPort(int min, int max) {
for (int port = min; port < max; port++) {
try (final DatagramSocket socket = new DatagramSocket(port)) {
socket.disconnect();
return port;
} catch (SocketException e) {
// 忽略
}
}
return 0;
}
}

View File

@@ -1,16 +1,20 @@
package com.acgist.taoyao.boot.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.lang3.StringUtils;
import com.acgist.taoyao.boot.model.MessageCode;
import com.acgist.taoyao.boot.model.MessageCodeException;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
/**
* 脚本工具
* 命令工具
*
* @author acgist
*/
@@ -27,36 +31,176 @@ public final class ScriptUtils {
*
* @return 执行结果
*/
public static final String execute(String script) {
public static final ScriptExecutor execute(String script) {
if(StringUtils.isEmpty(script)) {
throw MessageCodeException.of(MessageCode.CODE_1002, "无效命令:" + script);
}
String result = null;
Process process = null;
final ScriptExecutor executor = new ScriptExecutor(script);
try {
process = Runtime.getRuntime().exec(script);
try(
final InputStream input = process.getInputStream();
final InputStream error = process.getErrorStream();
) {
final String inputValue = new String(input.readAllBytes());
final String errorValue = new String(input.readAllBytes());
log.info("""
执行命令:{}
执行结果:{}
失败结果:{}
""", script, inputValue, errorValue);
result = StringUtils.isEmpty(inputValue) ? errorValue : inputValue;
}
executor.execute();
} catch (Exception e) {
log.error("执行命令异常:{}", script, e);
result = e.getMessage();
} finally {
if(process != null) {
process.destroy();
}
return executor;
}
/**
* 命令执行器
*
* @author acgist
*/
@Getter
@Setter
public static final class ScriptExecutor {
/**
* 执行结果
*/
private int code;
/**
* 是否正在运行
*/
private boolean running;
/**
* 命令进程
*/
private Process process;
/**
* 命令进程Builder
*/
private ProcessBuilder processBuilder;
/**
* 执行命令
*/
private final String script;
/**
* 日志输出
*/
private final StringBuilder input;
/**
* 错误输出
*/
private final StringBuilder error;
/**
* @param script 执行命令
*/
public ScriptExecutor(String script) {
this.script = script;
this.input = new StringBuilder();
this.error = new StringBuilder();
}
/**
* 执行命令
*
* @throws IOException IO异常
* @throws InterruptedException 线程异常
*/
public void execute() throws InterruptedException, IOException {
final boolean linux = FileUtils.linux();
if(linux) {
this.processBuilder = new ProcessBuilder("/bin/bash", "-c", this.script);
this.process = this.processBuilder.start();
} else {
this.processBuilder = new ProcessBuilder("cmd", "/c", this.script);
this.process = this.processBuilder.start();
}
log.debug("开始执行命令:{}", this.script);
this.running = true;
try (
final InputStream input = this.process.getInputStream();
final InputStream error = this.process.getErrorStream();
) {
this.streamThread(linux, "TaoyaoScriptInput", this.input, input);
this.streamThread(linux, "TaoyaoScriptError", this.error, error);
this.code = this.process.waitFor();
}
this.running = false;
log.debug("""
结束执行命令:{}
执行状态:{}
执行日志:{}
错误日志:{}
""", this.script, this.code, this.input, this.error);
}
/**
* @param linux 是否Linux
* @param name 线程名称
* @param builder 日志记录
* @param input 日志输入流
*/
private void streamThread(boolean linux, String name, StringBuilder builder, InputStream input) {
final Thread streamThread = new Thread(() -> {
try {
int length;
final byte[] bytes = new byte[1024];
while(this.running && (length = input.read(bytes)) >= 0) {
builder.append(linux ? new String(bytes, 0, length) : new String(bytes, 0, length, "GBK"));
}
} catch (Exception e) {
log.error("读取执行命令日志异常", e);
}
});
streamThread.setName(name);
streamThread.setDaemon(true);
streamThread.start();
}
/**
* 结束命令
*/
public void stop() {
this.stop(null);
}
/**
* 结束命令
*
* @param script 结束命令
*/
public void stop(String script) {
// 等待时间
long wait = 0;
// 使用按键结束
if(StringUtils.isNotEmpty(script)) {
try (final OutputStream output = this.process.getOutputStream();) {
output.write(script.getBytes());
} catch (Exception e) {
log.error("结束命令异常:{}", this.script, e);
}
wait = 5000;
}
// 等待正常结束
while(this.process.isAlive() && wait >= 0) {
wait -= 10;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.yield();
}
}
if(this.process.isAlive()) {
log.info("强制结束命令:{}", this.script);
// 所有子进程
this.process.children().forEach(process -> {
process.destroy();
});
// 当前父进程
this.process.destroy();
} else {
log.debug("正常结束命令:{}", this.script);
}
}
return result;
/**
* @return 执行结果
*/
public String getResult() {
return this.input.isEmpty() ? this.error.toString() : this.input.toString();
}
}
}