You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-15 14:13:16 +02:00
avformat/hls: Implement support for using AVSEEK_FLAG_BACKWARD when seeking
Before, seeking in hls streams would always seek to the next keyframe after the given timestamp. With this fix, if seeking in videostream and AVSEEK_FLAG_BACKWARD is set, seeking will be to the first keyframe of the segment containing the given timestamp. This fixes #7485. Signed-off-by: Gustav Grusell <gustav.grusell@gmail.com>
This commit is contained in:
committed by
Steven Liu
parent
2727ff069e
commit
e78d0810d1
@@ -1653,7 +1653,8 @@ static void add_metadata_from_renditions(AVFormatContext *s, struct playlist *pl
|
|||||||
/* if timestamp was in valid range: returns 1 and sets seq_no
|
/* if timestamp was in valid range: returns 1 and sets seq_no
|
||||||
* if not: returns 0 and sets seq_no to closest segment */
|
* if not: returns 0 and sets seq_no to closest segment */
|
||||||
static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls,
|
static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls,
|
||||||
int64_t timestamp, int64_t *seq_no)
|
int64_t timestamp, int64_t *seq_no,
|
||||||
|
int64_t *seg_start_ts)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ?
|
int64_t pos = c->first_timestamp == AV_NOPTS_VALUE ?
|
||||||
@@ -1668,6 +1669,9 @@ static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls,
|
|||||||
int64_t diff = pos + pls->segments[i]->duration - timestamp;
|
int64_t diff = pos + pls->segments[i]->duration - timestamp;
|
||||||
if (diff > 0) {
|
if (diff > 0) {
|
||||||
*seq_no = pls->start_seq_no + i;
|
*seq_no = pls->start_seq_no + i;
|
||||||
|
if (seg_start_ts) {
|
||||||
|
*seg_start_ts = pos;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
pos += pls->segments[i]->duration;
|
pos += pls->segments[i]->duration;
|
||||||
@@ -1691,7 +1695,7 @@ static int64_t select_cur_seq_no(HLSContext *c, struct playlist *pls)
|
|||||||
* playlist) and this is a complete file, find the matching segment
|
* playlist) and this is a complete file, find the matching segment
|
||||||
* by counting durations. */
|
* by counting durations. */
|
||||||
if (pls->finished && c->cur_timestamp != AV_NOPTS_VALUE) {
|
if (pls->finished && c->cur_timestamp != AV_NOPTS_VALUE) {
|
||||||
find_timestamp_in_playlist(c, pls, c->cur_timestamp, &seq_no);
|
find_timestamp_in_playlist(c, pls, c->cur_timestamp, &seq_no, NULL);
|
||||||
return seq_no;
|
return seq_no;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2362,7 +2366,7 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
|
|||||||
int i, j;
|
int i, j;
|
||||||
int stream_subdemuxer_index;
|
int stream_subdemuxer_index;
|
||||||
int64_t first_timestamp, seek_timestamp, duration;
|
int64_t first_timestamp, seek_timestamp, duration;
|
||||||
int64_t seq_no;
|
int64_t seq_no, seg_start_ts;
|
||||||
|
|
||||||
if ((flags & AVSEEK_FLAG_BYTE) || (c->ctx->ctx_flags & AVFMTCTX_UNSEEKABLE))
|
if ((flags & AVSEEK_FLAG_BYTE) || (c->ctx->ctx_flags & AVFMTCTX_UNSEEKABLE))
|
||||||
return AVERROR(ENOSYS);
|
return AVERROR(ENOSYS);
|
||||||
@@ -2372,8 +2376,7 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
|
|||||||
|
|
||||||
seek_timestamp = av_rescale_rnd(timestamp, AV_TIME_BASE,
|
seek_timestamp = av_rescale_rnd(timestamp, AV_TIME_BASE,
|
||||||
s->streams[stream_index]->time_base.den,
|
s->streams[stream_index]->time_base.den,
|
||||||
flags & AVSEEK_FLAG_BACKWARD ?
|
AV_ROUND_DOWN);
|
||||||
AV_ROUND_DOWN : AV_ROUND_UP);
|
|
||||||
|
|
||||||
duration = s->duration == AV_NOPTS_VALUE ?
|
duration = s->duration == AV_NOPTS_VALUE ?
|
||||||
0 : s->duration;
|
0 : s->duration;
|
||||||
@@ -2394,9 +2397,16 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
|
|||||||
}
|
}
|
||||||
/* check if the timestamp is valid for the playlist with the
|
/* check if the timestamp is valid for the playlist with the
|
||||||
* specified stream index */
|
* specified stream index */
|
||||||
if (!seek_pls || !find_timestamp_in_playlist(c, seek_pls, seek_timestamp, &seq_no))
|
if (!seek_pls || !find_timestamp_in_playlist(c, seek_pls, seek_timestamp, &seq_no, &seg_start_ts))
|
||||||
return AVERROR(EIO);
|
return AVERROR(EIO);
|
||||||
|
|
||||||
|
if (s->streams[stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
|
||||||
|
flags & AVSEEK_FLAG_BACKWARD && !(flags & AVSEEK_FLAG_ANY)) {
|
||||||
|
/* Seeking to start of segment ensures we seek to a keyframe located
|
||||||
|
* before the given timestamp. */
|
||||||
|
seek_timestamp = seg_start_ts;
|
||||||
|
}
|
||||||
|
|
||||||
/* set segment now so we do not need to search again below */
|
/* set segment now so we do not need to search again below */
|
||||||
seek_pls->cur_seq_no = seq_no;
|
seek_pls->cur_seq_no = seq_no;
|
||||||
seek_pls->seek_stream_index = stream_subdemuxer_index;
|
seek_pls->seek_stream_index = stream_subdemuxer_index;
|
||||||
@@ -2423,7 +2433,7 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
|
|||||||
|
|
||||||
if (pls != seek_pls) {
|
if (pls != seek_pls) {
|
||||||
/* set closest segment seq_no for playlists not handled above */
|
/* set closest segment seq_no for playlists not handled above */
|
||||||
find_timestamp_in_playlist(c, pls, seek_timestamp, &pls->cur_seq_no);
|
find_timestamp_in_playlist(c, pls, seek_timestamp, &pls->cur_seq_no, NULL);
|
||||||
/* seek the playlist to the given position without taking
|
/* seek the playlist to the given position without taking
|
||||||
* keyframes into account since this playlist does not have the
|
* keyframes into account since this playlist does not have the
|
||||||
* specified stream where we should look for the keyframes */
|
* specified stream where we should look for the keyframes */
|
||||||
|
Reference in New Issue
Block a user