diff mbox series

[FFmpeg-devel,v4] avformat/hls: Implement support for using AVSEEK_FLAG_BACKWARD when seeking

Message ID 20220128202320.1711694-1-gustav.grusell@gmail.com
State New
Headers show
Series [FFmpeg-devel,v4] avformat/hls: Implement support for using AVSEEK_FLAG_BACKWARD when seeking | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/make_ppc success Make finished
andriy/make_fate_ppc success Make fate finished
andriy/make_aarch64_jetson success Make finished
andriy/make_fate_aarch64_jetson success Make fate finished
andriy/make_armv7_RPi4 success Make finished
andriy/make_fate_armv7_RPi4 success Make fate finished

Commit Message

Gustav Grusell Jan. 28, 2022, 8:23 p.m. UTC
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>
---
 libavformat/hls.c | 24 +++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

Comments

Gustav Grusell Feb. 2, 2022, 4:31 p.m. UTC | #1
Any comments on this?

cheers,
Gustav

On Fri, Jan 28, 2022 at 9:24 PM Gustav Grusell <gustav.grusell@gmail.com>
wrote:

> 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>
> ---
>  libavformat/hls.c | 24 +++++++++++++++++-------
>  1 file changed, 17 insertions(+), 7 deletions(-)
>
> diff --git a/libavformat/hls.c b/libavformat/hls.c
> index 4568e72cb2..44afdaab42 100644
> --- a/libavformat/hls.c
> +++ b/libavformat/hls.c
> @@ -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 not: returns 0 and sets seq_no to closest segment */
>  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;
>      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;
>          if (diff > 0) {
>              *seq_no = pls->start_seq_no + i;
> +            if (seg_start_ts) {
> +                *seg_start_ts = pos;
> +            }
>              return 1;
>          }
>          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
>       * by counting durations. */
>      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;
>      }
>
> @@ -2362,7 +2366,7 @@ static int hls_read_seek(AVFormatContext *s, int
> stream_index,
>      int i, j;
>      int stream_subdemuxer_index;
>      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))
>          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,
>
>  s->streams[stream_index]->time_base.den,
> -                                    flags & AVSEEK_FLAG_BACKWARD ?
> -                                    AV_ROUND_DOWN : AV_ROUND_UP);
> +                                    AV_ROUND_DOWN);
>
>      duration = s->duration == AV_NOPTS_VALUE ?
>                 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
>       * 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);
>
> +    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 */
>      seek_pls->cur_seq_no = seq_no;
>      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) {
>              /* 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
>               * keyframes into account since this playlist does not have
> the
>               * specified stream where we should look for the keyframes */
> --
> 2.25.1
>
>
Liu Steven Feb. 3, 2022, 10:20 a.m. UTC | #2
> 2022年1月29日 上午4:23,Gustav Grusell <gustav.grusell@gmail.com> 写道:
> 
> 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>
> ---
> libavformat/hls.c | 24 +++++++++++++++++-------
> 1 file changed, 17 insertions(+), 7 deletions(-)
> 
> diff --git a/libavformat/hls.c b/libavformat/hls.c
> index 4568e72cb2..44afdaab42 100644
> --- a/libavformat/hls.c
> +++ b/libavformat/hls.c
> @@ -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 not: returns 0 and sets seq_no to closest segment */
> 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;
>     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;
>         if (diff > 0) {
>             *seq_no = pls->start_seq_no + i;
> +            if (seg_start_ts) {
> +                *seg_start_ts = pos;
> +            }
>             return 1;
>         }
>         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
>      * by counting durations. */
>     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;
>     }
> 
> @@ -2362,7 +2366,7 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
>     int i, j;
>     int stream_subdemuxer_index;
>     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))
>         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,
>                                     s->streams[stream_index]->time_base.den,
> -                                    flags & AVSEEK_FLAG_BACKWARD ?
> -                                    AV_ROUND_DOWN : AV_ROUND_UP);
> +                                    AV_ROUND_DOWN);
> 
>     duration = s->duration == AV_NOPTS_VALUE ?
>                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
>      * 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);
> 
> +    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 */
>     seek_pls->cur_seq_no = seq_no;
>     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) {
>             /* 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
>              * keyframes into account since this playlist does not have the
>              * specified stream where we should look for the keyframes */
> -- 
> 2.25.1
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> 

LGTM

Thanks

Steven Liu
Steven Liu Feb. 9, 2022, 5:56 a.m. UTC | #3
Steven Liu <lq@chinaffmpeg.org> 于2022年2月3日周四 18:22写道:
>
>
>
> > 2022年1月29日 上午4:23,Gustav Grusell <gustav.grusell@gmail.com> 写道:
> >
> > 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>
> > ---
> > libavformat/hls.c | 24 +++++++++++++++++-------
> > 1 file changed, 17 insertions(+), 7 deletions(-)
> >
> > diff --git a/libavformat/hls.c b/libavformat/hls.c
> > index 4568e72cb2..44afdaab42 100644
> > --- a/libavformat/hls.c
> > +++ b/libavformat/hls.c
> > @@ -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 not: returns 0 and sets seq_no to closest segment */
> > 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;
> >     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;
> >         if (diff > 0) {
> >             *seq_no = pls->start_seq_no + i;
> > +            if (seg_start_ts) {
> > +                *seg_start_ts = pos;
> > +            }
> >             return 1;
> >         }
> >         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
> >      * by counting durations. */
> >     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;
> >     }
> >
> > @@ -2362,7 +2366,7 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
> >     int i, j;
> >     int stream_subdemuxer_index;
> >     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))
> >         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,
> >                                     s->streams[stream_index]->time_base.den,
> > -                                    flags & AVSEEK_FLAG_BACKWARD ?
> > -                                    AV_ROUND_DOWN : AV_ROUND_UP);
> > +                                    AV_ROUND_DOWN);
> >
> >     duration = s->duration == AV_NOPTS_VALUE ?
> >                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
> >      * 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);
> >
> > +    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 */
> >     seek_pls->cur_seq_no = seq_no;
> >     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) {
> >             /* 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
> >              * keyframes into account since this playlist does not have the
> >              * specified stream where we should look for the keyframes */
> > --
> > 2.25.1
> >
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> >
> > To unsubscribe, visit link above, or email
> > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
> >
>
> LGTM

Applied as e78d0810d1741535c95e5ae0f198977626b1cdff


Thanks
Steven
diff mbox series

Patch

diff --git a/libavformat/hls.c b/libavformat/hls.c
index 4568e72cb2..44afdaab42 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -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 not: returns 0 and sets seq_no to closest segment */
 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;
     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;
         if (diff > 0) {
             *seq_no = pls->start_seq_no + i;
+            if (seg_start_ts) {
+                *seg_start_ts = pos;
+            }
             return 1;
         }
         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
      * by counting durations. */
     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;
     }
 
@@ -2362,7 +2366,7 @@  static int hls_read_seek(AVFormatContext *s, int stream_index,
     int i, j;
     int stream_subdemuxer_index;
     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))
         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,
                                     s->streams[stream_index]->time_base.den,
-                                    flags & AVSEEK_FLAG_BACKWARD ?
-                                    AV_ROUND_DOWN : AV_ROUND_UP);
+                                    AV_ROUND_DOWN);
 
     duration = s->duration == AV_NOPTS_VALUE ?
                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
      * 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);
 
+    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 */
     seek_pls->cur_seq_no = seq_no;
     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) {
             /* 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
              * keyframes into account since this playlist does not have the
              * specified stream where we should look for the keyframes */