国标云台控制状态

1.基本概念

    国标联网系统的信息传输、交换、控制方面的都是通过SIP服务器负责通讯得,SIP负责信令流逐级转发。其中最重要的一部分就是和摄像机进行信令交互。

    像安全注册、实时视音频点播、历史视音频的回放等应用的会话控制采用IETFRFC3261规定的Register、Invite等请求和响应方法实现,历史视音频回放控制采用SIP扩展协议IETFRFC2976 规定的INFO方法实现,前端设备控制、信息査询、报警事件通知和分发等应用的会话控制采用SIP 扩展协议IETFRFC3428规定的Message方法实现,SIP 消息应支持基于UDP和TCP的传输互联的系统平台及设备不应向对方的SIP端口发送应用无关消息,避免应用无关消息占用系统平台及设备的SIP消息处理资源。本标准基于IETFRFC3261等基础性协议,进行监控联网各项业务功能的规定,本标准中各项功能如有特殊规定应遵循本标准,否则应遵循IETFRFC3261等引用协议。

1.1前端设备控制协议格式

字节字节1字节2字节3字节4字节5字节6字节7字节8
含义A5H组合码1地址指令数据1数据2组合码2校验码

各字节定义如下:

  • 字节1:令的首字节为 A5H.
  • 字节2:组合码高位是版本信息,低4位是校验位。本标准的版本号是1.0,版本信息为 0H。校验位=(享节1的高4位十字节1的低4位十字节2的高4位)%16
  • 字节3:地址的低8亿
  • 字节 4:指令码。
  • 字节5、6:数据1和数据2。
  • 字节7:组合码2,高4位是数据3,低4位是地址的高4位;在后续叙述中,没有特别指明的高4位,表示该4位与所指定的功能无关。
  • 字节8:校验码,为前面的第1~7字节的算术和的低8位,即算术和对256取模后的结果字节8=(字节1十字节2字节3十字节4十字节5+字节6字节7)%256。地址范围000H~FFFH(即0~4095),其中000H地址作为广播地址。注:前端设备控制中,不使用字节3和字节7的低4位地址码,使用前端设备控制消息体中的(DeviceID)统一编码标识控制的前端设备。

1.2ptz指令

     PTZ指令见表 A.4,其中Bit5和 Bit4不应同时为1,Bit3和Bit2不应同时为1;Bitl和 Bit0 不应同时为1。镜头变倍指令、云台上下指令、云台左右指令三者可以组合。

1.3 FI指令

   FI指令见表A.6,其中 Bit3和Bit2不应同时为1,Bit1和Bit0不应同时为1;光圈控制和聚焦控制的指令可以组合。

2.前端代码实现

vue template

