在项目中需要接入大华设备,因此我们集成了大华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; } }