X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fhttp.c;h=948930a053d765cf462a39d265ff7df06d93f8ae;hb=25b6837f7cacd691b19cbc12b9dad1ce84a318a1;hp=41b1f1ba200b6ef2c927ea94e094b74b27183f3a;hpb=8075c3d8bb1f6aade0cc7c5c40db9bc1bcd84cab;p=ffmpeg diff --git a/libavformat/http.c b/libavformat/http.c index 41b1f1ba200..948930a053d 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -51,11 +51,14 @@ typedef struct { int http_code; /* Used if "Transfer-Encoding: chunked" otherwise -1. */ int64_t chunksize; - int64_t off, filesize; + int64_t off, end_off, filesize; char *location; HTTPAuthState auth_state; HTTPAuthState proxy_auth_state; char *headers; + char *mime_type; + char *user_agent; + char *content_type; /* Set if the server correctly handles Connection: close and will close * the connection after feeding us the content. */ int willclose; @@ -87,11 +90,16 @@ typedef struct { #define OFFSET(x) offsetof(HTTPContext, x) #define D AV_OPT_FLAG_DECODING_PARAM #define E AV_OPT_FLAG_ENCODING_PARAM +#define DEFAULT_USER_AGENT "Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION) static const AVOption options[] = { {"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 }, +{"headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, +{"content_type", "set a specific content type for the POST messages", OFFSET(content_type), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, +{"user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = DEFAULT_USER_AGENT}, 0, 0, D }, +{"user-agent", "override User-Agent header, for compatibility with ffmpeg", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = DEFAULT_USER_AGENT}, 0, 0, D }, {"multiple_requests", "use persistent connections", OFFSET(multiple_requests), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D|E }, -{"post_data", "custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E }, +{"post_data", "set custom HTTP post data", OFFSET(post_data), AV_OPT_TYPE_BINARY, .flags = D|E }, +{"mime_type", "export the MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, {0}, 0, 0, AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY }, {"icy", "request ICY metadata", OFFSET(icy), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D }, {"icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), AV_OPT_TYPE_STRING, {0}, 0, 0, AV_OPT_FLAG_EXPORT }, {"icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, {0}, 0, 0, AV_OPT_FLAG_EXPORT }, @@ -100,6 +108,8 @@ static const AVOption options[] = { {"basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_BASIC}, 0, 0, D|E, "auth_type" }, {"send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E }, {"location", "The actual location of the data received", OFFSET(location), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, +{"offset", "initial byte offset", OFFSET(off), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, D }, +{"end_offset", "try to limit the request to bytes preceding this offset", OFFSET(end_off), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, D }, {NULL} }; #define HTTP_CLASS(flavor)\ @@ -343,7 +353,7 @@ static int parse_location(HTTPContext *s, const char *p) } /* "bytes $from-$to/$document_size" */ -static void parse_content_range(URLContext *h, char *p) +static void parse_content_range(URLContext *h, const char *p) { HTTPContext *s = h->priv_data; const char *slash; @@ -357,7 +367,7 @@ static void parse_content_range(URLContext *h, char *p) h->is_streamed = 0; /* we _can_ in fact seek */ } -static int parse_content_encoding(URLContext *h, char *p) +static int parse_content_encoding(URLContext *h, const char *p) { HTTPContext *s = h->priv_data; @@ -395,6 +405,7 @@ static int parse_content_encoding(URLContext *h, char *p) static int parse_icy(HTTPContext *s, const char *tag, const char *p) { int len = 4 + strlen(p) + strlen(tag); + int is_first = !s->icy_metadata_headers; int ret; if (s->icy_metadata_headers) @@ -403,6 +414,9 @@ static int parse_icy(HTTPContext *s, const char *tag, const char *p) if ((ret = av_reallocp(&s->icy_metadata_headers, len)) < 0) return ret; + if (is_first) + *s->icy_metadata_headers = '\0'; + av_strlcatf(s->icy_metadata_headers, len, "%s: %s\n", tag, p); return 0; @@ -468,6 +482,9 @@ static int process_line(URLContext *h, char *line, int line_count, } else if (!av_strcasecmp(tag, "Connection")) { if (!strcmp(p, "close")) s->willclose = 1; + } else if (!av_strcasecmp (tag, "Content-Type")) { + av_free(s->mime_type); + s->mime_type = av_strdup(p); } else if (!av_strcasecmp (tag, "Icy-MetaInt")) { s->icy_metaint = strtoll(p, NULL, 10); } else if (!av_strncasecmp(tag, "Icy-", 4)) { @@ -557,14 +574,23 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, /* set default headers if needed */ if (!has_header(s->headers, "\r\nUser-Agent: ")) - len += av_strlcatf(headers + len, sizeof(headers) - len, - "User-Agent: %s\r\n", LIBAVFORMAT_IDENT); + len += av_strlcatf(headers + len, sizeof(headers) - len, + "User-Agent: %s\r\n", s->user_agent); if (!has_header(s->headers, "\r\nAccept: ")) len += av_strlcpy(headers + len, "Accept: */*\r\n", sizeof(headers) - len); - if (!has_header(s->headers, "\r\nRange: ") && !post) + // 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) { len += av_strlcatf(headers + len, sizeof(headers) - len, - "Range: bytes=%"PRId64"-\r\n", s->off); + "Range: bytes=%"PRId64"-", s->off); + if (s->end_off) + len += av_strlcatf(headers + len, sizeof(headers) - len, + "%"PRId64, s->end_off - 1); + len += av_strlcpy(headers + len, "\r\n", + sizeof(headers) - len); + } if (send_expect_100 && !has_header(s->headers, "\r\nExpect: ")) len += av_strlcatf(headers + len, sizeof(headers) - len, "Expect: 100-continue\r\n"); @@ -585,6 +611,10 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, if (!has_header(s->headers, "\r\nContent-Length: ") && s->post_data) len += av_strlcatf(headers + len, sizeof(headers) - len, "Content-Length: %d\r\n", s->post_datalen); + + if (!has_header(s->headers, "\r\nContent-Type: ") && s->content_type) + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Content-Type: %s\r\n", s->content_type); if (!has_header(s->headers, "\r\nIcy-MetaData: ") && s->icy) { len += av_strlcatf(headers + len, sizeof(headers) - len, "Icy-MetaData: %d\r\n", 1); @@ -881,6 +911,9 @@ static int64_t http_seek(URLContext *h, int64_t off, int whence) if (whence == AVSEEK_SIZE) return s->filesize; + else if ((whence == SEEK_CUR && off == 0) || + (whence == SEEK_SET && off == s->off)) + return s->off; else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed) return AVERROR(ENOSYS);