用浏览器直接播放RTSP流,绕过浏览器的安全监管

发布时间: 2026-05-31 09:39:33

有些需求,看起来只是“在网页里播放一路摄像头”。

真正落到项目里,才发现它像一条暗河,绕过浏览器、网络协议、视频封装、权限边界和跨平台安装,最后还要在用户面前呈现为一个安静的播放器:打开页面,画面出现,帧率自然,延迟轻盈,业务系统不必知道背后有多少水流奔涌。

这就是 RTSP Player 想解决的问题。

它不是一个把视频推到云端再转回来的中转系统,也不是让前端工程背上一整套媒体服务器。它把摄像头和 NVR 中最常见的 H.264 RTSP 拉到本机,在 Chrome 里以组件形式播放,并提供两种可以直接落地的接入方式:Chrome 扩展方案,以及 JS / Vue / React 通用组件方案。

项目完全免费使用。若你希望获得全部源码以及后续永久仓库更新,任意捐赠即可自助开通;没有固定门槛,也不需要漫长沟通,愿意支持多少,都可以从容开始。

为什么这件事一直让人头疼

RTSP 在摄像头、NVR、门店监控、园区管理、工业现场里太常见了。

但浏览器天生并不认识 RTSP。它不会直接打开 rtsp://,也不能随意连一条 TCP 长连接去收 RTP 包。于是许多团队在网页播放摄像头时,常常会被迫走向几条并不轻松的路:

把 RTSP 转成 HLS,延迟一下子被拉长;搭建 WebRTC 服务,链路和运维随之变重;引入 FFmpeg,在安装包、授权、进程、跨平台和资源占用之间反复权衡;继续依赖旧插件或私有控件,又很难适应今天的浏览器环境。

市场中真正空缺的,是这样一种形态:页面依旧像写普通组件一样写播放器,本机负责靠近摄像头的协议转换,浏览器负责最终解码与绘制;不绕远路,不把内网视频送去别处,也不让业务系统背负沉重的媒体基础设施。

RTSP Player 便是沿着这个空缺生长出来的。

它现在长什么样

页面中只需要一行标签:

<rtsp-player
  url="rtsp://user:pass@192.168.1.64:554/Streaming/Channels/101"
  width="960"
  height="540"
  autoplay
  controls>
</rtsp-player>

也可以在 React 中这样使用:

<RtspPlayer
  extensionId="你的扩展 ID"
  url="rtsp://user:pass@camera/stream"
  autoplay
  controls
/>

Vue、原生 JS、Web Component、独立浏览器脚本都已经准备好。对业务系统来说,它不必理解 RTSP、RTP、H.264 分片,也不必关心 Native Messaging 如何启动本地程序;它只需要声明一个播放器,并给它一个地址。

两条交付路径,给不同团队选择

第一条路径,是 Chrome 扩展方案。

它适合企业内网、NVR 管理台、SaaS 控制台、门店和园区类系统。业务页面只放 <rtsp-player>,Chrome 扩展负责注入播放器、打开 iframe、唤起本地 Runtime,并通过弹窗管理允许访问的业务域名。

第二条路径,是 通用组件 SDK。

它适合已有前端工程显式接入。你可以在 plain HTML 里使用预构建脚本,也可以在 React 和 Vue 里像普通组件一样引入。SDK 内置了与扩展通信所需的封装,便于在实际业务界面里做列表、卡片、九宫格、弹窗预览和大屏展示。

换句话说,它既能进入一个由管理员统一安装扩展的内网系统,也能进入一个由前端团队精细组织的现代工程。

真实播放,不是概念图

下面这张图来自公开 RTSP 地址的完整联调。链路经过业务页面、Chrome 扩展、Native Messaging、本地 Go Gateway、RTSP over TCP、RTP/H.264 解析、WebSocket、WebCodecs,最后由 Canvas 渲染。

公开 RTSP 源真实浏览器播放截图

验证时观察到的结果包括:

  • 解码器进入 ready 状态。
  • H.264 codec 为 avc1.4D401E。
  • 画面约 30 fps。
  • 播放队列保持在 0 附近。
  • 视频帧由浏览器 WebCodecs 解码后绘制到 Canvas。

