跳至主要内容

MediaMTX / RTSP / ONVIF 筆記

三者常被放在一起講,但其實不在同一層。先把分層搞清楚,後面就都好懂。

0. 分層關係

ONVIF ← 控制面:探索相機、查詢能力、取得串流 URL、控制 PTZ(SOAP/XML over HTTP)
RTSP ← 訊令面:DESCRIBE / SETUP / PLAY,協商 codec 與傳輸方式
RTP/RTCP ← 資料面:實際搬運 H.264 / H.265 封包與品質回饋
MediaMTX ← 中介 server / proxy:收進上述串流,再轉出 RTSP / RTMP / HLS / WebRTC / SRT

一句話總結:ONVIF 幫你「找到並問出」串流位址,RTSP 幫你「把串流拉下來」,MediaMTX 幫你「轉發給任何人看」。


1. RTSP(Real Time Streaming Protocol)

是什麼

IETF 標準(RFC 2326 / RFC 7826,1998 起)。設計理念是「網路上的錄影機遙控器」——它本身不搬運影像資料,只負責控制指令:

Method作用
OPTIONS問 server 支援哪些指令
DESCRIBE取得 SDP(有哪些 track、什麼 codec)
SETUP協商傳輸方式(UDP / TCP interleaved)與埠號
PLAY / PAUSE開始 / 暫停播放
TEARDOWN結束 session

影像本體走 RTP,品質回饋走 RTCP

為什麼重要

  • 幾乎所有 IP camera、NVR、車用與工控攝影機都內建 RTSP server,是監控產業事實上的共同語言
  • 延遲可壓到 100–200 ms 等級(HLS 通常 2–10 s),即時監看、機器視覺 pipeline 都靠它。
  • OpenCV、FFmpeg、GStreamer 全都原生支援,接分析流程幾乎零成本。

限制

  • NAT / 防火牆不友善:RTP over UDP 用動態埠,跨網段常掛掉 → 改用 TCP interleaved(rtsp_transport=tcp)。
  • 瀏覽器不能直接播:這正是需要 MediaMTX 這類 gateway 的主因。
  • 無標準化的鑑權以外安全機制(RTSPS 支援度參差)。

2. ONVIF(Open Network Video Interface Forum)

是什麼

一套讓不同廠牌 IP camera 有共同介面的產業標準。用 SOAP/XML over HTTP,主要服務:

服務用途
Device Service裝置資訊、時間、網路設定、使用者管理
Media Service列出 profile、取得串流 URL(GetStreamUri)、snapshot
PTZ Service雲台上下左右、變焦、preset
Events Service移動偵測、IO 觸發等事件訂閱(WS-BaseNotification)
Imaging Service亮度、對比、白平衡、對焦

