Merge commit '8075c3d8bb1f6aade0cc7c5c40db9bc1bcd84cab'

* commit '8075c3d8bb1f6aade0cc7c5c40db9bc1bcd84cab':
  http: Add support reading ICY metadata

Conflicts:
	doc/protocols.texi
	libavformat/http.c

See: a92fbe16f2
See: 636273d3d4
Merged-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
Michael Niedermayer 2014-03-12 03:16:21 +01:00
commit ca2369cdee
2 changed files with 71 additions and 40 deletions

View File

@ -213,7 +213,7 @@ m3u8 files.
HTTP (Hyper Text Transfer Protocol). HTTP (Hyper Text Transfer Protocol).
This protocol accepts the following options. This protocol accepts the following options:
@table @option @table @option
@item seekable @item seekable
@ -257,12 +257,14 @@ the @option{icy_metadata_headers} and @option{icy_metadata_packet} options.
The default is 0. The default is 0.
@item icy_metadata_headers @item icy_metadata_headers
If the server supports ICY metadata, this contains the ICY specific HTTP reply If the server supports ICY metadata, this contains the ICY-specific HTTP reply
headers, separated with newline characters. headers, separated by newline characters.
@item icy_metadata_packet @item icy_metadata_packet
If the server supports ICY metadata, and @option{icy} was set to 1, this If the server supports ICY metadata, and @option{icy} was set to 1, this
contains the last non-empty metadata packet sent by the server. contains the last non-empty metadata packet sent by the server. It should be
polled in regular intervals by applications interested in mid-stream metadata
updates.
@item cookies @item cookies
Set the cookies to be sent in future requests. The format of each cookie is the Set the cookies to be sent in future requests. The format of each cookie is the

View File

