CameraUtil.java 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. package com.ozs.utils;
  2. import com.alibaba.fastjson2.JSON;
  3. import com.alibaba.fastjson2.JSONArray;
  4. import com.alibaba.fastjson2.JSONObject;
  5. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  6. import com.hikvision.artemis.sdk.ArtemisHttpUtil;
  7. import com.hikvision.artemis.sdk.config.ArtemisConfig;
  8. import com.ozs.common.config.BaseConfig;
  9. import com.ozs.common.constant.Constants;
  10. import com.ozs.common.core.domain.AjaxResult;
  11. import com.ozs.common.core.domain.entity.SysDictData;
  12. import com.ozs.common.core.redis.RedisCache;
  13. import com.ozs.common.exception.base.BaseException;
  14. import com.ozs.common.utils.DateUtils;
  15. import com.ozs.common.utils.HttpClientUtil;
  16. import com.ozs.common.utils.http.HttpUtils;
  17. import com.ozs.entity.BaseTerminal;
  18. import com.ozs.entity.MsgHeartbeatAlarmMessage;
  19. import com.ozs.entity.vo.CamerasVo;
  20. import com.ozs.entity.vo.PlaybackVo;
  21. import com.ozs.framework.config.ServerConfig;
  22. import com.ozs.service.BaseTerminalService;
  23. import com.ozs.service.MsgHeartbeatAlarmMessageService;
  24. import com.ozs.system.mapper.SysDictDataMapper;
  25. import com.ozs.system.service.ISysDictTypeService;
  26. import lombok.SneakyThrows;
  27. import lombok.extern.slf4j.Slf4j;
  28. import org.apache.commons.lang3.StringUtils;
  29. import org.springframework.beans.factory.annotation.Autowired;
  30. import org.springframework.context.annotation.Configuration;
  31. import org.springframework.util.ObjectUtils;
  32. import javax.annotation.PostConstruct;
  33. import java.io.BufferedReader;
  34. import java.io.BufferedWriter;
  35. import java.io.File;
  36. import java.io.FileFilter;
  37. import java.io.IOException;
  38. import java.io.InputStreamReader;
  39. import java.io.OutputStreamWriter;
  40. import java.io.PrintWriter;
  41. import java.text.ParseException;
  42. import java.text.SimpleDateFormat;
  43. import java.time.LocalDateTime;
  44. import java.time.OffsetDateTime;
  45. import java.time.format.DateTimeFormatter;
  46. import java.util.ArrayList;
  47. import java.util.Calendar;
  48. import java.util.Date;
  49. import java.util.GregorianCalendar;
  50. import java.util.HashMap;
  51. import java.util.List;
  52. import java.util.Map;
  53. import java.util.Random;
  54. import java.util.Set;
  55. import java.util.UUID;
  56. import java.util.concurrent.Callable;
  57. import java.util.concurrent.CompletableFuture;
  58. import java.util.concurrent.ExecutionException;
  59. import java.util.concurrent.ExecutorService;
  60. import java.util.concurrent.Executors;
  61. import java.util.concurrent.TimeUnit;
  62. import java.util.concurrent.TimeoutException;
  63. import java.util.stream.Collectors;
  64. /**
  65. * 相机工具
  66. */
  67. @Configuration
  68. @Slf4j
  69. public class CameraUtil {
  70. private static final ExecutorService executor = Executors.newFixedThreadPool(20);
  71. private static String historyUrl;
  72. private static String ffmpegPath;
  73. private static String filePath;
  74. private static String transcribeFilePath;
  75. private static String webUrl;
  76. private static String bakUrl;
  77. private static String bakUrlRtsp;
  78. private static String wsUrl;
  79. private static CmdCameraUtil cUtil;
  80. private static RedisCache rc;
  81. private static ServerConfig sc;
  82. private static String recordUrl;
  83. private static String hkUrl;
  84. private static String host;
  85. private static String appKey;
  86. private static String appSecret;
  87. private static String rtmpUrl;
  88. @Autowired
  89. private CaneraConfig caneraConfig;
  90. @Autowired
  91. private CmdCameraUtil cmdCameraUtil;
  92. @Autowired
  93. private SysDictDataMapper dictDataMapper;
  94. @Autowired
  95. private RedisCache redisCache;
  96. @Autowired
  97. private ServerConfig serverConfig;
  98. @Autowired
  99. private BaseTerminalService baseTerminalService;
  100. @Autowired
  101. private MsgHeartbeatAlarmMessageService msgHeartbeatAlarmMessageService;
  102. @Autowired
  103. private ISysDictTypeService dictTypeService;
  104. @Autowired
  105. private RtspToMP4 rtspToMP4;
  106. private Map<String, Process> map = new HashMap<>();
  107. public final static String tsFilekey = "mergeVideoTsFile";
  108. /**
  109. * 历史回放
  110. *
  111. * @param fromVideoFileList
  112. * @param ph
  113. * @return
  114. * @throws IOException
  115. */
  116. public static String historyPlay(List<String> fromVideoFileList, String ph, boolean flay) {
  117. // 视频服务映射路径
  118. String NewfilePath = BaseConfig.getProfile() + "/" + ph;
  119. log.info("NewfilePath:{}", NewfilePath);
  120. log.info("fromVideoFileList:{}", fromVideoFileList);
  121. if (ObjectUtils.isEmpty(fromVideoFileList) || fromVideoFileList.size() <= 0) {
  122. throw new BaseException("当前相机无视频录像");
  123. }
  124. executor.submit(new Runnable() {
  125. @Override
  126. public void run() {
  127. try {
  128. txConvetor(fromVideoFileList, NewfilePath, flay);
  129. } catch (IOException e) {
  130. log.error(e.getMessage());
  131. e.printStackTrace();
  132. }
  133. }
  134. });
  135. // executor.shutdown();
  136. return Constants.RESOURCE_PREFIX + "/" + ph;
  137. }
  138. @SneakyThrows
  139. public static void txConvetor(List<String> fromVideoFileList,
  140. String newfilePath,
  141. boolean fly) throws IOException {
  142. /*
  143. * ffmpeg -i 20230411_155847_155947-d4c2265d-d83e-11ed-8e7f-fa163e4e1e9f.flv -c:v copy 1.ts
  144. ffmpeg -i 20230411_155948_160048-f91fea03-d83e-11ed-8e7f-fa163e4e1e9f.flv -c:v copy 2.ts
  145. ffmpeg -i "concat:1.ts|2.ts" -c copy output.mp4
  146. * */
  147. File file = new File(newfilePath);
  148. boolean flay = false;
  149. if (!file.getParentFile().exists()) {
  150. boolean mkdirs = file.getParentFile().mkdirs();
  151. log.info("创建文件夹:{}", file.getParentFile().getPath());
  152. log.info("创建文件夹结果:{}", mkdirs);
  153. flay = true;
  154. }
  155. cUtil.cmd("chomd -R 777 " + file.getParentFile().getPath());
  156. log.info("newfilePath:{}", newfilePath);
  157. StringBuffer sm = new StringBuffer(ffmpegPath + " -i \"concat:");
  158. List<String> fileTs = new ArrayList<>();
  159. for (int t = 0; t < fromVideoFileList.size(); t++) {
  160. File ft = new File(fromVideoFileList.get(t));
  161. if (ft.exists()) {
  162. log.info("file:{}", fromVideoFileList.get(t));
  163. String substring = fromVideoFileList.get(t).substring(0, fromVideoFileList.get(t).lastIndexOf("."));
  164. int x;//定义两变量
  165. Random ne = new Random();//实例化一个random的对象ne
  166. x = ne.nextInt(9999 - 1000 + 1) + 1000;//为变量赋随机值1000-9999
  167. substring = substring + x;//定义两变量
  168. String cmdstr = ffmpegPath + " -i " + fromVideoFileList.get(t) + " -c:v copy " + substring + ".ts";
  169. log.info("转换命令:{}", cmdstr);
  170. cUtil.cmd(cmdstr);
  171. fileTs.add(substring + ".ts");
  172. if (t != fromVideoFileList.size() - 1) {
  173. sm.append(substring + ".ts|");
  174. } else {
  175. sm.append(substring + ".ts\" ");
  176. }
  177. }
  178. }
  179. if (fileTs.size() > 0) {
  180. Map<String, Object> mergeVideoTsFile = rc.getCacheMap(tsFilekey);
  181. if (ObjectUtils.isEmpty(mergeVideoTsFile)) {
  182. mergeVideoTsFile = new HashMap<>();
  183. }
  184. if (!flay) {
  185. // 如果没有解除,把生成的文件放入要删除的定时任务 redis key 中
  186. fileTs.add(newfilePath);
  187. }
  188. mergeVideoTsFile.put(System.currentTimeMillis() + "", fileTs);
  189. rc.deleteObject(tsFilekey);
  190. if (mergeVideoTsFile.size() > 0) {
  191. rc.setCacheMap(tsFilekey, mergeVideoTsFile);
  192. }
  193. sm.append("-c copy " + newfilePath);
  194. log.info("合并命令:{}", sm.toString());
  195. cUtil.cmd(sm.toString());
  196. }
  197. }
  198. /**
  199. * 实时播放的拼接流
  200. *
  201. * @param cameraCode 相机编码
  202. * @param channel 相机通道
  203. * @return
  204. */
  205. public static String getPlayFlv(String cameraCode, String channel, boolean flay) {
  206. if (!flay) {
  207. return bakUrl + "/hdl/" + channel + "/" + cameraCode + ".flv";
  208. }
  209. return webUrl + "/hdl/" + channel + "/" + cameraCode + ".flv";
  210. // if (!flay) {
  211. // return bakUrl + "/ws/" + channel + "/" + cameraCode + ".flv";
  212. // }
  213. // return webUrl + "/ws/" + channel + "/" + cameraCode + ".flv";
  214. }
  215. /**
  216. * Rtsp实时播放的拼接流
  217. *
  218. * @param cameraCode 相机编码
  219. * @param channel 相机通道
  220. * @return
  221. */
  222. public static String getPlayFlvRtsp(String cameraCode, String channel, boolean flay) {
  223. if (!flay) {
  224. return bakUrlRtsp + "/" + channel + "/" + cameraCode;
  225. }
  226. return bakUrlRtsp + "/" + channel + "/" + cameraCode;
  227. // if (!flay) {
  228. // return bakUrl + "/ws/" + channel + "/" + cameraCode + ".flv";
  229. // }
  230. // return webUrl + "/ws/" + channel + "/" + cameraCode + ".flv";
  231. }
  232. /**
  233. * web页面实时流接口
  234. * @param cameraCode
  235. * @param channel
  236. * @return
  237. */
  238. public static String getPlayFlv(String cameraCode, String channel) {
  239. List<String> pathList = new ArrayList<>();
  240. String data = HttpUtils.sendGet(webUrl + "/rtsp/api/list");
  241. if (StringUtils.isNotEmpty(data)) {
  242. JSONArray jsonArray = JSONArray.parseArray(data);
  243. for (int i = 0; i < jsonArray.size(); i++) {
  244. JSONObject jsonObject = jsonArray.getJSONObject(i);
  245. String name = jsonObject.getString("Path");
  246. pathList.add(name);
  247. }
  248. boolean contains = pathList.contains(cameraCode + "/" + channel);
  249. if (contains) {
  250. return getPlayFlv(cameraCode, channel, true);
  251. } else {
  252. /**
  253. * jsonBody.put("cameraIndexCode", "01ea43e6676f4e47bd6c5cd9e02aa006");
  254. * jsonBody.put("streamType", 0);
  255. * jsonBody.put("protocol","rtsp");
  256. * jsonBody.put("transmode", 1);
  257. * jsonBody.put("expand","streamform=rtp");
  258. */
  259. CamerasVo camerasVo = new CamerasVo();
  260. camerasVo.setCameraIndexCode(cameraCode);
  261. camerasVo.setStreamType(0);
  262. camerasVo.setTransmode(1);
  263. camerasVo.setProtocol("rtsp");
  264. camerasVo.setExpand("streamform=rtp");
  265. previewURLs(camerasVo, channel);
  266. return getPlayFlv(cameraCode, channel, true);
  267. }
  268. } else {
  269. return "流媒体目前没有RTSP协议的流";
  270. }
  271. // return getPlayFlv(cameraCode, channel, true);
  272. }
  273. public static String invite(String cameraCode, String channel) {
  274. String result = null;
  275. String url = bakUrl + "/api/gb28181/invite?id=" + cameraCode + "&channel=" + channel;
  276. try {
  277. result = HttpClientUtil.get(url);
  278. log.info("result:{}", result);
  279. } catch (Exception e) {
  280. log.info(e.getMessage());
  281. e.printStackTrace();
  282. }
  283. return result;
  284. }
  285. /**
  286. * 开启录制功能
  287. *
  288. * @param cameraCode 相机编码
  289. * @param channel 相机通道
  290. * @return
  291. */
  292. public static String startRecording(String cameraCode, String channel) {
  293. return historyUrl + "/recordpro/api/start?streamPath=" + channel + "/" + cameraCode;
  294. }
  295. /**
  296. * 关闭录制功能
  297. *
  298. * @param taskId 录像接口返回的任务ID
  299. * @return
  300. */
  301. public static String endRecording(String taskId) {
  302. return historyUrl + "/recordpro/api/stop?id=" + taskId;
  303. }
  304. /**
  305. * 历史回放流(获取)
  306. *
  307. * @param channel 相机通道
  308. * @param startTm 开始时间
  309. * @param endTm 结束时间
  310. * @return
  311. */
  312. public static String historyPlayListStr(String channel, Date startTm, Date endTm, boolean flay) {
  313. List<String> list = filterPlayList(channel, startTm, endTm, filePath);
  314. String uuid = UUID.randomUUID().toString();
  315. String ph = "record/flv/" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, new Date())
  316. + "/" + channel + "/"
  317. + uuid + ".mp4";
  318. if (!ObjectUtils.isEmpty(list)) {
  319. try {
  320. return historyPlay(list, ph, flay);
  321. } catch (Exception e) {
  322. log.error(e.getMessage());
  323. e.printStackTrace();
  324. }
  325. } else {
  326. throw new BaseException("当前相机无视频录像");
  327. }
  328. return null;
  329. }
  330. /**
  331. * 过滤符合条件的视频
  332. *
  333. * @param channel
  334. * @param startTm
  335. * @param endTm
  336. * @param mappingUrl
  337. * @return
  338. */
  339. public static List<String> filterPlayList(String channel, Date startTm, Date endTm, String mappingUrl) {
  340. if (StringUtils.isBlank(channel)
  341. || ObjectUtils.isEmpty(startTm)
  342. || ObjectUtils.isEmpty(endTm)) {
  343. return null;
  344. }
  345. List<String> ls = new ArrayList<>();
  346. Map<Date, String> m = new HashMap<>();
  347. // 调用视频服务返回参数
  348. String startTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, startTm);
  349. String endTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, endTm);
  350. String param = "channel=" + channel + "&startTime=" + startTime + "&endTime=" + endTime;
  351. String s = HttpUtils.sendGet(historyUrl + "/api/record/flv/list", param);
  352. // 视频拼接
  353. if (!StringUtils.isBlank(s) && !"null".equals(s) && !s.startsWith("<!DOCTYPE html>")) {
  354. List<Map<String, Object>> maps = JSON.parseArray(s, Map.class);
  355. if (ObjectUtils.isEmpty(maps)) {
  356. return null;
  357. }
  358. for (Map<String, Object> map : maps) {
  359. Object path = map.get("Path");
  360. if (!ObjectUtils.isEmpty(path)) {
  361. String s1 = path.toString();
  362. String substring = s1.substring(s1.lastIndexOf("/") + 1, s1.length());
  363. String substring1 = substring.substring(0, substring.indexOf("-"));
  364. String[] s2 = substring1.split("_");
  365. if (!ObjectUtils.isEmpty(s2)) {
  366. String s3 = s2[0] + s2[1];
  367. String s4 = s2[0] + s2[2];
  368. Date sdate = DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS, s3);
  369. if (s2[1].startsWith("23") && s2[2].startsWith("00")) {
  370. sdate = DateUtils.addDays(sdate, -1);
  371. }
  372. Date edate = DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS, s4);
  373. /* sdate |startTm| edate |endTm| */
  374. if (startTm.compareTo(sdate) >= 0
  375. && startTm.compareTo(edate) <= 0
  376. && endTm.compareTo(edate) >= 0) {
  377. m.put(sdate, mappingUrl + path.toString());
  378. /* |startTm| sdate edate |endTm| */
  379. } else if (startTm.compareTo(sdate) <= 0 && endTm.compareTo(edate) >= 0) {
  380. m.put(sdate, mappingUrl + path.toString());
  381. /* |startTm| sdate |endTm| edate */
  382. } else if (startTm.compareTo(sdate) <= 0
  383. && endTm.compareTo(sdate) >= 0
  384. && endTm.compareTo(edate) <= 0) {
  385. m.put(sdate, mappingUrl + path.toString());
  386. /* sdate |startTm| |endTm| edate */
  387. } else if (startTm.compareTo(sdate) >= 0 && endTm.compareTo(edate) <= 0) {
  388. m.put(sdate, mappingUrl + path.toString());
  389. }
  390. }
  391. }
  392. }
  393. }
  394. if (!ObjectUtils.isEmpty(m) && m.size() > 0) {
  395. Set<Date> dates = m.keySet();
  396. // 排序
  397. dates.stream().parallel().collect(Collectors.toList()).stream().sorted().forEach(d -> {
  398. ls.add(m.get(d));
  399. });
  400. return ls;
  401. }
  402. return null;
  403. }
  404. /**
  405. * 合并视频(转化文件)
  406. *
  407. * @param fromVideoFileList 视频路径
  408. * @param newfilePath 生产新的视频文件路径
  409. * @throws IOException
  410. */
  411. public static Map<String, String> myConvetor(List<String> fromVideoFileList,
  412. String newfilePath,
  413. String uuid) throws IOException {
  414. /*
  415. * for f in *.flv; do echo "file '$f'" >> mylist.txt; done
  416. ffmpeg -f concat -i mylist.txt -c copy output.flv
  417. * */
  418. File file = new File(newfilePath);
  419. boolean flay = false;
  420. if (!file.getParentFile().exists()) {
  421. file.getParentFile().mkdirs();
  422. log.info("创建文件夹:{}", file.getParentFile().getPath());
  423. flay = true;
  424. }
  425. log.info("newfilePath:{}", newfilePath);
  426. StringBuffer sm = new StringBuffer("for f in ");
  427. String pathStr = null;
  428. for (int t = 0; t < fromVideoFileList.size(); t++) {
  429. File ft = new File(fromVideoFileList.get(t));
  430. if (ft.exists()) {
  431. if (t != fromVideoFileList.size() - 1) {
  432. // sm.append(fromVideoFileList.get(t) + " ");
  433. sm.append(fromVideoFileList.get(t).substring(fromVideoFileList.get(t).lastIndexOf("/") + 1, fromVideoFileList.get(t).length()) + " ");
  434. } else {
  435. // sm.append(fromVideoFileList.get(t));
  436. sm.append(fromVideoFileList.get(t).substring(fromVideoFileList.get(t).lastIndexOf("/") + 1, fromVideoFileList.get(t).length()));
  437. pathStr = fromVideoFileList.get(t).substring(0, fromVideoFileList.get(t).lastIndexOf("/"));
  438. }
  439. }
  440. }
  441. // String substring = newfilePath.substring(0, newfilePath.lastIndexOf("."));
  442. // substring = substring + ".txt";
  443. String substring = uuid + ".txt";
  444. log.info("临时转化的文件:{}", substring);
  445. sm.append("; do echo \"file '$f'\" >> " + substring + "; done");
  446. String commit = sm.toString();
  447. log.info("合并转化文件的命令:{}", commit);
  448. Process proc = Runtime.getRuntime().exec("/bin/bash", null, null);
  449. BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
  450. PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);
  451. try {
  452. String commit1 = ffmpegPath + " -f concat -safe 0 -i " + pathStr + "/" + substring + " -c copy " + newfilePath;
  453. log.info("合并视频命令:{}", commit1);
  454. List<String> commands = new ArrayList<>();
  455. if (flay) {
  456. commands.add("chomd -R 777 " + file.getParentFile().getPath());
  457. }
  458. //
  459. commands.add("cd " + pathStr);
  460. // 合并成一个临时文件
  461. commands.add(commit);
  462. // 临时文件转化为 flv
  463. // commands.add(commit1);
  464. log.info("录像视频命令:{}", commands);
  465. for (String line : commands) {
  466. log.info("命令:{}", line);
  467. out.println(line);
  468. }
  469. out.println("exit");// 这个命令必须执行,否则in流不结束。
  470. log.info("命令:{}", "exit");
  471. String rspLine = "";
  472. while ((rspLine = in.readLine()) != null) {
  473. log.info("*****:{}", rspLine);
  474. }
  475. proc.waitFor();
  476. Map<String, String> map = new HashMap<>();
  477. map.put("cmd", commit1);
  478. map.put("path", pathStr + "/" + substring);
  479. return map;
  480. } catch (InterruptedException e) {
  481. log.error(e.getMessage());
  482. e.printStackTrace();
  483. } finally {
  484. in.close();
  485. out.close();
  486. proc.destroy();
  487. }
  488. return null;
  489. }
  490. @PostConstruct
  491. public void init() {
  492. historyUrl = caneraConfig.getHistoryUrl();
  493. ffmpegPath = caneraConfig.getFfmpegPath();
  494. filePath = caneraConfig.getFilePath();
  495. transcribeFilePath = caneraConfig.getTranscribeFilePath();
  496. webUrl = caneraConfig.getWebUrl();
  497. bakUrl = caneraConfig.getBakUrl();
  498. bakUrlRtsp = caneraConfig.getBakUrlRtsp();
  499. rc = redisCache;
  500. cUtil = cmdCameraUtil;
  501. sc = serverConfig;
  502. wsUrl = caneraConfig.getWsUrl();
  503. recordUrl = caneraConfig.getRecordUrl();
  504. hkUrl = caneraConfig.getHkUrl();
  505. host = caneraConfig.getHost();
  506. appKey = caneraConfig.getAppKey();
  507. appSecret = caneraConfig.getAppSecret();
  508. rtmpUrl=caneraConfig.getRtmpUrl();
  509. }
  510. /**
  511. * 定时任务参数flv文件
  512. *
  513. * @throws IOException
  514. * @throws InterruptedException
  515. */
  516. public void deleteFlv() throws IOException, InterruptedException {
  517. SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");
  518. Date date = new Date();
  519. Calendar calendar = new GregorianCalendar();
  520. calendar.setTime(date);
  521. calendar.add(Calendar.DATE, -1); //把日期往后增加一天,整数 往后推,负数往前移动
  522. date = calendar.getTime(); //这个时间就是日期往后推一天的结果
  523. String path = BaseConfig.getProfile() + "/flv/" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, date);
  524. File file = new File(path);
  525. if (file.exists()) {
  526. List<String> rspList = new ArrayList<String>();
  527. Process proc = Runtime.getRuntime().exec("/bin/bash", null, null);
  528. BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
  529. PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);
  530. String commit = " rm -rf " + path;
  531. List<String> commands = new ArrayList<>();
  532. // 删除
  533. commands.add(commit);
  534. log.info("删除昨天的录像视频命令:{}", commands);
  535. for (String line : commands) {
  536. out.println(line);
  537. }
  538. out.println("exit");// 这个命令必须执行,否则in流不结束。
  539. String rspLine = "";
  540. while ((rspLine = in.readLine()) != null) {
  541. System.out.println(rspLine);
  542. rspList.add(rspLine);
  543. }
  544. int i = proc.waitFor();
  545. log.info("执行结果:{}", i);
  546. in.close();
  547. out.close();
  548. proc.destroy();
  549. }
  550. }
  551. /**
  552. * 定时任务:删除超过配置时长的录制视频
  553. *
  554. * @throws IOException
  555. * @throws InterruptedException
  556. */
  557. public void deleteFlvExceed() throws IOException, InterruptedException, ParseException {
  558. //字典中设置的值
  559. List<SysDictData> sysCameraRecordTime = dictDataMapper.selectDictDataByType("sys_camera_record_time");
  560. SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
  561. Date date = new Date();
  562. Calendar calendar = new GregorianCalendar();
  563. calendar.setTime(date);
  564. //正数,日期天数加
  565. //负数,日期天数减 提前45天
  566. if (!ObjectUtils.isEmpty(sysCameraRecordTime)) {
  567. String dictValue = sysCameraRecordTime.get(0).getDictValue();
  568. calendar.add(Calendar.DAY_OF_YEAR, Integer.parseInt("-" + dictValue));
  569. } else {
  570. calendar.add(Calendar.DAY_OF_YEAR, -45);
  571. }
  572. date = calendar.getTime();
  573. //遍历每个进行视频录制的摄像头
  574. File file = new File(transcribeFilePath);
  575. log.info("file.Name()======================================" + file.getName());
  576. log.info("file.exists()======================================" + file.exists());
  577. if (file.exists() && file.isDirectory()) {
  578. //获取文件夹中所有的子文件夹和文件
  579. File[] files = file.listFiles();
  580. if (!ObjectUtils.isEmpty(files) && files.length > 0) {
  581. for (File file1 : files) {
  582. // file1=/opt/streams/record/flv/34020000001320000167
  583. log.info("file1.Name()======================================" + file1.getName());
  584. if (file1.exists() && file1.isDirectory()) {
  585. //相机文件
  586. File[] fs = file1.listFiles();
  587. if (!ObjectUtils.isEmpty(fs) && fs.length > 0) {
  588. for (File f : fs) {
  589. // f=/opt/streams/record/flv/34020000001320000167/20230328_192033_192037-91509173-cd5a-11ed-8a42-fa163e4e1e9f.flv
  590. //fName=20230328_192033_192037-91509173-cd5a-11ed-8a42-fa163e4e1e9f.flv
  591. String fName = f.getName();
  592. log.info("fName======================================" + fName);
  593. String[] split = fName.split("_");
  594. if (split.length > 0) {
  595. //20230328
  596. String s = split[0];
  597. Date parse = sdf.parse(s);
  598. long fTime = parse.getTime();
  599. long dateTime = date.getTime();
  600. //删除过期文件
  601. if (fTime < dateTime) {
  602. f.delete();
  603. }
  604. }
  605. }
  606. }
  607. }
  608. }
  609. }
  610. }
  611. }
  612. public static File[] getCurFilesList(String filePath) {
  613. File path = new File(filePath);
  614. File[] listFiles = path.listFiles(new FileFilter() {
  615. @Override
  616. public boolean accept(File pathname) {
  617. if (pathname.isFile()) {
  618. return true;
  619. } else {
  620. return false;
  621. }
  622. }
  623. });
  624. return listFiles;
  625. }
  626. public static void execute(String command) {
  627. try {
  628. ProcessBuilder process = new ProcessBuilder(command);
  629. process.inheritIO().start().waitFor();
  630. } catch (Exception e) {
  631. e.printStackTrace();
  632. }
  633. }
  634. /**
  635. * web页面视频回放接口
  636. * @param code
  637. * @param startTm
  638. * @param endTm
  639. * @return
  640. */
  641. public static String getRecordList(String code, Date startTm, Date endTm) {
  642. PlaybackVo playbackVo = new PlaybackVo();
  643. playbackVo.setCameraIndexCode(code);
  644. // 格式化时间
  645. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  646. playbackVo.setBeginTime(dateFormat.format(startTm));
  647. playbackVo.setEndTime(dateFormat.format(endTm));
  648. playbackVo.setProtocol("ws");
  649. //对象转换json字符串
  650. String body = JSONObject.toJSONString(playbackVo);
  651. //调用天网接口
  652. String playbackURLs = getPreviewURLs("/api/video/v1/cameras/playbackURLs", body);
  653. JSONObject outJson = (JSONObject) JSONObject.parse(playbackURLs);
  654. log.info("outJson-------->" + outJson);
  655. if ("0".equals(outJson.getString("code"))) {
  656. JSONObject data = outJson.getJSONObject("data");
  657. String urls = data.getString("url");
  658. return urls;
  659. } else {
  660. return "调用天网接口失败";
  661. }
  662. // return filterRecordList(channel, startTm, endTm, filePath, recordUrl + "profile/");
  663. }
  664. /**
  665. * HTTP流转RTSP流
  666. */
  667. public String streamConversion(String code) {
  668. ExecutorService executor = Executors.newSingleThreadExecutor();
  669. Callable<String> task = new Callable<String>() {
  670. @Override
  671. public String call() throws Exception {
  672. // 执行网络请求...
  673. log.info(ffmpegPath + " -re -i /opt/streams/compress/" + code + ".mp4 -c:v copy -c:a copy -f flv "+rtmpUrl+"/live/" + code);
  674. cmdCameraUtil.cmd(ffmpegPath + " -re -i /opt/streams/compress/" + code + ".mp4 -c:v copy -c:a copy -f flv "+rtmpUrl+"/live/" + code);
  675. return "Response";
  676. }
  677. };
  678. try {
  679. String result = executor.submit(task).get(1, TimeUnit.SECONDS); // 设置5秒超时时间
  680. System.out.println("Response: " + result);
  681. } catch (InterruptedException | ExecutionException | TimeoutException e) {
  682. // 请求超时处理逻辑
  683. System.out.println("Request timeout");
  684. }
  685. executor.shutdown();
  686. return bakUrlRtsp+"/live/"+code;
  687. }
  688. /**
  689. * RTSP流视频压缩
  690. */
  691. public void videoCompression(String code) {
  692. // 执行网络请求...
  693. log.info(ffmpegPath + " -i /opt/streams/map/"+code+".mp4 -c:v libx264 -s 640x480 -c:a adpcm_swf -ar 44100 /opt/streams/compress/" + code+".mp4");
  694. cmdCameraUtil.cmd(ffmpegPath + " -i /opt/streams/map/"+code+".mp4 -c:v libx264 -s 640x480 -c:a adpcm_swf -ar 44100 /opt/streams/compress/" + code+".mp4");
  695. }
  696. public static List<Map<String, Object>> filterRecordList(String channel,
  697. Date startTm,
  698. Date endTm,
  699. String mappingUrl,
  700. String wUrl) {
  701. List<Map<String, Object>> rmaps = new ArrayList<>();
  702. if (StringUtils.isBlank(channel)
  703. || ObjectUtils.isEmpty(startTm)
  704. || ObjectUtils.isEmpty(endTm)) {
  705. return null;
  706. }
  707. Map<Date, Map<String, Object>> m = new HashMap<>();
  708. // 调用视频服务返回参数
  709. String startTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, startTm);
  710. String endTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, endTm);
  711. String param = "channel=" + channel + "&startTime=" + startTime + "&endTime=" + endTime;
  712. // /api/record/flv/list
  713. String s = HttpUtils.sendGet(webUrl + "/recordpro/api/list", param);
  714. // 视频拼接
  715. if (!StringUtils.isBlank(s) || "null".equals(s)) {
  716. List<Map<String, Object>> maps = JSON.parseArray(s, Map.class);
  717. if (ObjectUtils.isEmpty(maps)) {
  718. return null;
  719. }
  720. for (Map<String, Object> map : maps) {
  721. Object path = map.get("Path");
  722. Object size = map.get("Size");
  723. Object duration = map.get("Duration");
  724. if (!ObjectUtils.isEmpty(path)) {
  725. String s1 = path.toString();
  726. String substring = s1.substring(s1.lastIndexOf("/") + 1, s1.length());
  727. String substring1 = substring.substring(0, substring.indexOf("-"));
  728. String[] s2 = substring1.split("_");
  729. if (!ObjectUtils.isEmpty(s2)) {
  730. Map<String, Object> mo = new HashMap<>();
  731. String s3 = s2[0] + s2[1];
  732. String s4 = s2[0] + s2[2];
  733. Date sdate = DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS, s3);
  734. if (s2[1].startsWith("23") && s2[2].startsWith("00")) {
  735. sdate = DateUtils.addDays(sdate, -1);
  736. }
  737. Date edate = DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS, s4);
  738. mo.put("startTime", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, sdate));
  739. mo.put("entTime", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, edate));
  740. mo.put("url", wUrl + path.toString());
  741. mo.put("path", mappingUrl + path.toString());
  742. mo.put("fileName", substring);
  743. mo.put("size", size);
  744. mo.put("duration", duration);
  745. /* sdate |startTm| edate |endTm| */
  746. if (startTm.compareTo(sdate) >= 0
  747. && startTm.compareTo(edate) <= 0
  748. && endTm.compareTo(edate) >= 0) {
  749. m.put(sdate, mo);
  750. /* |startTm| sdate edate |endTm| */
  751. } else if (startTm.compareTo(sdate) <= 0 && endTm.compareTo(edate) >= 0) {
  752. m.put(sdate, mo);
  753. /* |startTm| sdate |endTm| edate */
  754. } else if (startTm.compareTo(sdate) <= 0
  755. && endTm.compareTo(sdate) >= 0
  756. && endTm.compareTo(edate) <= 0) {
  757. m.put(sdate, mo);
  758. /* sdate |startTm| |endTm| edate */
  759. } else if (startTm.compareTo(sdate) >= 0 && endTm.compareTo(edate) <= 0) {
  760. m.put(sdate, mo);
  761. }
  762. }
  763. }
  764. }
  765. }
  766. if (!ObjectUtils.isEmpty(m) && m.size() > 0) {
  767. Set<Date> dates = m.keySet();
  768. // 排序
  769. dates.stream().parallel().collect(Collectors.toList()).stream().sorted().forEach(d -> {
  770. rmaps.add(m.get(d));
  771. });
  772. log.info("rmaps:{}", rmaps);
  773. return rmaps;
  774. }
  775. return null;
  776. }
  777. /**
  778. * 机车状态修改
  779. */
  780. public void heartbeat() {
  781. List<BaseTerminal> list = baseTerminalService.list();
  782. for (BaseTerminal baseTerminal : list) {
  783. QueryWrapper<MsgHeartbeatAlarmMessage> wrapper = new QueryWrapper<>();
  784. wrapper.eq("terminal_code", wrapper);
  785. wrapper.orderByDesc("create_time");
  786. wrapper.last("limit 1");
  787. MsgHeartbeatAlarmMessage msgHeartbeatAlarmMessage = msgHeartbeatAlarmMessageService.getOne(wrapper);
  788. if (ObjectUtils.isEmpty(msgHeartbeatAlarmMessage)) {
  789. baseTerminal.setStatus(2);
  790. baseTerminalService.updateById(baseTerminal);
  791. } else {
  792. List<SysDictData> heartbeat = dictTypeService.selectDictDataByType("heartbeat");
  793. long timeNow = System.currentTimeMillis();
  794. long createTime = msgHeartbeatAlarmMessage.getCreateTime().getTime();
  795. int times = Integer.parseInt(heartbeat.get(0).getDictValue());
  796. long thereHourMillis = 60 * 1000 * 3 * times;
  797. long timeNew = timeNow - thereHourMillis;
  798. if (timeNew > createTime) {
  799. baseTerminal.setStatus(2);
  800. baseTerminalService.updateById(baseTerminal);
  801. } else {
  802. baseTerminal.setStatus(1);
  803. baseTerminalService.updateById(baseTerminal);
  804. }
  805. }
  806. }
  807. }
  808. /**
  809. * 天网接口
  810. *
  811. * @return
  812. */
  813. public static String getPreviewURLs(String url, String body) {
  814. /**
  815. * STEP1:设置平台参数,根据实际情况,设置host appkey appsecret 三个参数.
  816. */
  817. ArtemisConfig.host = host; // 平台的ip端口
  818. ArtemisConfig.appKey = appKey; // 密钥appkey
  819. ArtemisConfig.appSecret = appSecret;// 密钥appSecret
  820. /**
  821. * STEP2:设置OpenAPI接口的上下文
  822. */
  823. final String ARTEMIS_PATH = "/artemis";
  824. /**
  825. * STEP3:设置接口的URI地址
  826. */
  827. final String previewURLsApi = ARTEMIS_PATH + url;
  828. Map<String, String> path = new HashMap<String, String>(2) {
  829. {
  830. put("https://", previewURLsApi);//根据现场环境部署确认是http还是https
  831. }
  832. };
  833. /**
  834. * STEP4:设置参数提交方式
  835. */
  836. String contentType = "application/json";
  837. /**
  838. * STEP6:调用接口
  839. */
  840. String result = ArtemisHttpUtil.doPostStringArtemis(path, body, null, null, contentType, null);// post请求application/json类型参数
  841. log.info("GetCameraPreviewURL----->" + result);
  842. return result;
  843. }
  844. /**
  845. * 车载终端实时流调用的远程天网接口
  846. * @param camerasVo
  847. * @param channel
  848. */
  849. public String apiPreviewURLs(CamerasVo camerasVo) {
  850. /**
  851. * jsonBody.put("cameraIndexCode", "01ea43e6676f4e47bd6c5cd9e02aa006");
  852. * jsonBody.put("streamType", 0);
  853. * jsonBody.put("protocol","rtsp");
  854. * jsonBody.put("transmode", 1);
  855. * jsonBody.put("expand","streamform=rtp");
  856. */
  857. camerasVo.setProtocol("rtsp");
  858. camerasVo.setTransmode(1);
  859. camerasVo.setExpand("streamform=rtp");
  860. String body = JSONObject.toJSONString(camerasVo);
  861. String previewURLs = getPreviewURLs("/api/video/v1/cameras/previewURLs", body);
  862. log.info("-------------------------------->>>>>previewURLs" + previewURLs);
  863. JSONObject outJson = JSONObject.parse(previewURLs);
  864. if ("0".equals(outJson.getString("code"))) {
  865. log.info("outJson----->" + outJson);
  866. JSONObject data = outJson.getJSONObject("data");
  867. String urls = data.getString("url");
  868. return urls;
  869. }else {
  870. return "1";
  871. }
  872. }
  873. /**
  874. * web页面实时流调用的远程天网接口
  875. * @param camerasVo
  876. * @param channel
  877. */
  878. public static void previewURLs(CamerasVo camerasVo, String channel) {
  879. /**
  880. * jsonBody.put("cameraIndexCode", "01ea43e6676f4e47bd6c5cd9e02aa006");
  881. * jsonBody.put("streamType", 0);
  882. * jsonBody.put("protocol","rtsp");
  883. * jsonBody.put("transmode", 1);
  884. * jsonBody.put("expand","streamform=rtp");
  885. */
  886. camerasVo.setStreamType(0);
  887. camerasVo.setProtocol("rtsp");
  888. camerasVo.setTransmode(1);
  889. camerasVo.setExpand("streamform=rtp");
  890. String body = JSONObject.toJSONString(camerasVo);
  891. String previewURLs = getPreviewURLs("/api/video/v1/cameras/previewURLs", body);
  892. log.info("-------------------------------->>>>>previewURLs" + previewURLs);
  893. JSONObject outJson = JSONObject.parse(previewURLs);
  894. if ("0".equals(outJson.getString("code"))) {
  895. log.info("outJson----->" + outJson);
  896. JSONObject data = outJson.getJSONObject("data");
  897. String urls = data.getString("url");
  898. HttpUtils.sendGet(historyUrl + "rtsp/api/pull?target=" + urls + "&streamPath=" + camerasVo.getCameraIndexCode() + "/" + channel + "&save=0");
  899. }
  900. }
  901. /**
  902. * 报警回放本地测试
  903. * 从天网拉回放流,然后根据url把流下载下来变成文件,然后进行视频压缩
  904. * @param list
  905. * @param alarmPlayTimeValue
  906. */
  907. public void playbackURLs(List<String> list, String alarmPlayTimeValue) {
  908. log.info("--------------->playbackURLs");
  909. CompletableFuture future = CompletableFuture.supplyAsync(() -> {
  910. log.info("异步任务开始-----》");
  911. // for (String code : list) {
  912. String code="42010001541320000024";
  913. PlaybackVo playbackVo = new PlaybackVo();
  914. playbackVo.setCameraIndexCode(code);
  915. // 获取当前时间
  916. OffsetDateTime currentTime = OffsetDateTime.now();
  917. // 获取当前时间的前10分钟时间
  918. OffsetDateTime beforeTenMinutes = currentTime.minusMinutes(Long.parseLong(alarmPlayTimeValue));
  919. // 获取当前时间的后10分钟时间
  920. OffsetDateTime afterTenMinutes = currentTime.plusMinutes(Long.parseLong(alarmPlayTimeValue));
  921. // 格式化时间
  922. DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
  923. playbackVo.setBeginTime(beforeTenMinutes.format(formatter));
  924. //现在当前时间:后续需要修改成报警时间的后几分钟
  925. playbackVo.setEndTime(afterTenMinutes.format(formatter));
  926. // jsonBody.put("protocol","rtsp");
  927. // jsonBody.put("expand","streamform=rtp");
  928. playbackVo.setProtocol("rtsp");
  929. playbackVo.setExpand("streamform=rtp");
  930. //对象转换json字符串
  931. String body = JSONObject.toJSONString(playbackVo);
  932. //调用天网接口
  933. log.info("body----->" + body);
  934. // String playbackURLs = getPreviewURLs("/api/video/v1/cameras/playbackURLs", body);
  935. // log.info("playbackURLs----->" + playbackURLs);
  936. // JSONObject outJson = (JSONObject) JSONObject.parse(playbackURLs);
  937. // if ("0".equals(outJson.getString("code"))) {
  938. // log.info("outJson----->" + outJson);
  939. // JSONObject data = outJson.getJSONObject("data");
  940. // String urls = data.getString("url");
  941. // log.info("--------------->urls::" + urls);
  942. String FilePath = "/opt/streams/map/"+code+".mp4";
  943. // String FilePath = "/opt/streams/map/01ea43e6676f4e47bd6c5cd9e02aa006.mp4";
  944. // try {
  945. // TimeUnit.SECONDS.sleep(62*Integer.parseInt(alarmPlayTimeValue));
  946. // } catch (InterruptedException e) {
  947. // e.printStackTrace();
  948. // }
  949. Process process = rtspToMP4.StartRecord(ffmpegPath, "rtsp://124.70.58.209:8554/"+code+"/"+code, FilePath);
  950. log.info("------playbackURLs----->>>>:" + process);
  951. if (null != process) {
  952. map.put(code, process);
  953. }
  954. try {
  955. TimeUnit.SECONDS.sleep(70*Integer.parseInt(alarmPlayTimeValue));
  956. } catch (InterruptedException e) {
  957. e.printStackTrace();
  958. }
  959. log.info("------videoCompression----->>>>:");
  960. videoCompression(code);
  961. return 1;
  962. });
  963. future.join();
  964. }
  965. public AjaxResult stop(String id) {
  966. if (map.containsKey(id)) {
  967. Process process = map.get(id);
  968. log.info("-----stop------>>>" + process);
  969. if (null != process) {
  970. rtspToMP4.stopRecord(process);
  971. return AjaxResult.success();
  972. }
  973. }
  974. return AjaxResult.error();
  975. }
  976. public static void main(String[] args) throws InterruptedException, ParseException, IOException {
  977. // CameraUtil cameraUtil = new CameraUtil();
  978. // cameraUtil.closeRecording();
  979. // String s = "/opt/streams/record/flv/42010001541320001116/20230403_235506_000506-22e1523b-d170-11ed-8a42-fa163e4e1e9f.flv";
  980. // String fileStr = s.substring(s.lastIndexOf("/") + 1, s.length());
  981. // String pathStr = s.substring(0, s.lastIndexOf("/"));
  982. // System.out.println(fileStr);
  983. // System.out.println(pathStr);
  984. String s = "20230403235512";
  985. Date sdate = DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS, s);
  986. sdate = DateUtils.addDays(sdate, -1);
  987. System.out.println(DateUtils.parseDateToStr(DateUtils.YYYYMMDDHHMMSS, sdate));
  988. }
  989. }