<template>
    <div id="ptz-control" v-if="showDialog" v-on:mouseover="drag('ptz-control','wonderMain',true)" ref="videoCloudCheck" :class="[className]">
        <div class="video-cloud-check-content" >
            <div class="win-dialog-title">
                <span class="ptztab" :class="{'active': currentTab=='ptz'}" @click="changeTab('ptz')">云台控制</span>
                <span v-show="isShowPresetAndCruise">
                    <span class="vline"></span>
                    <span class="ptztab" :class="{'active': currentTab=='preset'}" @click="changeTab('preset')">预置位</span>
                    <span class="vline"></span>
                    <span class="ptztab" :class="{'active': currentTab=='cruise'}" @click="changeTab('cruise')">巡航</span>
                </span>
                <span class="close iconfont i-f_a_close" @click="close"></span>
            </div>
            <div class="win-dialog-neck"></div>
            <!--云台控制开始-->
            <div class="view ptz" v-if="currentTab=='ptz'">
                <div class="dir-control">
                    <span class="cmd vertical dir right" @mousedown="ptzCtrlMouseDown(0)" @mouseup="ptzCtrlMouseUp(0)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd dir rightup" @mousedown="ptzCtrlMouseDown(1)" @mouseup="ptzCtrlMouseUp(1)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd dir up" @mousedown="ptzCtrlMouseDown(2)" @mouseup="ptzCtrlMouseUp(2)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd dir leftup" @mousedown="ptzCtrlMouseDown(3)" @mouseup="ptzCtrlMouseUp(3)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd vertical dir left" @mousedown="ptzCtrlMouseDown(4)" @mouseup="ptzCtrlMouseUp(4)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd dir leftdown" @mousedown="ptzCtrlMouseDown(5)" @mouseup="ptzCtrlMouseUp(5)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd dir down" @mousedown="ptzCtrlMouseDown(6)" @mouseup="ptzCtrlMouseUp(6)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd dir rightdown" @mousedown="ptzCtrlMouseDown(7)" @mouseup="ptzCtrlMouseUp(7)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd zoom up adjust" title="焦距增大" @mousedown="ptzCtrlMouseDown(11, 1)" @mouseup="ptzCtrlMouseUp(11, 1)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd zoom down adjust" title="焦距减小" @mousedown="ptzCtrlMouseDown(11, -1)" @mouseup="ptzCtrlMouseUp(11, -1)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd focus up adjust" title="焦点增大" @mousedown="ptzCtrlMouseDown(12, 1)" @mouseup="ptzCtrlMouseUp(12, 1)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd focus down adjust" title="焦点减小" @mousedown="ptzCtrlMouseDown(12, -1)" @mouseup="ptzCtrlMouseUp(12, -1)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd iris up adjust" title="光圈增大" @mousedown="ptzCtrlMouseDown(10, 1)" @mouseup="ptzCtrlMouseUp(10, 1)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                    <span class="cmd iris down adjust" title="光圈减小" @mousedown="ptzCtrlMouseDown(10, -1)" @mouseup="ptzCtrlMouseUp(10, -1)" v-btnPer="'CAMERA_ptzCtrl'">
                    </span>
                </div>
                <div class="ptz-speed">
                    <div class="control-speed">
                        <span class="item">
                            控制速度:
                        </span>
                        <div class="speedSlider">
                            <xui-slider v-model="speed" :options="sliderOptions" @change="changeHandle" v-btnPer="'CAMERA_ptzCtrl'"></xui-slider>
                        </div>
                    </div>
                    <div class="equipment">
                        <span class="choose">
                            <xui-select v-model="equip" :disabled="!isOn" :options="equipOptions" @change="changeSelect" v-btnPer="'CAMERA_ptzCtrl'"></xui-select>
                        </span>
                        <span class="switch">
                            <label>开关:</label>
                            <xui-switch v-model="isOn" class="switch-input" size="normal" @change="changeSwitch" v-btnPer="'CAMERA_ptzCtrl'"></xui-switch>
                        </span>
                    </div>
                </div>
            </div>
            <!--云台控制结束-->
            <!--预置位开始-->
            <div class="view preset" v-if="currentTab=='preset'">
                <div style="display:none;">
                    <xui-button color="primary" size="mini" @click="addPreset">新增</xui-button>
                </div>
                <div class="head">
                    <span class="preset-index">编号</span>
                    <span class="preset-img">图片</span>
                    <span class="preset-name">&nbsp;&nbsp;&nbsp;&nbsp;标题</span>
                    <span class="preset-do">操作</span>
                </div>
                <xui-scroll class="preset-content">
                    <ul class="preset-list">
                        <li v-for="(item,index) in presetsList" :key="index">
                            <span class="preset-index num">{{index+1}}</span>
                            <span class='preset-img'><img :src="getImgUrl(item.imageUrl)" @dblclick="zooImg(item.imageUrl, item.name)"/></span>
                            <span class='preset-name' :title="item.name">{{item.name}}</span>
                            <span class='preset-do'>
                                <span class='preset-use' @click="presetUse(item)"></span>
                                <span class='preset-delete' @click="presetDelete(item)"></span>
                            </span>
                        </li>
                    </ul>
                </xui-scroll>
                <div class="hline"></div>
            </div>
            <!--预置位结束-->
            <!--巡航计划开始-->
            <div class="view cruise" v-if="currentTab=='cruise'">
                <div class="auto-content" v-if="isShowCruiseLists">
                    <div class="auto-title" v-if="cruisesList.length >0">
                        <span class="l-index">编号</span>
                        <span class="l-name">名称</span>
                        <span class="l-controller">操作</span>
                    </div>
                    <xui-scroll class="show-cruise-list" v-if="cruisesList.length >0">
                        <ul>
                            <li class="auto-list common-list" v-for="(item,index) in cruisesList" :key="index">
                                <span class="l-index">{{(index+1)}}</span>
                                <span class="l-name too-long" :title="item.name">{{item.name}}</span>
                                <span class="l-controller">
                                    <xui-switch v-model="item.status" :ctx="item" size="mini" @change="switchCruise(item)"></xui-switch>
                                    <i class="common-icon edit" @click="editCruise(item)"></i>
                                    <i class="common-icon del" @click="delCruise(item)"></i>
                                </span>
                            </li>
                        </ul>
                    </xui-scroll>
                    <span v-else class="no-data">暂无巡航数据</span>
                </div>
                <xui-button color="primary" size="mini" v-if="isShowCruiseLists" @click="showAddCruise">新增巡航</xui-button>
                <div class="add-content add-auto" v-if="isShowAddCruise">
                    <div class="n-title clearfix">
                        <span class="add-new" title="新增自动巡航预置位" @click="addCruiseList"></span>
                        <span class="go-back" @click="goBack">返回</span>
                    </div>
                    <div class="n-content">
                        <div class="auto-title common-title">
                            <span class="l-index">预置位标题</span>
                            <span class="l-name">停留时间(秒)</span>
                            <span class="l-controller">操作</span>
                        </div>
                        <xui-scroll class="auto-ul">
                            <ul>
                                <li class="auto-list" v-for="(item,index) in mockList" :key="index">
                                    <!-- 选择已有预置位 -->
                                    <div class="pre-name">
                                        <xui-select v-model="item.presetId" :options="selectOptions" class="preset-select"></xui-select>
                                    </div>
                                    <div class='pre-time'>
                                        <xui-input v-model="item.internalTime" @blur="validate('internalTime', item.internalTime)" :options="inputOptions"></xui-input>
                                    </div>
                                    <div class='pre-controller'>
                                        <i class="common-icon up" title="上移" v-if="index !== 0" @click="moveUp(index,item)"></i>
                                        <i class="common-icon down" title="下移" v-if="index !== (mockList.length-1)" @click="moveDown(index,item)"></i>
                                        <i class="common-icon del" title="删除" @click="deleteLi(index)"></i>
                                    </div>
                                </li>
                            </ul>
                        </xui-scroll>
                        <div class="new-cruise-info">
                            <div class="title-name">
                                <span class="cruise-title">名&nbsp;&nbsp;&nbsp;称:</span>
                                <xui-input v-model="cruiseName" :options="cruiseNameOptions" class="cruiseName"></xui-input>
                            </div>
                            <div class="auto-point">
                                <span class="after-down">回位点:</span>
                                <xui-select v-model="homingPresetId" :options="selectOptions" @change="changePreset" class="preset-select"></xui-select>
                            </div>
                            <div class="save-or-cancel">
                                <xui-button color="primary" size="mini" style="margin-right: 10px" @click="saveCruise">保存</xui-button>
                                <xui-button color="ghost" size="mini" @click="cancelCruise">取消</xui-button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <!--巡航计划结束-->
            
        </div>
     <iframe scroll="none" src="about:blank" class="video-cloud-check-iframe"></iframe>
    </div>
