前言
視頻識別的一個項目,前期采用Java調用OpenCV讀取的方案,然后發送到Redis服務器,由於OpenCV采圖像不穩定,需要研究使用海康SDK讀取圖像。由於本人海康SDK的demo已成功運行,這里直接刪除了demo里不需要的東西,demo正常運行也會附到本文。
海康windows64SDK下載
opencv-4-4-0下載
代碼示例下載
1、加載動態鏈接庫dll
下載海康SDK,並放到指定文件夾
HCNetSDK INSTANCE = (HCNetSDK) Native.loadLibrary("D:\\Java\\HCNetSDK\\庫文件\\HCNetSDK.dll", HCNetSDK.class);
PlayCtrl INSTANCE = (PlayCtrl) Native.loadLibrary("D:\\Java\\HCNetSDK\\庫文件\\PlayCtrl.dll", PlayCtrl.class);
這里采用了直接加載dll的方式,沒有用eclipse加載lib文件的方式,HCNetSDK用於登錄、預覽,PlayCtrl用於播放,OpenCV 4.4.0
2、調用邏輯
先調用NET_DVR_Init方法進行初始化,調用NET_DVR_Login_V30進行登錄,調用NET_DVR_RealPlay_V30進行預覽
重點來了
lPreviewHandle = hCNetSDK.NET_DVR_RealPlay_V30(lUserID, m_strClientInfo, fRealDataCallBack, null, true);
這里需要fRealDataCallBack預覽回調,是一個參數,需要自定義一個類去實現一個接口,
case HCNetSDK.NET_DVR_SYSHEAD是預覽時第一次會進入系統頭這個邏輯,相當於配置一下,這里是配置了PlayM4_SetDecCallBackSDK解碼回調,這里需要一個fDecCallBackSDK解碼回調函數,在下面附上
case HCNetSDK.NET_DVR_STREAMDATA:是上面配置完,下一次就會進入這個case語句,這個邏輯是放入碼流數據
class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {
public void invoke(NativeLong lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize, Pointer pUser) {
switch (dwDataType) {
case HCNetSDK.NET_DVR_SYSHEAD: //系統頭
System.out.println(logHead + "FRealDataCallBack 系統頭");
if (!playControl.PlayM4_GetPort(m_lPort)) //獲取播放庫未使用的通道號
{
break;
}
if (dwBufSize > 0) {
if (!playControl.PlayM4_SetDecCallBack(m_lPort.getValue(), fDecCallBack)) //設置解碼回調
{
break;
}
// 設置解碼回調函數 解碼且顯示
// if (!playControl.PlayM4_SetDecCallBackEx(m_lPort.getValue(), fDecCallBack, null, null)) {
// break;
// }
if (!playControl.PlayM4_SetStreamOpenMode(m_lPort.getValue(), PlayCtrl.STREAME_REALTIME)) //設置實時流播放模式
{
break;
}
if (!playControl.PlayM4_OpenStream(m_lPort.getValue(), pBuffer, dwBufSize, 1024 * 1024)) //打開流接口
{
break;
}
if (!playControl.PlayM4_Play(m_lPort.getValue(), null)) //播放開始
{
break;
}
}
case HCNetSDK.NET_DVR_STREAMDATA: //碼流數據
if (!playControl.PlayM4_InputData(m_lPort.getValue(), pBuffer, dwBufSize)) {
break;
}
}
}
}
fDecCallBackSDK解碼回調函數如下,解碼回調函數里面的數據,是sdk軟解碼以后的數據,可以直接拿來處理,不過里面的代碼需要在40ms里處理完畢,不然會產生很大的延遲
class FDecCallBack implements PlayCtrl.DecCallBack {
@Override
public void invoke(NativeLong nPort, ByteByReference pBuffer, NativeLong nSize, PlayCtrl.FRAME_INFO frameInfo, NativeLong nReserved1, NativeLong nReserved2) {
if (++count % 6 == 0) {
try {
handle(pBuffer, nSize.intValue(), frameInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//這樣在回調函數DecCallBack 中可以得到視音頻數據,其中視頻數據是YV12格式的,音頻數據是PCM格式的。
public void handle(ByteByReference pBuffer, int dwBufSize, PlayCtrl.FRAME_INFO frameInfo) {
src = null;
dst = null;
bufferedImage = null;
boolean isLog = new Date().getSeconds() % 10 == 0;
String printStr = null;
if (isLog) {
stopWatch.reset();
stopWatch.start();
}
int width = frameInfo.nWidth.intValue();
int height = frameInfo.nHeight.intValue();
byte[] byteArray = pBuffer.getPointer().getByteArray(0, dwBufSize);
src = new Mat(height + height / 2, width, CvType.CV_8UC1);
src.put(0, 0, byteArray);
if (isLog) {
stopWatch.split();
printStr = "";
printStr += logHead + "put " + stopWatch.getSplitTime() + "\r\n";
}
dst = new Mat(height, width, CvType.CV_8UC3);
Imgproc.cvtColor(src, dst, Imgproc.COLOR_YUV2BGR_YV12);
if (isLog) {
stopWatch.split();
printStr += logHead + "cvtColor " + stopWatch.getSplitTime() + "\r\n";
}
bufferedImage = Mat2BufImg.Mat2BufImg(dst);
if (isLog) {
stopWatch.split();
printStr += logHead + "Mat2BufImg " + stopWatch.getSplitTime() + "\r\n";
}
String imgHex = Mat2BufImg.bufImgToBase64(bufferedImage, "jpg");
if (isLog) {
stopWatch.split();
printStr += logHead + "bufImgToBase64 " + stopWatch.getSplitTime() + "\r\n";
}
Jedis jedis = JedisUtil.getJedis();
jedis.set(ip, imgHex);
JedisUtil.close(jedis);
if (isLog) {
stopWatch.split();
printStr += logHead + "set " + stopWatch.getSplitTime() + "\r\n";
System.out.println(printStr);
}
}
}
3、結尾
大體流程就是這樣子,要不我畫一張流程圖哇,哈哈

