OvenMediaEngine 0.21.0 Docker Compose 部署指南

OvenMediaEngine 0.21.0 Docker Compose 部署指南:端口规划映射、Server.xml 三处关键修改、RTMP 推流与 LLHLS/HLS/WebRTC 分发全流程实测

OvenMediaEngineOvenMediaEngine流媒体DockerDocker Compose媒体服务器2026/10/30

OvenMediaEngine 0.21.0 Docker Compose 部署指南

目标:在一台 Linux 服务器上用 Docker Compose 部署 OvenMediaEngine(OME)v0.21.0,完成 RTMP 推流接入与 LLHLS / HLS / WebRTC 三路分发验证,并打通 REST API 管理面。本文所有命令与回显均在下述实测环境中逐条执行过;端口规划采用「官方默认端口 → 宿主高位端口」的映射方案(原因与对应关系见第三节)。

一、环境信息

项值说明
操作系统Ubuntu 24.04(x86_64,内核 7.0.0-38-generic)服务器为双栈(IPv4/IPv6)
Docker29.9.2docker --version 实测
Docker Composev5.6.1docker compose version 实测
OME 版本v0.21.0(官方最新稳定版,2026-08-13 发布)docker image inspect 显示镜像构建于 2026-08-13,与 Release 同日
镜像airensoft/ovenmediaengine:v0.21.0约 512 MB,基于 Ubuntu 22.04,内嵌 FFmpeg 7.1.5、libaom v3.12.0、SRT 1.5.2、libSRTP 2.4.2
部署目录/opt/ovenmediaengine-compose/存放 docker-compose.yml 与 Server.xml
网络专用桥接网络 ovenmediaengine-0.21.0-net(子网 10.89.64.0/24)与宿主上其他服务隔离
验证时间2026-10-29全部命令实测当日

💡 镜像命名空间说明:OME 官方镜像同时发布在 airensoft/ovenmediaengine 与 ovenmedialabs/ovenmediaengine 两个命名空间,v0.21 起官方文档站推荐后者;实测两者 v0.21.0 为同一构建。tag 规律为 v 前缀(v0.21.0),不存在不带前缀的 0.21.0 tag,写错会报 manifest unknown。

二、前置条件

  1. 已安装 Docker 与 Docker Compose v2+(docker compose version 有输出即可);
  2. 服务器可达公网(首次启动 OME 会通过 STUN 服务器 stun.ovenmediaengine.com:13478 解析公网 IP,用于 WebRTC 候选地址;不通则需显式设置 OME_HOST_IP);
  3. 已规划端口并确认空闲(本机复核命令:ss -ltn 与 ss -lun)。

三、端口规划与官方默认值的映射关系

OME 各协议的官方默认端口如下(取自默认配置 Server.xml 中的 ${env:OME_*} 缺省值):

协议/用途官方默认环境变量传输层
RTMP 推流1935OME_RTMP_PROV_PORTTCP
SRT 推流9999OME_SRT_PROV_PORTUDP(SRT)
WebRTC 信令 + HLS/LLHLS3333OME_WEBRTC_SIGNALLING_PORT / OME_LLHLS_STREAM_PORTTCP(LLHLS 兼容 HTTP/1.1)
WebRTC 信令/LLHLS TLS3334OME_WEBRTC_SIGNALLING_TLS_PORT / OME_LLHLS_STREAM_TLS_PORTTCP(需证书)
REST API8081OME_API_PORTTCP
OVT(Origin-Edge)9000OME_ORIGIN_PORTTCP
WebRTC ICE(默认候选段)10000-10003/udpOME_WEBRTC_CANDIDATE_PORTUDP
WebRTC ICE TCP 回退10000/tcpOME_WEBRTC_TCP_ICE_PORTTCP(RFC 6544)
WebRTC TURN(内嵌中继)3478OME_WEBRTC_TCP_RELAY_PORTTCP
MPEG-2 TS 推流4000-4005/udpOME_MPEGTS_PROV_PORTUDP

本服务器 1935/9999/3333/8081 等标准端口已被其他业务占用,因此全部映射到宿主空闲高位段(UDP 端口同样映射),映射关系如下——这是本服务器端口规划与官方默认值的对应关系,非官方默认配置:

OME 功能官方默认本服务器宿主端口映射到容器内
RTMP 推流1935/TCP81601935
SRT 推流9999/UDP81619999
WebRTC 信令 + HLS/LLHLS3333/TCP81623333
REST API8081/TCP81638081
WebRTC ICE(UDP)10000/UDP816410000
WebRTC ICE(TCP 回退)10000/TCP816510000

