Skip to content
Snippets Groups Projects
Commit fb2b4fc5 authored by Ronald S. Bultje's avatar Ronald S. Bultje Committed by Guillaume Poirier
Browse files

add support for HTTP seeking

patch by Ronald S. Bultje % rbultje A ronald P bitfreak P net %
Original thread:
Date: Jan 4, 2007 5:42 AM
Subject: [Ffmpeg-devel] [PATCH/RFC] http seeking

Originally committed as revision 7505 to svn://svn.ffmpeg.org/ffmpeg/trunk
parent 597e3232
No related branches found
No related tags found
No related merge requests found
...@@ -40,40 +40,34 @@ ...@@ -40,40 +40,34 @@
/* used for protocol handling */ /* used for protocol handling */
#define BUFFER_SIZE 1024 #define BUFFER_SIZE 1024
#define URL_SIZE 4096 #define URL_SIZE 4096
#define MAX_REDIRECTS 8
typedef struct { typedef struct {
URLContext *hd; URLContext *hd;
unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end; unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
int line_count; int line_count;
int http_code; int http_code;
offset_t off, filesize;
char location[URL_SIZE]; char location[URL_SIZE];
} HTTPContext; } HTTPContext;
static int http_connect(URLContext *h, const char *path, const char *hoststr, static int http_connect(URLContext *h, const char *path, const char *hoststr,
const char *auth); const char *auth, int *new_location);
static int http_write(URLContext *h, uint8_t *buf, int size); static int http_write(URLContext *h, uint8_t *buf, int size);
/* return non zero if error */ /* return non zero if error */
static int http_open(URLContext *h, const char *uri, int flags) static int http_open_cnx(URLContext *h)
{ {
const char *path, *proxy_path; const char *path, *proxy_path;
char hostname[1024], hoststr[1024]; char hostname[1024], hoststr[1024];
char auth[1024]; char auth[1024];
char path1[1024]; char path1[1024];
char buf[1024]; char buf[1024];
int port, use_proxy, err; int port, use_proxy, err, location_changed = 0, redirects = 0;
HTTPContext *s; HTTPContext *s = h->priv_data;
URLContext *hd = NULL; URLContext *hd = NULL;
h->is_streamed = 1;
s = av_malloc(sizeof(HTTPContext));
if (!s) {
return -ENOMEM;
}
h->priv_data = s;
proxy_path = getenv("http_proxy"); proxy_path = getenv("http_proxy");
use_proxy = (proxy_path != NULL) && !getenv("no_proxy") && use_proxy = (proxy_path != NULL) && !getenv("no_proxy") &&
strstart(proxy_path, "http://", NULL); strstart(proxy_path, "http://", NULL);
...@@ -82,7 +76,7 @@ static int http_open(URLContext *h, const char *uri, int flags) ...@@ -82,7 +76,7 @@ static int http_open(URLContext *h, const char *uri, int flags)
redo: redo:
/* needed in any case to build the host string */ /* needed in any case to build the host string */
url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port, url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
path1, sizeof(path1), uri); path1, sizeof(path1), s->location);
if (port > 0) { if (port > 0) {
snprintf(hoststr, sizeof(hoststr), "%s:%d", hostname, port); snprintf(hoststr, sizeof(hoststr), "%s:%d", hostname, port);
} else { } else {
...@@ -92,7 +86,7 @@ static int http_open(URLContext *h, const char *uri, int flags) ...@@ -92,7 +86,7 @@ static int http_open(URLContext *h, const char *uri, int flags)
if (use_proxy) { if (use_proxy) {
url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port, url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname), &port,
NULL, 0, proxy_path); NULL, 0, proxy_path);
path = uri; path = s->location;
} else { } else {
if (path1[0] == '\0') if (path1[0] == '\0')
path = "/"; path = "/";
...@@ -108,22 +102,44 @@ static int http_open(URLContext *h, const char *uri, int flags) ...@@ -108,22 +102,44 @@ static int http_open(URLContext *h, const char *uri, int flags)
goto fail; goto fail;
s->hd = hd; s->hd = hd;
if (http_connect(h, path, hoststr, auth) < 0) if (http_connect(h, path, hoststr, auth, &location_changed) < 0)
goto fail; goto fail;
if (s->http_code == 303 && s->location[0] != '\0') { if (s->http_code == 303 && location_changed == 1) {
/* url moved, get next */ /* url moved, get next */
uri = s->location;
url_close(hd); url_close(hd);
if (redirects++ >= MAX_REDIRECTS)
return AVERROR_IO;
location_changed = 0;
goto redo; goto redo;
} }
return 0; return 0;
fail: fail:
if (hd) if (hd)
url_close(hd); url_close(hd);
av_free(s);
return AVERROR_IO; return AVERROR_IO;
} }
static int http_open(URLContext *h, const char *uri, int flags)
{
HTTPContext *s;
int ret;
h->is_streamed = 1;
s = av_malloc(sizeof(HTTPContext));
if (!s) {
return -ENOMEM;
}
h->priv_data = s;
s->filesize = -1;
s->off = 0;
pstrcpy (s->location, URL_SIZE, uri);
ret = http_open_cnx(h);
if (ret != 0)
av_free (s);
return ret;
}
static int http_getc(HTTPContext *s) static int http_getc(HTTPContext *s)
{ {
int len; int len;
...@@ -141,8 +157,10 @@ static int http_getc(HTTPContext *s) ...@@ -141,8 +157,10 @@ static int http_getc(HTTPContext *s)
return *s->buf_ptr++; return *s->buf_ptr++;
} }
static int process_line(HTTPContext *s, char *line, int line_count) static int process_line(URLContext *h, char *line, int line_count,
int *new_location)
{ {
HTTPContext *s = h->priv_data;
char *tag, *p; char *tag, *p;
/* end of header */ /* end of header */
...@@ -172,18 +190,32 @@ static int process_line(HTTPContext *s, char *line, int line_count) ...@@ -172,18 +190,32 @@ static int process_line(HTTPContext *s, char *line, int line_count)
p++; p++;
if (!strcmp(tag, "Location")) { if (!strcmp(tag, "Location")) {
strcpy(s->location, p); strcpy(s->location, p);
*new_location = 1;
} else if (!strcmp (tag, "Content-Length") && s->filesize == -1) {
s->filesize = atoll(p);
} else if (!strcmp (tag, "Content-Range")) {
/* "bytes $from-$to/$document_size" */
const char *slash;
if (!strncmp (p, "bytes ", 6)) {
p += 6;
s->off = atoll(p);
if ((slash = strchr(p, '/')) && strlen(slash) > 0)
s->filesize = atoll(slash+1);
}
h->is_streamed = 0; /* we _can_ in fact seek */
} }
} }
return 1; return 1;
} }
static int http_connect(URLContext *h, const char *path, const char *hoststr, static int http_connect(URLContext *h, const char *path, const char *hoststr,
const char *auth) const char *auth, int *new_location)
{ {
HTTPContext *s = h->priv_data; HTTPContext *s = h->priv_data;
int post, err, ch; int post, err, ch;
char line[1024], *q; char line[1024], *q;
char *auth_b64; char *auth_b64;
offset_t off = s->off;
/* send http header */ /* send http header */
...@@ -191,15 +223,17 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr, ...@@ -191,15 +223,17 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr,
auth_b64 = av_base64_encode((uint8_t *)auth, strlen(auth)); auth_b64 = av_base64_encode((uint8_t *)auth, strlen(auth));
snprintf(s->buffer, sizeof(s->buffer), snprintf(s->buffer, sizeof(s->buffer),
"%s %s HTTP/1.0\r\n" "%s %s HTTP/1.1\r\n"
"User-Agent: %s\r\n" "User-Agent: %s\r\n"
"Accept: */*\r\n" "Accept: */*\r\n"
"Range: bytes=%llu-\r\n"
"Host: %s\r\n" "Host: %s\r\n"
"Authorization: Basic %s\r\n" "Authorization: Basic %s\r\n"
"\r\n", "\r\n",
post ? "POST" : "GET", post ? "POST" : "GET",
path, path,
LIBAVFORMAT_IDENT, LIBAVFORMAT_IDENT,
s->off,
hoststr, hoststr,
auth_b64); auth_b64);
...@@ -211,7 +245,7 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr, ...@@ -211,7 +245,7 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr,
s->buf_ptr = s->buffer; s->buf_ptr = s->buffer;
s->buf_end = s->buffer; s->buf_end = s->buffer;
s->line_count = 0; s->line_count = 0;
s->location[0] = '\0'; s->off = 0;
if (post) { if (post) {
sleep(1); sleep(1);
return 0; return 0;
...@@ -231,11 +265,11 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr, ...@@ -231,11 +265,11 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr,
#ifdef DEBUG #ifdef DEBUG
printf("header='%s'\n", line); printf("header='%s'\n", line);
#endif #endif
err = process_line(s, line, s->line_count); err = process_line(h, line, s->line_count, new_location);
if (err < 0) if (err < 0)
return err; return err;
if (err == 0) if (err == 0)
return 0; break;
s->line_count++; s->line_count++;
q = line; q = line;
} else { } else {
...@@ -243,6 +277,8 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr, ...@@ -243,6 +277,8 @@ static int http_connect(URLContext *h, const char *path, const char *hoststr,
*q++ = ch; *q++ = ch;
} }
} }
return (off == s->off) ? 0 : -1;
} }
...@@ -261,6 +297,8 @@ static int http_read(URLContext *h, uint8_t *buf, int size) ...@@ -261,6 +297,8 @@ static int http_read(URLContext *h, uint8_t *buf, int size)
} else { } else {
len = url_read(s->hd, buf, size); len = url_read(s->hd, buf, size);
} }
if (len > 0)
s->off += len;
return len; return len;
} }
...@@ -279,11 +317,40 @@ static int http_close(URLContext *h) ...@@ -279,11 +317,40 @@ static int http_close(URLContext *h)
return 0; return 0;
} }
static offset_t http_seek(URLContext *h, offset_t off, int whence)
{
HTTPContext *s = h->priv_data;
URLContext *old_hd = s->hd;
offset_t old_off = s->off;
if (whence == AVSEEK_SIZE)
return s->filesize;
else if ((s->filesize == -1 && whence == SEEK_END) || h->is_streamed)
return -1;
/* we save the old context in case the seek fails */
s->hd = NULL;
if (whence == SEEK_CUR)
off += s->off;
else if (whence == SEEK_END)
off += s->filesize;
s->off = off;
/* if it fails, continue on old connection */
if (http_open_cnx(h) < 0) {
s->hd = old_hd;
s->off = old_off;
return -1;
}
url_close(old_hd);
return off;
}
URLProtocol http_protocol = { URLProtocol http_protocol = {
"http", "http",
http_open, http_open,
http_read, http_read,
http_write, http_write,
NULL, /* seek */ http_seek,
http_close, http_close,
}; };
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment