summaryrefslogtreecommitdiffstats
path: root/stream
diff options
context:
space:
mode:
Diffstat (limited to 'stream')
-rw-r--r--stream/stream_lavf.c93
1 files changed, 89 insertions, 4 deletions
diff --git a/stream/stream_lavf.c b/stream/stream_lavf.c
index 2e97eac19d..a21b46c46d 100644
--- a/stream/stream_lavf.c
+++ b/stream/stream_lavf.c
@@ -30,7 +30,11 @@
#include "network.h"
#include "cookies.h"
+#include "core/bstr.h"
+#include "core/mp_talloc.h"
+
static int open_f(stream_t *stream, int mode, void *opts, int *file_format);
+static char **read_icy(stream_t *stream);
static int fill_buffer(stream_t *s, char *buffer, int max_len)
{
@@ -99,6 +103,12 @@ static int control(stream_t *s, int cmd, void *arg)
if (ts >= 0)
return 1;
break;
+ case STREAM_CTRL_GET_METADATA: {
+ *(char ***)arg = read_icy(s);
+ if (!*(char ***)arg)
+ break;
+ return 1;
+ }
case STREAM_CTRL_RECONNECT: {
if (avio && avio->write_flag)
break; // don't bother with this
@@ -111,6 +121,15 @@ static int control(stream_t *s, int cmd, void *arg)
return STREAM_UNSUPPORTED;
}
+static bool mp_avio_has_opts(AVIOContext *avio)
+{
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(54, 0, 0)
+ return avio->av_class != NULL;
+#else
+ return false;
+#endif
+}
+
static const char * const prefix[] = { "lavf://", "ffmpeg://" };
static int open_f(stream_t *stream, int mode, void *opts, int *file_format)
@@ -179,6 +198,7 @@ static int open_f(stream_t *stream, int mode, void *opts, int *file_format)
if (strlen(cust_headers))
av_dict_set(&dict, "headers", cust_headers, 0);
#endif
+ av_dict_set(&dict, "icy", "1", 0);
int err = avio_open2(&avio, filename, flags, NULL, &dict);
if (err < 0) {
@@ -188,13 +208,13 @@ static int open_f(stream_t *stream, int mode, void *opts, int *file_format)
goto out;
}
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(54, 0, 0)
- if (avio->av_class) {
+ if (mp_avio_has_opts(avio)) {
uint8_t *mt = NULL;
- if (av_opt_get(avio, "mime_type", AV_OPT_SEARCH_CHILDREN, &mt) >= 0)
+ if (av_opt_get(avio, "mime_type", AV_OPT_SEARCH_CHILDREN, &mt) >= 0) {
stream->mime_type = talloc_strdup(stream, mt);
+ av_free(mt);
+ }
}
-#endif
char *rtmp[] = {"rtmp:", "rtmpt:", "rtmpe:", "rtmpte:", "rtmps:"};
for (int i = 0; i < FF_ARRAY_ELEMS(rtmp); i++)
@@ -226,6 +246,71 @@ out:
return res;
}
+static void append_meta(char ***info, int *num_info, bstr name, bstr val)
+{
+ if (name.len && val.len) {
+ char *cname = talloc_asprintf(*info, "%.*s", BSTR_P(name));
+ char *cval = talloc_asprintf(*info, "%.*s", BSTR_P(val));
+ MP_TARRAY_APPEND(NULL, *info, *num_info, cname);
+ MP_TARRAY_APPEND(NULL, *info, *num_info, cval);
+ }
+}
+
+static char **read_icy(stream_t *s)
+{
+ AVIOContext *avio = s->priv;
+
+ if (!mp_avio_has_opts(avio))
+ return NULL;
+
+ uint8_t *icy_header = NULL;
+ if (av_opt_get(avio, "icy_metadata_headers", AV_OPT_SEARCH_CHILDREN,
+ &icy_header) < 0)
+ icy_header = NULL;
+
+ uint8_t *icy_packet;
+ if (av_opt_get(avio, "icy_metadata_packet", AV_OPT_SEARCH_CHILDREN,
+ &icy_packet) < 0)
+ icy_packet = NULL;
+
+ char **res = NULL;
+
+ if ((!icy_header || !icy_header[0]) && (!icy_packet || !icy_packet[0]))
+ goto done;
+
+ res = talloc_new(NULL);
+ int num_res = 0;
+ bstr header = bstr0(icy_header);
+ while (header.len) {
+ bstr line = bstr_strip_linebreaks(bstr_getline(header, &header));
+ bstr name, val;
+ if (bstr_split_tok(line, ": ", &name, &val)) {
+ bstr_eatstart0(&name, "icy-");
+ append_meta(&res, &num_res, name, val);
+ }
+ }
+
+ bstr packet = bstr0(icy_packet);
+ bstr head = bstr0("StreamTitle='");
+ int i = bstr_find(packet, head);
+ if (i >= 0) {
+ packet = bstr_cut(packet, i + head.len);
+ int end = bstrchr(packet, '\'');
+ packet = bstr_splice(packet, 0, end);
+ append_meta(&res, &num_res, bstr0("title"), packet);
+ }
+
+ if (res) {
+ MP_TARRAY_APPEND(NULL, res, num_res, NULL);
+ MP_TARRAY_APPEND(NULL, res, num_res, NULL);
+ }
+
+done:
+ av_free(icy_header);
+ av_free(icy_packet);
+ return res;
+}
+
const stream_info_t stream_info_ffmpeg = {
"FFmpeg",
"ffmpeg",