@ -54,8 +54,6 @@ typedef struct {
char *content_type; char *content_type;
char *user_agent; char *user_agent;
int64_t off, filesize, req_end_offset; int64_t off, filesize, req_end_offset;
int icy_data_read; ///< how much data was read since last ICY metadata packet
int icy_metaint; ///< after how many bytes of read data a new metadata packet will be found
char *location; char *location;
HTTPAuthState auth_state; HTTPAuthState auth_state;
HTTPAuthState proxy_auth_state; HTTPAuthState proxy_auth_state;
@ -78,6 +76,10 @@ typedef struct {
char *mime_type; char *mime_type;
char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name) char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name)
int icy; int icy;
/* how much data was read since the last ICY metadata packet */
int icy_data_read;
/* after how many bytes of read data a new metadata packet will be found */
int icy_metaint;
char *icy_metadata_headers; char *icy_metadata_headers;
char *icy_metadata_packet; char *icy_metadata_packet;
#if CONFIG_ZLIB #if CONFIG_ZLIB
@ -104,8 +106,8 @@ static const AVOption options[] = {
{"mime_type", "set MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 }, {"mime_type", "set MIME type", OFFSET(mime_type), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 },
{"cookies", "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies), AV_OPT_TYPE_STRING, {0}, 0, 0, D }, {"cookies", "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies), AV_OPT_TYPE_STRING, {0}, 0, 0, D },
{"icy", "request ICY metadata", OFFSET(icy), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, D }, {"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, 0 }, {"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, 0 }, {"icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, {0}, 0, 0, AV_OPT_FLAG_EXPORT },
{"auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, {.i64 = HTTP_AUTH_NONE}, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D|E, "auth_type" }, {"auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, {.i64 = HTTP_AUTH_NONE}, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D|E, "auth_type" },
{"none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_NONE}, 0, 0, D|E, "auth_type" }, {"none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_NONE}, 0, 0, D|E, "auth_type" },
{"basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_BASIC}, 0, 0, D|E, "auth_type" }, {"basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, {.i64 = HTTP_AUTH_BASIC}, 0, 0, D|E, "auth_type" },
@ -403,6 +405,23 @@ static int parse_content_encoding(URLContext *h, char *p)
return 0; return 0;
} }
// Concat all Icy- header lines
static int parse_icy(HTTPContext *s, const char *tag, const char *p)
{
int len = 4 + strlen(p) + strlen(tag);
int ret;
if (s->icy_metadata_headers)
len += strlen(s->icy_metadata_headers);
if ((ret = av_reallocp(&s->icy_metadata_headers, len)) < 0)
return ret;
av_strlcatf(s->icy_metadata_headers, len, "%s: %s\n", tag, p);
return 0;
}
static int process_line(URLContext *h, char *line, int line_count, static int process_line(URLContext *h, char *line, int line_count,
int *new_location) int *new_location)
{ {
@ -489,13 +508,8 @@ static int process_line(URLContext *h, char *line, int line_count,
} else if (!av_strcasecmp (tag, "Icy-MetaInt")) { } else if (!av_strcasecmp (tag, "Icy-MetaInt")) {
s->icy_metaint = strtoll(p, NULL, 10); s->icy_metaint = strtoll(p, NULL, 10);
} else if (!av_strncasecmp(tag, "Icy-", 4)) { } else if (!av_strncasecmp(tag, "Icy-", 4)) {
// Concat all Icy- header lines if ((ret = parse_icy(s, tag, p)) < 0)
char *buf = av_asprintf("%s%s: %s\n", return ret;
s->icy_metadata_headers ? s->icy_metadata_headers : "", tag, p);
if (!buf)
return AVERROR(ENOMEM);
av_freep(&s->icy_metadata_headers);
s->icy_metadata_headers = buf;
} else if (!av_strcasecmp(tag, "Content-Encoding")) { } else if (!av_strcasecmp(tag, "Content-Encoding")) {
if ((ret = parse_content_encoding(h, p)) < 0) if ((ret = parse_content_encoding(h, p)) < 0)
return ret; return ret;
@ -905,37 +919,52 @@ static int http_read_stream_all(URLContext *h, uint8_t *buf, int size)
return pos; return pos;
} }
static int store_icy(URLContext *h, int size)
{
HTTPContext *s = h->priv_data;
/* until next metadata packet */
int remaining = s->icy_metaint - s->icy_data_read;
if (remaining < 0)
return AVERROR_INVALIDDATA;
if (!remaining) {
// The metadata packet is variable sized. It has a 1 byte header
// which sets the length of the packet (divided by 16). If it's 0,
// the metadata doesn't change. After the packet, icy_metaint bytes
// of normal data follow.
uint8_t ch;
int len = http_read_stream_all(h, &ch, 1);
if (len < 0)
return len;
if (ch > 0) {
char data[255 * 16 + 1];
int ret;
len = ch * 16;
ret = http_read_stream_all(h, data, len);
if (ret < 0)
return ret;
data[len + 1] = 0;
if ((ret = av_opt_set(s, "icy_metadata_packet", data, 0)) < 0)
return ret;
}
s->icy_data_read = 0;
remaining = s->icy_metaint;
}
return FFMIN(size, remaining);
}
static int http_read(URLContext *h, uint8_t *buf, int size) static int http_read(URLContext *h, uint8_t *buf, int size)
{ {
HTTPContext *s = h->priv_data; HTTPContext *s = h->priv_data;
if (s->icy_metaint > 0) { if (s->icy_metaint > 0) {
int remaining = s->icy_metaint - s->icy_data_read; /* until next metadata packet */ size = store_icy(h, size);
if (!remaining) { if (size < 0)
// The metadata packet is variable sized. It has a 1 byte header return size;
// which sets the length of the packet (divided by 16). If it's 0,
// the metadata doesn't change. After the packet, icy_metaint bytes
// of normal data follow.
uint8_t ch;
int len = http_read_stream_all(h, &ch, 1);
if (len < 1)
return len;
if (ch > 0) {
char data[255 * 16 + 1];
int ret;
len = ch * 16;
ret = http_read_stream_all(h, data, len);
if (ret < len)
return ret;
data[len + 1] = 0;
if ((ret = av_opt_set(s, "icy_metadata_packet", data, 0)) < 0)
return ret;
}
s->icy_data_read = 0;
remaining = s->icy_metaint;
}
size = FFMIN(size, remaining);
} }
size = http_read_stream(h, buf, size); size = http_read_stream(h, buf, size);
if (size > 0) if (size > 0)
s->icy_data_read += size; s->icy_data_read += size;