PlayServiceImpl.java 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. package com.genersoft.iot.vmp.service.impl;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONArray;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.genersoft.iot.vmp.common.StreamInfo;
  6. import com.genersoft.iot.vmp.conf.UserSetting;
  7. import com.genersoft.iot.vmp.gb28181.bean.*;
  8. import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
  9. import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  10. import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  11. import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
  12. import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  13. import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
  14. import com.genersoft.iot.vmp.gb28181.utils.DateUtil;
  15. import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
  16. import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
  17. import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
  18. import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
  19. import com.genersoft.iot.vmp.service.IMediaServerService;
  20. import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
  21. import com.genersoft.iot.vmp.service.bean.PlayBackCallback;
  22. import com.genersoft.iot.vmp.service.bean.PlayBackResult;
  23. import com.genersoft.iot.vmp.service.bean.SSRCInfo;
  24. import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
  25. import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
  26. import com.genersoft.iot.vmp.utils.redis.RedisUtil;
  27. import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
  28. import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
  29. import com.genersoft.iot.vmp.service.IMediaService;
  30. import com.genersoft.iot.vmp.service.IPlayService;
  31. import gov.nist.javax.sip.stack.SIPDialog;
  32. import org.slf4j.Logger;
  33. import org.slf4j.LoggerFactory;
  34. import org.springframework.beans.factory.annotation.Autowired;
  35. import org.springframework.http.HttpStatus;
  36. import org.springframework.http.ResponseEntity;
  37. import org.springframework.stereotype.Service;
  38. import org.springframework.util.ResourceUtils;
  39. import org.springframework.web.context.request.async.DeferredResult;
  40. import java.io.FileNotFoundException;
  41. import java.math.BigDecimal;
  42. import java.util.*;
  43. @SuppressWarnings(value = {"rawtypes", "unchecked"})
  44. @Service
  45. public class PlayServiceImpl implements IPlayService {
  46. private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class);
  47. @Autowired
  48. private IVideoManagerStorage storager;
  49. @Autowired
  50. private SIPCommander cmder;
  51. @Autowired
  52. private SIPCommanderFroPlatform sipCommanderFroPlatform;
  53. @Autowired
  54. private IRedisCatchStorage redisCatchStorage;
  55. @Autowired
  56. private RedisUtil redis;
  57. @Autowired
  58. private DeferredResultHolder resultHolder;
  59. @Autowired
  60. private ZLMRESTfulUtils zlmresTfulUtils;
  61. @Autowired
  62. private AssistRESTfulUtils assistRESTfulUtils;
  63. @Autowired
  64. private IMediaService mediaService;
  65. @Autowired
  66. private IMediaServerService mediaServerService;
  67. @Autowired
  68. private VideoStreamSessionManager streamSession;
  69. @Autowired
  70. private UserSetting userSetting;
  71. @Override
  72. public PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId,
  73. ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
  74. Runnable timeoutCallback) {
  75. PlayResult playResult = new PlayResult();
  76. RequestMessage msg = new RequestMessage();
  77. String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
  78. msg.setKey(key);
  79. String uuid = UUID.randomUUID().toString();
  80. msg.setId(uuid);
  81. playResult.setUuid(uuid);
  82. DeferredResult<ResponseEntity<String>> result = new DeferredResult<>(userSetting.getPlayTimeout());
  83. playResult.setResult(result);
  84. // 录像查询以channelId作为deviceId查询
  85. resultHolder.put(key, uuid, result);
  86. if (mediaServerItem == null) {
  87. WVPResult wvpResult = new WVPResult();
  88. wvpResult.setCode(-1);
  89. wvpResult.setMsg("未找到可用的zlm");
  90. msg.setData(wvpResult);
  91. resultHolder.invokeResult(msg);
  92. return playResult;
  93. }
  94. Device device = redisCatchStorage.getDevice(deviceId);
  95. StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
  96. playResult.setDevice(device);
  97. result.onCompletion(()->{
  98. // 点播结束时调用截图接口
  99. // TODO 应该在上流时调用更好,结束也可能是错误结束
  100. try {
  101. String classPath = ResourceUtils.getURL("classpath:").getPath();
  102. // 兼容打包为jar的class路径
  103. if(classPath.contains("jar")) {
  104. classPath = classPath.substring(0, classPath.lastIndexOf("."));
  105. classPath = classPath.substring(0, classPath.lastIndexOf("/") + 1);
  106. }
  107. if (classPath.startsWith("file:")) {
  108. classPath = classPath.substring(classPath.indexOf(":") + 1);
  109. }
  110. String path = classPath + "static/static/snap/";
  111. // 兼容Windows系统路径(去除前面的“/”)
  112. if(System.getProperty("os.name").contains("indows")) {
  113. path = path.substring(1);
  114. }
  115. String fileName = deviceId + "_" + channelId + ".jpg";
  116. ResponseEntity responseEntity = (ResponseEntity)result.getResult();
  117. if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
  118. WVPResult wvpResult = (WVPResult)responseEntity.getBody();
  119. if (Objects.requireNonNull(wvpResult).getCode() == 0) {
  120. StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData();
  121. MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId());
  122. String streamUrl = streamInfoForSuccess.getFmp4();
  123. // 请求截图
  124. logger.info("[请求截图]: " + fileName);
  125. zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName);
  126. }
  127. }
  128. } catch (FileNotFoundException e) {
  129. e.printStackTrace();
  130. }
  131. });
  132. if (streamInfo != null) {
  133. String streamId = streamInfo.getStream();
  134. if (streamId == null) {
  135. WVPResult wvpResult = new WVPResult();
  136. wvpResult.setCode(-1);
  137. wvpResult.setMsg("点播失败, redis缓存streamId等于null");
  138. msg.setData(wvpResult);
  139. resultHolder.invokeAllResult(msg);
  140. return playResult;
  141. }
  142. String mediaServerId = streamInfo.getMediaServerId();
  143. MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
  144. JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId);
  145. if (rtpInfo != null && rtpInfo.getBoolean("exist")) {
  146. WVPResult wvpResult = new WVPResult();
  147. wvpResult.setCode(0);
  148. wvpResult.setMsg("success");
  149. wvpResult.setData(streamInfo);
  150. msg.setData(wvpResult);
  151. resultHolder.invokeAllResult(msg);
  152. if (hookEvent != null) {
  153. hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo)));
  154. }
  155. }else {
  156. redisCatchStorage.stopPlay(streamInfo);
  157. storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
  158. streamInfo = null;
  159. }
  160. }
  161. if (streamInfo == null) {
  162. String streamId = null;
  163. if (mediaServerItem.isRtpEnable()) {
  164. streamId = String.format("%s_%s", device.getDeviceId(), channelId);
  165. }
  166. SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck());
  167. play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response)->{
  168. if (hookEvent != null) {
  169. hookEvent.response(mediaServerItem, response);
  170. }
  171. }, event -> {
  172. // sip error错误
  173. WVPResult wvpResult = new WVPResult();
  174. wvpResult.setCode(-1);
  175. wvpResult.setMsg(String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg));
  176. msg.setData(wvpResult);
  177. resultHolder.invokeAllResult(msg);
  178. if (errorEvent != null) {
  179. errorEvent.response(event);
  180. }
  181. }, (code, msgStr)->{
  182. // invite点播超时
  183. WVPResult wvpResult = new WVPResult();
  184. wvpResult.setCode(-1);
  185. if (code == 0) {
  186. wvpResult.setMsg("点播超时,请稍候重试");
  187. }else if (code == 1) {
  188. wvpResult.setMsg("收流超时,请稍候重试");
  189. }
  190. msg.setData(wvpResult);
  191. // 回复之前所有的点播请求
  192. resultHolder.invokeAllResult(msg);
  193. }, uuid);
  194. }
  195. return playResult;
  196. }
  197. @Override
  198. public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
  199. ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent,
  200. InviteTimeOutCallback timeoutCallback, String uuid) {
  201. String streamId = null;
  202. if (mediaServerItem.isRtpEnable()) {
  203. streamId = String.format("%s_%s", device.getDeviceId(), channelId);
  204. }
  205. if (ssrcInfo == null) {
  206. ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck());
  207. }
  208. // 超时处理
  209. Timer timer = new Timer();
  210. SSRCInfo finalSsrcInfo = ssrcInfo;
  211. timer.schedule(new TimerTask() {
  212. @Override
  213. public void run() {
  214. logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", device.getDeviceId(), channelId));
  215. SIPDialog dialog = streamSession.getDialogByStream(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
  216. if (dialog != null) {
  217. timeoutCallback.run(1, "收流超时");
  218. // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
  219. cmder.streamByeCmd(device.getDeviceId(), channelId, finalSsrcInfo.getStream(), null);
  220. }else {
  221. timeoutCallback.run(0, "点播超时");
  222. mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
  223. mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
  224. streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
  225. }
  226. }
  227. }, userSetting.getPlayTimeout());
  228. cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
  229. logger.info("收到订阅消息: " + response.toJSONString());
  230. timer.cancel();
  231. // hook响应
  232. onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId, uuid);
  233. hookEvent.response(mediaServerItemInuse, response);
  234. }, (event) -> {
  235. timer.cancel();
  236. mediaServerService.closeRTPServer(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
  237. // 释放ssrc
  238. mediaServerService.releaseSsrc(mediaServerItem.getId(), finalSsrcInfo.getSsrc());
  239. streamSession.remove(device.getDeviceId(), channelId, finalSsrcInfo.getStream());
  240. errorEvent.response(event);
  241. });
  242. }
  243. @Override
  244. public void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId, String uuid) {
  245. RequestMessage msg = new RequestMessage();
  246. if (uuid != null) {
  247. msg.setId(uuid);
  248. }
  249. msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId);
  250. StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId);
  251. if (streamInfo != null) {
  252. DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId);
  253. if (deviceChannel != null) {
  254. deviceChannel.setStreamId(streamInfo.getStream());
  255. storager.startPlay(deviceId, channelId, streamInfo.getStream());
  256. }
  257. redisCatchStorage.startPlay(streamInfo);
  258. WVPResult wvpResult = new WVPResult();
  259. wvpResult.setCode(0);
  260. wvpResult.setMsg("success");
  261. wvpResult.setData(streamInfo);
  262. msg.setData(wvpResult);
  263. resultHolder.invokeAllResult(msg);
  264. } else {
  265. logger.warn("设备预览API调用失败!");
  266. msg.setData("设备预览API调用失败!");
  267. resultHolder.invokeAllResult(msg);
  268. }
  269. }
  270. @Override
  271. public MediaServerItem getNewMediaServerItem(Device device) {
  272. if (device == null) return null;
  273. String mediaServerId = device.getMediaServerId();
  274. MediaServerItem mediaServerItem;
  275. if (mediaServerId == null) {
  276. mediaServerItem = mediaServerService.getMediaServerForMinimumLoad();
  277. }else {
  278. mediaServerItem = mediaServerService.getOne(mediaServerId);
  279. }
  280. if (mediaServerItem == null) {
  281. logger.warn("点播时未找到可使用的ZLM...");
  282. }
  283. return mediaServerItem;
  284. }
  285. @Override
  286. public DeferredResult<ResponseEntity<String>> playBack(String deviceId, String channelId, String startTime,
  287. String endTime,InviteStreamCallback inviteStreamCallback,
  288. PlayBackCallback callback) {
  289. Device device = storager.queryVideoDevice(deviceId);
  290. if (device == null) return null;
  291. MediaServerItem newMediaServerItem = getNewMediaServerItem(device);
  292. SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true);
  293. return playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, inviteStreamCallback, callback);
  294. }
  295. @Override
  296. public DeferredResult<ResponseEntity<String>> playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,
  297. String deviceId, String channelId, String startTime,
  298. String endTime, InviteStreamCallback infoCallBack,
  299. PlayBackCallback playBackCallback) {
  300. if (mediaServerItem == null || ssrcInfo == null) return null;
  301. String uuid = UUID.randomUUID().toString();
  302. String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId;
  303. DeferredResult<ResponseEntity<String>> result = new DeferredResult<>(30000L);
  304. Device device = storager.queryVideoDevice(deviceId);
  305. if (device == null) {
  306. result.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
  307. return result;
  308. }
  309. resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId, uuid, result);
  310. RequestMessage msg = new RequestMessage();
  311. msg.setId(uuid);
  312. msg.setKey(key);
  313. PlayBackResult<RequestMessage> playBackResult = new PlayBackResult<>();
  314. Timer timer = new Timer();
  315. timer.schedule(new TimerTask() {
  316. @Override
  317. public void run() {
  318. logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId));
  319. playBackResult.setCode(-1);
  320. playBackResult.setData(msg);
  321. playBackCallback.call(playBackResult);
  322. SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, ssrcInfo.getStream());
  323. // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
  324. if (dialog != null) {
  325. // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
  326. cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
  327. }else {
  328. mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  329. mediaServerService.closeRTPServer(deviceId, channelId, ssrcInfo.getStream());
  330. streamSession.remove(deviceId, channelId, ssrcInfo.getStream());
  331. }
  332. cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
  333. // 回复之前所有的点播请求
  334. playBackCallback.call(playBackResult);
  335. }
  336. }, userSetting.getPlayTimeout());
  337. cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack,
  338. (InviteStreamInfo inviteStreamInfo) -> {
  339. logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
  340. timer.cancel();
  341. StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
  342. if (streamInfo == null) {
  343. logger.warn("设备回放API调用失败!");
  344. msg.setData("设备回放API调用失败!");
  345. playBackResult.setCode(-1);
  346. playBackResult.setData(msg);
  347. playBackCallback.call(playBackResult);
  348. return;
  349. }
  350. redisCatchStorage.startPlayback(streamInfo, inviteStreamInfo.getCallId());
  351. msg.setData(JSON.toJSONString(streamInfo));
  352. playBackResult.setCode(0);
  353. playBackResult.setData(msg);
  354. playBackResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
  355. playBackResult.setResponse(inviteStreamInfo.getResponse());
  356. playBackCallback.call(playBackResult);
  357. }, event -> {
  358. timer.cancel();
  359. msg.setData(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg));
  360. playBackResult.setCode(-1);
  361. playBackResult.setData(msg);
  362. playBackResult.setEvent(event);
  363. playBackCallback.call(playBackResult);
  364. streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  365. });
  366. return result;
  367. }
  368. @Override
  369. public DeferredResult<ResponseEntity<String>> download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack) {
  370. Device device = storager.queryVideoDevice(deviceId);
  371. if (device == null) return null;
  372. MediaServerItem newMediaServerItem = getNewMediaServerItem(device);
  373. SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true);
  374. return download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed,infoCallBack, hookCallBack);
  375. }
  376. @Override
  377. public DeferredResult<ResponseEntity<String>> download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack) {
  378. if (mediaServerItem == null || ssrcInfo == null) return null;
  379. String uuid = UUID.randomUUID().toString();
  380. String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId;
  381. DeferredResult<ResponseEntity<String>> result = new DeferredResult<>(30000L);
  382. Device device = storager.queryVideoDevice(deviceId);
  383. if (device == null) {
  384. result.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
  385. return result;
  386. }
  387. resultHolder.put(key, uuid, result);
  388. RequestMessage msg = new RequestMessage();
  389. msg.setId(uuid);
  390. msg.setKey(key);
  391. WVPResult<StreamInfo> wvpResult = new WVPResult<>();
  392. msg.setData(wvpResult);
  393. PlayBackResult<RequestMessage> downloadResult = new PlayBackResult<>();
  394. downloadResult.setData(msg);
  395. Timer timer = new Timer();
  396. timer.schedule(new TimerTask() {
  397. @Override
  398. public void run() {
  399. logger.warn(String.format("录像下载请求超时,deviceId:%s ,channelId:%s", deviceId, channelId));
  400. wvpResult.setCode(-1);
  401. wvpResult.setMsg("录像下载请求超时");
  402. downloadResult.setCode(-1);
  403. hookCallBack.call(downloadResult);
  404. SIPDialog dialog = streamSession.getDialogByStream(deviceId, channelId, ssrcInfo.getStream());
  405. // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
  406. if (dialog != null) {
  407. // 点播超时回复BYE 同时释放ssrc以及此次点播的资源
  408. cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
  409. }else {
  410. mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  411. mediaServerService.closeRTPServer(deviceId, channelId, ssrcInfo.getStream());
  412. streamSession.remove(deviceId, channelId, ssrcInfo.getStream());
  413. }
  414. cmder.streamByeCmd(device.getDeviceId(), channelId, ssrcInfo.getStream(), null);
  415. // 回复之前所有的点播请求
  416. hookCallBack.call(downloadResult);
  417. }
  418. }, userSetting.getPlayTimeout());
  419. cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
  420. inviteStreamInfo -> {
  421. logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
  422. timer.cancel();
  423. StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
  424. streamInfo.setStartTime(startTime);
  425. streamInfo.setEndTime(endTime);
  426. if (streamInfo == null) {
  427. logger.warn("录像下载API调用失败!");
  428. wvpResult.setCode(-1);
  429. wvpResult.setMsg("录像下载API调用失败");
  430. downloadResult.setCode(-1);
  431. hookCallBack.call(downloadResult);
  432. return ;
  433. }
  434. redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
  435. wvpResult.setCode(0);
  436. wvpResult.setMsg("success");
  437. wvpResult.setData(streamInfo);
  438. downloadResult.setCode(0);
  439. downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
  440. downloadResult.setResponse(inviteStreamInfo.getResponse());
  441. hookCallBack.call(downloadResult);
  442. }, event -> {
  443. timer.cancel();
  444. downloadResult.setCode(-1);
  445. wvpResult.setCode(-1);
  446. wvpResult.setMsg(String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg));
  447. downloadResult.setEvent(event);
  448. hookCallBack.call(downloadResult);
  449. streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  450. });
  451. return result;
  452. }
  453. @Override
  454. public StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream) {
  455. StreamInfo streamInfo = redisCatchStorage.queryDownload(deviceId, channelId, stream, null);
  456. if (streamInfo != null) {
  457. if (streamInfo.getProgress() == 1) {
  458. return streamInfo;
  459. }
  460. // 获取当前已下载时长
  461. String mediaServerId = streamInfo.getMediaServerId();
  462. MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
  463. if (mediaServerItem == null) {
  464. logger.warn("查询录像信息时发现节点已离线");
  465. return null;
  466. }
  467. if (mediaServerItem.getRecordAssistPort() != 0) {
  468. JSONObject jsonObject = assistRESTfulUtils.fileDuration(mediaServerItem, streamInfo.getApp(), streamInfo.getStream(), null);
  469. if (jsonObject != null && jsonObject.getInteger("code") == 0) {
  470. long duration = jsonObject.getLong("data");
  471. if (duration == 0) {
  472. streamInfo.setProgress(0);
  473. }else {
  474. String startTime = streamInfo.getStartTime();
  475. String endTime = streamInfo.getEndTime();
  476. long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime);
  477. long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime);
  478. BigDecimal currentCount = new BigDecimal(duration/1000);
  479. BigDecimal totalCount = new BigDecimal(end-start);
  480. BigDecimal divide = currentCount.divide(totalCount,2, BigDecimal.ROUND_HALF_UP);
  481. double process = divide.doubleValue();
  482. streamInfo.setProgress(process);
  483. }
  484. }
  485. }
  486. }
  487. return streamInfo;
  488. }
  489. @Override
  490. public void onPublishHandlerForDownload(InviteStreamInfo inviteStreamInfo, String deviceId, String channelId, String uuid) {
  491. RequestMessage msg = new RequestMessage();
  492. msg.setKey(DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId);
  493. msg.setId(uuid);
  494. StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
  495. if (streamInfo != null) {
  496. redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
  497. msg.setData(JSON.toJSONString(streamInfo));
  498. resultHolder.invokeResult(msg);
  499. } else {
  500. logger.warn("设备预览API调用失败!");
  501. msg.setData("设备预览API调用失败!");
  502. resultHolder.invokeResult(msg);
  503. }
  504. }
  505. public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId) {
  506. String streamId = resonse.getString("stream");
  507. JSONArray tracks = resonse.getJSONArray("tracks");
  508. StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem,"rtp", streamId, tracks);
  509. streamInfo.setDeviceID(deviceId);
  510. streamInfo.setChannelId(channelId);
  511. return streamInfo;
  512. }
  513. @Override
  514. public void zlmServerOffline(String mediaServerId) {
  515. // 处理正在向上推流的上级平台
  516. List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServer(null);
  517. if (sendRtpItems.size() > 0) {
  518. for (SendRtpItem sendRtpItem : sendRtpItems) {
  519. if (sendRtpItem.getMediaServerId().equals(mediaServerId)) {
  520. ParentPlatform platform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
  521. sipCommanderFroPlatform.streamByeCmd(platform, sendRtpItem.getCallId());
  522. }
  523. }
  524. }
  525. // 处理正在观看的国标设备
  526. List<SsrcTransaction> allSsrc = streamSession.getAllSsrc();
  527. if (allSsrc.size() > 0) {
  528. for (SsrcTransaction ssrcTransaction : allSsrc) {
  529. if(ssrcTransaction.getMediaServerId().equals(mediaServerId)) {
  530. cmder.streamByeCmd(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(),
  531. ssrcTransaction.getStream(), null);
  532. }
  533. }
  534. }
  535. }
  536. }