Android平臺上集成海康SDK(二)

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方法時傳入不同的碼流類型即可實現切換畫質,注意:切換畫質只支持實時預覽,不支持遠程回放。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章