diff --git a/Dockerfile b/Dockerfile
index e0f0434aa..63d3783f5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -57,5 +57,5 @@ RUN ldd /usr/local/srs/objs/ffmpeg/bin/ffmpeg && \
# Default workdir and command.
WORKDIR /usr/local/srs
ENV SRS_DAEMON=off
-CMD ["./objs/srs", "-c", "conf/srs.conf"]
+CMD ["./objs/srs", "-c", "conf/docker.conf"]
diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf
index cfd4259c6..32cbe6ff6 100644
--- a/trunk/conf/full.conf
+++ b/trunk/conf/full.conf
@@ -1751,27 +1751,6 @@ vhost hls.srs.com {
# default: off
enabled on;
- # Whether enable hls_ctx for HLS streaming, for which we create a "fake" connection for HTTP API and callback.
- # For each HLS streaming session, we use a child m3u8 with a session identified by query "hls_ctx", it simply
- # work as the session id.
- # Once the HLS streaming session is created, we will cleanup it when timeout in 2*hls_window seconds. So it
- # takes a long time period to identify the timeout.
- # Now we got a HLS stremaing session, just like RTMP/WebRTC/HTTP-FLV streaming, we're able to stat the session
- # as a "fake" connection, do HTTP callback when start playing the HLS streaming. You're able to do querying and
- # authentication.
- # Note that it will make NGINX edge cache always missed, so never enable HLS streaming if use NGINX edges.
- # Overwrite by env SRS_VHOST_HLS_HLS_CTX for all vhosts.
- # Default: on
- hls_ctx on;
- # For HLS pseudo streaming, whether enable the session for each TS segment.
- # If enabled, SRS HTTP API will show the statistics about HLS streaming bandwidth, both m3u8 and ts file. Please
- # note that it also consumes resource, because each ts file should be served by SRS, all NGINX cache will be
- # missed because we add session id to each ts file.
- # Note that it will make NGINX edge cache always missed, so never enable HLS streaming if use NGINX edges.
- # Overwrite by env SRS_VHOST_HLS_HLS_TS_CTX for all vhosts.
- # Default: on
- hls_ts_ctx on;
-
# the hls fragment in seconds, the duration of a piece of ts.
# Overwrite by env SRS_VHOST_HLS_HLS_FRAGMENT for all vhosts.
# default: 10
@@ -1780,15 +1759,15 @@ vhost hls.srs.com {
# EXT-X-TARGETDURATION = hls_td_ratio * hls_fragment // init
# EXT-X-TARGETDURATION = max(ts_duration, EXT-X-TARGETDURATION) // for each ts
# Overwrite by env SRS_VHOST_HLS_HLS_TD_RATIO for all vhosts.
- # default: 1.5
- hls_td_ratio 1.5;
+ # default: 1.0
+ hls_td_ratio 1.0;
# the audio overflow ratio.
# for pure audio, the duration to reap the segment.
- # for example, the hls_fragment is 10s, hls_aof_ratio is 2.0,
- # the segment will reap to 20s for pure audio.
+ # for example, the hls_fragment is 10s, hls_aof_ratio is 1.2,
+ # the segment will reap to 12s for pure audio.
# Overwrite by env SRS_VHOST_HLS_HLS_AOF_RATIO for all vhosts.
- # default: 2.0
- hls_aof_ratio 2.0;
+ # default: 1.2
+ hls_aof_ratio 1.2;
# the hls window in seconds, the number of ts in m3u8.
# Overwrite by env SRS_VHOST_HLS_HLS_WINDOW for all vhosts.
# default: 60
@@ -1837,13 +1816,6 @@ vhost hls.srs.com {
# Overwrite by env SRS_VHOST_HLS_HLS_TS_FILE for all vhosts.
# default: [app]/[stream]-[seq].ts
hls_ts_file [app]/[stream]-[seq].ts;
- # whether use floor for the hls_ts_file path generation.
- # if on, use floor(timestamp/hls_fragment) as the variable [timestamp],
- # and use enhanced algorithm to calc deviation for segment.
- # @remark when floor on, recommend the hls_segment>=2*gop.
- # Overwrite by env SRS_VHOST_HLS_HLS_TS_FLOOR for all vhosts.
- # default: off
- hls_ts_floor off;
# the hls entry prefix, which is base url of ts url.
# for example, the prefix is:
# http://your-server/
@@ -1879,20 +1851,48 @@ vhost hls.srs.com {
# @remark 0 to disable dispose for publisher.
# @remark apply for publisher timeout only, while "etc/init.d/srs stop" always dispose hls.
# Overwrite by env SRS_VHOST_HLS_HLS_DISPOSE for all vhosts.
- # default: 0
- hls_dispose 0;
- # the max size to notify hls,
- # to read max bytes from ts of specified cdn network,
- # @remark only used when on_hls_notify is config.
- # Overwrite by env SRS_VHOST_HLS_HLS_NB_NOTIFY for all vhosts.
- # default: 64
- hls_nb_notify 64;
+ # default: 120
+ hls_dispose 120;
# whether wait keyframe to reap segment,
# if off, reap segment when duration exceed the fragment,
# if on, reap segment when duration exceed and got keyframe.
# Overwrite by env SRS_VHOST_HLS_HLS_WAIT_KEYFRAME for all vhosts.
# default: on
hls_wait_keyframe on;
+ # whether use floor for the hls_ts_file path generation.
+ # if on, use floor(timestamp/hls_fragment) as the variable [timestamp],
+ # and use enhanced algorithm to calc deviation for segment.
+ # @remark when floor on, recommend the hls_segment>=2*gop.
+ # Overwrite by env SRS_VHOST_HLS_HLS_TS_FLOOR for all vhosts.
+ # default: off
+ hls_ts_floor off;
+ # the max size to notify hls,
+ # to read max bytes from ts of specified cdn network,
+ # @remark only used when on_hls_notify is config.
+ # Overwrite by env SRS_VHOST_HLS_HLS_NB_NOTIFY for all vhosts.
+ # default: 64
+ hls_nb_notify 64;
+
+ # Whether enable hls_ctx for HLS streaming, for which we create a "fake" connection for HTTP API and callback.
+ # For each HLS streaming session, we use a child m3u8 with a session identified by query "hls_ctx", it simply
+ # work as the session id.
+ # Once the HLS streaming session is created, we will cleanup it when timeout in 2*hls_window seconds. So it
+ # takes a long time period to identify the timeout.
+ # Now we got a HLS stremaing session, just like RTMP/WebRTC/HTTP-FLV streaming, we're able to stat the session
+ # as a "fake" connection, do HTTP callback when start playing the HLS streaming. You're able to do querying and
+ # authentication.
+ # Note that it will make NGINX edge cache always missed, so never enable HLS streaming if use NGINX edges.
+ # Overwrite by env SRS_VHOST_HLS_HLS_CTX for all vhosts.
+ # Default: on
+ hls_ctx on;
+ # For HLS pseudo streaming, whether enable the session for each TS segment.
+ # If enabled, SRS HTTP API will show the statistics about HLS streaming bandwidth, both m3u8 and ts file. Please
+ # note that it also consumes resource, because each ts file should be served by SRS, all NGINX cache will be
+ # missed because we add session id to each ts file.
+ # Note that it will make NGINX edge cache always missed, so never enable HLS streaming if use NGINX edges.
+ # Overwrite by env SRS_VHOST_HLS_HLS_TS_CTX for all vhosts.
+ # Default: on
+ hls_ts_ctx on;
# whether using AES encryption.
# Overwrite by env SRS_VHOST_HLS_HLS_KEYS for all vhosts.
diff --git a/trunk/conf/hls.origin.conf b/trunk/conf/hls.origin.conf
index aa7bc23b8..fd4b95c9b 100644
--- a/trunk/conf/hls.origin.conf
+++ b/trunk/conf/hls.origin.conf
@@ -12,6 +12,7 @@ vhost __defaultVhost__ {
enabled on;
# Note that it will make NGINX edge cache always missed, so never enable HLS streaming if use NGINX edges.
hls_ctx off;
+ hls_ts_ctx off;
}
}
diff --git a/trunk/conf/hls.realtime.conf b/trunk/conf/hls.realtime.conf
index 11dac8e14..843284eb7 100644
--- a/trunk/conf/hls.realtime.conf
+++ b/trunk/conf/hls.realtime.conf
@@ -14,8 +14,7 @@ http_server {
vhost __defaultVhost__ {
hls {
enabled on;
- hls_fragment 0.2;
- hls_window 2;
- hls_wait_keyframe off;
+ hls_fragment 2;
+ hls_window 10;
}
}
diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md
index 790376e0f..6410321d9 100644
--- a/trunk/doc/CHANGELOG.md
+++ b/trunk/doc/CHANGELOG.md
@@ -7,6 +7,7 @@ The changelog for SRS.
## SRS 6.0 Changelog
+* v6.0, 2023-10-08, Merge [#3824](https://github.com/ossrs/srs/pull/3824): Solve the problem of inaccurate HLS TS duration. v6.0.87 (#3824)
* v6.0, 2023-10-08, Merge [#3815](https://github.com/ossrs/srs/pull/3815): Use new cache image name. v6.0.86 (#3815)
* v6.0, 2023-09-26, Merge [#3811](https://github.com/ossrs/srs/pull/3811): Turn off the related utests H265 option. v6.0.85 (#3811)
* v6.0, 2023-09-25, Merge [#3810](https://github.com/ossrs/srs/pull/3810): Change dev code for John. v6.0.84 (#3810)
@@ -77,7 +78,7 @@ The changelog for SRS.
* v6.0, 2023-01-19, Merge [#3318](https://github.com/ossrs/srs/pull/3318): RTC: fix rtc publisher pli cid. v6.0.19 (#3318)
* v6.0, 2023-01-18, Merge [#3382](https://github.com/ossrs/srs/pull/3382): Rewrite research/api-server code by Go, remove Python. v6.0.18 (#3382)
* v6.0, 2023-01-18, Merge [#3386](https://github.com/ossrs/srs/pull/3386): SRT: fix crash when srt_to_rtmp off. v6.0.17 (#3386)
-* v5.0, 2023-01-17, Merge [#3385](https://github.com/ossrs/srs/pull/3385): API: Support server/pid/service label for exporter and api. v6.0.16 (#3385)
+* v6.0, 2023-01-17, Merge [#3385](https://github.com/ossrs/srs/pull/3385): API: Support server/pid/service label for exporter and api. v6.0.16 (#3385)
* v6.0, 2023-01-17, Merge [#3379](https://github.com/ossrs/srs/pull/3379): H265: Support demux vps/pps info. v6.0.15
* v6.0, 2023-01-08, Merge [#3360](https://github.com/ossrs/srs/pull/3360): H265: Support DVR HEVC stream to MP4. v6.0.14
* v6.0, 2023-01-06, Merge [#3363](https://github.com/ossrs/srs/issues/3363): HTTP: Add CORS Header for private network access. v6.0.13
@@ -98,6 +99,8 @@ The changelog for SRS.
## SRS 5.0 Changelog
+* v5.0, 2023-10-08, Merge [#3824](https://github.com/ossrs/srs/pull/3824): Solve the problem of inaccurate HLS TS duration. v5.0.187 (#3824)
+* v5.0, 2023-10-08, Merge [#3815](https://github.com/ossrs/srs/pull/3815): Use new cache image name. v5.0.186 (#3815)
* v5.0, 2023-09-28, Merge [#3816](https://github.com/ossrs/srs/pull/3816): cherry-pick from develop, for srt utest. v5.0.185 (#3816)
* v5.0, 2023-09-21, Merge [#3806](https://github.com/ossrs/srs/pull/3806): Build: Support sys-ssl for srt. v5.0.184 (#3806)
* v5.0, 2023-09-21, Merge [#3808](https://github.com/ossrs/srs/pull/3808): Upgrade libsrt to v1.5.3. v5.0.183 (#3808)
diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp
index 176f7f8fd..4dd8e4f68 100644
--- a/trunk/src/app/srs_app_config.cpp
+++ b/trunk/src/app/srs_app_config.cpp
@@ -6973,7 +6973,7 @@ double SrsConfig::get_hls_td_ratio(string vhost)
{
SRS_OVERWRITE_BY_ENV_FLOAT("srs.vhost.hls.hls_td_ratio"); // SRS_VHOST_HLS_HLS_TD_RATIO
- static double DEFAULT = 1.5;
+ static double DEFAULT = 1.0;
SrsConfDirective* conf = get_hls(vhost);
if (!conf) {
@@ -6992,7 +6992,7 @@ double SrsConfig::get_hls_aof_ratio(string vhost)
{
SRS_OVERWRITE_BY_ENV_FLOAT("srs.vhost.hls.hls_aof_ratio"); // SRS_VHOST_HLS_HLS_AOF_RATIO
- static double DEFAULT = 2.0;
+ static double DEFAULT = 1.2;
SrsConfDirective* conf = get_hls(vhost);
if (!conf) {
@@ -7183,7 +7183,7 @@ srs_utime_t SrsConfig::get_hls_dispose(string vhost)
{
SRS_OVERWRITE_BY_ENV_SECONDS("srs.vhost.hls.hls_dispose"); // SRS_VHOST_HLS_HLS_DISPOSE
- static srs_utime_t DEFAULT = 0;
+ static srs_utime_t DEFAULT = 120 * SRS_UTIME_SECONDS;
SrsConfDirective* conf = get_hls(vhost);
if (!conf) {
diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp
index 15ec9cd45..fff731eb7 100644
--- a/trunk/src/app/srs_app_hls.cpp
+++ b/trunk/src/app/srs_app_hls.cpp
@@ -542,9 +542,12 @@ bool SrsHlsMuxer::is_segment_overflow()
return false;
}
- // use N% deviation, to smoother.
+ // Use N% deviation, to smoother.
srs_utime_t deviation = hls_ts_floor? SRS_HLS_FLOOR_REAP_PERCENT * deviation_ts * hls_fragment : 0;
- return current->duration() >= hls_fragment + deviation;
+
+ // Keep in mind that we use max_td for the base duration, not the hls_fragment. To calculate
+ // max_td, multiply hls_fragment by hls_td_ratio.
+ return current->duration() >= max_td + deviation;
}
bool SrsHlsMuxer::wait_keyframe()
@@ -586,8 +589,8 @@ srs_error_t SrsHlsMuxer::flush_audio(SrsTsMessageCache* cache)
}
// update the duration of segment.
- current->append(cache->audio->pts / 90);
-
+ update_duration(cache->audio->dts);
+
if ((err = current->tscw->write_audio(cache->audio)) != srs_success) {
return srs_error_wrap(err, "hls: write audio");
}
@@ -615,7 +618,7 @@ srs_error_t SrsHlsMuxer::flush_video(SrsTsMessageCache* cache)
srs_assert(current);
// update the duration of segment.
- current->append(cache->video->dts / 90);
+ update_duration(cache->video->dts);
if ((err = current->tscw->write_video(cache->video)) != srs_success) {
return srs_error_wrap(err, "hls: write video");
@@ -627,6 +630,11 @@ srs_error_t SrsHlsMuxer::flush_video(SrsTsMessageCache* cache)
return err;
}
+void SrsHlsMuxer::update_duration(uint64_t dts)
+{
+ current->append(dts / 90);
+}
+
srs_error_t SrsHlsMuxer::segment_close()
{
srs_error_t err = do_segment_close();
@@ -657,9 +665,9 @@ srs_error_t SrsHlsMuxer::do_segment_close()
// valid, add to segments if segment duration is ok
// when too small, it maybe not enough data to play.
// when too large, it maybe timestamp corrupt.
- // make the segment more acceptable, when in [min, max_td * 2], it's ok.
+ // make the segment more acceptable, when in [min, max_td * 3], it's ok.
bool matchMinDuration = current->duration() >= SRS_HLS_SEGMENT_MIN_DURATION;
- bool matchMaxDuration = current->duration() <= max_td * 2 * 1000;
+ bool matchMaxDuration = current->duration() <= max_td * 3 * 1000;
if (matchMinDuration && matchMaxDuration) {
// rename from tmp to real path
if ((err = current->rename()) != srs_success) {
@@ -920,8 +928,9 @@ srs_error_t SrsHlsController::on_publish(SrsRequest* req)
std::string vhost = req->vhost;
std::string stream = req->stream;
std::string app = req->app;
-
+
srs_utime_t hls_fragment = _srs_config->get_hls_fragment(vhost);
+ double hls_td_ratio = _srs_config->get_hls_td_ratio(vhost);
srs_utime_t hls_window = _srs_config->get_hls_window(vhost);
// get the hls m3u8 ts list entry prefix config
@@ -965,9 +974,9 @@ srs_error_t SrsHlsController::on_publish(SrsRequest* req)
// This config item is used in SrsHls, we just log its value here.
bool hls_dts_directly = _srs_config->get_vhost_hls_dts_directly(req->vhost);
- srs_trace("hls: win=%dms, frag=%dms, prefix=%s, path=%s, m3u8=%s, ts=%s, aof=%.2f, floor=%d, clean=%d, waitk=%d, dispose=%dms, dts_directly=%d",
+ srs_trace("hls: win=%dms, frag=%dms, prefix=%s, path=%s, m3u8=%s, ts=%s, tdr=%.2f, aof=%.2f, floor=%d, clean=%d, waitk=%d, dispose=%dms, dts_directly=%d",
srsu2msi(hls_window), srsu2msi(hls_fragment), entry_prefix.c_str(), path.c_str(), m3u8_file.c_str(), ts_file.c_str(),
- hls_aof_ratio, ts_floor, cleanup, wait_keyframe, srsu2msi(hls_dispose), hls_dts_directly);
+ hls_td_ratio, hls_aof_ratio, ts_floor, cleanup, wait_keyframe, srsu2msi(hls_dispose), hls_dts_directly);
return err;
}
@@ -1017,6 +1026,10 @@ srs_error_t SrsHlsController::write_audio(SrsAudioFrame* frame, int64_t pts)
if ((err = tsmc->cache_audio(frame, pts)) != srs_success) {
return srs_error_wrap(err, "hls: cache audio");
}
+
+ // First, update the duration of the segment, as we might reap the segment. The duration should
+ // cover from the first frame to the last frame.
+ muxer->update_duration(tsmc->audio->dts);
// reap when current source is pure audio.
// it maybe changed when stream info changed,
@@ -1064,6 +1077,10 @@ srs_error_t SrsHlsController::write_video(SrsVideoFrame* frame, int64_t dts)
if ((err = tsmc->cache_video(frame, dts)) != srs_success) {
return srs_error_wrap(err, "hls: cache video");
}
+
+ // First, update the duration of the segment, as we might reap the segment. The duration should
+ // cover from the first frame to the last frame.
+ muxer->update_duration(tsmc->video->dts);
// when segment overflow, reap if possible.
if (muxer->is_segment_overflow()) {
diff --git a/trunk/src/app/srs_app_hls.hpp b/trunk/src/app/srs_app_hls.hpp
index e111b68b6..8b74f48db 100644
--- a/trunk/src/app/srs_app_hls.hpp
+++ b/trunk/src/app/srs_app_hls.hpp
@@ -204,6 +204,10 @@ public:
virtual bool pure_audio();
virtual srs_error_t flush_audio(SrsTsMessageCache* cache);
virtual srs_error_t flush_video(SrsTsMessageCache* cache);
+ // When flushing video or audio, we update the duration. But, we should also update the
+ // duration before closing the segment. Keep in mind that it's fine to update the duration
+ // several times using the same dts timestamp.
+ void update_duration(uint64_t dts);
// Close segment(ts).
virtual srs_error_t segment_close();
private:
diff --git a/trunk/src/core/srs_core_version5.hpp b/trunk/src/core/srs_core_version5.hpp
index 3d6467ecb..6f5942ec2 100644
--- a/trunk/src/core/srs_core_version5.hpp
+++ b/trunk/src/core/srs_core_version5.hpp
@@ -9,6 +9,6 @@
#define VERSION_MAJOR 5
#define VERSION_MINOR 0
-#define VERSION_REVISION 184
+#define VERSION_REVISION 187
#endif
diff --git a/trunk/src/core/srs_core_version6.hpp b/trunk/src/core/srs_core_version6.hpp
index 55575bd89..c5dce36a0 100644
--- a/trunk/src/core/srs_core_version6.hpp
+++ b/trunk/src/core/srs_core_version6.hpp
@@ -9,6 +9,6 @@
#define VERSION_MAJOR 6
#define VERSION_MINOR 0
-#define VERSION_REVISION 86
+#define VERSION_REVISION 87
#endif
diff --git a/trunk/src/utest/srs_utest_config.cpp b/trunk/src/utest/srs_utest_config.cpp
index 88431c4f3..40fb3d714 100644
--- a/trunk/src/utest/srs_utest_config.cpp
+++ b/trunk/src/utest/srs_utest_config.cpp
@@ -2085,7 +2085,7 @@ VOID TEST(ConfigUnitTest, CheckDefaultValuesVhost)
if (true) {
HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF));
- EXPECT_EQ(0, (int)conf.get_hls_dispose(""));
+ EXPECT_EQ(120 * SRS_UTIME_SECONDS, (int)conf.get_hls_dispose(""));
EXPECT_EQ(10 * SRS_UTIME_SECONDS, conf.get_hls_fragment(""));
EXPECT_EQ(60 * SRS_UTIME_SECONDS, conf.get_hls_window(""));