SuperMap iClient3D for WebGL如何使用大华的RTSP流和WebSocket地址做视频投放

SuperMap iClient3D for WebGL如何使用大华的RTSP流和WebSocket地址做视频投放

前言

在有些项目的需求中会有遇到使用其他的RTSP流和WebSocket地址做视频投放的情况,会发现直接使用示例代码更改 视频流地址以及WebSocket地址后无法加载出对应的流视频.那么为什么呢?

原因

此处以大华为例,最大的原因是大华的WebSocket使用的子协议是私有的协议而咱们官网的解析WebSocket地址的依赖是基于明确的WSP 协议(WebSocket Streaming Protocol),有公开的 INIT/JOIN/WRAP 命令的WebSocket 子协议做的解析.所以无法正确解析大华的WebSocket 地址.

解决方法

大华的官方提供了解析大华WebSocket 地址的依赖.可自行前往大华官方下载也可下载下面网盘链接中的依赖文件使用: https://pan.baidu.com/s/1mo0D5jdyXZl6eTuJrpLiFA?pwd=me9g。

详细代码以及流程

可以看到上述网盘链接中有大华提供的加载示例代码。我们现在需要的就是将其解析方式搬到咱们SuperMap iClient3D for WebGL的代码中去,做视频流的解析,再去投放到建筑上。

替换依赖

将代码中解析公开WebSocket 子协议的streamedian.js替换为大华的依赖文件WSPlayer.js和PlaySDKInterface.js文件.替换依赖后代码如下:

<link href="../../Build/SuperMap3D/Widgets/widgets.css" rel="stylesheet"><link href="./css/pretty.css" rel="stylesheet"><link rel="stylesheet" href="./lib/WSPlayer/player.css"><link rel="stylesheet" href="./lib/WSPlayer/window.division.css"><script src="./js/echarts.min.js"></script><script src="js/polyfill.js"></script><script src="./js/jquery.min.js"></script><script src="./js/slider.js"></script><script type="text/javascript" src="../../Build/SuperMap3D/SuperMap3D.js"></script><script src="./js/config.js"></script><script type="text/javascript" src="./lib/WSPlayer/PlaySDKInterface.js"></script><script type="text/javascript" src="./lib/WSPlayer/WSPlayer.js"></script>

替换WebSocket 解析以及视频播放代码

这里就直接贴代码了,不做过多赘述,可自行差查看代码理解逻辑

