[FFmpeg-devel] [PATCH] http: add support for reading streamcast metadata
wm4
nfxjfg at googlemail.com
Wed Jun 26 02:16:06 CEST 2013
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
+If the server supports ICY metadata, this contains the ICY specific HTTP reply
+headers, separated with newline characters.
+
+ at item icy_meta_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;
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);
}
}
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);
+ }
return http_buf_read(h, buf, size);
}
@@ -744,6 +794,9 @@ static int http_close(URLContext *h)
int ret = 0;
HTTPContext *s = h->priv_data;
+ av_freep(&s->icy_meta_header);
+ av_freep(&s->icy_meta_packet);
+
if (!s->end_chunked_post) {
/* Close the write direction by sending the end of chunked encoding. */
ret = http_shutdown(h, h->flags);
--
1.8.3.1
More information about the ffmpeg-devel
mailing list