这意味着它不是停留在“协议打通”的阶段,而是已经穿过了真实视频播放最关键的路径:摄像头流进来,浏览器画面出来。

最难的部分,不是写一个播放器界面

播放器界面并不复杂,真正艰难的是让浏览器吃到一口正确的视频。

RTSP 过来的并不是浏览器可以直接播放的 MP4,也不是一个现成的 <video> 标签地址。摄像头会把 H.264 切成 RTP 包,有时是一颗完整 NALU,有时是多个 NALU 打包在一起,有时一个关键帧被切成许多片段。播放器必须理解 Single NALU、STAP-A、FU-A,重新拼出可以解码的 H.264 Access Unit。

还不止如此。

H.264 的关键帧需要 SPS/PPS 这些参数集,WebCodecs 在 Annex-B 模式下并不需要额外的 description,但 key chunk 必须携带解码所需的信息。于是 Go 端会缓存 SPS/PPS,并在 IDR 前自动补齐,让浏览器拿到一份完整而清晰的画面入口。

这一层如果处理得不细,结果往往不是黑屏,就是第一帧迟迟不来,或者画面在某些摄像头上才出现异常。

为什么没有选择 FFmpeg

FFmpeg 是强大的,甚至可以说是视频世界的群山之一。最开始,使用 FFmpeg 确实是最容易想到的答案。

但这个项目追求的是另一种质地:体积轻、链路短、依赖少、安装清楚、便于放进产品。为了播放一路 H.264 RTSP,如果引入完整 FFmpeg,随之而来的会是更大的分发包、更多进程管理细节、跨平台差异,以及业务团队未必愿意承受的复杂度。

也曾认真看过 gortsplib / MediaMTX 这一类成熟项目。它们方向可靠,能力丰厚。但当前目标并不是搭建完整媒体服务器,而是完成“拉一路 H.264 RTSP,并把它变成浏览器可以低延迟解码的二进制帧”。

最后选择了 Go 原生最小协议栈:只实现必要路径,不把系统带入不必要的庞大迷宫。

Native Messaging 只做控制,视频走 WebSocket

Chrome Native Messaging 是官方通道,可以让扩展启动本地 Host,并通过 stdin/stdout 交换带长度前缀的 JSON 消息。

但视频帧不是温柔的小消息。它们持续、密集、二进制、对延迟敏感。如果把每一帧都塞进 Native Messaging 的 JSON 通道,性能和单消息大小都会成为枷锁。

因此 RTSP Player 做了一个清晰分工:

Native Messaging 只负责启动和控制;真正的视频帧,通过本机 127.0.0.1 上随机端口的 WebSocket 传输。

这让控制面保持官方、安全、可管;让数据面保持轻盈、二进制、低开销。浏览器最终拿到的是 Annex-B H.264 access unit,再交给 WebCodecs VideoDecoder,请求硬件优先解码,之后绘制到 Canvas。

为流畅度做的那些细节

画面是否顺滑,往往不取决于一句“用了硬解”,而取决于每一层是否少做无谓的事情。

RTSP Player 在这条路上做了几件关键选择:

  • RTSP 使用 TCP interleaved,避免浏览器侧和复杂网络环境中的 UDP 不确定性。
  • Go Gateway 输出二进制 WebSocket 帧,减少 JSON 编解码和文本膨胀。
  • H.264 access unit 在本地整理好,再交给浏览器,不让前端猜测分片边界。
  • WebCodecs 使用 hardwareAcceleration: "prefer-hardware",优先调用浏览器已有的解码能力。
  • 播放队列采用低延迟策略,旧帧不被无限堆积,画面尽量贴近实时。
  • Canvas 负责最终渲染,便于在业务页面中做自定义 UI、统计信息和播放器状态。

这也是为什么真实测试里可以看到约 30 fps、队列接近 0 的表现。它不是靠堆硬件换来的,而是把每一段不必要的迂回都尽量拿掉。

当前已经覆盖的能力

如果你正在评估它是否适合自己的项目,可以先看这一组边界。

