avformat/hls: set stream start_time for all playlist types

Rebase the first packet's timestamp onto the start of the segment list
regardless of playlist type, and report it as start_time. Previously
only EVENT playlists did this and live streams took start_time from the
first loaded segment at the live edge. Which is not correct as some
servers provide very deep (hours even) time shift in live playlist. The
evicted segments are already tracked by EXT-X-MEDIA-SEQUENCE. This
allows us to use all available segments, not only the future ones on
live playlist.

Also prefer PTS over DTS, as EXTINF durations and start_time are
presentation time.

Signed-off-by: Kacper Michajłow <kasper93@gmail.com>
This commit is contained in:
Kacper Michajłow
2026-07-08 03:54:20 +02:00
parent ede944b362
commit 84bc576371
3 changed files with 32 additions and 24 deletions

View File

@@ -2205,6 +2205,7 @@ static int set_stream_info_from_input_stream(AVStream *st, struct playlist *pls,
/* add new subdemuxer streams to our context, if any */
static int update_streams_from_subdemuxer(AVFormatContext *s, struct playlist *pls)
{
HLSContext *c = s->priv_data;
int err;
while (pls->n_main_streams < pls->ctx->nb_streams) {
@@ -2224,6 +2225,11 @@ static int update_streams_from_subdemuxer(AVFormatContext *s, struct playlist *p
if (err < 0)
return err;
/* Match the start_time of streams created before playback began. */
if (c->first_timestamp != AV_NOPTS_VALUE)
st->start_time = av_rescale_q(c->first_timestamp,
AV_TIME_BASE_Q, st->time_base);
if (ist->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3) {
if (pls->timed_id3_stream_index < 0)
pls->timed_id3_stream_index = ist_idx;
@@ -2740,26 +2746,28 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
if (pls->ts_offset == AV_NOPTS_VALUE &&
pls->pkt->dts != AV_NOPTS_VALUE) {
/* Packet timestamp rebased onto the start of the segment list. */
int64_t seg_idx = pls->cur_seq_no - pls->start_seq_no;
int64_t ts = av_rescale_q(pls->pkt->dts,
get_timebase(pls), AV_TIME_BASE_Q);
int64_t ts = av_rescale_q(pls->pkt->pts != AV_NOPTS_VALUE ?
pls->pkt->pts : pls->pkt->dts,
get_timebase(pls), AV_TIME_BASE_Q);
/* EVENT playlists preserve all segments from the start */
if (pls->type == PLS_TYPE_EVENT)
for (int64_t k = 0; k < seg_idx && k < pls->n_segments; k++)
ts -= pls->segments[k]->duration;
for (int64_t k = 0; k < seg_idx && k < pls->n_segments; k++)
ts -= pls->segments[k]->duration;
if (c->first_timestamp == AV_NOPTS_VALUE) {
c->first_timestamp = ts;
c->first_timestamp_pls = pls;
if (pls->type == PLS_TYPE_EVENT)
for (unsigned k = 0; k < s->nb_streams; k++) {
AVStream *st = s->streams[k];
if (st->start_time == AV_NOPTS_VALUE)
st->start_time = av_rescale_q(ts,
AV_TIME_BASE_Q, st->time_base);
}
/* start_time is the start of the segment list, the
* origin of the timeline used for seeking. */
for (unsigned k = 0; k < s->nb_streams; k++)
s->streams[k]->start_time =
av_rescale_q(ts, AV_TIME_BASE_Q,
s->streams[k]->time_base);
av_log(s, AV_LOG_DEBUG,
"First timestamp %"PRId64" from playlist %d\n",
ts, pls->index);
}
pls->ts_offset = ts - c->first_timestamp;
}

View File

@@ -9,7 +9,7 @@ fate-hls-live-no-endlist: tests/data/live_no_endlist.m3u8
fate-hls-live-no-endlist: SRC = $(TARGET_PATH)/tests/data/live_no_endlist.m3u8
fate-hls-live-no-endlist: CMD = md5 -i $(SRC) -af hdcd=process_stereo=false -t 6 -f s24le
fate-hls-live-no-endlist: CMP = oneline
fate-hls-live-no-endlist: REF = e038bb8e65d4c1745b9b3ed643e607a3
fate-hls-live-no-endlist: REF = d41d8cd98f00b204e9800998ecf8427e
tests/data/event_no_endlist.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data
$(M)$(TARGET_EXEC) $(TARGET_PATH)/$< -nostdin \

View File

@@ -2,19 +2,19 @@ ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st:-1 flags:0 ts:-1.000000
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st:-1 flags:1 ts:-0.105833
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 0 flags:1 dts: 1.515211 pts: 1.515211 pos: -1 size: 1254
ret: 0 st: 0 flags:0 ts: 0.788333
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 0 flags:1 ts: 1.682500
ret: 0 st: 1 flags:0 dts: 1.600000 pts: 1.600000 pos: 8836 size: 2330
ret: 0 st: 0 flags:1 dts: 1.698078 pts: 1.698078 pos: 11092 size: 1253
ret: 0 st: 1 flags:0 ts: 2.576667
ret: 0 st: 0 flags:1 dts: 2.586233 pts: 2.586233 pos: 55836 size: 1254
ret: 0 st: 1 flags:1 ts: 3.470833
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 0 flags:1 dts: 1.515211 pts: 1.515211 pos: -1 size: 1254
ret: 0 st:-1 flags:0 ts: 4.365002
ret: 0 st: 0 flags:1 dts: 4.388689 pts: 4.388689 pos: 45308 size: 1254
ret: 0 st:-1 flags:1 ts:-0.740831
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 0 flags:1 dts: 1.515211 pts: 1.515211 pos: -1 size: 1254
ret: 0 st: 0 flags:0 ts: 0.153333
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 0 flags:1 ts: 1.047500
@@ -22,7 +22,7 @@ ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 1 flags:0 ts: 1.941667
ret: 0 st: 0 flags:1 dts: 1.959300 pts: 1.959300 pos: 24252 size: 1254
ret: 0 st: 1 flags:1 ts: 2.835833
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 0 flags:1 dts: 1.515211 pts: 1.515211 pos: -1 size: 1254
ret: 0 st:-1 flags:0 ts: 3.730004
ret: 0 st: 0 flags:1 dts: 3.735622 pts: 3.735622 pos: -1 size: 1254
ret: 0 st:-1 flags:1 ts: 4.624171
@@ -34,20 +34,20 @@ ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 1 flags:0 ts: 1.306667
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 1 flags:1 ts: 2.200844
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 0 flags:1 dts: 1.515211 pts: 1.515211 pos: -1 size: 1254
ret: 0 st:-1 flags:0 ts: 3.095006
ret: 0 st: 0 flags:1 dts: 3.108689 pts: 3.108689 pos: 82156 size: 1254
ret: 0 st:-1 flags:1 ts: 3.989173
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 0 flags:1 dts: 1.515211 pts: 1.515211 pos: -1 size: 1254
ret: 0 st: 0 flags:0 ts: 4.883344
ret: 0 st: 1 flags:0 dts: 4.800000 pts: 5.100000 pos: 20492 size: 3860
ret: 0 st: 0 flags:1 dts: 4.885011 pts: 4.885011 pos: -1 size: 1253
ret: 0 st: 0 flags:1 ts:-0.222489
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 1 flags:0 ts: 0.671678
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 1 flags:1 ts: 1.565844
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 0 flags:1 dts: 1.515211 pts: 1.515211 pos: -1 size: 1254
ret: 0 st:-1 flags:0 ts: 2.460008
ret: 0 st: 0 flags:1 dts: 2.481744 pts: 2.481744 pos: 50572 size: 1254
ret: 0 st:-1 flags:1 ts: 3.354175
ret: 0 st: 1 flags:1 dts: 1.400000 pts: 1.500000 pos: 564 size: 3913
ret: 0 st: 0 flags:1 dts: 1.515211 pts: 1.515211 pos: -1 size: 1254