PlayController.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. package com.genersoft.iot.vmp.vmanager.play;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONArray;
  4. import com.genersoft.iot.vmp.common.StreamInfo;
  5. import com.genersoft.iot.vmp.conf.MediaServerConfig;
  6. import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  7. import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  8. import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
  9. import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
  10. import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
  11. import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  12. import com.genersoft.iot.vmp.vmanager.service.IPlayService;
  13. import org.slf4j.Logger;
  14. import org.slf4j.LoggerFactory;
  15. import org.springframework.beans.factory.annotation.Autowired;
  16. import org.springframework.beans.factory.annotation.Value;
  17. import org.springframework.http.HttpStatus;
  18. import org.springframework.http.ResponseEntity;
  19. import org.springframework.web.bind.annotation.CrossOrigin;
  20. import org.springframework.web.bind.annotation.GetMapping;
  21. import org.springframework.web.bind.annotation.PathVariable;
  22. import org.springframework.web.bind.annotation.PostMapping;
  23. import org.springframework.web.bind.annotation.RequestMapping;
  24. import org.springframework.web.bind.annotation.RestController;
  25. import com.alibaba.fastjson.JSONObject;
  26. import com.genersoft.iot.vmp.gb28181.bean.Device;
  27. import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  28. import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
  29. import org.springframework.web.context.request.async.DeferredResult;
  30. import javax.sip.message.Response;
  31. import java.text.DecimalFormat;
  32. import java.util.UUID;
  33. @CrossOrigin
  34. @RestController
  35. @RequestMapping("/api")
  36. public class PlayController {
  37. private final static Logger logger = LoggerFactory.getLogger(PlayController.class);
  38. @Autowired
  39. private SIPCommander cmder;
  40. @Autowired
  41. private IVideoManagerStorager storager;
  42. @Autowired
  43. private IRedisCatchStorage redisCatchStorage;
  44. @Autowired
  45. private ZLMRESTfulUtils zlmresTfulUtils;
  46. @Autowired
  47. private DeferredResultHolder resultHolder;
  48. @Autowired
  49. private IPlayService playService;
  50. @GetMapping("/play/{deviceId}/{channelId}")
  51. public DeferredResult<ResponseEntity<String>> play(@PathVariable String deviceId,
  52. @PathVariable String channelId) {
  53. Device device = storager.queryVideoDevice(deviceId);
  54. StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
  55. UUID uuid = UUID.randomUUID();
  56. DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>();
  57. // 录像查询以channelId作为deviceId查询
  58. resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid, result);
  59. if (streamInfo == null) {
  60. // 发送点播消息
  61. cmder.playStreamCmd(device, channelId, (JSONObject response) -> {
  62. logger.info("收到订阅消息: " + response.toJSONString());
  63. playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString());
  64. }, event -> {
  65. RequestMessage msg = new RequestMessage();
  66. msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
  67. Response response = event.getResponse();
  68. msg.setData(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
  69. resultHolder.invokeResult(msg);
  70. });
  71. } else {
  72. String streamId = streamInfo.getStreamId();
  73. JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
  74. if (rtpInfo.getBoolean("exist")) {
  75. RequestMessage msg = new RequestMessage();
  76. msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
  77. msg.setData(JSON.toJSONString(streamInfo));
  78. resultHolder.invokeResult(msg);
  79. } else {
  80. redisCatchStorage.stopPlay(streamInfo);
  81. storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
  82. cmder.playStreamCmd(device, channelId, (JSONObject response) -> {
  83. logger.info("收到订阅消息: " + response.toJSONString());
  84. playService.onPublishHandlerForPlay(response, deviceId, channelId, uuid.toString());
  85. }, event -> {
  86. RequestMessage msg = new RequestMessage();
  87. msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
  88. Response response = event.getResponse();
  89. msg.setData(String.format("点播失败, 错误码: %s, %s", response.getStatusCode(), response.getReasonPhrase()));
  90. resultHolder.invokeResult(msg);
  91. });
  92. }
  93. }
  94. // 超时处理
  95. result.onTimeout(()->{
  96. logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId));
  97. // 释放rtpserver
  98. cmder.closeRTPServer(device, channelId);
  99. RequestMessage msg = new RequestMessage();
  100. msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
  101. msg.setData("Timeout");
  102. resultHolder.invokeResult(msg);
  103. });
  104. return result;
  105. }
  106. @PostMapping("/play/{streamId}/stop")
  107. public DeferredResult<ResponseEntity<String>> playStop(@PathVariable String streamId) {
  108. logger.debug(String.format("设备预览/回放停止API调用,streamId:%s", streamId));
  109. UUID uuid = UUID.randomUUID();
  110. DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>();
  111. // 录像查询以channelId作为deviceId查询
  112. resultHolder.put(DeferredResultHolder.CALLBACK_CMD_STOP + uuid, result);
  113. cmder.streamByeCmd(streamId, event -> {
  114. StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
  115. if (streamInfo == null) {
  116. RequestMessage msg = new RequestMessage();
  117. msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
  118. msg.setData("streamId not found");
  119. resultHolder.invokeResult(msg);
  120. }else {
  121. redisCatchStorage.stopPlay(streamInfo);
  122. storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
  123. RequestMessage msg = new RequestMessage();
  124. msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid);
  125. Response response = event.getResponse();
  126. msg.setData(String.format("success"));
  127. resultHolder.invokeResult(msg);
  128. }
  129. });
  130. if (streamId != null) {
  131. JSONObject json = new JSONObject();
  132. json.put("streamId", streamId);
  133. RequestMessage msg = new RequestMessage();
  134. msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
  135. msg.setData(json.toString());
  136. resultHolder.invokeResult(msg);
  137. } else {
  138. logger.warn("设备预览/回放停止API调用失败!");
  139. RequestMessage msg = new RequestMessage();
  140. msg.setId(DeferredResultHolder.CALLBACK_CMD_PlAY + uuid);
  141. msg.setData("streamId null");
  142. resultHolder.invokeResult(msg);
  143. }
  144. // 超时处理
  145. result.onTimeout(()->{
  146. logger.warn(String.format("设备预览/回放停止超时,streamId:%s ", streamId));
  147. RequestMessage msg = new RequestMessage();
  148. msg.setId(DeferredResultHolder.CALLBACK_CMD_STOP + uuid);
  149. msg.setData("Timeout");
  150. resultHolder.invokeResult(msg);
  151. });
  152. return result;
  153. }
  154. /**
  155. * 将不是h264的视频通过ffmpeg 转码为h264 + aac
  156. * @param streamId 流ID
  157. * @return
  158. */
  159. @PostMapping("/play/{streamId}/convert")
  160. public ResponseEntity<String> playConvert(@PathVariable String streamId) {
  161. StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
  162. if (streamInfo == null) {
  163. logger.warn("视频转码API调用失败!, 视频流已经停止!");
  164. return new ResponseEntity<String>("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK);
  165. }
  166. JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(streamId);
  167. if (!rtpInfo.getBoolean("exist")) {
  168. logger.warn("视频转码API调用失败!, 视频流已停止推流!");
  169. return new ResponseEntity<String>("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK);
  170. } else {
  171. MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
  172. String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(),
  173. streamId );
  174. String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), streamId);
  175. JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(srcUrl, dstUrl, "1000000");
  176. System.out.println(jsonObject);
  177. JSONObject result = new JSONObject();
  178. if (jsonObject != null && jsonObject.getInteger("code") == 0) {
  179. result.put("code", 0);
  180. JSONObject data = jsonObject.getJSONObject("data");
  181. if (data != null) {
  182. result.put("key", data.getString("key"));
  183. StreamInfo streamInfoResult = new StreamInfo();
  184. streamInfoResult.setRtmp(dstUrl);
  185. streamInfoResult.setRtsp(String.format("rtsp://%s:%s/convert/%s", mediaInfo.getWanIp(), mediaInfo.getRtspPort(), streamId));
  186. streamInfoResult.setStreamId(streamId);
  187. streamInfoResult.setFlv(String.format("http://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
  188. streamInfoResult.setWs_flv(String.format("ws://%s:%s/convert/%s.flv", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
  189. streamInfoResult.setHls(String.format("http://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
  190. streamInfoResult.setWs_hls(String.format("ws://%s:%s/convert/%s/hls.m3u8", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
  191. streamInfoResult.setFmp4(String.format("http://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
  192. streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/convert/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
  193. streamInfoResult.setTs(String.format("http://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
  194. streamInfoResult.setWs_ts(String.format("ws://%s:%s/convert/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), streamId));
  195. result.put("data", streamInfoResult);
  196. }
  197. }else {
  198. result.put("code", 1);
  199. result.put("msg", "cover fail");
  200. }
  201. return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK);
  202. }
  203. }
  204. /**
  205. * 结束转码
  206. * @param key
  207. * @return
  208. */
  209. @PostMapping("/play/convert/stop/{key}")
  210. public ResponseEntity<String> playConvertStop(@PathVariable String key) {
  211. JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(key);
  212. System.out.println(jsonObject);
  213. JSONObject result = new JSONObject();
  214. if (jsonObject != null && jsonObject.getInteger("code") == 0) {
  215. result.put("code", 0);
  216. JSONObject data = jsonObject.getJSONObject("data");
  217. if (data != null && data.getBoolean("flag")) {
  218. result.put("code", "0");
  219. result.put("msg", "success");
  220. }else {
  221. }
  222. }else {
  223. result.put("code", 1);
  224. result.put("msg", "delFFmpegSource fail");
  225. }
  226. return new ResponseEntity<String>( result.toJSONString(), HttpStatus.OK);
  227. }
  228. }