package com.ozs.utils; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.hikvision.artemis.sdk.ArtemisHttpUtil; import com.hikvision.artemis.sdk.config.ArtemisConfig; import com.ozs.common.config.BaseConfig; import com.ozs.common.constant.Constants; import com.ozs.common.core.domain.AjaxResult; import com.ozs.common.core.domain.entity.SysDictData; import com.ozs.common.core.redis.RedisCache; import com.ozs.common.exception.base.BaseException; import com.ozs.common.utils.DateUtils; import com.ozs.common.utils.HttpClientUtil; import com.ozs.common.utils.http.HttpUtils; import com.ozs.entity.BaseTerminal; import com.ozs.entity.MsgHeartbeatAlarmMessage; import com.ozs.entity.vo.CamerasVo; import com.ozs.entity.vo.PlaybackVo; import com.ozs.framework.config.ServerConfig; import com.ozs.service.BaseTerminalService; import com.ozs.service.MsgHeartbeatAlarmMessageService; import com.ozs.system.mapper.SysDictDataMapper; import com.ozs.system.service.ISysDictTypeService; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.util.ObjectUtils; import javax.annotation.PostConstruct; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; /** * 相机工具 */ @Configuration @Slf4j public class CameraUtil { private static final ExecutorService executor = Executors.newFixedThreadPool(20); private static String historyUrl; private static String ffmpegPath; private static String filePath; private static String transcribeFilePath; private static String webUrl; private static String bakUrl; private static String bakUrlRtsp; private static String wsUrl; private static String httpUrl; private static CmdCameraUtil cUtil; private static RedisCache rc; private static ServerConfig sc; private static String recordUrl; private static String hkUrl; private static String host; private static String appKey; private static String appSecret; private static String rtmpUrl; @Autowired private CaneraConfig caneraConfig; @Autowired private CmdCameraUtil cmdCameraUtil; @Autowired private SysDictDataMapper dictDataMapper; @Autowired private RedisCache redisCache; @Autowired private ServerConfig serverConfig; @Autowired private BaseTerminalService baseTerminalService; @Autowired private MsgHeartbeatAlarmMessageService msgHeartbeatAlarmMessageService; @Autowired private ISysDictTypeService dictTypeService; @Autowired private RtspToMP4 rtspToMP4; private Map map = new HashMap<>(); public final static String tsFilekey = "mergeVideoTsFile"; /** * 历史回放 * * @param fromVideoFileList * @param ph * @return * @throws IOException */ public static String historyPlay(List fromVideoFileList, String ph, boolean flay) { // 视频服务映射路径 String NewfilePath = BaseConfig.getProfile() + "/" + ph; log.info("NewfilePath:{}", NewfilePath); log.info("fromVideoFileList:{}", fromVideoFileList); if (ObjectUtils.isEmpty(fromVideoFileList) || fromVideoFileList.size() <= 0) { throw new BaseException("当前相机无视频录像"); } executor.submit(new Runnable() { @Override public void run() { try { txConvetor(fromVideoFileList, NewfilePath, flay); } catch (IOException e) { log.error(e.getMessage()); e.printStackTrace(); } } }); // executor.shutdown(); return Constants.RESOURCE_PREFIX + "/" + ph; } @SneakyThrows public static void txConvetor(List fromVideoFileList, String newfilePath, boolean fly) throws IOException { /* * ffmpeg -i 20230411_155847_155947-d4c2265d-d83e-11ed-8e7f-fa163e4e1e9f.flv -c:v copy 1.ts ffmpeg -i 20230411_155948_160048-f91fea03-d83e-11ed-8e7f-fa163e4e1e9f.flv -c:v copy 2.ts ffmpeg -i "concat:1.ts|2.ts" -c copy output.mp4 * */ File file = new File(newfilePath); boolean flay = false; if (!file.getParentFile().exists()) { boolean mkdirs = file.getParentFile().mkdirs(); log.info("创建文件夹:{}", file.getParentFile().getPath()); log.info("创建文件夹结果:{}", mkdirs); flay = true; } cUtil.cmd("chomd -R 777 " + file.getParentFile().getPath()); log.info("newfilePath:{}", newfilePath); StringBuffer sm = new StringBuffer(ffmpegPath + " -i \"concat:"); List fileTs = new ArrayList<>(); for (int t = 0; t < fromVideoFileList.size(); t++) { File ft = new File(fromVideoFileList.get(t)); if (ft.exists()) { log.info("file:{}", fromVideoFileList.get(t)); String substring = fromVideoFileList.get(t).substring(0, fromVideoFileList.get(t).lastIndexOf(".")); int x;//定义两变量 Random ne = new Random();//实例化一个random的对象ne x = ne.nextInt(9999 - 1000 + 1) + 1000;//为变量赋随机值1000-9999 substring = substring + x;//定义两变量 String cmdstr = ffmpegPath + " -i " + fromVideoFileList.get(t) + " -c:v copy " + substring + ".ts"; log.info("转换命令:{}", cmdstr); cUtil.cmd(cmdstr); fileTs.add(substring + ".ts"); if (t != fromVideoFileList.size() - 1) { sm.append(substring + ".ts|"); } else { sm.append(substring + ".ts\" "); } } } if (fileTs.size() > 0) { Map mergeVideoTsFile = rc.getCacheMap(tsFilekey); if (ObjectUtils.isEmpty(mergeVideoTsFile)) { mergeVideoTsFile = new HashMap<>(); } if (!flay) { // 如果没有解除,把生成的文件放入要删除的定时任务 redis key 中 fileTs.add(newfilePath); } mergeVideoTsFile.put(System.currentTimeMillis() + "", fileTs); rc.deleteObject(tsFilekey); if (mergeVideoTsFile.size() > 0) { rc.setCacheMap(tsFilekey, mergeVideoTsFile); } sm.append("-c copy " + newfilePath); log.info("合并命令:{}", sm.toString()); cUtil.cmd(sm.toString()); } } /** * 实时播放的拼接流 * * @param cameraCode 相机编码 * @param channel 相机通道 * @return */ public static String getPlayFlv(String cameraCode, String channel, boolean flay) { if (!flay) { return bakUrl + "/hdl/" + cameraCode + "/" + channel + ".flv"; } return wsUrl + "/ws/" + cameraCode + "/" + channel + ".flv"; // if (!flay) { // return bakUrl + "/ws/" + channel + "/" + cameraCode + ".flv"; // } // return webUrl + "/ws/" + channel + "/" + cameraCode + ".flv"; } /** * Rtsp实时播放的拼接流 * * @param cameraCode 相机编码 * @param channel 相机通道 * @return */ public static String getPlayFlvRtsp(String cameraCode, String channel, boolean flay) { if (!flay) { return bakUrlRtsp + "/" + channel + "/" + cameraCode; } return bakUrlRtsp + "/" + channel + "/" + cameraCode; // if (!flay) { // return bakUrl + "/ws/" + channel + "/" + cameraCode + ".flv"; // } // return webUrl + "/ws/" + channel + "/" + cameraCode + ".flv"; } public String heartbeatgetPlayFlv(String cameraCode, String channel) { List pathList = new ArrayList<>(); String data = HttpUtils.sendGet(webUrl + "/rtsp/api/list"); if (StringUtils.isNotEmpty(data)) { JSONArray jsonArray = JSONArray.parseArray(data); for (int i = 0; i < jsonArray.size(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); String name = jsonObject.getString("Path"); pathList.add(name); } boolean contains = pathList.contains(cameraCode + "/" + channel); if (!contains) { return null; } else { return getPlayFlv(cameraCode, channel, true); } } return null; } public String heartbeatgetUrl(String cameraCode, String channel) { CamerasVo camerasVo = new CamerasVo(); camerasVo.setCameraIndexCode(cameraCode); previewURLs(camerasVo, channel); return getPlayFlv(cameraCode, channel, true); } /** * web页面实时流接口 * * @param cameraCode * @param channel * @return */ public static String getPlayFlv(String cameraCode, String channel) { List pathList = new ArrayList<>(); String data = HttpUtils.sendGet(webUrl + "/rtsp/api/list"); if (StringUtils.isNotEmpty(data)) { JSONArray jsonArray = JSONArray.parseArray(data); for (int i = 0; i < jsonArray.size(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); String name = jsonObject.getString("Path"); pathList.add(name); } boolean contains = pathList.contains(cameraCode + "/" + channel); if (contains) { return getPlayFlv(cameraCode, channel, true); } else { /** * jsonBody.put("cameraIndexCode", "01ea43e6676f4e47bd6c5cd9e02aa006"); * jsonBody.put("streamType", 0); * jsonBody.put("protocol","rtsp"); * jsonBody.put("transmode", 1); * jsonBody.put("expand","streamform=rtp"); */ CamerasVo camerasVo = new CamerasVo(); camerasVo.setCameraIndexCode(cameraCode); previewURLs(camerasVo, channel); return getPlayFlv(cameraCode, channel, true); } } else { CamerasVo camerasVo = new CamerasVo(); camerasVo.setCameraIndexCode(cameraCode); previewURLs(camerasVo, channel); return getPlayFlv(cameraCode, channel, true); } // return getPlayFlv(cameraCode, channel, true); } public static String invite(String cameraCode, String channel) { String result = null; String url = bakUrl + "/api/gb28181/invite?id=" + cameraCode + "&channel=" + channel; try { result = HttpClientUtil.get(url); log.info("result:{}", result); } catch (Exception e) { log.info(e.getMessage()); e.printStackTrace(); } return result; } /** * 开启录制功能 * * @param cameraCode 相机编码 * @param channel 相机通道 * @return */ public static String startRecording(String cameraCode, String channel) { return historyUrl + "/recordpro/api/start?streamPath=" + channel + "/" + cameraCode; } /** * 关闭录制功能 * * @param taskId 录像接口返回的任务ID * @return */ public static String endRecording(String taskId) { return historyUrl + "/recordpro/api/stop?id=" + taskId; } /** * 历史回放流(获取) * * @param channel 相机通道 * @param startTm 开始时间 * @param endTm 结束时间 * @return */ public static String historyPlayListStr(String channel, Date startTm, Date endTm, boolean flay) { List list = filterPlayList(channel, startTm, endTm, filePath); String uuid = UUID.randomUUID().toString(); String ph = "record/flv/" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, new Date()) + "/" + channel + "/" + uuid + ".mp4"; if (!ObjectUtils.isEmpty(list)) { try { return historyPlay(list, ph, flay); } catch (Exception e) { log.error(e.getMessage()); e.printStackTrace(); } } else { throw new BaseException("当前相机无视频录像"); } return null; } /** * 过滤符合条件的视频 * * @param channel * @param startTm * @param endTm * @param mappingUrl * @return */ public static List filterPlayList(String channel, Date startTm, Date endTm, String mappingUrl) { if (StringUtils.isBlank(channel) || ObjectUtils.isEmpty(startTm) || ObjectUtils.isEmpty(endTm)) { return null; } List ls = new ArrayList<>(); Map m = new HashMap<>(); // 调用视频服务返回参数 String startTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, startTm); String endTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, endTm); String param = "channel=" + channel + "&startTime=" + startTime + "&endTime=" + endTime; String s = HttpUtils.sendGet(historyUrl + "/api/record/flv/list", param); // 视频拼接 if (!StringUtils.isBlank(s) && !"null".equals(s) && !s.startsWith("")) { List> maps = JSON.parseArray(s, Map.class); if (ObjectUtils.isEmpty(maps)) { return null; } for (Map map : maps) { Object path = map.get("Path"); if (!ObjectUtils.isEmpty(path)) { String s1 = path.toString(); String substring = s1.substring(s1.lastIndexOf("/") + 1, s1.length()); String substring1 = substring.substring(0, substring.indexOf("-")); String[] s2 = substring1.split("_"); if (!ObjectUtils.isEmpty(s2)) { String s3 = s2[0] + s2[1]; String s4 = s2[0] + s2[2]; Date sdate = DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS, s3); if (s2[1].startsWith("23") && s2[2].startsWith("00")) { sdate = DateUtils.addDays(sdate, -1); } Date edate = DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS, s4); /* sdate |startTm| edate |endTm| */ if (startTm.compareTo(sdate) >= 0 && startTm.compareTo(edate) <= 0 && endTm.compareTo(edate) >= 0) { m.put(sdate, mappingUrl + path.toString()); /* |startTm| sdate edate |endTm| */ } else if (startTm.compareTo(sdate) <= 0 && endTm.compareTo(edate) >= 0) { m.put(sdate, mappingUrl + path.toString()); /* |startTm| sdate |endTm| edate */ } else if (startTm.compareTo(sdate) <= 0 && endTm.compareTo(sdate) >= 0 && endTm.compareTo(edate) <= 0) { m.put(sdate, mappingUrl + path.toString()); /* sdate |startTm| |endTm| edate */ } else if (startTm.compareTo(sdate) >= 0 && endTm.compareTo(edate) <= 0) { m.put(sdate, mappingUrl + path.toString()); } } } } } if (!ObjectUtils.isEmpty(m) && m.size() > 0) { Set dates = m.keySet(); // 排序 dates.stream().parallel().collect(Collectors.toList()).stream().sorted().forEach(d -> { ls.add(m.get(d)); }); return ls; } return null; } /** * 合并视频(转化文件) * * @param fromVideoFileList 视频路径 * @param newfilePath 生产新的视频文件路径 * @throws IOException */ public static Map myConvetor(List fromVideoFileList, String newfilePath, String uuid) throws IOException { /* * for f in *.flv; do echo "file '$f'" >> mylist.txt; done ffmpeg -f concat -i mylist.txt -c copy output.flv * */ File file = new File(newfilePath); boolean flay = false; if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); log.info("创建文件夹:{}", file.getParentFile().getPath()); flay = true; } log.info("newfilePath:{}", newfilePath); StringBuffer sm = new StringBuffer("for f in "); String pathStr = null; for (int t = 0; t < fromVideoFileList.size(); t++) { File ft = new File(fromVideoFileList.get(t)); if (ft.exists()) { if (t != fromVideoFileList.size() - 1) { // sm.append(fromVideoFileList.get(t) + " "); sm.append(fromVideoFileList.get(t).substring(fromVideoFileList.get(t).lastIndexOf("/") + 1, fromVideoFileList.get(t).length()) + " "); } else { // sm.append(fromVideoFileList.get(t)); sm.append(fromVideoFileList.get(t).substring(fromVideoFileList.get(t).lastIndexOf("/") + 1, fromVideoFileList.get(t).length())); pathStr = fromVideoFileList.get(t).substring(0, fromVideoFileList.get(t).lastIndexOf("/")); } } } // String substring = newfilePath.substring(0, newfilePath.lastIndexOf(".")); // substring = substring + ".txt"; String substring = uuid + ".txt"; log.info("临时转化的文件:{}", substring); sm.append("; do echo \"file '$f'\" >> " + substring + "; done"); String commit = sm.toString(); log.info("合并转化文件的命令:{}", commit); Process proc = Runtime.getRuntime().exec("/bin/bash", null, null); BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream())); PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true); try { String commit1 = ffmpegPath + " -f concat -safe 0 -i " + pathStr + "/" + substring + " -c copy " + newfilePath; log.info("合并视频命令:{}", commit1); List commands = new ArrayList<>(); if (flay) { commands.add("chomd -R 777 " + file.getParentFile().getPath()); } // commands.add("cd " + pathStr); // 合并成一个临时文件 commands.add(commit); // 临时文件转化为 flv // commands.add(commit1); log.info("录像视频命令:{}", commands); for (String line : commands) { log.info("命令:{}", line); out.println(line); } out.println("exit");// 这个命令必须执行,否则in流不结束。 log.info("命令:{}", "exit"); String rspLine = ""; while ((rspLine = in.readLine()) != null) { log.info("*****:{}", rspLine); } proc.waitFor(); Map map = new HashMap<>(); map.put("cmd", commit1); map.put("path", pathStr + "/" + substring); return map; } catch (InterruptedException e) { log.error(e.getMessage()); e.printStackTrace(); } finally { in.close(); out.close(); proc.destroy(); } return null; } @PostConstruct public void init() { historyUrl = caneraConfig.getHistoryUrl(); ffmpegPath = caneraConfig.getFfmpegPath(); filePath = caneraConfig.getFilePath(); transcribeFilePath = caneraConfig.getTranscribeFilePath(); webUrl = caneraConfig.getWebUrl(); bakUrl = caneraConfig.getBakUrl(); bakUrlRtsp = caneraConfig.getBakUrlRtsp(); rc = redisCache; cUtil = cmdCameraUtil; sc = serverConfig; wsUrl = caneraConfig.getWsUrl(); httpUrl = caneraConfig.getHttpUrl(); recordUrl = caneraConfig.getRecordUrl(); hkUrl = caneraConfig.getHkUrl(); host = caneraConfig.getHost(); appKey = caneraConfig.getAppKey(); appSecret = caneraConfig.getAppSecret(); rtmpUrl = caneraConfig.getRtmpUrl(); } /** * 定时任务参数flv文件 * * @throws IOException * @throws InterruptedException */ public void deleteFlv() throws IOException, InterruptedException { SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd"); Date date = new Date(); Calendar calendar = new GregorianCalendar(); calendar.setTime(date); calendar.add(Calendar.DATE, -1); //把日期往后增加一天,整数 往后推,负数往前移动 date = calendar.getTime(); //这个时间就是日期往后推一天的结果 String path = BaseConfig.getProfile() + "/flv/" + DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, date); File file = new File(path); if (file.exists()) { List rspList = new ArrayList(); Process proc = Runtime.getRuntime().exec("/bin/bash", null, null); BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream())); PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true); String commit = " rm -rf " + path; List commands = new ArrayList<>(); // 删除 commands.add(commit); log.info("删除昨天的录像视频命令:{}", commands); for (String line : commands) { out.println(line); } out.println("exit");// 这个命令必须执行,否则in流不结束。 String rspLine = ""; while ((rspLine = in.readLine()) != null) { System.out.println(rspLine); rspList.add(rspLine); } int i = proc.waitFor(); log.info("执行结果:{}", i); in.close(); out.close(); proc.destroy(); } } /** * 定时任务:删除超过配置时长的录制视频 * * @throws IOException * @throws InterruptedException */ public void deleteFlvExceed() throws IOException, InterruptedException, ParseException { //字典中设置的值 List sysCameraRecordTime = dictDataMapper.selectDictDataByType("sys_camera_record_time"); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); Date date = new Date(); Calendar calendar = new GregorianCalendar(); calendar.setTime(date); //正数,日期天数加 //负数,日期天数减 提前45天 if (!ObjectUtils.isEmpty(sysCameraRecordTime)) { String dictValue = sysCameraRecordTime.get(0).getDictValue(); calendar.add(Calendar.DAY_OF_YEAR, Integer.parseInt("-" + dictValue)); } else { calendar.add(Calendar.DAY_OF_YEAR, -45); } date = calendar.getTime(); //遍历每个进行视频录制的摄像头 File file = new File(transcribeFilePath); log.info("file.Name()======================================" + file.getName()); log.info("file.exists()======================================" + file.exists()); if (file.exists() && file.isDirectory()) { //获取文件夹中所有的子文件夹和文件 File[] files = file.listFiles(); if (!ObjectUtils.isEmpty(files) && files.length > 0) { for (File file1 : files) { // file1=/opt/streams/record/flv/34020000001320000167 log.info("file1.Name()======================================" + file1.getName()); if (file1.exists() && file1.isDirectory()) { //相机文件 File[] fs = file1.listFiles(); if (!ObjectUtils.isEmpty(fs) && fs.length > 0) { for (File f : fs) { // f=/opt/streams/record/flv/34020000001320000167/20230328_192033_192037-91509173-cd5a-11ed-8a42-fa163e4e1e9f.flv //fName=20230328_192033_192037-91509173-cd5a-11ed-8a42-fa163e4e1e9f.flv String fName = f.getName(); log.info("fName======================================" + fName); String[] split = fName.split("_"); if (split.length > 0) { //20230328 String s = split[0]; Date parse = sdf.parse(s); long fTime = parse.getTime(); long dateTime = date.getTime(); //删除过期文件 if (fTime < dateTime) { f.delete(); } } } } } } } } } public static File[] getCurFilesList(String filePath) { File path = new File(filePath); File[] listFiles = path.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { if (pathname.isFile()) { return true; } else { return false; } } }); return listFiles; } public static void execute(String command) { try { ProcessBuilder process = new ProcessBuilder(command); process.inheritIO().start().waitFor(); } catch (Exception e) { e.printStackTrace(); } } /** * web页面视频回放接口 * * @param code * @param startTm * @param endTm * @return */ public static String getRecordList(String code, Date startTm, Date endTm, String channel) { PlaybackVo playbackVo = new PlaybackVo(); playbackVo.setCameraIndexCode(code); // 将 Date 转换为 Instant Instant startInstant = startTm.toInstant(); Instant endInstant = endTm.toInstant(); // 将 Instant 转换为 OffsetDateTime OffsetDateTime startOffsetDateTime = startInstant.atOffset(ZoneOffset.UTC); OffsetDateTime endOffsetDateTime = endInstant.atOffset(ZoneOffset.UTC); // 格式化为字符串 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); String formattedStartTime = startOffsetDateTime.format(formatter); String formattedEndTime = endOffsetDateTime.format(formatter); // 将格式化后的时间设置到 playbackVo 中 playbackVo.setBeginTime(formattedStartTime); playbackVo.setEndTime(formattedEndTime); playbackVo.setProtocol("rtsp"); playbackVo.setExpand("streamform=rtp"); //对象转换json字符串 String body = JSONObject.toJSONString(playbackVo); //调用天网接口 String playbackURLs = getPreviewURLs("/api/video/v1/cameras/playbackURLs", body); JSONObject outJson = (JSONObject) JSONObject.parse(playbackURLs); log.info("outJson-------->" + outJson); if ("0".equals(outJson.getString("code"))) { JSONObject data = outJson.getJSONObject("data"); String urls = data.getString("url"); log.info("urls----------->" + urls); try { TimeUnit.SECONDS.sleep(3); String msg = HttpUtils.sendGet(httpUrl + "/rtsp/api/pull?target=" + urls + "&streamPath=" + code + "/" + channel + "&save=0"); log.info("msg---------->" + msg); } catch (InterruptedException e) { e.printStackTrace(); } return getPlayFlv(code, channel, true); } else { return "调用天网接口失败"; } // return filterRecordList(channel, startTm, endTm, filePath, recordUrl + "profile/"); } /** * HTTP流转RTSP流 */ public String streamConversion(String code) { ExecutorService executor = Executors.newSingleThreadExecutor(); Callable task = new Callable() { @Override public String call() throws Exception { // 执行网络请求... log.info(ffmpegPath + " -re -i /opt/streams/compress/70b0bd685b0d4df1b4faf74ff5c1e7fd.mp4 -c:v copy -c:a copy -f flv rtmp://10.48.36.47:1935/live/70b0bd685b0d4df1b4faf74ff5c1e7fd"); cmdCameraUtil.cmd(ffmpegPath + " -re -i /opt/streams/compress/" + code + ".mp4 -c:v copy -c:a copy -f flv " + rtmpUrl + "/live/" + code); return "Response"; } }; try { String result = executor.submit(task).get(1, TimeUnit.SECONDS); // 设置5秒超时时间 System.out.println("Response: " + result); } catch (InterruptedException | ExecutionException | TimeoutException e) { // 请求超时处理逻辑 System.out.println("Request timeout"); } executor.shutdown(); return bakUrlRtsp + "/live/" + code; } /** * RTSP流视频压缩 */ public void videoCompression(String code) { // 执行网络请求... /** * /usr/bin/ffmpeg -i /opt/streams/map/70b0bd685b0d4df1b4faf74ff5c1e7fd.mp4 -c:v libx264 -s 640x480 -c:a aac -ar 44100 /opt/streams/compress/70b0bd685b0d4df1b4faf74ff5c1e7fd.mp4 */ log.info(ffmpegPath + " -i /opt/streams/map/" + code + ".mp4 -c:v libx264 -s 640x480 -c:a aac -ar 44100 /opt/streams/compress/" + code + ".mp4"); cmdCameraUtil.cmd(ffmpegPath + " -i /opt/streams/map/" + code + ".mp4 -c:v libx264 -s 640x480 -c:a aac -ar 44100 /opt/streams/compress/" + code + ".mp4"); } public static List> filterRecordList(String channel, Date startTm, Date endTm, String mappingUrl, String wUrl) { List> rmaps = new ArrayList<>(); if (StringUtils.isBlank(channel) || ObjectUtils.isEmpty(startTm) || ObjectUtils.isEmpty(endTm)) { return null; } Map> m = new HashMap<>(); // 调用视频服务返回参数 String startTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, startTm); String endTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, endTm); String param = "channel=" + channel + "&startTime=" + startTime + "&endTime=" + endTime; // /api/record/flv/list String s = HttpUtils.sendGet(webUrl + "/recordpro/api/list", param); // 视频拼接 if (!StringUtils.isBlank(s) || "null".equals(s)) { List> maps = JSON.parseArray(s, Map.class); if (ObjectUtils.isEmpty(maps)) { return null; } for (Map map : maps) { Object path = map.get("Path"); Object size = map.get("Size"); Object duration = map.get("Duration"); if (!ObjectUtils.isEmpty(path)) { String s1 = path.toString(); String substring = s1.substring(s1.lastIndexOf("/") + 1, s1.length()); String substring1 = substring.substring(0, substring.indexOf("-")); String[] s2 = substring1.split("_"); if (!ObjectUtils.isEmpty(s2)) { Map mo = new HashMap<>(); String s3 = s2[0] + s2[1]; String s4 = s2[0] + s2[2]; Date sdate = DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS, s3); if (s2[1].startsWith("23") && s2[2].startsWith("00")) { sdate = DateUtils.addDays(sdate, -1); } Date edate = DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS, s4); mo.put("startTime", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, sdate)); mo.put("entTime", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, edate)); mo.put("url", wUrl + path.toString()); mo.put("path", mappingUrl + path.toString()); mo.put("fileName", substring); mo.put("size", size); mo.put("duration", duration); /* sdate |startTm| edate |endTm| */ if (startTm.compareTo(sdate) >= 0 && startTm.compareTo(edate) <= 0 && endTm.compareTo(edate) >= 0) { m.put(sdate, mo); /* |startTm| sdate edate |endTm| */ } else if (startTm.compareTo(sdate) <= 0 && endTm.compareTo(edate) >= 0) { m.put(sdate, mo); /* |startTm| sdate |endTm| edate */ } else if (startTm.compareTo(sdate) <= 0 && endTm.compareTo(sdate) >= 0 && endTm.compareTo(edate) <= 0) { m.put(sdate, mo); /* sdate |startTm| |endTm| edate */ } else if (startTm.compareTo(sdate) >= 0 && endTm.compareTo(edate) <= 0) { m.put(sdate, mo); } } } } } if (!ObjectUtils.isEmpty(m) && m.size() > 0) { Set dates = m.keySet(); // 排序 dates.stream().parallel().collect(Collectors.toList()).stream().sorted().forEach(d -> { rmaps.add(m.get(d)); }); log.info("rmaps:{}", rmaps); return rmaps; } return null; } /** * 机车状态修改 */ public void heartbeat() { List list = baseTerminalService.list(); for (BaseTerminal baseTerminal : list) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("terminal_code", wrapper); wrapper.orderByDesc("create_time"); wrapper.last("limit 1"); MsgHeartbeatAlarmMessage msgHeartbeatAlarmMessage = msgHeartbeatAlarmMessageService.getOne(wrapper); if (ObjectUtils.isEmpty(msgHeartbeatAlarmMessage)) { baseTerminal.setStatus(2); baseTerminalService.updateById(baseTerminal); } else { List heartbeat = dictTypeService.selectDictDataByType("heartbeat"); long timeNow = System.currentTimeMillis(); long createTime = msgHeartbeatAlarmMessage.getCreateTime().getTime(); int times = Integer.parseInt(heartbeat.get(0).getDictValue()); long thereHourMillis = 60 * 1000 * 3 * times; long timeNew = timeNow - thereHourMillis; if (timeNew > createTime) { baseTerminal.setStatus(2); baseTerminalService.updateById(baseTerminal); } else { baseTerminal.setStatus(1); baseTerminalService.updateById(baseTerminal); } } } } /** * 天网接口 * * @return */ public static String getPreviewURLs(String url, String body) { /** * STEP1:设置平台参数,根据实际情况,设置host appkey appsecret 三个参数. */ ArtemisConfig.host = host; // 平台的ip端口 ArtemisConfig.appKey = appKey; // 密钥appkey ArtemisConfig.appSecret = appSecret;// 密钥appSecret /** * STEP2:设置OpenAPI接口的上下文 */ final String ARTEMIS_PATH = "/artemis"; /** * STEP3:设置接口的URI地址 */ final String previewURLsApi = ARTEMIS_PATH + url; Map path = new HashMap(2) { { put("https://", previewURLsApi);//根据现场环境部署确认是http还是https } }; /** * STEP4:设置参数提交方式 */ String contentType = "application/json"; /** * STEP6:调用接口 */ String result = ArtemisHttpUtil.doPostStringArtemis(path, body, null, null, contentType, null);// post请求application/json类型参数 log.info("GetCameraPreviewURL----->" + result); return result; } /** * 车载终端实时流调用的远程天网接口 * * @param camerasVo * @param channel */ public String apiPreviewURLs(CamerasVo camerasVo) { /** * jsonBody.put("cameraIndexCode", "01ea43e6676f4e47bd6c5cd9e02aa006"); * jsonBody.put("streamType", 0); * jsonBody.put("protocol","rtsp"); * jsonBody.put("transmode", 1); * jsonBody.put("expand","streamform=rtp"); */ camerasVo.setProtocol("rtsp"); camerasVo.setTransmode(1); camerasVo.setExpand("streamform=rtp"); String body = JSONObject.toJSONString(camerasVo); String previewURLs = getPreviewURLs("/api/video/v1/cameras/previewURLs", body); log.info("-------------------------------->>>>>previewURLs" + previewURLs); JSONObject outJson = JSONObject.parse(previewURLs); if ("0".equals(outJson.getString("code"))) { log.info("outJson----->" + outJson); JSONObject data = outJson.getJSONObject("data"); String urls = data.getString("url"); return urls; } else { return "1"; } } /** * web页面实时流调用的远程天网接口 * * @param camerasVo * @param channel */ public static void previewURLs(CamerasVo camerasVo, String channel) { /** * jsonBody.put("cameraIndexCode", "01ea43e6676f4e47bd6c5cd9e02aa006"); * jsonBody.put("streamType", 0); * jsonBody.put("protocol","rtsp"); * jsonBody.put("transmode", 1); * jsonBody.put("expand","streamform=rtp"); */ camerasVo.setStreamType(0); camerasVo.setProtocol("rtsp"); camerasVo.setTransmode(1); camerasVo.setExpand("streamform=rtp"); String body = JSONObject.toJSONString(camerasVo); String previewURLs = getPreviewURLs("/api/video/v1/cameras/previewURLs", body); log.info("-------------------------------->>>>>previewURLs" + previewURLs); JSONObject outJson = JSONObject.parse(previewURLs); if ("0".equals(outJson.getString("code"))) { log.info("outJson----->" + outJson); JSONObject data = outJson.getJSONObject("data"); String urls = data.getString("url"); log.info("urls----->" + urls); try { TimeUnit.SECONDS.sleep(3); String msg = HttpUtils.sendGet(httpUrl + "/rtsp/api/pull?target=" + urls + "&streamPath=" + camerasVo.getCameraIndexCode() + "/" + channel + "&save=0"); log.info("msg---------->" + msg); } catch (InterruptedException e) { e.printStackTrace(); } } else { log.info("流媒体获取流失败----->"); } } /** * 报警回放本地测试 * 从天网拉回放流,然后根据url把流下载下来变成文件,然后进行视频压缩 * * @param list * @param alarmPlayTimeValue */ public void playbackURLs(List list, String alarmPlayTimeValue) { log.info("--------------->playbackURLs"); CompletableFuture future = CompletableFuture.supplyAsync(() -> { log.info("异步任务开始-----》"); for (String code : list) { PlaybackVo playbackVo = new PlaybackVo(); playbackVo.setCameraIndexCode(code); // 获取当前时间 OffsetDateTime currentTime = OffsetDateTime.now(); // 获取当前时间的前10分钟时间 OffsetDateTime beforeTenMinutes = currentTime.minusMinutes(Long.parseLong(alarmPlayTimeValue)); // 获取当前时间的后10分钟时间 OffsetDateTime afterTenMinutes = currentTime.plusMinutes(Long.parseLong(alarmPlayTimeValue)); // 格式化时间 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); playbackVo.setBeginTime(beforeTenMinutes.format(formatter)); //现在当前时间:后续需要修改成报警时间的后几分钟 playbackVo.setEndTime(afterTenMinutes.format(formatter)); // jsonBody.put("protocol","rtsp"); // jsonBody.put("expand","streamform=rtp"); playbackVo.setProtocol("rtsp"); playbackVo.setExpand("streamform=rtp"); //对象转换json字符串 String body = JSONObject.toJSONString(playbackVo); //调用天网接口 log.info("body----->" + body); String playbackURLs = getPreviewURLs("/api/video/v1/cameras/playbackURLs", body); log.info("playbackURLs----->" + playbackURLs); JSONObject outJson = (JSONObject) JSONObject.parse(playbackURLs); if ("0".equals(outJson.getString("code"))) { log.info("outJson----->" + outJson); JSONObject data = outJson.getJSONObject("data"); String urls = data.getString("url"); log.info("--------------->urls::" + urls); String FilePath = "/opt/streams/map/" + code + ".mp4"; // String FilePath = "/opt/streams/map/01ea43e6676f4e47bd6c5cd9e02aa006.mp4"; // try { // TimeUnit.SECONDS.sleep(62*Integer.parseInt(alarmPlayTimeValue)); // } catch (InterruptedException e) { // e.printStackTrace(); // } Process process = rtspToMP4.StartRecord(ffmpegPath, urls, FilePath); log.info("------playbackURLs----->>>>:" + process); if (null != process) { map.put(code, process); } try { TimeUnit.SECONDS.sleep(70 * Integer.parseInt(alarmPlayTimeValue)); } catch (InterruptedException e) { e.printStackTrace(); } log.info("------videoCompression----->>>>:"); videoCompression(code); } } return 1; }); future.join(); } public AjaxResult stop(String id) { if (map.containsKey(id)) { Process process = map.get(id); log.info("-----stop------>>>" + process); if (null != process) { rtspToMP4.stopRecord(process); return AjaxResult.success(); } } return AjaxResult.error(); } public static void main(String[] args) throws InterruptedException, ParseException, IOException { // CameraUtil cameraUtil = new CameraUtil(); // cameraUtil.closeRecording(); // String s = "/opt/streams/record/flv/42010001541320001116/20230403_235506_000506-22e1523b-d170-11ed-8a42-fa163e4e1e9f.flv"; // String fileStr = s.substring(s.lastIndexOf("/") + 1, s.length()); // String pathStr = s.substring(0, s.lastIndexOf("/")); // System.out.println(fileStr); // System.out.println(pathStr); String s = "20230403235512"; Date sdate = DateUtils.dateTime(DateUtils.YYYYMMDDHHMMSS, s); sdate = DateUtils.addDays(sdate, -1); System.out.println(DateUtils.parseDateToStr(DateUtils.YYYYMMDDHHMMSS, sdate)); } }