diff mbox series

[FFmpeg-devel,01/10] avformat/hls: fix repeated requests for media init section

Message ID tencent_CC8C262F5097A6EA27D52AA3531D1C57CB06@qq.com
State New
Headers show
Series [FFmpeg-devel,01/10] avformat/hls: fix repeated requests for media init section | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Zhao Zhili April 12, 2022, 8:15 a.m. UTC
---
 libavformat/hls.c | 59 ++++++++++++++++++++++++++++++++---------------
 1 file changed, 40 insertions(+), 19 deletions(-)

Comments

Jun Zhao April 12, 2022, 11:45 a.m. UTC | #1
On Tue, Apr 12, 2022 at 4:15 PM Zhao Zhili <quinkblack@foxmail.com> wrote:
>
> ---
>  libavformat/hls.c | 59 ++++++++++++++++++++++++++++++++---------------
>  1 file changed, 40 insertions(+), 19 deletions(-)
>
> diff --git a/libavformat/hls.c b/libavformat/hls.c
> index 83ff4cc607..67c9650e0b 100644
> --- a/libavformat/hls.c
> +++ b/libavformat/hls.c
> @@ -407,48 +407,65 @@ struct init_section_info {
>      char byterange[32];
>  };
>
> -static struct segment *new_init_section(struct playlist *pls,
> +/*
> + * Return a new init section if info is new, otherwise return an entry
> + * from pls->init_sections.
> + */
> +static struct segment *get_init_section(struct playlist *pls,
>                                          struct init_section_info *info,
> -                                        const char *url_base)
> +                                        const char *url_base,
> +                                        int *new_init)
>  {
> -    struct segment *sec;
> +    struct segment *sec_ptr = NULL;
> +    struct segment sec = {};
>      char tmp_str[MAX_URL_SIZE], *ptr = tmp_str;
>
> +    *new_init = 1;
>      if (!info->uri[0])
>          return NULL;
>
> -    sec = av_mallocz(sizeof(*sec));
> -    if (!sec)
> -        return NULL;
> -
>      if (!av_strncasecmp(info->uri, "data:", 5)) {
>          ptr = info->uri;
>      } else {
>          ff_make_absolute_url(tmp_str, sizeof(tmp_str), url_base, info->uri);
>          if (!tmp_str[0]) {
> -            av_free(sec);
>              return NULL;
>          }
>      }
> -    sec->url = av_strdup(ptr);
> -    if (!sec->url) {
> -        av_free(sec);
> -        return NULL;
> -    }
> +    // Don't dup until make sure it's a new initialization section
> +    sec.url = ptr;
>
>      if (info->byterange[0]) {
> -        sec->size = strtoll(info->byterange, NULL, 10);
> +        sec.size = strtoll(info->byterange, NULL, 10);
>          ptr = strchr(info->byterange, '@');
>          if (ptr)
> -            sec->url_offset = strtoll(ptr+1, NULL, 10);
> +            sec.url_offset = strtoll(ptr+1, NULL, 10);
>      } else {
>          /* the entire file is the init section */
> -        sec->size = -1;
> +        sec.size = -1;
>      }
>
> -    dynarray_add(&pls->init_sections, &pls->n_init_sections, sec);
> +    for (int i = 0; i < pls->n_init_sections; i++) {
> +        sec_ptr = pls->init_sections[i];
> +        if (!strcmp(sec_ptr->url, sec.url) &&
> +            sec_ptr->size == sec.size &&
> +            sec_ptr->url_offset == sec.url_offset) {
> +            *new_init = 0;
> +            return sec_ptr;
> +        }
> +    }
> +
> +    sec_ptr = av_malloc(sizeof(*sec_ptr));
> +    if (!sec_ptr) return NULL;
> +    *sec_ptr = sec;
> +    sec_ptr->url = av_strdup(ptr);
> +    if (!sec_ptr->url) {
> +        av_free(sec_ptr);
> +        return NULL;
> +    }
> +    dynarray_add(&pls->init_sections, &pls->n_init_sections, sec_ptr);
>
> -    return sec;
> +    return sec_ptr;
>  }
>
>  static void handle_init_section_args(struct init_section_info *info, const char *key,
> @@ -851,13 +868,17 @@ static int parse_playlist(HLSContext *c, const char *url,
>              else if (!strcmp(ptr, "VOD"))
>                  pls->type = PLS_TYPE_VOD;
>          } else if (av_strstart(line, "#EXT-X-MAP:", &ptr)) {
> +            int new_init = 1;
>              struct init_section_info info = {{0}};
>              ret = ensure_playlist(c, &pls, url);
>              if (ret < 0)
>                  goto fail;
>              ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_init_section_args,
>                                 &info);
> -            cur_init_section = new_init_section(pls, &info, url);
> +            cur_init_section = get_init_section(pls, &info, url, &new_init);
> +            /* Skip if it's the same init section */
> +            if (!new_init)
> +                continue;
>              if (!cur_init_section) {
>                  ret = AVERROR(ENOMEM);
>                  goto fail;
> --
> 2.31.1
Can these patches handle multiple instances of the media init sections?
Zhao Zhili April 12, 2022, 1:26 p.m. UTC | #2
> On Apr 12, 2022, at 7:45 PM, mypopy@gmail.com wrote:
> 
> On Tue, Apr 12, 2022 at 4:15 PM Zhao Zhili <quinkblack@foxmail.com> wrote:
>> 
>> ---
>> libavformat/hls.c | 59 ++++++++++++++++++++++++++++++++---------------
>> 1 file changed, 40 insertions(+), 19 deletions(-)
>> 
> Can these patches handle multiple instances of the media init sections?

Before and after the patch, struct playlist do take multiple media
initialization sections into consideration. However, I think it doesn’t
work for HLS as a whole. To support multiple media init sections, it
needs to handle EXT-X-DISCONTINUITY and recreate mp4 demuxer.

So even if new media init section is show up, it will be downloaded and
then dropped by mp4 muxer:
```
    if (c->found_moov) {
        av_log(c->fc, AV_LOG_WARNING, "Found duplicated MOOV Atom. Skipped it\n");
        avio_skip(pb, atom.size);
        return 0;
    }
```

What the patch does is to avoid downloading the same init sections again
and again, and then dropped by mp4 muxer. It’s common to have init section
repeat in live mode.

Just for reference, hls.js had the same issue, and have been fixed by

https://github.com/video-dev/hls.js/pull/4452

> _______________________________________________
> 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".
Zhao Zhili April 12, 2022, 1:29 p.m. UTC | #3
> On Apr 12, 2022, at 9:26 PM, zhilizhao(赵志立) <quinkblack@foxmail.com> wrote:
> 
> 
> 
>> On Apr 12, 2022, at 7:45 PM, mypopy@gmail.com wrote:
>> 
>> On Tue, Apr 12, 2022 at 4:15 PM Zhao Zhili <quinkblack@foxmail.com> wrote:
>>> 
>>> ---
>>> libavformat/hls.c | 59 ++++++++++++++++++++++++++++++++---------------
>>> 1 file changed, 40 insertions(+), 19 deletions(-)
>>> 
>> Can these patches handle multiple instances of the media init sections?
> 
> Before and after the patch, struct playlist do take multiple media
> initialization sections into consideration. However, I think it doesn’t
> work for HLS as a whole. To support multiple media init sections, it
> needs to handle EXT-X-DISCONTINUITY and recreate mp4 demuxer.
> 
> So even if new media init section is show up, it will be downloaded and
> then dropped by mp4 muxer:

I mean mp4 demuxer.

> ```
>    if (c->found_moov) {
>        av_log(c->fc, AV_LOG_WARNING, "Found duplicated MOOV Atom. Skipped it\n");
>        avio_skip(pb, atom.size);
>        return 0;
>    }
> ```
> 
> What the patch does is to avoid downloading the same init sections again
> and again, and then dropped by mp4 muxer. It’s common to have init section
> repeat in live mode.

Ditto.

> 
> Just for reference, hls.js had the same issue, and have been fixed by
> 
> https://github.com/video-dev/hls.js/pull/4452
> 
>> _______________________________________________
>> 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".
> 
> _______________________________________________
> 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".
diff mbox series

Patch

diff --git a/libavformat/hls.c b/libavformat/hls.c
index 83ff4cc607..67c9650e0b 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -407,48 +407,65 @@  struct init_section_info {
     char byterange[32];
 };
 
-static struct segment *new_init_section(struct playlist *pls,
+/*
+ * Return a new init section if info is new, otherwise return an entry
+ * from pls->init_sections.
+ */
+static struct segment *get_init_section(struct playlist *pls,
                                         struct init_section_info *info,
-                                        const char *url_base)
+                                        const char *url_base,
+                                        int *new_init)
 {
-    struct segment *sec;
+    struct segment *sec_ptr = NULL;
+    struct segment sec = {};
     char tmp_str[MAX_URL_SIZE], *ptr = tmp_str;
 
+    *new_init = 1;
     if (!info->uri[0])
         return NULL;
 
-    sec = av_mallocz(sizeof(*sec));
-    if (!sec)
-        return NULL;
-
     if (!av_strncasecmp(info->uri, "data:", 5)) {
         ptr = info->uri;
     } else {
         ff_make_absolute_url(tmp_str, sizeof(tmp_str), url_base, info->uri);
         if (!tmp_str[0]) {
-            av_free(sec);
             return NULL;
         }
     }
-    sec->url = av_strdup(ptr);
-    if (!sec->url) {
-        av_free(sec);
-        return NULL;
-    }
+    // Don't dup until make sure it's a new initialization section
+    sec.url = ptr;
 
     if (info->byterange[0]) {
-        sec->size = strtoll(info->byterange, NULL, 10);
+        sec.size = strtoll(info->byterange, NULL, 10);
         ptr = strchr(info->byterange, '@');
         if (ptr)
-            sec->url_offset = strtoll(ptr+1, NULL, 10);
+            sec.url_offset = strtoll(ptr+1, NULL, 10);
     } else {
         /* the entire file is the init section */
-        sec->size = -1;
+        sec.size = -1;
     }
 
-    dynarray_add(&pls->init_sections, &pls->n_init_sections, sec);
+    for (int i = 0; i < pls->n_init_sections; i++) {
+        sec_ptr = pls->init_sections[i];
+        if (!strcmp(sec_ptr->url, sec.url) &&
+            sec_ptr->size == sec.size &&
+            sec_ptr->url_offset == sec.url_offset) {
+            *new_init = 0;
+            return sec_ptr;
+        }
+    }
+
+    sec_ptr = av_malloc(sizeof(*sec_ptr));
+    if (!sec_ptr) return NULL;
+    *sec_ptr = sec;
+    sec_ptr->url = av_strdup(ptr);
+    if (!sec_ptr->url) {
+        av_free(sec_ptr);
+        return NULL;
+    }
+    dynarray_add(&pls->init_sections, &pls->n_init_sections, sec_ptr);
 
-    return sec;
+    return sec_ptr;
 }
 
 static void handle_init_section_args(struct init_section_info *info, const char *key,
@@ -851,13 +868,17 @@  static int parse_playlist(HLSContext *c, const char *url,
             else if (!strcmp(ptr, "VOD"))
                 pls->type = PLS_TYPE_VOD;
         } else if (av_strstart(line, "#EXT-X-MAP:", &ptr)) {
+            int new_init = 1;
             struct init_section_info info = {{0}};
             ret = ensure_playlist(c, &pls, url);
             if (ret < 0)
                 goto fail;
             ff_parse_key_value(ptr, (ff_parse_key_val_cb) handle_init_section_args,
                                &info);
-            cur_init_section = new_init_section(pls, &info, url);
+            cur_init_section = get_init_section(pls, &info, url, &new_init);
+            /* Skip if it's the same init section */
+            if (!new_init)
+                continue;
             if (!cur_init_section) {
                 ret = AVERROR(ENOMEM);
                 goto fail;