</template>

javascript

export default {
    props: {
        options: {}
    },
    mixins: [DialogPollify],
    data() {
        return {
            statusCache:[],
            showDialog: false,
            currentTab: "ptz", //可选值ptz,preset,cruise
            className: "",
            sliderOptions: {
                min: 1,
                max: 15,
                step: 1,
                sliderWidth: 220,
                range: false
            },
            speed: 10,
            equipOptions: {
                multiple: false,
                clearable: false,
                filter: false,
                placeholder: "请选择",
                pickerClass: 'equipment-select',
                data: [
                    {
                        text: "雨刷",
                        value: 17
                    },
                    {
                        text: "灯光",
                        value: 18
                    }
                ]
            },
            equip: 17,
            isOn: true,
            cameraInfo: null,
            _index: 0,
            H5Player: null,
            presetsList: [],
            cruisesList: [],
            isShowCruiseLists: true,
            isShowAddCruise: false,
            selectOptions: {
                placeholder: "选择预置位",
                multiple: false,
                clearable: true,
                filter: false,
                data: [],
                pickerClass: 'position-select'
            },
            inputOptions: {
                type: "text",
                clearable: false
            },
            cruiseNameOptions: {
                type: "text",
                placeholder: "请输入巡航名称",
                clearable: true
            },
            homingPresetId: null,
            mockList: [{
                "presetId": "",
                "internalTime": 5
            }],
            cruiseName: "",
            cruiseId: "",
            isShowPresetAndCruise: true,
            playerInstance: {}
        };
    },
    computed: {},
    methods: {
        /**
         * 确定当前摄像机通道和播放窗口
         */
        getPlayWindow(){
            return{
                index:this._index,
                channelNo:this._channel.channelNo
            }
        },
        getImgUrl: function (src) {
            // return "/vdtimg/" + src;
               return Toolkits.getUrl(src, 0, false, true, true);
        },
        zooImg(url, title) {
            window.VueExpress.$structImage.show({
                image: url,
                name: title
                //imgBase64: pic
            });
        },
        show(options, index, className, isShowPresetAndCruise) {
            if (!options || !options.cameraList) {
                return;
            }
            this.showDialog = true;
            this.playerInstance = options;
            this.cameraInfo = options.cameraList[index].deviceInfo;
            this.speed = options.cameraList[index].windowPtzSpeed || 10;
            //存储控制通道
            this._channel = Toolkits.getPtzChannel(this.cameraInfo.channelList);
            this._index = index;
            //云台状态记录
            this.statusCache[index]={};
            this.statusCache[index].status="open";
            this.H5Player = options.h5PlayerC;
            //样式
            this.className = className || "";
            if (typeof isShowPresetAndCruise === "boolean") {
                this.isShowPresetAndCruise = isShowPresetAndCruise;
            }
            this.currentTab = "ptz";
            var CAMERA_ptzCtrl = $permission.getPermissionMap()["CAMERA_ptzCtrl"];
            if (!CAMERA_ptzCtrl) {
                //不具有云台控制权限
                console.log("不具有云台控制权限");
            }
        },
        close() {
            this.showDialog = false;
            this.className = "";
             //云台状态记录
            this.statusCache[this._index].status="close";
            this.destoryInstance();
        },
        //弹出框拖拽事件
        drag: function (imgId, parentId, isLimitedDrag) {
            Toolkits.drag(imgId, parentId, isLimitedDrag);
        },
        changeTab: function (tab) {
            this.currentTab = tab;
            //如果是预置位面板,则需要加载预置位列表
            if (tab === "preset") {
                this.showPreset();
            }
            //如果是计划巡航面板,则需要加载计划巡航列表
            if (tab === "cruise") {
                this.isShowCruiseLists = true;
                this.isShowAddCruise = false;
                this.$nextTick(() => {
                    this.showCruise();
                });
            }
        },
        changeHandle: function (v) {
            this.speed = v;
            this.playerInstance.cameraList[this._index].speed = v;
            this.playerInstance.setPtzSpeed(this._index,v)
        },
        changeSwitch: function (v) {
            this.isOn = v;
        },
        changeSelect: function (v) {
            // if(!this.isOn) return;
            var self = this;
            if (!self.cameraInfo || self.cameraInfo.channelList.length == 0) {
                return;
            }
            //组装参数并发送请求
            var data = {
                channelNo: this._channel.channelNo,
                cmd: v,
                param: this.isOn ? 1 : 0,
                cameraId: this._channel.cameraId
            };
            STORE.setPTZDir(data);
        },
        ptzCtrlMouseDown: function (cmd, param) {
            // if(!this.isOn) return;
            var self = this;
            //收集参数
            if (typeof (param) !== "number") {
                param = this.speed;
            }
            if (!self.cameraInfo || self.cameraInfo.channelList.length == 0) {
                return;
            }

            var data = {
                channelNo: this._channel.channelNo,
                cmd: cmd,
                param: param,
                cameraId: this._channel.cameraId
            };
            STORE.setPTZDir(data);
        },
        ptzCtrlMouseUp: function (cmd) {
            var self = this;
            if (!self.cameraInfo || self.cameraInfo.channelList.length == 0) {
                return;
            }
            //发送停止命令
            setTimeout(function () {
                STORE.setPTZDir({
                    channelNo: self._channel.channelNo,
                    cmd: cmd,
                    param: 0,
                    cameraId: self._channel.cameraId
                });
            }, 200);
        },
        showPreset: function () {
            var self = this;
            if (!self.cameraInfo || self.cameraInfo.channelList.length == 0) {
                return;
            }
            STORE.getPresets(this._channel.channelNo).then((res) => {
                self.presetsList = res;
            });
        },
        presetUse: function (item) {
            STORE.callPreset(item.id).then((res) => {
                if (res) {
                    notify.success("调用预置位成功");
                } else {
                    notify.error("调用预置位失败");
                }
            });
        },
        presetDelete: function (item) {
            // this.$confirm("确定要删除该预置位吗?", "删除提示", {
            //     confirmButtonText: "确定",
            //     cancelButtonText: "取消",
            //     type: "warning"
            // }).then(() => {
            //     STORE.hasCruiseByPresetId(item.id).then((res) => {
            //         if (res) {
            //             notify.error("该预置位存在于巡航中,请先删除巡航再删除此预置位!");
            //         } else {
            //             STORE.deletePreset(item.id).then((res) => {
            //                 if (res) {
            //                     notify.success("删除预置位成功!");
            //                     this.showPreset();
            //                 } else {
            //                     notify.error("删除预置位失败!");
            //                 }
            //             });
            //         }
            //     });
            // })
            $confirm("确定要删除该预置位吗?").then( () => {
                STORE.hasCruiseByPresetId(item.id).then((res) => {
                    if (res) {
                        notify.error("该预置位存在于巡航中,请先删除巡航再删除此预置位!");
                    } else {
                        STORE.deletePreset(item.id).then((res) => {
                            if (res) {
                                notify.success("删除预置位成功!");
                                this.videoPresetRefresh()
                                this.showPreset();
                            } else {
                                notify.error("删除预置位失败!");
                            }
                        });
                    }
                });

            })
        },
        addPreset: function () {
            var self = this;
            var imgStr = self.H5Player.capturePicture(self._index, "png", 300, 0);
            var num = Math.floor(Math.random() * 10 + 1);
            if (self.cameraInfo.channelList.length == 0) {
                return;
            }
            var param = {
                "cameraId": self.cameraInfo.id,
                "channelNo": this._channel.channelNo,
                "imageStream": imgStr,
                "isRestoration": 0,
                "name": "我是预置位" + num,
                "server": {
                    "apsType": 0,
                    "id": this._channel.id,
                    "ip": this._channel.ip,
                    "password": this._channel.password,
                    "port": this._channel.port,
                    "userName": this._channel.userName
                },
                "stopTime": 5
            }
            STORE.createPreset(param).then((res) => {
                if (res) {
                    this.showPreset();
                }
            });
        },
        showCruise: function () {
            if (!this.cameraInfo || this.cameraInfo.channelList.length == 0) {
                return;
            }
            STORE.getCruiseByCameraId(this.cameraInfo.id).then((res) => {
                if (res.length > 0) {
                    this.cruisesList = res.map(item=>{item.status=!!item.status;return item});
                } else {
                    this.cruisesList = [];
                }
            });
        },
        showAddCruise: function () {

            var self = this;
            if (!self.cameraInfo || self.cameraInfo.channelList.length == 0) {
                return;
            }
            self.cruiseId = "";
            //首先查询预置位列表,填充selectOptions.data数据
            STORE.getPresets(this._channel.channelNo).then((res) => {
                self.presetsList = res;
                self.selectOptions.data = self.presetsList.map((item) => {
                    return {
                        text: item.name,
                        value: item.id
                    }
                });
                //切换新增面板
                self.isShowAddCruise = true;
                self.isShowCruiseLists = false;
                //清空数据
                self.mockList = [];
                self.homingPresetId = null;
                self.cruiseName = "";
            });
        },
        addCruiseList: function () {
            var data = {
                "presetId": null,
                "internalTime": 5
            };
            this.mockList.push(data);
        },
        changePreset: function (v) {
            this.homingPresetId = v;
        },
        saveCruise: function () {
            var self = this;
            if (!self.cameraInfo || self.cameraInfo.channelList.length == 0) {
                return;
            }
            let validate = true;
            this.mockList.map((item, pos) => {
                item.sortNo = pos;
                if (!this.validate("internalTime", item.internalTime)) validate = false;
            });
            if (!validate) return;
            if (!self.cruiseName) {
                notify.warn("请输入巡航名称");
                return;
            }
            if (self.cruiseName.length > 20) {
                notify.warn("巡航名称不能超过20个字符");
                return;
            }
            var param = {
                "cameraId": self.cameraInfo.id,
                "channelNo": this._channel.channelNo,
                "cprList": self.mockList,
                "homingPresetId": self.homingPresetId, //回归点的预置位id
                "id": self.cruiseId ? self.cruiseId : "",
                "name": self.cruiseName,
                "status": self.cruiseId?+self.status:0
            };
            console.log("param", param);
            //编辑
            if (self.cruiseId) {
                STORE.updateCruise(param).then((res) => {
                    if (res) {
                        notify.success("编辑自动巡航成功!");
                        self.cruiseId = "";
                        //切换新增面板
                        self.isShowAddCruise = false;
                        self.isShowCruiseLists = true;
                        self.showCruise();
                    } else {
                        notify.warn("编辑自动巡航失败!");
                    }
                });
            } else {
                STORE.addCruise(param).then((res) => {
                    if (res) {
                        notify.success("新增自动巡航成功!");
                        self.cruiseId = "";
                        //切换新增面板
                        self.isShowAddCruise = false;
                        self.isShowCruiseLists = true;
                        self.showCruise();
                    } else {
                        notify.warn("新增自动巡航失败!");
                    }
                });
            }
        },
        cancelCruise: function () {
            //切换新增面板
            this.isShowAddCruise = false;
            this.isShowCruiseLists = true;
        },
        moveUp: function (index, item) {
            var pre = this.mockList[index - 1];
            this.mockList.splice(index - 1, 2, item, pre);
        },
        moveDown: function (index, item) {
            var next = this.mockList[index + 1];
            this.mockList.splice(index, 2, next, item);
        },
        deleteLi: function (index) {
            this.mockList.splice(index, 1);
        },
        validate: function (type, val) {
            if (type === "internalTime" && !$regexp.isPositiveIntegers(val)) {
                $tip.warn("停留时间须为正整数");
                return false;
            }
            return true;
        },
        goBack: function () {
            //切换新增面板
            this.isShowAddCruise = false;
            this.isShowCruiseLists = true;
        },
        editCruise: function (item) {
            var self = this;
            if (!self.cameraInfo || self.cameraInfo.channelList.length == 0) {
                return;
            }
            //#97994
            if(item.status){ notify.warn("正在巡航,请先关闭巡航,然后编辑");return}
            //debugger
            //首先查询预置位列表,填充selectOptions.data数据
            if (self.selectOptions.data.length === 0) {
                STORE.getPresets(this._channel.channelNo).then((res) => {
                    self.presetsList = res;
                    self.selectOptions.data = self.presetsList.map((item) => {
                        return {
                            text: item.name,
                            value: item.id
                        }
                    });
                });
            }
            self.cruiseName = item.name;
            self.homingPresetId = item.homingPresetId;
            self.mockList = IX.clone(item.cprList);
            self.cruiseId = item.id;
            self.status = item.status;
            //切换新增面板
            self.isShowAddCruise = true;
            self.isShowCruiseLists = false;
        },
        delCruise: function (item) {
            //没有在巡航
            if (!item.status) {
                $confirm("确定要删除该巡航吗?").then(() => {
                    STORE.deleteCruise(item.id).then((res) => {
                        notify.success("删除巡航成功");
                        this.showCruise();
                      
                    });
                })
            } else {
                notify.warn("正在巡航中,清先关闭巡航,然后删除");
            }
        },
        switchCruise: function (item) {
            var param = {
                cruiseId: item.id,
                status: +item.status
            };
            STORE.switchOnOffCruise(param).then((res) => {
               
                //this.playerInstance.setWindowPtz(this._index, true);  //云台
                notify.success("修改巡航状态成功");
                // 判断有没有开启的任务,如果有就锁定,没有就不锁
                let hasTask = this.cruisesList.some(i => i.status);
                if (hasTask) {
                    STORE.videoCloudLock({cameraId: this.cameraInfo.id}).then(res => {
                      this.H5Player.setWindowPtz(this._index, true);  //<E4><BA><91><E5><8F><B0>
                      this.H5Player.showOrHideToolbar(this._index, true, '29');
                      this.H5Player.showOrHideToolbar(this._index, false, '28');
                    })
                } else {
                    STORE.videoCloudUnlock({cameraId: this.cameraInfo.id}).then(res => {
                       this.H5Player.setWindowPtz(this._index, true);  //<E4><BA><91><E5><8F><B0>
                       this.H5Player.showOrHideToolbar(this._index, true, '28');
                       this.H5Player.showOrHideToolbar(this._index, false, '29');
                    })
                }
            })
        }
    }
};

