diff options
-rw-r--r-- | doc/protocols.texi | 14 | ||||
-rw-r--r-- | libavformat/http.c | 51 | ||||
-rw-r--r-- | libavformat/version.h | 2 |
3 files changed, 66 insertions, 1 deletions
diff --git a/doc/protocols.texi b/doc/protocols.texi index 97ff62d9e1..e8427aa277 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -228,6 +228,20 @@ not specified. @item mime_type Set MIME type. +@item icy +If set to 1 request ICY (SHOUTcast) metadata from the server. If the server +supports this, the metadata has to be retrieved by the application by reading +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. + +@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. + @item cookies Set the cookies to be sent in future requests. The format of each cookie is the same as the value of a Set-Cookie HTTP response field. Multiple cookies can be diff --git a/libavformat/http.c b/libavformat/http.c index 91f8d1fe97..be82352b9f 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -49,6 +49,8 @@ typedef struct { char *content_type; char *user_agent; int64_t off, filesize; + 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[MAX_URL_SIZE]; HTTPAuthState auth_state; HTTPAuthState proxy_auth_state; @@ -65,6 +67,9 @@ typedef struct { int rw_timeout; char *mime_type; char *cookies; ///< holds newline (\n) delimited Set-Cookie header field values (without the "Set-Cookie: " field name) + int icy; + char *icy_metadata_headers; + char *icy_metadata_packet; } HTTPContext; #define OFFSET(x) offsetof(HTTPContext, x) @@ -82,6 +87,9 @@ static const AVOption options[] = { {"timeout", "set timeout of socket I/O operations", OFFSET(rw_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, {"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, 0 }, +{"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 }, {NULL} }; #define HTTP_CLASS(flavor)\ @@ -218,6 +226,7 @@ int ff_http_do_new_request(URLContext *h, const char *uri) HTTPContext *s = h->priv_data; s->off = 0; + s->icy_data_read = 0; av_strlcpy(s->location, uri, sizeof(s->location)); return http_open_cnx(h); @@ -375,6 +384,16 @@ static int process_line(URLContext *h, char *line, int line_count, snprintf(s->cookies, str_size, "%s\n%s", tmp, p); av_free(tmp); } + } 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; } } return 1; @@ -580,6 +599,10 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, av_free(cookies); } } + if (!has_header(s->headers, "\r\nIcy-MetaData: ") && s->icy) { + len += av_strlcatf(headers + len, sizeof(headers) - len, + "Icy-MetaData: %d\r\n", 1); + } /* now add in custom headers */ if (s->headers) @@ -613,6 +636,7 @@ static int http_connect(URLContext *h, const char *path, const char *local_path, s->buf_end = s->buffer; s->line_count = 0; s->off = 0; + s->icy_data_read = 0; s->filesize = -1; s->willclose = 0; s->end_chunked_post = 0; @@ -652,6 +676,7 @@ static int http_buf_read(URLContext *h, uint8_t *buf, int size) } if (len > 0) { s->off += len; + s->icy_data_read += len; if (s->chunksize > 0) s->chunksize -= len; } @@ -693,6 +718,32 @@ static int http_read(URLContext *h, uint8_t *buf, int size) } size = FFMIN(size, s->chunksize); } + 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. + int ch = http_getc(s); + if (ch < 0) + return ch; + if (ch > 0) { + char data[255 * 16 + 1]; + int n; + int ret; + ch *= 16; + for (n = 0; n < ch; n++) + data[n] = http_getc(s); + data[ch + 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); + } return http_buf_read(h, buf, size); } diff --git a/libavformat/version.h b/libavformat/version.h index 45932d4972..3614c44ef7 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -31,7 +31,7 @@ #define LIBAVFORMAT_VERSION_MAJOR 55 #define LIBAVFORMAT_VERSION_MINOR 10 -#define LIBAVFORMAT_VERSION_MICRO 100 +#define LIBAVFORMAT_VERSION_MICRO 101 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ |