[FFmpeg-devel] [PATCH] Option to prevent probing for HTTP seekability

Clément Bœsch ubitux at gmail.com
Wed Sep 26 07:47:33 CEST 2012


On Tue, Sep 25, 2012 at 09:38:09PM +0100, Duncan Salerno wrote:
> Some HLS servers return 403 when the Range header is present. Add an option to HTTP to control sending the range header (default on). HLS sets this option to disabled.
> ---
>  libavformat/hls.c  |   27 +++++++++++++++++++++------
>  libavformat/http.c |    6 ++++--
>  2 files changed, 25 insertions(+), 8 deletions(-)
> 
> diff --git a/libavformat/hls.c b/libavformat/hls.c
> index 00c3cf0..3bd29bc 100644
> --- a/libavformat/hls.c
> +++ b/libavformat/hls.c
> @@ -212,10 +212,16 @@ static int parse_playlist(HLSContext *c, const char *url,
>      int close_in = 0;
>  
>      if (!in) {
> +        AVDictionary *opts = NULL;
> +        /* Some HLS servers dont like being sent the range header */
> +        av_dict_set(&opts, "probe_seek", "0", 0);
>          close_in = 1;
>          if ((ret = avio_open2(&in, url, AVIO_FLAG_READ,
> -                              c->interrupt_callback, NULL)) < 0)
> +                              c->interrupt_callback, &opts)) < 0) {
> +            av_dict_free(&opts);
>              return ret;
> +        }
> +        av_dict_free(&opts);

It looks like you could use the fail label instead here and put the
av_dict_free in that place. Also, since the code seems a bit weird, you
might need to move close_in after avio_open2 succeed.

>      }
>  
>      read_chomp_line(in, line, sizeof(line));
> @@ -325,17 +331,21 @@ fail:
>  
>  static int open_input(struct variant *var)
>  {
> +    AVDictionary *opts = NULL;
>      struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no];
> +    av_dict_set(&opts, "probe_seek", "0", 0);
>      if (seg->key_type == KEY_NONE) {
> -        return ffurl_open(&var->input, seg->url, AVIO_FLAG_READ,
> -                          &var->parent->interrupt_callback, NULL);
> +        int ret = ffurl_open(&var->input, seg->url, AVIO_FLAG_READ,
> +                             &var->parent->interrupt_callback, &opts);
> +        av_dict_free(&opts);
> +        return ret;
>      } else if (seg->key_type == KEY_AES_128) {
>          char iv[33], key[33], url[MAX_URL_SIZE];
>          int ret;
>          if (strcmp(seg->key, var->key_url)) {
>              URLContext *uc;
>              if (ffurl_open(&uc, seg->key, AVIO_FLAG_READ,
> -                           &var->parent->interrupt_callback, NULL) == 0) {
> +                           &var->parent->interrupt_callback, &opts) == 0) {
>                  if (ffurl_read_complete(uc, var->key, sizeof(var->key))
>                      != sizeof(var->key)) {
>                      av_log(NULL, AV_LOG_ERROR, "Unable to read key file %s\n",
> @@ -356,17 +366,22 @@ static int open_input(struct variant *var)
>          else
>              snprintf(url, sizeof(url), "crypto:%s", seg->url);
>          if ((ret = ffurl_alloc(&var->input, url, AVIO_FLAG_READ,
> -                               &var->parent->interrupt_callback)) < 0)
> +                               &var->parent->interrupt_callback)) < 0) {
> +            av_dict_free(&opts);
>              return ret;
> +        }
>          av_opt_set(var->input->priv_data, "key", key, 0);
>          av_opt_set(var->input->priv_data, "iv", iv, 0);
> -        if ((ret = ffurl_connect(var->input, NULL)) < 0) {
> +        if ((ret = ffurl_connect(var->input, &opts)) < 0) {
>              ffurl_close(var->input);
>              var->input = NULL;
> +            av_dict_free(&opts);
>              return ret;
>          }
> +        av_dict_free(&opts);
>          return 0;
>      }
> +    av_dict_free(&opts);
>      return AVERROR(ENOSYS);

I'm pretty sure you can get a simpler code with only one av_dict_free()
call instead of five by using the same model (ret value and fail label).

>  }
>  
> diff --git a/libavformat/http.c b/libavformat/http.c
> index ede4e8b..6d47882 100644
> --- a/libavformat/http.c
> +++ b/libavformat/http.c
> @@ -50,6 +50,7 @@ typedef struct {
>      HTTPAuthState proxy_auth_state;
>      char *headers;
>      int willclose;          /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */
> +    int probe_seek;

Please add a doxy.

>      int chunked_post;
>      int end_chunked_post;   /**< A flag which indicates if the end of chunked encoding has been sent. */
>      int end_header;         /**< A flag which indicates we have finished to read POST reply. */
> @@ -64,6 +65,7 @@ typedef struct {
>  #define E AV_OPT_FLAG_ENCODING_PARAM
>  #define DEC AV_OPT_FLAG_DECODING_PARAM
>  static const AVOption options[] = {
> +{"probe_seek", "probe if server supports seeking by sending range header", OFFSET(probe_seek), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, D },
>  {"chunked_post", "use chunked transfer-encoding for posts", OFFSET(chunked_post), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, E },
>  {"headers", "custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E },
>  {"user-agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC},
> @@ -411,10 +413,10 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
>      if (!has_header(s->headers, "\r\nAccept: "))
>          len += av_strlcpy(headers + len, "Accept: */*\r\n",
>                            sizeof(headers) - len);
> -    // Note: we send this on purpose even when s->off is 0,
> +    // Note: we send this on purpose even when s->off is 0 and probe_seek set,
>      // since it allows us to detect more reliably if a (non-conforming)
>      // server supports seeking by analysing the reply headers.
> -    if (!has_header(s->headers, "\r\nRange: ") && !post)
> +    if (!has_header(s->headers, "\r\nRange: ") && !post && (s->off > 0 || s->probe_seek))
>          len += av_strlcatf(headers + len, sizeof(headers) - len,
>                             "Range: bytes=%"PRId64"-\r\n", s->off);

BTW, can't you hack is_streamed instead? We used it for special case, but
maybe would make sense as an option. Maybe a negative value for a "forced"
option (ignoring server settings).

-- 
Clément B.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20120926/5cd3f2d0/attachment.asc>


More information about the ffmpeg-devel mailing list