package com.ard.zlm.controller;
|
|
import com.alibaba.fastjson2.JSONObject;
|
import com.ard.common.core.constant.Constants;
|
import com.ard.common.core.constant.SecurityConstants;
|
import com.ard.common.core.domain.R;
|
import com.ard.common.core.enums.LiveStreamType;
|
import com.ard.common.core.utils.StringUtils;
|
import com.ard.common.core.web.domain.AjaxResult;
|
import com.ard.gb28181.api.RemoteGb28181Service;
|
import com.ard.gb28181.api.domain.Device;
|
import com.ard.gb28181.api.domain.DeviceChannel;
|
import com.ard.gb28181.api.domain.GbChannelDTO;
|
import com.ard.zlm.domain.StreamChannel;
|
import com.ard.zlm.service.ZlmStreamService;
|
import com.ard.work.api.RemoteCameraService;
|
import com.ard.zlm.api.domain.*;
|
import com.ard.zlm.common.InviteErrorCode;
|
import com.ard.zlm.common.InviteSessionType;
|
import com.ard.zlm.config.UserSetting;
|
import com.ard.zlm.domain.Snap;
|
import com.ard.zlm.mediaServer.MediaServerChangeEvent;
|
import com.ard.zlm.service.ErrorCallback;
|
import com.ard.zlm.service.IInviteStreamService;
|
import com.ard.zlm.service.IMediaServerService;
|
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameters;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
import jakarta.annotation.Resource;
|
import jakarta.servlet.http.HttpServletRequest;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.context.annotation.Lazy;
|
import org.springframework.util.Assert;
|
import org.springframework.util.ObjectUtils;
|
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.context.request.async.DeferredResult;
|
|
import java.net.MalformedURLException;
|
import java.net.URL;
|
import java.util.List;
|
|
/**
|
* zlm 接口
|
*
|
* @FileName ZlmController
|
* @Description
|
* @Author fengcheng
|
* @date 2026-04-01
|
**/
|
@Tag(name = "zlm接口")
|
@Slf4j
|
@RestController
|
public class ZlmController {
|
|
@Resource
|
private IMediaServerService mediaServerService;
|
|
@Resource
|
private UserSetting userSetting;
|
|
@Resource
|
private ApplicationEventPublisher applicationEventPublisher;
|
|
@Resource
|
private RemoteGb28181Service remoteGb28181Service;
|
|
@Resource
|
private ZlmStreamService zlmStreamService;
|
|
@Resource
|
@Lazy
|
private IInviteStreamService inviteStreamService;
|
|
/**
|
* 拉流播放
|
*
|
* @param streamPullPlay 拉流播放请求参数
|
* @param request HttpServletRequest
|
* @return
|
*/
|
@Operation(summary = "拉流播放代理")
|
@PostMapping("/streamPullPlay")
|
public DeferredResult<R<StreamContent>> streamPullPlay(@RequestBody StreamPullPlay streamPullPlay, HttpServletRequest request) {
|
log.info("拉流播放代理: app:{}-stream:{}", streamPullPlay.getApp(), streamPullPlay.getStream());
|
DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
|
|
ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
|
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
R<StreamContent> r = R.ok();
|
if (streamInfo != null) {
|
if (userSetting.getUseSourceIpAsStreamIp()) {
|
streamInfo = streamInfo.clone();//深拷贝
|
String host;
|
try {
|
URL url = new URL(request.getRequestURL().toString());
|
host = url.getHost();
|
} catch (MalformedURLException e) {
|
host = request.getLocalAddr();
|
}
|
streamInfo.changeStreamIp(host);
|
}
|
if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix()) && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
|
streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
|
}
|
r.setData(new StreamContent(streamInfo));
|
} else {
|
r.setCode(code);
|
r.setMsg(msg);
|
}
|
|
result.setResult(r);
|
} else {
|
result.setResult(R.fail(code, msg));
|
}
|
};
|
|
mediaServerService.streamPullPlay(streamPullPlay, callback);
|
return result;
|
}
|
|
/**
|
* 停止拉流播放
|
*
|
* @param streamPullPlay 拉流播放请求参数
|
* @return
|
*/
|
@Operation(summary = "停止拉流播放")
|
@PostMapping("/stopStreamPullPlay")
|
public AjaxResult stopStreamPullPlay(@RequestBody StreamPullPlay streamPullPlay) {
|
mediaServerService.stopStreamPullPlay(streamPullPlay);
|
return AjaxResult.success();
|
}
|
|
/**
|
* 获取截图
|
*
|
* @param snap 截图参数
|
* @return
|
*/
|
@Operation(summary = "获取截图")
|
@PostMapping("/getSnap")
|
public AjaxResult getSnap(@RequestBody Snap snap) {
|
ZlmMediaServer mediaServer = mediaServerService.getMediaServerForMinimumLoad(null);
|
if (mediaServer == null) {
|
throw new RuntimeException("无可用的流媒体服务器");
|
}
|
String filePath = mediaServerService.getSnap(mediaServer, snap);
|
return AjaxResult.success(filePath);
|
}
|
|
/**
|
* rtp播放
|
*
|
* @param rtpServerParam 创建rtp端口请求参数
|
* @param request HttpServletRequest
|
* @return
|
*/
|
@Operation(summary = "rtp播放")
|
@PostMapping("/rtpPlay")
|
public DeferredResult<R<StreamContent>> rtpPlay(@RequestBody RTPServerParam rtpServerParam, HttpServletRequest request) {
|
log.info("rtp播放: app:{}-stream:{}", rtpServerParam.getApp(), rtpServerParam.getStreamId());
|
|
if (!(LiveStreamType.HIK_SDK.getCode().equals(rtpServerParam.getType())
|
|| LiveStreamType.HIK_ISUP.getCode().equals(rtpServerParam.getType())
|
|| LiveStreamType.DAHUA_SDK.getCode().equals(rtpServerParam.getType())
|
)) {
|
log.error("不支持的播放类型:{}", rtpServerParam.getType());
|
throw new RuntimeException("不支持的播放类型");
|
}
|
|
DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
|
|
result.onTimeout(() -> {
|
log.info("[rtp播放等待超时] app:{}, stream:{}", rtpServerParam.getApp(), rtpServerParam.getStreamId());
|
R<StreamContent> wvpResult = R.fail();
|
wvpResult.setMsg("rtp播放超时");
|
result.setResult(wvpResult);
|
|
inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, rtpServerParam.getId());
|
mediaServerService.stopRtpPlay(rtpServerParam);
|
});
|
|
ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
|
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
R<StreamContent> r = R.ok();
|
if (streamInfo != null) {
|
if (userSetting.getUseSourceIpAsStreamIp()) {
|
streamInfo = streamInfo.clone();//深拷贝
|
String host;
|
try {
|
URL url = new URL(request.getRequestURL().toString());
|
host = url.getHost();
|
} catch (MalformedURLException e) {
|
host = request.getLocalAddr();
|
}
|
streamInfo.changeStreamIp(host);
|
}
|
if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix()) && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
|
streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
|
}
|
r.setData(new StreamContent(streamInfo));
|
} else {
|
r.setCode(code);
|
r.setMsg(msg);
|
}
|
|
result.setResult(r);
|
} else {
|
result.setResult(R.fail(code, msg));
|
}
|
};
|
|
mediaServerService.rtpPlay(rtpServerParam, callback);
|
return result;
|
}
|
|
/**
|
* 停止rtp播放
|
*
|
* @param rtpServerParam 创建rtp端口请求参数
|
* @return
|
*/
|
@Operation(summary = "停止rtp播放")
|
@PostMapping("/stopRtpPlay")
|
public AjaxResult stopRtpPlay(@RequestBody RTPServerParam rtpServerParam) {
|
log.info("停止rtp播放: id:{}", rtpServerParam.getId());
|
if (!(LiveStreamType.HIK_SDK.getCode().equals(rtpServerParam.getType())
|
|| LiveStreamType.HIK_ISUP.getCode().equals(rtpServerParam.getType())
|
|| LiveStreamType.DAHUA_SDK.getCode().equals(rtpServerParam.getType())
|
)) {
|
log.error("不支持的播放类型:{}", rtpServerParam.getType());
|
throw new RuntimeException("不支持的播放类型");
|
}
|
mediaServerService.stopRtpPlay(rtpServerParam);
|
return AjaxResult.success();
|
}
|
|
/**
|
* 加载文件形成播放地址
|
*
|
* @param id 设备id
|
* @return
|
*/
|
@GetMapping("/loadRecord/{id}")
|
public DeferredResult<R<StreamContent>> loadRecord(@PathVariable Long id, HttpServletRequest request) {
|
DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
|
|
result.onTimeout(() -> {
|
log.info("[加载录像文件超时] id={}", id);
|
R<StreamContent> wvpResult = R.fail();
|
wvpResult.setMsg("加载录像文件超时");
|
result.setResult(wvpResult);
|
});
|
|
ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
|
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
R<StreamContent> r = R.ok();
|
if (streamInfo != null) {
|
if (userSetting.getUseSourceIpAsStreamIp()) {
|
streamInfo = streamInfo.clone();//深拷贝
|
String host;
|
try {
|
URL url = new URL(request.getRequestURL().toString());
|
host = url.getHost();
|
} catch (MalformedURLException e) {
|
host = request.getLocalAddr();
|
}
|
streamInfo.changeStreamIp(host);
|
}
|
if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix()) && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
|
streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
|
}
|
r.setData(new StreamContent(streamInfo));
|
} else {
|
r.setCode(code);
|
r.setMsg(msg);
|
}
|
result.setResult(r);
|
} else {
|
result.setResult(R.fail(code, msg));
|
}
|
};
|
|
mediaServerService.loadRecord(id, callback);
|
return result;
|
}
|
|
/**
|
* 关闭流文件形成播放地址
|
*
|
* @param id 加载文件参数
|
* @return
|
*/
|
@GetMapping("/closeStreams/{id}")
|
public AjaxResult closeStreams(@PathVariable Long id) {
|
mediaServerService.closeStreams(id);
|
return AjaxResult.success();
|
}
|
|
/**
|
* 获取流媒体服务器列表
|
*
|
* @return
|
*/
|
@Operation(summary = "获取流媒体服务器列表")
|
@GetMapping(value = "/list")
|
public AjaxResult getMediaServerList() {
|
List<ZlmMediaServer> list = mediaServerService.getAll();
|
return AjaxResult.success(list);
|
}
|
|
/**
|
* 移除流媒体服务
|
*
|
* @param id 流媒体ID
|
*/
|
@Operation(summary = "移除流媒体服务")
|
@DeleteMapping(value = "/delete/{id}")
|
public AjaxResult deleteMediaServer(@PathVariable("id") String id) {
|
ZlmMediaServer mediaServer = mediaServerService.getOne(id);
|
if (mediaServer == null) {
|
throw new RuntimeException("流媒体不存在");
|
}
|
mediaServerService.delete(mediaServer);
|
return AjaxResult.success();
|
}
|
|
/**
|
* 保存流媒体服务
|
*
|
* @param mediaServer 流媒体信息
|
*/
|
@Operation(summary = "保存流媒体服务")
|
@PostMapping(value = "/save")
|
public AjaxResult saveMediaServer(@RequestBody ZlmMediaServer mediaServer) {
|
ZlmMediaServer mediaServerItemInDatabase = mediaServerService.getOneFromDatabase(mediaServer.getId());
|
|
if (mediaServerItemInDatabase != null) {
|
mediaServerService.update(mediaServer);
|
} else {
|
mediaServerService.add(mediaServer);
|
// 发送事件
|
MediaServerChangeEvent event = new MediaServerChangeEvent(this);
|
event.setMediaServerItemList(mediaServer);
|
applicationEventPublisher.publishEvent(event);
|
}
|
|
return AjaxResult.success();
|
}
|
|
/**
|
* 测试流媒体服务
|
*
|
* @param ip 流媒体服务IP
|
* @param port 流媒体服务HTT端口
|
* @param secret 流媒体服务secret
|
* @param type 流媒体服务类型
|
* @return
|
*/
|
@Operation(summary = "测试流媒体服务")
|
@GetMapping(value = "/check")
|
public AjaxResult checkMediaServer(@RequestParam String ip, @RequestParam int port, @RequestParam String secret, @RequestParam String type) {
|
ZlmMediaServer mediaServer = mediaServerService.checkMediaServer(ip, port, secret, type);
|
return AjaxResult.success(mediaServer);
|
}
|
|
/**
|
* 获取流媒体服务
|
*
|
* @param id 流媒体服务ID
|
* @return
|
*/
|
@Operation(summary = "获取流媒体服务")
|
@GetMapping(value = "/one/{id}")
|
public AjaxResult getMediaServer(@PathVariable String id) {
|
ZlmMediaServer mediaServer = mediaServerService.getOne(id);
|
return AjaxResult.success(mediaServer);
|
}
|
|
/**
|
* 获取流信息
|
*
|
* @param app 应用名
|
* @param stream 流ID
|
* @param mediaServerId 流媒体ID
|
* @return
|
*/
|
@Operation(summary = "获取流信息")
|
@GetMapping(value = "/media_info")
|
public AjaxResult getMediaInfo(@RequestParam String app, @RequestParam String stream, @RequestParam String mediaServerId) {
|
Assert.hasText(app, "app参数不能为空");
|
Assert.hasText(stream, "stream参数不能为空");
|
Assert.hasText(mediaServerId, "mediaServerId参数不能为空");
|
ZlmMediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
if (mediaServer == null) {
|
throw new RuntimeException("流媒体不存在");
|
}
|
return AjaxResult.success(mediaServerService.getMediaInfo(mediaServer, app, stream));
|
}
|
|
/**
|
* 重启流媒体
|
*
|
* @param mediaServerId 流媒体ID
|
* @return
|
*/
|
@Operation(summary = "重启流媒体")
|
@GetMapping(value = "/restartServer/{mediaServerId}")
|
public AjaxResult restartServer(@PathVariable String mediaServerId) {
|
ZlmMediaServer mediaServer = mediaServerService.getOne(mediaServerId);
|
if (mediaServer == null) {
|
throw new RuntimeException("流媒体不存在");
|
}
|
mediaServerService.restartServer(mediaServer);
|
return AjaxResult.success();
|
}
|
|
/**
|
* 获取所有在线媒体服务器
|
*
|
* @return
|
*/
|
@Operation(summary = "获取所有在线媒体服务器")
|
@GetMapping(value = "/getAllOnlineMediaServe")
|
public AjaxResult getAllOnlineMediaServe() {
|
return AjaxResult.success(mediaServerService.getAllOnlineMediaServe());
|
}
|
|
/**
|
* 生成推流地址
|
*
|
* @return
|
*/
|
@Operation(summary = "生成推流地址")
|
@GetMapping(value = "/getStreamPushAddress/{id}")
|
public AjaxResult getStreamPushAddress(@PathVariable Long id, String callId) {
|
if (StringUtils.isEmpty(callId)) {
|
return AjaxResult.error("callId不能是空");
|
}
|
return AjaxResult.success(mediaServerService.getStreamPushAddress(id, callId));
|
}
|
|
/**
|
* 推流播放
|
*
|
* @param request
|
* @param id
|
* @return
|
*/
|
@Operation(summary = "推流播放")
|
@GetMapping(value = "/streamPullPush")
|
public DeferredResult<R<StreamContent>> streamPullPush(HttpServletRequest request, @RequestParam Long id) {
|
Assert.notNull(id, "设备ID不可为NULL");
|
DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
|
result.onTimeout(() -> {
|
log.info("[等待推流超时] id={}", id);
|
R<StreamContent> fail = R.fail("等待推流超时");
|
result.setResult(fail);
|
});
|
|
mediaServerService.streamPullPush(id, (code, msg, streamInfo) -> {
|
if (code == 0 && streamInfo != null) {
|
if (userSetting.getUseSourceIpAsStreamIp()) {
|
streamInfo = streamInfo.clone();//深拷贝
|
String host;
|
try {
|
URL url = new URL(request.getRequestURL().toString());
|
host = url.getHost();
|
} catch (MalformedURLException e) {
|
host = request.getLocalAddr();
|
}
|
streamInfo.changeStreamIp(host);
|
}
|
R<StreamContent> success = R.ok(new StreamContent(streamInfo));
|
result.setResult(success);
|
} else {
|
// 处理失败情况
|
log.info("[等待推流失败] id={}, code={}, msg={}", id, code, msg);
|
R<StreamContent> fail = R.fail(code, msg);
|
result.setResult(fail);
|
}
|
});
|
return result;
|
}
|
|
/**
|
* gb28181 播放
|
*
|
* @param request
|
* @param id 设备id
|
* @return
|
*/
|
@Operation(summary = "gb28181播放")
|
@GetMapping("/startGb28181Play/{id}")
|
public DeferredResult<R<StreamContent>> startGb28181Play(
|
HttpServletRequest request,
|
@PathVariable Long id
|
) {
|
log.info("[gb28181 开始点播] id:{} ", id);
|
Assert.notNull(id, "设备id");
|
|
R<StreamChannel> qsDevicer = zlmStreamService.getQsDeviceInfo(id, SecurityConstants.INNER);
|
if (qsDevicer.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("获取设备信息失败 id:" + id);
|
}
|
Assert.notNull(qsDevicer.getData(), "设备不存在 id:" + id);
|
|
StreamChannel streamChannel = qsDevicer.getData();
|
|
if (!Boolean.TRUE.equals(streamChannel.getDeviceOnLine())) {
|
throw new RuntimeException("设备不在线 id:" + id);
|
}
|
|
R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(streamChannel.getGbDeviceId(), SecurityConstants.INNER);
|
if (deviceR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备信息失败 id:" + streamChannel.getGbDeviceId());
|
}
|
Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 id:" + streamChannel.getGbDeviceId());
|
|
if (!deviceR.getData().isOnLine()) {
|
throw new RuntimeException("gb28181 国标设备不在线失败 id:" + streamChannel.getGbDeviceId());
|
}
|
|
R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(streamChannel.getGbDeviceId(), streamChannel.getGbChannelId(), SecurityConstants.INNER);
|
if (deviceChannelR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
|
}
|
|
Assert.notNull(deviceChannelR.getData(), "gb28181 获取设备通道失败不存在 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
|
|
if (!"ON".equals(deviceChannelR.getData().getStatus())) {
|
throw new RuntimeException("gb28181 国标设备通道不在线失败 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
|
}
|
|
DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
|
|
result.onTimeout(() -> {
|
log.info("[点播等待超时] gbDeviceId:{}, gbChannelId:{}, ", streamChannel.getGbDeviceId(), streamChannel.getGbChannelId());
|
// 释放rtpserver
|
R<StreamContent> wvpResult = R.fail();
|
wvpResult.setMsg("点播超时");
|
result.setResult(wvpResult);
|
|
inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, streamChannel.getId());
|
mediaServerService.stopGb28181Play(InviteSessionType.PLAY, streamChannel, deviceR.getData(), streamChannel.getDeviceCode());
|
});
|
|
ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
|
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
R<StreamContent> r = R.ok();
|
if (streamInfo != null) {
|
if (userSetting.getUseSourceIpAsStreamIp()) {
|
streamInfo = streamInfo.clone();//深拷贝
|
String host;
|
try {
|
URL url = new URL(request.getRequestURL().toString());
|
host = url.getHost();
|
} catch (MalformedURLException e) {
|
host = request.getLocalAddr();
|
}
|
streamInfo.changeStreamIp(host);
|
}
|
if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix()) && !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
|
streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
|
}
|
r.setData(new StreamContent(streamInfo));
|
} else {
|
r.setCode(code);
|
r.setMsg(msg);
|
}
|
|
result.setResult(r);
|
} else {
|
result.setResult(R.fail(code, msg));
|
}
|
};
|
|
streamChannel.setStreamMode(deviceR.getData().getStreamMode());
|
mediaServerService.startGb28181Play(streamChannel, deviceR.getData(), callback);
|
return result;
|
}
|
|
/**
|
* gb28181 停止点播
|
*
|
* @param id 设备id
|
* @return
|
*/
|
@GetMapping("/stopGb28181Play/{id}")
|
public AjaxResult stopGb28181Play(@PathVariable Long id) {
|
|
log.info("[gb28181 停止点播] id:{} ", id);
|
Assert.notNull(id, "设备id");
|
|
R<StreamChannel> qsDevicer = zlmStreamService.getQsDeviceInfo(id, SecurityConstants.INNER);
|
if (qsDevicer.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("获取设备信息失败 id:" + id);
|
}
|
Assert.notNull(qsDevicer.getData(), "设备不存在 id:" + id);
|
|
StreamChannel streamChannel = qsDevicer.getData();
|
|
if (!Boolean.TRUE.equals(streamChannel.getDeviceOnLine())) {
|
throw new RuntimeException("设备不在线 id:" + id);
|
}
|
|
R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(streamChannel.getGbDeviceId(), SecurityConstants.INNER);
|
if (deviceR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备信息失败 id:" + streamChannel.getGbDeviceId());
|
}
|
Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 id:" + streamChannel.getGbDeviceId());
|
|
if (!deviceR.getData().isOnLine()) {
|
throw new RuntimeException("gb28181 国标设备不在线失败 id:" + streamChannel.getGbDeviceId());
|
}
|
|
R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(streamChannel.getGbDeviceId(), streamChannel.getGbChannelId(), SecurityConstants.INNER);
|
if (deviceChannelR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
|
}
|
|
Assert.notNull(deviceChannelR.getData(), "gb28181 获取设备通道失败不存在 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
|
|
if (!"ON".equals(deviceChannelR.getData().getStatus())) {
|
throw new RuntimeException("gb28181 国标设备通道不在线失败 gbDeviceId:" + streamChannel.getGbDeviceId() + ",gbChannelId:" + streamChannel.getGbChannelId());
|
}
|
|
mediaServerService.stopGb28181Play(InviteSessionType.PLAY, streamChannel, deviceR.getData(), streamChannel.getDeviceCode());
|
JSONObject json = new JSONObject();
|
json.put("deviceId", streamChannel.getGbDeviceId());
|
json.put("channelId", streamChannel.getGbChannelId());
|
return AjaxResult.success(json);
|
}
|
|
/**
|
* gb28181 播放(基于GbDevice,不依赖QS,按国标编码播放)
|
*
|
* @param request
|
* @param gbDeviceId 国标设备编码
|
* @param gbChannelId 国标通道编码
|
* @return
|
*/
|
@Operation(summary = "gb28181播放(按国标编码)")
|
@GetMapping("/startGb28181PlayByGbDeviceId/{gbDeviceId}/{gbChannelId}")
|
public DeferredResult<R<StreamContent>> startGb28181PlayByGbDeviceId(
|
HttpServletRequest request,
|
@PathVariable String gbDeviceId,
|
@PathVariable String gbChannelId
|
) {
|
log.info("[gb28181 开始点播] gbDeviceId:{}, gbChannelId:{}", gbDeviceId, gbChannelId);
|
Assert.hasText(gbDeviceId, "国标设备编码不可为空");
|
Assert.hasText(gbChannelId, "国标通道编码不可为空");
|
|
// 1. 查询 GbChannel 配置
|
R<GbChannelDTO> channelR = remoteGb28181Service.getGbChannel(gbDeviceId, gbChannelId, SecurityConstants.INNER);
|
if (channelR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("获取GbChannel配置失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId
|
+ "。设备注册后会自动创建,请确认设备已上线并完成目录同步。");
|
}
|
Assert.notNull(channelR.getData(), "GbChannel不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
|
StreamChannel streamChannel = ZlmStreamService.toStreamChannel(channelR.getData());
|
|
// 2. 查询国标设备
|
R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(gbDeviceId, SecurityConstants.INNER);
|
if (deviceR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备信息失败 deviceId:" + gbDeviceId);
|
}
|
Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 deviceId:" + gbDeviceId);
|
|
if (!deviceR.getData().isOnLine()) {
|
throw new RuntimeException("gb28181 国标设备不在线 deviceId:" + gbDeviceId);
|
}
|
|
// 3. 查询国标通道
|
R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(gbDeviceId, gbChannelId, SecurityConstants.INNER);
|
if (deviceChannelR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
}
|
Assert.notNull(deviceChannelR.getData(), "gb28181 国标设备通道不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
|
if (!"ON".equals(deviceChannelR.getData().getStatus())) {
|
throw new RuntimeException("gb28181 国标设备通道不在线 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
}
|
|
DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
|
|
result.onTimeout(() -> {
|
log.info("[点播等待超时] gbDeviceId:{}, gbChannelId:{}", gbDeviceId, gbChannelId);
|
R<StreamContent> fail = R.fail();
|
fail.setMsg("点播超时");
|
result.setResult(fail);
|
|
inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, streamChannel.getId());
|
mediaServerService.stopGb28181PlayByGbChannel(InviteSessionType.PLAY, streamChannel, deviceR.getData(), streamChannel.getDeviceCode());
|
});
|
|
ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
|
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
R<StreamContent> r = R.ok();
|
if (streamInfo != null) {
|
if (userSetting.getUseSourceIpAsStreamIp()) {
|
streamInfo = streamInfo.clone();
|
String host;
|
try {
|
URL url = new URL(request.getRequestURL().toString());
|
host = url.getHost();
|
} catch (MalformedURLException e) {
|
host = request.getLocalAddr();
|
}
|
streamInfo.changeStreamIp(host);
|
}
|
if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix())
|
&& !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
|
streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
|
}
|
r.setData(new StreamContent(streamInfo));
|
} else {
|
r.setCode(code);
|
r.setMsg(msg);
|
}
|
result.setResult(r);
|
} else {
|
result.setResult(R.fail(code, msg));
|
}
|
};
|
|
mediaServerService.startGb28181PlayByGbChannel(streamChannel, deviceR.getData(), callback);
|
return result;
|
}
|
|
/**
|
* gb28181 停止点播(基于GbDevice,不依赖QS,按国标编码停止)
|
*
|
* @param gbDeviceId 国标设备编码
|
* @param gbChannelId 国标通道编码
|
* @return
|
*/
|
@Operation(summary = "gb28181停止点播(按国标编码)")
|
@GetMapping("/stopGb28181PlayByGbDeviceId/{gbDeviceId}/{gbChannelId}")
|
public AjaxResult stopGb28181PlayByGbDeviceId(
|
@PathVariable String gbDeviceId,
|
@PathVariable String gbChannelId
|
) {
|
log.info("[gb28181 停止点播] gbDeviceId:{}, gbChannelId:{}", gbDeviceId, gbChannelId);
|
Assert.hasText(gbDeviceId, "国标设备编码不可为空");
|
Assert.hasText(gbChannelId, "国标通道编码不可为空");
|
|
// 1. 查询 GbChannel 配置
|
R<GbChannelDTO> channelR = remoteGb28181Service.getGbChannel(gbDeviceId, gbChannelId, SecurityConstants.INNER);
|
if (channelR.getCode() != Constants.SUCCESS || channelR.getData() == null) {
|
throw new RuntimeException("GbChannel不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
}
|
|
StreamChannel streamChannel = ZlmStreamService.toStreamChannel(channelR.getData());
|
|
// 2. 查询国标设备
|
R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(gbDeviceId, SecurityConstants.INNER);
|
if (deviceR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备信息失败 deviceId:" + gbDeviceId);
|
}
|
Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 deviceId:" + gbDeviceId);
|
|
// 3. 查询国标通道
|
R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(gbDeviceId, gbChannelId, SecurityConstants.INNER);
|
if (deviceChannelR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
}
|
Assert.notNull(deviceChannelR.getData(), "gb28181 国标设备通道不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
|
mediaServerService.stopGb28181PlayByGbChannel(InviteSessionType.PLAY, streamChannel,
|
deviceR.getData(), streamChannel.getDeviceCode());
|
|
JSONObject json = new JSONObject();
|
json.put("gbDeviceId", gbDeviceId);
|
json.put("gbChannelId", gbChannelId);
|
return AjaxResult.success(json);
|
}
|
|
/**
|
* gb28181 播放(基于GbChannel主键ID)
|
*
|
* @param request
|
* @param channelId GbChannel主键ID
|
* @return
|
*/
|
@Operation(summary = "gb28181播放(按通道主键ID)")
|
@GetMapping("/startGb28181PlayByChannelId/{channelId}")
|
public DeferredResult<R<StreamContent>> startGb28181PlayByChannelId(
|
HttpServletRequest request,
|
@PathVariable Long channelId
|
) {
|
log.info("[gb28181 开始点播] channelId:{}", channelId);
|
Assert.notNull(channelId, "通道ID不可为空");
|
|
// 1. 根据主键ID查询 GbChannel
|
R<GbChannelDTO> channelR = remoteGb28181Service.getGbChannelById(channelId, SecurityConstants.INNER);
|
if (channelR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("获取GbChannel失败 channelId:" + channelId);
|
}
|
Assert.notNull(channelR.getData(), "GbChannel不存在 channelId:" + channelId);
|
|
StreamChannel streamChannel = ZlmStreamService.toStreamChannel(channelR.getData());
|
String gbDeviceId = streamChannel.getGbDeviceId();
|
String gbChannelId = streamChannel.getGbChannelId();
|
|
// 2. 查询国标设备
|
R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(gbDeviceId, SecurityConstants.INNER);
|
if (deviceR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备信息失败 deviceId:" + gbDeviceId);
|
}
|
Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 deviceId:" + gbDeviceId);
|
|
if (!deviceR.getData().isOnLine()) {
|
throw new RuntimeException("gb28181 国标设备不在线 deviceId:" + gbDeviceId);
|
}
|
|
// 3. 查询国标通道
|
R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(gbDeviceId, gbChannelId, SecurityConstants.INNER);
|
if (deviceChannelR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
}
|
Assert.notNull(deviceChannelR.getData(), "gb28181 国标设备通道不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
|
if (!"ON".equals(deviceChannelR.getData().getStatus())) {
|
throw new RuntimeException("gb28181 国标设备通道不在线 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
}
|
|
DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
|
|
result.onTimeout(() -> {
|
log.info("[点播等待超时] channelId:{}, gbDeviceId:{}, gbChannelId:{}", channelId, gbDeviceId, gbChannelId);
|
R<StreamContent> fail = R.fail();
|
fail.setMsg("点播超时");
|
result.setResult(fail);
|
|
inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, streamChannel.getId());
|
mediaServerService.stopGb28181PlayByGbChannel(InviteSessionType.PLAY, streamChannel, deviceR.getData(), streamChannel.getDeviceCode());
|
});
|
|
ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
|
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
R<StreamContent> r = R.ok();
|
if (streamInfo != null) {
|
if (userSetting.getUseSourceIpAsStreamIp()) {
|
streamInfo = streamInfo.clone();
|
String host;
|
try {
|
URL url = new URL(request.getRequestURL().toString());
|
host = url.getHost();
|
} catch (MalformedURLException e) {
|
host = request.getLocalAddr();
|
}
|
streamInfo.changeStreamIp(host);
|
}
|
if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix())
|
&& !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
|
streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
|
}
|
r.setData(new StreamContent(streamInfo));
|
} else {
|
r.setCode(code);
|
r.setMsg(msg);
|
}
|
result.setResult(r);
|
} else {
|
result.setResult(R.fail(code, msg));
|
}
|
};
|
|
mediaServerService.startGb28181PlayByGbChannel(streamChannel, deviceR.getData(), callback);
|
return result;
|
}
|
|
/**
|
* gb28181 停止点播(基于GbChannel主键ID)
|
*
|
* @param channelId GbChannel主键ID
|
* @return
|
*/
|
@Operation(summary = "gb28181停止点播(按通道主键ID)")
|
@GetMapping("/stopGb28181PlayByChannelId/{channelId}")
|
public AjaxResult stopGb28181PlayByChannelId(@PathVariable Long channelId) {
|
log.info("[gb28181 停止点播] channelId:{}", channelId);
|
Assert.notNull(channelId, "通道ID不可为空");
|
|
// 1. 根据主键ID查询 GbChannel
|
R<GbChannelDTO> channelR = remoteGb28181Service.getGbChannelById(channelId, SecurityConstants.INNER);
|
if (channelR.getCode() != Constants.SUCCESS || channelR.getData() == null) {
|
throw new RuntimeException("GbChannel不存在 channelId:" + channelId);
|
}
|
|
StreamChannel streamChannel = ZlmStreamService.toStreamChannel(channelR.getData());
|
String gbDeviceId = streamChannel.getGbDeviceId();
|
String gbChannelId = streamChannel.getGbChannelId();
|
|
// 2. 查询国标设备
|
R<Device> deviceR = remoteGb28181Service.getDeviceByDeviceId(gbDeviceId, SecurityConstants.INNER);
|
if (deviceR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备信息失败 deviceId:" + gbDeviceId);
|
}
|
Assert.notNull(deviceR.getData(), "gb28181 国标设备不存在 deviceId:" + gbDeviceId);
|
|
// 3. 查询国标通道
|
R<DeviceChannel> deviceChannelR = remoteGb28181Service.getDeviceChannelByChannelId(gbDeviceId, gbChannelId, SecurityConstants.INNER);
|
if (deviceChannelR.getCode() != Constants.SUCCESS) {
|
throw new RuntimeException("gb28181 获取设备通道失败 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
}
|
Assert.notNull(deviceChannelR.getData(), "gb28181 国标设备通道不存在 gbDeviceId:" + gbDeviceId + " gbChannelId:" + gbChannelId);
|
|
mediaServerService.stopGb28181PlayByGbChannel(InviteSessionType.PLAY, streamChannel,
|
deviceR.getData(), streamChannel.getDeviceCode());
|
|
JSONObject json = new JSONObject();
|
json.put("channelId", channelId);
|
json.put("gbDeviceId", gbDeviceId);
|
json.put("gbChannelId", gbChannelId);
|
return AjaxResult.success(json);
|
}
|
|
/**
|
* 拉流代理(通过通道ID,简化传参)
|
*
|
* @param request HttpServletRequest
|
* @param channelId 通道ID
|
* @return
|
*/
|
@Operation(summary = "拉流代理(通过通道ID,简化传参)")
|
@PostMapping("/streamPullPlayByChannelId/{channelId}")
|
public DeferredResult<R<StreamContent>> streamPullPlayByChannelId(
|
HttpServletRequest request,
|
@PathVariable String channelId
|
) {
|
log.info("[拉流代理-通道ID] channelId:{}", channelId);
|
Assert.hasText(channelId, "通道ID不可为空");
|
|
DeferredResult<R<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
|
|
ErrorCallback<StreamInfo> callback = (code, msg, streamInfo) -> {
|
if (code == InviteErrorCode.SUCCESS.getCode()) {
|
R<StreamContent> r = R.ok();
|
if (streamInfo != null) {
|
if (userSetting.getUseSourceIpAsStreamIp()) {
|
streamInfo = streamInfo.clone();
|
String host;
|
try {
|
URL url = new URL(request.getRequestURL().toString());
|
host = url.getHost();
|
} catch (MalformedURLException e) {
|
host = request.getLocalAddr();
|
}
|
streamInfo.changeStreamIp(host);
|
}
|
if (!ObjectUtils.isEmpty(streamInfo.getMediaServer().getTranscodeSuffix())
|
&& !"null".equalsIgnoreCase(streamInfo.getMediaServer().getTranscodeSuffix())) {
|
streamInfo.setStream(streamInfo.getStream() + "_" + streamInfo.getMediaServer().getTranscodeSuffix());
|
}
|
r.setData(new StreamContent(streamInfo));
|
} else {
|
r.setCode(code);
|
r.setMsg(msg);
|
}
|
result.setResult(r);
|
} else {
|
result.setResult(R.fail(code, msg));
|
}
|
};
|
|
mediaServerService.streamPullPlayByChannelId(channelId, callback);
|
return result;
|
}
|
|
/**
|
* 停止拉流代理(通过通道ID)
|
*
|
* @param channelId 通道ID
|
* @return
|
*/
|
@Operation(summary = "停止拉流代理(通过通道ID)")
|
@PostMapping("/stopStreamPullPlayByChannelId/{channelId}")
|
public AjaxResult stopStreamPullPlayByChannelId(@PathVariable String channelId) {
|
log.info("[停止拉流代理-通道ID] channelId:{}", channelId);
|
Assert.hasText(channelId, "通道ID不可为空");
|
mediaServerService.stopStreamPullPlayByChannelId(channelId);
|
return AjaxResult.success();
|
}
|
|
}
|