在项目中需要接入大华设备,因此我们集成了大华Android版本SDK。与海康SDK类似,它也是分为NetSDK和PlaySDK。
前者用于设备连接、网络通信;后者用于解码、播放。
在APP中,关于大华设备,我们实现了以下功能:添加设备、获取通道、实时预览、远程回放。
接下来,我们一个一个来分析如何实现。
首先,登录设备,用到的方法是INetSDK.LoginEx(String pchDVRIP, int wDVRPort, String pchUserName, String pchPassword, int nSpecCap, Object pCapParam, NET_DEVICEINFO lpDeviceInfo, Integer nError)
具体代码实现如下:
首先,我们需要做一些准备工作:
INetSDK.LoadLibrarys();
DeviceDisConnect disConnect = new DeviceDisConnect();
INetSDK.Init(disConnect); //初始化SDK,在所有的SDK函数之前调用
/**
* 设置连接设备超时时间和尝试次数
* @param nWaitTime 客户端与设备的连接等待时间,毫秒级
* @param nTryTimes 连接次数
*/
INetSDK.SetConnectTime(4000, 2);
NET_PARAM stNetParam = new NET_PARAM(); //NET_PARAM 该类用于设置登录时的相关参数
stNetParam.nWaittime = 10000; //等待超时时间(毫秒为单位)
stNetParam.nSearchRecordTime = 10000; //按时间查询录像文件的超时时间(毫秒为单位)
INetSDK.SetNetworkParam(stNetParam); //设置登陆网络环境
NET_DEVICEINFO deviceInfo = new NET_DEVICEINFO(); //设备信息
Integer error = new Integer(0);
DeviceReConnect reConnect = new DeviceReConnect();
INetSDK.SetAutoReconnect(reConnect); //设置断线重连成功回调函数,设置后SDK内部断线自动重连
DeviceSubDisConnect subDisConnect = new DeviceSubDisConnect();
INetSDK.SetSubconnCallBack(subDisConnect); //设置动态子连接断线回调函数,目前SVR设备的监视和回放是短连接的
INetSDK.SetDVRMessCallBack(new Test_CB_fMessageCallBack()); //设置消息回调函数
/**
* 设备未连接上
*/
public class DeviceDisConnect implements CB_fDisConnect {
@Override
public void invoke(long lLoginID, String pchDVRIP, int nDVRPort) {
return;
}
}
/**
* 设备连接恢复
*/
public class DeviceReConnect implements CB_fHaveReConnect {
@Override
public void invoke(long lLoginID, String pchDVRIP, int nDVRPort) {}
}
/**
* 动态子连接断开
*/
public class DeviceSubDisConnect implements CB_fSubDisConnect {
@Override
public void invoke(int emInterfaceType, boolean bOnline,
long lOperateHandle, long lLoginID) {}
}
/**
* 消息回调函数
*/
class Test_CB_fMessageCallBack implements CB_fMessageCallBack {
@Override
public boolean invoke(int lCommand, long lLoginID, Object obj, String pchDVRIP, int nDVRPort) {
if (12295 == lCommand) {
DEV_PLAY_RESULT stResult = (DEV_PLAY_RESULT) obj;
}
return true;
}
}
接下来,调用登录方法:
long loginHandle = INetSDK.LoginEx(ipAddress, port,
userName, password, mSpecCap, null, deviceInfo, error);
mSpecCap为设备支持的能力,这里值为20。
如果loginHandle的值不为0,那么登录成功。
获取通道
m_speedCtrl = false;
m_schedule = 0;
SDK_DEV_ENABLE_INFO stEnableInfo = new SDK_DEV_ENABLE_INFO();
if (INetSDK.QuerySystemInfo(loginHandle, SDK_SYS_ABILITY.ABILITY_DEVALL_INFO, stEnableInfo, 3000)) {
if (stEnableInfo.IsFucEnable[SDK_DEV_FUNC.EN_PLAYBACK_SPEED_CTRL] != 0) {
m_speedCtrl = true;
}
m_schedule = stEnableInfo.IsFucEnable[SDK_DEV_FUNC.EN_SCHEDULE];
}
stCfgCapAlarm = new CFG_CAP_ALARM_INFO();
char szOutBuffer[] = new char[10240];
Integer stError = new Integer(0);
boolean bQN = INetSDK.QueryNewSystemInfo(loginHandle, FinalVar.CFG_CAP_ALARM, 0, szOutBuffer, stError, 5000);
if (bQN) {
bQN = INetSDK.ParseData(FinalVar.CFG_CAP_ALARM, szOutBuffer, stCfgCapAlarm, null); //解析查询到的配置信息
if (!bQN) {
Log.e("dahua", "INetSDK.ParseData CFG_CAP_ALARM error");
}
} else {
Log.e("dahua", "INetSDK.QueryNewSystemInfo CFG_CAP_ALARM error");
}
mCount = deviceInfo.byChanNum;
if (-1 == deviceInfo.byChanNum) {
SDK_PRODUCTION_DEFNITION stDef = new SDK_PRODUCTION_DEFNITION();
boolean bRet = INetSDK.QueryProductionDefinition(loginHandle, stDef, 3000);
if (bRet) {
mCount = stDef.nVideoInChannel + stDef.nMaxRemoteInputChannels;
}
}
最终得到的mCount就是通道数。
对大华摄像头进行实时预览,用到的方法是INetSDK.RealPlayEx(int lLoginID, int nChannelID, int rType)
用之前获取的loginHandle,
long lRealHandle = INetSDK.RealPlayEx(loginHandle, channelNo, streamType);
如果lRealHandle不为0,则成功,接下来:
mPort = IPlaySDK.PLAYGetFreePort();
boolean bOpenRet = IPlaySDK.PLAYOpenStream(mPort, null, 0, 1024 * 1024 * 2) == 0 ? false : true;
if (bOpenRet) {
boolean bPlayRet = IPlaySDK.PLAYPlay(mPort, surfaceView) == 0 ? false : true;
if (bPlayRet) {
boolean bSuccess = IPlaySDK.PLAYPlaySoundShare(mPort) == 0 ? false : true;
/*if (!bSuccess) {
IPlaySDK.PLAYStop(mPort);
IPlaySDK.PLAYCloseStream(mPort);
return false;
}
if (-1 == nCurVolume) {
nCurVolume = IPlaySDK.PLAYGetVolume(mPort);
} else {
IPlaySDK.PLAYSetVolume(mPort, nCurVolume);
}*/
} else {
IPlaySDK.PLAYCloseStream(mPort);
return false;
}
} else {
//IPlaySDK.PLAYCloseStream(mPort);
return false;
}
return true;
如果最终return true,接下来,我们继续:
public class TestRealDataCallBackEx implements CB_fRealDataCallBackEx {
public int port;
public TestRealDataCallBackEx(int port) {
this.port = port;
}
/**
* 网络断线回调函数
* @param lRealHandle 实时监视ID
* @param dwDataType 回调出来的数据类型
* @param pBuffer 回调数据,根据数据类型的不同每次回调不同的长度的数据,除类型0,其他数据类型都是按帧,每次回调一帧数据
* @param dwBufSize 回调数据参数结构体,根据不同的类型,参数结构也不一致
* @param param 回调数据的长度,根据不同的类型,长度也不同(单位字节)
*/
@Override
public void invoke(long lRealHandle, int dwDataType, byte[] pBuffer, int dwBufSize, int param) {
if (dwDataType == 0) {
IPlaySDK.PLAYInputData(port, pBuffer, pBuffer.length);
}
}
}
public class TestVideoDataCallBack implements IPlaySDKCallBack.fDemuxCBFun {
public FileOutputStream outputStream;
public TestVideoDataCallBack(FileOutputStream outputStream) {
this.outputStream = outputStream;
}
@Override
public void invoke(int nPort, byte[] pOrgBuffer, int nOrgLen, byte[] pBuffer, int nLen,
IPlaySDKCallBack.DEMUX_INFO stInfo, long dwUser) {
try {
if (null != outputStream) {
outputStream.write(pBuffer);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
TestRealDataCallBackEx m_callbac = new TestRealDataCallBackEx(mPort);
TestVideoDataCallBack m_VideoCallback = new TestVideoDataCallBack(m_Fout);
if (lRealHandle != 0) {
INetSDK.SetRealDataCallBackEx(lRealHandle, m_callbac, 1);
}
停止实时预览:
/**
* 停止实时预览
* @param port
* @param lRealHandle
*/
public void stopLive(int port, long lRealHandle) {
IPlaySDK.PLAYStop(port);
IPlaySDK.PLAYStopSoundShare(port);
IPlaySDK.PLAYCloseStream(port);
INetSDK.StopRealPlayEx(lRealHandle);
}
对大华摄像头进行录像回放,我们这里是采用按照时间进行回放,用到的方法是INetSDK.PlayBackTimeEx(long lLoginID, int nChannelID, NET_TIME startTime, NET_TIME stopTime, CB_fDowmLoadPosCallBack posUser, CB_fDataCallBack dataUser)
具体实现代码如下:
先做一些准备工作:
NET_TIME stTimeStart = new NET_TIME();
NET_TIME stTimeEnd = new NET_TIME();
getPlaybackTime(stTimeStart, stTimeEnd, bgYear, bgMonth, bgDay, bgHour, bgMinute, bgSecond,
endYear, endMonth, endDay, endHour, endMinute, endSecond);
public void getPlaybackTime(NET_TIME stTimeStart, NET_TIME stTimeEnd,int bgYear,int bgMonth,int bgDay,int bgHour,int bgMinute,int bgSecond,
int endYear,int endMonth,int endDay,int endHour,int endMinute,int endSecond) {
stTimeStart.dwYear = bgYear;
stTimeStart.dwMonth = bgMonth;
stTimeStart.dwDay = bgDay;
stTimeStart.dwHour = bgHour;
stTimeStart.dwMinute = bgMinute;
stTimeStart.dwSecond = bgSecond;
stTimeEnd.dwYear = endYear;
stTimeEnd.dwMonth = endMonth;
stTimeEnd.dwDay = endDay;
stTimeEnd.dwHour = endHour;
stTimeEnd.dwMinute = endMinute;
stTimeEnd.dwSecond = endSecond;
}
public class TestfDataCallBack implements CB_fDataCallBack {
@Override
public int invoke(long lPlaybackHandle, int dwDataType, byte buffer[], int dwBufferSize) {
if (0 == dwDataType) {
return IPlaySDK.PLAYInputData(nPort, buffer, buffer.length);
} else if ( dwDataType == 2 ) {
//m_PlayView.drawColor(pBuffer);
} else if ( dwDataType == 3 ) {
//m_PlayView.drawColor(pBuffer);
}
return 0;
}
}
public class TestDownLoadPosCallBack implements CB_fDownLoadPosCallBack {
@Override
public void invoke(long lPlayHandle, int dwTotalSize, int dwDownLoadSize) {
Log.i("playback",dwTotalSize+"");
if (-1 == dwDownLoadSize) {
//m_sbPbByTime.setProgress(dwTotalSize);
new Thread(new Runnable() {
@Override
public void run() {
if (0 != lPlaybackHandle) {
stopPlayBackDahua();
}
}
}).start();
} else {
//m_sbPbByTime.setProgress(dwDownLoadSize);
}
}
}
TestfDataCallBack m_callback = new TestfDataCallBack();
TestDownLoadPosCallBack posUser = new TestDownLoadPosCallBack();
long lPlaybackHandle = INetSDK.PlayBackByTimeEx(loginHandle, channelNo, stTimeStart,
stTimeEnd, posUser, m_callback);
如果lPlaybackHandle不为0,则继续:
IPlaySDK.PLAYSetPlayedTimeEx(nPort, 0);
IPlaySDK.PLAYResetBuffer(nPort, 1); //清码流分析库
IPlaySDK.PLAYResetBuffer(nPort, 3); //清播放队列
boolean bOpenRet = IPlaySDK.PLAYOpenStream(nPort, null, 0, 1024*1024*2) == 0 ? false : true;
IPlaySDK.PLAYSetStreamOpenMode(nPort, Constants.STREAME_FILE);
if (bOpenRet) {
boolean bPlayRet = IPlaySDK.PLAYPlay(nPort, surfaceView) == 0 ? false : true;
if (bPlayRet) {
boolean bSuccess = IPlaySDK.PLAYPlaySoundShare(nPort) == 0 ? false : true;
/*if(!bSuccess)
{
IPlaySDK.PLAYStop(nPort);
IPlaySDK.PLAYCloseStream(nPort);
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "Failed to start playback";
handler.sendMessage(msg);
return;
} else {
}*/
loadFinish = true;
Message msg = Message.obtain();
msg.what = 2;
handler.sendMessage(msg);
} else {
IPlaySDK.PLAYCloseStream(nPort);
return;
}
} else {
return;
}
停止回放:
public void stopPlayBackDahua() {
if (lPlaybackHandle != 0) {
IPlaySDK.PLAYStop(nPort);
IPlaySDK.PLAYCloseStream(nPort); //在PLAYStop之后调用
INetSDK.StopPlayBack(lPlaybackHandle);
lPlaybackHandle = 0;
}
}