已经支持:

  • H.264 RTSP 视频流。
  • RTSP over TCP interleaved。
  • RTSP Basic / Digest 鉴权。
  • SDP 中 H.264 track 解析。
  • RTP/H.264 Single NALU、STAP-A、FU-A。
  • SPS/PPS 缓存与 IDR 前补齐。
  • Annex-B H.264 access unit 输出。
  • 本地 WebSocket 二进制传输。
  • Chrome MV3 扩展。
  • <rtsp-player> 网页组件。
  • JS / React / Vue SDK。
  • WebCodecs 硬件优先解码。
  • Canvas 渲染。
  • 播放状态、fps、queue 统计。
  • ready、error 等组件事件。
  • macOS、Linux、Windows 构建与 Native Host 注册脚本。

暂未包含:

  • 音频。
  • H.265 / HEVC。
  • UDP RTP。
  • FFmpeg 转码。
  • Node 网关。

如果你的摄像头当前是 H.265,建议在 NVR 或摄像头后台把主码流或子码流切到 H.264。对大多数监控与工业摄像头而言,这是最容易获得兼容性的路径。

安全边界从一开始就被放在桌面上

视频是敏感数据,尤其是内网摄像头。这个项目没有把“能播放”放在安全之前。

它默认让 Gateway 只监听 127.0.0.1;Native Host 使用随机 secret 调用本地 API;每一路流都有一次性 token;WebSocket 会校验 chrome-extension:// 来源;RTSP 地址不会被塞进 iframe query string;日志里会遮蔽用户名和密码;业务网页 origin 需要在扩展弹窗中授权。

生产环境中,还建议把扩展 manifest.json 里的 content script matches 从开发期的宽泛匹配收窄到你的业务域名。让播放器出现在哪里,应当由你决定,而不是由任何网页决定。

它能够支撑哪些场景

如果你做过这些系统,大概一眼就能明白它的价值:

  • 门店、仓库、园区、工厂的摄像头预览。
  • NVR 管理台和设备管理后台。
  • AI 标注、算法调试、边缘设备控制台。
  • 物业、安防、巡检、生产现场的内网大屏。
  • SaaS 平台里需要嵌入客户本地摄像头的页面。
  • 前端系统需要用 Vue、React 组件直接拼出多画面布局的场景。

它并不试图替代大型视频平台。它更像一把优雅的钥匙:当你的需求只是“把现有 RTSP 摄像头放进网页”,你不必为了开一扇门去建一座城。

那些绕路,也是它的一部分

这个项目并不是一次笔直的抵达。

Native Messaging 一开始看起来像天然通道,但它更适合控制,而不是视频洪流。于是视频被移到本地 WebSocket。

Go Native Host 最初如果作为长进程停留,会让 Chrome 的 sendNativeMessage 等不到应有的结束。于是它被调整为一次消息、一份回应、随即退出;真正需要常驻的是本地 Gateway daemon。

macOS 对用户文档目录有保护机制,如果让 Chrome 从某些位置直接启动 native host,可能出现令人困惑的等待。安装脚本因此改为把二进制放到 ~/Library/Application Support/rtsp-web-player/,再写入 Native Messaging manifest。

这些细节不浪漫,却决定了一个方案能不能在别人电脑上被安心使用。

免费使用,任意捐赠

RTSP Player 会保持完全免费使用。你可以把它用于学习、验证、内网试点和业务集成。

如果你希望获得全部源码、完整仓库权限和后续永久更新,可以通过任意捐赠自助开通。没有固定金额,没有复杂流程,支持之后即可进入仓库,后续更新也会持续跟随,省去反复沟通和等待。

项目地址:

https://github.com/flyfish-dev/rtsp[1]

主页与文档:

https://rtsp-roan.vercel.app[2]

很多工程问题并不缺少庞大的答案,缺少的是刚好合适的答案。

RTSP Player 想做的,就是把摄像头播放这件事从沉重的部署、古老的插件和迟缓的转码中带出来,放回一个现代网页该有的样子:组件化、可嵌入、可授权、可本地运行,也足够安静。

