一个专为低配设备优化的高性能 Bilibili 直播录制后端。
- ✅ 开箱即用 - 零配置起步,运行后直接访问 Web 界面即可管理任务
- ✅ 多模式录制 - 支持手动开始录制或配置直播间自动开播录制
- ✅ 多格式支持 - 完整支持 HTTP-FLV / HLS-TS / HLS-fMP4 格式
- ✅ 灵活录制时长 - 支持指定时长上限、定时停止或设置无限时长录制
- ✅ 开播即时通知 - 支持网页/手机端实时推送开播动态(Web Push / SSE)
- ✅ 自动分段轮转 - 录制过程中检测到直播 PK 或分辨率变更时,自动切换新文件以防止花屏和损坏
- ✅ 多路并发录制 - 支持同时录制多个直播间,在低配硬件上也能稳定运行
- ✅ 故障自动恢复 - 自动修复 FLV 时间戳并处理流中断,确保连接波动时录制不中断
- ✅ 自动 MP4 转换 - 录制完成后可自动转换为 MP4,支持本地 FFmpeg 或 CloudConvert 云端处理
- ✅ 多端运行与接入 - 提供 RESTful API、Web 界面(支持 PWA),并支持通过 Android App 直接在手机上运行录制后端
- ✅ FRP 内网穿透 - 内置 FRP 客户端,无需公网 IP 即可在外网安全访问后端管理任务与文件
- ✅ 文件管理与播放 - 提供文件列表浏览、批量删除及在浏览器中直接预览 MP4 视频
- ✅ 账号登录与刷新 - 支持匿名或 B 站账号登录,并自动刷新 Cookie 保持登录有效
- ✅ 低配设备深度优化 - 针对 microSD 卡优化写入策略,显著降低 SD 卡磨损与 I/O 峰值
服务器为 树莓派5B 16GB · Docker 容器(1 GB 内存限制) · 默认配置 · 每路直播 1080p。
以下数据仅供参考(实测快照,改配置或版本后会有偏差)。下表为默认配置下的常见实测峰值;按环境变量规划容器上限请用 内存占用估算(偏保守,通常高于本表)。
| 并发路数 | CPU峰值占用 | 内存峰值占用 |
|---|---|---|
| 初始闲置 | ~0.0% | ~7MB |
| 1路并发 | ~1.8% | ~72MB |
| 2路并发 | ~2.7% | ~117MB |
| 3路并发 | ~3.6% | ~141MB |
| 4路并发 | ~4.1% | ~165MB |
| 5路并发 | ~4.9% | ~191MB |
| 恢复闲置 | ~0.0% | ~54MB |
Note
本程序使用了大量内存池,停止录制后部分内存会保留复用(见上表「恢复闲置」),以减轻 GC 压力。 内存占用与缓冲配置、录制路数相关;真实占用请按 内存占用估算 计算,勿直接套用上表数字。
可以从 GitHub Releases 页面下载预编译的二进制文件,选择适合你系统的版本:
bilirec-linux-amd64:适用于 x86_64 架构的 Linux 系统bilirec-linux-arm64:适用于 ARM64 架构的 Linux 系统bilirec-windows.exe:适用于 Windows 系统libbilirec-android-arm64-v8a.so/libbilirec-android-x86_64.so:适用于 Android(由cmd/androidlib以 c-shared 方式编译)
启动服务:
# 如果你下载了 amd64 版本
./bilirec-linux-amd64
# 或者如果你下载了 arm64 版本
./bilirec-linux-arm64如果你是 Windows 用户,直接双击 bilirec-windows.exe 启动服务。
可以通过构建镜像或直接运行容器来启动 Bilirec。
从源码构建镜像并运行(示例):
# 在仓库根目录构建镜像
docker build -t bilirec:latest .
# 运行容器(示例)
docker run -d \
--name bilirec \
-p 8080:8080 \
-v /path/to/records:/app/records \
-v /path/to/secrets:/app/secrets \
-v /path/to/database:/app/database \
bilirec:latest你也可以直接从 Docker Hub 拉取并运行镜像:
docker pull eric1008818/bilirec:latest # 最新测试版本请用 :edge
docker run -d \
--name bilirec \
-p 8080:8080 \
-v /path/to/records:/app/records \
-v /path/to/secrets:/app/secrets \
-v /path/to/database:/app/database \
eric1008818/bilirec:latestBilirec 已内置 Android 嵌入入口(cmd/androidlib),可将后端以 .so 方式集成到 Android App,并支持在 Android 上通过 ffmpeg.so / ffmpeg-kit 本机库执行转码任务。
Android 端日志已增强,现支持更清晰的多行 FFmpeg 日志输出和更可靠的任务取消处理。
推荐直接参考官方 Android 客户端项目进行集成与使用:
构建示例(需安装 Android NDK 24,并准备好 lib/arm64-v8a/libffmpegkit.so 与 lib/x86_64/libffmpegkit.so):
make android os=linux如果你在 Windows 主机上构建,请改为:
make android os=windows构建脚本会使用 Android NDK 的 clang 工具链,并要求将 FFmpegKit 的 shared library 放置在 ./lib/<abi>/libffmpegkit.so。
NDK_HOME 会从 ANDROID_NDK_LATEST_HOME、ANDROID_NDK_ROOT 或 ANDROID_NDK_HOME 中选取,请确保这些环境变量之一已指向你的 Android NDK 安装目录。
输出目录:dist/android/<abi>/libbilirec.so(默认构建 arm64-v8a 与 x86_64)。
请先参阅上面的安装部分完成部署,再按下方配置设置环境变量后启动服务。
默认使用 controller 模式登录。服务启动后,请打开 Web 界面并点击“登入”,再到右上角点击头像按钮完成 Bilibili 扫码登录。
你也可以使用 startup 模式改为在终端显示二维码然后扫码登录,或者使用 anonymous 模式直接匿名登录(未登录可能无法录制 1080p 直播)。
-
直接访问
https://app.bilirec.org/进入登入界面 -
根据你所设置的
USERNAME和PASSWORD进行登录(如果未设置则直接进入)
如果启用 FRP 内网穿透:
-
启动内网穿透后,日志中会显示 FRP 连接状态和公网访问地址(如
https://abc1234567.tunnel.bilirec.org) -
进入
https://app.bilirec.org/后,在登录界面的服务器地址栏位输入公网访问地址(如https://abc1234567.tunnel.bilirec.org) -
使用
USERNAME和PASSWORD登录(暴露到公网后请务必设置USERNAME和PASSWORD)
推荐使用官方 Android 客户端:
使用方式:
- 优先从
bilirec-mobile的 Releases 下载并安装 APK。 - 打开 App 后,先在界面中点击启动按钮;启动成功后会出现“打开录制程序”按钮。
- 点击“打开录制程序”后,会跳转到
https://app.bilirec.org/,如果已安装 PWA 则会优先打开 PWA。 - 进入页面后直接点击“登入”即可;Android 默认连接
http://localhost:8080,且默认不启用账号密码。 - 若你需要自行开发或定制,再按
bilirec-mobile文档选择源码构建或库集成;库模式可先执行make android生成libbilirec.so。
如果你正在将 libbilirec.so 集成到自己的 Android 项目,该库导出两个方法:
Start(configJson):启动服务(传入 JSON 配置)Stop():停止服务
Start 当前支持字段:
{
"basePath": "/data/user/0/your.app/files",
"env": {
"PORT": "8080",
"HOST": "127.0.0.1",
"FRONTEND_URL": "https://app.bilirec.org",
"MAX_CONCURRENT_RECORDINGS": "3"
}
}其中 basePath 必填,env 可选(用于覆盖默认环境变量)。
Android 模式下会先注入一组移动端默认值(如 HOST=127.0.0.1、PORT=8080、OUTPUT_DIR/SECRET_DIR/DATABASE_DIR 指向 basePath 下目录),再套用 env 覆盖;并默认设置以下行为(与服务器版区分,可通过 env 覆盖):
BILIBILI_LOGIN_MODE=controllerFRP_ENABLED=falseSILENT_ACCESS_LOG=true
并使用更保守的移动端 I/O / 内存参数(例如较小写入缓冲、较低磁盘空间阈值)以避免前台卡顿。Android 已支持本机 FFmpeg 转码,如需录制后自动转 MP4,可在 env 中设置 CONVERT_TO_MP4=true。
除 Web 界面与 Android App 外,Bilirec 也提供 REST API 供程序调用、脚本控制或自行集成,详见下方 REST API 章节。
所有配置通过环境变量设置:
| 环境变量 | 说明 | 默认值 |
|---|---|---|
BILIBILI_LOGIN_MODE |
登录模式:startup(启动时登录)/ controller(由 API 控制登录)/ anonymous(匿名登录) |
controller |
HOST |
API 服务绑定地址(空值时监听所有网卡) | (空字符串) |
PORT |
API 服务端口 | 8080 |
SERVER_CRT |
可选:HTTPS 证书文件路径;当与 SERVER_KEY 同时设置时,Fiber 默认启用 HTTPS |
(未设置) |
SERVER_KEY |
可选:HTTPS 私钥文件路径;当与 SERVER_CRT 同时设置时,Fiber 默认启用 HTTPS |
(未设置) |
TRUSTED_PROXIES |
受信任反向代理 IP/CIDR 列表(逗号分隔),可填写 Nginx/FRP 等代理 IP,用于安全信任 X-Forwarded-For;默认 161.33.159.26 为公共 FRP 服务 IP。 |
161.33.159.26 |
FRP_ENABLED |
是否启用 FRP 内网穿透 | false |
FRP_SERVER |
FRP 服务器地址(格式:host:port) |
tunnel.bilirec.org:7000 |
FRP_TOKEN |
FRP 认证 Token;如果你有自己的 FRP 服务就填写,没有就留空。 | (空字符串) |
FRP_BASE_DOMAIN |
FRP 公网基础域名(用于组装公开访问地址) | tunnel.bilirec.org |
FRP_HTTPS |
FRP 代理使用的协议(true=HTTPS,false=HTTP) |
false |
FRP_SCHEME_HTTPS |
公网 URL scheme(true=https,false=http) |
true |
MAX_CONCURRENT_RECORDINGS |
最大同时录制数 | 3 |
MAX_RECORDING_HOURS |
单次录制最长时间(小时) | 5 |
MAX_RECOVERY_ATTEMPTS |
单次录制的最大重连尝试次数 | 5 |
MAX_RETRY_MINUTES |
直播中断后判断是否仍在直播的最长容忍时间(分钟) | 10 |
OUTPUT_DIR |
录制文件保存目录 | records |
SECRET_DIR |
Cookie 和 Token 保存目录 | secrets |
CONVERT_TO_MP4 |
录制完成后是否将可转换源文件(如 FLV/TS)转为 MP4 | false |
DELETE_SOURCE_AFTER_CONVERT |
转换后是否删除原始源文件 | false |
NO_CONVERT_IF_INVALID |
当源文件缺少音视频流时是否停止转码;true 时会阻止转换并返回错误,false 时只记录警告并继续转换。若当前系统未安装 ffprobe,原始视频检查会自动放行,转换仍会继续执行。 |
false |
PUBLIC_BASE_URL |
后端公开基址(仅用于生成预签名 URL,需为完整 URL) | (空字符串) |
FRONTEND_URL |
前端 URL(用于 CORS) | http://localhost:8080 |
WEBPUSH_SUBSCRIBER |
Web Push VAPID 的 subject(建议使用 mailto:you@example.com) |
mailto:webpush@example.com |
NOTIFY_SSE_TOKEN |
SSE 通知订阅 token(用于 /notify/sse?token=...;为空则禁用 SSE) |
(空字符串) |
USERNAME |
可选:启用用户名/密码认证时的用户名 | (未设置) |
PASSWORD |
可选:启用用户名/密码认证时的密码 | (未设置) |
VIEWER_USERNAME |
可选:仅查看权限的访客用户名 | (未设置) |
VIEWER_PASSWORD |
可选:仅查看权限的访客密码 | (未设置) |
JWT_SECRET |
JWT 签名密钥 | bilirec_secret |
DEBUG |
启用调试模式(会开启 pprof 和临时 hex token) | false |
PRODUCTION_MODE |
启用生产模式(影响 cookie 与 CORS) | false |
SILENT_ACCESS_LOG |
启用静默访问日志(仅记录 4xx/5xx 响应) | false |
DATABASE_DIR |
本地数据库目录(bbolt,用于持久化转换任务等) | database |
CLOUDCONVERT_THRESHOLD |
使用 CloudConvert 的文件大小阈值(字节) | 1073741824 (1 GB) |
CLOUDCONVERT_API_KEY |
可选:CloudConvert API Key(为空则禁用 CloudConvert) | (未设置) |
CLOUDCONVERT_CHECK_INTERVAL_SECS |
CloudConvert 任务状态轮询间隔(秒) | 180 |
CLOUDCONVERT_MAX_CONCURRENT_DOWNLOADS |
CloudConvert 最大并发下载数 | 1 |
CLOUDCONVERT_ALLOW_DURING_RECORDING |
是否允许在录制进行中时执行 CloudConvert 转换任务 | false |
CLOUDCONVERT_ALLOW_DURING_RECORDING_MAX_ACTIVE_RECORDINGS |
仅当活跃录制数 <= 此值时,才允许在录制中执行 CloudConvert;<1 表示不设门槛(仍可通过 CLOUDCONVERT_ALLOW_DURING_RECORDING 控制) |
1 |
FFMPEG_CHECK_INTERVAL_SECS |
本地 FFmpeg 转换任务轮询间隔(秒) | 60 |
FFMPEG_MAX_CONCURRENT_TASKS |
本地 FFmpeg 最大并发转换任务数 | 1 |
FFMPEG_ALLOW_DURING_RECORDING |
是否允许在录制进行中时执行 FFmpeg 转换任务 | false |
FFMPEG_ALLOW_DURING_RECORDING_MAX_ACTIVE_RECORDINGS |
仅当活跃录制数 <= 此值时,才允许在录制中执行 FFmpeg;<1 表示不设门槛(仍可通过 FFMPEG_ALLOW_DURING_RECORDING 控制) |
1 |
SUBCHECK_CHECK_INTERVAL_SECS |
订阅房间自动录制检查的完整轮询周期(秒);K 个分片在该周期内轮流触发,用于摊平 CPU 峰值 | 60 |
SUBCHECK_SHARD_COUNT |
订阅检查分片数(K);房间按 roomID % K 分配,约每 SUBCHECK_CHECK_INTERVAL_SECS / K 秒处理一个分片。订阅房间较多时可增大以降低单次 tick 的 CPU 峰值 |
6 |
UPLOAD_BUFFER_SIZE |
上传时或向外部服务(如 CloudConvert)传输文件使用的缓冲区大小(字节) | 5242880 (5 MB) |
DOWNLOAD_BUFFER_SIZE |
文件下载 / 导出时使用的缓冲区大小(字节) | 5242880 (5 MB) |
STREAM_WRITER_BUFFER_SIZE |
流写入器(写入文件)缓冲区大小(字节) | 1048576 (1 MB) |
READ_STREAM_BYTES_POOL_SIZE |
1080p 及以下 FLV 每次读取的缓冲块大小(字节) | 524288 (512 KB) |
READ_STREAM_BYTES_POOL_SIZE_HIGH |
2K/4K FLV 读块大小(qn ≥ 20000 时自动启用,见下方说明) |
1048576 (1 MB) |
READ_STREAM_CHAN_BUFFER_SIZE |
1080p 及以下读侧 channel 深度(chunk 数) | 16 |
READ_STREAM_CHAN_BUFFER_SIZE_HIGH |
2K/4K 读侧 channel 深度(qn ≥ 20000 时自动启用);高码率时建议高于默认值 |
48 |
LIVE_STREAM_WRITER_BUFFER_SIZE |
实时流写入缓冲区(用于直播录制或实时下载,字节);更大的值减少 flush 频率,降低 SD 卡磨损 | 8388608 (8 MB) |
LIVE_STREAM_WRITER_SYNC_PERIOD_SECS |
实时流写入器执行周期性 sync 的周期(秒);设为 0 禁用周期性 sync(仅在 Close 时 sync),大幅减少 SD 卡磨损 |
0 |
LIVE_STREAM_WRITER_FLUSH_PERIOD_SECS |
实时流写入器执行周期性 flush 的周期(秒);值越大 flush 频率越低,越有利于减少 SD 卡写入频次 |
10 |
LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE |
实时流写入器通道缓冲区大小(数据块数);更大的值可容忍写入延迟突变,但会增加内存占用 | 64 |
LIVE_STREAM_WRITER_BYTES_POOL_SIZE |
1080p 及以下写侧缓冲块大小(字节) | 524288 (512 KB) |
LIVE_STREAM_WRITER_BYTES_POOL_SIZE_HIGH |
2K/4K 写侧块大小(qn ≥ 20000 时自动启用,见下方说明) |
1048576 (1 MB) |
SKIP_SMALL_FLUSH_THRESHOLD |
启用 SKIP_SMALL_FLUSH 后,延迟创建文件并缓存内存的阈值(字节);达到阈值后才开始落盘。 |
1048576 (1 MB) |
SKIP_SMALL_FLUSH |
启用 microSD 磨损保护:在达到 SKIP_SMALL_FLUSH_THRESHOLD 之前仅缓存内存,不创建文件 |
true |
SEQUENTIAL_WRITE |
启用全局写盘锁以序列化多路录制的写入操作;多路并发时定时 flush 会自动错开(内部 round-robin),降低 I/O 峰值。多路并发写入同一物理磁盘时建议启用 | true |
MIN_DISK_SPACE_BYTES |
录制所需的最小磁盘空间(字节),低于此值将拒绝新录制任务 | 5368709120 (5 GB) |
Bilirec 为 1080p 与 高画质(2K/4K/杜比) 各准备了一套内存池。录制时根据实际流的 qn 自动选用,无需在配置里手动切换「4K 模式」。
| 画质 | 判定(qn) |
FLV 读块 | 读侧 channel | 写侧块 |
|---|---|---|---|---|
| 1080p 及以下 | < 20000 |
READ_STREAM_BYTES_POOL_SIZE(512 KB) |
READ_STREAM_CHAN_BUFFER_SIZE(16) |
LIVE_STREAM_WRITER_BYTES_POOL_SIZE(512 KB) |
| 2K / 4K / 杜比 | ≥ 20000 |
READ_STREAM_BYTES_POOL_SIZE_HIGH(1 MB) |
READ_STREAM_CHAN_BUFFER_SIZE_HIGH(48) |
LIVE_STREAM_WRITER_BYTES_POOL_SIZE_HIGH(1 MB) |
高规格专用变量共三个:READ_STREAM_BYTES_POOL_SIZE_HIGH、READ_STREAM_CHAN_BUFFER_SIZE_HIGH、LIVE_STREAM_WRITER_BYTES_POOL_SIZE_HIGH(读写块大小建议保持一致)。
补充说明:
| 要点 | 说明 |
|---|---|
| 懒加载 | 只有真的在录高画质时才初始化 *_HIGH 池;不录 4K 时不会常驻占内存 |
| HLS 读侧 | 不走 bytes pool,但 qn ≥ 20000 时同样使用 READ_STREAM_CHAN_BUFFER_SIZE_HIGH |
| 写侧 channel | LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE 不分档,1080p 与 4K 共用 |
启用 FRP (FRP_ENABLED=true) 时,配置 Bilirec 作为内网穿透客户端连接到 FRP 服务器。
如果你只开启 FRP_ENABLED=true,程序会默认使用官方免费的公共 FRP 服务;只有当你另外设置 FRP_SERVER、FRP_BASE_DOMAIN 或 FRP_TOKEN 时,才会切换成自定义 FRP 配置。
基础参数:
FRP_SERVER:FRP 服务器地址和端口(格式:host:port)FRP_TOKEN:FRP 服务的认证 Token;有自己的 FRP 服务时填写,没有就留空。FRP_BASE_DOMAIN:生成公网访问地址的基础域名TRUSTED_PROXIES:受信任代理列表(逗号分隔),可填写 Nginx/FRP 等代理 IP;只有来自这些代理的X-Forwarded-For才会被信任。默认值161.33.159.26为公共 FRP 服务 IP
协议配置(可选):
FRP_HTTPS:FRP 代理使用的协议false(默认):使用 HTTP 代理(适合内部自架、有前置 HTTPS 层的情况)true:使用 HTTPS 代理(适合公开 FRP 服务)- ❗配置约束:当
FRP_ENABLED=true且FRP_HTTPS=true时,必须同时设置SERVER_CRT+SERVER_KEY(确保 Fiber 启用 HTTPS);否则会发生协议不匹配并导致 FRP 无法工作,程序会在启动阶段直接报错退出。 - ❗配置约束:当同时设置
SERVER_CRT+SERVER_KEY(Fiber 仅提供 HTTPS)且FRP_ENABLED=true时,FRP_HTTPS不能为false。否则 FRP 会以 HTTP 回源到 HTTPS 本地服务,发生协议不匹配并导致 FRP 无法工作,程序会在启动阶段直接报错退出。
FRP_SCHEME_HTTPS:生成的公网 URL scheme(默认true)false:公网 URL 为http://<random>.<FRP_BASE_DOMAIN>true:公网 URL 为https://<random>.<FRP_BASE_DOMAIN>
常见配置示例:
-
使用官方公共服务 bilirec.org(推荐)
FRP_ENABLED=true # 只开启 FRP_ENABLED=true 时,就会使用官方免费的公共 FRP 服务 -
自定义 FRP(需要自己提供服务器信息)
FRP_ENABLED=true FRP_SERVER=your-frp-ip:7000 FRP_TOKEN=your-token # 你的 FRP 服务需要什么,就按你的服务填写 FRP_BASE_DOMAIN=your-domain.com FRP_HTTPS=false # 内部用 HTTP FRP_SCHEME_HTTPS=true # Caddy 或前置代理提供 HTTPS
FRP enabled in official-public mode
FRP enabled in custom-selfhost mode
official-public:当前使用官方免费的公共 FRP 服务custom-selfhost:当前使用自定义 FRP 配置
使用 FRP 后,你可以把本机的 Bilirec 系统暴露到外网。这样无论你在家里、办公室,还是外出时,只要打开 Web 界面,就能随时登录和管理自己的录制任务、查看录制状态、处理文件与通知。
export BILIBILI_LOGIN_MODE=controller
# 可选:指定监听网卡;留空表示监听所有网卡
# export HOST=0.0.0.0
export PORT=8080
# 可选:启用 HTTPS(当两者都设置时,Fiber 默认启用 HTTPS)
# export SERVER_CRT=/path/to/server.crt
# export SERVER_KEY=/path/to/server.key
# 可选:FRP 内网穿透(只开启 FRP_ENABLED=true 时默认走官方公共服务)
export FRP_ENABLED=true
export FRP_SERVER=tunnel.bilirec.org:7000
# 若要自定义 FRP 服务,再设置 FRP_SERVER / FRP_BASE_DOMAIN / FRP_TOKEN
export FRP_BASE_DOMAIN=tunnel.bilirec.org
export TRUSTED_PROXIES=161.33.159.26 # 受信任代理 IP,默认值为公共 FRP 服务 IP;多个以逗号分隔
export FRP_HTTPS=false # 使用 HTTP 代理
export FRP_SCHEME_HTTPS=true # 公网 URL 为 HTTPS
export MAX_CONCURRENT_RECORDINGS=3
export MAX_RECORDING_HOURS=5
export MAX_RECOVERY_ATTEMPTS=5
export MAX_RETRY_MINUTES=10
export OUTPUT_DIR=/path/to/records
export SECRET_DIR=/path/to/secrets
export NO_CONVERT_IF_INVALID=false
export DATABASE_DIR=/path/to/database
export CONVERT_TO_MP4=false
export DELETE_SOURCE_AFTER_CONVERT=false
export MIN_DISK_SPACE_BYTES=5368709120
# 可选:CloudConvert(如果启用会对大文件使用云端转换)
export CLOUDCONVERT_THRESHOLD=1073741824
export CLOUDCONVERT_API_KEY=
export CLOUDCONVERT_CHECK_INTERVAL_SECS=180
export CLOUDCONVERT_MAX_CONCURRENT_DOWNLOADS=1
export CLOUDCONVERT_ALLOW_DURING_RECORDING=false
export CLOUDCONVERT_ALLOW_DURING_RECORDING_MAX_ACTIVE_RECORDINGS=1
# FFmpeg 本地转换参数
export FFMPEG_CHECK_INTERVAL_SECS=60
export FFMPEG_MAX_CONCURRENT_TASKS=1
export FFMPEG_ALLOW_DURING_RECORDING=false
export FFMPEG_ALLOW_DURING_RECORDING_MAX_ACTIVE_RECORDINGS=1
export SUBCHECK_CHECK_INTERVAL_SECS=60
export SUBCHECK_SHARD_COUNT=6
export PUBLIC_BASE_URL=http://localhost:8080
export FRONTEND_URL=http://localhost:8080
export WEBPUSH_SUBSCRIBER=mailto:webpush@example.com
export UPLOAD_BUFFER_SIZE=5242880
export DOWNLOAD_BUFFER_SIZE=5242880
export STREAM_WRITER_BUFFER_SIZE=1048576
export READ_STREAM_BYTES_POOL_SIZE=524288
export READ_STREAM_CHAN_BUFFER_SIZE=16
export READ_STREAM_BYTES_POOL_SIZE_HIGH=1048576
export READ_STREAM_CHAN_BUFFER_SIZE_HIGH=48
export LIVE_STREAM_WRITER_BUFFER_SIZE=8388608
export LIVE_STREAM_WRITER_SYNC_PERIOD_SECS=0
export LIVE_STREAM_WRITER_FLUSH_PERIOD_SECS=10
export LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE=64
export LIVE_STREAM_WRITER_BYTES_POOL_SIZE=524288
export LIVE_STREAM_WRITER_BYTES_POOL_SIZE_HIGH=1048576
export SKIP_SMALL_FLUSH_THRESHOLD=1048576
export SKIP_SMALL_FLUSH=true
export SEQUENTIAL_WRITE=true
export JWT_SECRET=bilirec_secret
export DEBUG=false
# 可选:启用 REST API 认证
export USERNAME=admin
export PASSWORD=changeme
export PRODUCTION_MODE=false如果你是使用二进制文件:
- 启动时会优先尝试加载当前工作目录下的
.env.local(若存在); - 若不存在
.env.local,程序会在可执行文件同目录下生成(或读取).env模板文件; - 在 Docker 环境下会跳过
.env自动加载,主要使用容器环境变量。
你可以编辑 .env.local / .env 来修改配置,或直接设置环境变量覆盖。
PUBLIC_BASE_URL 为空或不是有效 URL(必须包含 http/https)时,预签名 URL 会记录 warning 并回退为不含 base URL 的相对地址(/files/tempdownload?presigned=...);CloudConvert 在该情况下会直接报错并拒绝创建任务。
Web Push 的 VAPID key 会由后端在启动时自动生成并写入 SECRET_DIR(默认 secrets)下的 _webpush_public_key 与 _webpush_private_key。后续重启会优先复用已存在的 key。
Bilirec 当前默认配置已针对树莓派 5B + microSD 场景优化,在“降低写入次数”和“控制内存占用”之间取平衡:
| 优化项 | 说明 |
|---|---|
LIVE_STREAM_WRITER_SYNC_PERIOD_SECS=0 |
禁用周期性 fsync(最昂贵的操作),数据仅在录制结束时同步到磁盘,减少 I/O 突峰。权衡:意外断电可能丢失最后一段尚未持久化的数据。 |
LIVE_STREAM_WRITER_BUFFER_SIZE=8388608 (8MB) |
8MB 进一步降低 flush 频率,优先降低 SD 卡写入频次。@1080p30fps (4.5Mbps) 约每 14.2 秒触发一次满缓冲写入。 |
LIVE_STREAM_WRITER_FLUSH_PERIOD_SECS=10 |
将周期性 flush 改为 10 秒,优先降低 SD 卡磨损;代价是异常断电时未 flush 数据窗口会略增。 |
LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE=64 |
控制在途内存占用。以 512KB chunk 估算,单录制任务约 32MB 队列数据;比 128(约 64MB)更适合 4GB 设备。 |
SKIP_SMALL_FLUSH=true |
延迟建文件:开播后数据先缓存在内存,累计达到 SKIP_SMALL_FLUSH_THRESHOLD(默认 1 MB)后才创建文件并正常写盘,减少极短直播对 SD 卡的无效写入。 |
LIVE_STREAM_WRITER_BYTES_POOL_SIZE=524288 (512KB) |
与常见 stream chunk 大小一致,减少额外分配与拷贝。 |
SEQUENTIAL_WRITE=true |
启用全局写盘锁,序列化多路录制的写入操作,多路并发时有效降低 I/O 峰值;默认启用以保护 microSD。 |
MAX_CONCURRENT_RECORDINGS=3 |
默认同时录制上限(保守值);树莓派 5B + 1 GB 容器、1080p 默认缓冲下,生产环境可验证至 5 路(见效能指标),按需调大。 |
容器运行时默认值也同步为树莓派 5B 取向:GOMEMLIMIT=768MiB、GOGC=100。
SKIP_SMALL_FLUSH=true 时,是否落盘由 SKIP_SMALL_FLUSH_THRESHOLD 决定(默认 1MB,独立于 LIVE_STREAM_WRITER_BUFFER_SIZE)。
默认的 512 KB(1080p)+ 三个 *_HIGH 变量(高画质)已覆盖大多数场景:录 1080p 不用改;录 4K / 杜比时传对应 qn 即可,程序会自动启用高规格配置。
只有出现卡顿、内存不足、或单块数据(FLV 读块 / HLS segment)明显偏大时,再按下表微调。
常见场景:
| 场景 | 要不要改配置 | 建议 |
|---|---|---|
| 1080p 原画 | 否 | 保持默认 |
| 首次录 4K / 杜比 | 通常否 | API 传 qn=20000(或 30000),自动走高画质池 |
| 4K FLV 块被拆太碎、分配频繁 | 是 | 同时加大 READ_STREAM_BYTES_POOL_SIZE_HIGH 与 LIVE_STREAM_WRITER_BYTES_POOL_SIZE_HIGH(两者保持一致) |
| 4K HLS segment 很大 | 是 | 主要加大 LIVE_STREAM_WRITER_BYTES_POOL_SIZE_HIGH |
| 4K 网络抖动、读侧阻塞 | 是 | 加大 READ_STREAM_CHAN_BUFFER_SIZE_HIGH(默认 48) |
| 4GB 设备多路 4K | 视情况 | 优先减小 LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE(如 48)或 MAX_CONCURRENT_RECORDINGS |
推荐起点(三个 *_HIGH 默认值通常已够用,下表供参考):
| 场景 | READ_STREAM_BYTES_POOL_SIZE_HIGH |
READ_STREAM_CHAN_BUFFER_SIZE_HIGH |
LIVE_STREAM_WRITER_BYTES_POOL_SIZE_HIGH |
LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE |
|---|---|---|---|---|
| 4K / 杜比 FLV 单路 | 1048576 (1 MB) |
48 |
1048576 (1 MB) |
64 |
| 4K / 杜比 HLS 单路 | 对 FLV 读块无影响 | 48 |
1048576 (1 MB) |
64 |
| SSD / NAS 单路极高码率 | 1048576~2097152 |
48~64 |
与读块 *_HIGH 相同 |
64~128 |
注意:调 4K 请改带
_HIGH后缀的变量。不要动不带_HIGH的基础变量——那是 1080p 用的,改大只会让所有录制都多占内存。
示例(4K 默认仍不够用时):
export READ_STREAM_BYTES_POOL_SIZE_HIGH=2097152 # 2 MB,与写侧保持一致
export READ_STREAM_CHAN_BUFFER_SIZE_HIGH=48
export LIVE_STREAM_WRITER_BYTES_POOL_SIZE_HIGH=2097152
export LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE=64 # 多路 4K 可降到 48内存粗算(单路上限): 见 内存占用估算;默认 4K 单路总峰值约 165~172 MB。
默认配置主要为降低单板电脑(如树莓派)的 microSD 卡磨损而设计,采取了极度保守的缓冲与刷盘策略。如果你的存储介质是常规 HDD、SSD 或是自建的本地 NAS,具备更好的读写寿命和性能,建议将侧重点从“保护存储”转移到“提高录制完整性与多路并发能力”。
请根据你的具体硬件情况选择以下配置方向:
SSD 具备极高的随机读写性能,无需过度担忧小块文件的写入磨损,且开启应用层的写入序列化反而会限制其并发优势。
# 解放 I/O 限制,优先保障数据实时性与高并发
export LIVE_STREAM_WRITER_SYNC_PERIOD_SECS=30 # 恢复周期性 sync,降低断电导致的数据丢失风险
export SKIP_SMALL_FLUSH=false # 优先保障录制完整性,不再跳过小块 flush
export SEQUENTIAL_WRITE=false # 【关键】禁用全局写盘锁,解放 SSD 多路并发 I/O 能力
export LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE=128 # 提高内存队列深度(容忍更高的网络突发流量)
export MAX_CONCURRENT_RECORDINGS=10 # 可视设备的 CPU 与内存宽裕程度放宽限制
机械硬盘(如 16TB 级别的存储盘)的物理弱点是磁头寻道延迟。当发生多路并发录制时,频繁的随机写入会导致严重的卡顿。调整核心在于在内存中合并大块数据并强制顺序写入。
# 优化大块连续写入,保护磁头并提升系统整体流畅度
export LIVE_STREAM_WRITER_SYNC_PERIOD_SECS=45 # 适当拉长 sync 周期,避免频繁打断顺序写入
export SKIP_SMALL_FLUSH=false
export SEQUENTIAL_WRITE=true # 【关键】保持全局写锁,多路并发时强制转换为顺序写入,大幅降低物理寻道延迟
export LIVE_STREAM_WRITER_BUFFER_SIZE=16777216 # (16MB) 增大单次落盘大小,让文件在磁盘上的分布更连续
export MAX_CONCURRENT_RECORDINGS=5 # 视硬盘转速适度调整
Tip
关于自建 NAS 与混合部署
- 文件系统缓存(如 ZFS): 如果你的底层存储池使用了 ZFS 这种自带强大内存缓存机制(ARC)的文件系统,频繁的应用层
sync可能会引发写入放大并拖慢效能。此时可以将LIVE_STREAM_WRITER_SYNC_PERIOD_SECS设为0或更长的时间(如60),将落盘调度完全交由底层系统接管。 - 与私有云服务共存: 若你的硬盘同时还在运行其他个人服务(如 Cloudreve 私有云、影音服务器等),强烈建议保持
SEQUENTIAL_WRITE=true。这能有效避免 bilirec 的碎片化写入与其他服务发生磁头争抢,保障 NAS 在私人使用时的整体响应速度。
录制时,直播数据会在内存里「排队」:先从网络读进来,再排队等待写入磁盘。用下面公式估算规划容器时要留多少内存即可(偏保守的上限,实际往往略低)。
每路录制的在途内存,由读侧排队、写侧排队、落盘缓冲三块组成。录 1080p 用不带 _HIGH 的变量;录 4K / 杜比(qn ≥ 20000)自动改用带 _HIGH 的读侧变量,写侧块大小也用 _HIGH。
| 作用 | 1080p 环境变量 | 4K / 杜比环境变量 | 默认值 |
|---|---|---|---|
| 读侧能排多少「块」 | READ_STREAM_CHAN_BUFFER_SIZE |
READ_STREAM_CHAN_BUFFER_SIZE_HIGH |
16 / 48 |
| 读侧每块多大 | READ_STREAM_BYTES_POOL_SIZE |
READ_STREAM_BYTES_POOL_SIZE_HIGH |
512 KB / 1 MB |
| 写侧能排多少「块」 | LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE |
(两档共用) | 64 |
| 写侧每块多大 | LIVE_STREAM_WRITER_BYTES_POOL_SIZE |
LIVE_STREAM_WRITER_BYTES_POOL_SIZE_HIGH |
512 KB / 1 MB |
| 落盘前合并缓冲 | LIVE_STREAM_WRITER_BUFFER_SIZE |
(两档共用) | 8 MB |
路数由 MAX_CONCURRENT_RECORDINGS 决定,直接代入下方公式里的 N。
注意:读侧和写侧各有一份数据拷贝,下面公式已把两侧都算上。
总峰值 ≈ 43~50 MB + N × 单路在途峰值
单路在途峰值 = 读侧排队 + 写侧排队 + 落盘缓冲
读侧排队 = (READ_STREAM_CHAN_BUFFER_SIZE + 1) × READ_STREAM_BYTES_POOL_SIZE
写侧排队 = (LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE + 1) × LIVE_STREAM_WRITER_BYTES_POOL_SIZE
落盘缓冲 = LIVE_STREAM_WRITER_BUFFER_SIZE
其中 43~50 MB 是容器里程序本身 + 系统内核的固定开销(和缓冲配置几乎无关,也不能通过环境变量调小)。录 4K / 杜比时,读侧两个变量换成 *_HIGH,写侧块大小换成 LIVE_STREAM_WRITER_BYTES_POOL_SIZE_HIGH。
默认 1080p 示例:
| 路数 N | 计算 | 预计总峰值 |
|---|---|---|
| 1 | (43~50) + 1×49 | 约 92~99 MB |
| 3 | (43~50) + 3×49 | 约 190~197 MB |
| 5 | (43~50) + 5×49 | 约 288~295 MB |
(单路在途:读 8.5 MB + 写 32.5 MB + 落盘 8 MB ≈ 49 MB)
默认 4K 单路: (4350) + 122 ≈ **165172 MB**(单路在途 ≈ 122 MB)
HLS 读进来的是一整段分片(大小由 m3u8 决定,常见 2–6 MB),读侧用分片体积 S 估算:
总峰值 ≈ 43~50 MB + N × (读侧排队 + 写侧排队 + 落盘缓冲)
读侧排队 ≈ (READ_STREAM_CHAN_BUFFER_SIZE + 3) × S
写侧仍按上一节的写侧排队 + 落盘缓冲计算。
- 容器总内存虚高:写入磁盘后,系统可能额外缓存一份文件,容器监控显示的总用量会远高于上表,规划时以上表为准即可。
- 停录后仍偏高:内存池会保留部分已用过的块(见效能指标「恢复闲置 ~54 MB」),属正常。
- 读/写 bytes pool 保温上限:空闲复用块数量由
READ_STREAM_CHAN_BUFFER_SIZE、READ_STREAM_CHAN_BUFFER_SIZE_HIGH、LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE推导(约为 channel 深度的 1/4 与 1/8);在途内存公式不变。
按效果从大到小:
LIVE_STREAM_WRITER_CHAN_BUFFER_SIZE(默认 64 → 32,写侧排队约减半)READ_STREAM_CHAN_BUFFER_SIZE(仅 FLV;HLS 同理改读侧 channel 或_HIGH版)LIVE_STREAM_WRITER_BUFFER_SIZE(减小落盘缓冲,但 flush 更频繁,SD 磨损增加)MAX_CONCURRENT_RECORDINGS(直接减少同时录制的路数)
Swagger UI 会在服务器运行时于根路径
/提供 — 在浏览器中打开该地址即可查看与测试 API。
如果设置了 USERNAME 与 PASSWORD,REST API 会启用基于 JWT 的认证(登录会在 cookie 中设置 jwtToken)。使用:
POST /login
Content-Type: application/json
{ "user": "<username>", "pass": "<password>" }登录成功后会在响应中设置 JWT cookie(键名 jwtToken),随后对需要认证的接口请携带该 cookie。若未设置用户名/密码,API 默认为公开访问。
当 BILIBILI_LOGIN_MODE=controller 时,可以通过 /auth/bilibili 相关接口在服务运行后触发扫码登录。
-
GET /auth/bilibili/status- 用途:查询当前认证状态(
idle/awaiting_qr/authenticating/authenticated/failed等) - 返回:是否已登录、账号信息、二维码 URL(若已生成)、最近错误(若有)
- 响应示例(已登录):
{ "authenticated": true, "state": "authenticated", "account": { "mid": 123456789, "uname": "bilibili_user" } } - 响应示例(等待扫码):
{ "authenticated": false, "state": "awaiting_qr", "qr": { "url": "https://api.bilibili.com/x/web-interface/qrcode/generate?qrcode_key=abc123..." } }
- 用途:查询当前认证状态(
-
POST /auth/bilibili/init- 用途:创建新的二维码登录会话(支持账号切换,即使已登录也可重新发起登录)
- 成功:
201 Created - 错误:
400 Bad Request(当前不在 controller 模式) /409 Conflict(已存在未完成的登录会话) /500 Internal Server Error(获取二维码失败) - 响应示例(成功):
{ "qr": { "url": "https://api.bilibili.com/x/web-interface/qrcode/generate?qrcode_key=def456..." } } - 响应示例(400 Bad Request):
{ "error": "后端没有启用 controller 模式" } - 响应示例(500 Internal Server Error):
{ "error": "failed to get QR code: ..." }
典型流程:
- 先
POST /login(如果你启用了USERNAME/PASSWORD) - 调用
POST /auth/bilibili/init获取二维码链接 - 用户扫码后,轮询
GET /auth/bilibili/status,直到state=authenticated
账号切换流程:已登录用户可随时调用 POST /auth/bilibili/init 发起新的登录会话,扫码后会切换到新的账号。
示例(使用 cookie 持久化会话):
# 1) 登录(启用了 USERNAME/PASSWORD 时需要)
curl -i -c cookies.txt -X POST http://127.0.0.1:8080/login \
-H "Content-Type: application/json" \
-d '{"user":"admin","pass":"changeme"}'
# 2) 初始化二维码登录
curl -i -b cookies.txt -X POST http://127.0.0.1:8080/auth/bilibili/init
# 3) 查询登录状态
curl -s -b cookies.txt http://127.0.0.1:8080/auth/bilibili/status-
开始录制
POST /record/:roomID/start?duration_minutes=<N>&stream_profile=<profile>&qn=<quality>&only_audio=<bool>
duration_minutes为可选 query 参数:值 说明 不传 使用系统预设( MAX_RECORDING_HOURS)0使用系统预设( MAX_RECORDING_HOURS)(同不传)-1无限录制,不自动停止 N(正整数)N 分钟后自动停止 stream_profile为可选 query 参数,用于优先指定录制流格式:值 说明 不传 自动选择可用流(默认行为) http-flv优先使用 HTTP-FLV 录制 hls-ts优先使用 HLS-TS 录制 hls-fmp4优先使用 HLS-fMP4 录制 qn为可选 query 参数,用于指定 Bilibili 画质码:值 说明 不传 原画( 10000)80流畅 150高清 250超清 400蓝光 10000原画 200004K 30000杜比 only_audio为可选 bool query 参数:值 说明 不传 / false录制默认流,包含音视频 true尝试仅录制音频流(请求 only_audio=1)注意:
- 录 4K / 杜比请显式传
qn(如qn=20000、qn=30000)。 qn ≥ 20000时自动启用*_BYTES_POOL_SIZE_HIGH高画质池,一般无需改环境变量。- 若遇卡顿或内存不足,参见 高码率 / 4K 调参建议。
例如:
curl -X POST "http://127.0.0.1:8080/record/12345/start?duration_minutes=30&stream_profile=http-flv&qn=150&only_audio=true" - 录 4K / 杜比请显式传
-
停止录制
POST /record/:roomID/stop
-
批量获取录制状态
POST /record/statuses Content-Type: application/json { "roomIDs": [123, 456, 789] }
也支持通过查询参数传入(
roomIDs=123,456,789)。响应示例:
{ "123": "recording", "456": "idle", "789": "stopped" } -
批量获取录制统计
POST /record/stats Content-Type: application/json { "roomIDs": [123, 456, 789] }
也支持通过查询参数传入(
roomIDs=123,456,789)。响应示例:
{ "123": { "bytes_written": 1048576, "status": "recording", "start_time": 1234567890, "elapsed_seconds": 120, "output_path": "records/tester-123/live-title.flv" }, "456": { "bytes_written": 0, "status": "idle", "start_time": 0, "elapsed_seconds": 0, "output_path": "" } } -
列出所有录制任务
GET /record/list
-
列出文件
GET /files/browse/*
支持以下查询参数:
参数 说明 默认值 offset分页偏移量(跳过前 N 条) 0limit每页最大条数(0 表示不限制,最大 200) 0search按文件名搜索(大小写不敏感) (不过滤) 返回
PagedTree对象:{ "total": 42, "items": [ { "name": "title-20260428.flv", "is_dir": false, "path": "room123/title-20260428.flv", "size": 1048576 } ] }total为过滤/搜索后的总条数(不受offset/limit影响),items为当前页的文件列表。 -
下载文件
GET /files/download/*
下载接口直接返回存储的文件。 若要将录制的源文件(如 FLV/TS)转为 MP4,请启用
CONVERT_TO_MP4:在录制完成时,recorder 会将可转换文件加入转换队列,由后台任务异步转换为 MP4(转换行为受DELETE_SOURCE_AFTER_CONVERT控制)。 当同时设置了CLOUDCONVERT_API_KEY且文件大小 >=CLOUDCONVERT_THRESHOLD(默认 1 GB)时,系统会优先使用 CloudConvert(异步任务,可通过/convert/tasks查询转换状态);否则由本地 ffmpeg 后台任务处理。 -
临时 / 预签名下载(Presigned)
POST /files/presigned/{path}?ttl=<seconds>
该接口需要身份认证(JWT),用于为文件创建一个临时的预签名下载令牌(
ttl可选,单位秒,默认 3600)。成功创建后会返回包含临时令牌或 URL 的响应。使用该令牌可以进行匿名下载:GET /files/tempdownload?presigned=<token>
GET /files/tempdownload无需认证,但必须提供有效的presigned查询参数。该临时链接会在创建时设置过期时间,过期后将无法使用。 -
删除多个文件
DELETE /files/batch
请求体:JSON 数组,包含要删除的相对文件路径,示例:
["room123/20250101.flv", "room456/20250102.flv"]
-
删除目录
DELETE /files/{path}
-
在线播放视频
GET /files/playback/{path}
在浏览器中直接播放已录制的 MP4 视频(VOD)。该接口返回视频流,浏览器会直接在网页中显示播放器而非下载文件。
支持的格式:
video/mp4- MP4 格式视频
使用示例:
<video controls width="100%"> <source src="/files/playback/username-roomID/20250101.mp4" type="video/mp4"> Your browser does not support HTML5 video. </video>
注意:
- 只支持 MP4 的文件(可启用
CONVERT_TO_MP4后转换得到) - 无法播放正在进行中的录制文件
- 支持浏览器的 Range 请求(可快进/快退)
-
列出进行中的转换任务
GET /convert/tasks
-
取消转换任务
DELETE /convert/tasks/:task_id
返回
204 No Content表示取消成功,若任务不存在返回404。
-
获取房间信息
GET /room/:roomID/info
获取指定直播房间的详细信息。返回房间的基本信息如标题、粉丝数、直播封面等。
响应示例:
{ "room_id": 123, "short_id": 456, "title": "房间标题", "description": "房间描述", "live_status": 1, "followers": 10000, "cover": "https://example.com/cover.jpg" }- 状态
200: 成功获取房间信息 - 状态
400: 无效的房间 ID - 状态
404: 房间不存在 - 状态
500: 服务器内部错误
- 状态
-
批量获取房间信息
POST /room/infos Content-Type: application/json { "roomIDs": [123, 456, 789] }
支持两种传参方式:
- Query 参数:
POST /room/infos?roomIDs=123,456,789 - JSON payload:
{"roomIDs":[123,456,789]}(字段名也可使用room_ids或roomIds)
响应为房间 ID 到房间信息的映射:
{ "123": { "room_id": 123, "title": "标题1", ... }, "456": { "room_id": 456, "title": "标题2", ... } } - Query 参数:
-
检查直播状态(单个房间)
GET /room/:roomID/live
检查指定房间的直播是否进行中。返回:
{ "room_id": 123, "is_live": true }- 状态
200: 成功获取直播状态 - 状态
400: 无效的房间 ID - 状态
404: 房间不存在 - 状态
500: 服务器内部错误
- 状态
-
检查多个房间的直播状态
POST /room/lives
批量检查多个房间的直播状态。
支持两种传参方式:
- Query 参数:
POST /room/lives?roomIDs=123,456,789 - JSON payload:
{"roomIDs":[123,456,789]}(字段名也可使用room_ids或roomIds)
响应示例:
{ "123": true, "456": false, "789": true } - Query 参数:
-
订阅房间
POST /room/:roomID
订阅一个直播房间,当房间开播时会接收更新。
- 状态
200: 订阅成功 - 状态
409: 已订阅此房间 - 状态
400: 无效的房间 ID
- 状态
-
取消订阅
DELETE /room/:roomID
取消订阅直播房间。
- 状态
200: 取消订阅成功 - 状态
404: 未订阅此房间 - 状态
400: 无效的房间 ID
- 状态
-
检查订阅状态
GET /room/subscribe/:roomID
检查是否已订阅指定房间。返回:
{ "room_id": 123, "is_subscribed": true } -
列出所有订阅房间
GET /room/subscribe
获取所有已订阅房间的 ID 列表。返回:
{ "room_ids": [123, 456, 789] } -
获取房间配置
GET /room/:roomID/config
获取指定房间的配置(自动录制、通知等)。返回:
{ "room_id": 123, "auto_record": true, "notify": true, "record_duration_minutes": 120 } -
更新房间配置
PUT /room/:roomID/config
更新房间的配置(自动录制、通知等)。请求体:
{ "auto_record": true, "notify": true, "record_duration_minutes": 120 }record_duration_minutes为可选字段,仅在auto_record: true时生效,作为自动录制的时长上限:值 说明 0(默认)使用系统预设( MAX_RECORDING_HOURS)-1无限录制,不自动停止 N(正整数)N 分钟后自动停止 注意: 如果你想让这个录制时长套用到手动触发的录制任务,请自行在调用
/record/:roomID/start时传入相同的duration_minutes参数;否则手动录制将不受此配置影响,仍然使用系统预设或你在启动录制时指定的时长。
-
获取 Web Push 公钥
GET /notify/public-key
获取前端订阅 Web Push 所需的 VAPID 公钥。
-
注册 Web Push 订阅
POST /notify/subscribe Content-Type: application/json
请求体为浏览器 Push API 产生的 subscription JSON(包含
endpoint与keys)。 -
取消 Web Push 订阅
DELETE /notify/subscribe
可通过 query 参数
endpoint或 JSON body 提供 endpoint。 -
SSE 实时订阅(Android backend / libbilirec.so 推荐)
GET /notify/sse?token=YOUR_NOTIFY_SSE_TOKEN Accept: text/event-stream
- 该端点用于无法依赖 Google FCM 的场景(例如中国大陆网络环境),可由 Android 后端应用通过 SSE 获取通知,再由 Android 创建本地通知。
token必填,来源于环境变量NOTIFY_SSE_TOKEN。- 当
NOTIFY_SSE_TOKEN为空时,SSE 端点返回503(禁用状态)。 - 建议通过 HTTPS 使用 SSE,避免 token 在传输链路中明文暴露。
- 启用调试:设置环境变量
DEBUG=true启用调试模式,服务器启动时会在日志中打印一个临时十六进制令牌(hex token)。 - pprof 性能分析:调试模式下会在
/debug/pprof挂载 pprof 以便性能分析。该路由受保护:需要在请求头Authorization中填入启动日志中显示的 hex 令牌。 - 实现参考:该逻辑位于
internal/modules/rest/rest.go中(DEBUG控制是否启用,令牌授权访问)。
.
├── .github/ # CI / workflows
├── Dockerfile
├── go.mod
├── LICENSE
├── README.md
├── swagger.go
├── dotenv.go
├── cmd/
│ ├── backend/ # 后端可执行入口
│ └── androidlib/ # Android c-shared 动态库入口
├── internal/ # 内部包(不对外暴露)
│ ├── controllers/ # HTTP 控制器
│ │ ├── auth/ # Bilibili 登录控制
│ │ ├── convert/ # 转换任务管理
│ │ ├── file/ # 文件管理
│ │ ├── notify/ # 实时通知(Web Push + SSE)
│ │ ├── record/ # 录制管理
│ │ └── room/ # 房间信息与订阅
│ ├── modules/ # 核心模块
│ │ ├── bilibili/ # Bilibili API 封装与认证
│ │ ├── config/ # 配置管理
│ │ └── rest/ # REST 服务
│ ├── processors/ # 流处理器
│ └── services/ # 业务逻辑服务
│ ├── convert/ # 转换服务
│ ├── expose/ # FRP 暴露服务
│ ├── file/ # 文件操作
│ ├── janitor/ # 录制文件清理与巡检
│ ├── notify/ # 实时通知服务
│ ├── path/ # 路径管理
│ ├── recorder/ # 直播录制
│ ├── room/ # 房间信息与订阅
│ ├── stream/ # 流处理
│ ├── subcheck/ # 订阅检查与自动录制
│ └── subscribe/ # 房间订阅管理
├── pkg/ # 可复用库与工具
│ ├── backoff/ # 重试退避策略
│ ├── cloudconvert/ # CloudConvert API 客户端
│ ├── coordinator/ # 通用信号调度
│ ├── db/ # 数据库抽象层
│ ├── ds/ # 数据结构
│ ├── ffmpeg/ # Android / 本地 FFmpeg 封装
│ ├── flv/ # FLV 格式处理
│ ├── fp/ # 函数式编程工具(maps、slices)
│ ├── hls/ # HLS 格式处理
│ ├── monitor/ # 监控与统计
│ ├── pipeline/ # 流处理管道
│ ├── pool/ # 内存池
│ ├── rw/ # 日志格式化与多行日志处理
│ ├── signeddownload/ # 预签名下载
│ ├── swr/ # Stale-While-Revalidate 缓存
│ └── tx/ # 事务钩子与协调
└── utils/ # 工具函数库
- 通过
bilibili.Client获取直播流地址(默认自动选择可用 profile,也可按需指定stream_profile) - 使用
stream.Service读取流数据 recorder.Service管理录制任务(自动重连与恢复)- 数据写入到录制文件(扩展名依流格式可能为
.flv、.ts或.fmp4),保存在配置的输出目录:
-
HTTP-FLV 格式: 当检测到直播 PK 等 FLV 文件头变更时(分辨率切换)录制器会自动轮转到新的分段文件;
-
HLS-fMP4 格式: 默认将 init segment 与媒体分片顺序追加到同一文件;若播放列表重发内容相同的 init(如 CDN 序列回退),会自动跳过并继续写入同一文件;仅当 init 内容变更(PK、换源、编解码参数变化)时才轮转到新分段,且新文件会以完整 init 开头,可正常
CONVERT_TO_MP4; -
HLS-TS 格式: 不执行文件轮转,直播 PK 等不连续性由 HLS 播放列表层自然处理,录制器仅在流中断时重连并继续写入同一文件。
首个文件名形如 标题-时间戳.ext,后续分段(HTTP-FLV / HLS-fMP4 轮转后)会命名为 标题-时间戳-1.ext、标题-时间戳-2.ext。如果启用了 CONVERT_TO_MP4,每个可转换分段在完成时都会自动加入转换队列,并由后台任务异步转换/封装为 .mp4 文件(转换行为受 DELETE_SOURCE_AFTER_CONVERT 控制,转换任务可通过 /convert/tasks 查询)。
- 自动恢复: 当流中断时自动重连,详见
recorder.Service - 自动分段轮转(HTTP-FLV / HLS-fMP4):
- HTTP-FLV: 当检测到 FLV 文件头变更(直播 PK 等分辨率切换)时,自动轮转到新的分段文件并重写文件头,降低画质切换导致输出异常的风险
- HLS-fMP4: 相同 init 重发时继续写入同一文件;init 内容变更时自动分段,新文件带完整 init,避免 ffprobe/FFmpeg 解析失败
- 自动录制: 为订阅的直播间配置自动开播录制,后台定期检查直播间状态并自动启动录制,详见
subcheck.Service - 实时通知: 支持 Web Push 与 SSE 两种通知方式,推送直播开播通知和自动录制状态,详见
notify.Service - Android 嵌入支持: 提供
cmd/androidlib以生成 Android c-shared 库,支持ffmpeg.so/ffmpeg-kit本机库以及 Android 端的本地转码执行 - 内存池: 使用
pool.BufferPool减少内存分配 - SWR 缓存: 房间信息缓存采用 Stale-While-Revalidate 策略(
pkg/swr),结合 singleflight 去重,缓存命中时立即返回旧数据并在后台异步刷新,软 TTL 5 分钟、硬 TTL 30 分钟,有效降低 Bilibili API 请求压力 - 低资源占用: 设计注重低内存,低SD卡磨損和低 CPU 使用,适合树莓派等资源受限设备
- 文件管理: 支持列出、预览、下载(可转换格式)、批量删除文件及删除目录,详见
internal/controllers/file/file.go - 自动转换: 如果启用
CONVERT_TO_MP4,录制完成时会自动将可转换源文件(例如 FLV、TS)转为 MP4;可通过DELETE_SOURCE_AFTER_CONVERT控制是否删除原始源文件。已修复部分不支持的编解码器导致转换失败的问题,并具备 FFmpeg 任务冷却机制避免卡死失败任务 - 在线播放: 支持在浏览器中直接播放已转换的 MP4 视频,提供原生 HTML5 video 标签体验,支持暂停/快进/全屏等操作
- 实时修复(Realtime Fixer): 在流式写入场景下逐个修复 FLV Tag 的时间戳并输出,包含重复 Tag 去重(可查询去重统计),并通过内存池、去重缓存与周期清理来保持低延迟与低内存占用,适合边录制边推送或实时下载的场景。
- 函数式编程工具: 提供
fp包含便捷的 maps 和 slices 操作函数 - REST API 文档: Swagger UI 在根路径
/提供(由swag生成,参见internal/modules/rest) - 认证与调试: 可选用户名/密码登录(设置
USERNAME和PASSWORD)启用 JWT 认证;也可配置VIEWER_USERNAME/VIEWER_PASSWORD提供只读账号(写操作仍需 admin 角色);调试模式下可通过/debug/pprof访问 pprof(受临时 token 保护) - Cookie 管理: 自动刷新 Bilibili Cookie 保持登录状态
主要依赖库:
- github.com/gofiber/fiber/v3 - Web 框架
- github.com/CuteReimu/bilibili/v2 - Bilibili API 客户端
- go.uber.org/fx - 依赖注入框架
- github.com/sirupsen/logrus - 日志库
- gopkg.in/natefinch/lumberjack.v2 - 日志文件回滚与轮转
请参阅项目许可证文件。
欢迎提交 Issue 和 Pull Request!