海康威視SDK基于JAVA二次開發
標簽: java
項目背景
項目中用到了海康威視的攝像機視頻服務器。項目要求,要將海康威視的攝像視頻同步按時間至我們自己的服務器,并且在項目中記錄文件信息。
項目環境
- SpringBoot + JDK1.8
- 海康威視設備型號:DS-7608N-K2/8P
引入SDK
首先在海康衛視下載對應的SDK包。
官網下載地址:https://www.hikvision.com/cn/download_61.html
解壓下載的SDK文件,如下圖:
- Demo示例:其中有java的一個demo,大家也可以參考一下
- 開發文檔:包含了海康威視不同設備的SDK對接文檔
- 庫文件:需要部分dll文件引用,通過dll文件去調用海康威視的服務器,需要的文件下文詳細介紹
打開我們的SpringBoot項目
-
首先將demo示例中java例子的HCNetSDK.java文件拷貝至我們項目,項目位置沒有特殊要求。
-
然后將庫文件中的HCNetSDKCom文件夾及下面的所有文件、AudioRender.dll、HCCore.dll、HCNetSDK.dll、PlayCtrl.dll、SuperRender.dll文件放置指定的文件夾
-
修改HCNetSDK.java文件的HCNetSDK加載dll文件的位置。如下:D:\HKSDK\HCNetSDK為dll文件存放的目錄。
HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("D:\\HKSDK\\HCNetSDK", HCNetSDK.class);
- pom文件引入SDK二次開發所需要的jar包。jar包均手動引入,不是通過maven網上下載。官方示例中的javaDemo中有相應的jar包。
<dependency>
<groupId>net.java.jna</groupId>
<artifactId>jna</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>net.java.jna</groupId>
<artifactId>examples</artifactId>
<version>1.0.0</version>
</dependency>
編寫程序代碼
import com.sun.jna.NativeLong;
import com.sun.jna.ptr.IntByReference;
import com.urgentlogistic.util.HCNetSDK;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
/**
* @author ZhangFZ
* @date 2020/4/20 20:23
**/
public class VideoDowload {
private static Logger logger = LoggerFactory.getLogger(VideoDowload.class);
private static HCNetSDK hcNetSDK = HCNetSDK.INSTANCE;
private NativeLong userId;//用戶句柄
private NativeLong loadHandle;//下載句柄
private Timer downloadTimer;
/**
* 按時間下載視頻
*/
private boolean downloadVideo(Dvr dvr, Date startTime, Date endTime, String filePath, int channel) {
boolean initFlag = hcNetSDK.NET_DVR_Init();
if (!initFlag) { //返回值為布爾值 fasle初始化失敗
logger.warn("hksdk(視頻)-海康sdk初始化失敗!");
return false;
}
HCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30();
userId = hcNetSDK.NET_DVR_Login_V30(dvr.getDvrip(), (short) dvr.getDvrport(), dvr.getDvrusername(), dvr.getDvrpassword(), deviceInfo);
logger.info("hksdk(視頻)-登錄海康錄像機信息,狀態值:" + hcNetSDK.NET_DVR_GetLastError());
long lUserId = userId.longValue();
if (lUserId == -1) {
logger.warn("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);
logger.info("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;
logger.info("hksdk(視頻)-視頻下載進度:" + "==" + produce + "%");
}
if (produce == 100) {//下載成功
hcNetSDK.NET_DVR_StopGetFile(loadHandle);
loadHandle.setValue(-1);
hcNetSDK.NET_DVR_Logout(userId);//退出錄像機
logger.info("hksdk(視頻)-退出狀態" + hcNetSDK.NET_DVR_GetLastError());
//hcNetSDK.NET_DVR_Cleanup();
return true;
}
if (produce > 100) {//下載失敗
hcNetSDK.NET_DVR_StopGetFile(loadHandle);
loadHandle.setValue(-1);
logger.warn("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("20200422170000"); //開始時間
endTime = sdf.parse("20200422180000"); //結束時間
} catch (ParseException e) {
e.printStackTrace();
}
VideoDowload test = new VideoDowload();
Dvr dvr = new Dvr("http://192.168.0.167",80,"admin","123456");
int channel = 33;//通道
System.out.print(test.downloadVideo(dvr, startTime, endTime, "D:\\testhk\\test.mp4", channel));
}
}
Dvr類:
import lombok.Data;
/**
* @author ZhangFZ
* @date 2020/4/21 9:32
**/
@Data
public class Dvr {
// 視頻服務器ip地址
private String dvrip;
// 視頻服務器端口號
private int dvrport;
// 視頻服務器用戶名
private String dvrusername;
// 視頻服務器密碼
private String dvrpassword;
public Dvr(String dvrip, int dvrport, String dvrusername, String dvrpassword) {
this.dvrip = dvrip;
this.dvrport = dvrport;
this.dvrusername = dvrusername;
this.dvrpassword = dvrpassword;
}
}
注意事項:
- 自己的服務器信息請手動修改,保存目錄等信息請手動修改
- channel通道號是32起,比如你的通道為1,這里需要寫33,原因咨詢海康威視的技術。
- 這里只是一個簡單的demo,具體操作根據自己業務的實際情況來。
視頻無法播放的問題
因為你的音頻不是mpeg4容器支持的音頻格式。通過mediainfo分析可知,你的音頻格式是pcm_alaw,并且還有一行警告信息:
FileExtension_Invalid : mpeg mpg m2p vob vro pss evo
你的視頻格式應該是MPEG-PS格式,但是后綴卻是mp4,其實是一個非法的MP4。mediainfo還會告訴你合法的后綴應該是上述那幾個。由于你的音頻格式根本不被瀏覽器支持,但是視頻格式是被瀏覽器支持的(H.264/AVC)。那么解決方案其實也簡單,抽調音頻即可。參考ffmpeg命令行(不轉碼,直接復制流):
ffmpeg -i demo.mp4 -c copy -an demp_enc.mp4
速度很快,使用-an參數屏蔽掉音頻流,將封裝格式轉為mp4。再次用mediainfo查看,格式已經顯示MPEG-4,是一個標準的mp4容器封裝了,在當今主流的瀏覽器都能直接播放。
下載ffmpeg解碼工具官網下載ffmpeg,解壓安裝找到ffmpeg.exe。
添加一下工具類
// ffmpeg.exe存放的位置
private static String ffmpegEXE = "D:\\software\\ffmpg\\bin\\ffmpeg.exe";
//ffmpeg -i demo.mp4 -c copy -an demp_enc.mp4
public static void convetor(String videoInputPath, String videoOutPath) throws Exception {
List<String> command = new ArrayList<String>();
command.add(ffmpegEXE);
command.add("-i");
command.add(videoInputPath);
command.add("-c");
command.add("copy");
command.add("-an");
command.add(videoOutPath);
ProcessBuilder builder = new ProcessBuilder(command);
Process process = null;
try {
process = builder.start();
} catch (IOException e) {
// TODO Auto-generated catch block
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();
}
}
下載視頻成功后,調用該方法進行轉碼,修改VideoDowload.java代碼為以下:
if (produce == 100) {//下載成功
hcNetSDK.NET_DVR_StopGetFile(loadHandle);
loadHandle.setValue(-1);
hcNetSDK.NET_DVR_Logout(userId);//退出錄像機
logger.info("hksdk(視頻)-退出狀態" + hcNetSDK.NET_DVR_GetLastError());
hcNetSDK.NET_DVR_Cleanup();
Media media = new Media();
media.setDisasterId(disasterId);
media.setType(MediaType.VIDEO);
media.setCreateDate(new Date());
media.setUploadTime(new Date());
media.setAttachmentName("z" + fileName);
media.setIsDeleted(false);
media.setUpdate_rhtx(1);
media.setFlag("OTHER");
media.setNewsFeedId(0);
String http_path = restUrlLocalhost;
media.setUrl(http_path + "/urgentlogistic/file/mp4/" + newDate + "/z" +fileName);
mediaService.create(media);
try {
// 視頻進行轉碼
convetor(filePath + fileName,filePath + "z" +fileName);
File file = new File(filePath + fileName);
// 如果文件路徑所對應的文件存在,并且是一個文件,則直接刪除
file.delete();
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
運行完就會多一個視頻文件,然后就可以用通用播放器播放這個新的視頻了,至于老的視頻任憑自己需求處理掉就行啦
最后在附上官方開發人員的郵箱 [email protected]
如果對您有幫助,請點個贊,關注下小編再走喲,有什么問題可以在評論區留言,經常在線,看到一定回復。
本章到此結束。后續文章會陸續更新,文檔會同步在CSDN和GitHub保持同步更新。
CSDN:https://blog.csdn.net/qq_34988304/category_8820134.html
Github文檔:https://github.com/hack-feng/Java-Notes/tree/master/src/note/SpringCloud
智能推薦
關于海康威視網絡攝像機二次開發問題
最近一個星期一直在研究海康威視的網絡攝像機二次開發問題,糾結了很久。今天終于走通了一部分,記下來希望能夠幫助到更多人。現在是用的 VS2013 + opencv 進行測試。 首先推薦兩篇對我幫助很大的博文: http://blog.csdn.net/wanghuiqi2008/...
海康威視攝像頭web二次開發(angular)
海康威視官網有提供二次開發的web開發包,主要由js,html組成,但是開發包更新迭代太快了,甲方的攝像頭還是老版本,幸好甲方提供了它們的開發包(內涵demo,doc)。 測試環境 在web包里面里面有demo>cn文件夾,內含js、html、css文件,直接點擊html文件,在瀏覽器中打開,可以直接使用,輸入ip,port,username,password,點擊登錄,點擊預覽,可以查看對...
freemarker + ItextRender 根據模板生成PDF文件
1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...
電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!
Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...
猜你喜歡
requests實現全自動PPT模板
http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...
Linux C系統編程-線程互斥鎖(四)
互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...
統計學習方法 - 樸素貝葉斯
引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...
styled-components —— React 中的 CSS 最佳實踐
https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...