package com.persagy.cameractl.controller; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestBody; import com.persagy.cameractl.service.HCNetSDKWindows; import com.persagy.cameractl.utils.Camera; import com.persagy.cameractl.utils.OtherTools; import com.persagy.cameractl.utils.ResultClass; import com.sun.jna.NativeLong; import com.sun.jna.ptr.IntByReference; import cn.hutool.core.date.DateUtil; //@RestController public class TestController { public static Logger logger = LoggerFactory.getLogger(TestController.class); private volatile boolean isInit = false; private volatile HCNetSDKWindows hcNetSdk; //@RequestMapping(value = "/test", method = RequestMethod.POST) public ResultClass hello(@RequestBody Camera camera) { ResultClass returnResult = new ResultClass(); String targetFile = OtherTools.getVideoFilePath(); returnResult.name = false; if (targetFile.equals("")) { returnResult.reason = "回放文件名称生成失败"; return returnResult; } logger.info("playback path: " + targetFile); File mp4File = new File(targetFile); String targetFileName = mp4File.getName(); File fileParent = mp4File.getParentFile(); String targetFilePath = fileParent.getPath(); // SDK初始化 boolean init = this.init(); if (!init) { returnResult.reason = "SDK初始化失败"; return returnResult; } // 文件下载,这里不使用原文件,因为还需要转码 String downloadFile = targetFilePath + "/_" + targetFileName; String downloadVideo = this.downloadVideo(camera, downloadFile); if (!"true".equals(downloadVideo)) { returnResult.reason = downloadVideo; return returnResult; } try { logger.info("-------- start transcoding --------"); Map data = new HashMap(4); convetorVideo(downloadFile, targetFile); data.put("url", OtherTools.playMp4RootUrl + OtherTools.getMp4NamePrefix(targetFileName)); returnResult.resultData = data; } catch (Exception e) { e.printStackTrace(); } returnResult.name = true; returnResult.reason = "回放文件生成成功"; return returnResult; } /** * 按时间进行视频下载,实现回放 * * @param Camera 请求参数 * @param filePath 文件路径,包含文件名称及后缀 * @param channel 通道号 * @return */ private String downloadVideo(Camera camera, String filePath) { // 注册设备 -1表示失败,其他值表示返回的用户ID值。接口返回失败请调用NET_DVR_GetLastError获取错误码,通过错误码判断出错原因。 HCNetSDKWindows.NET_DVR_DEVICEINFO_V30 m_strDeviceInfo = new HCNetSDKWindows.NET_DVR_DEVICEINFO_V30(); NativeLong lUserID = hcNetSdk.NET_DVR_Login_V30(camera.cameraIp, (short) camera.cameraPort, camera.userName, camera.password, m_strDeviceInfo); // 注册失败 if (lUserID.longValue() == -1) { int errCode = hcNetSdk.NET_DVR_GetLastError(); // 返回失败时的错误码 return "设备注册失败,失败码:" + errCode; } // 准备文件下载 NativeLong loadHandle = hcNetSdk.NET_DVR_GetFileByTime(lUserID, new NativeLong(camera.channel), getHkTime(camera.startDateStr), getHkTime(camera.endDateStr), filePath); logger.info("hksdk playback file status: " + loadHandle.longValue()); if (loadHandle.longValue() >= 0) { // 判断文件夹是否存在 File files = new File(filePath); if(!files.exists()){ files.mkdirs(); } // 发送回放开始的信号 hcNetSdk.NET_DVR_PlayBackControl(loadHandle, HCNetSDKWindows.NET_DVR_PLAYSTART, 0, null); IntByReference pos = new IntByReference(); while (true) { boolean backFlag = hcNetSdk.NET_DVR_PlayBackControl(loadHandle, HCNetSDKWindows.NET_DVR_PLAYGETPOS, 0, pos); // 回放下载异常,直接跳出 if (!backFlag) { logger.warn("hksdk download exception"); break; } int produce = pos.getValue(); if (produce > 0) { logger.info("hksdk download progress: " + produce + "%"); } if (produce == 100) {//下载成功 hcNetSdk.NET_DVR_StopGetFile(loadHandle); hcNetSdk.NET_DVR_Logout(lUserID); logger.info("hksdk exit status: " + hcNetSdk.NET_DVR_GetLastError()); //hcNetSdk.NET_DVR_Cleanup(); // 这里不进行释放 return "true"; } if (produce > 100) { //下载失败,异常终止,这时返回true hcNetSdk.NET_DVR_StopGetFile(loadHandle); logger.info("hksdk exit status: " + hcNetSdk.NET_DVR_GetLastError()); hcNetSdk.NET_DVR_Logout(lUserID); return "true"; } } } int net_DVR_GetLastError = hcNetSdk.NET_DVR_GetLastError(); logger.error("hksdk download failed: " + net_DVR_GetLastError); return "文件下载失败,失败码:" + net_DVR_GetLastError; } /** * 初始化SDK * * @return */ private boolean init() { if (!isInit) { // 初始化 hcNetSdk = HCNetSDKWindows.INSTANCE; isInit = hcNetSdk.NET_DVR_Init(); } return isInit; } /** * 获取海康录像机格式的时间 */ private HCNetSDKWindows.NET_DVR_TIME getHkTime(String timeParam) { Date time = DateUtil.parse(timeParam); HCNetSDKWindows.NET_DVR_TIME dvrTime = new HCNetSDKWindows.NET_DVR_TIME(); dvrTime.dwYear = DateUtil.year(time); dvrTime.dwMonth = DateUtil.month(time) + 1; dvrTime.dwDay = DateUtil.dayOfMonth(time); dvrTime.dwHour = DateUtil.hour(time, true); dvrTime.dwMinute = DateUtil.minute(time); dvrTime.dwSecond = DateUtil.second(time); return dvrTime; } /** * 文件转码 * * @param videoInputFile * @param videoOutFile * @throws Exception */ public static void convetor(String videoInputFile, String videoOutFile) throws Exception { File sourceFile = new File(videoInputFile); // 把源MP4转为页面上可播放的MP4 Runtime run = Runtime.getRuntime(); Process p = run.exec("ffmpeg -i \"" + videoInputFile + "\" -c copy -y \"" + videoOutFile + "\""); // 读取标准输入流、输出流,防止进程阻塞 // 标准输入流(必须写在 waitFor 之前) String inputStreamStr = OtherTools.consumeInputStream(p.getInputStream()); // 标准错误流(必须写在 waitFor 之前) String errStreamStr = OtherTools.consumeInputStream(p.getErrorStream()); int retCode = p.waitFor(); if (retCode != 0) { String errStr = errStreamStr != null ? errStreamStr : inputStreamStr; logger.error("ffmpeg转换mp4时失败,错误信息:" + errStr); } p.destroy(); sourceFile.delete(); } /** * 文件转码 * * @param videoInputFile * @param videoOutFile * @throws Exception */ @SuppressWarnings("unused") public static void convetorVideo(String videoInputFile, String videoOutFile) throws Exception { List command = new ArrayList(); command.add("ffmpeg"); command.add("-i"); command.add(videoInputFile); command.add("-c"); command.add("copy"); command.add("-an"); command.add(videoOutFile); ProcessBuilder builder = new ProcessBuilder(command); Process process = null; try { process = builder.start(); } catch (IOException e) { e.printStackTrace(); } // 使用这种方式会在瞬间大量消耗CPU和内存等系统资源,所以这里我们需要对流进行处理 InputStream errorStream = process.getErrorStream(); InputStreamReader inputStreamReader = new InputStreamReader(errorStream); BufferedReader br = new BufferedReader(inputStreamReader); String line = ""; while ((line = br.readLine()) != null) { } if (br != null) { br.close(); } if (inputStreamReader != null) { inputStreamReader.close(); } if (errorStream != null) { errorStream.close(); } //File sourceFile = new File(videoInputFile); //sourceFile.delete(); } public static void main(String[] args) throws Exception { //convetor("C:\\Users\\25694\\Desktop\\testffmepg\\sss.mp4", "C:\\Users\\25694\\Desktop\\testffmepg\\demo.mp4"); convetorVideo("C:\\Users\\25694\\Desktop\\testffmepg\\sss.mp4", "C:\\Users\\25694\\Desktop\\testffmepg\\demoss.mp4"); } }