css 

<style lang="less">
@imgPath: "/viua/assets/player";
.position-select{
    z-index: 1000;
}
.equipment-select{
    z-index: 1000;
}
#ptz-control.left-bottom {
    bottom: 0px;
    left: 0px;
}
#ptz-control.right-bottom {
    bottom: 0px;
    right: 0px;
}
#ptz-control.left-top {
    top: 0px;
    left: 0px;
}
#ptz-control.right-top {
    top: 0px;
    right: 0px;
}
#ptz-control {
    position: fixed;
    z-index: 1000;
    width: 282px;
    height: 330px;
    border: solid 1px var(--border-color-gray);
    background-color: var(--bg-fill-normal);
    border-radius: 10px;
    cursor: move;
    bottom: 25%;
    left: 50%;
    .video-cloud-check-content, .video-cloud-check-iframe {
        width: 100%;
        height: 100%;
        position: absolute;
    }
    .video-cloud-check-content {
        z-index: 1;
    }
    .video-cloud-check-iframe {
        z-index: 0;
    }
    .win-dialog-title {
        font-family: "Microsoft YaHei" !important;
        height: 43px;
        line-height: 43px;
        padding-left: 10px;
        padding-top: 0px;
        padding-right: 3px;
        font-size: 13px;
        .close {
            float: right;
            margin-right: 10px;
            cursor: pointer;
        }
        .ptztab {
            display: inline-block;
            text-align: center;
            padding-left: 10px;
            padding-right: 10px;
            *display: inline;
            zoom: 1;
            *width: 60px;
        }
        & .ptztab:hover {
            border-bottom: solid 2px var(--prod-normal-color);
            color: var(--prod-normal-color);
            cursor: default;
        }
        & .ptztab.active {
            border-bottom: solid 2px var(--prod-normal-color);
            color: var(--prod-normal-color);
        }
        .vline {
            width: 2px;
            height: 18px;
            border-left: solid 1px var(--border-color-gray);
            border-right: solid 1px var(--border-color-gray);
        }
    }
    .win-dialog-neck {
        font-size: 0;
        height: 1px;
        line-height: 1px;
        border-top: solid 1px var(--border-color-gray);
        border-bottom: solid 1px var(--border-color-gray);
    }
    .ptz {
        .dir-control {
            display: block;
            width: auto;
            height: 134px;
            background: url("@{imgPath}/ptz-disable.png") no-repeat 0 0;
            margin-top: 30px;
            margin-left: 20px;
            position: relative;
            border: solid 0 red;
            *width: 240px;
            span {
                display: inline-block;
                background: url("@{imgPath}/ptz-normal.png") no-repeat 0 0;
                position: absolute;
                width: 15px;
                height: 15px;
                border: solid 0 red;
                cursor: pointer;
                &:hover {
                    background: url("@{imgPath}/ptz-hover.png") no-repeat 0 0;
                }
                &:active {
                    background: url("@{imgPath}/ptz-active.png") no-repeat 0 0;
                }
                &.dir.up {
                    left: 59px;
                    top: 21px;
                    background-position: -59px -21px;
                }
                &.dir.leftup {
                    left: 35px;
                    top: 36px;
                    background-position: -35px -36px;
                }
                &.dir.left {
                    left: 23px;
                    top: 61px;
                    background-position: -23px -61px;
                }
                &.dir.leftdown {
                    left: 35px;
                    top: 87px;
                    background-position: -35px -87px;
                }
                &.dir.down {
                    left: 60px;
                    top: 101px;
                    background-position: -60px -101px;
                }
                &.dir.rightdown {
                    left: 86px;
                    top: 87px;
                    background-position: -86px -87px;
                }
                &.dir.right {
                    left: 101px;
                    top: 61px;
                    background-position: -101px -61px;
                }
                &.dir.rightup {
                    left: 86px;
                    top: 36px;
                    background-position: -86px -36px;
                }
                &.zoom.up.adjust {
                    left: 152px;
                    top: 45px;
                    background-position: -152px -45px;
                }
                &.zoom.down.adjust {
                    left: 152px;
                    top: 76px;
                    background-position: -152px -76px;
                }
                &.focus.up.adjust {
                    left: 183px;
                    top: 44px;
                    background-position: -183px -44px;
                }
                &.focus.down.adjust {
                    left: 183px;
                    top: 75px;
                    background-position: -183px -75px;
                }
                &.iris.up.adjust {
                    left: 214px;
                    top: 44px;
                    background-position: -214px -44px;
                }
                &.iris.down.adjust {
                    left: 214px;
                    top: 75px;
                    background-position: -214px -75px;
                }
            }
        }
        .ptz-speed {
            margin-left: 25px;
            margin-top: 10px;
            .control-speed {
                color: var(--part-font-color);
                font-size: 12px;
            }
        }
        .equipment {
            margin-top: 10px;
            .choose {
                display: inline-block;
                width: 100px;
                color: var(--part-font-color);
                font-size: 12px;
            }
            .switch {
                display: inline-block;
                width: 130px;
                height: 18px;
                text-align: center;
                margin-left: 20px;
                position: relative;
                top: -10px;
                label {
                    color: var(--part-font-color);
                }
                .switch-input {
                    position: relative;
                    top: -2px;
                    left: -5px;
                    background: var(--switch-normal-track-bg-color);
                    &:checked {
                        background: var(--switch-normal-track-bg-color);
                    }
                }
            }
        }
    }
    .preset {
        .head {
            height: 32px;
            line-height: 32px;
            text-align: center;
            *width: 260px;
            *padding-left: 0;
            *text-align: left;
            *font-size: 0;
            span {
                display: inline-block;
                color: var(--part-font-color);
                text-align: center;
                *display: inline;
                zoom: 1;
                *font-size: 12px;
                &.preset-index {
                    width: 40px;
                }
                &.preset-img {
                    width: 64px;
                }
                &.preset-name {
                    width: 90px;
                    text-align: left;
                    *text-align: center;
                }
                &.preset-do {
                    width: 50px;
                    text-align: left;
                    *text-align: right;
                    padding-left: 0;
                }
            }
        }
        .preset-content {
            height: 235px;
            border-top: solid 1px var(--border-color-gray);
            ul {
                margin: 10px 0 0 0;
                padding: 0;
                border: solid 0 red;
                li {
                    height: 60px;
                    line-height: 60px;
                    border-bottom: solid 1px var(--border-color-gray);
                    white-space: nowrap;
                    span {
                        vertical-align: middle;
                        display: inline-block;
                        border: solid 0 red;
                        text-align: center;
                        &.preset-index {
                            width: 40px;
                            color: var(--part-font-color);
                        }
                        &.preset-img {
                            width: 64px;
                            height: 50px;
                            position: relative;
                            top: 5px;
                        }
                        &.preset-img img {
                            vertical-align: middle;
                            padding: 0;
                            margin: -27px 0 0 0;
                            width: 64px;
                            height: 50px;
                        }
                        &.preset-name {
                            width: 80px;
                            max-width: 80px;
                            margin: 0px 5px;
                            overflow: hidden;
                            text-overflow: ellipsis;
                            white-space: nowrap;
                        }
                        &.preset-do {
                            width: 50px;
                        }
                        &.preset-use {
                            width: 18px;
                            height: 18px;
                            background: url("@{imgPath}/preset-use.png")
                                no-repeat 0 0;
                            cursor: pointer;
                        }
                        &.preset-use:hover {
                            background-position: -22px 0;
                        }
                        &.preset-delete {
                            width: 18px;
                            height: 18px;
                            background: url("@{imgPath}/preset-delete.png")
                                no-repeat 0 0;
                            cursor: pointer;
                        }
                        &.preset-delete:hover {
                            background-position: -22px 0;
                        }
                    }
                }
            }
        }
    }
    .cruise {
        height: calc(~"100% - 45px");
        text-align: center;
        .auto-content {
            height: calc(~"100% - 30px");
            .auto-title {
                display: inline-block;
                width: 90%;
                height: 30px;
                line-height: 30px;
                white-space: nowrap;
                border-bottom: 1px solid var(--border-color-gray);
                color: var(--part-font-color);
                span {
                    display: inline-block;
                    white-space: nowrap;
                }
                .l-index {
                    width: 25px;
                }
                .l-name {
                    width: 150px;
                }
                .l-controller {
                    width: 85px;
                }
            }
            .show-cruise-list {
                height: 220px;
                width: 90%;
                margin: auto;
                .auto-list {
                    line-height: 30px;
                    border-bottom: 1px solid var(--border-color-gray);
                    table-layout: fixed;
                    display: table;
                    span {
                        display: table-cell;
                    }
                    .l-index {
                        width: 25px;
                    }
                    .l-name {
                        width: 130px;
                        overflow: hidden;
                        text-overflow: ellipsis;
                        white-space: nowrap;
                        display: inline-block;
                        vertical-align: middle;
                    }
                    .l-controller {
                        width: 85px;
                        .common-icon {
                            cursor: pointer;
                            display: inline-block;
                        }
                        .edit {
                            position: relative;
                            top: 3px;
                            height: 16px;
                            width: 18px;
                            margin-left: 5px;
                            margin-right: 5px;
                            background: url("@{imgPath}/edit.png") no-repeat
                                scroll 0 center;
                            &:hover {
                                margin-right: 5px;
                                background: url("@{imgPath}/edit.png") no-repeat
                                    scroll -22px center;
                            }
                        }
                        .del {
                            position: relative;
                            top: 3px;
                            height: 16px;
                            width: 14px;
                            background: url("@{imgPath}/del.png") no-repeat
                                scroll 0 center;
                            &:hover {
                                background: url("@{imgPath}/del.png") no-repeat
                                    scroll -22px center;
                            }
                        }
                    }
                }
            }
            .no-data {
                display: inline-block;
                margin-top: 115px;
                color: var(--part-font-color);
            }
        }
        .add-content {
            height: 100%;
            .n-title {
                height: 30px;
                line-height: 30px;
                border-bottom: 1px solid var(--border-color-gray);
                .add-new {
                    cursor: pointer;
                    background: url("@{imgPath}/add.png") no-repeat 0 0;
                    display: inline-block;
                    height: 16px;
                    width: 16px;
                    margin-left: 205px;
                    margin-top: 7px;
                    &:hover {
                        background: url("@{imgPath}/add.png") no-repeat scroll -21px
                            center;
                    }
                }
                .go-back {
                    cursor: pointer;
                    color:var(--prod-normal-color);
                    font-weight: bold;
                    background: url("@{imgPath}/back.png") no-repeat 0px 10px;
                    padding-left: 10px;
                    margin-left: 10px;
                    float: left;
                }
            }
            .n-content {
                border-top: 1px solid var(--border-color-gray);
                .auto-title {
                    display: inline-block;
                    height: 30px;
                    line-height: 30px;
                    white-space: nowrap;
                    border-bottom: 1px solid var(--border-color-gray);
                    width: 93%;
                    margin: 0 10px;
                    span {
                        display: inline-block;
                        white-space: nowrap;
                        text-align: center;
                    }
                    .l-index {
                        width: 40%;
                    }
                    .l-name {
                        width: 40%;
                    }
                    .l-controller {
                        width: 20%;
                    }
                }
                .auto-ul {
                    height: 105px;
                    .auto-list {
                        display: flex;
                        align-items: center;
                        justify-content: space-around;
                        border-bottom: 1px solid var(--border-color-gray);
                        padding: 2px 0;
                    }
                    .pre-name {
                        display: inline-block;
                        flex: 2;
                        text-align: center;
                        .preset-select {
                            width: 100px;
                            max-width: 100px;
                            background-color: var(--bg-fill-normal);
                        }
                        .xui-select-inputwrap {
                            line-height: normal;
                        }
                        .xui-single-text {
                            position: relative;
                            top: 4px;
                            max-width: 64px;
                            overflow: hidden;
                            text-overflow: ellipsis;
                            white-space: nowrap;
                        }
                        .select-trigger-icon {
                            top: 4px;
                        }
                    }
                    .pre-time {
                        display: inline-block;
                        flex: 1;
                        .xui-input-style {
                            width: 40px;
                        }
                    }
                    .pre-controller {
                        display: inline-block;
                        flex: 1;
                        .common-icon {
                            cursor: pointer;
                            display: inline-block;
                            margin-left: 4px;
                        }
                        .up {
                            height: 16px;
                            width: 14px;
                            background: url("@{imgPath}/up.png") no-repeat
                                scroll 0 center;
                            &:hover {
                                background: url("@{imgPath}/up.png") no-repeat
                                    scroll -14px center;
                            }
                        }
                        .down {
                            height: 16px;
                            width: 14px;
                            background: url("@{imgPath}/down.png") no-repeat
                                scroll 0 center;
                            &:hover {
                                background: url("@{imgPath}/down.png") no-repeat
                                    scroll -14px center;
                            }
                        }
                        .del {
                            height: 16px;
                            width: 14px;
                            background: url("@{imgPath}/del.png") no-repeat
                                scroll 0 center;
                            &:hover {
                                background: url("@{imgPath}/del.png") no-repeat
                                    scroll -22px center;
                            }
                        }
                    }
                }
                .new-cruise-info {
                    padding-top: 10px;
                    border-top: 1px solid var(--border-color-gray);
                    .title-name, .auto-point {
                        padding-right: 10px;
                        display: flex;
                        align-items: center;
                        span{
                            flex: 1;
                        }
                        div{
                            flex: 3;
                        }
                    }
                    .title-name {
                        .cruise-title {
                            &:before{
                                    content: "*";
                                    color: rgb(255, 0, 0);
                                    padding: 0px 2px;
                            }
                        }
                    }
                    .auto-point {
                        margin-top: 5px;
                        .preset-select {
                            background-color: var(--bg-fill-normal);
                            text-align: left;
                        }
                    }
                    .save-or-cancel {
                        position:absolute;
                        bottom:5px;
                        width: 100%;
                        display: flex;
                        justify-content: center;

                    }
                    .xui-select-inputwrap{
                        line-height: normal;
                        display: inline-flex;
                        align-items: center;
                    }
                    .select-trigger-icon{
                        top: 5px;
                    }
                    .xui-single-text {
                     
                        max-width: 165px;
                        overflow: hidden;
                        text-overflow: ellipsis;
                        white-space: nowrap;
                    }
                }
            }
        }
    }
}
</style>

