添加拉流代理接口-新增拉流代理
parent
c186ce94c1
commit
8db0192a27
|
@ -7,10 +7,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
*/
|
*/
|
||||||
@Schema(description = "拉流代理的信息")
|
@Schema(description = "拉流代理的信息")
|
||||||
public class StreamProxy {
|
public class StreamProxy {
|
||||||
|
|
||||||
@Schema(description = "ID")
|
@Schema(description = "ID")
|
||||||
private int id;
|
private int id;
|
||||||
|
|
||||||
@Schema(description = "类型")
|
@Schema(description = "类型")
|
||||||
private String type;
|
private String type;
|
||||||
@Schema(description = "应用名")
|
@Schema(description = "应用名")
|
||||||
|
@ -31,11 +29,13 @@ public class StreamProxy {
|
||||||
private String ffmpegCmdKey;
|
private String ffmpegCmdKey;
|
||||||
@Schema(description = "rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播")
|
@Schema(description = "rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播")
|
||||||
private String rtpType;
|
private String rtpType;
|
||||||
|
@Schema(description = "代理失败的原因")
|
||||||
|
private String proxyError;
|
||||||
@Schema(description = "是否启用")
|
@Schema(description = "是否启用")
|
||||||
private boolean enable;
|
private boolean enable;
|
||||||
@Schema(description = "是否启用音频")
|
@Schema(description = "是否启用音频")
|
||||||
private boolean enableAudio;
|
private boolean enableAudio;
|
||||||
@Schema(description = "是否启用MP4")
|
@Schema(description = "是否录制")
|
||||||
private boolean enableMp4;
|
private boolean enableMp4;
|
||||||
@Schema(description = "是否 无人观看时删除")
|
@Schema(description = "是否 无人观看时删除")
|
||||||
private boolean enableRemoveNoneReader;
|
private boolean enableRemoveNoneReader;
|
||||||
|
@ -267,4 +267,12 @@ public class StreamProxy {
|
||||||
public void setCommonGbChannelId(int commonGbChannelId) {
|
public void setCommonGbChannelId(int commonGbChannelId) {
|
||||||
this.commonGbChannelId = commonGbChannelId;
|
this.commonGbChannelId = commonGbChannelId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getProxyError() {
|
||||||
|
return proxyError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProxyError(String proxyError) {
|
||||||
|
this.proxyError = proxyError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,4 +124,8 @@ public interface IStreamProxyService {
|
||||||
*/
|
*/
|
||||||
List<StreamProxy> getAllForEnable();
|
List<StreamProxy> getAllForEnable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加拉流代理
|
||||||
|
*/
|
||||||
|
void add(StreamProxy param, GeneralCallback<StreamInfo> callback);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,8 @@ import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -229,6 +231,113 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void add(StreamProxy param, GeneralCallback<StreamInfo> callback) {
|
||||||
|
MediaServerItem mediaInfo;
|
||||||
|
if (ObjectUtils.isEmpty(param.getMediaServerId()) || "auto".equals(param.getMediaServerId())){
|
||||||
|
mediaInfo = mediaServerService.getMediaServerForMinimumLoad(null);
|
||||||
|
}else {
|
||||||
|
mediaInfo = mediaServerService.getOne(param.getMediaServerId());
|
||||||
|
}
|
||||||
|
if (mediaInfo == null) {
|
||||||
|
logger.warn("[添加拉流代理] 未找到在线的ZLM...");
|
||||||
|
throw new ControllerException(ErrorCode.ERROR100.getCode(), "保存代理未找到可用的ZLM");
|
||||||
|
}
|
||||||
|
if ("ffmpeg".equalsIgnoreCase(param.getType())) {
|
||||||
|
if (ObjectUtils.isEmpty(param.getDstUrl())) {
|
||||||
|
logger.warn("[添加拉流代理] 未设置目标URL地址(DstUrl)");
|
||||||
|
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未设置目标URL地址");
|
||||||
|
}
|
||||||
|
logger.info("[添加拉流代理] ffmpeg,源地址: {}, 目标地址为:{}", param.getUrl(), param.getDstUrl());
|
||||||
|
if (ObjectUtils.isEmpty(param.getApp()) || ObjectUtils.isEmpty(param.getStream())) {
|
||||||
|
try {
|
||||||
|
URL url = new URL(param.getDstUrl());
|
||||||
|
String path = url.getPath();
|
||||||
|
if (path.indexOf("/", 1) < 0) {
|
||||||
|
throw new ControllerException(ErrorCode.ERROR100.getCode(), "解析DstUrl失败, 至少两层路径");
|
||||||
|
}
|
||||||
|
int appIndex = path.indexOf("/", 1);
|
||||||
|
String app = path.substring(1, appIndex);
|
||||||
|
String stream = path.substring(path.indexOf(app));
|
||||||
|
param.setApp(app);
|
||||||
|
param.setStream(stream);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new ControllerException(ErrorCode.ERROR100.getCode(), "解析DstUrl失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
logger.info("[添加拉流代理] 直接拉流,源地址: {}, app: {}, stream: {}", param.getUrl(), param.getApp(), param.getStream());
|
||||||
|
}
|
||||||
|
param.setMediaServerId(mediaInfo.getId());
|
||||||
|
StreamProxy streamProxyInDb = streamProxyMapper.selectOne(param.getApp(), param.getStream());
|
||||||
|
if (streamProxyInDb != null) {
|
||||||
|
throw new ControllerException(ErrorCode.ERROR100.getCode(), "app/stream已经存在");
|
||||||
|
}
|
||||||
|
if (!param.isEnable()) {
|
||||||
|
param.setStatus(false);
|
||||||
|
saveProxyToDb(param);
|
||||||
|
StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
|
||||||
|
mediaInfo, param.getApp(), param.getStream(), null, null);
|
||||||
|
callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String talkKey = UUID.randomUUID().toString();
|
||||||
|
String delayTalkKey = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
HookSubscribeForStreamChange hookSubscribeForStreamChange = HookSubscribeFactory.on_stream_changed(param.getApp(), param.getStream(), true, "rtsp", mediaInfo.getId());
|
||||||
|
hookSubscribe.addSubscribe(hookSubscribeForStreamChange, (mediaServerItem, response) -> {
|
||||||
|
dynamicTask.stop(talkKey);
|
||||||
|
param.setStatus(true);
|
||||||
|
saveProxyToDb(param);
|
||||||
|
StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(
|
||||||
|
mediaInfo, param.getApp(), param.getStream(), null, null);
|
||||||
|
callback.run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), streamInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
dynamicTask.startDelay(delayTalkKey, ()->{
|
||||||
|
hookSubscribe.removeSubscribe(hookSubscribeForStreamChange);
|
||||||
|
dynamicTask.stop(talkKey);
|
||||||
|
callback.run(ErrorCode.ERROR100.getCode(), "启用超时,请检查源地址是否可用", null);
|
||||||
|
if (param.isEnableRemoveNoneReader()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
param.setProxyError("启用超时");
|
||||||
|
param.setStatus(false);
|
||||||
|
saveProxyToDb(param);
|
||||||
|
}, 10000);
|
||||||
|
JSONObject jsonObject = addStreamProxyToZlm(param);
|
||||||
|
if (jsonObject != null && jsonObject.getInteger("code") != 0) {
|
||||||
|
hookSubscribe.removeSubscribe(hookSubscribeForStreamChange);
|
||||||
|
dynamicTask.stop(talkKey);
|
||||||
|
callback.run(ErrorCode.ERROR100.getCode(), jsonObject.getString("msg"), null);
|
||||||
|
if (param.isEnableRemoveNoneReader()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
param.setProxyError("启用失败: " + jsonObject.getString("msg"));
|
||||||
|
param.setStatus(false);
|
||||||
|
saveProxyToDb(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveProxyToDb(StreamProxy param) {
|
||||||
|
// 未启用的数据可以直接保存了
|
||||||
|
if (!ObjectUtils.isEmpty(param.getGbId())) {
|
||||||
|
CommonGbChannel commonGbChannel = CommonGbChannel.getInstance(param);
|
||||||
|
if (commonGbChannelService.add(commonGbChannel) > 0) {
|
||||||
|
param.setCommonGbChannelId(commonGbChannel.getCommonGbId());
|
||||||
|
}else {
|
||||||
|
throw new ControllerException(ErrorCode.ERROR100.getCode(), "添加通用通道失败,请检查是否国标编码重复");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
param.setUpdateTime(DateUtil.getNow());
|
||||||
|
param.setCreateTime(DateUtil.getNow());
|
||||||
|
int addStreamProxyResult = streamProxyMapper.add(param);
|
||||||
|
if (addStreamProxyResult <= 0) {
|
||||||
|
throw new ControllerException(ErrorCode.ERROR100.getCode(), "添加拉流代理失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String getSchemaFromFFmpegCmd(String ffmpegCmd) {
|
private String getSchemaFromFFmpegCmd(String ffmpegCmd) {
|
||||||
ffmpegCmd = ffmpegCmd.replaceAll(" + ", " ");
|
ffmpegCmd = ffmpegCmd.replaceAll(" + ", " ");
|
||||||
String[] paramArray = ffmpegCmd.split(" ");
|
String[] paramArray = ffmpegCmd.split(" ");
|
||||||
|
@ -283,8 +392,6 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
|
||||||
result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl().trim(),
|
result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl().trim(),
|
||||||
param.isEnableAudio(), param.isEnableMp4(), param.getRtpType());
|
param.isEnableAudio(), param.isEnableMp4(), param.getRtpType());
|
||||||
}
|
}
|
||||||
System.out.println("addStreamProxyToZlm====");
|
|
||||||
System.out.println(result);
|
|
||||||
if (result != null && result.getInteger("code") == 0) {
|
if (result != null && result.getInteger("code") == 0) {
|
||||||
JSONObject data = result.getJSONObject("data");
|
JSONObject data = result.getJSONObject("data");
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ public interface StreamProxyMapper {
|
||||||
"(#{type}, #{name}, #{app}, #{stream}, #{mediaServerId}, #{url}, #{srcUrl}, #{dstUrl}, " +
|
"(#{type}, #{name}, #{app}, #{stream}, #{mediaServerId}, #{url}, #{srcUrl}, #{dstUrl}, " +
|
||||||
"#{timeoutMs}, #{ffmpegCmdKey}, #{rtpType}, #{enableAudio}, #{enableMp4}, #{enable}, #{status}, #{streamKey}, " +
|
"#{timeoutMs}, #{ffmpegCmdKey}, #{rtpType}, #{enableAudio}, #{enableMp4}, #{enable}, #{status}, #{streamKey}, " +
|
||||||
"#{enableRemoveNoneReader}, #{enableDisableNoneReader}, #{createTime} , #{longitude} , #{latitude}, #{commonGbChannelId} )")
|
"#{enableRemoveNoneReader}, #{enableDisableNoneReader}, #{createTime} , #{longitude} , #{latitude}, #{commonGbChannelId} )")
|
||||||
|
@Options(useGeneratedKeys=true, keyProperty="id", keyColumn="id")
|
||||||
int add(StreamProxy streamProxy);
|
int add(StreamProxy streamProxy);
|
||||||
|
|
||||||
@Update("UPDATE wvp_stream_proxy " +
|
@Update("UPDATE wvp_stream_proxy " +
|
||||||
|
|
|
@ -123,6 +123,46 @@ public class StreamProxyController {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "添加代理", security = @SecurityRequirement(name = JwtUtils.HEADER), parameters = {
|
||||||
|
@Parameter(name = "param", description = "代理参数", required = true),
|
||||||
|
})
|
||||||
|
@PostMapping(value = "/add")
|
||||||
|
@ResponseBody
|
||||||
|
public DeferredResult<Object> add(@RequestBody StreamProxy param){
|
||||||
|
logger.info("添加代理: " + JSONObject.toJSONString(param));
|
||||||
|
if (ObjectUtils.isEmpty(param.getMediaServerId())) {
|
||||||
|
param.setMediaServerId("auto");
|
||||||
|
}
|
||||||
|
if (ObjectUtils.isEmpty(param.getType())) {
|
||||||
|
param.setType("default");
|
||||||
|
}
|
||||||
|
if (ObjectUtils.isEmpty(param.getRtpType())) {
|
||||||
|
param.setRtpType("1");
|
||||||
|
}
|
||||||
|
if (ObjectUtils.isEmpty(param.getGbId())) {
|
||||||
|
param.setGbId(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeferredResult<Object> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
|
||||||
|
// 录像查询以channelId作为deviceId查询
|
||||||
|
result.onTimeout(()->{
|
||||||
|
WVPResult<StreamInfo> wvpResult = new WVPResult<>();
|
||||||
|
wvpResult.setCode(ErrorCode.ERROR100.getCode());
|
||||||
|
wvpResult.setMsg("超时");
|
||||||
|
result.setResult(wvpResult);
|
||||||
|
});
|
||||||
|
|
||||||
|
streamProxyService.add(param, (code, msg, streamInfo) -> {
|
||||||
|
logger.info("[添加拉流代理] {}", code == ErrorCode.SUCCESS.getCode()? "成功":"失败: " + msg);
|
||||||
|
if (code == ErrorCode.SUCCESS.getCode()) {
|
||||||
|
result.setResult(new StreamContent(streamInfo));
|
||||||
|
}else {
|
||||||
|
result.setResult(WVPResult.fail(code, msg));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/ffmpeg_cmd/list")
|
@GetMapping(value = "/ffmpeg_cmd/list")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@Operation(summary = "获取ffmpeg.cmd模板", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
@Operation(summary = "获取ffmpeg.cmd模板", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||||
|
|
|
@ -140,6 +140,9 @@ alter table wvp_stream_proxy
|
||||||
alter table wvp_stream_proxy
|
alter table wvp_stream_proxy
|
||||||
add status bool default false;
|
add status bool default false;
|
||||||
|
|
||||||
|
alter table wvp_stream_proxy
|
||||||
|
add proxy_error varchar(255) default NULL;
|
||||||
|
|
||||||
alter table wvp_device
|
alter table wvp_device
|
||||||
drop column auto_sync_channel;
|
drop column auto_sync_channel;
|
||||||
|
|
||||||
|
|
|
@ -245,6 +245,7 @@ create table wvp_stream_proxy (
|
||||||
enable_remove_none_reader bool default false,
|
enable_remove_none_reader bool default false,
|
||||||
create_time character varying(50),
|
create_time character varying(50),
|
||||||
name character varying(255),
|
name character varying(255),
|
||||||
|
proxy_error character varying(255) default null,
|
||||||
update_time character varying(50),
|
update_time character varying(50),
|
||||||
stream_key character varying(255),
|
stream_key character varying(255),
|
||||||
enable_disable_none_reader bool default false,
|
enable_disable_none_reader bool default false,
|
||||||
|
|
|
@ -212,7 +212,8 @@ create table wvp_stream_proxy (
|
||||||
status boolean,
|
status boolean,
|
||||||
enable_remove_none_reader bool default false,
|
enable_remove_none_reader bool default false,
|
||||||
create_time character varying(50),
|
create_time character varying(50),
|
||||||
name character varying(255),
|
name character varying(255) default null,
|
||||||
|
proxy_error character varying(255) default null,
|
||||||
update_time character varying(50),
|
update_time character varying(50),
|
||||||
stream_key character varying(255),
|
stream_key character varying(255),
|
||||||
enable_disable_none_reader bool default false,
|
enable_disable_none_reader bool default false,
|
||||||
|
|
Loading…
Reference in New Issue