summaryrefslogtreecommitdiff
path: root/libavformat/http.c
diff options
context:
space:
mode:
authorwm4 <nfxjfg@googlemail.com>2013-06-26 00:53:26 +0200
committerStefano Sabatini <stefasab@gmail.com>2013-07-02 02:00:56 +0200
commita92fbe16f2dc118c0d3adc222484268831388648 (patch)
tree92c2b4034d7b1a0e28bdcdaa43a372e741cde7ef /libavformat/http.c
parentbf87908cd8da31e8f8fe75c06577170928ea70a8 (diff)
lavf/http: add support for reading streamcast metadata
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_metadata_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. Signed-off-by: Stefano Sabatini <stefasab@gmail.com>
Diffstat (limited to 'libavformat/http.c')
-rw-r--r--libavformat/http.c51
1 files changed, 51 insertions, 0 deletions
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);
}