上一个实现方案比较丑陋,没有正经的前端,而且延迟其实还是有点高,这次我又发现了一个更厉害的方案,不仅前端美观,还能直播间发消息,并且延迟可以做到低于1秒!
前言
这不是一个教程,这篇文章是基于我自己的实践经验写的,只是想记录一下当时的做法。如果你也想要搭建一个自己的直播服务器,我强烈建议阅读官方的入门文档,并根据你的需要调整这些步骤。
使用的项目是Oven Media Engine,前端用的是Vue写的Radium
由于官方文档写的不够友好,导致我浪费了好多时间踩坑,好在找到一篇大佬的教程,多亏了他的教程省了我不少时间研究
还得站在前人的肩膀上啊
最终效果展示
首先推流到直播服务器

打开网页来观看直播流,并测试延迟

搭建
准备工作
- 一台有公网ip的Linux服务器
- 一个域名(可选)
- 我搭建的环境为
Ubuntu 22.04
Docker Engine - Community 25.0.2
启动服务
使用docker compose来启动直播流媒体服务,和直播间服务
version: '3.6' services: # 直播流媒体服务器 ovenmediaengine: image: airensoft/ovenmediaengine:latest ports: - "9001:9000/tcp" # OVT(Origin) - "1935:1935/tcp" # RTMP Provider - "9999:9999/udp" # SRT - "3333:3333/tcp" # ws协议 - "3334:3334/tcp" # wss协议,加密传输 - "3478:3478/tcp" # WebRTC TURN - "10000-10009:10000-10009/udp" # WebRTC Candidate environment: - OME_HOST_IP=你的服务器ip - OME_ORIGIN_PORT=9000 - OME_RTMP_PROV_PORT=1935 - OME_SRT_PROV_PORT=9999 - OME_LLHLS_STREAM_PORT=3333 - OME_WEBRTC_SIGNALLING_PORT=3333 - OME_WEBRTC_TCP_RELAY_PORT=3478 - OME_WEBRTC_CANDIDATE_PORT=10000-10009/udp restart: always volumes: - ./cert/:/cert # 证书目录 # - ./config:/opt/ovenmediaengine/bin/origin_conf # 配置目录 第一次启动先注释掉,因为我们没有默认的配置文件 # 直播客户端, 先注释,之后会说明 # radium: # container_name: radium # ports: # - "3000:3000" # environment: # - BASE_URL=# 写你的域名或ip,例如127.0.0.1:3000 # - INITIAL_SOURCE_TYPE=webrtc # - INITIAL_SOURCE_URL=# 这里写webrtc协议拉流地址,之后会说明 # image: ghcr.io/zibbp/radium:next
启动容器后,使用如下命令把容器里的默认配置文件拷贝出来,再取消配置文件映射的注释,重启容器,之后就能方便的修改了
docker cp 容器id:/opt/ovenmediaengine/bin/origin_conf 本地的路径
我的服务器开放端口参考

HTTP
这里要说明的是,如果你对加密传输没有需求,就可以不用修改任何配置文件
由于现代浏览器的限制,非加密传输的网站HTTP协议只能和非加密的ws协议通信,也就是
3333
端口所以服务端的
3334
端口也可以删了客户端的BASE_URL也只能使用HTTP协议
docker compose down
先停掉容器,把客户端取消注释填写好拉流地址
INITIAL_SOURCE_URL=
ws://你的ip或域名:3333/app/stream
再
docker compose up -d
启动所有容器至此你就可以向这个地址推流了
rtmp://你的ip或域名:1935/app
srt://你的ip或域名:9999?streamid=srt%3A%2F%2F你的ip或域名%3A9999%2Fapp%2Fstream
推流码默认为
stream
前往前端网页
http://你的ip或域名:3000
观看HTTPS
证书可以去Let’s Encrypt免费申请,Nginx Proxy Manager上也可以很方便的申请并下载