💡 说明:为把 ICE 端口段收敛为单端口,实测通过环境变量 OME_WEBRTC_CANDIDATE_PORT=10000/udp 把默认的 10000-10003 四端口候选段改为单端口 10000;TURN 3478 仅在内网穿透受限时才需要映射,本方案未暴露。若宿主 8160-8165 不可用,请顺延选择空闲端口并同步修改 compose 文件,其余不变。

四、部署步骤

1. 建目录与专用网络

mkdir -p /opt/ovenmediaengine-compose
docker network create --subnet 10.89.64.0/24 ovenmediaengine-0.21.0-net

⚠️ --subnet 建议显式指定并先确认与宿主上既有 docker 网络不冲突(docker network inspect 逐个核对);不指定时 Docker 会从默认地址池自动分配,可能与并行部署的服务撞池。

2. 提取默认配置文件

官方镜像内自带默认配置,直接用一次性命令提取到宿主(实测输出为 Server.xml version="8" 结构):

cd /opt/ovenmediaengine-compose
docker run --rm airensoft/ovenmediaengine:v0.21.0 \
  cat /opt/ovenmediaengine/bin/origin_conf/Server.xml > Server.xml

默认配置即可接收 RTMP 推流并输出 LLHLS(3333 端口同端口复用信令与 HLS),本方案在其基础上做三处修改(见下节)。

3. 修改 Server.xml(三处)

第一处:启用 REST API。 v0.21 的 API 配置分居两处——监听端口在 Bind/Managers/API(默认被注释,含 <Port> 与 <WorkerCount>),访问令牌在 Server/Managers/API(默认整段缺失,需手工新增 <AccessToken>)。两处都配好 API 才能启动:

<!-- ① 在 <Bind> 下(与 <Providers> 平级)取消注释/新增: -->
<Bind>
    <Managers>
        <API>
            <Port>${env:OME_API_PORT:8081}</Port>
            <WorkerCount>1</WorkerCount>
        </API>
    </Managers>
    <Providers>
        ...
    </Providers>
</Bind>

<!-- ② 在 <Server> 下(与 <Bind> 平级)新增: -->
<Managers>
    <API>
        <AccessToken>q1w2e3r4</AccessToken>
    </API>
</Managers>

⚠️ 两处放错位置会直接启动失败:<AccessToken> 写进 Bind/Managers/API 会报 Unknown item found: Server.bind.managers.api.AccessToken;<AccessToken> 缺失则 API 模块拒绝启动并退出进程(Empty <AccessToken> is not allowed)。排障实录见运维篇。

第二处(可选):启用 HLSv3(传统 TS 打包)。 默认配置的 Bind/Publishers 中没有 <HLS> 绑定,对应 Publisher 处于禁用状态(启动日志明示 HLSv3 Publisher is disabled by configuration)。需要向不支持 fMP4 的传统设备分发时,在 Bind/Publishers 下新增:

<Publishers>
    ...
    <HLS>
        <Port>3333</Port>
        <WorkerCount>1</WorkerCount>
    </HLS>
</Publishers>

第三处(可选):自定义 ABR 档位。 默认 OutputProfiles 使用 RenditionTemplate 自动匹配档位;要显式控制档位组合,改写 Applications/Application/OutputProfiles/OutputProfile/Playlist(v0.21 中 <Rendition> 是单数元素直接挂在 <Playlist> 下,可重复出现,没有 <Renditions> 包装层——写成包装层会报 Unknown item 拒绝启动):

<Playlist>
    <Name>default</Name>
    <FileName>master</FileName>
    <Options>
        <WebRtcAutoAbr>true</WebRtcAutoAbr>
        <HLSChunklistPathDepth>0</HLSChunklistPathDepth>
        <EnableTsPackaging>true</EnableTsPackaging>
    </Options>
    <Rendition>
        <Name>bypass</Name>
        <Video>bypass_video</Video>
        <Audio>aac_audio</Audio>
    </Rendition>
    <Rendition>
        <Name>720p</Name>
        <Video>video_720</Video>
        <Audio>aac_audio</Audio>
    </Rendition>
</Playlist>

同时在 Encodes 中保留(或补上)与 Rendition 引用名一致的编码器定义,例如 video_720:

<Video>
    <Name>video_720</Name>
    <Codec>h264</Codec>
    <Bitrate>2024000</Bitrate>
    <Framerate>30</Framerate>
    <Width>1280</Width>
    <Height>720</Height>
    <Preset>faster</Preset>
</Video>