<body><div id="Container"></div><!-- 控制面板(可选保留,用于调整参数和调试) --><div id="toolbar" class="param-container tool-bar"><button type="button" id="active" class="button black">重新投放</button><button type="button" id="clear" class="button black">清除投放</button><br /><label><input type="checkbox" id="visibleLine" checked> 显示投放参考线</label><br><br><div style="margin: 8px 0;"><b>宽度(°)</b><br><input type="number" id="horizontal" min="1" step="0.1" value="20"></div><div style="margin: 8px 0;"><b>高度(°)</b><br><input type="number" id="vertical" min="1" step="0.1" value="10"></div><div style="margin: 8px 0;"><b>投影距离(m)</b><br><input type="number" id="distance" min="10" step="10" value="350"></div><div style="margin-top:15px;"><small>WS URL:</small><br><input id="real-ws" value="ws://ip:40088" style="width:220px;"></div><div style="margin-top:8px;"><small>RTSP:</small><br><input id="real-rtsp" value="rtsp://ip:40088/dss/monitor/param/cameraid=1003919%240%26substream=1" style="width:220px;"></div></div><!-- 性能监控图表 --><div style="position: absolute; right: 20px; top: 120px;"><div id="msChart" style="width: 300px; height: 150px; background:rgba(0,0,0,0.4);"></div></div><div style="position: absolute; right: 20px; top: 320px;"><div id="fpsChart" style="width: 300px; height: 150px; background:rgba(0,0,0,0.4);"></div></div><!-- 隐藏的视频播放容器 --><div id="ws-real-player" style="width:720px; height:480px; visibility:hidden; position:absolute; top:-9999px; left:-9999px;"></div><script type="module"> import PlayerManager from './icc/PlayerManager.js';functiononload(SuperMap3D){ const EngineType =getEngineType(); const viewer =newSuperMap3D.Viewer('Container',{ contextOptions:{ contextType:Number(EngineType)}}); viewer.scenePromise.then(scene =>{init(SuperMap3D, scene, viewer);});}functioninit(SuperMap3D, scene, viewer){ viewer.resolutionScale = window.devicePixelRatio;addBaseMap(viewer);// ── 性能监控 ──────────────────────────────────────── const msChart = echarts.init(document.getElementById('msChart')); const fpsChart = echarts.init(document.getElementById('fpsChart')); let msData =[], fpsData =[], step =0; scene.debugShowFramesPerSecond =true; scene.shadowMap.darkness =1.275; scene.hdrEnabled =false; scene.sun.show =true;setInterval(()=>{if(step <=6&& scene.debugShowFramesPerSecond){ const msEl = document.querySelector(".supermap3d-performanceDisplay-ms"); const fpsEl = document.querySelector(".supermap3d-performanceDisplay-fps");if(msEl && fpsEl){ msData[step]=parseFloat(msEl.innerHTML.replace(/[^0-9.]/g,'')); fpsData[step]=parseFloat(fpsEl.innerHTML.replace(/[^0-9.]/g,'')); step++;}}else{ step =0;}},200);setInterval(()=>{ msChart.setOption({ title:{ subtext:'MS', left:'right', subtextStyle:{color:'#eee'}}, xAxis:{ type:'category', data:Array(7).fill(''), axisLabel:{color:'#ccc'}}, yAxis:{ type:'value', axisLabel:{color:'#ccc'}}, series:[{ data: msData, type:'line'}], grid:{ left:'5%', right:'5%', top:'25%', bottom:'5%', containLabel:true}}); fpsChart.setOption({ title:{ subtext:'FPS', left:'right', subtextStyle:{color:'#eee'}}, xAxis:{ type:'category', data:Array(7).fill(''), axisLabel:{color:'#ccc'}}, yAxis:{ type:'value', axisLabel:{color:'#ccc'}}, series:[{ data: fpsData, type:'line', areaStyle:{}}], grid:{ left:'5%', right:'5%', top:'25%', bottom:'5%', containLabel:true}});},1000);if(!scene.pickPositionSupported){alert('当前浏览器/设备不支持深度拾取,投影功能受限');}// 加载模型 SuperMap3D.when.all([ scene.addS3MTilesLayerByScp(URL_CONFIG.SCP_CBD_BUILD,{name:'build'}), scene.addS3MTilesLayerByScp(URL_CONFIG.SCP_CBD_GROUND1,{name:'ground1'}), scene.addS3MTilesLayerByScp(URL_CONFIG.SCP_CBD_LAKE,{name:'lake'}), scene.addS3MTilesLayerByScp(URL_CONFIG.SCP_CBD_TREE,{name:'tree'}), scene.addS3MTilesLayerByScp(URL_CONFIG.SCP_CBD_ROAD,{name:'road'}), scene.addS3MTilesLayerByScp(URL_CONFIG.SCP_CBD_BRIDGE,{name:'bridge'}),], layers =>{// 设置初始相机视角 scene.camera.setView({ destination: SuperMap3D.Cartesian3.fromDegrees(116.4486,39.9092,91.3293), orientation:{ heading:3.179304500963121, pitch:-0.46239072362282485, roll:6.283185307179583}}); layers.forEach(l => l.selectEnabled =false); const perfEl = document.querySelector(".supermap3d-performanceDisplay");if(perfEl) perfEl.style.display ="none";// ── 核心:视频投影对象 ──────────────────────────────── const projectionImage =newSuperMap3D.ProjectionImage(scene);// 固定投放目标点(经纬高) const targetLon =116.4486895458964; const targetLat =39.907729088565965; const targetHeight =32.51768911137742;// ── WSPlayer 初始化 ─────────────────────────────────── const realPlayer =newPlayerManager({ el:"ws-real-player", type:'real', maxNum:1, num:1, showControl:false, prefixUrl:'lib', receiveMessageFromWSPlayer:(method, data, err)=>{if(method ==='initializationCompleted'){ realPlayer.setPlayerAdapter("stretching");// 初始化完成后自动开始播放(可选移到下面按钮事件)playVideoAndProject();}elseif(method ==='realSuccess'){ console.log("视频流播放成功,开始构建投影"); const videoEl = document.querySelector('#ws-real-player video');if(videoEl){ projectionImage.setImage({ video: videoEl }); projectionImage.build();}}elseif(method ==='realError'){ console.error("视频流失败", err);}}});// 投放核心逻辑functionprojectToFixedPoint(){ const cameraPos = scene.camera.positionCartographic; const viewPos =[ SuperMap3D.Math.toDegrees(cameraPos.longitude), SuperMap3D.Math.toDegrees(cameraPos.latitude), cameraPos.height ]; projectionImage.viewPosition = viewPos; projectionImage.horizontalFov =Number($("#horizontal").val()); projectionImage.verticalFov =Number($("#vertical").val()); projectionImage.distance =Number($("#distance").val()); projectionImage.hintLineVisible = $("#visibleLine").is(":checked");// 固定朝向目标点 projectionImage.setDistDirByPoint([targetLon, targetLat, targetHeight]);}// 播放视频并投影functionplayVideoAndProject(){ const wsURL = $("#real-ws").val(); const rtspURL = $("#real-rtsp").val(); realPlayer.realByUrl({ rtspURL, wsURL, selectIndex:0, channelId:'', streamType:1, playerAdapter:'stretching'});// 立即设置投影参数(视频可能还未ready,ready后会build)projectToFixedPoint();}// 页面加载完成后自动执行一次投放setTimeout(()=>{playVideoAndProject();},1500);// 延迟1.5秒,确保场景和播放器基本就绪// 按钮:手动重新投放(调试用) $("#active").click(()=>{ realPlayer.close(0);setTimeout(playVideoAndProject,300);});// 清除 $("#clear").click(()=>{ realPlayer.close(0); projectionImage.destroy && projectionImage.destroy();});// 参数实时更新 $("#horizontal, #vertical, #distance").on("input", projectToFixedPoint); $("#visibleLine").on("change", projectToFixedPoint);});}if(typeof SuperMap3D !=='undefined'){ window.startupCalled =true;onload(SuperMap3D);}</script></body>

最终加载效果

最终完成之后的效果如下图,可以看到视频流已经成功投放到模型建筑上

在这里插入图片描述

Read more

Java编程进阶:智能仿真无人机项目4.0

Java编程进阶:智能仿真无人机项目4.0

一、项目前期准备 V4 版本在 V3 “双向对抗” 基础上,新增定点任务处理、鼠标交互、多线程协作、状态机管理四大核心功能,新手需在 V3 基础(集合、线程通信、扫描攻击)上,额外掌握以下知识点: 1. 鼠标事件监听(MouseListener) * 作用:捕捉鼠标操作(点击、按压、释放等),实现 “鼠标点击生成任务” 的交互; * 核心接口:MouseListener,需重写 5 个方法(重点用mousePressed:鼠标按压时触发); * 关键步骤:给窗口注册鼠标监听器→重写mousePressed方法→获取鼠标点击坐标。 2. 距离计算(勾股定理) * 作用:找到 “离任务点最近的无人机”,实现任务分配逻辑; 代码实现: (int)

【前端小站】HTML 标签:网页骨架,从空白到惊艳,全靠这些 HTML 标签搞事情

【前端小站】HTML 标签:网页骨架,从空白到惊艳,全靠这些 HTML 标签搞事情

半桔:个人主页  🔥 个人专栏: 《前端扫盲》《手撕面试算法》《C++从入门到入土》 🔖为什么有人总是赞美生活的丰富多彩?我想这是因为他们善于品尝生活中随时出现的意外。 -余华- 文章目录 * 前言 * 一. HTML结构 * 1.1 初始HTML标签 * 1.2 标签的层次 * 二. HTML文本标签 * 2.1 标题标签 * 2.2 段落标签 * 2.3 强调标签 * 2.3.1 加粗 * 2.3.2 倾斜 * 2.3.3 删除线 * 2.3.4 下划线 * 三. 媒体与交互标签 * 3.

RexUniNLU前端联动:Vue组件库封装+Schema可视化编辑器+实时效果预览

RexUniNLU前端联动:Vue组件库封装+Schema可视化编辑器+实时效果预览 1. 为什么需要一套“看得见、调得动、信得过”的NLU前端方案? 你有没有遇到过这样的场景:后端模型已经跑通,analyze_text("查明天北京天气", ["查询天气", "城市", "时间"]) 能准确返回结构化结果,但业务同学盯着命令行输出直摇头——“这怎么嵌进我们App里?”“客户要改个标签得找我改代码再发版?”“能不能让我自己拖拽试试效果?” RexUniNLU本身是轻量、零样本、开箱即用的,但它默认只提供Python接口。真正落地到产品中,光有test.py远远不够。用户需要的是:能直观定义Schema的界面、能即时看到识别效果的预览区、能一键集成到Vue项目的可复用组件。 这不是锦上添花,而是把NLU能力从“技术验证”推向“业务可用”的关键一跃。本文不讲模型原理,不堆参数配置,