由于之前映射了证书的目录,我们只需要在配置文件里修改一下证书的位置就行,还需要开启TLS的端口
<Bind> <!-- For API Server --> <Managers> <API> <Port>8081</Port> <TLSPort>8082</TLSPort> <WorkerCount>1</WorkerCount> </API> </Managers> <!-- For Providers --> <Providers> <WebRTC> <Signalling> <Port>3333</Port> <!-- 这里的TLS端口,默认应该是开启的 --> <TLSPort>3334</TLSPort> <WorkerCount>1</WorkerCount> </Signalling> ... </WebRTC> </Providers> <!-- For Publishers --> <Publishers> <LLHLS> <Port>80</Port> <TLSPort>443</TLSPort> </LLHLS> <WebRTC> <Signalling> <Port>3333</Port> <!-- 这里的TLS端口,默认应该是开启的 --> <TLSPort>3334</TLSPort> </Signalling> ... </WebRTC> </Publishers> </Bind> ... <VirtualHosts> <VirtualHost> <!-- For Vitual Host --> <Host> <Names> <Name>*</Name> </Names> <TLS> <!-- 这里修改成你的证书位置 --> <CertPath>/etc/pki/airensoft.com/_airensoft_com.crt</CertPath> <KeyPath>/etc/pki/airensoft.com/_airensoft_com.key</KeyPath> <ChainCertPath>/etc/pki/airensoft.com/_airensoft_com.ca-bundle</ChainCertPath> </TLS> </Host>
完事之后
docker compose down
停止下容器填写好拉流地址
INITIAL_SOURCE_URL=
wss://你的ip或域名:3334/app/stream
再
docker compose up -d
启动所有容器至此你就可以向这个地址推流了
rtmp://你的ip或域名:1935/app
srt://你的ip或域名:9999?streamid=srt%3A%2F%2F你的ip或域名%3A9999%2Fapp%2Fstream
推流码默认为
stream
去官方的Demo player里填写拉流地址
wss://你的ip或域名:3334/app/stream
,看看能不能正常播放
我这里选择了用Nginx Proxy Manager反向代理前端

总之你得用HTTPS来访问前端才可以正常观看

关于推流的协议选择
我测试了SRT和RTMP,在我这里还是RTMP比较好,不管是延迟还是画面都没有问题
但是SRT的话想要低延迟就会出现花屏现象,解决办法就是推流地址后面加个延迟10,0000(100ms)
出现花屏现象就可以适当调高来缓解
srt://你的ip或域名:9999?streamid=srt%3A%2F%2F你的ip或域名%3A9999%2Fapp%2Fstream&latency=500000
总体来说选择RTMP就行
2024年11月26号更新:
突然发现OBS 30版本以后也支持WebRTC推流,更低的延迟!
只要服务选择
WHIP
推流地址就是
https://你的服务器ip:3334/app/stream?direction=whip
结尾
总之就是这样,其实不止这么简单的玩法,官方文档里还给了很多例子,我可能大概似乎好像以后也会去试一试?然后如果成了的话,会继续更新的。。。
扩展阅读
配置多个推流地址
注意,如果你仅仅想要多个直播同时进行,通过改变推流码就可以实现一个直播地址,多个直播源的效果
Server.xml
里配置<Applications> <!-- 推流1 --> <Application> <!-- 每个推流地址的名字要唯一 --> <Name>app</Name> ... </Application> <!-- 推流2 --> <Application> <Name>app1</Name> ... </Application> <!-- 推流3 --> <Application> <Name>app2</Name> ... </Application> </Applications>
请参照官方文档,可以单独设置每个推流地址支持的协议,视频音频编码等等。。。
WebRTC推流 - 使用网页进行直播
前往GitHub打包下载demo文件夹,里面有静态网页文件

然后放到服务器上的某个目录下面

