[FFmpeg-devel] [PATCH v4] Option to prevent probing for HTTP seekability
Duncan Salerno
duncan.salerno at gmail.com
Tue Oct 2 23:22:44 CEST 2012
Some HLS servers return 403 when the Range header is present. Add an tri-state (seek, non seek, automatic detection) option to HTTP to control seekability (default: automatic), set to disabled by HLS to prevent the Range header being sent to probe.
---
libavformat/hls.c | 35 ++++++++++++++++++++++++-----------
libavformat/http.c | 20 ++++++++++++++------
2 files changed, 38 insertions(+), 17 deletions(-)
diff --git a/libavformat/hls.c b/libavformat/hls.c
index 00c3cf0..1f5309b 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -212,9 +212,14 @@ static int parse_playlist(HLSContext *c, const char *url,
int close_in = 0;
if (!in) {
+ AVDictionary *opts = NULL;
close_in = 1;
- if ((ret = avio_open2(&in, url, AVIO_FLAG_READ,
- c->interrupt_callback, NULL)) < 0)
+ /* Some HLS servers dont like being sent the range header */
+ av_dict_set(&opts, "seekable", "0", 0);
+ ret = avio_open2(&in, url, AVIO_FLAG_READ,
+ c->interrupt_callback, &opts);
+ av_dict_free(&opts);
+ if (ret < 0)
return ret;
}
@@ -325,17 +330,20 @@ fail:
static int open_input(struct variant *var)
{
+ AVDictionary *opts = NULL;
+ int ret;
struct segment *seg = var->segments[var->cur_seq_no - var->start_seq_no];
+ av_dict_set(&opts, "seekable", "0", 0);
if (seg->key_type == KEY_NONE) {
- return ffurl_open(&var->input, seg->url, AVIO_FLAG_READ,
- &var->parent->interrupt_callback, NULL);
+ ret = ffurl_open(&var->input, seg->url, AVIO_FLAG_READ,
+ &var->parent->interrupt_callback, &opts);
+ goto cleanup;
} 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",
@@ -357,17 +365,22 @@ static int open_input(struct variant *var)
snprintf(url, sizeof(url), "crypto:%s", seg->url);
if ((ret = ffurl_alloc(&var->input, url, AVIO_FLAG_READ,
&var->parent->interrupt_callback)) < 0)
- return ret;
+ goto cleanup;
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;
- return ret;
+ goto cleanup;
}
- return 0;
+ ret = 0;
}
- return AVERROR(ENOSYS);
+ else
+ ret = AVERROR(ENOSYS);
+
+cleanup:
+ av_dict_free(&opts);
+ return ret;
}
static int read_data(void *opaque, uint8_t *buf, int buf_size)
diff --git a/libavformat/http.c b/libavformat/http.c
index ede4e8b..375f0a8 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 seekable; /**< Control seekability, 0 = disable, 1 = enable, -1 = probe. */
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[] = {
+{"seekable", "Control seekability of connection", OFFSET(seekable), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 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},
@@ -207,7 +209,10 @@ static int http_open(URLContext *h, const char *uri, int flags)
{
HTTPContext *s = h->priv_data;
- h->is_streamed = 1;
+ if( s->seekable == 1 )
+ h->is_streamed = 0;
+ else
+ h->is_streamed = 1;
s->filesize = -1;
av_strlcpy(s->location, uri, sizeof(s->location));
@@ -318,9 +323,9 @@ static int process_line(URLContext *h, char *line, int line_count,
if ((slash = strchr(p, '/')) && strlen(slash) > 0)
s->filesize = strtoll(slash+1, NULL, 10);
}
- if (!s->is_akamai || s->filesize != 2147483647)
+ if (s->seekable == -1 && (!s->is_akamai || s->filesize != 2147483647))
h->is_streamed = 0; /* we _can_ in fact seek */
- } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5)) {
+ } else if (!av_strcasecmp(tag, "Accept-Ranges") && !strncmp(p, "bytes", 5) && s->seekable == -1) {
h->is_streamed = 0;
} else if (!av_strcasecmp (tag, "Transfer-Encoding") && !av_strncasecmp(p, "chunked", 7)) {
s->filesize = -1;
@@ -411,10 +416,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 when we're probing,
// 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->seekable == -1))
len += av_strlcatf(headers + len, sizeof(headers) - len,
"Range: bytes=%"PRId64"-\r\n", s->off);
@@ -702,7 +707,10 @@ static int http_proxy_open(URLContext *h, const char *uri, int flags)
char *authstr;
int new_loc;
- h->is_streamed = 1;
+ if( s->seekable == 1 )
+ h->is_streamed = 0;
+ else
+ h->is_streamed = 1;
av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
pathbuf, sizeof(pathbuf), uri);
--
1.7.9.5
More information about the ffmpeg-devel
mailing list