💡 <FileName>master</FileName> 决定该播放列表的访问文件名(master.m3u8);默认还内置一个自动生成的 llhls.m3u8(仅含单档位),两者并存,自定义 ABR 验证时注意区分(详见运维篇)。配置项逐段精讲见《OvenMediaEngine 0.21.0 配置参数详解》。

4. 编写 docker-compose.yml

name: ovenmediaengine-0.21.0-stack

services:
  ovenmediaengine:
    image: airensoft/ovenmediaengine:v0.21.0
    container_name: ovenmediaengine-0.21.0-app
    hostname: ovenmediaengine-0.21.0-app
    restart: "no"
    environment:
      OME_HOST_IP: 192.168.146.128        # 改为客户端可达的本机 IP(公网或 LAN)
      OME_WEBRTC_CANDIDATE_PORT: "10000/udp"  # 候选端口段收敛为单端口
    networks:
      - ome
    ports:
      - "8160:1935/tcp"    # RTMP Provider
      - "8161:9999/udp"    # SRT Provider
      - "8162:3333/tcp"    # WebRTC Signaling + HLS/LLHLS Publisher
      - "8163:8081/tcp"    # REST API Server
      - "8164:10000/udp"   # WebRTC ICE (UDP)
      - "8165:10000/tcp"   # WebRTC ICE (TCP, RFC 6544)
    volumes:
      - ./Server.xml:/opt/ovenmediaengine/bin/origin_conf/Server.xml:ro

networks:
  ome:
    name: ovenmediaengine-0.21.0-net
    external: true

要点:

  • OME_HOST_IP 必须是客户端可达的 IP;WebRTC offer 中下发的 ICE 候选地址取自它(实测设为内网地址时,日志打印 ICE candidate found: 192.168.146.128:10000);
  • UDP 端口必须按 /udp 显式映射,漏写会导致 WebRTC 播放黑屏;
  • 配置文件以只读方式挂载覆盖容器内默认配置,改配置只需改宿主文件后重启容器。

5. 启动与启动日志验证

cd /opt/ovenmediaengine-compose
docker compose up -d
docker logs -f ovenmediaengine-0.21.0-app

启动成功时日志横幅与关键行(实测原文节选,时间戳与内存地址已省略):

▄██████▀███▄
█████▀ ▄██████  OvenMediaEngine Launcher v0.1
███▄▄▄▄▀▀▀▀███
██████▀ ▄█████  https://github.com/OvenMediaLabs/OvenMediaEngine
 ▀███▄██████▀
...
OvenMediaEngine | banner.cpp:23 | OvenMediaEngine v0.21.0 (v0.21.0) is started on [ovenmediaengine-0.21.0-app] (Linux x86_64 - 7.0.0-38-generic, ...)
OvenMediaEngine | banner.cpp:28 |   FFmpeg 7.1.5
OvenMediaEngine | banner.cpp:36 |   libaom: v3.12.0
OvenMediaEngine | banner.cpp:37 |   SRT: 1.5.2
OvenMediaEngine | main.cpp:99  | Resolved public IP address (182.54.113.x) from stun server (stun.ovenmediaengine.com:13478)
ICE | ice_port_manager.cpp:342 | ICE candidate found: 192.168.146.128:10000
ICE | ice_port.cpp:123  | ICE ports are listening on *:10000/TCP (...), *:10000/UDP (...) (for WebRTC Publisher)
ICE | ice_port_manager.cpp:227 | TURN port is listening on *:3478/TCP (for WebRTC Publisher)
OVT  | ovt_publisher.cpp:90   | OVTPublisher is listening on *:9000/TCP...
SrtProvider   | srt_provider.cpp:125 | SrtProvider is listening on *:9999/SRT
RTMPProvider  | rtmp_provider.cpp:169 | RTMPProvider is listening on *:1935

默认被禁用的组件会在日志文件(容器内 /var/log/ovenmediaengine/ovenmediaengine.log)中以 W 级别明示,例如 HLSv3 Publisher is disabled by configuration、SRTPublisher is disabled by configuration、ThumbnailPublisher is disabled by configuration——按第三节的修改补上绑定后,对应组件即随启动注册(docker logs 中出现 HLS Publisher ... HLS Stream ... All playlists are ready to play 等运行期行)。

6. 推流与分发验证

推流(本方案在专用网络内以 ffmpeg 长驻推流;宿主机装有 ffmpeg 时可直接对宿主端口推):

