功能背景:
项目里面使用到海康威视摄像头录播课堂,之后取出视频分享。
推荐内容
项目环境:
SpringBoot、JDK1.8、IntelliJ IDEA 2020.1、SDK V6.1、FFMPEG、康威视摄像头、录像机。
详细步骤:
一、引入SDK (海康威视 [官网入口] ),下载对应自己服务器版本的SDK,我选择的是《设备网络SDK_Win64 V6.1.6.45_build20210302》。
二、下载SDK解压后,如图:
- Demo示例 :里面有个java Demo可以提供参考。
- 开发文档:设备网络SDK使用手册.chm这个文档帮助大,其它我看不习惯。
- 库文件:dll文件引用。
- 头文件:非java使用观看的文件。
- HCNetSDKCom文件夹必须加载到工程.txt :说明文件。
三、库文件准备,打开SDK解压文件夹找到【库文件】里的HCNetSDK.dll、HCCore.dll、PlayCtrl.dll、SuperRender.dll、AudioRender.dll、HCNetSDKCom文件夹、ssleay32.dll、libeay32.dll、hlog.dll、hpr.dll、zlib1.dll、log4cxx.properties等文件均要加载到程序里面,【HCNetSDKCom文件夹】(包含里面的功能组件dll库文件)需要和HCNetSDK.dll、HCCore.dll一起加载,放在同一个目录下,且HCNetSDKCom文件夹名不能修改。拷贝到D盘自己创建的文件夹内。
四、新创建一个SpringBoot项目,pom文件引入SDK二次开发所需要的jar包,手动引入可以拷贝下载的SDK JAVA Demo里面有。
- 手动引入方式拷贝
CH-HCNetSDKV6.1.6.45_build20210302_win64\Demo示例\4- Java 开发示例\1-ClientDemo\ClientDemo-NetBeansPro
下的 jna.jar、examples.jar - maven
<!--海康威视--> <dependency> <groupId>net.java.jna</groupId> <artifactId>jna</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>net.java.examples</groupId> <artifactId>examples</artifactId> <version>1.0.0</version> </dependency>
五、找到下载JAVA Demo中的HCNetSDK.java文件,拷贝到自己新建的项目里面,修改dll引入路径。
HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("D:\\Hk\\HCNetSDK.dll",HCNetSDK.class);
六、创建类 DVRLogin.java
import lombok.Data; /** * 登录录像机对象 */ @Data public class DVRLogin { /** * 视频服务器ip地址 */ private String ip; /** * 视频服务器端口号 */ private int report; /** * 视频服务器用户名 */ private String userName; /** * 视频服务器密码 */ private String password; public DVRLogin() { } public DVRLogin(String ip, int report, String userName, String password) { this.ip = ip; this.report = report; this.userName = userName; this.password = password; } }
七、创建测试类 VideoDowload.java
import com.sun.jna.NativeLong; import com.sun.jna.ptr.IntByReference; import java.io.File; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import static com.xzl.hk.FfmpegUtil.executeCodecs; public class VideoDowload { private static HCNetSDK hcNetSDK = HCNetSDK.INSTANCE; private NativeLong userId;//用户句柄 private NativeLong loadHandle;//下载句柄 /** * 按时间下载视频 */ private boolean downloadVideo(DVRLogin DVRLogin, Date startTime, Date endTime, String filePath, int channel) { boolean initFlag = hcNetSDK.NET_DVR_Init(); if (!initFlag) { //返回值为布尔值 fasle初始化失败 System.err.println("hksdk(视频)-海康sdk初始化失败!"); return false; } HCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30(); userId = hcNetSDK.NET_DVR_Login_V30(DVRLogin.getIp(), (short) DVRLogin.getReport(), DVRLogin.getUserName(), DVRLogin.getPassword(), deviceInfo); System.out.println("hksdk(视频)-登录海康录像机信息,状态值:" + hcNetSDK.NET_DVR_GetLastError()); long lUserId = userId.longValue(); if (lUserId == -1) { System.err.println("hksdk(视频)-海康sdk登录失败!"); return false; } loadHandle = new NativeLong(-1); if (loadHandle.intValue() == -1) { loadHandle = hcNetSDK.NET_DVR_GetFileByTime(userId, new NativeLong(channel), getHkTime(startTime), getHkTime(endTime), filePath); System.out.println("hksdk(视频)-获取播放句柄信息,状态值:" + hcNetSDK.NET_DVR_GetLastError()); if (loadHandle.intValue() >= 0) { // 判断文件夹是否存在 File files = new File(filePath); if (!files.exists()) { files.mkdirs(); } boolean downloadFlag = hcNetSDK.NET_DVR_PlayBackControl(loadHandle, hcNetSDK.NET_DVR_PLAYSTART, 0, null); int tmp = -1; IntByReference pos = new IntByReference(); while (true) { boolean backFlag = hcNetSDK.NET_DVR_PlayBackControl(loadHandle, hcNetSDK.NET_DVR_PLAYGETPOS, 0, pos); if (!backFlag) {//防止单个线程死循环 return downloadFlag; } int produce = pos.getValue(); if ((produce % 10) == 0 && tmp != produce) {//输出进度 tmp = produce; System.out.println("hksdk(视频)-视频下载进度:" + "==" + produce + "%"); } if (produce == 100) {//下载成功 hcNetSDK.NET_DVR_StopGetFile(loadHandle); loadHandle.setValue(-1); hcNetSDK.NET_DVR_Logout(userId);//退出录像机 System.out.println("hksdk(视频)-退出状态" + hcNetSDK.NET_DVR_GetLastError()); hcNetSDK.NET_DVR_Cleanup(); try { // 视频进行转码 executeCodecs(); File file = new File(filePath); // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除 file.delete(); } catch (Exception e) { e.printStackTrace(); } return true; } if (produce > 100) {//下载失败 hcNetSDK.NET_DVR_StopGetFile(loadHandle); loadHandle.setValue(-1); System.err.println("hksdk(视频)-海康sdk由于网络原因或DVR忙,下载异常终止!错误原因:" + hcNetSDK.NET_DVR_GetLastError()); hcNetSDK.NET_DVR_Logout(userId);//退出录像机 logger.info("hksdk(视频)-退出状态"+hcNetSDK.NET_DVR_GetLastError()); return false; } } } else { System.out.println("hksdk(视频)-下载失败" + hcNetSDK.NET_DVR_GetLastError()); return false; } } return false; } /** * 获取海康录像机格式的时间 */ private HCNetSDK.NET_DVR_TIME getHkTime(Date time) { HCNetSDK.NET_DVR_TIME structTime = new HCNetSDK.NET_DVR_TIME(); String str = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(time); String[] times = str.split("-"); structTime.dwYear = Integer.parseInt(times[0]); structTime.dwMonth = Integer.parseInt(times[1]); structTime.dwDay = Integer.parseInt(times[2]); structTime.dwHour = Integer.parseInt(times[3]); structTime.dwMinute = Integer.parseInt(times[4]); structTime.dwSecond = Integer.parseInt(times[5]); return structTime; } public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); Date startTime = null; Date endTime = null; try { startTime = sdf.parse("20211101113000"); //开始时间 endTime = sdf.parse("20211101113035"); //结束时间 } catch (ParseException e) { e.printStackTrace(); } VideoDowload test = new VideoDowload(); DVRLogin DVRLogin = new DVRLogin("192.168.1.29", 8000, "admin", "abc123456"); int channel = 33;//通道 System.out.print(test.downloadVideo(DVRLogin, startTime, endTime, "D:\\testhk\\test_other.mp4", channel)); try { executeCodecs(); } catch (Exception e) { e.printStackTrace(); } } }
八、创建两个工具类FfmpegUtil.java、StreamGobbler.java。
FfmpegUtil.java,解决Jna抓取的视频文件不能播放问题,视频格式是MPEG-PS,需要转文件。
import java.io.IOException; public class FfmpegUtil { // ffmpeg.exe存放的位置 private static String ffmpegEXE = "D:\\tools\\ffmpeg\\bin\\ffmpeg.exe"; public static void executeCodecs() { // 创建一个List集合来保存转换视频文件为flv格式的命令 // 拼接命令 String command = ffmpegEXE + " -i D:\\testhk\\test_other.mp4 -y -vol 1800 -ar 44100 -r 29.97 -s 1920x1080 D:\\testhk\\demp_other_encan1.mp4"; // 执行命令行 try { Process process = Runtime.getRuntime().exec(command); StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR"); StreamGobbler outGobbler = new StreamGobbler(process.getInputStream(), "STDOUT"); // kick off stderr errorGobbler.start(); outGobbler.start(); try { int i = process.waitFor(); if (i == 0) { System.out.println("进程完成."); } else { System.out.println("进程失败."); } } catch (InterruptedException e) { System.out.println("进程失败!"); e.printStackTrace(); } int i = process.exitValue(); //接收执行完毕的返回值 if (i == 0) { System.out.println("执行完成."); } else { System.out.println("执行失败."); } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { try { executeCodecs(); } catch (Exception e) { e.printStackTrace(); } } }
StreamGobbler.java 解决process.waitFor();
阻塞进程。
import lombok.SneakyThrows; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; /** * 用于处理Runtime.getRuntime().exec产生的错误流及输出流 */ public class StreamGobbler extends Thread { InputStream is; String type; OutputStream os; StreamGobbler(InputStream is, String type) { this(is, type, null); } StreamGobbler(InputStream is, String type, OutputStream redirect) { this.is = is; this.type = type; this.os = redirect; } @SneakyThrows public void run() { InputStreamReader isr = null; BufferedReader br = null; PrintWriter pw = null; try { if (os != null) pw = new PrintWriter(os); isr = new InputStreamReader(is); br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { if (pw != null) pw.println(line); System.out.println(type + ">" + line); } if (pw != null) pw.flush(); } catch (IOException ioe) { ioe.printStackTrace(); } finally { if (pw != null) { pw.close(); } if (br != null) { br.close(); } if (isr != null) { isr.close(); } } } }
注意:上面内容中使用到 ffmpeg 安装教程。