Skip to content

Commit 6590871

Browse files
ossrs-aiwinlinvip
authored andcommitted
AI: HLS: Support hls_master_m3u8_path_relative for reverse proxy compatibility. v7.0.104 (#4338)
1 parent b7828e1 commit 6590871

File tree

9 files changed

+73
-3
lines changed

9 files changed

+73
-3
lines changed

trunk/3rdparty/srs-docs/doc/hls.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,23 @@ Then configure `hls_path` or create a soft link to the directory.
426426

427427
To deploy an HLS distribution cluster and edge distribution cluster for your own CDN to handle a large number of viewers, please refer to [Nginx for HLS](./nginx-for-hls.md).
428428

429+
## HLS with Reverse Proxy
430+
431+
When deploying SRS behind a reverse proxy with path rewriting, you may encounter issues where HLS master playlists use absolute paths (e.g., `/live/livestream.m3u8?hls_ctx=xxx`), which can break playback when the proxy rewrites request paths. For example, if the external URL is `http://proxy/srs/live/stream.m3u8` but gets rewritten to `http://origin/live/stream.m3u8`, the absolute path in the master playlist will drop the `/srs` prefix, causing a 404 error.
432+
433+
SRS (v7.0.104+) provides the `hls_master_m3u8_path_relative` option to use relative paths in master playlists for reverse proxy compatibility:
434+
435+
```bash
436+
vhost __defaultVhost__ {
437+
hls {
438+
enabled on;
439+
hls_master_m3u8_path_relative on; # Use relative path for reverse proxy
440+
}
441+
}
442+
```
443+
444+
When enabled, the master playlist uses relative paths (e.g., `livestream.m3u8?hls_ctx=xxx`) instead of absolute paths, allowing the player to correctly resolve URLs through the reverse proxy. This is useful for deployments with Nginx, Apache, HAProxy, API gateways, or multi-tenant systems with path-based routing. The default is `off` for backward compatibility.
445+
429446
## HLS Low Latency
430447

431448
How to reduce HLS latency? The key is to reduce the number of slices and the number of TS files in the m3u8. SRS's default configuration is 10 seconds per slice and 60 seconds per m3u8, resulting in a latency of about 30 seconds. Some players start requesting slices from the middle position, so there will be a delay of 3 slices.

trunk/conf/full.conf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,6 +1478,16 @@ vhost hls.srs.com {
14781478
# Overwrite by env SRS_VHOST_HLS_HLS_TS_CTX for all vhosts.
14791479
# Default: on
14801480
hls_ts_ctx on;
1481+
# Whether use relative path for media playlist URL in HLS master playlist.
1482+
# When on, the master playlist uses relative path like "livestream.m3u8?hls_ctx=xxx".
1483+
# When off, the master playlist uses absolute path like "/live/livestream.m3u8?hls_ctx=xxx".
1484+
# Relative path is useful for reverse proxy scenarios with path rewriting.
1485+
# For example, if external URL is "http://proxy/srs/live/stream.m3u8" and it's rewritten to
1486+
# "http://origin/live/stream.m3u8", relative path ensures the player can correctly resolve
1487+
# the media playlist URL.
1488+
# Overwrite by env SRS_VHOST_HLS_HLS_MASTER_M3U8_PATH_RELATIVE for all vhosts.
1489+
# Default: off
1490+
hls_master_m3u8_path_relative off;
14811491

14821492
# whether using AES encryption.
14831493
# Overwrite by env SRS_VHOST_HLS_HLS_KEYS for all vhosts.

trunk/doc/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ The changelog for SRS.
77
<a name="v7-changes"></a>
88

99
## SRS 7.0 Changelog
10+
* v7.0, 2025-10-26, HLS: Support hls_master_m3u8_path_relative for reverse proxy compatibility. v7.0.104 (#4338)
1011
* v7.0, 2025-10-25, API: Remove minimum limit of 10 for count parameter in /api/v1/streams and /api/v1/clients. v7.0.103 (#4358)
1112
* v7.0, 2025-10-22, AI: Only support AAC/MP3/Opus audio codec. v7.0.102 (#4516)
1213
* v7.0, 2025-10-22, AI: Fix AAC audio sample rate reporting in API. v7.0.101 (#4518)

trunk/src/app/srs_app_config.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2290,7 +2290,7 @@ srs_error_t SrsConfig::check_normal_config()
22902290
} else if (n == "hls") {
22912291
for (int j = 0; j < (int)conf->directives_.size(); j++) {
22922292
string m = conf->at(j)->name_;
2293-
if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" && m != "hls_wait_keyframe" && m != "hls_dispose" && m != "hls_keys" && m != "hls_fragments_per_key" && m != "hls_key_file" && m != "hls_key_file_path" && m != "hls_key_url" && m != "hls_dts_directly" && m != "hls_ctx" && m != "hls_ts_ctx" && m != "hls_use_fmp4" && m != "hls_fmp4_file" && m != "hls_init_file" && m != "hls_recover") {
2293+
if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor" && m != "hls_cleanup" && m != "hls_nb_notify" && m != "hls_wait_keyframe" && m != "hls_dispose" && m != "hls_keys" && m != "hls_fragments_per_key" && m != "hls_key_file" && m != "hls_key_file_path" && m != "hls_key_url" && m != "hls_dts_directly" && m != "hls_ctx" && m != "hls_ts_ctx" && m != "hls_use_fmp4" && m != "hls_fmp4_file" && m != "hls_init_file" && m != "hls_recover" && m != "hls_master_m3u8_path_relative") {
22942294
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.hls.%s of %s", m.c_str(), vhost->arg0().c_str());
22952295
}
22962296

@@ -6692,6 +6692,25 @@ string SrsConfig::get_hls_key_url(std::string vhost)
66926692
return conf->arg0();
66936693
}
66946694

6695+
bool SrsConfig::get_hls_master_m3u8_path_relative(string vhost)
6696+
{
6697+
SRS_OVERWRITE_BY_ENV_BOOL("srs.vhost.hls.hls_master_m3u8_path_relative"); // SRS_VHOST_HLS_HLS_MASTER_M3U8_PATH_RELATIVE
6698+
6699+
static bool DEFAULT = false;
6700+
6701+
SrsConfDirective *conf = get_hls(vhost);
6702+
if (!conf) {
6703+
return DEFAULT;
6704+
}
6705+
6706+
conf = conf->get("hls_master_m3u8_path_relative");
6707+
if (!conf || conf->arg0().empty()) {
6708+
return DEFAULT;
6709+
}
6710+
6711+
return SRS_CONF_PREFER_FALSE(conf->arg0());
6712+
}
6713+
66956714
bool SrsConfig::get_hls_recover(string vhost)
66966715
{
66976716
SRS_OVERWRITE_BY_ENV_BOOL2("srs.vhost.hls.hls_recover"); // SRS_VHOST_HLS_HLS_RECOVER

trunk/src/app/srs_app_config.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ class ISrsAppConfig : public ISrsConfig
519519
virtual bool get_vhost_hls_dts_directly(std::string vhost) = 0;
520520
virtual bool get_hls_ctx_enabled(std::string vhost) = 0;
521521
virtual bool get_hls_ts_ctx_enabled(std::string vhost) = 0;
522+
virtual bool get_hls_master_m3u8_path_relative(std::string vhost) = 0;
522523
virtual bool get_hls_recover(std::string vhost) = 0;
523524
virtual bool get_dash_enabled(std::string vhost) = 0;
524525
virtual bool get_dash_enabled(SrsConfDirective *vhost) = 0;
@@ -1366,6 +1367,11 @@ class SrsConfig : public ISrsAppConfig
13661367
// Whether enable session for ts file.
13671368
// The ts file including .ts file for MPEG-ts segment, .m4s file and init.mp4 file for fmp4 segment.
13681369
virtual bool get_hls_ts_ctx_enabled(std::string vhost);
1370+
// Whether use relative path for media playlist URL in HLS master playlist.
1371+
// When on, uses relative path (e.g., "livestream.m3u8?hls_ctx=xxx").
1372+
// When off, uses absolute path (e.g., "/live/livestream.m3u8?hls_ctx=xxx").
1373+
// Default is off for backward compatibility.
1374+
virtual bool get_hls_master_m3u8_path_relative(std::string vhost);
13691375
// Toggles HLS recover mode.
13701376
// In this mode HLS sequence number is started from where it stopped last time.
13711377
// Old fragments are kept. Default is on.

trunk/src/app/srs_app_http_static.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,21 @@ srs_error_t SrsHlsStream::serve_new_session(ISrsHttpResponseWriter *w, ISrsHttpM
178178
return srs_error_wrap(err, "HLS: http_hooks_on_play");
179179
}
180180

181+
// Determine the media playlist URL path in master playlist based on configuration.
182+
// When hls_master_m3u8_path_relative is on, use relative path (just filename) for better
183+
// compatibility with reverse proxies that do path rewriting.
184+
// For example, convert "/live/livestream.m3u8" to "livestream.m3u8"
185+
// When off (default), use absolute path for backward compatibility.
186+
std::string media_playlist_url = hr->path();
187+
if (_srs_config->get_hls_master_m3u8_path_relative(req->vhost_)) {
188+
SrsPath path;
189+
media_playlist_url = path.filepath_base(hr->path());
190+
}
191+
181192
std::stringstream ss;
182193
ss << "#EXTM3U" << SRS_CONSTS_LF;
183194
ss << "#EXT-X-STREAM-INF:BANDWIDTH=1,AVERAGE-BANDWIDTH=1" << SRS_CONSTS_LF;
184-
ss << hr->path() << "?" << SRS_CONTEXT_IN_HLS << "=" << ctx;
195+
ss << media_playlist_url << "?" << SRS_CONTEXT_IN_HLS << "=" << ctx;
185196
if (!hr->query().empty() && hr->query_get(SRS_CONTEXT_IN_HLS).empty()) {
186197
ss << "&" << hr->query();
187198
}

trunk/src/core/srs_core_version7.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99

1010
#define VERSION_MAJOR 7
1111
#define VERSION_MINOR 0
12-
#define VERSION_REVISION 103
12+
#define VERSION_REVISION 104
1313

1414
#endif

trunk/src/utest/srs_utest_manual_mock.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,11 @@ bool MockAppConfig::get_hls_ts_ctx_enabled(std::string vhost)
842842
return true;
843843
}
844844

845+
bool MockAppConfig::get_hls_master_m3u8_path_relative(std::string vhost)
846+
{
847+
return false;
848+
}
849+
845850
bool MockAppConfig::get_hls_recover(std::string vhost)
846851
{
847852
return true;

trunk/src/utest/srs_utest_manual_mock.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ class MockAppConfig : public ISrsAppConfig
456456
virtual bool get_vhost_hls_dts_directly(std::string vhost);
457457
virtual bool get_hls_ctx_enabled(std::string vhost);
458458
virtual bool get_hls_ts_ctx_enabled(std::string vhost);
459+
virtual bool get_hls_master_m3u8_path_relative(std::string vhost);
459460
virtual bool get_hls_recover(std::string vhost);
460461
virtual bool get_forward_enabled(std::string vhost);
461462
virtual SrsConfDirective *get_forwards(std::string vhost);

0 commit comments

Comments
 (0)