当一条 RTSP 流终于在浏览器里自然呈现,背后那些复杂的协议、分片、鉴权、参数集和解码队列,都退到帷幕后面。

用户看到的,只是一块画面。

而开发者终于可以把注意力,重新交还给自己的业务。

引用链接

 

[1]https://github.com/flyfish-dev/rtsp

[2]https://rtsp-roan.vercel.app

平台对接融合~客户基于smarteye WEB SDK做二次开发,通过客户自己的业务平台调取smarteye的视音频、定位等数据,https://www.besovideo.com/detail?t=1&i=376

移动视频图传技术文档大全WiKi,http://wiki.besovideo.com:7788/

新一代smarteye配套的WEB融合可视指挥调度平台软件常用功能,https://www.besovideo.com/detail?t=1&i=166

客户自建smarteye server 私有化部署,完全自己可控,新版server(内含软件下载网盘地址): https://www.besovideo.com/detail?t=1&i=208

移动视频类设备&平台国标GB28181输入/输出GB28181平台对接、RTSP/RTMP输入&输出, https://www.besovideo.com/detail?t=1&i=229

多源统一视频融合通信可视指挥调度平台VMS/smarteye概述,https://www.besovideo.com/detail?t=1&i=240

关于移动视频-无线图传产品中的录像机制-云端录像-录像计划、主动录像、主动上传、下载计划-录像导出等说明,https://www.besovideo.com/detail?t=1&i=146

关于移动视频-无线图传系统中的语音对讲、集群通话(有用户权限差异和无用户权限差异),https://www.besovideo.com/detail?t=1&i=149

客户私有化部署自建4G图传流媒体服务器平台需求性能估算,https://www.besovideo.com/detail?t=1&i=91

关于移动视频管理平台的平台对接常见问题,https://www.besovideo.com/detail?t=1&i=95

固定点监控及第三方网络摄像头设备接入smarteye平台与移动视频监控的融合, https://www.besovideo.com/detail?t=1&i=152

移动视频无线图传系统里面的RTSP/ONVIF流输出的实现机制,https://www.besovideo.com/detail?t=1&i=156

移动视频无线图传系统里面的RTMP流输出的实现机制,https://www.besovideo.com/detail?t=1&i=157

smarteye平台通过MQTT与第三方平台交换数据,https://www.besovideo.com/detail?t=1&i=142

VMS/smarteye设置电子围栏和线路规划,https://www.besovideo.com/detail?t=1&i=182

smarteye对离线地图的支持,https://www.besovideo.com/detail?t=1&i=140

使用WEB浏览器(chrome)来配合设备模拟器(MCP)测试可视指挥调度平台(server-E),https://www.besovideo.com/detail?t=1&i=155

4G单兵执法仪/智能安全帽等前端人脸比对系统使用说明, https://www.besovideo.com/detail?t=1&i=365

4G单兵执法仪、布控球等前端车牌识别系统使用说明,https://www.besovideo.com/detail?t=1&i=167

4G/5G执法记录仪、智能安全帽、smarteye平台使用常见问题解答FAQ,https://www.besovideo.com/detail?t=1&i=371

统一视频~融合通信指挥调度~安全生产可视化管控平台smarteye WEB大图展示,https://www.besovideo.com/detail?t=1&i=337

安卓APP

使用adb工具来安装APP到安卓执法记录仪、智能安全帽、头盔摄像头等安卓终端, https://www.besovideo.com/detail?t=1&i=121

移动视频录传-公网对讲APP-MCP常用操作说明,适用于带屏的安卓终端,包括手机,执法记录仪等,https://www.besovideo.com/detail?t=1&i=126

安卓类4G/5G移动视频无线图传终端产品使用常见问题,https://www.besovideo.com/detail?t=1&i=128

最新的MPU,用手机浏览器直接下载安装,http://up.besovideo.com:7780/mpu_na.apk

移动视频录传APP-MPU常用操作说明,适用于智能安全帽/头盔摄像头/智能手电等无屏终端,https://www.besovideo.com/detail?t=1&i=130

用投屏软件配置无屏4G记录仪、智能安全帽-头盔摄像头,https://www.besovideo.com/detail?t=1&i=127