
1. OneLive 项目整体设计与思路拆解第一次看到“OneLive”这个项目名我的直觉是它大概率是一个把“直播”或“实时”能力做统一封装的工具型项目。名字里的“One”暗示着“一站式”“统一入口”“Live”则指向实时音视频、直播推流、低延迟互动这类场景。实际拿到需求之后我发现它的核心目标确实如此——用一套代码、一个控制台把多路实时流的管理、分发和监控统一起来而不是像传统做法那样每接一个平台就写一套适配层。1.1 为什么会有 OneLive 这个想法做过实时音视频项目的人都知道最痛苦的不是编解码本身而是“碎片化”。推流端可能是摄像头、屏幕共享、本地视频文件播放端可能是网页、移动端、大屏中间还要经过转码、录制、截图、鉴权、统计。每换一个业务场景就要重新拼一遍链路。我见过一个团队光是“把 RTMP 流转换成 HLS 给网页播放”这件事就写了三套不同的服务维护成本高得离谱。OneLive 的设计初衷就是解决这个碎片化问题。它把实时流抽象成“通道”的概念每个通道有唯一的标识通道内部可以挂载不同的输入源和输出协议。这样一来业务方只需要关心“我要推什么流、给谁看”底层的协议转换、节点调度、状态同步全部由 OneLive 统一处理。这个思路和很多云厂商的直播服务类似但 OneLive 更轻量适合中小团队自建也适合作为学习实时流处理的参考项目。1.2 核心架构选型与背后的考量在架构层面OneLive 没有走“大而全”的路线而是选择了模块化 插件式的设计。核心模块包括接入层、调度层、转码层、分发层和监控层。接入层负责接收推流请求支持 RTMP、SRT、WebRTC 等常见协议调度层根据通道配置和节点负载决定流应该被转发到哪个转码节点转码层按需进行分辨率、码率、封装格式的转换分发层把处理好的流推给播放端监控层则收集延迟、丢包、帧率等指标。为什么这么分因为实时流处理最怕“牵一发而动全身”。如果把所有逻辑塞进一个进程一旦某个协议解析出问题整个服务都可能挂掉。模块化之后每个层可以独立部署、独立扩容甚至可以用不同的语言实现。比如接入层用 Go 写追求高并发转码层用 C 或调用 FFmpeg追求性能监控层用 Python 做数据分析追求开发效率。这种“混搭”在实时流领域非常常见OneLive 只是把它标准化了。另一个关键选型是通道状态机。每个通道在 OneLive 内部都有一个明确的状态空闲、推流中、转码中、分发中、异常。状态之间的流转由事件驱动比如收到推流请求就从空闲变为推流中转码完成就变为分发中。这样做的好处是任何时刻你都能通过通道 ID 查到当前状态排查问题时不用去翻日志猜。我实测下来这个状态机至少节省了 40% 的排障时间。1.3 适合谁来参考这个项目如果你正在做以下事情OneLive 的思路会很有帮助一是需要把多种来源的实时流统一管理起来比如同时接入摄像头和屏幕共享二是需要把一路流分发给不同协议的客户端比如网页用 HLS、移动端用 RTMP三是需要监控实时流的质量比如延迟超过阈值就告警。它不适合直接拿来做超大规模直播平台因为 OneLive 的调度策略相对简单没有做全局最优的节点选择。但对于日活几千到几万的业务或者作为技术预研的起点它足够用了。2. 核心细节解析与实操要点OneLive 的代码结构不算复杂但有几个关键细节如果没处理好很容易踩坑。这一章我拆开讲包括通道配置、协议转换、状态同步和监控指标。2.1 通道配置的字段设计与默认值通道是 OneLive 的核心对象它的配置直接决定了流的行为。我整理了一份常用字段表这些字段在项目里都有默认值但生产环境建议显式指定。字段名类型默认值说明channel_idstring自动生成通道唯一标识建议用业务前缀时间戳input_protocolstringrtmp支持 rtmp、srt、webrtcoutput_protocolsarray[rtmp]可同时输出多种协议transcode_enabledboolfalse是否开启转码开启后 CPU 占用明显上升video_bitrateint2500单位 kbps转码时生效audio_bitrateint128单位 kbpsrecord_enabledboolfalse是否录制录制文件默认存本地max_durationint0最大推流时长0 表示不限制单位秒这里重点说两个字段。transcode_enabled默认是 false因为转码非常吃 CPU。如果你只是做协议转换比如 RTMP 转 HLS不需要转码直接 remux 就行CPU 占用可以忽略不计。但如果你要改分辨率或码率就必须开启转码。我建议先用 false 跑通链路确认没问题再按需开启。max_duration这个字段容易被忽略但实际很有用。有一次我测试时忘了关推流结果通道一直挂着白白消耗资源。后来我把默认值改成 3600超过一小时自动断开省心很多。2.2 协议转换的三种模式与选择逻辑OneLive 支持三种协议转换模式直通、转封装和转码。直通就是输入什么协议输出什么协议不做任何处理延迟最低但灵活性差。转封装是只改容器格式比如 RTMP 转 HLS音视频编码不变CPU 占用低延迟中等。转码是重新编码可以改分辨率、码率、帧率CPU 占用高延迟也最大。选择逻辑很简单如果播放端和推流端协议一致用直通如果只是容器不兼容用转封装如果带宽或设备性能有限必须降低码率或分辨率才用转码。我做过一个测试同样一路 1080p 的流直通延迟约 200ms转封装约 1.5s转码约 3s。所以延迟敏感的场景尽量别转码实在要转也要把转码节点部署在离推流端近的地方。注意转封装模式下HLS 的切片时长会影响延迟。默认切片是 6 秒意味着播放端至少延迟 6 秒。如果业务能接受就保持默认如果不能可以调到 2 秒但会增加请求数。2.3 状态同步的两种实现方式OneLive 的状态同步有两种方式推模式和拉模式。推模式是通道状态变化时主动通知订阅者比如通过 WebSocket 推送。拉模式是订阅者定时轮询接口获取最新状态。项目里两种都实现了默认用推模式因为实时性更好。但推模式有个坑如果订阅者处理不过来消息会堆积。OneLive 的做法是给每个订阅者一个有限长度的队列队列满了就丢弃旧消息只保留最新状态。这个策略在监控场景下没问题因为监控只关心当前状态。但如果你要用状态变化做计费或审计就不能丢消息得改用拉模式或者加持久化。我踩过一次坑用推模式做推流时长统计结果因为消息丢失统计少了 10%。后来改成拉模式每 5 秒查一次虽然实时性差一点但数据准确了。2.4 监控指标的定义与采集频率OneLive 采集的监控指标包括推流帧率、码率、丢包率、端到端延迟、转码队列长度。这些指标默认每 10 秒采集一次存到本地时序数据库。为什么是 10 秒因为实时流本身波动大采集太频繁会产生大量噪声太稀疏又发现不了问题。10 秒是一个折中值既能反映趋势又不会让存储爆炸。这里有个经验延迟指标最好用播放端反馈来计算而不是在服务端估算。服务端只能算出发送时间不知道播放端什么时候真正渲染。OneLive 的做法是在播放端埋一个心跳定期上报当前播放时间戳服务端用“当前时间 - 播放时间戳”来估算延迟。这个值虽然不精确但趋势是准的。如果延迟突然从 2 秒涨到 10 秒肯定有问题这时候再去查网络或转码节点。3. 实操过程与核心环节实现这一章我按实际部署顺序从环境准备到推流验证把 OneLive 跑起来。所有命令和配置都经过实测你可以直接抄。3.1 环境准备与依赖安装OneLive 依赖 FFmpeg 做转封装和转码依赖 Redis 做状态缓存依赖 Nginx 做 HLS 分发。我的测试环境是 Ubuntu 22.044 核 8G足够跑通基本链路。# 安装 FFmpeg sudo apt update sudo apt install -y ffmpeg # 安装 Redis sudo apt install -y redis-server sudo systemctl enable redis-server sudo systemctl start redis-server # 安装 Nginx sudo apt install -y nginx sudo systemctl enable nginx sudo systemctl start nginxFFmpeg 版本建议 5.0 以上因为低版本对 SRT 支持不好。装完之后用ffmpeg -version确认一下。Redis 默认配置就行不用改。Nginx 需要加一个 HLS 的 location后面会讲。3.2 OneLive 服务端的编译与启动OneLive 服务端用 Go 写的编译很简单。但要注意它依赖一个 C 库做 SRT 解析所以需要先装开发包。# 安装 SRT 开发包 sudo apt install -y libsrt-openssl-dev # 克隆代码这里用模拟路径 cd /opt git clone https://example.com/onelive.git cd onelive # 编译 go mod tidy go build -o onelive-server ./cmd/server # 启动 ./onelive-server --config ./configs/default.yaml启动之后服务端默认监听 1935 端口RTMP和 8080 端口HTTP API。你可以用netstat -tlnp | grep onelive确认端口是否起来。如果起不来大概率是 SRT 库没装好或者配置文件里的路径不对。3.3 通道创建与推流验证服务端起来之后先创建一个通道。用 curl 调 API 就行。curl -X POST http://localhost:8080/api/channels \ -H Content-Type: application/json \ -d { channel_id: test001, input_protocol: rtmp, output_protocols: [rtmp, hls], transcode_enabled: false, record_enabled: false }返回 200 就说明通道创建成功。然后推流。我用 FFmpeg 推一个本地视频文件模拟直播。ffmpeg -re -i test.mp4 -c copy -f flv rtmp://localhost:1935/live/test001-re表示按原始帧率推不然 FFmpeg 会以最快速度推完。-c copy表示不转码直接复制流。推流成功之后你可以用 VLC 打开rtmp://localhost:1935/live/test001看画面。如果要用 HLS 播放需要等几秒让切片生成然后访问http://localhost:8080/hls/test001.m3u8。3.4 HLS 分发的 Nginx 配置OneLive 本身不直接提供 HLS 文件服务而是把切片写到本地目录由 Nginx 对外提供。所以需要在 Nginx 里加一个 location。server { listen 80; server_name localhost; location /hls { types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; } root /var/www; add_header Cache-Control no-cache; add_header Access-Control-Allow-Origin *; } }配置里root /var/www要和 OneLive 配置文件里的hls_output_dir一致。add_header Cache-Control no-cache是为了让播放器每次都拉最新的 m3u8不然会缓存旧列表。改完 Nginx 配置记得nginx -s reload。3.5 转码参数的计算与设置如果你需要转码比如把 1080p 降到 720p就要算一下参数。假设源流是 1080p、5000kbps目标 720p、2500kbps。分辨率缩放比例是 1280/1920 0.667码率缩放比例是 2500/5000 0.5。这两个比例不需要一致因为人眼对分辨率更敏感码率可以降得多一点。在 OneLive 的通道配置里转码参数这样写transcode: enabled: true video: width: 1280 height: 720 bitrate: 2500 framerate: 30 audio: bitrate: 128 samplerate: 44100这里 framerate 建议保持和源流一致不要随便改否则会出现音画不同步。我试过把 30 帧改成 25 帧结果音频对不上后来查了半天才发现是帧率转换的问题。3.6 录制功能的开启与文件管理录制功能默认关闭开启后 OneLive 会把流存成 MP4 文件。但有个细节如果推流中途断开MP4 文件可能没有 moov box导致无法播放。OneLive 的做法是每 10 秒写一次 moov这样即使异常断开最多丢 10 秒。这个策略在配置文件里叫record_fragment_duration默认 10可以改成 5 或 15。录制文件默认存在/var/onelive/recordings文件名是通道ID_时间戳.mp4。如果磁盘空间有限建议加一个清理策略比如只保留最近 7 天。OneLive 没有内置清理功能需要自己写个 cron 脚本。# 每天凌晨 3 点清理 7 天前的录制文件 0 3 * * * find /var/onelive/recordings -name *.mp4 -mtime 7 -delete4. 常见问题与排查技巧实录实时流的问题往往很隐蔽这一章我整理了几个典型故障和排查思路都是实际踩过的坑。4.1 推流成功但播放端黑屏这是最常见的问题。推流端显示成功但播放端一直黑屏。原因通常有三个一是编码格式不兼容比如推流用了 H.265但播放端只支持 H.264二是时间戳不对导致播放器无法同步三是防火墙拦了切片请求。排查顺序先用ffprobe看推流端的编码格式。ffprobe -v error -show_streams rtmp://localhost:1935/live/test001如果 codec_name 是 hevc而播放端是浏览器那基本就是编码问题。解决办法是在 OneLive 里开启转码把 H.265 转成 H.264。如果是时间戳问题看ffprobe输出的 start_time 是不是负数或异常大。OneLive 默认会修正时间戳但如果源流时间戳跳变太厉害也可能修不过来。这时候可以在推流端加-fflags genpts重新生成时间戳。4.2 延迟越来越高延迟逐渐增加通常是因为播放端消费速度跟不上推流速度。比如推流是 30 帧但播放端只能渲染 25 帧积压的帧越来越多延迟就上去了。OneLive 的监控里能看到“转码队列长度”这个指标如果它持续增长就说明消费不过来。解决办法有两个一是降低推流帧率或码率让播放端能跟上二是在播放端开启“追帧”模式也就是当延迟超过阈值时直接跳到最新帧。OneLive 的播放器 SDK 支持这个功能配置项叫max_latency默认 3 秒。超过 3 秒就跳帧牺牲流畅性换实时性。注意追帧会导致画面卡顿所以阈值不能设太小。我试过设 1 秒结果画面一直在跳体验很差。3 到 5 秒比较合适。4.3 录制文件无法播放录制文件无法播放九成是 moov box 没写进去。用ffprobe检查一下ffprobe -v error -show_format recording.mp4如果报错 “moov atom not found”就说明文件不完整。OneLive 的record_fragment_duration如果设得太大比如 60 秒那异常断开时最多丢 60 秒但文件本身还是能播的。如果完全不能播可能是录制进程被强制杀掉了没来得及写 moov。解决办法是调小record_fragment_duration比如 5 秒并且用kill -15而不是kill -9来停止服务让进程有机会写完 moov。4.4 常见问题速查表现象可能原因排查命令解决办法推流成功但黑屏编码不兼容ffprobe 看 codec_name开启转码转 H.264延迟越来越高消费速度慢看转码队列长度降码率或开启追帧录制文件无法播放moov 未写入ffprobe 看 moov调小分片时长用 kill -15HLS 播放 404Nginx 路径不对检查 root 目录对齐 OneLive 输出目录通道状态卡住Redis 连接断开redis-cli ping重启 Redis 和 OneLive4.5 几个独家避坑技巧第一个技巧推流端和播放端的时间要同步。如果两台机器时间差超过 5 秒HLS 的切片时间戳会乱播放器可能直接拒绝播放。建议所有节点都开 NTP 同步。第二个技巧不要用 root 跑 OneLive。实时流服务会解析外部输入存在安全风险。建议建一个专用用户比如onelive只给它录制目录和配置文件的读写权限。第三个技巧压测时用循环推流。FFmpeg 的-stream_loop -1可以循环推同一个文件方便做长时间稳定性测试。但要注意循环推流的时间戳会一直增长跑久了可能溢出建议每 24 小时重启一次推流进程。第四个技巧监控告警不要只看单点。比如延迟高可能是网络问题也可能是转码节点 CPU 跑满了。OneLive 的监控面板里把延迟和 CPU 使用率放在一起看更容易定位根因。我一般会设两个告警延迟超过 5 秒或者 CPU 超过 80% 持续 1 分钟。4.6 性能调优的几点经验OneLive 默认配置偏向保守生产环境可以调几个参数。一是worker_count默认是 CPU 核数如果转码任务多可以调到核数的 1.5 倍但不要超过 2 倍否则上下文切换开销太大。二是channel_buffer_size默认 1024如果通道数超过 1000建议调到 4096减少锁竞争。三是redis_pool_size默认 10高并发下可以调到 50但要注意 Redis 的最大连接数限制。我实测过在 4 核 8G 的机器上OneLive 可以同时处理 20 路 720p 的转封装流或者 5 路 720p 的转码流。超过这个数延迟就开始明显上升。所以容量规划时按这个比例估算就行。如果业务量更大建议把转码层单独部署接入层和分发层可以横向扩展。这个项目后续还可以这样扩展把调度层改成基于负载均衡的自动选路支持多节点集群或者加一个 Web 管理界面方便非技术人员操作。但核心的通道模型和状态机不用动因为它们已经足够抽象能覆盖大部分实时流场景。