自建视频流媒体平台:从OBS推流到SRS分发的完整实践指南

发布时间:2026/9/16 8:17:47
自建视频流媒体平台:从OBS推流到SRS分发的完整实践指南
搞视频自建这套东西前前后后折腾了三四年中间踩过的坑比我写过的代码都多。这次想把手里的一个项目完整拆开聊聊项目名就叫LunaTV一个自建视频流媒体服务平台。Luna是月神的名字取这个名倒没什么深意就是希望它安静、稳定、夜里有光。现在我已经把它跑在公网VPS上给一个小圈子里的朋友做直播和点播用效果相当稳。LunaTV不是什么商业产品也不是开源大作它就是一套完全自管理的视频直播与点播方案核心链路是OBS推流 - SRS媒体服务器接收 - RTMP/WebRTC入流 - HLS/HTTP-FLV分发 - 浏览器播放器拉流观看。你可以把它理解成一个私有化的“小电视台”内容完全归自己控制。适合谁参考如果你是一个想要给社群做稳定直播的技术博主或者纯粹想把家里的监控、窗外风景、宠物画面做成一个长期直播链接分享给朋友又或者需要一个可归档、可重看的私有视频存储系统这篇东西应该能帮你省掉好几周摸索的时间。我做这套东西的时候市面上不是没有现成的方案。B站直播、视频号、YouTube都能一键开播为什么还要自己搭一套答案很简单控制权和数据隐私。平台直播有审核规则、有延时、有广告插播最要命的是内容不由你决定去留。LunaTV这套方案里流是自己收的片子是自己存的推流密码自己管连播放器样式都能改成自己想要的。说白了这是一个折腾过的人才写得出来的完整过程记录包含我踩过的大部分坑和最终跑通的配置。1. 内容整体设计与思路拆解1.1 这个方案到底要解决什么问题在决定自建之前我先列了一下痛点写得很直白。第一平台直播的延迟不可控交互性强的场景根本没法用。第二平台内容审核机制不可控不过审就全白干。第三直播结束之后想保存一个高质量回放平台会压缩画面还会在某个时间自动清理。第四我需要同时服务固定的一批人不想让第三方平台每开一场直播就生成一个新链接我想给一个固定的入口内容持续归档。LunaTV的核心设计目标因此定成五个词低延迟、高可控、可归档、免维护、能看回放。低延迟我走了WebRTC和HTTP-FLV两条线本地测试的时候端到端延迟压到1到3秒高可控体现在所有配置都在一个web管理面板里完成可归档靠的是SRS的录制回放功能免维护意思是不需要我在直播现场盯着看跑定时脚本加探活就够了回放功能就绑定在录制文件上一台服务器全搞定。围绕这五个目标我放弃了几个看起来很美的方案。比如一开始考虑过用Nginx搭配RTMP模块经典但老气性能瓶颈明显。也考虑过直接上GStreamer搭建全链路灵活是灵活但运维成本太高不适合长期跑。最后选了SRS不是因为它功能最全而是因为它干的事情足够聚焦设计上就是一个为直播和流媒体分发而生的服务器RTC、RTMP、HLS、HTTP-FLV、SRT全都支持社区也活跃踩坑时能搜到答案。1.2 方案选型的具体考量和取舍逻辑先给一个我当时做选型的对比表这份表多少能反映出我在做技术决策时的思考方式方案延迟表现部署难度运维成本协议支持适合场景Nginx RTMP模块中3-5秒低中RTMP为主HLS需额外插件轻量直播临时跑一跑GStreamer全链路低1-2秒高高任意全靠自己搭研究实验不适合长期生产SRS低1-3秒低低RTC/RTMP/HLS/HTTP-FLV/SRT全支持个人与中小规模生产商业云直播低极低极低全面但封闭有预算且不想自己管我当时毫不犹豫选了SRS。一个核心理由是它的部署友好度一个二进制文件就够配置文件是扁平化的官方文档写得非常实用每一项参数的默认值都经过生产验证不需要我再去猜。另外一个理由是它的HLS切片能力在个人服务器上性能表现很好实测4M码率的1080p直播流切片在普通机械硬盘上也能跑得动没有任何卡顿这就给回放功能打好了底子。对比之下Nginx加RTMP组合的问题在于Nginx本身不是为流媒体设计的RTMP模块的生态也停更了很久。虽然网上教程最多但它解决问题的路径往往是“要HLS就再配一个ffmpeg切片”整体链路拉得很长出问题的时候排查也麻烦。如果你只是想偶尔开一次直播Nginx方案勉强能用但如果你像我一样想要一个长期稳定、带录制归档的私有电视台直接上SRS一天就能跑通没必要走弯路。1.3 整体架构与数据流向LunaTV整个架构可以拆成四层。采集层我用OBS Studio做推流客户端它负责把摄像头画面、屏幕内容、音频混合成一个输出流。接入层SRS监听1935端口接收RTMP推流同时监听1985端口提供HTTP API和HTTP-FLV服务此外还启动了一个WebRTC端口做低延迟实时交互。分发层SRS内部把RTMP流转封装成HLS切片切成一个个几秒钟的.ts文件同时通过播放器拉取m3u8索引来观看HTTP-FLV则是推流端直接可用的低延迟分发协议。存储层SRS开启了录像功能直接把收到的RTMP流写成flv文件放在指定目录方便后续处理或者直接当点播资源。数据流向比想得简单OBS把流推到rtmp://你的服务器地址:1935/live/lunatvSRS接收后同时做三件事一是转成HLS切片文件放到web目录二是作为HTTP-FLV源直接喂给网页播放器三是以flv文件形式落盘存档。浏览器端播放器的逻辑是优先用HTTP-FLV做直播延迟低如果浏览器不支持比如Safari就走HLS。这套分流设计让我在电脑Chrome上延迟只有2秒左右手机Safari也能稳定播放。2. 核心细节解析与实操要点2.1 视频流的三个关键环节整个链路里最值得花时间理解的是三个环节采集与推流、服务端转封装、播放端拉流。采集与推流核心是不要再传一遍无损画面。很多人第一次用OBS推流直接选“无损”或者极高码率结果带宽全被吃光服务器瞬间傻眼播放端反而卡成幻灯片。实际做法是根据你上行带宽和服务器下行带宽来定码率个人场景1080p推流给3000到5000kbps够了画面清晰度完全够用720p的话2000到3000kbps更稳。为什么是这个范围因为1080p的H.264编码在3000kbps以上已经能保证大部分动态画面不出现明显马赛克再低就会糊得没法看再高则对服务端转码和客户端解码都带来压力。服务端转封装这里的“转封装”不是转码。SRS默认不改变流的编码格式它只是把RTMP协议封装里的数据重新打包成HLS切片或者FLV标签。这意味着H.264/H.265的视频流、AAC的音频流都需要你的推流端先把原始画面和声音压缩好。理解这一层很重要因为很多人在服务器上找转码参数找不到其实SRS默认就没有转码引擎。如果你确实需要服务器对视频重新编码得单独引入FFmpeg作为转码中间层但个人场景一般不需要推流端编码就够了。播放端拉流这一层最大的坑是“浏览器兼容性”。PC端Chrome、Firefox对HTTP-FLV支持不错Safari则就走HLS移动端iOS Safari原生支持HLS安卓Chrome则更依赖HTTP-FLV。为了不做一个只兼容一半的播放器我在前端集成了两个核心解析器mpegts.js处理HTTP-FLVhls.js处理HLS。这样一套页面在几乎所有主流浏览器里都能稳定播放用户根本不用手动装插件。2.2 转码与码率参数的“为什么”很多教程上来就让你把参数照抄但参数背后的逻辑才是真正值钱的部分照抄不一定是适合你的场景。先讲码率。公式其实很简单码率等于单帧数据量乘以帧率单位是kbps或Mbps。画面分辨率定下来后编码复杂度越高单帧数据量越大。所以1080p60的视频比1080p30需要更高码率才能保持同样画质。我当时按3个场景做了三个档位场景分辨率帧率码率关键帧间隔屏幕分享/教程1920x1080303500kbps2秒唱歌/聊天类直播1280x720302500kbps2秒户外长时间挂机直播854x480241000kbps4秒关键帧间隔这一点特别容易被忽略但它在低延迟播放里作用非常大。HTTP-FLV和HLS本质上是边下载边播放播放器只有拿到关键帧才能开始解码如果关键帧间隔太长观众打开页面的时候就要等好几秒才能出画面。设成2秒意味着每2秒一个关键帧观众基本是秒开体验。代价是这个关键帧体积比较大会多一些带宽消耗实际测算下来关键帧在总码率里占的比例不超过10%这点开销换低延迟绝对划算。再说音频部分个人项目里音频编码用AAC-LC采样率44100Hz码率128kbps足够。如果做纯音乐类直播可以上256kbps但一般128就够了。音频参数上很多人犯的错是跟着视频码率一起往上抬纯属浪费带宽。128kbps的AAC-LC在听感上已经超过大部分人的日常需求把它当成固定值就行。2.3 播放协议选型与延迟实测选播放协议这事其实可以一句话总结直播要低延迟就HTTP-FLV加WebRTC回放和兼容面优先就HLS。但这背后有一些实验数据支撑这里直接放我自己的实测结果协议端到端延迟浏览器兼容首帧耗时适用场景HTTP-FLV1-3秒Chrome/FF优秀Safari较差500msPC端低延迟直播HLS5-10秒几乎所有浏览器1-3秒跨端兼容直播/回放WebRTC800ms现代浏览器均支持300ms互动连线、低延迟对谈我在LunaTV里默认开了HTTP-FLV和HLS两种协议。网页播放器先探测浏览器是否支持FLV如果支持就拉HTTP-FLV流不支持就自动切HLS。这个切换逻辑是整个播放器最核心的部分一开始我没做自动切换结果一半观众流畅一半观众卡最后才发现是浏览器兼容问题。有一点必须提醒WebRTC虽然延迟最低但它在服务器端对带宽和CPU消耗都更大一个WebRTC观众另外还需要单独维护一条连接不像HTTP-FLV那样走普通HTTP就能多路复用。所以我的策略是默认场景不用WebRTC只在做互动问答之类的场景才临时切换既保证体验又控制成本。3. 实操过程与核心环节实现3.1 服务器选型与环境初始化搭建LunaTV首先得有一台公网可达的服务器。我选的是2核4G内存的云主机带宽30Mbps系统Ubuntu 22.04 LTS。为什么这个配置2核主要给SRS和可能的FFmpeg转码进程用4G内存足够跑SRS加录制模块30Mbps带宽在个人直播场景下可以容纳约10个1080p观众同时观看性价比合适。如果你主要做小圈子直播2核2G带宽10M就能跑但录制回放和切片会稍微吃力一点内存不足时HLS切片会有延迟。装好系统后先更新基础环境sudo apt update sudo apt upgrade -y sudo apt install -y git wget curl build-essential然后下载SRS。我直接用官方release版本没有从源码编译省时间也稳定cd /opt wget https://github.com/ossrs/srs/releases/download/v5.0.5/srs-5.0.5-linux-amd64.tar.gz tar xf srs-5.0.5-linux-amd64.tar.gz mv srs-5.0.5-linux-amd64 srs cd srsSRS的二进制包解压就能用这点是真的友好。我用的是v5版本配置文件在conf/srs.conf不过我没有直接改默认配置而是新建了一个lunatv.conf保持默认配置可用作回滚。3.2 SRS配置与启动这份配置是我经过数次调整后一直在用的每一个参数都有明确意图listen 1935; max_connections 1000; daemon on; pid ./objs/srs.pid; http_api { enabled on; listen 1985; } http_server { enabled on; listen 8080; dir ./objs/nginx/html; } rtc_server { enabled on; listen 8000; candidate $CANDIDATE; } vhost __defaultVhost__ { hls { enabled on; hls_path ./objs/nginx/html; hls_fragment 4; hls_window 60; } http_remux { enabled on; mount [vhost]/[app]/[stream].flv; } dvr { enabled on; dvr_path ./objs/nginx/html/record/[stream].[timestamp].flv; dvr_plan session; } play { gop_cache on; queue_length 10; } }解释几个关键项。hls_fragment设成4秒意思是每个切片4秒长适合快速起播hls_window设成60秒表示播放器最多往回看60秒的直播内容。这是直播的滑动窗口超过了就会被清理不用担心存储爆炸。dvr_plan设成session意思是每次推流开始到结束落盘成一个完整的FLV文件对回放归档最友好。gop_cache开起来是为了让观众进入直播间时能快速首帧代价是多缓存一个关键帧组这个缓存机制实测让秒开体验提升非常明显。启动命令export CANDIDATE你的服务器公网IP ./objs/srs -c conf/lunatv.conf如果不设CANDIDATE变量WebRTC会拿不到正确IP这是SRS里最容易翻车的地方之一。启动后确认一下日志看到“start server successfully”就说明起来了。3.3 OBS推流参数设置服务端就绪后客户端用OBS推流。在“设置-直播”里服务选择“自定义”服务器填rtmp://你的服务器IP:1935/live/串流密钥填一个你想用的流名称比如lunatv。这样完整推流地址就是rtmp://你的服务器IP:1935/live/lunatv。OBS的“输出”设置里建议按我之前给你的参数来视频编码器用硬件编码器NVENC或AMD码率按场景选择CPU编码也行但会占用较多机器资源可能导致画面掉帧。音频编码器选AAC采样率44100码率128。视频设置里基础分辨率和输出分辨率保持一致不要开自动缩放缩放会带来额外的CPU和画质损失。一个很重要的小细节是OBS里的“关键帧间隔”。它默认是0自动但自动模式下间隔可能过大我会手动设成2秒配合SRS的gop_cache观众进入直播间时基本能做到秒开。另一个小细节是把“麦克风/辅助音频”的采样率设成44.1kHz避免音频采样率不匹配导致的声音发闷。OBS开始推流后可以先去服务端验证一下是否收到了流curl http://127.0.0.1:1985/api/v1/streams/如果返回里能看到applive、namelunatv这样的信息就说明流已经进来了。3.4 网页播放器接入播放页面是最直接面对观众的部分。我写了一个简化版播放器页面集成了HTTP-FLV和HLS两个解析器。首先是引入依赖script srchttps://cdn.jsdelivr.net/npm/mpegts.js1.2.1/dist/mpegts.js/script script srchttps://cdn.jsdelivr.net/npm/hls.js1.5.7/dist/hls.min.js/script然后核心播放逻辑const streamName lunatv; const baseUrl 你的服务器IP:8080; function initPlayer() { const videoElement document.getElementById(video); // 优先尝试HTTP-FLV if (mpegts.isSupported()) { const flvUrl http://${baseUrl}/live/${streamName}.flv; const player mpegts.createPlayer({ type: flv, isLive: true, url: flvUrl }); player.attachMediaElement(videoElement); player.load(); player.play(); } else if (Hls.isSupported()) { // Safari等不支持FLV时走HLS const hlsUrl http://${baseUrl}/live/${streamName}.m3u8; const hls new Hls(); hls.loadSource(hlsUrl); hls.attachMedia(videoElement); hls.on(Hls.Events.MANIFEST_PARSED, () videoElement.play()); } } initPlayer();这个页面的核心就是先探测支持哪个协议再选择对应的解析器。mpegts.js支持HTTP-FLV直接用浏览器原生Media Source Extensions解码hls.js同理但走的是HLS。实测下来Chrome首选FLV首帧700毫秒左右Safari走HLS大概2秒出画面整体体验非常顺滑。当时写这个播放器页面时有个地方反复改了几次。就是mpegts.js的参数里isLive: true必须显式设置不然默认按点播逻辑去缓冲直播延迟能飙到十几秒。改完之后延迟立刻从12秒降到2秒左右这个细节强烈建议注意。3.5 点播回放与录制归档SRS的dvr录制模块会让每次推流自动生成一个FLV文件存放在配置里指定的record目录。但FLV格式不适合直接做点播浏览器原生播放不了所以我在LunaTV的回放模块里加了一步用FFmpeg把FLV转成MP4然后放到Nginx静态目录供点播。转换命令很直接ffmpeg -i /path/to/record/xxx.flv -c copy /path/to/archive/xxx.mp4用-c copy是直接复制编码数据不重新编码所以速度极快一个1小时的直播文件大概1分钟内就能转完画质零损失。然后我就写了个小脚本每天凌晨扫描record目录把前一天新生成的FLV转成MP4并移入点播目录同时清理老的FLV源文件。这样直播和点播就形成了完整的闭环。点播播放就更简单了HTML5的video标签原生支持MP4不需要额外写播放器逻辑。放一个视频列表页扫一下目录里的文件列成链接就能看回放。4. 常见问题与排查技巧实录4.1 播放黑屏却显示流在推这个问题我遇到过好几次特征是OBS显示推流稳定服务器也能看到流但网页播放器黑屏。排查思路要顺着协议链路走。先确认服务器端的流状态用SRS的API查询curl http://127.0.0.1:1985/api/v1/streams/ | jq如果流确实存在再看播放器拉的是哪个地址。HTTP-FLV的问题通常是端口或者路径写错比如8080端口没开放或者flv路径里的vhost、app、stream没对齐。HLS黑屏则最常见于切片目录下没有生成.m3u8文件而此时能生成说明hls模块配置没启用。我手头遇到最多的场景是改配置后忘了重启SRS改完hls_fragment之类的参数不重启的话根本不生效。4.2 推流画面延迟越来越大正常SRS配置下直播延迟应该在2到5秒内。如果你发现观众看到的画面比实际时间慢很多甚至越来越慢十有八九是播放器缓存和缓冲设置的问题。mpegts.js默认会用较大的缓冲来抗网络抖动这在点播场景很好但在直播场景是灾难。需要把参数调成低延时模式const player mpegts.createPlayer({ type: flv, isLive: true, url: flvUrl, lazyLoad: false, liveBufferLatencyChasing: true, liveBufferLatencyMaxLatency: 3, liveBufferLatencyMinRemain: 1 });这几个参数的意义在于告诉播放器不要无限堆积缓冲区当延迟超过3秒就开始追帧最小保留1秒缓冲保证流畅。加了这套配置后我的直播延迟稳定在2秒左右观感像是真的在“直播”。服务器端也能配合调整gop_cache on为观众提供快速起播但前提是缓存窗口不要开太大。如果SRS配置了很大的gop缓存新观众进入时会先加载缓存的关键帧组反而会感觉到较大的延迟所以配置窗口要克制4秒一个切片、缓存60秒就够。4.3 公网访问不通与安全防护跑公网服务最常见的问题是端口没开。LunaTV依赖3个端口1935RTMP推流端口、1985HTTP API端口、8080HTTP播放端口另外WebRTC用8000/UDP。云服务商的安全组和服务器iptables都要放行这些端口。当时我排查了很久才发现是云平台安全组里默认不开放1935推流一直报连接超时。安全方面有几个优先级很高的建议播放和推流地址不要让外网随便猜。串流密钥用一个足够长的随机字符串比如lunatv_9f8d7c6b5a4e这种不要用test、live这种弱密钥。SRS的HTTP API端口最好绑定在内网或者限制IP不然任何人调用API都能看到你的流列表。录制文件目录不要直接暴露到公网除非你有意做公开点播否则配置一个访问鉴权会更安全。如果长期跑建议在Nginx反代层开启Basic Auth或者token验证保护播放页面。4.4 观众并发上来后卡顿个人直播通常不会遇到特别大的并发但难免有朋友拉好几个平台转播的情况。如果200人在线30Mbps带宽就会被打满。这里可以做个简单的估算每人观看1080p流大约需要3.5Mbps带宽。30Mbps理论上只够8个人流畅观看一旦观众再多就开始出现缓冲。优化手段有几个降码率分发。在SRS前面加一层转码给观众提供一个720p的档位每人只占2Mbps左右能多容纳近一半观众。走CDN分发。自建边缘节点成本高个人项目直接买云直播CDN按量付费最划算。限制单路流并发。如果只是给几个朋友看的直播在Nginx层面限制连接数就行避免服务器被拖垮。我当时的选择是直接限制并发超过8个观众就暂时拒绝额外连接宁可少数人看流畅也不要几百个人卡成PPT。5. 后续扩展与长期维护体会5.1 可扩展的方向LunaTV跑通之后可延展的空间其实很大。我最想分享的几个方向是多清晰度切换。在SRS前面挂一个FFmpeg转码服务把收到的1080p流转成720p和480p两路然后播放器根据网速自动切换档位。这是现成视频平台的基本功能自建也能做到只是需要额外跑转码进程对服务器CPU要求会高一截。弹幕系统。给直播页面接一个轻量级WebSocket服务观众发弹幕实时显示配合HTTP-FLV低延迟直播互动体验可以做得非常像商业产品。多机位直播。SRS天然支持多路流同时推入不同app或stream名网页端用JavaScript控制切换播放源就能实现简单的导播台效果适合一些简单的演唱会或者课程直播。录制文件自动归档到对象存储。本地硬盘满了以后把录制的flv转MP4再传到对象存储等于做了一个私有长视频库永远不用担心存储空间。5.2 我在实际运行中的几个体会第一能不转码就不转码。在个人服务器上FFmpeg转码非常吃CPU跑一路1080p转720p的转码进程CPU占用率轻松上到60%以上。推流端编码是免费的服务端转码却要花钱买CPU所以个人项目尽量在OBS里直接输出目标码率少做服务端转码。第二录制文件的存储规划要趁早做。我的常驻直播流是24小时开着的一个晚上下来FLV录制文件能到10G以上。一个月就是300G这个增长速度远超我的预估。合理做法是录制文件只保留最近一周点播归档后同步删除原始文件。第三名片式的推广页面很重要。LunaTV做出来以后我给朋友分享的不是一个裸播放器地址而是一个简单的落地页上面有直播画面、节目时间表、回放列表和推流状态。这看起来只是很小的前端工作但从观众体验角度来说它的价值可能比后端技术本身还要大。一个统一入口的“私人电视台”形象一下子就能立起来。最后说一个运维层面的小技巧。SRS长时间运行后内存会有缓慢增长我写了一个定时探活脚本每10分钟请求一次API连续3次失败就自动重启SRS进程。同时配了一个磁盘清理任务超过15天的录像文件自动删除。有了这两个脚本LunaTV基本达到了“部署一次后两周不用管”的状态我只需要偶尔看一眼监控面板确认它在正常转其他时间完全可以安心睡觉。如果你也想折腾一套类似的东西我的建议是别一上来就追求功能全先把最核心的链路跑通推流、播放、录制。这三件事通了后面加东西都只是锦上添花。踩坑是难免的但每一步踩坑之后换来的稳定运行才是这套自建方案真正值钱的地方。