Feature request: Support multi-level app paths for HTTP remux (multi-camera/multi-tenant streams)
Summary
SRS HTTP remux (http_remux) rejects stream paths whose slash count differs from the mount template. This prevents multi-level app paths like tenant-1/live/0/0/AZR-001 from serving HTTP-TS/FLV/HLS — even though SRT ingest and WebRTC playback handle them fine. We request an opt-in config to support multi-level app paths.
Use Case
Multi-tenant streaming platforms with multi-camera drones need structured stream paths:
{tenant}/live/{camera_id}/{slot}/{device_id}Example:
tenant-1/live/0/0/AZR-001— drone AZR-001, camera 0, slot 0tenant-1/live/1/0/AZR-001— drone AZR-001, camera 1, slot 0tenant-1/live/0/1/AZR-001— drone AZR-001, camera 0, slot 1
The camera_id and slot segments are required to:
- Route each camera to a separate stream
- Segment DVR recordings per camera
- Allow the frontend to construct playback URLs per camera
Collapsing to 2 levels (tenant-1/AZR-001-cam0-slot0) loses the structural separation that SRS routing, DVR segmentation, and stream hooks rely on.
Current Behavior
SrsLiveStream::serve_http_impl in src/app/srs_app_http_stream.cpp (lines ~1307–1316) rejects paths whose slash count differs from the mount template:
// not-matched for "/live/show/livestream.flv"
if (srs_strings_count(upath, "/") != srs_strings_count(entry->mount_, "/")) {
return err; // reject
}This is documented as intentional design — the mount template [vhost]/[app]/[stream].ext expects exactly 2 slashes, and multi-level app paths like tenant-1/live/0/0/AZR-001.ts have 5 slashes → rejected → 404.
What Works Today
| Protocol | Multi-level path | Status |
|---|---|---|
| SRT ingest | ✅ | Works — SRT streamid is parsed independently |
| WebRTC/WHEP play | ✅ | Works — SDP negotiation uses the full stream URL |
| HTTP-TS remux | ❌ | 404 — slash count mismatch |
| HTTP-FLV remux | ❌ | 404 — same restriction |
| HLS | ❌ | 404 — same restriction |
Proposed Solution
Add an opt-in config directive:
http_vhost {
http_remux {
mount [vhost]/[app]/[stream].ts;
allow_multilevel_app on; # default off — backward compatible
}
}When allow_multilevel_app is on, split the request path at the last slash to resolve app and stream, instead of requiring an exact slash count match:
// When allow_multilevel_app is enabled:
// Split at last slash: stream = after last slash, app = before last slash
std::string::size_type slash = upath.rfind("/");
if (slash != std::string::npos) {
req->stream_ = upath.substr(slash + 1);
req->app_ = srs_strings_trim_start(upath.substr(0, slash), "/");
}This is backward compatible:
allow_multilevel_app off(default): existing behavior unchangedallow_multilevel_app on: multi-level paths work, 2-level paths still work
Why Not Just Use 2-Level Paths?
Encoding camera/slot into the stream name (tenant-1/AZR-001-cam0-slot0) is possible but:
- Loses structural separation for SRS
on_publish/on_playhooks - Makes DVR segment naming ambiguous (camera/slot buried in stream name)
- Requires custom parsing in every consumer (frontend, analytics, DVR uploader)
- Breaks existing SRT streamid conventions that already use multi-level paths
Scope
This request is for the C++ SRS media server (src/app/srs_app_http_stream.cpp). The restriction applies to all HTTP remux outputs (TS, FLV, MP4, HLS).
Source: ossrs/srs