diff --git a/common/av_common.c b/common/av_common.c index 142699d4b4..a4fa5981ff 100644 --- a/common/av_common.c +++ b/common/av_common.c @@ -271,6 +271,18 @@ char **mp_get_lavf_demuxers(void) return list; } +char **mp_get_lavf_protocols(void) +{ + char **list = NULL; + int num = 0; + void *opaque = NULL; + const char *name; + while ((name = avio_enum_protocols(&opaque, 0))) + MP_TARRAY_APPEND(NULL, list, num, talloc_strdup(list, name)); + MP_TARRAY_APPEND(NULL, list, num, NULL); + return list; +} + int mp_codec_to_av_codec_id(const char *codec) { int id = AV_CODEC_ID_NONE; diff --git a/common/av_common.h b/common/av_common.h index c584085890..b019aa88e1 100644 --- a/common/av_common.h +++ b/common/av_common.h @@ -42,6 +42,7 @@ void mp_set_avcodec_threads(struct mp_log *l, AVCodecContext *avctx, int threads void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type); void mp_add_lavc_encoders(struct mp_decoder_list *list); char **mp_get_lavf_demuxers(void); +char **mp_get_lavf_protocols(void); int mp_codec_to_av_codec_id(const char *codec); const char *mp_codec_from_av_codec_id(int codec_id); bool mp_codec_is_lossless(const char *codec); diff --git a/stream/stream.c b/stream/stream.c index 06dd92930c..eca44e4afb 100644 --- a/stream/stream.c +++ b/stream/stream.c @@ -66,8 +66,6 @@ static const stream_info_t *const stream_list[] = { #if HAVE_CDDA &stream_info_cdda, #endif - &stream_info_ffmpeg, - &stream_info_ffmpeg_unsafe, &stream_info_avdevice, #if HAVE_DVBIN &stream_info_dvb, @@ -92,6 +90,8 @@ static const stream_info_t *const stream_list[] = { &stream_info_slice, &stream_info_fd, &stream_info_cb, + &stream_info_ffmpeg, + &stream_info_ffmpeg_unsafe, }; // Because of guarantees documented on STREAM_BUFFER_SIZE. @@ -325,12 +325,17 @@ static int stream_create_instance(const stream_info_t *sinfo, if (!sinfo->local_fs) return STREAM_NO_MATCH; } else { - for (int n = 0; sinfo->protocols && sinfo->protocols[n]; n++) { - path = match_proto(url, sinfo->protocols[n]); + char **get_protocols = sinfo->get_protocols ? sinfo->get_protocols() : NULL; + char **protocols = get_protocols ? get_protocols : (char **)sinfo->protocols; + + for (int n = 0; protocols && protocols[n]; n++) { + path = match_proto(url, protocols[n]); if (path) break; } + talloc_free(get_protocols); + if (!path) return STREAM_NO_MATCH; } @@ -864,16 +869,17 @@ char **stream_get_proto_list(void) for (int i = 0; i < MP_ARRAY_SIZE(stream_list); i++) { const stream_info_t *stream_info = stream_list[i]; - if (!stream_info->protocols) - continue; + char **get_protocols = stream_info->get_protocols ? stream_info->get_protocols() : NULL; + char **protocols = get_protocols ? get_protocols : (char **)stream_info->protocols; - for (int j = 0; stream_info->protocols[j]; j++) { - if (*stream_info->protocols[j] == '\0') - continue; + for (int j = 0; protocols && protocols[j]; j++) { + if (*protocols[j] == '\0') + continue; - MP_TARRAY_APPEND(NULL, list, num, - talloc_strdup(NULL, stream_info->protocols[j])); + MP_TARRAY_APPEND(NULL, list, num, talloc_strdup(list, protocols[j])); } + + talloc_free(get_protocols); } MP_TARRAY_APPEND(NULL, list, num, NULL); return list; @@ -888,7 +894,6 @@ void stream_print_proto_list(struct mp_log *log) for (int i = 0; list[i]; i++) { mp_info(log, " %s://\n", list[i]); count++; - talloc_free(list[i]); } talloc_free(list); mp_info(log, "\nTotal: %d protocols\n", count); @@ -899,10 +904,19 @@ bool stream_has_proto(const char *proto) for (int i = 0; i < MP_ARRAY_SIZE(stream_list); i++) { const stream_info_t *stream_info = stream_list[i]; - for (int j = 0; stream_info->protocols && stream_info->protocols[j]; j++) { - if (strcmp(stream_info->protocols[j], proto) == 0) - return true; + bool match = false; + char **get_protocols = stream_info->get_protocols ? stream_info->get_protocols() : NULL; + char **protocols = get_protocols ? get_protocols : (char **)stream_info->protocols; + + for (int j = 0; protocols && protocols[j]; j++) { + if (strcmp(protocols[j], proto) == 0) { + match = true; + break; + } } + + talloc_free(get_protocols); + return match; } return false; diff --git a/stream/stream.h b/stream/stream.h index 58b55e1a43..866affe1fe 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -114,6 +114,8 @@ typedef struct stream_info_st { // Alternative to open(). Only either open() or open2() can be set. int (*open2)(struct stream *st, const struct stream_open_args *args); const char *const *protocols; + // Alternative to protocols. For stream_lavf. + char **(*get_protocols)(void); bool can_write; // correctly checks for READ/WRITE modes bool local_fs; // supports STREAM_LOCAL_FS_ONLY int stream_origin; // 0 or set of STREAM_ORIGIN_*; if 0, the same origin diff --git a/stream/stream_lavf.c b/stream/stream_lavf.c index a2db551466..30c823c58f 100644 --- a/stream/stream_lavf.c +++ b/stream/stream_lavf.c @@ -238,6 +238,96 @@ void mp_setup_av_network_options(AVDictionary **dict, const char *target_fmt, talloc_free(temp); } +#define PROTO(...) (const char *[]){__VA_ARGS__, NULL} + +// List of safe protocols and their aliases +static const char **safe_protos[] = { + PROTO("data"), + PROTO("gopher"), + PROTO("gophers"), + PROTO("http", "dav", "webdav"), + PROTO("httpproxy"), + PROTO("https", "davs", "webdavs"), + PROTO("ipfs"), + PROTO("ipns"), + PROTO("mmsh", "mms", "mmshttp"), + PROTO("mmst"), + PROTO("rist"), + PROTO("rtmp"), + PROTO("rtmpe"), + PROTO("rtmps"), + PROTO("rtmpt"), + PROTO("rtmpte"), + PROTO("rtmpts"), + PROTO("rtp"), + PROTO("srt"), + PROTO("srtp"), + NULL, +}; + +static char **get_safe_protocols(void) +{ + int num = 0; + char **protocols = NULL; + char **ffmpeg_demuxers = mp_get_lavf_demuxers(); + char **ffmpeg_protos = mp_get_lavf_protocols(); + + for (int i = 0; ffmpeg_protos[i]; i++) { + for (int j = 0; safe_protos[j]; j++) { + if (strcmp(ffmpeg_protos[i], safe_protos[j][0]) != 0) + continue; + for (int k = 0; safe_protos[j][k]; k++) + MP_TARRAY_APPEND(NULL, protocols, num, talloc_strdup(protocols, safe_protos[j][k])); + break; + } + } + + // rtsp is a demuxer not protocol in ffmpeg so it is handled separately + for (int i = 0; ffmpeg_demuxers[i]; i++) { + if (strcmp("rtsp", ffmpeg_demuxers[i]) == 0) { + MP_TARRAY_APPEND(NULL, protocols, num, talloc_strdup(protocols, "rtsp")); + MP_TARRAY_APPEND(NULL, protocols, num, talloc_strdup(protocols, "rtsps")); + break; + } + } + + MP_TARRAY_APPEND(NULL, protocols, num, NULL); + + talloc_free(ffmpeg_demuxers); + talloc_free(ffmpeg_protos); + + return protocols; +} + +static char **get_unsafe_protocols(void) +{ + int num = 0; + char **protocols = NULL; + char **safe_protocols = get_safe_protocols(); + char **ffmpeg_protos = mp_get_lavf_protocols(); + + for (int i = 0; ffmpeg_protos[i]; i++) { + bool safe_protocol = false; + for (int j = 0; safe_protocols[j]; j++) { + if (strcmp(ffmpeg_protos[i], safe_protocols[j]) == 0) { + safe_protocol = true; + break; + } + } + if (!safe_protocol) + MP_TARRAY_APPEND(NULL, protocols, num, talloc_strdup(protocols, ffmpeg_protos[i])); + } + + MP_TARRAY_APPEND(NULL, protocols, num, talloc_strdup(protocols, "ffmpeg")); + MP_TARRAY_APPEND(NULL, protocols, num, talloc_strdup(protocols, "lavf")); + + MP_TARRAY_APPEND(NULL, protocols, num, NULL); + + talloc_free(ffmpeg_protos); + talloc_free(safe_protocols); + return protocols; +} + // Escape http URLs with unescaped, invalid characters in them. // libavformat's http protocol does not do this, and a patch to add this // in a 100% safe case (spaces only) was rejected. @@ -431,12 +521,7 @@ done: const stream_info_t stream_info_ffmpeg = { .name = "ffmpeg", .open = open_f, - .protocols = (const char *const[]){ - "rtmp", "rtsp", "rtsps", "http", "https", "mms", "mmst", "mmsh", "mmshttp", - "rtp", "httpproxy", "rtmpe", "rtmps", "rtmpt", "rtmpte", "rtmpts", "srt", - "rist", "srtp", "gopher", "gophers", "data", "ipfs", "ipns", "dav", - "davs", "webdav", "webdavs", - NULL }, + .get_protocols = get_safe_protocols, .can_write = true, .stream_origin = STREAM_ORIGIN_NET, }; @@ -448,10 +533,7 @@ const stream_info_t stream_info_ffmpeg = { const stream_info_t stream_info_ffmpeg_unsafe = { .name = "ffmpeg", .open = open_f, - .protocols = (const char *const[]){ - "lavf", "ffmpeg", "udp", "ftp", "tcp", "tls", "unix", "sftp", "md5", - "concat", "smb", - NULL }, + .get_protocols = get_unsafe_protocols, .stream_origin = STREAM_ORIGIN_UNSAFE, .can_write = true, };