Skip to content

Commit

Permalink
Solve the problem of inaccurate HLS TS duration
Browse files Browse the repository at this point in the history
1. The comment on the ratio configuration says it can affect the slice duration, but there is no effect after configuring it.
2. The default hls_td_ratio is 1.5, and after setting it to 1, the duration is still slightly more than 10 seconds.
3. Even if the GOP is an integer, like 1 second, the slice is still a non-integer, like 0.998 seconds, which seems a bit unreliable.
4. In the duration of the TS in the m3u8 file, it is one frame less than the duration of the slice.
  • Loading branch information
winlinvip committed Oct 7, 2023
1 parent a2a7132 commit 63af4ca
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 8 deletions.
4 changes: 2 additions & 2 deletions trunk/conf/full.conf
Original file line number Diff line number Diff line change
Expand Up @@ -1780,8 +1780,8 @@ 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,
Expand Down
2 changes: 1 addition & 1 deletion trunk/src/app/srs_app_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
27 changes: 22 additions & 5 deletions trunk/src/app/srs_app_hls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 collect 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,
Expand Down Expand Up @@ -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 collect 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()) {
Expand Down
4 changes: 4 additions & 0 deletions trunk/src/app/srs_app_hls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ class SrsHlsMuxer
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:
Expand Down

0 comments on commit 63af4ca

Please sign in to comment.