diff --git a/doc/protocols.texi b/doc/protocols.texi index c39ab614f3..5f6b118dc6 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -213,7 +213,7 @@ m3u8 files. HTTP (Hyper Text Transfer Protocol). -This protocol accepts the following options. +This protocol accepts the following options: @table @option @item seekable @@ -257,12 +257,14 @@ the @option{icy_metadata_headers} and @option{icy_metadata_packet} options. The default is 0. @item icy_metadata_headers -If the server supports ICY metadata, this contains the ICY specific HTTP reply -headers, separated with newline characters. +If the server supports ICY metadata, this contains the ICY-specific HTTP reply +headers, separated by newline characters. @item icy_metadata_packet 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 Set the cookies to be sent in future requests. The format of each cookie is the diff --git a/libavformat/http.c b/libavformat/http.c index fd5861ce25..95a64e4c9a 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -54,8 +54,6 @@ typedef struct { char *content_type; char *user_agent; 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; HTTPAuthState auth_state; HTTPAuthState proxy_auth_state; @@ -78,6 +76,10 @@ typedef struct { char *mime_type; char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name) 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_packet; #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 }, {"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_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), 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, 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, 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" }, {"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" }, @@ -403,6 +405,23 @@ static int parse_content_encoding(URLContext *h, char *p) 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, 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")) { s->icy_metaint = strtoll(p, NULL, 10); } else if (!av_strncasecmp(tag, "Icy-", 4)) { - // Concat all Icy- header lines - char *buf = av_asprintf("%s%s: %s\n", - 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; + if ((ret = parse_icy(s, tag, p)) < 0) + return ret; } else if (!av_strcasecmp(tag, "Content-Encoding")) { if ((ret = parse_content_encoding(h, p)) < 0) return ret; @@ -905,37 +919,52 @@ static int http_read_stream_all(URLContext *h, uint8_t *buf, int size) 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) { HTTPContext *s = h->priv_data; if (s->icy_metaint > 0) { - int remaining = s->icy_metaint - s->icy_data_read; /* until next metadata packet */ - 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 < 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 = store_icy(h, size); + if (size < 0) + return size; } + size = http_read_stream(h, buf, size); if (size > 0) s->icy_data_read += size;