[FFmpeg-devel] avformat/http: handle "content-range: <a>-<b>/*" responses

Submitted by Aman Gupta on Dec. 13, 2017, 7:11 p.m.

Details

Message ID 20171213191108.41005-1-ffmpeg@tmm1.net
State New
Headers show

Commit Message

Aman Gupta Dec. 13, 2017, 7:11 p.m.
From: Aman Gupta <aman@tmm1.net>

The HTTP spec for the Content-Range response header specifies
that '*' may be used when the total size of the document is unknown.

Practically speaking, this can be used by a webserver to indicate
that the underlying video file is still growing, i.e. more video
data is being appended to the end.

With this commit, the http protocol parses the '*' and instead uses
the range end when reponding to AVSEEK_SIZE queries.
---
 libavformat/http.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

Comments

Aman Gupta Dec. 21, 2017, 2:02 a.m.
On Wed, Dec 13, 2017 at 11:11 AM, Aman Gupta <ffmpeg@tmm1.net> wrote:

> From: Aman Gupta <aman@tmm1.net>
>
> The HTTP spec for the Content-Range response header specifies
> that '*' may be used when the total size of the document is unknown.
>
> Practically speaking, this can be used by a webserver to indicate
> that the underlying video file is still growing, i.e. more video
> data is being appended to the end.
>
> With this commit, the http protocol parses the '*' and instead uses
> the range end when reponding to AVSEEK_SIZE queries.
>

Any thoughts on this patch Anssi?

Thanks,
Aman



