南昌县城乡规划建设局官方网站,北京seo优化技术,跨境电子商务,企业光纤局域网组网方案背景
10年前就有在浏览器播放监控画面的需求#xff0c;当初需求会稍微复杂一点#xff0c;除了播放实时监控画面之外#xff0c;可能还需要对摄像机做控制#xff0c;比如云台旋转、画面截图、录像、调取录像进行回访、快进、倒退等等。目前项目上又碰到了浏览器展示实时…背景
10年前就有在浏览器播放监控画面的需求当初需求会稍微复杂一点除了播放实时监控画面之外可能还需要对摄像机做控制比如云台旋转、画面截图、录像、调取录像进行回访、快进、倒退等等。目前项目上又碰到了浏览器展示实时监控画面的需求不过目前来看需要要简单一些只要能展示实时监控画面就可以。
10年前没有找到很好的解决方案可能是需求本身相对复杂一点而且当初也许没有现在这样相对成熟的解决方案毕竟经过这么多年的发展摄像机厂商也不像当年那样各自为战视频编码格式以及控制方式各种不统一目前据简单了解通过ONVIF协议实现控制也相对比较简单关键是绝大部分的摄像机厂商都会遵守ONVIF协议。
实现方式
以前是通过厂商SDK编写浏览器插件实现的现在当然不需要了只是展示实时监控录像实现方式会简单许多。
从摄像机获取实时监控视频流的最简单方式应该是获取他的rtsp流毕竟一条rtsp://xxxx命令就可以拉流过来了只不过由于浏览器不支持RTSP所以还需要想办法做一些转换。
浏览器播放实时监控视频其实不外乎两个方案一个还是以前的老方案通过插件的方式直接播放RTSP但是插件几乎要被淘汰了即使不淘汰使用起来也非常不方便所以这个方案也基本不考虑。另外一个方案就是想办法转换RTSP为浏览器支持的WebRTC我们就采用这一方案实现。
所以第一步就是想办法转换RTSP为浏览器支持的WebRTC。
MediaMTX
Media MTX 是一个高性能的开源媒体服务器用于处理实时音视频流。它支持多种协议包括 RTSP、RTMP 和 WebRTC适合用作直播、视频监控如 IPCAM、和流媒体中继的解决方案。Media MTX 轻量、灵活并具有广泛的协议兼容性非常适合嵌入式设备和云环境。
既然他支持RTSP以及WebRTC我们就用他来做流媒体中继服务器目的是让mediaMTX接收到摄像机的RTSP流然后转换为WebRTC为前端浏览器访问摄像机实时监控画面提供服务。
享受开源的好处吧没有开源我们现在寸步难行。
下载地址https://github.com/bluenviron/mediamtx/releases/ 找到适合自己的版本我用的是Ubantu系统所以找了这个
rootkmkf2:/mediaMTX# mkdir /mediaMTX
rootkmkf2:/mediaMTX# cd /mediaMTX
rootkmkf2:/mediaMTX# wget https://github.com/bluenviron/mediamtx/releases/download/v1.12.3/mediamtx_v1.12.3_linux_amd64.tar.gz之后查看一下下载的内容解包
rootkmkf2:/mediaMTX# ls -lrt
-rw-r--r-- 1 root root 16784260 May 28 04:43 mediamtx_v1.12.3_linux_amd64.tar.gz
rootkmkf2:/mediaMTX# tar -zxvf mediamtx_v1.12.3_linux_amd64.tar.gz
rootkmkf2:/mediaMTX# ls -lrt
-rw-r--r-- 1 root root 29435 May 28 04:39 mediamtx.yml.bak
-rw-r--r-- 1 root root 1062 May 28 04:39 LICENSE
-rwxr-xr-x 1 root root 33002954 May 28 04:41 mediamtx
-rw-r--r-- 1 root root 16784260 May 28 04:43 mediamtx_v1.12.3_linux_amd64.tar.gz
-rw-r--r-- 1 root root 29609 Jun 13 11:51 mediamtx.yml齐活了可以开干了。
配置mediaMTX
配置文件是mediamtx.yml配置也非常简单只有最后面这几行需要配置
# Path settings# Settings in paths are applied to specific paths, and the map key
# is the name of the path.
# Any setting in pathDefaults can be overridden here.
# Its possible to use regular expressions by using a tilde as prefix,
# for example ~^(test1|test2)$ will match both test1 and test2,
# for example ~^prefix will match all paths that start with prefix.
paths:# example:# my_camera:# source: rtsp://my_cameracam1:source: rtsp://admin:xxxyouripcip:554/stream1#sourceProtocol: rtspsourceOnDemand: yes # 表示当有人观看时才连接摄像头 需要注意的是cam1 和 source: rtsp://admin:xxxyouripcip:554/stream1 这两处
cam1是你的摄像机名称浏览器访问的识别参数。source配置摄像机其中rtsp协议后面的admin:xxx是访问用户名和密码,youripcip是摄像机ip地址这个地址当然要能访问得到才行554是摄像机rtsp协议的默认端口后面的stream1是摄像机提供rtsp服务的具体访问路径这里的每一个配置都和摄像机厂商有关不同的厂商访问地址是不一样的需要查看摄像机说明书。
这里配置完成之后就可以启动服务了
rootkmkf2:/mediaMTX# ./mediamtx
2025/06/13 21:49:18 INF MediaMTX v1.12.3
2025/06/13 21:49:18 INF configuration loaded from /mediaMTX/mediamtx.yml
2025/06/13 21:49:18 INF [metrics] listener opened on :9998
2025/06/13 21:49:18 INF [RTSP] listener opened on :8554 (TCP), :8000 (UDP/RTP), :8001 (UDP/RTCP)
2025/06/13 21:49:18 INF [RTMP] listener opened on :1935
2025/06/13 21:49:18 INF [HLS] listener opened on :8888
2025/06/13 21:49:18 INF [WebRTC] listener opened on :8889 (HTTP), :8189 (ICE/UDP)
2025/06/13 21:49:18 INF [SRT] listener opened on :8890 (UDP)
2025/06/13 21:49:18 INF [API] listener opened on :9997如果能看到以上信息配置就成功了。但是是否能连接到摄像机只有访问的时候才能知道。
编写html访问
接下来就可以编写html代码访问实时监控画面了。
!DOCTYPE html
html
headmeta charsetUTF-8titleWebRTC 监控流/titlestyle.container {display: flex;flex-direction: column;align-items: center;gap: 20px;padding: 20px;}.button {padding: 20px 40px;font-size: 24px;cursor: pointer;background-color: #4CAF50;color: white;border: none;border-radius: 10px;min-width: 300px;transition: background-color 0.3s;font-weight: bold;}.button:hover {background-color: #45a049;}video {max-width: 100%;height: auto;}/style
/head
bodydiv classcontainerh1摄像头/h1video idvideo autoplay playsinline controls muted/videobutton classbutton onclickcaptureImage()抓图/button/divscriptasync function startStream(videoId, camPath) {try {console.log(开始建立WebRTC连接...);const pc new RTCPeerConnection();pc.ontrack function (event) {console.log(收到视频流);document.getElementById(videoId).srcObject event.streams[0];};pc.oniceconnectionstatechange function() {console.log(ICE连接状态:, pc.iceConnectionState);};pc.onicecandidate function(event) {console.log(ICE候选:, event.candidate);};const offer await pc.createOffer({ offerToReceiveAudio: true, offerToReceiveVideo: true });await pc.setLocalDescription(offer);console.log(本地描述设置完成);console.log(正在连接服务器...);const res await fetch(http://192.168.xx.xxx:8889/cam1/whep, {method: POST,headers: {Content-Type: application/sdp},body: offer.sdp,});if (!res.ok) {throw new Error(HTTP error! status: ${res.status});}const answer await res.text();console.log(收到服务器响应);await pc.setRemoteDescription({ type: answer, sdp: answer });console.log(远程描述设置完成);} catch (error) {console.error(发生错误:, error);alert(连接失败: error.message);}}async function captureImage() {try {// 使用canvas从视频流中抓取图片const video document.getElementById(video);const canvas document.createElement(canvas);canvas.width video.videoWidth;canvas.height video.videoHeight;const ctx canvas.getContext(2d);ctx.drawImage(video, 0, 0, canvas.width, canvas.height);// 将canvas转换为blobconst blob await new Promise(resolve canvas.toBlob(resolve, image/jpeg, 0.95));const url URL.createObjectURL(blob);// 创建下载链接const a document.createElement(a);a.href url;a.download snapshot_${new Date().toISOString().replace(/[:.]/g, -)}.jpg;document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url);} catch (error) {console.error(抓图失败:, error);alert(抓图失败: error.message);}}startStream(video, cam1);/script
/body
/html
以上代码包括调试过程都交给cursor完成的cursor确实很强大个人认为比通义灵码方便且强大方便只操作方面生成的代码可以一键生效强大是他的能力。 然后就能看到实时监控画面了如果看不到可以继续问cursorAI比师傅好的地方就是你可以不停问他会不厌其烦反复修改你不用跟他客气他就是帮你干活的你尽管用命令的语气、使用合适prompt问他就OK。
以上