[FFmpeg-devel] [PATCH] http: add support for reading streamcast metadata
Stefano Sabatini
stefasab at gmail.com
Thu Jun 27 15:14:58 CEST 2013
On date Wednesday 2013-06-26 02:16:06 +0200, wm4 encoded:
> Allow applications to request reading streamcast metadata. This uses
> AVOptions as API, and requires the application to explicitly request
> and read metadata. Metadata can be updated mid-stream; if an
> application is interested in that, it has to poll for the data by
> reading the "icy_meta_packet" option in regular intervals.
>
> There doesn't seem to be a nice way to transfer the metadata in a nicer
> way. Converting the metadata to ID3v2 tags might be a nice idea, but
> the libavformat mp3 demuxer doesn't seem to read these tags mid-stream,
> and even then we couldn't guarantee that tags are not inserted in the
> middle of mp3 packet data.
>
> This commit provides the minimum to enable applications to retrieve
> this information at all.
> ---
> doc/protocols.texi | 14 ++++++++++++++
> libavformat/http.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 67 insertions(+)
>
> diff --git a/doc/protocols.texi b/doc/protocols.texi
> index 97ff62d..0a559d8 100644
> --- a/doc/protocols.texi
> +++ b/doc/protocols.texi
> @@ -228,6 +228,20 @@ not specified.
> @item mime_type
> Set MIME type.
>
> + at 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_meta_header} and @option{icy_meta_packet} options. The default
> +is 0.
> +
> + at item icy_meta_header
Nit icy_metadata_header
since meta is a preposition and "meta-header" doesn't make much sense.
Also probably less confusing: headerS
> +If the server supports ICY metadata, this contains the ICY specific HTTP reply
> +headers, separated with newline characters.
> +
> + at item icy_meta_packet
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 91f8d1f..c991fa7 100644
> --- a/libavformat/http.c
> +++ b/libavformat/http.c
> @@ -28,6 +28,7 @@
> #include "httpauth.h"
> #include "url.h"
> #include "libavutil/opt.h"
> +#include "libavutil/bprint.h"
>
> /* XXX: POST protocol is not completely implemented because ffmpeg uses
> only a subset of it. */
> @@ -49,6 +50,8 @@ typedef struct {
> char *content_type;
> char *user_agent;
> int64_t off, filesize;
> + int icy_data_read;
> + int icy_metaint;
short doxy?
> char location[MAX_URL_SIZE];
> HTTPAuthState auth_state;
> HTTPAuthState proxy_auth_state;
> @@ -65,6 +68,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_meta_header;
> + char *icy_meta_packet;
> } HTTPContext;
>
> #define OFFSET(x) offsetof(HTTPContext, x)
> @@ -82,6 +88,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_meta_header", "return ICY metadata header", OFFSET(icy_meta_header), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 },
> +{"icy_meta_packet", "return current ICY metadata packet", OFFSET(icy_meta_packet), AV_OPT_TYPE_STRING, {0}, 0, 0, 0 },
> {NULL}
> };
> #define HTTP_CLASS(flavor)\
> @@ -218,6 +227,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 +385,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)) {
> + AVBPrint bp;
> + av_bprint_init(&bp, 1, -1);
> + if (s->icy_meta_header)
> + av_bprintf(&bp, "%s", s->icy_meta_header);
> + av_bprintf(&bp, "%s: %s\n", tag, p);
> + av_freep(&s->icy_meta_header);
> + av_bprint_finalize(&bp, &s->icy_meta_header);
Nit++: probably easier if you first free, then fill the new value but
maybe it's just me
> }
> }
> return 1;
> @@ -580,6 +600,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 +637,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 +677,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 +719,30 @@ 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;
> + if (!remaining) {
> + char data[4096];
> + char *buf;
> + int n;
> + int ch = http_getc(s);
> + if (ch < 0)
> + return ch;
> + if (ch > 0) {
> + ch *= 16;
> + for (n = 0; n < ch; n++)
> + data[n] = http_getc(s);
> + buf = av_mallocz(ch + 1);
> + if (buf)
> + memcpy(buf, data, ch);
> + av_freep(&s->icy_meta_packet);
> + s->icy_meta_packet = buf;
> + }
> + s->icy_data_read = 0;
> + remaining = s->icy_metaint;
> + }
> + size = FFMIN(size, remaining);
> + }
I can't really comment about this part. Can you add some generic
comments to explain what it's doing?
--
FFmpeg = Fostering and Foolish Mean Problematic Elastic God
More information about the ffmpeg-devel
mailing list