Android平台上集成海康SDK
以上是我之前写的一篇Android平台上集成海康SDK的文章,其中对于Android平台上集成海康SDK、基于海康SDK进行二次开发基本上进行了详细地介绍。
这篇文章,在之前的基础上我对代码进行了优化和封装,我们来看,
/**
* Created by Administrator on 2017/2/28.
* 封装的海康播放库
*/
public class PlayerHikvision {
private static final String TAG = "Hikvision";
public int mLogId = -1;
public int mPort = -1;
public int mPlayId = -1;
public int mPlaybackId = -1;
private SurfaceView surfaceView;
public boolean mStopPlayback;
private Handler handler;
private int chanNo;
public PlayerHikvision(SurfaceView surfaceView, Handler handler) {
this.surfaceView = surfaceView;
this.handler = handler;
}
/**
* 登录设备
* @param ip
* @param port
* @param userName
* @param password
* @param channelNo
*/
private void login(String ip, int port, String userName, String password, int channelNo) {
HCNetSDK.getInstance().NET_DVR_Init(); //初始化海康的Device Network SDK
NET_DVR_DEVICEINFO_V30 oNetDvrDeviceInfoV30 = new NET_DVR_DEVICEINFO_V30(); //设备参数
mLogId = HCNetSDK.getInstance().NET_DVR_Login_V30(ip, port, userName, password, oNetDvrDeviceInfoV30); //调用设备登录接口
LogUtils.i(TAG, "logId:" + mLogId);
if (mLogId == -1) { //登录失败
int errorCode = HCNetSDK.getInstance().NET_DVR_GetLastError(); //得到错误码,并根据错误码得到具体原因
switch (errorCode) {
case 1:
messageFeedback("连接设备失败:设备的用户名或者密码错误"); //设备的用户名或者密码错误
break;
case 7:
messageFeedback("连接设备失败:设备离线或者连接超时"); //连接设备失败:设备离线或者连接超时
break;
default:
messageFeedback("连接设备失败:其他错误"); //连接设备失败:其他错误
break;
}
} else { //成功登录设备,动态换算通道号
NET_DVR_IPPARACFG_V40 net_dvr_ipparacfg_v40 = new NET_DVR_IPPARACFG_V40(); //IP设备资源和IP通道资源配置
HCNetSDK.getInstance().NET_DVR_GetDVRConfig(mLogId, HCNetSDK.NET_DVR_GET_IPPARACFG_V40, 0, net_dvr_ipparacfg_v40); //获取设备设置信息
LogUtils.i(TAG, "模拟通道数:" + net_dvr_ipparacfg_v40.dwAChanNum);
LogUtils.i(TAG, "IP通道数:" + net_dvr_ipparacfg_v40.dwDChanNum);
if (net_dvr_ipparacfg_v40.dwAChanNum == 0 && net_dvr_ipparacfg_v40.dwDChanNum == 0) {
chanNo = channelNo;
} else {
if (channelNo > net_dvr_ipparacfg_v40.dwAChanNum) {
chanNo = (channelNo - net_dvr_ipparacfg_v40.dwAChanNum) + 32;
} else {
chanNo = channelNo;
}
}
}
}
/**
* 实时预览
* @param ip
* @param port
* @param userName
* @param password
* @param streamType
* @param channelNo
*/
public void live(String ip, int port, String userName, String password, int streamType, int channelNo) {
if (mLogId == -1) { //未登录设备,先登录设备
login(ip, port, userName, password, channelNo);
}
if (mLogId != -1) { //已经登录设备,下一步:实时预览
RealPlayCallBack fRealDataCallBack = getRealPlayerCbf(); //得到实时预览回调
if (fRealDataCallBack == null) {
LogUtils.e(TAG, "实时预览回调对象创建失败");
messageFeedback("实时预览失败");
return;
}
NET_DVR_PREVIEWINFO previewInfo = new NET_DVR_PREVIEWINFO(); //实时预览设置
previewInfo.lChannel = chanNo; //通道号
previewInfo.dwStreamType = streamType; //码流类型
previewInfo.bBlocked = 1; //阻塞流获取
mPlayId = HCNetSDK.getInstance().NET_DVR_RealPlay_V40(mLogId, previewInfo, fRealDataCallBack); //调用实时预览接口
if (mPlayId == -1) { //实时预览失败
if (streamType == 1) {
previewInfo.dwStreamType = 0; //尝试换一种码流播放
mPlayId = HCNetSDK.getInstance().NET_DVR_RealPlay_V40(mLogId, previewInfo, fRealDataCallBack);
if (mPlayId == -1) {
LogUtils.e(TAG, "实时预览失败,错误码:" + HCNetSDK.getInstance().NET_DVR_GetLastError());
messageFeedback("实时预览失败");
return;
}
} else {
LogUtils.e(TAG, "实时预览失败,错误码:" + HCNetSDK.getInstance().NET_DVR_GetLastError());
messageFeedback("实时预览失败");
return;
}
}
}
}
/**
* 远程回放
* @param ip
* @param port
* @param userName
* @param password
* @param channelNo
* @param beginYear
* @param beginMonth
* @param beginDay
* @param beginHour
* @param beginMinute
* @param beginSecond
* @param endYear
* @param endMonth
* @param endDay
* @param endHour
* @param endMinute
* @param endSecond
*/
public void playback(String ip, int port, String userName, String password, int channelNo,
int beginYear, int beginMonth, int beginDay, int beginHour, int beginMinute, int beginSecond,
int endYear, int endMonth, int endDay, int endHour, int endMinute, int endSecond) {
if (mLogId == -1) { //未登录设备,先登录设备
login(ip, port, userName, password, channelNo);
}
if (mLogId != -1) { //已经登录设备,下一步:远程回放
PlaybackCallBack fPlaybackCallBack = getPlaybackPlayerCbf(); //得到远程回放回调
if (fPlaybackCallBack == null) {
LogUtils.e(TAG, "远程回放回调对象创建失败");
messageFeedback("远程回放失败");
return;
}
NET_DVR_TIME beginTime = new NET_DVR_TIME();
NET_DVR_TIME endTime = new NET_DVR_TIME();
beginTime.dwYear = beginYear;
beginTime.dwMonth = beginMonth;
beginTime.dwDay = beginDay;
beginTime.dwHour = beginHour;
beginTime.dwMinute = beginMinute;
beginTime.dwSecond = beginSecond;
endTime.dwYear = endYear;
endTime.dwMonth = endMonth;
endTime.dwDay = endDay;
endTime.dwHour = endHour;
endTime.dwMinute = endMinute;
endTime.dwSecond = endSecond;
mPlaybackId = HCNetSDK.getInstance().NET_DVR_PlayBackByTime(mLogId, chanNo, beginTime, endTime); //调用远程回放接口
if (mPlaybackId != -1) {
if (!HCNetSDK.getInstance().NET_DVR_SetPlayDataCallBack(mPlaybackId, fPlaybackCallBack)) {
LogUtils.e(TAG, "设置远程回放回调失败,错误码:" + HCNetSDK.getInstance().NET_DVR_GetLastError());
messageFeedback("远程回放失败");
return;
}
NET_DVR_PLAYBACK_INFO net_dvr_playback_info = null;
if (!HCNetSDK.getInstance().NET_DVR_PlayBackControl_V40(mPlaybackId, PlaybackControlCommand.NET_DVR_PLAYSTART, null, 0, net_dvr_playback_info)) {
LogUtils.e(TAG, "远程回放开始失败,错误码:" + HCNetSDK.getInstance().NET_DVR_GetLastError());
messageFeedback("远程回放失败");
return;
}
mStopPlayback = false;
} else {
LogUtils.e(TAG, "远程回放失败,错误码:" + HCNetSDK.getInstance().NET_DVR_GetLastError());
messageFeedback("远程回放失败");
}
}
}
/**
* 获取录像段文件
* @param ip
* @param port
* @param userName
* @param password
* @param channelNo
* @param beginYear
* @param beginMonth
* @param beginDay
* @param beginHour
* @param beginMinute
* @param beginSecond
* @param endYear
* @param endMonth
* @param endDay
* @param endHour
* @param endMinute
* @param endSecond
* @return
*/
public List<NET_DVR_FINDDATA_V30> getRecordFile(String ip, int port, String userName, String password, int channelNo,
int beginYear, int beginMonth, int beginDay, int beginHour, int beginMinute, int beginSecond,
int endYear, int endMonth, int endDay, int endHour, int endMinute, int endSecond) {
List<NET_DVR_FINDDATA_V30> recordList = new ArrayList<NET_DVR_FINDDATA_V30>();
if (mLogId == -1) { //未登录设备,先登录设备
login(ip, port, userName, password, channelNo);
}
if (mLogId != -1) {
NET_DVR_FILECOND net_dvr_filecond = new NET_DVR_FILECOND(); //被搜索录像文件信息
net_dvr_filecond.lChannel = chanNo; //通道号
net_dvr_filecond.dwFileType = 0xff; //录像文件类型 0xff代表所有
net_dvr_filecond.dwIsLocked = 0xff; //是否锁定 0xff代表所有
net_dvr_filecond.dwUseCardNo = 0; //是否使用卡号搜索
NET_DVR_TIME beginTime = new NET_DVR_TIME();
NET_DVR_TIME endTime = new NET_DVR_TIME();
beginTime.dwYear = beginYear;
beginTime.dwMonth = beginMonth;
beginTime.dwDay = beginDay;
beginTime.dwHour = beginHour;
beginTime.dwMinute = beginMinute;
beginTime.dwSecond = beginSecond;
endTime.dwYear = endYear;
endTime.dwMonth = endMonth;
endTime.dwDay = endDay;
endTime.dwHour = endHour;
endTime.dwMinute = endMinute;
endTime.dwSecond = endSecond;
net_dvr_filecond.struStartTime = beginTime;
net_dvr_filecond.struStopTime = endTime;
int findFileId = HCNetSDK.getInstance().NET_DVR_FindFile_V30(mLogId, net_dvr_filecond); //调用搜索录像文件接口
if (findFileId != -1) { //查询录像文件成功
for (int i = 0; i < 4000; i++) { //最多4000份录像段文件
NET_DVR_FINDDATA_V30 net_dvr_finddata_v30 = new NET_DVR_FINDDATA_V30();
int iRet = HCNetSDK.getInstance().NET_DVR_FindNextFile_V30(findFileId, net_dvr_finddata_v30);
if (-1 == iRet) { //调用失败
break;
} else {
if (iRet == 1000) { //文件信息获取成功
recordList.add(net_dvr_finddata_v30);
} else if (iRet == 1003) { //无更多文件
break;
}
//还有一些其他状态,这里不可任何处理,既不加上该录像段文件,也不跳出循环
}
}
} else {
LogUtils.e(TAG, "查询录像失败,错误码:" + HCNetSDK.getInstance().NET_DVR_GetLastError());
}
}
return recordList;
}
/**
* 停止实时预览
* @param playId
* @param port
*/
public void stopLive(int playId, int port) {
if (playId != -1) {
if (HCNetSDK.getInstance().NET_DVR_StopRealPlay(playId)) {
Player.getInstance().stopSound();
if (Player.getInstance().stop(port)) {
if (Player.getInstance().closeStream(port)) {
if (Player.getInstance().freePort(port)) {
LogUtils.i(TAG, "停止实时预览成功");
} else {
LogUtils.e(TAG, "停止实时预览,调用freePort接口失败,错误码:" + Player.getInstance().getLastError(port));
}
} else {
LogUtils.e(TAG, "停止实时预览,关闭流失败,错误码:" + Player.getInstance().getLastError(port));
}
} else {
LogUtils.e(TAG, "停止实时预览,调用stop接口失败,错误码:" + Player.getInstance().getLastError(port));
}
} else {
LogUtils.e(TAG, "调用停止实时预览接口失败,错误码:" + HCNetSDK.getInstance().NET_DVR_GetLastError());
}
}
}
/**
* 停止远程回放
* @param playbackId
* @param port
*/
public void stopPlayback(int playbackId, int port) {
if (playbackId != -1) {
if (HCNetSDK.getInstance().NET_DVR_StopPlayBack(playbackId)) {
Player.getInstance().stopSound();
if (Player.getInstance().stop(port)) {
if (Player.getInstance().closeStream(port)) {
if (Player.getInstance().freePort(port)) {
mStopPlayback = true;
LogUtils.i(TAG, "停止远程回放成功");
} else {
LogUtils.e(TAG, "停止远程回放,调用freePort接口失败,错误码:" + Player.getInstance().getLastError(port));
}
} else {
LogUtils.e(TAG, "停止远程回放,关闭流失败,错误码:" + Player.getInstance().getLastError(port));
}
} else {
LogUtils.e(TAG, "停止远程回放,调用stop接口失败,错误码:" + Player.getInstance().getLastError(port));
}
} else {
LogUtils.e(TAG, "调用停止远程回放接口失败,错误码:" + HCNetSDK.getInstance().NET_DVR_GetLastError());
}
}
}
/**
* 登出设备
* @param logId
*/
public void logout(int logId) {
if (logId != -1) {
if (HCNetSDK.getInstance().NET_DVR_Logout_V30(logId)) {
LogUtils.i(TAG, "登出设备成功");
} else {
LogUtils.e(TAG, "登出设备失败,错误码:" + HCNetSDK.getInstance().NET_DVR_GetLastError());
}
}
}
private RealPlayCallBack getRealPlayerCbf() {
RealPlayCallBack cbf = new RealPlayCallBack() {
public void fRealDataCallBack(int iRealHandle, int iDataType, byte[] pDataBuffer, int iDataSize) {
processRealData(1, iDataType, pDataBuffer, iDataSize, Player.STREAM_REALTIME);
}
};
return cbf;
}
private PlaybackCallBack getPlaybackPlayerCbf() {
PlaybackCallBack cbf = new PlaybackCallBack() {
@Override
public void fPlayDataCallBack(int iPlaybackHandle, int iDataType, byte[] pDataBuffer, int iDataSize) {
processRealData(1, iDataType, pDataBuffer, iDataSize, Player.STREAM_FILE);
}
};
return cbf;
}
private void processRealData(int iPlayViewNo, int iDataType, byte[] pDataBuffer, int iDataSize, int iStreamMode) {
if (HCNetSDK.NET_DVR_SYSHEAD == iDataType) {
mPort = Player.getInstance().getPort();
if (mPort == -1) {
LogUtils.e(TAG, "获取端口失败,错误码:" + Player.getInstance().getLastError(mPort));
return;
}
LogUtils.i(TAG, "获取端口成功,端口:" + mPort);
Player.getInstance().renderPrivateData(mPort, 0x00000002, 0); //取消显示移动侦测
if (iDataSize > 0) {
if (!Player.getInstance().setStreamOpenMode(mPort, iStreamMode)) {
LogUtils.e(TAG, "设置流打开类型失败");
return;
}
if (!Player.getInstance().openStream(mPort, pDataBuffer, iDataSize, 2 * 1024 * 1024)) {
LogUtils.e(TAG, "打开流失败");
return;
}
if (!Player.getInstance().play(mPort, surfaceView.getHolder())) {
LogUtils.e(TAG, "播放失败");
return;
}
if (!Player.getInstance().playSound(mPort)) {
LogUtils.e(TAG, "播放声音失败");
return;
}
messageFeedback(null);
}
} else {
if (!Player.getInstance().inputData(mPort, pDataBuffer, iDataSize)) {
for (int i = 0; i < 4000 && mPlaybackId >= 0 && !mStopPlayback; i++) {
if (Player.getInstance().inputData(mPort, pDataBuffer, iDataSize)) {
break;
}
if (i % 100 == 0) {
LogUtils.e(TAG, "输入数据失败,错误码:" + Player.getInstance().getLastError(mPort) + ", i:" + i);
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
/**
* 抓图
* @return
*/
public Bitmap capturePicture() {
Bitmap bitmap = null;
if (mPort >= 0) {
Player.MPInteger stWidth = new Player.MPInteger();
Player.MPInteger stHeight = new Player.MPInteger();
if (Player.getInstance().getPictureSize(mPort, stWidth, stHeight)) {
int nSize = 5 * stWidth.value * stHeight.value;
byte[] picBuf = new byte[nSize];
Player.MPInteger stSize = new Player.MPInteger();
if (Player.getInstance().getBMP(mPort, picBuf, nSize, stSize)) {
bitmap = BitmapFactory.decodeByteArray(picBuf, 0, stSize.value);
}
}
}
return bitmap;
}
/**
* 暂停/播放
* @param status 1---暂停;0---播放
*/
public void pauseOrPlay(int status) {
Player.getInstance().pause(mPort, status);
}
private void messageFeedback(String msg) {
Message message = Message.obtain();
message.obj = msg;
handler.sendMessage(message);
}
}
这个类是已经封装好的可以直接拿去用的。这里做几点说明:
1、代码中有一处是动态换算通道号。这是因为在我们业务系统下,我们是在一个Web的设备管理系统中添加上设备,而在这里添加上的设备的通道号是默认从1开始的,1、2、3、4…这种,所以我们在APP中需要动态换算通道号。我们并没有在APP中调用海康SDK的接口去获取通道号。
2、在停止实时预览和停止远程回放的方法中,我们并没有对关闭声音的结果做判断。这是因为我们经过测试发现,关闭声音可能失败,但是实时预览或者远程回放仍然可以停止,并且有些视频就是听不到声音的,对于是否可以听声音,这个国内国外貌似还有不同的政策上的限制,也许是前端设备上的设置导致听不到声音并且关闭声音失败,具体原因不清楚。
3、在PlayerHikvision中我们没有写出切换画质。其实这个很简单,这里就说一下,
切换画质:
我们在调用live方法时传入不同的码流类型即可实现切换画质,注意:切换画质只支持实时预览,不支持远程回放。