裝置探索用 WS-Discovery:UDP 多播到 239.255.255.250:3702,相機回覆自己的 XAddr(例如 http://192.168.1.64/onvif/device_service)。

常見 Profile

Profile內容
S基本串流 + PTZ(最常見)
TH.265、進階事件、metadata
G錄影與回放
M分析 metadata(物件偵測結果等)
A / C / D門禁控制相關

為什麼重要

不同品牌的 RTSP 路徑天差地遠:

Hikvision : rtsp://ip:554/Streaming/Channels/101
Dahua : rtsp://ip:554/cam/realmonitor?channel=1&subtype=0
Axis : rtsp://ip:554/axis-media/media.amp

有 ONVIF 就不必硬記——GetProfiles()GetStreamUri() 直接問出來。做多品牌自動化佈署時,這是關鍵


3. MediaMTX

是什麼

Go 寫的單一 binary、零外部依賴的即時媒體 server + proxy(前身是 rtsp-simple-server)。

輸入:RTSP、RTMP、SRT、WebRTC (WHIP)、HLS、UDP/MPEG-TS 輸出:RTSP、RTMP、SRT、WebRTC (WHEP)、HLS / LL-HLS

其他功能:

  • sourceOnDemand:沒人看就不連相機,省頻寬與相機連線數
  • 錄影分段(MP4 / fMP4)+自動清理
  • HTTP Control API(動態增刪 path)
  • runOnDemand / runOnPublish 等 hook(可掛 AI 推論)
  • Prometheus metrics、pprof
  • 多種鑑權(internal / HTTP / JWT)

為什麼重要

以前要 nginx-rtmp + Janus + GStreamer 才能拼出來的東西,現在一個 binary + 一份 YAML 就搞定。特別適合:

  • CI / 測試環境的假相機:用影片檔模擬 RTSP source,不用真的擺硬體
  • 協定轉換 gateway:讓瀏覽器(WebRTC)看得到 RTSP 相機
  • 邊緣裝置的串流中樞:ARM binary 只有十幾 MB

4. 實作:怎麼用

4.1 起 server

# Docker(建議 host network,否則 RTSP/WebRTC 埠會很麻煩)
docker run --rm -it --network=host bluenviron/mediamtx:latest

# 或直接抓 binary,會讀同目錄的 mediamtx.yml
./mediamtx

預設埠:

服務
RTSP8554
RTMP1935
HLS8888
WebRTC8889
SRT8890
API9997

4.2 代理一台真相機

mediamtx.yml

paths:
cam1:
source: rtsp://admin:pass@192.168.1.64:554/Streaming/Channels/101
sourceOnDemand: yes # 沒 client 就不連線
sourceOnDemandCloseAfter: 10s
sourceProtocol: tcp # 丟包嚴重時改 TCP
record: yes
recordPath: ./rec/cam1/%Y-%m-%d_%H-%M-%S
recordSegmentDuration: 1h
recordDeleteAfter: 168h # 保留 7 天

播放端:

RTSP : rtsp://<host>:8554/cam1
WebRTC : http://<host>:8889/cam1 ← 瀏覽器直接開,延遲最低
HLS : http://<host>:8888/cam1/index.m3u8

4.3 用影片檔模擬一台相機(CI / 壓測神器)

ffmpeg -re -stream_loop -1 -i sample.mp4 -c copy \
-f rtsp -rtsp_transport tcp rtsp://localhost:8554/fake_cam

或讓 MediaMTX 自己拉起來:

paths:
fake_cam:
runOnDemand: >
ffmpeg -re -stream_loop -1 -i /media/sample.mp4 -c copy
-f rtsp rtsp://localhost:8554/$MTX_PATH
runOnDemandRestart: yes

4.4 ONVIF 探索並自動接上

MediaMTX 本身不做 ONVIF discovery,需另外的工具:

pip install onvif-zeep wsdiscovery
from wsdiscovery.discovery import ThreadedWSDiscovery as WSD
from onvif import ONVIFCamera
import yaml, urllib.parse

# --- Step 1: 區網探索 ---
wsd = WSD(); wsd.start()
services = wsd.searchServices()
addrs = [x for s in services for x in s.getXAddrs()]
wsd.stop()
print(addrs) # ['http://192.168.1.64:80/onvif/device_service', ...]

# --- Step 2: 問出 RTSP URL ---
paths = {}
for i, xaddr in enumerate(addrs):
host = urllib.parse.urlparse(xaddr).hostname
cam = ONVIFCamera(host, 80, 'admin', 'pass')
media = cam.create_media_service()
profile = media.GetProfiles()[0]
uri = media.GetStreamUri({
'StreamSetup': {'Stream': 'RTP-Unicast',
'Transport': {'Protocol': 'RTSP'}},
'ProfileToken': profile.token
}).Uri
# 把帳密塞回 URL
uri = uri.replace('rtsp://', 'rtsp://admin:pass@')
paths[f'cam{i}'] = {'source': uri, 'sourceOnDemand': True}

# --- Step 3: 產生 MediaMTX 設定 ---
yaml.safe_dump({'paths': paths}, open('mediamtx.yml', 'w'))

PTZ 控制也是同一個 client:

ptz = cam.create_ptz_service()
ptz.ContinuousMove({'ProfileToken': profile.token,
'Velocity': {'PanTilt': {'x': 0.5, 'y': 0.0}}})

4.5 接分析 pipeline

import cv2
cap = cv2.VideoCapture('rtsp://localhost:8554/cam1', cv2.CAP_FFMPEG)
# 若延遲累積,設環境變數:
# OPENCV_FFMPEG_CAPTURE_OPTIONS="rtsp_transport;tcp|buffer_size;1024000"

5. 常見的坑

症狀原因 / 解法
ONVIF 回 401 UnauthorizedWS-UsernameToken digest 用 UTC 時間簽章,相機時間差 > 5 秒就失敗 → 先 SetSystemDateAndTime 或開 NTP
WS-Discovery 找不到相機多播跨不了 VLAN / Docker bridge → 用 --network=host,或改用 IP 逐台掃
畫面破圖、馬賽克UDP 丟包 → sourceProtocol: tcp(FFmpeg 端是 -rtsp_transport tcp
延遲越看越久client 端 buffer 累積 → OpenCV 設小 buffer,或改用 WebRTC 播放
相機連線數爆掉多個 client 各自連相機 → 用 MediaMTX 當單一 source 再 fan-out
瀏覽器 WebRTC 連不上跨網段需要 STUN/TURN → 設 webrtcICEServerswebrtcAdditionalHosts
CPU 飆高不小心觸發轉碼 → 確認用 -c copy,MediaMTX 預設就是 remux 不轉碼
解析度太高吃資源改抓 sub-stream(Hikvision /Streaming/Channels/102、Dahua subtype=1),預覽與分析夠用

6. 快速決策表

需求建議
只是要看單台相機VLC 直接開 RTSP URL
要給瀏覽器看MediaMTX → WebRTC(延遲 500 ms 以內)或 LL-HLS
多品牌自動接入ONVIF discovery + GetStreamUri → 產生 MediaMTX config
CI 需要穩定的假串流MediaMTX + FFmpeg loop 影片檔
要錄影 + 即時看MediaMTX record: yes,同時開 RTSP / WebRTC 輸出
極低延遲跨公網SRT 或 WebRTC,別用 RTSP

參考資料