sip信令交互

package http_api

import (
	"github.com/gin-gonic/gin"
	"sipserver-rebuild/pkg/constant"
	"sipserver-rebuild/pkg/constant/model"
	"sipserver-rebuild/pkg/logUtil"
	"sipserver-rebuild/pkg/sipapi"
	"strconv"
)

// @Summary     设备保活心跳设置
// @Description 用来获取下级设备或下级平台的设备目录列表,会包含地理位置等。
// @Tags        records
// @Accept      x-www-form-urlencoded
// @Produce     json
// @Param       id    path     string true "通道id"
// @Param       ip query    string    true "通道ip"
// @Param       port   query    string    true "通道port"
// @Success     0     {object} sipapi.Catalogs
// @Failure     1000  {object} string
// @Failure     1001  {object} string
// @Failure     1002  {object} string
// @Failure     1003  {object} string
// @Router      /channels/{id}/records [get]
func HolderControl(c *gin.Context) {

	logUtil.Log.Info("")
	channelid := c.Query("gbid")
	ip := c.Query("ip")
	port := c.Query("port")
	channelNum := c.Query("channelNum")
	order := c.Query("order")
	num := c.Query("num")
	speed := c.Query("speed")
	userName := c.Query("userId")
	controlPriority := c.Query("controlPriority")
	uuid := c.Query("uuid") // 同一会话日志唯一标识
	traceId := c.Query("traceId")

	var numcmd uint64
	var speedcmd uint64
	if order == "" {
		constant.JsonResponse(c, constant.OrderNil, "order不能为空")
		return
	}
	ordercmd, err := strconv.ParseUint(order, 16, 8)
	if err != nil || ordercmd < 0 {
		constant.JsonResponse(c, constant.OrderParamFail, "order错误")
		return
	}
	if userName == "" {
		constant.JsonResponse(c, constant.OrderUserNil, "用户名不能为空")
		return
	}
	if controlPriority == "" {
		constant.JsonResponse(c, constant.OrderControlPriorityNil, "控制权限不能为空")
		return
	}
	speedcmd, err = strconv.ParseUint(speed, 16, 8)
	if speedcmd < 0 {
		constant.JsonResponse(c, constant.OrderSpeedNil, "巡航速度错误")
		return
	}
	numcmd, err = strconv.ParseUint(num, 10, 8)
	if numcmd < 0 {
		constant.JsonResponse(c, constant.OrderNumNil, "巡航速度错误")
		return
	}
	var device model.HolderControlQuery

	if channelNum != "" {
		cn, e := strconv.Atoi(channelNum)
		if e != nil {
			logUtil.Log.Error("channelNum is error :" + e.Error())
		} else {
			device.ChannelNum = cn
		}
	}

	device.Channel.ChannelID = channelid
	device.Channel.Ip = ip
	device.Channel.Port = port
	device.Order = ordercmd
	device.Num = numcmd
	device.Speed = speedcmd
	device.UserName = userName
	device.UUID = uuid
	device.ControlPriority = controlPriority
	device.IsCross = "N"
	device.Cmd = order
	res, err := sipapi.SipHolderControl(&device, traceId)
	if err != nil {
		constant.JsonResponse(c, constant.HoldControlERR, err)
		return
	}
	if res.Code == 1005 {
		constant.JsonResponse(c, constant.HoldControlERR, res)
		return
	}
	constant.JsonResponse(c, res.Code, res)
}

4.要求信令网络传视质量

      联网系统IP网络的传输质量(如传输时延、包丢失率、包误差率、虚假包率等)应符合如下要求
a)网络时延上限值为400ms;
b)时延抖动上限值为50ms;
丢包率上限值为1x10-3;。)
d)包误差率上限值为1x10-
5.6视频帧率

5.相关调用流程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西安小哥

劳动不易

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值