docker run -d --name ovenmediaengine-0.21.0-publisher \
  --network ovenmediaengine-0.21.0-net --restart unless-stopped \
  jrottenberg/ffmpeg:5-alpine \
  -re -f lavfi -i "testsrc2=size=1280x720:rate=30" \
  -f lavfi -i "sine=frequency=1000:sample_rate=48000" \
  -c:v libx264 -preset veryfast -tune zerolatency -profile:v main -pix_fmt yuv420p \
  -b:v 2000k -g 60 -c:a aac -b:a 128k -ac 2 -ar 48000 \
  -f flv rtmp://ovenmediaengine-0.21.0-app:1935/app/stream

推流建立后 OME 日志出现(实测原文):

RTMPProvider  | rtmp_provider.cpp:228 | A RTMP client has connected from <ClientSocket: ..., Connected, TCP, Nonblocking, <推流端地址>>
MediaRouter   | mediarouter_application.cpp:467 | [#default#app/stream(100)] Stream has been created
WebRTC Publisher | rtc_stream.cpp:283 | WebRTC Stream has been created : stream/775775263

分发验证(逐项实测回显):

# ① LLHLS(自动默认列表,单档位)
curl -s http://127.0.0.1:8162/app/stream/llhls.m3u8 | head -6
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac_audio",NAME="Audio_1",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="/app/stream/chunklist_2_audio_7930408358254892257_llhls.m3u8"

#EXT-X-STREAM-INF:BANDWIDTH=2158032,RESOLUTION=1280x720,FRAME-RATE=30.000,CODECS="avc1.4d401f,mp4a.40.2",AUDIO="aac_audio"
/app/stream/chunklist_0_video_7930408358254892257_llhls.m3u8
# ② ABR 双档列表(自定义 Playlist,文件名 master)
curl -s http://127.0.0.1:8162/app/stream/master.m3u8
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac_audio",NAME="Audio_1",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="chunklist_2_audio_7930408358254892257_llhls.m3u8"


#EXT-X-STREAM-INF:BANDWIDTH=2158032,RESOLUTION=1280x720,FRAME-RATE=30.000,CODECS="avc1.4d401f,mp4a.40.2",AUDIO="aac_audio"
chunklist_0_video_7930408358254892257_llhls.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2149000,RESOLUTION=1280x720,FRAME-RATE=30.000,CODECS="avc1.42c01f,mp4a.40.2",AUDIO="aac_audio"
chunklist_1_video_7930408358254892257_llhls.m3u8

(avc1.4d401f 档为 video_720 重编码输出,avc1.42c01f 档为 bypass 直通原流,ABR 双档生效。)

# ③ HLSv3(TS 打包,URL 以 ts: 前缀区分;仅 <HLS> 绑定启用后可用)
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8162/app/stream/ts:playlist.m3u8   # 200
# ④ REST API(认证头为 Basic base64(AccessToken),token 原样编码、不带冒号)
curl -s -H "authorization: Basic $(printf 'q1w2e3r4' | base64)" http://127.0.0.1:8163/v1/vhosts
{"message":"OK","response":["default"],"statusCode":200}
# ⑤ WebRTC 信令(WebSocket,应答 offer 与 ICE 候选)
python3 - <<'EOF'
import websocket, json
ws = websocket.create_connection("ws://127.0.0.1:8162/app/stream", timeout=10)
ws.send(json.dumps({"command": "request_offer", "direction": "sendrecv", "id": "test-viewer-1"}))
msg = json.loads(ws.recv())
print(msg.get("command"), msg.get("code"), "| candidates:", len(msg.get("candidates", [])))
ws.close()
EOF
offer 200 | candidates: 1

浏览器端播放可用官方演示播放器 http://demo.ovenplayer.com(WebRTC 源 ws://<服务器IP>:8162/app/stream,LLHLS 源 http://<服务器IP>:8162/app/stream/llhls.m3u8),或部署 OvenPlayer 自建播放页。

五、数据持久化说明

OME 默认为无状态内存打包:流分片在内存中生成后直接经 HTTP 分发,不落盘;进程重启即丢流(推流端自动重连后重建)。需要持久化时按需挂载:

数据容器内路径挂载建议
主配置/opt/ovenmediaengine/bin/origin_conf/Server.xml宿主文件只读挂载(本方案采用)
运行日志/var/log/ovenmediaengine/ovenmediaengine.log需留存时挂载为卷
录制输出(File Publisher)/opt/ovenmediaengine/media/启用录制后挂载持久卷

六、环境变量速查

镜像通过 ${env:VAR:default} 语法在默认配置中引用以下环境变量(实测提取自 v0.21.0 镜像内置 Server.xml):

环境变量默认值用途
OME_HOST_IP192.168.0.160WebRTC 对外候选 IP(必须改为客户端可达地址)
OME_RTMP_PROV_PORT1935RTMP 推流端口
OME_SRT_PROV_PORT9999SRT 推流端口
OME_MPEGTS_PROV_PORT4000-4005/udpMPEG-2 TS 推流端口
OME_WEBRTC_SIGNALLING_PORT3333WebRTC 信令端口
OME_WEBRTC_SIGNALLING_TLS_PORT3334WebRTC 信令 TLS 端口
OME_WEBRTC_CANDIDATE_PORT10000-10003/udpICE 候选端口段
OME_WEBRTC_TCP_ICE_PORT10000/tcpICE TCP 回退端口
OME_WEBRTC_TCP_RELAY_PORT3478内嵌 TURN 端口
OME_LLHLS_STREAM_PORT3333LLHLS 分发端口
OME_LLHLS_STREAM_TLS_PORT3334LLHLS TLS 端口
OME_API_PORT8081REST API 端口
OME_ORIGIN_PORT9000OVT(Origin-Edge)端口

💡 这些变量只覆盖端口/IP 类值;结构性配置(Publisher 开关、转码档位、AccessToken 等)必须改 Server.xml。修改配置后执行 docker compose restart 生效——OME 无热加载,实测改完立即生效无需重建容器。

七、等价 docker run 命令

不想用 compose 时,以下单命令与上述编排等价(长命令建议拆行或转 systemd unit):

docker run -d --name ovenmediaengine-0.21.0-app --restart unless-stopped \
  --network ovenmediaengine-0.21.0-net \
  -e OME_HOST_IP=192.168.146.128 \
  -e OME_WEBRTC_CANDIDATE_PORT=10000/udp \
  -p 8160:1935/tcp -p 8161:9999/udp -p 8162:3333/tcp \
  -p 8163:8081/tcp -p 8164:10000/udp -p 8165:10000/tcp \
  -v /opt/ovenmediaengine-compose/Server.xml:/opt/ovenmediaengine/bin/origin_conf/Server.xml:ro \
  airensoft/ovenmediaengine:v0.21.0

八、常用运维命令

docker compose ps                                  # 栈内容器状态
docker logs -f ovenmediaengine-0.21.0-app          # 跟踪 stdout 日志
docker exec ovenmediaengine-0.21.0-app \
  tail -f /var/log/ovenmediaengine/ovenmediaengine.log   # 跟踪引擎日志文件(含 W/E 级别详情)
docker compose restart                             # 改配置后重启生效
docker compose down                                # 停止并移除(外部网络保留)
ss -ltn | grep -E ':(8160|8162|8163|8165)\b'       # 核对 TCP 端口监听
ss -lun | grep -E ':(8161|8164)\b'                 # 核对 UDP 端口监听

日志分级、API 运维面与真实排障案例(Unknown item / Empty AccessToken / HLSv3 404 / SRT streamid 解析失败等 8 例)见《OvenMediaEngine 0.21.0 常用运维命令与排障详解》。

九、常见问题

  1. 拉不到 v0.21.0(或 0.21.0)tag:官方 tag 带 v 前缀,用 airensoft/ovenmediaengine:v0.21.0;国内环境可先 docker pull 官方镜像,失败时为 Docker 守护进程配置可用的镜像加速或代理后重试。
  2. API 端口不通 / 进程反复退出:检查 AccessToken 是否配置在 Server/Managers/API 下(不在 Bind 内)、<Port> 是否配置在 Bind/Managers/API 下;两处错位的报错原文见运维篇 6.1/6.2。
  3. /app/stream/playlist.m3u8 返回 404:HLSv3 默认禁用,按第四节第二处新增 <HLS> 绑定后重启;LLHLS 的默认列表是 /app/stream/llhls.m3u8,不要混淆。
  4. WebRTC 播放黑屏:按序检查 OME_HOST_IP 是否为客户端可达 IP、8164/udp 是否映射、客户端到该 UDP 端口是否连通;TCP 回退(8165)仅在播放器显式启用 ICE-TCP 时生效。
  5. 推流容器启动报 Name does not resolve:OME 与推流器同网络启动存在时序差,推流器配置自动重试(本方案的 --restart unless-stopped + ffmpeg 内部重连)即可自愈,也可用 depends_on 约束。
  6. 改了 ABR 档位但 llhls.m3u8 没变化:llhls.m3u8 是引擎自动生成的默认单档列表;自定义多档列表访问的是 Playlist/FileName 指定的名字(本方案 master.m3u8)。

十、参考链接