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