简介webrtc-streamer 是面向 WebRTC 实时音视频传输场景的开源流媒体服务器适合需要在浏览器端接入视频流、验证信令与媒体转发流程的开发者及学习者。该包为 Windows AMD64 平台的预编译版本涵盖可执行程序、前端页面、配置示例与模型文件可用于本地搭建流媒体服务、调试 ICE 与 STUN/TURN 连接并观察多用户流媒体中继的基本表现。压缩包共 94 个文件约 7.29MB以 JavaScript、HTML、CSS 等前端示例与页面资源为主辅以 wasm 模块、模型文件、JSON 配置、字体图标及 Windows 可执行文件兼顾服务运行与浏览器端演示。已有 857 人学习下载说明其在 WebRTC 入门与联调场景中具有一定参考价值。读者可通过包内示例理解信令交互、视频流渲染与前端模型加载方式借助配置文件和命令行参数完成本机验证并以此为基础排查网络穿透、流地址配置与页面兼容性问题适合作为 WebRTC 流媒体服务的实践起点。1. 从一条命令行到浏览器出画面先别急着下载要把一路 rtsp 摄像头接进网页播放webrtc-streamer 是我这几年反复装的一套东西它把 libwebrtc 和一个 HTTP 服务打包成单个可执行文件起一条命令就能把 rtsp、本地视频文件甚至采集设备转成 webrtc 流推给浏览器。它真正难在选装本身并不难预编译包按 x86_64、armv7、aarch64 分了好几份拿错架构直接报“无法执行二进制文件”系统 glibc 太老跑起来就提示找不到符号还有人图省事拉了最新版结果跟手上前端用的 webrtcstreamer.js 版本对不上页面转圈转到怀疑人生。我见过最冤枉的一次是同事把程序扔在 /tmp 里跑机器重启后进程没了排查了半小时才发现问题不在 webrtc 本身。这套环境适合两类人一类是要把监控画面嵌进业务系统的后端和运维另一类是想在本机先验证一下能不能通的开发者。下面按我实际部署的顺序走先把环境和版本对清楚再走二进制、源码、容器三条安装路线然后拆启动参数和 config.json最后把 ICE 不通、黑屏、SDP 为空这些翻车现场挨个过一遍。2. 环境准备webrtc-streamer 的版本线、架构包与依赖清单装之前先把机器体检一遍这一步省掉后面百分之八十的报错都要重新回到这里。webrtc-streamer 是 C 编译出来的原生程序不像 Node 或 Python 项目那样装个运行时就能跑它对 CPU 架构、C 运行库版本、动态链接库都有硬要求。我的习惯是先花三分钟把下面这些信息打出来写在部署记录里出问题时对着看比现场猜要快得多。2.1 三条安装路线预编译包、源码编译、容器这个项目对外提供的获取方式基本就三条先选路线再谈版本顺序反了会白折腾。第一条是预编译发行包。项目在发布时会按平台打好压缩包Linux 下常见的是 x86_64 与 arm 两种架构解压即有可执行文件加一个 html 目录。优点是快缺点是绑定运行时环境——包里链接的库版本是固定的系统太老就跑不起来。适合在比较新的发行版上做快速验证或者干脆在容器里跑。第二条是源码编译。要装编译器、CMake编译过程中 CMake 脚本通常会自动去取一份预编译好的 libwebrtc 归档这是最容易卡住的一步。适合两种人一是目标机器架构特殊、官方没出包二是需要改代码或者锁定某个 libwebrtc 版本。我第一次在 arm 板子上装就是走这条路前后折腾了一个下午。第三条是容器。把二进制和 html 目录一起打进镜像跑的时候用宿主网络模式省掉一堆 UDP 端口映射的麻烦。生产上我越来越倾向这条尤其是同一套服务要在多台机器上铺开的时候。路线典型耗时主要风险我一般什么时候用预编译包5 分钟架构不符、glibc 太老本机验证、新系统快速上线源码编译30 分钟到数小时libwebrtc 归档下载中断、依赖缺失特殊架构、需要改代码容器15 分钟UDP 未映射、时区与日志多机部署、版本要固化2.2 依赖与端口装之前先把这张表对一遍先确认架构和 C 运行库这两条决定了你到底该下哪个包。# 确认 CPU 架构x86_64 / aarch64 / armv7l 对应不同的发行包 uname -m # 确认 glibc 版本预编译包对它有下限要求 ldd --version | head -n 1 # 确认系统版本便于判断包能不能直接用 cat /etc/os-release | head -n 2这段命令里没有需要改的参数直接抄。看输出的时候注意两点uname -m返回aarch64就别去拿x86_64的包名字里带 arm 的包一般还会细分 32 位和 64 位ldd --version打出来的版本号如果明显低于打包机上的版本后面运行大概率会报version GLIBC_2.xx not found这种错误没法靠装库解决只能换包或者上容器。拿到包之后再用ldd反查缺什么动态库# 列出可执行文件依赖的所有动态库重点看 not found ldd ./webrtc-streamer | grep -i not found # 若缺的是 libssl 一类按发行版补齐即可 # Debian/Ubuntu 系 sudo apt-get install -y libssl3 ca-certificates # RHEL/CentOS 系 sudo yum install -y openssl-libs ca-certificatesgrep -i not found是关键别直接ldd完就翻页走人缺库是逐条列出来的漏一条就起不来。libssl缺失最常见因为程序要用它做 HTTPS 和部分拉流ca-certificates容易被忽略缺了以后拉 https 源会报证书校验失败现象是连接超时很容易误判成网络问题。端口这边提前规划好别等上线了才发现被占。端口/协议用途说明8000/TCPHTTP 服务、REST API、静态页面可改改完前端地址要同步改UDP 动态端口WebRTC 媒体传输跨网段访问必须放通容器要特别注意554/TCP 出方向拉 rtsp 源程序作为客户端主动连出去3478/UDPSTUN 服务有公网或跨网段需求时才需要WebRTC 的媒体不走 8000 端口这是新手最容易误解的一点8000 只负责信令和页面真正的音视频走的是另行协商出来的 UDP 端口。所以「页面能打开但没画面」和「页面打不开」是两类完全不同的故障。2.3 目录布局与拉取别把程序扔进 /tmp我把所有这类中间件都放在/opt下按 bin/conf/html/log 分开好处是升级时只换 bin 里的二进制配置文件不动回滚时把旧二进制拷回来就行不用回忆当初改了哪。# 建立标准目录权限交给专用用户 sudo mkdir -p /opt/webrtc-streamer/{bin,conf,html,log} sudo useradd -r -s /sbin/nologin webrtc 2/dev/null || true sudo chown -R webrtc:webrtc /opt/webrtc-streamer # 解压发行包到临时目录看看里面到底有什么 tar -tzf webrtc-streamer-*.tar.gz | head -n 20第一条命令建目录并创建一个不能登录的系统用户程序不需要 root 权限用专用账号跑能省掉后面很多安全审查的口舌。第二条只是预览tar -tzf不会真的解压先看清包里是「单个可执行文件」还是「可执行文件 html 目录」决定了你后面-w参数该怎么给。有的发行包把 html 目录放在二进制同级有的放在./html子目录下-w指错地方的结果是页面 404而 API 却一切正常。3. 安装落地Linux 上把 webrtc-streamer 跑起来环境对完就开始装。这一章三条路线我都给可执行步骤你按自己的场景挑一条走通即可不要三条混着来——混着来最容易出现「用源码编译的二进制配的却是发行包里带的 html」。3.1 预编译包解压、权限与首次启动# 解压到临时目录确认结构 mkdir -p /tmp/wrs tar -xzf webrtc-streamer-*.tar.gz -C /tmp/wrs ls -l /tmp/wrs # 把二进制和 html 分别就位 sudo cp /tmp/wrs/webrtc-streamer /opt/webrtc-streamer/bin/ sudo cp -r /tmp/wrs/html/. /opt/webrtc-streamer/html/ sudo chmod 755 /opt/webrtc-streamer/bin/webrtc-streamer # 首次启动先看帮助别急着加业务参数 /opt/webrtc-streamer/bin/webrtc-streamer -hchmod 755是必须的压缩包解压出来的可执行位有时候会丢丢了以后报的是「权限不够」而不是「文件不存在」。最后那条-h是我每次装完的第一个动作不是为了看用法是为了确认这个版本到底认哪些参数——不同版本之间参数有增删网上抄来的命令经常有一两个参数在你这版里根本不存在程序会直接退出。以-h的输出为准这是唯一靠谱的参照。确认参数没问题用最小配置起一次前台进程# 前台跑日志直接打屏方便第一次看它到底在干什么 /opt/webrtc-streamer/bin/webrtc-streamer \ -H 0.0.0.0:8000 \ -w /opt/webrtc-streamer/html-H 0.0.0.0:8000表示监听所有网卡的 8000 端口写成127.0.0.1:8000就只有本机能访问这是内网测试和正式上线的分水岭很多「同事打不开」的问题都出在这里。-w指向静态页面目录指对了内置的webrtcstreamer.html才能被访问到。跑起来以后先别关终端另开一个窗口curl一下确认 HTTP 服务活着再谈拉流。3.2 源码编译CMake 参数、libwebrtc 归档与常见中断源码编译的坑集中在依赖和归档下载两处。先把编译工具链补齐# Debian/Ubuntu 系构建依赖 sudo apt-get install -y build-essential cmake pkg-config git \ libssl-dev zlib1g-dev ca-certificates # 建构建目录不要在源码根目录里直接 cmake mkdir -p ~/wrs-src/build cd ~/wrs-src/build # Release 构建体积小、运行快调试信息按需再开 cmake -DCMAKE_BUILD_TYPERelease ..-DCMAKE_BUILD_TYPERelease一定要给默认配置往往是空类型编出来的二进制跑起来性能差一截转多路流的时候特别明显。..指向源码根目录必须带。构建目录单独建是硬规矩在源码目录里直接 cmake 会把中间产物和源码混在一起下次换参数重编时清不干净。# 并行编译核数按机器来别把内存打满 make -j$(nproc) # 编完确认产物 ls -lh webrtc-streamer-j$(nproc)是按 CPU 核数并行但 C 编译很吃内存小内存机器上建议降到-j4甚至-j2否则会看到c: fatal error: Killed signal terminated program cc1plus——这不是编译错误是被系统的 OOM killer 干掉了我第一次碰到还以为是代码问题查了半天日志。编译过程中如果卡在「下载 libwebrtc 归档」这一步通常是目标网络访问不了外部存储。常见做法是找到 CMake 脚本里那个归档地址和保存路径在另一台能访问的机器上取下来手工放进指定目录再重新执行一次 cmake。归档一般按架构命名放错架构会在链接阶段报符号不匹配而不是在下载阶段报错所以放之前先核对文件名里的架构标识。3.3 容器化镜像构建与网络模式的选择容器这条路的重点不在 Dockerfile在网络模式。先写构建文件FROM debian:12-slim # 只装运行时依赖编译工具不要进最终镜像 RUN apt-get update apt-get install -y --no-install-recommends \ libssl3 ca-certificates \ rm -rf /var/lib/apt/lists/* WORKDIR /app COPY webrtc-streamer /app/webrtc-streamer COPY html/ /app/html/ COPY conf/config.json /app/conf/config.json # 对外暴露信令与页面端口 EXPOSE 8000/tcp # 前台运行别加 -d 之类的后台参数否则容器会立刻退出 ENTRYPOINT [/app/webrtc-streamer, \ -H, 0.0.0.0:8000, \ -w, /app/html, \ -C, /app/conf/config.json]镜像里只保留运行时依赖编译工具链留在构建机上最终镜像能小好几百兆。ENTRYPOINT用了 JSON 数组形式好处是信号能直接传给进程docker stop时能优雅退出用 shell 形式的话进程会被包一层停容器时容易变成强杀。最后一行没有加任何后台参数容器里必须前台跑这是铁律。启动时用宿主网络模式docker run -d --name wrs \ --network host \ --restart unless-stopped \ -v /opt/webrtc-streamer/conf:/app/conf \ -v /opt/webrtc-streamer/log:/app/log \ wrs:latest--network host是关键。WebRTC 协商出来的媒体端口是动态的用默认的桥接网络你得把一大段 UDP 端口映射出去还得保证程序和映射规则一致非常容易漏。宿主网络模式直接共享网络栈省掉这一整类问题代价是端口不能冲突所以同机多实例要提前分配好 8000 这类端口。-v把配置和日志挂出来改配置不用重新打镜像看日志也不用docker exec。4. 配置详解命令行参数、config.json 与对外暴露到这一步程序能起来了接下来是让它按你的业务跑。参数和配置文件的分工是临时调整、环境相关的用命令行源列表这种经常增删的放 config.json。两者都能改端口和源但维护成本差很多。4.1 启动参数-H、-w、-C、-a、-s 到底改哪个/opt/webrtc-streamer/bin/webrtc-streamer \ -H 0.0.0.0:8000 \ -w /opt/webrtc-streamer/html \ -C /opt/webrtc-streamer/conf/config.json \ -a \ -s stun:你的STUN地址:3478逐个说。-H是监听地址和端口格式IP:PORT写成0.0.0.0表示全部网卡如果机器有多张网卡又只想在内网暴露写具体内网 IP 更安全。-w是静态页面根目录内置的播放页和webrtcstreamer.js都在里面指错就 404。-C指定配置文件路径不给就用当前工作目录下的默认名所以用 systemd 托管时一定要显式给绝对路径否则工作目录不对配置读不到还不报错。-a是打开音频采集/转发默认关。视频监控场景通常不需要开了反而多占带宽还可能因为源没有音频轨导致协商异常我一般只在做音视频对讲类需求时才加。-s指定 STUN 服务地址只有当你需要跨网段、跨 NAT 访问时才必须纯内网同网段的话不加也行加了也无害但要确认这个地址是真实可达的写错了会拖长 ICE 协商时间表现是画面出来得特别慢。提示不同版本的参数集合不完全相同尤其是端口范围、令牌校验这一类。上线前把-h的输出存一份到部署记录里比事后翻文档快。4.2 config.jsonurls 映射与 iceServers配置文件解决的是「源地址写在哪」这个问题。把源写进配置前端只传一个短名字好处是摄像头密码不会出现在浏览器地址栏和访问日志里。{ urls: { camera-01: rtsp://用户名:密码10.0.0.11:554/Streaming/Channels/101, camera-02: rtsp://用户名:密码10.0.0.12:554/Streaming/Channels/101, demo-file: file:///data/demo.mp4 }, iceServers: [ { urls: [stun:你的STUN地址:3478] } ] }urls是一个名字到地址的映射表左边这个键就是前端要传的值起名建议带业务前缀别用1、2这种日志里根本看不出是哪一路。右边支持 rtsp、本地文件等多种协议本地文件那一条在做演示环境时很好用不用依赖摄像头就能验证整条链路通不通。iceServers里放 STUN 配置格式是数组一个对象可以带多个urls需要中继的时候再补 TURN 相关字段。字段名请以你手上这个版本自带的示例文件为准版本之间有过调整照抄别人的配置文件经常出现「解析成功但一项都没生效」的情况——JSON 语法对、字段名不对程序会默默忽略。改完配置必须重启进程这个文件不是热加载的。我一般的做法是改完先python3 -m json.tool校验一遍语法再重启省得因为一个逗号把服务弄挂。4.3 前端接入内置播放页与自行调 API 的区别最快验证方式是用内置页面把源地址 URL 编码后拼进查询串// 内置播放页video 参数是源地址必须整段编码否则 和 ? 会被截断 const server http://10.0.0.20:8000; const source rtsp://用户名:密码10.0.0.11:554/Streaming/Channels/101; const page ${server}/webrtcstreamer.html?video${encodeURIComponent(source)}; window.open(page);encodeURIComponent这行是重点。rtsp 地址里常带?、、不编码的话这些字符会被当成外层 URL 的分隔符服务端收到的地址被截断然后返回一个空的 SDP——现象是请求 200 但页面没画面非常容易误判成服务端问题。如果要嵌进自己的业务页面就自己调 API核心是每个播放会话用一个唯一的peerid// 自行接入peerid 唯一标识一路会话重复会导致串流或抢占 const peerid cam01-${Date.now()}-${Math.floor(Math.random() * 1000)}; const url /api/call?peerid${peerid}url${encodeURIComponent(camera-01)}; // 离开页面务必挂断否则服务端资源不会释放 window.addEventListener(beforeunload, () { navigator.sendBeacon(/api/hangup?peerid${peerid}); });peerid用时间戳加随机数拼保证同一页面刷新多次也不会撞车撞车的表现很怪第二个页面能出画面第一个页面直接卡死。beforeunload里挂断这一步很多人省掉短期看不出问题跑几天以后服务端会话越堆越多新连接协商越来越慢最后需要重启——这就是典型的资源泄漏别等出事故才补。4.4 反向代理与 TLS非本机访问绕不开的一步页面走 HTTPS、而 webrtc-streamer 还是 HTTP 的时候浏览器会拦混合内容和 WebSocket表现是控制台一堆报错、画面出不来。两种解法给服务本身配证书或者在前面加一层反向代理终结 TLS。生产上我倾向后者证书续期、统一入口都好管。反向代理要满足三个条件转发Upgrade和Connection头、关闭响应缓冲、超时时间调大。少一条就会出现「握手成功但立刻断开」或者「画面卡在第一帧」。# 代理配好后先用 curl 验证信令通道是否正常 curl -s -o /dev/null -w %{http_code}\n https://你的域名/api/version # 再验证一次调用接口看返回的 SDP 是否非空 curl -s https://你的域名/api/call?peeridtest-001urlcamera-01 | head -c 200第一条只看状态码200 说明代理链路和证书都没问题第二条是关键head -c 200截前 200 个字符看内容正常应该是一段以v0开头的 SDP 文本。如果返回的是空字符串或者一段 HTML 错误页说明请求根本没打到后端先查代理的路由规则别去查 webrtc 参数。5. 避坑与排查五类翻车现场下面这五条是我这几年真实踩过的按「现象 → 原因 → 解决」记遇到时对号入座比从头捋链路快。5.1 页面一直转圈ICE 停在 checking现象内置页面能打开点了播放以后一直转圈浏览器控制台里 ICE 连接状态停在checking过一会儿变failed。原因信令通了但媒体通道没通。8000 端口的 HTTP 走的是 TCP而 WebRTC 媒体走 UDP两者是完全独立的路径。跨网段、跨防火墙、容器桥接网络这三种情况最容易只放通了 TCP。解决先确认程序所在机器和浏览器所在机器之间的 UDP 是否可达内网可以临时把防火墙关掉做对比测试确认需要跨网段时补上 STUN 配置用容器的话改成宿主网络模式。这条排查顺序别反先怀疑网络再怀疑代码能省一半时间。5.2 /api/call 返回 200SDP 却是空的现象接口返回状态码 200响应体是空的或者只有几个字节页面侧表现为「连上了但没画面」。原因九成是url参数没做 URL 编码rtsp 地址里的?、把参数截断了服务端拿到一个残缺地址拉流失败又不一定报错。剩下的一成是配置里的键名写错传了个不存在的名字。解决前端传参一律套encodeURIComponent用curl直接测时给地址加引号并手工编码如果传的是配置里的短名先用grep确认这个键在config.json里真的存在且改完配置后重启过进程。5.3 拉流成功但黑屏日志里能看到流信息现象服务端日志显示已经连上源、也收到了数据浏览器端连接状态是connected但画面全黑。原因编码格式不被支持。WebRTC 在浏览器侧基本只吃 H.264、VP8、VP9 这几种很多摄像头默认输出 H.265服务端能拉下来但没法转成浏览器能解的样子。解决登录摄像头或录像机把对应通道的编码改成 H.264子码流优先改完确认关键帧间隔不要太大间隔太大表现为「画面几秒才动一下」。这一条我踩过两次第一次查了两小时网络最后发现改个编码就好了。5.4 编译到一半卡在下载 libwebrtc 归档现象cmake或make执行到某一步停住日志显示在下载一个大文件最后超时失败重试多次都一样。原因构建脚本需要一份预编译的 libwebrtc 归档目标机器访问不了那个存储地址或者中间链路限速导致超时。解决换一台能访问的机器把归档取下来按脚本里写的路径和文件名手工放好再重新执行cmake。放之前核对文件名里的架构标识放错了不会在下载阶段报错而是在链接阶段报一堆符号找不到反而更难定位。5.5 容器里 HTTP 通了画面出不来现象docker logs正常宿主上curl8000 端口返回 200页面能打开就是没画面。原因默认桥接网络下WebRTC 协商出的媒体端口没有映射到宿主UDP 包出不去另外容器内的 IP 被当成候选地址发给了浏览器浏览器根本路由不到这个地址。解决直接用--network host跑绕开端口映射和地址错误两个问题。如果业务上必须用桥接网络就得固定媒体端口范围并把整段 UDP 映射出去同时确认程序报给浏览器的候选地址是宿主地址——这一步配置成本明显更高能不用就不用。6. 验证跑通从 curl 到画面四层检查清单装完别急着交付按层验证。我习惯从下往上走任何一层不通就停在那里不要跳。第一层进程和端口。ps -ef | grep webrtc-streamer看进程在不在ss -lntp | grep 8000看端口有没有真的监听起来。进程在但端口没监听多半是-H写错了地址或者端口被占导致启动失败但被后台脚本吞了日志。第二层HTTP 与 API 存活。这一步只验证服务本身不涉及拉流# 服务版本最简单的存活探测 curl -s http://127.0.0.1:8000/api/version # 发起一路呼叫正常应返回以 v0 开头的 SDP curl -s http://127.0.0.1:8000/api/call?peeridverify-001urlcamera-01 | head -c 120 # 验证完记得挂断别把测试会话留在服务端 curl -s http://127.0.0.1:8000/api/hangup?peeridverify-001第二条是整个验证的核心能返回 SDP说明信令、配置解析、源地址三件事全对了。返回空就先回第 5.2 条查编码和键名。第三条别省测试会话不清掉后面正式联调时会出现「同一个 peerid 已被占用」这类莫名其妙的问题。第三层ICE 候选。在浏览器里打开内置播放页按 F12 看网络面板确认 ICE 候选收集到了、连接状态最终是connected而不是checking。停在checking就回到第 5.1 条。第四层画面与稳定性。画面出来只是及格线我会再挂十分钟看三件事帧率稳不稳、内存有没有缓慢上涨、日志里有没有反复重连。内存缓慢上涨基本就是会话没挂断导致的泄漏反复重连多半是关键帧间隔太大或者源本身不稳。层级判断依据不通时先查进程端口进程存在、8000 监听-H参数、端口占用HTTP/API/api/version返回 200防火墙、反向代理路由信令/api/call返回非空 SDPURL 编码、配置键名媒体ICE 状态connected、画面滚动UDP 可达性、STUN、容器网络注意验证顺序不要跳层。跳过信令直接看画面等于把两类完全不同的故障混在一起排查最费时间。从那以后我每次装完这套环境都强制走一遍这四层哪怕只是给同事临时搭个演示——因为省下的那五分钟往往要用一小时的排查去还。前面那份-h输出和四层检查结果我都会一起贴进部署记录下次换机器照着抄一遍就能起来。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?