> ---
>  libavformat/http.c | 28 +++++++++++++++++++++-------
>  1 file changed, 21 insertions(+), 7 deletions(-)
>
> diff --git a/libavformat/http.c b/libavformat/http.c
> index cf86adc617..d4b0d7c929 100644
> --- a/libavformat/http.c
> +++ b/libavformat/http.c
> @@ -67,7 +67,7 @@ typedef struct HTTPContext {
>      /* Used if "Transfer-Encoding: chunked" otherwise -1. */
>      uint64_t chunksize;
>      int chunkend;
> -    uint64_t off, end_off, filesize;
> +    uint64_t off, end_off, filesize, fileend;
>      char *location;
>      HTTPAuthState auth_state;
>      HTTPAuthState proxy_auth_state;
> @@ -501,6 +501,7 @@ static int http_open(URLContext *h, const char *uri,
> int flags,
>          h->is_streamed = 1;
>
>      s->filesize = UINT64_MAX;
> +    s->fileend = UINT64_MAX;
>      s->location = av_strdup(uri);
>      if (!s->location)
>          return AVERROR(ENOMEM);
> @@ -624,12 +625,21 @@ static void parse_content_range(URLContext *h, const
> char *p)
>  {
>      HTTPContext *s = h->priv_data;
>      const char *slash;
> +    const char *dash;
>
>      if (!strncmp(p, "bytes ", 6)) {
> +        uint64_t range_end = UINT64_MAX;
>          p     += 6;
>          s->off = strtoull(p, NULL, 10);
> -        if ((slash = strchr(p, '/')) && strlen(slash) > 0)
> -            s->filesize = strtoull(slash + 1, NULL, 10);
> +        if ((dash = strchr(p, '-')))
> +            range_end = strtoull(dash + 1, NULL, 10);
> +        if ((slash = strchr(p, '/')) && strlen(slash) > 0) {
> +            if (slash[1] == '*') {
> +                s->fileend = range_end;
> +            } else {
> +                s->filesize = strtoull(slash + 1, NULL, 10);
> +            }
> +        }
>      }
>      if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647))
>          h->is_streamed = 0; /* we _can_ in fact seek */
> @@ -1253,6 +1263,7 @@ static int http_connect(URLContext *h, const char
> *path, const char *local_path,
>      s->off              = 0;
>      s->icy_data_read    = 0;
>      s->filesize         = UINT64_MAX;
> +    s->fileend          = UINT64_MAX;
>      s->willclose        = 0;
>      s->end_chunked_post = 0;
>      s->end_header       = 0;
> @@ -1601,19 +1612,22 @@ static int64_t http_seek_internal(URLContext *h,
> int64_t off, int whence, int fo
>      int old_buf_size, ret;
>      AVDictionary *options = NULL;
>
> -    if (whence == AVSEEK_SIZE)
> +    if (whence == AVSEEK_SIZE) {
> +        if (s->fileend != UINT64_MAX) {
> +            return s->off > s->fileend ? s->off : s->fileend;
> +        }
>          return s->filesize;
> -    else if (!force_reconnect &&
> +    } else if (!force_reconnect &&
>               ((whence == SEEK_CUR && off == 0) ||
>                (whence == SEEK_SET && off == s->off)))
>          return s->off;
> -    else if ((s->filesize == UINT64_MAX && whence == SEEK_END))
> +    else if ((s->filesize == UINT64_MAX && whence == SEEK_END &&
> s->fileend == UINT64_MAX))
>          return AVERROR(ENOSYS);
>
>      if (whence == SEEK_CUR)
>          off += s->off;
>      else if (whence == SEEK_END)
> -        off += s->filesize;
> +        off += s->fileend != UINT64_MAX ? s->fileend : s->filesize;
>      else if (whence != SEEK_SET)
>          return AVERROR(EINVAL);
>      if (off < 0)
> --
> 2.14.3 (Apple Git-98)
>
>

Patch hide | download patch | download mbox

diff --git a/libavformat/http.c b/libavformat/http.c
index cf86adc617..d4b0d7c929 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -67,7 +67,7 @@  typedef struct HTTPContext {
     /* Used if "Transfer-Encoding: chunked" otherwise -1. */
     uint64_t chunksize;
     int chunkend;
-    uint64_t off, end_off, filesize;
+    uint64_t off, end_off, filesize, fileend;
     char *location;
     HTTPAuthState auth_state;
     HTTPAuthState proxy_auth_state;
@@ -501,6 +501,7 @@  static int http_open(URLContext *h, const char *uri, int flags,
         h->is_streamed = 1;
 
     s->filesize = UINT64_MAX;
+    s->fileend = UINT64_MAX;
     s->location = av_strdup(uri);
     if (!s->location)
         return AVERROR(ENOMEM);
@@ -624,12 +625,21 @@  static void parse_content_range(URLContext *h, const char *p)
 {
     HTTPContext *s = h->priv_data;
     const char *slash;
+    const char *dash;
 
     if (!strncmp(p, "bytes ", 6)) {
+        uint64_t range_end = UINT64_MAX;
         p     += 6;
         s->off = strtoull(p, NULL, 10);
-        if ((slash = strchr(p, '/')) && strlen(slash) > 0)
-            s->filesize = strtoull(slash + 1, NULL, 10);
+        if ((dash = strchr(p, '-')))
+            range_end = strtoull(dash + 1, NULL, 10);
+        if ((slash = strchr(p, '/')) && strlen(slash) > 0) {
+            if (slash[1] == '*') {
+                s->fileend = range_end;
+            } else {
+                s->filesize = strtoull(slash + 1, NULL, 10);
+            }
+        }
     }
     if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647))
         h->is_streamed = 0; /* we _can_ in fact seek */
@@ -1253,6 +1263,7 @@  static int http_connect(URLContext *h, const char *path, const char *local_path,
     s->off              = 0;
     s->icy_data_read    = 0;
     s->filesize         = UINT64_MAX;
+    s->fileend          = UINT64_MAX;
     s->willclose        = 0;
     s->end_chunked_post = 0;
     s->end_header       = 0;
@@ -1601,19 +1612,22 @@  static int64_t http_seek_internal(URLContext *h, int64_t off, int whence, int fo
     int old_buf_size, ret;
     AVDictionary *options = NULL;
 
-    if (whence == AVSEEK_SIZE)
+    if (whence == AVSEEK_SIZE) {
+        if (s->fileend != UINT64_MAX) {
+            return s->off > s->fileend ? s->off : s->fileend;
+        }
         return s->filesize;
-    else if (!force_reconnect &&
+    } else if (!force_reconnect &&
              ((whence == SEEK_CUR && off == 0) ||
               (whence == SEEK_SET && off == s->off)))
         return s->off;
-    else if ((s->filesize == UINT64_MAX && whence == SEEK_END))
+    else if ((s->filesize == UINT64_MAX && whence == SEEK_END && s->fileend == UINT64_MAX))
         return AVERROR(ENOSYS);
 
     if (whence == SEEK_CUR)
         off += s->off;
     else if (whence == SEEK_END)
-        off += s->filesize;
+        off += s->fileend != UINT64_MAX ? s->fileend : s->filesize;
     else if (whence != SEEK_SET)
         return AVERROR(EINVAL);
     if (off < 0)