用docker compose部署静态网页,使用的是轻量的nginx镜像
version: "3.3" services: webrtc: image: joseluisq/static-web-server:2-alpine container_name: "website" ports: - 8079:80 restart: unless-stopped environment: # Note: those envs are customizable but also optional - SERVER_ROOT=/var/public - SERVER_CONFIG_FILE=/etc/config.toml volumes: - ./static:/var/public # 把你的静态网页路径映射到容器里 - ./config.toml:/etc/config.toml
用网页直播的静态网页是
demo_input.index
,你可以根据自己的喜好修改相应的内容,有相应的编程知识可以很轻松的修改,在此不在展开,我做了一些小小的汉化和加了一档2k的分辨率,以及把默认的推流地址改成我的地址
这里要注意的是,推流地址是这样的结构
ws(s)://你的服务器ip:TLS端口号/推流名字/推流码?direction=send
OBS WHIP推流地址
https://你的服务器ip:TLS端口号/推流名字/推流码?direction=whip
转码
在
Server.xml
配置文件里添加或修改如下位置<OutputProfiles> ... <OutputProfile> <Name>bypass_stream</Name> <OutputStreamName>${OriginStreamName}</OutputStreamName> <!-- 在此处添加播放列表 --> <Playlist> <Name>for-llhls</Name> <!-- 播放地址如下 LLHLS : http[s]://<domain>[:port]/<app>/<stream>/<FileName>.m3u8 WebRTC : ws[s]://<domain>[:port]/<app>/<stream>/<FileName> Note that the keywords "playlist" and "chunklist" MUST NOT be included in FileName. --> <FileName>llhls</FileName> <!-- Options is optional. --> <Options> <EnableTsPackaging>true</EnableTsPackaging> </Options> <!-- 这里添加各个清晰度 --> <Rendition> <Name>direct</Name> <Video>bypass_video</Video> <Audio>aac_audio</Audio> </Rendition> <Rendition> <Name>1080p</Name> <Video>1080p</Video> <Audio>aac_audio</Audio> </Rendition> </Playlist> <!-- <Playlist> <Name>for-webrtc</Name> <FileName>webrtc</FileName> <Options> <EnableTsPackaging>true</EnableTsPackaging> </Options> <Rendition> webrtc只能使用opus音频编码 <Name>direct</Name> <Video>bypass_video</Video> <Audio>opus_audio</Audio> </Rendition> <Rendition> <Name>1080p</Name> <Video>1080p</Video> <Audio>opus_audio</Audio> </Rendition> </Playlist> --> <!-- 这里设置编码格式名字要和上面Rendition的对应 --> <Encodes> <Video> <Name>bypass_video</Name> <Bypass>true</Bypass> </Video> <Audio> <Name>aac_audio</Name> <Codec>aac</Codec> <Bitrate>128000</Bitrate> <Samplerate>48000</Samplerate> <Channel>2</Channel> <BypassIfMatch> <Codec>eq</Codec> </BypassIfMatch> </Audio> <Audio> <Name>opus_audio</Name> <Codec>opus</Codec> <Bitrate>128000</Bitrate> <Samplerate>48000</Samplerate> <Channel>2</Channel> <BypassIfMatch> <Codec>eq</Codec> </BypassIfMatch> </Audio> <Video> <Name>1080p</Name> <Codec>h264</Codec> <Bitrate>8000000</Bitrate> <!-- 码率单位为bps 1kbps=1000bps --> <Framerate>60</Framerate> <Width>1920</Width> <Height>1080</Height> <Preset>medium</Preset> <!-- 预设详见文档 --> </Video> </Encodes> </OutputProfile> </OutputProfiles>
要注意的是编码非常吃CPU,酌情设置档位及数量
自动更新服务器证书
可以通过acme.sh来方便的申请和更新证书
安装完之后,我这里以cloudflare为例,使用DNS验证来申请证书
export CF_Token="7apkRwxxxqTWLxxxxxmnpNf-jxxxxxxxxX" # 生成api token 权限为编辑dns export CF_Zone_ID="b149cxxxxxcb0xx1c22xxxxe241exxx" # Zone为特定的域名 export CF_Account_ID="fxxxxxxb27d95xxxxb4xxxxxxxx" acme.sh --issue --dns dns_cf -d example.com -d "*.example.com" # 这里导出证书路径以你的为准 acme.sh --install-cert -d example.com \ --cert-file .../cert/cert.pem \ --key-file .../cert/privkey.pem \ --fullchain-file .../cert/fullchain.pem \ --ca-file .../cert/chain.pem
这样我们就不用手动来更新证书了
但是还有一个问题,就是直播服务器它不会自动加载更新后的SSL证书,要么重启容器,要么用服务器的api接口来刷新证书
总之方法有很多,最简单的应该是写一个cron定时任务脚本,过一段时间重启一下容器
但是我,偏偏选择了最难的一种方法
首先我编辑了配置文件,开启了OME的api服务器功能,让它可以接受restful http请求,详见REST API | OvenMediaEngine
然后我使用了iOS shortcuts创建了一个发送SSH指令的捷径,来定时的给我的服务器发送向直播服务器发送刷新证书HTTP请求,然后通知我的Telegram bot,这样我就知道它有没有偷懒了