PlayController.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. package com.genersoft.iot.vmp.gb28181.controller;
  2. import com.alibaba.fastjson2.JSONArray;
  3. import com.alibaba.fastjson2.JSONObject;
  4. import com.genersoft.iot.vmp.common.InviteSessionType;
  5. import com.genersoft.iot.vmp.common.StreamInfo;
  6. import com.genersoft.iot.vmp.conf.UserSetting;
  7. import com.genersoft.iot.vmp.conf.exception.ControllerException;
  8. import com.genersoft.iot.vmp.conf.security.JwtUtils;
  9. import com.genersoft.iot.vmp.gb28181.bean.Device;
  10. import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
  11. import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  12. import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  13. import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
  14. import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  15. import com.genersoft.iot.vmp.media.bean.MediaServer;
  16. import com.genersoft.iot.vmp.media.service.IMediaServerService;
  17. import com.genersoft.iot.vmp.service.IDeviceChannelService;
  18. import com.genersoft.iot.vmp.service.IDeviceService;
  19. import com.genersoft.iot.vmp.service.IInviteStreamService;
  20. import com.genersoft.iot.vmp.service.IPlayService;
  21. import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
  22. import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  23. import com.genersoft.iot.vmp.utils.DateUtil;
  24. import com.genersoft.iot.vmp.vmanager.bean.AudioBroadcastResult;
  25. import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
  26. import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
  27. import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
  28. import io.swagger.v3.oas.annotations.Operation;
  29. import io.swagger.v3.oas.annotations.Parameter;
  30. import io.swagger.v3.oas.annotations.security.SecurityRequirement;
  31. import io.swagger.v3.oas.annotations.tags.Tag;
  32. import lombok.extern.slf4j.Slf4j;
  33. import org.springframework.beans.factory.annotation.Autowired;
  34. import org.springframework.util.ObjectUtils;
  35. import org.springframework.web.bind.annotation.*;
  36. import org.springframework.web.context.request.async.DeferredResult;
  37. import javax.servlet.http.HttpServletRequest;
  38. import java.net.MalformedURLException;
  39. import java.net.URL;
  40. import java.util.List;
  41. import java.util.UUID;
  42. /**
  43. * @author lin
  44. */
  45. @Tag(name = "国标设备点播")
  46. @Slf4j
  47. @RestController
  48. @RequestMapping("/api/play")
  49. public class PlayController {
  50. @Autowired
  51. private SIPCommander cmder;
  52. @Autowired
  53. private VideoStreamSessionManager streamSession;
  54. @Autowired
  55. private IVideoManagerStorage storager;
  56. @Autowired
  57. private IInviteStreamService inviteStreamService;
  58. @Autowired
  59. private DeferredResultHolder resultHolder;
  60. @Autowired
  61. private IPlayService playService;
  62. @Autowired
  63. private IMediaServerService mediaServerService;
  64. @Autowired
  65. private UserSetting userSetting;
  66. @Autowired
  67. private IDeviceService deviceService;
  68. @Autowired
  69. private IDeviceChannelService deviceChannelService;
  70. @Operation(summary = "开始点播", security = @SecurityRequirement(name = JwtUtils.HEADER))
  71. @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  72. @Parameter(name = "channelId", description = "通道国标编号", required = true)
  73. @GetMapping("/start/{deviceId}/{channelId}")
  74. public DeferredResult<WVPResult<StreamContent>> play(HttpServletRequest request, @PathVariable String deviceId,
  75. @PathVariable String channelId) {
  76. log.info("[开始点播] deviceId:{}, channelId:{}, ", deviceId, channelId);
  77. if (ObjectUtils.isEmpty(deviceId) || ObjectUtils.isEmpty(channelId)) {
  78. throw new ControllerException(ErrorCode.ERROR400);
  79. }
  80. // 获取可用的zlm
  81. Device device = deviceService.getDevice(deviceId);
  82. MediaServer newMediaServerItem = playService.getNewMediaServerItem(device);
  83. RequestMessage requestMessage = new RequestMessage();
  84. String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
  85. requestMessage.setKey(key);
  86. String uuid = UUID.randomUUID().toString();
  87. requestMessage.setId(uuid);
  88. DeferredResult<WVPResult<StreamContent>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());
  89. result.onTimeout(()->{
  90. log.info("[点播等待超时] deviceId:{}, channelId:{}, ", deviceId, channelId);
  91. // 释放rtpserver
  92. WVPResult<StreamInfo> wvpResult = new WVPResult<>();
  93. wvpResult.setCode(ErrorCode.ERROR100.getCode());
  94. wvpResult.setMsg("点播超时");
  95. requestMessage.setData(wvpResult);
  96. resultHolder.invokeAllResult(requestMessage);
  97. inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
  98. deviceChannelService.stopPlay(deviceId, channelId);
  99. });
  100. // 录像查询以channelId作为deviceId查询
  101. resultHolder.put(key, uuid, result);
  102. playService.play(newMediaServerItem, deviceId, channelId, null, (code, msg, data) -> {
  103. WVPResult<StreamContent> wvpResult = new WVPResult<>();
  104. if (code == InviteErrorCode.SUCCESS.getCode()) {
  105. wvpResult.setCode(ErrorCode.SUCCESS.getCode());
  106. wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
  107. if (data != null) {
  108. StreamInfo streamInfo = (StreamInfo)data;
  109. if (userSetting.getUseSourceIpAsStreamIp()) {
  110. streamInfo=streamInfo.clone();//深拷贝
  111. String host;
  112. try {
  113. URL url=new URL(request.getRequestURL().toString());
  114. host=url.getHost();
  115. } catch (MalformedURLException e) {
  116. host=request.getLocalAddr();
  117. }
  118. streamInfo.channgeStreamIp(host);
  119. }
  120. if (!ObjectUtils.isEmpty(newMediaServerItem.getTranscodeSuffix()) && !"null".equalsIgnoreCase(newMediaServerItem.getTranscodeSuffix())) {
  121. streamInfo.setStream(streamInfo.getStream() + "_" + newMediaServerItem.getTranscodeSuffix());
  122. }
  123. wvpResult.setData(new StreamContent(streamInfo));
  124. }else {
  125. wvpResult.setCode(code);
  126. wvpResult.setMsg(msg);
  127. }
  128. }else {
  129. wvpResult.setCode(code);
  130. wvpResult.setMsg(msg);
  131. }
  132. requestMessage.setData(wvpResult);
  133. // 此处必须释放所有请求
  134. resultHolder.invokeAllResult(requestMessage);
  135. });
  136. return result;
  137. }
  138. @Operation(summary = "停止点播", security = @SecurityRequirement(name = JwtUtils.HEADER))
  139. @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  140. @Parameter(name = "channelId", description = "通道国标编号", required = true)
  141. @GetMapping("/stop/{deviceId}/{channelId}")
  142. public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId) {
  143. log.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId ));
  144. if (deviceId == null || channelId == null) {
  145. throw new ControllerException(ErrorCode.ERROR400);
  146. }
  147. Device device = deviceService.getDevice(deviceId);
  148. if (device == null) {
  149. throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备[" + deviceId + "]不存在");
  150. }
  151. playService.stopPlay(device, channelId);
  152. JSONObject json = new JSONObject();
  153. json.put("deviceId", deviceId);
  154. json.put("channelId", channelId);
  155. return json;
  156. }
  157. /**
  158. * 结束转码
  159. */
  160. @Operation(summary = "结束转码", security = @SecurityRequirement(name = JwtUtils.HEADER))
  161. @Parameter(name = "key", description = "视频流key", required = true)
  162. @Parameter(name = "mediaServerId", description = "流媒体服务ID", required = true)
  163. @PostMapping("/convertStop/{key}")
  164. public void playConvertStop(@PathVariable String key, String mediaServerId) {
  165. if (mediaServerId == null) {
  166. throw new ControllerException(ErrorCode.ERROR400.getCode(), "流媒体:" + mediaServerId + "不存在" );
  167. }
  168. MediaServer mediaInfo = mediaServerService.getOne(mediaServerId);
  169. if (mediaInfo == null) {
  170. throw new ControllerException(ErrorCode.ERROR100.getCode(), "使用的流媒体已经停止运行" );
  171. }else {
  172. Boolean deleted = mediaServerService.delFFmpegSource(mediaInfo, key);
  173. if (!deleted) {
  174. throw new ControllerException(ErrorCode.ERROR100 );
  175. }
  176. }
  177. }
  178. @Operation(summary = "语音广播命令", security = @SecurityRequirement(name = JwtUtils.HEADER))
  179. @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  180. @Parameter(name = "deviceId", description = "通道国标编号", required = true)
  181. @Parameter(name = "timeout", description = "推流超时时间(秒)", required = true)
  182. @GetMapping("/broadcast/{deviceId}/{channelId}")
  183. @PostMapping("/broadcast/{deviceId}/{channelId}")
  184. public AudioBroadcastResult broadcastApi(@PathVariable String deviceId, @PathVariable String channelId, Integer timeout, Boolean broadcastMode) {
  185. if (log.isDebugEnabled()) {
  186. log.debug("语音广播API调用");
  187. }
  188. Device device = deviceService.getDevice(deviceId);
  189. if (device == null) {
  190. throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到设备: " + deviceId);
  191. }
  192. if (channelId == null) {
  193. throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到通道: " + channelId);
  194. }
  195. return playService.audioBroadcast(device, channelId, broadcastMode);
  196. }
  197. @Operation(summary = "停止语音广播")
  198. @Parameter(name = "deviceId", description = "设备Id", required = true)
  199. @Parameter(name = "channelId", description = "通道Id", required = true)
  200. @GetMapping("/broadcast/stop/{deviceId}/{channelId}")
  201. @PostMapping("/broadcast/stop/{deviceId}/{channelId}")
  202. public void stopBroadcast(@PathVariable String deviceId, @PathVariable String channelId) {
  203. if (log.isDebugEnabled()) {
  204. log.debug("停止语音广播API调用");
  205. }
  206. // try {
  207. // playService.stopAudioBroadcast(deviceId, channelId);
  208. // } catch (InvalidArgumentException | ParseException | SipException e) {
  209. // logger.error("[命令发送失败] 停止语音: {}", e.getMessage());
  210. // throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
  211. // }
  212. playService.stopAudioBroadcast(deviceId, channelId);
  213. }
  214. @Operation(summary = "获取所有的ssrc", security = @SecurityRequirement(name = JwtUtils.HEADER))
  215. @GetMapping("/ssrc")
  216. public JSONObject getSSRC() {
  217. if (log.isDebugEnabled()) {
  218. log.debug("获取所有的ssrc");
  219. }
  220. JSONArray objects = new JSONArray();
  221. List<SsrcTransaction> allSsrc = streamSession.getAllSsrc();
  222. for (SsrcTransaction transaction : allSsrc) {
  223. JSONObject jsonObject = new JSONObject();
  224. jsonObject.put("deviceId", transaction.getDeviceId());
  225. jsonObject.put("channelId", transaction.getChannelId());
  226. jsonObject.put("ssrc", transaction.getSsrc());
  227. jsonObject.put("streamId", transaction.getStream());
  228. objects.add(jsonObject);
  229. }
  230. JSONObject jsonObject = new JSONObject();
  231. jsonObject.put("data", objects);
  232. jsonObject.put("count", objects.size());
  233. return jsonObject;
  234. }
  235. @Operation(summary = "获取截图", security = @SecurityRequirement(name = JwtUtils.HEADER))
  236. @Parameter(name = "deviceId", description = "设备国标编号", required = true)
  237. @Parameter(name = "channelId", description = "通道国标编号", required = true)
  238. @Parameter(name = "isSubStream", description = "是否子码流(true-子码流,false-主码流),默认为false", required = true)
  239. @GetMapping("/snap")
  240. public DeferredResult<String> getSnap(String deviceId, String channelId,boolean isSubStream) {
  241. if (log.isDebugEnabled()) {
  242. log.debug("获取截图: {}/{}", deviceId, channelId);
  243. }
  244. DeferredResult<String> result = new DeferredResult<>(3 * 1000L);
  245. String key = DeferredResultHolder.CALLBACK_CMD_SNAP + deviceId;
  246. String uuid = UUID.randomUUID().toString();
  247. resultHolder.put(key, uuid, result);
  248. RequestMessage message = new RequestMessage();
  249. message.setKey(key);
  250. message.setId(uuid);
  251. String fileName = deviceId + "_" + channelId + "_" + DateUtil.getNowForUrl() + ".jpg";
  252. playService.getSnap(deviceId, channelId, fileName, (code, msg, data) -> {
  253. if (code == InviteErrorCode.SUCCESS.getCode()) {
  254. message.setData(data);
  255. }else {
  256. message.setData(WVPResult.fail(code, msg));
  257. }
  258. resultHolder.invokeResult(message);
  259. });
  260. return result;
  261. }
  262. }