New file |
| | |
| | | [submodule "be.teletask.onvif-java"] |
| | | path = be.teletask.onvif-java |
| | | url = https://gitee.com/18010473990/be.teletask.onvif-java.git |
| | |
| | | </parent> |
| | | |
| | | <groupId>com.genersoft</groupId> |
| | | <artifactId>wvp</artifactId> |
| | | <artifactId>wvp-gb28181</artifactId> |
| | | <version>2.0.0</version> |
| | | <name>web video platform</name> |
| | | |
| | |
| | | |
| | | <properties> |
| | | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| | | <maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format> |
| | | |
| | | <!-- 依赖版本 --> |
| | | <pagehelper.version>5.2.0</pagehelper.version> |
| | |
| | | <artifactId>okhttp</artifactId> |
| | | <version>4.9.0</version> |
| | | </dependency> |
| | | |
| | | <!-- okhttp-digest --> |
| | | <dependency> |
| | | <groupId>com.burgstaller</groupId> |
| | | <artifactId>okhttp-digest</artifactId> |
| | | <version>2.1</version> |
| | | </dependency> |
| | | |
| | | <!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 --> |
| | | <dependency> |
| | | <groupId>net.sf.kxml</groupId> |
| | | <artifactId>kxml2</artifactId> |
| | | <version>2.3.0</version> |
| | | </dependency> |
| | | |
| | | |
| | | <!-- onvif协议栈 --> |
| | | <dependency> |
| | | <groupId>be.teletask</groupId> |
| | | <artifactId>onvif-java</artifactId> |
| | | <version>1.0.2</version> |
| | | <scope>system</scope> |
| | | <systemPath>${project.basedir}/libs/onvif-java-1.0.2.jar</systemPath> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | |
| | | |
| | | <build> |
| | | <finalName>wvp-2.0</finalName> |
| | | <finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName> |
| | | <plugins> |
| | | <plugin> |
| | | <groupId>org.springframework.boot</groupId> |
| | |
| | |
|
| | | public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
|
| | |
|
| | | public static final String CALLBACK_ONVIF = "CALLBACK_ONVIF";
|
| | |
|
| | | public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
|
| | |
|
| | | public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
|
New file |
| | |
| | | package com.genersoft.iot.vmp.onvif; |
| | | |
| | | import be.teletask.onvif.models.OnvifDevice; |
| | | import com.genersoft.iot.vmp.onvif.dto.ONVIFCallBack; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface IONVIFServer { |
| | | |
| | | void search(int timeout, ONVIFCallBack<List<String>> callBack); |
| | | |
| | | void getRTSPUrl(int timeout, OnvifDevice device, ONVIFCallBack<String> callBack); |
| | | } |
New file |
| | |
| | | package com.genersoft.iot.vmp.onvif.dto; |
| | | |
| | | public interface ONVIFCallBack<T> { |
| | | void run(int errorCode, T t); |
| | | } |
New file |
| | |
| | | package com.genersoft.iot.vmp.onvif.impl; |
| | | |
| | | |
| | | import be.teletask.onvif.DiscoveryManager; |
| | | import be.teletask.onvif.OnvifManager; |
| | | import be.teletask.onvif.listeners.*; |
| | | import be.teletask.onvif.models.*; |
| | | import be.teletask.onvif.responses.OnvifResponse; |
| | | import com.genersoft.iot.vmp.onvif.IONVIFServer; |
| | | import com.genersoft.iot.vmp.onvif.dto.ONVIFCallBack; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.net.URI; |
| | | import java.net.URISyntaxException; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 处理onvif的各种操作 |
| | | */ |
| | | @Service |
| | | public class ONVIFServerIMpl implements IONVIFServer { |
| | | |
| | | private final static Logger logger = LoggerFactory.getLogger(ONVIFServerIMpl.class); |
| | | |
| | | @Override |
| | | public void search(int timeout, ONVIFCallBack<List<String>> callBack) { |
| | | DiscoveryManager manager = new DiscoveryManager(); |
| | | manager.setDiscoveryTimeout(timeout); |
| | | Map<String, Device> deviceMap = new HashMap<>(); |
| | | // 搜索设备 |
| | | manager.discover(new DiscoveryListener() { |
| | | @Override |
| | | public void onDiscoveryStarted() { |
| | | logger.info("Discovery started"); |
| | | } |
| | | |
| | | @Override |
| | | public void onDevicesFound(List<Device> devices) { |
| | | if (devices == null || devices.size() == 0) return; |
| | | for (Device device : devices){ |
| | | System.out.println(device.getHostName()); |
| | | deviceMap.put(device.getHostName(), device); |
| | | } |
| | | } |
| | | |
| | | // 搜索结束 |
| | | @Override |
| | | public void onDiscoveryFinished() { |
| | | ArrayList<String> result = new ArrayList<>(); |
| | | for (Device device : deviceMap.values()) { |
| | | System.out.println(device.getHostName()); |
| | | result.add(device.getHostName()); |
| | | } |
| | | callBack.run(0, result); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | | public void getRTSPUrl(int timeout, OnvifDevice device, ONVIFCallBack<String> callBack) { |
| | | if (device.getHostName() == null ){ |
| | | callBack.run(400, null); |
| | | } |
| | | OnvifManager onvifManager = new OnvifManager(); |
| | | onvifManager.setOnvifResponseListener(new OnvifResponseListener(){ |
| | | |
| | | @Override |
| | | public void onResponse(OnvifDevice onvifDevice, OnvifResponse response) { |
| | | System.out.println("[RESPONSE] " + onvifDevice.getHostName() |
| | | + "======" + response.getErrorCode() |
| | | + "======" + response.getErrorMessage()); |
| | | } |
| | | |
| | | @Override |
| | | public void onError(OnvifDevice onvifDevice, int errorCode, String errorMessage) { |
| | | System.out.println("[ERROR] " + onvifDevice.getHostName() + "======" + errorCode + "=======" + errorMessage); |
| | | callBack.run(errorCode, errorMessage); |
| | | } |
| | | }); |
| | | |
| | | try { |
| | | onvifManager.getServices(device, (OnvifDevice onvifDevice, OnvifServices services) -> { |
| | | if (services.getProfilesPath().equals("/onvif/Media")) { |
| | | onvifDevice.setPath(services); |
| | | onvifManager.getMediaProfiles(onvifDevice, new OnvifMediaProfilesListener() { |
| | | @Override |
| | | public void onMediaProfilesReceived(OnvifDevice device, List<OnvifMediaProfile> mediaProfiles) { |
| | | for (OnvifMediaProfile mediaProfile : mediaProfiles) { |
| | | System.out.println(mediaProfile.getName()); |
| | | System.out.println(mediaProfile.getToken()); |
| | | if (mediaProfile.getName().equals("mainStream")) { |
| | | onvifManager.getMediaStreamURI(device, mediaProfile, (OnvifDevice onvifDevice, |
| | | OnvifMediaProfile profile, String uri) -> { |
| | | |
| | | uri = uri.replace("rtsp://", "rtsp://"+ device.getUsername() + ":"+ device.getPassword() + "@"); |
| | | logger.info(onvifDevice.getHostName() + "的地址" + uri); |
| | | callBack.run(0, uri); |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | }catch (Exception e) { |
| | | callBack.run(400, e.getMessage()); |
| | | } |
| | | |
| | | |
| | | } |
| | | } |
New file |
| | |
| | | package com.genersoft.iot.vmp.vmanager.onvif; |
| | | |
| | | import be.teletask.onvif.models.OnvifDevice; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; |
| | | import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; |
| | | import com.genersoft.iot.vmp.onvif.IONVIFServer; |
| | | import com.genersoft.iot.vmp.vmanager.bean.WVPResult; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiImplicitParam; |
| | | import io.swagger.annotations.ApiImplicitParams; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.context.request.async.DeferredResult; |
| | | |
| | | import java.util.List; |
| | | import java.util.UUID; |
| | | |
| | | @Api(tags = "onvif设备") |
| | | @CrossOrigin |
| | | @RestController |
| | | @RequestMapping("/api/onvif") |
| | | public class ONVIFController { |
| | | |
| | | |
| | | @Autowired |
| | | private DeferredResultHolder resultHolder; |
| | | |
| | | @Autowired |
| | | private IONVIFServer onvifServer; |
| | | |
| | | |
| | | @ApiOperation("搜索") |
| | | @ApiImplicitParams({ |
| | | @ApiImplicitParam(name="timeout", value = "超时时间", required = true, dataTypeClass = Integer.class), |
| | | }) |
| | | @GetMapping(value = "/search") |
| | | @ResponseBody |
| | | public DeferredResult<ResponseEntity<WVPResult>> search(@RequestParam(required = false)Integer timeout){ |
| | | DeferredResult<ResponseEntity<WVPResult>> result = new DeferredResult<>(timeout + 10L); |
| | | UUID uuid = UUID.randomUUID(); |
| | | result.onTimeout(()->{ |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setId(DeferredResultHolder.CALLBACK_ONVIF + uuid); |
| | | WVPResult<String> wvpResult = new WVPResult(); |
| | | wvpResult.setCode(0); |
| | | wvpResult.setMsg("搜索超时"); |
| | | msg.setData(wvpResult); |
| | | resultHolder.invokeResult(msg); |
| | | }); |
| | | resultHolder.put(DeferredResultHolder.CALLBACK_ONVIF + uuid, result); |
| | | |
| | | onvifServer.search(timeout, (errorCode, onvifDevices) ->{ |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setId(DeferredResultHolder.CALLBACK_ONVIF + uuid); |
| | | WVPResult<List<String>> resultData = new WVPResult(); |
| | | resultData.setCode(errorCode); |
| | | if (errorCode == 0) { |
| | | resultData.setMsg("success"); |
| | | resultData.setData(onvifDevices); |
| | | }else { |
| | | resultData.setMsg("fail"); |
| | | } |
| | | msg.setData(resultData); |
| | | msg.setData(resultData); |
| | | resultHolder.invokeResult(msg); |
| | | }); |
| | | |
| | | return result; |
| | | } |
| | | |
| | | @ApiOperation("获取onvif的rtsp地址") |
| | | @ApiImplicitParams({ |
| | | @ApiImplicitParam(name="timeout", value = "超时时间", required = true, dataTypeClass = Integer.class), |
| | | @ApiImplicitParam(name="hostname", value = "onvif地址", required = true, dataTypeClass = String.class), |
| | | @ApiImplicitParam(name="username", value = "用户名", required = true, dataTypeClass = String.class), |
| | | @ApiImplicitParam(name="password", value = "密码", required = true, dataTypeClass = String.class), |
| | | }) |
| | | @GetMapping(value = "/rtsp") |
| | | @ResponseBody |
| | | public DeferredResult<ResponseEntity<WVPResult>> getRTSPUrl(@RequestParam(value="timeout", required=false, defaultValue="3000") Integer timeout, |
| | | @RequestParam(required = true) String hostname, |
| | | @RequestParam(required = false) String username, |
| | | @RequestParam(required = false) String password |
| | | ){ |
| | | |
| | | DeferredResult<ResponseEntity<WVPResult>> result = new DeferredResult<>(timeout + 10L); |
| | | UUID uuid = UUID.randomUUID(); |
| | | result.onTimeout(()->{ |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setId(DeferredResultHolder.CALLBACK_ONVIF + uuid); |
| | | WVPResult<String> wvpResult = new WVPResult(); |
| | | wvpResult.setCode(0); |
| | | wvpResult.setMsg("获取onvif的rtsp地址超时"); |
| | | msg.setData(wvpResult); |
| | | resultHolder.invokeResult(msg); |
| | | }); |
| | | resultHolder.put(DeferredResultHolder.CALLBACK_ONVIF + uuid, result); |
| | | OnvifDevice onvifDevice = new OnvifDevice(hostname, username, password); |
| | | onvifServer.getRTSPUrl(timeout, onvifDevice, (errorCode, url) ->{ |
| | | RequestMessage msg = new RequestMessage(); |
| | | msg.setId(DeferredResultHolder.CALLBACK_ONVIF + uuid); |
| | | WVPResult<String> resultData = new WVPResult(); |
| | | resultData.setCode(errorCode); |
| | | if (errorCode == 0) { |
| | | resultData.setMsg("success"); |
| | | resultData.setData(url); |
| | | }else { |
| | | resultData.setMsg(url); |
| | | } |
| | | msg.setData(resultData); |
| | | |
| | | resultHolder.invokeResult(msg); |
| | | }); |
| | | |
| | | return result; |
| | | } |
| | | |
| | | } |
| | |
| | | </div> |
| | | <div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;font-size: 14px;"> |
| | | <el-button icon="el-icon-plus" size="mini" style="margin-right: 1rem;" type="primary" @click="addStreamProxy">添加代理</el-button> |
| | | <el-button v-if="false" icon="el-icon-search" size="mini" style="margin-right: 1rem;" type="primary" @click="addOnvif">搜索ONVIF</el-button> |
| | | </div> |
| | | <devicePlayer ref="devicePlayer"></devicePlayer> |
| | | <el-table :data="streamProxyList" border style="width: 100%" :height="winHeight"> |
| | |
| | | :total="total"> |
| | | </el-pagination> |
| | | <streamProxyEdit ref="streamProxyEdit" ></streamProxyEdit> |
| | | <onvifEdit ref="onvifEdit" ></onvifEdit> |
| | | </el-main> |
| | | </el-container> |
| | | </div> |
| | |
| | | |
| | | <script> |
| | | import streamProxyEdit from './dialog/StreamProxyEdit.vue' |
| | | import onvifEdit from './dialog/onvifEdit.vue' |
| | | import devicePlayer from './dialog/devicePlayer.vue' |
| | | import uiHeader from './UiHeader.vue' |
| | | export default { |
| | |
| | | components: { |
| | | devicePlayer, |
| | | streamProxyEdit, |
| | | onvifEdit, |
| | | uiHeader |
| | | }, |
| | | data() { |
| | |
| | | }, |
| | | mounted() { |
| | | this.initData(); |
| | | // this.updateLooper = setInterval(this.initData, 10000); |
| | | this.updateLooper = setInterval(this.initData, 1000); |
| | | }, |
| | | destroyed() { |
| | | this.$destroy('videojs'); |
| | |
| | | addStreamProxy: function(){ |
| | | this.$refs.streamProxyEdit.openDialog(null, this.initData) |
| | | }, |
| | | addOnvif: function(){ |
| | | this.getListLoading = true; |
| | | this.getListLoading = true; |
| | | this.$axios({ |
| | | method: 'get', |
| | | url:`/api/onvif/search?timeout=3000`, |
| | | }).then((res) =>{ |
| | | this.getListLoading = false; |
| | | if (res.data.code == 0 ){ |
| | | if (res.data.data.length > 0) { |
| | | console.log(res.data.data) |
| | | this.$refs.onvifEdit.openDialog(res.data.data, (url)=>{ |
| | | if (url != null) { |
| | | this.$refs.onvifEdit.close(); |
| | | this.$refs.streamProxyEdit.openDialog({type: "default", url: url, src_url: url}, this.initData()) |
| | | } |
| | | }) |
| | | }else { |
| | | this.$message.success("未找到可用设备"); |
| | | } |
| | | }else { |
| | | this.$message.error(res.data.msg); |
| | | } |
| | | |
| | | }).catch((error)=> { |
| | | this.getListLoading = false; |
| | | this.$message.error(error.response.data.msg); |
| | | }); |
| | | |
| | | }, |
| | | saveStreamProxy: function(){ |
| | | }, |
| | | play: function(row){ |
| | |
| | | this.listChangeCallback = callback; |
| | | if (proxyParam != null) { |
| | | this.proxyParam = proxyParam; |
| | | this.onSubmit_text = "保存"; |
| | | } else { |
| | | this.onSubmit_text = "立即创建"; |
| | | } |
| | | }, |
| | | onSubmit: function () { |
| | |
| | | that.showDialog = false; |
| | | if (that.listChangeCallback != null) { |
| | | that.listChangeCallback(); |
| | | that.dialogLoading = false; |
| | | } |
| | | } |
| | | }).catch(function (error) { |
New file |
| | |
| | | <template> |
| | | <div id="onvif搜索" v-loading="isLoging"> |
| | | <el-dialog |
| | | title="onvif搜索" |
| | | width="40%" |
| | | top="2rem" |
| | | :close-on-click-modal="false" |
| | | :visible.sync="showDialog" |
| | | :destroy-on-close="true" |
| | | @close="close()" |
| | | > |
| | | <div id="shared" style="margin-top: 1rem;margin-right: 100px;"> |
| | | <el-form ref="form" :rules="rules" :model="form" label-width="140px" > |
| | | <el-form-item label="地址" prop="hostName" > |
| | | <el-select v-model="form.hostName" style="float: left; width: 100%" > |
| | | <el-option |
| | | v-for="item in hostNames" |
| | | :key="item" |
| | | :label="item.replace('http://', '')" |
| | | :value="item"> |
| | | </el-option> |
| | | </el-select> |
| | | |
| | | </el-form-item> |
| | | <el-form-item label="用户名" prop="username"> |
| | | <el-input v-model="form.username" clearable></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="密码" prop="password"> |
| | | <el-input v-model="form.password" clearable></el-input> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <div style="float: right;"> |
| | | <el-button type="primary" @click="onSubmit" >确认</el-button> |
| | | <el-button @click="close">取消</el-button> |
| | | </div> |
| | | |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: "onvifEdit", |
| | | props: {}, |
| | | computed: {}, |
| | | created() {}, |
| | | data() { |
| | | return { |
| | | listChangeCallback: null, |
| | | showDialog: false, |
| | | isLoging: false, |
| | | hostNames:[], |
| | | form: { |
| | | hostName: null, |
| | | username: "admin", |
| | | password: "admin123", |
| | | }, |
| | | |
| | | rules: { |
| | | hostName: [{ required: true, message: "请选择", trigger: "blur" }], |
| | | username: [{ required: true, message: "请输入用户名", trigger: "blur" }], |
| | | password: [{ required: true, message: "请输入密码", trigger: "blur" }], |
| | | }, |
| | | }; |
| | | }, |
| | | methods: { |
| | | openDialog: function (hostNamesParam, callback) { |
| | | console.log(hostNamesParam) |
| | | this.showDialog = true; |
| | | this.listChangeCallback = callback; |
| | | if (hostNamesParam != null) { |
| | | this.hostNames = hostNamesParam; |
| | | } |
| | | |
| | | }, |
| | | onSubmit: function () { |
| | | console.log("onSubmit"); |
| | | console.log(this.form); |
| | | this.$axios({ |
| | | method: 'get', |
| | | url:`api/onvif/rtsp`, |
| | | params: { |
| | | hostname: this.form.hostName, |
| | | timeout: 3000, |
| | | username: this.form.username, |
| | | password: this.form.password, |
| | | } |
| | | }).then((res) => { |
| | | console.log(res.data) |
| | | if (res.data.code == 0) { |
| | | if (res.data.data != null) { |
| | | this.listChangeCallback(res.data.data) |
| | | }else { |
| | | this.$message({ |
| | | showClose: true, |
| | | message: res.data.msg, |
| | | type: "error", |
| | | }); |
| | | } |
| | | |
| | | }else { |
| | | this.$message({ |
| | | showClose: true, |
| | | message: res.data.msg, |
| | | type: "error", |
| | | }); |
| | | } |
| | | }).catch(function (error) { |
| | | console.log(error); |
| | | }); |
| | | }, |
| | | close: function () { |
| | | this.showDialog = false; |
| | | this.$refs.form.resetFields(); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |