Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH] Use AVOption calls to parse URL options (PR #20555)
@ 2025-09-19 19:07 Marton Balint via ffmpeg-devel
  0 siblings, 0 replies; only message in thread
From: Marton Balint via ffmpeg-devel @ 2025-09-19 19:07 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Marton Balint

PR #20555 opened by Marton Balint (cus)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20555
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20555.patch

This removes lots of parsing code duplications, unifies parsing rules and also enhances the documentation.

Some notable changes, some of them slightly changes behaviour:

- unconditional complication of urldecode.c
- always doing URL decoding for URL option keys and values, previously URL decoding was only used in a few places
- proper rejection of invalid option values
- rejection of unknown options for libsrt and sctp. (other protocols might nest each other so unkown options can be OK)
- supporting OPT_TYPE_BOOL options with no value (e.g. ?verify instead of ?verify=1), previously only a few boolean options were supported this way
- the order of parsing AVOptions / URLOptions is unified, URL options now always override AVOptions, this mostly affects the TLS protocol
- previously you could merge multicast sources from AVOptions and URLOptions, from now only one of those is parsed.


>From 016015b8ef97443c1a65617ff5603748abd1a512 Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Tue, 26 Aug 2025 22:57:40 +0200
Subject: [PATCH 01/10] avformat/urldecode: add ff_urldecode_len function

This will be used later to decode partial strings.

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 libavformat/urldecode.c | 18 ++++++++++++++++++
 libavformat/urldecode.h | 16 ++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/libavformat/urldecode.c b/libavformat/urldecode.c
index e7fa27b3fa..fdaa41784f 100644
--- a/libavformat/urldecode.c
+++ b/libavformat/urldecode.c
@@ -28,6 +28,8 @@
 
 #include <string.h>
 
+#include "libavutil/error.h"
+#include "libavutil/macros.h"
 #include "libavutil/mem.h"
 #include "libavutil/avstring.h"
 #include "urldecode.h"
@@ -93,3 +95,19 @@ char *ff_urldecode(const char *url, int decode_plus_sign)
 
     return dest;
 }
+
+int ff_urldecode_len(char *dest, size_t dest_len, const char *url, size_t url_max_len, int decode_plus_sign)
+{
+    size_t written_bytes;
+    size_t url_len = strlen(url);
+
+    url_len = FFMIN(url_len, url_max_len);
+
+    if (dest_len <= url_len)
+        return AVERROR(EINVAL);
+
+    written_bytes = urldecode(dest, url, url_len, decode_plus_sign);
+    dest[written_bytes] = '\0';
+
+    return written_bytes;
+}
diff --git a/libavformat/urldecode.h b/libavformat/urldecode.h
index 80b11c3428..adca3e89a6 100644
--- a/libavformat/urldecode.h
+++ b/libavformat/urldecode.h
@@ -33,4 +33,20 @@
  */
 char *ff_urldecode(const char *url, int decode_plus_sign);
 
+/**
+ * Decodes an URL from its percent-encoded form back into normal
+ * representation. This function returns the decoded URL in a string.
+ * The URL to be decoded does not necessarily have to be encoded but
+ * in that case the original string is duplicated.
+ *
+ * @param dest the destination buffer.
+ * @param dest_len the maximum available space in the destination buffer.
+ *                 Must be bigger than FFMIN(strlen(url), url_max_len) to avoid
+ *                 an AVERROR(EINVAL) result
+ * @param url_max_len the maximum number of chars to read from url
+ * @param decode_plus_sign if nonzero plus sign is decoded to space
+ * @return the number of written bytes to dest excluding the zero terminator,
+ *         negative on error
+ */
+int ff_urldecode_len(char *dest, size_t dest_len, const char *url, size_t url_max_len, int decode_plus_sign);
 #endif /* AVFORMAT_URLDECODE_H */
-- 
2.49.1


>From 717ff1eb3823f88f74b19066a0377dcb167ad91b Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Tue, 26 Aug 2025 23:03:40 +0200
Subject: [PATCH 02/10] avformat: compile urldecode unconditionally

It will be used by the generic helper function to set options from URLs.

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 libavformat/Makefile | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/libavformat/Makefile b/libavformat/Makefile
index ab5551a735..801a0b0330 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -28,6 +28,7 @@ OBJS = allformats.o         \
        sdp.o                \
        seek.o               \
        url.o                \
+       urldecode.o          \
        utils.o              \
        version.o            \
 
@@ -536,10 +537,8 @@ OBJS-$(CONFIG_RTP_MUXER)                 += rtp.o         \
                                             rtpenc_vp8.o  \
                                             rtpenc_vp9.o                \
                                             rtpenc_xiph.o
-OBJS-$(CONFIG_RTSP_DEMUXER)              += rtsp.o rtspdec.o httpauth.o \
-                                            urldecode.o
-OBJS-$(CONFIG_RTSP_MUXER)                += rtsp.o rtspenc.o httpauth.o \
-                                            urldecode.o
+OBJS-$(CONFIG_RTSP_DEMUXER)              += rtsp.o rtspdec.o httpauth.o
+OBJS-$(CONFIG_RTSP_MUXER)                += rtsp.o rtspenc.o httpauth.o
 OBJS-$(CONFIG_S337M_DEMUXER)             += s337m.o spdif.o
 OBJS-$(CONFIG_SAMI_DEMUXER)              += samidec.o subtitles.o
 OBJS-$(CONFIG_SAP_DEMUXER)               += sapdec.o
@@ -684,13 +683,13 @@ OBJS-$(CONFIG_FFRTMPCRYPT_PROTOCOL)      += rtmpcrypt.o rtmpdigest.o rtmpdh.o
 OBJS-$(CONFIG_FFRTMPHTTP_PROTOCOL)       += rtmphttp.o
 OBJS-$(CONFIG_FILE_PROTOCOL)             += file.o
 OBJS-$(CONFIG_FD_PROTOCOL)               += file.o
-OBJS-$(CONFIG_FTP_PROTOCOL)              += ftp.o urldecode.o
+OBJS-$(CONFIG_FTP_PROTOCOL)              += ftp.o
 OBJS-$(CONFIG_GOPHER_PROTOCOL)           += gopher.o
 OBJS-$(CONFIG_GOPHERS_PROTOCOL)          += gopher.o
 OBJS-$(CONFIG_HLS_PROTOCOL)              += hlsproto.o
-OBJS-$(CONFIG_HTTP_PROTOCOL)             += http.o httpauth.o urldecode.o
-OBJS-$(CONFIG_HTTPPROXY_PROTOCOL)        += http.o httpauth.o urldecode.o
-OBJS-$(CONFIG_HTTPS_PROTOCOL)            += http.o httpauth.o urldecode.o
+OBJS-$(CONFIG_HTTP_PROTOCOL)             += http.o httpauth.o
+OBJS-$(CONFIG_HTTPPROXY_PROTOCOL)        += http.o httpauth.o
+OBJS-$(CONFIG_HTTPS_PROTOCOL)            += http.o httpauth.o
 OBJS-$(CONFIG_ICECAST_PROTOCOL)          += icecast.o
 OBJS-$(CONFIG_MD5_PROTOCOL)              += md5proto.o
 OBJS-$(CONFIG_MMSH_PROTOCOL)             += mmsh.o mms.o asf_tags.o
@@ -724,7 +723,7 @@ OBJS-$(CONFIG_UDPLITE_PROTOCOL)          += udp.o ip.o
 OBJS-$(CONFIG_UNIX_PROTOCOL)             += unix.o
 
 # external library protocols
-OBJS-$(CONFIG_LIBAMQP_PROTOCOL)          += libamqp.o urldecode.o
+OBJS-$(CONFIG_LIBAMQP_PROTOCOL)          += libamqp.o
 OBJS-$(CONFIG_LIBRIST_PROTOCOL)          += librist.o
 OBJS-$(CONFIG_LIBRTMP_PROTOCOL)          += librtmp.o
 OBJS-$(CONFIG_LIBRTMPE_PROTOCOL)         += librtmp.o
@@ -732,7 +731,7 @@ OBJS-$(CONFIG_LIBRTMPS_PROTOCOL)         += librtmp.o
 OBJS-$(CONFIG_LIBRTMPT_PROTOCOL)         += librtmp.o
 OBJS-$(CONFIG_LIBRTMPTE_PROTOCOL)        += librtmp.o
 OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL)     += libsmbclient.o
-OBJS-$(CONFIG_LIBSRT_PROTOCOL)           += libsrt.o urldecode.o
+OBJS-$(CONFIG_LIBSRT_PROTOCOL)           += libsrt.o
 OBJS-$(CONFIG_LIBSSH_PROTOCOL)           += libssh.o
 OBJS-$(CONFIG_LIBZMQ_PROTOCOL)           += libzmq.o
 
-- 
2.49.1


>From 2c1295d878347a82e3cf4f06fe4462eae88a292a Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Mon, 25 Aug 2025 00:54:29 +0200
Subject: [PATCH 03/10] avformat/utils: add helper function to set opts from
 query string

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 libavformat/internal.h | 12 ++++++++
 libavformat/utils.c    | 66 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+)

diff --git a/libavformat/internal.h b/libavformat/internal.h
index 0dfbf02ca0..1a50ba07d3 100644
--- a/libavformat/internal.h
+++ b/libavformat/internal.h
@@ -649,4 +649,16 @@ int ff_bprint_get_frame_filename(struct AVBPrint *buf, const char *path, int64_t
  */
 int ff_dict_set_timestamp(AVDictionary **dict, const char *key, int64_t timestamp);
 
+/**
+ * Set a list of query string options on an object. Only the objects own
+ * options will be set.
+ *
+ * @param obj the object to set options on
+ * @param str the query string
+ * @param allow_unknown ignore unknown query string options. This can be OK if
+ *                      nested protocols are used.
+ * @return <0 on error
+ */
+int ff_parse_opts_from_query_string(void *obj, const char *str, int allow_unkown);
+
 #endif /* AVFORMAT_INTERNAL_H */
diff --git a/libavformat/utils.c b/libavformat/utils.c
index 3573aa918e..a1af20cffb 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -25,10 +25,13 @@
 #include "config.h"
 
 #include "libavutil/avstring.h"
+#include "libavutil/avassert.h"
 #include "libavutil/bprint.h"
 #include "libavutil/dict.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
 #include "libavutil/time.h"
 #include "libavutil/time_internal.h"
 
@@ -41,6 +44,7 @@
 #include "network.h"
 #endif
 #include "os_support.h"
+#include "urldecode.h"
 
 /**
  * @file
@@ -618,3 +622,65 @@ int ff_dict_set_timestamp(AVDictionary **dict, const char *key, int64_t timestam
         return AVERROR_EXTERNAL;
     }
 }
+
+static const AVOption* find_opt(void *obj, const char *name, size_t len)
+{
+    char decoded_name[128];
+
+    if (ff_urldecode_len(decoded_name, sizeof(decoded_name), name, len, 1) < 0)
+        return NULL;
+
+    return av_opt_find(obj, decoded_name, NULL, 0, 0);
+}
+
+int ff_parse_opts_from_query_string(void *obj, const char *str, int allow_unknown)
+{
+    const AVOption *opt;
+    char optval[512];
+    int ret;
+
+    if (*str == '?')
+        str++;
+    while (*str) {
+        size_t len = strcspn(str, "=&");
+        opt = find_opt(obj, str, len);
+        if (!opt) {
+            if (!allow_unknown) {
+                av_log(obj, AV_LOG_ERROR, "Query string option '%.*s' does not exist\n", (int)len, str);
+                return AVERROR_OPTION_NOT_FOUND;
+            }
+            av_log(obj, AV_LOG_VERBOSE, "Ignoring unknown query string option '%.*s'\n", (int)len, str);
+        }
+        str += len;
+        if (!opt) {
+            len = strcspn(str, "&");
+            str += len;
+        } else if (*str == '&' || *str == '\0') {
+            /* Check for bool options without value, e.g. "?verify".
+             * Unfortunately "listen" is a tri-state INT for some protocols so
+             * we also have to allow that for backward compatibility. */
+            if (opt->type != AV_OPT_TYPE_BOOL && strcmp(opt->name, "listen")) {
+                av_log(obj, AV_LOG_ERROR, "Non-bool query string option '%s' has no value\n", opt->name);
+                return AVERROR(EINVAL);
+            }
+            ret = av_opt_set_int(obj, opt->name, 1, 0);
+            if (ret < 0)
+                return ret;
+        } else {
+            av_assert2(*str == '=');
+            str++;
+            len = strcspn(str, "&");
+            if (ff_urldecode_len(optval, sizeof(optval), str, len, 1) < 0) {
+                av_log(obj, AV_LOG_ERROR, "Query string option '%s' value is too long\n", opt->name);
+                return AVERROR(EINVAL);
+            }
+            ret = av_opt_set(obj, opt->name, optval, 0);
+            if (ret < 0)
+                return ret;
+            str += len;
+        }
+        if (*str)
+            str++;
+    }
+    return 0;
+}
-- 
2.49.1


>From 6eca18b7a2217c673d7f0f6d4c182791aac02953 Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Mon, 25 Aug 2025 01:50:13 +0200
Subject: [PATCH 04/10] avformat/libsrt: use ff_parse_opts_from_query_string()
 to set URL parameters

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 doc/protocols.texi   |  15 ++---
 libavformat/libsrt.c | 142 +------------------------------------------
 2 files changed, 8 insertions(+), 149 deletions(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index cd0726cc7a..133574b4db 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -1554,18 +1554,13 @@ srt://@var{hostname}:@var{port}[?@var{options}]
 @end example
 
 @var{options} contains a list of &-separated options of the form
-@var{key}=@var{val}.
+@var{key}=@var{val}. Standard percent-encoding (and using the plus sign for
+space) can be used to escape keys and values.
 
-or
+Options can also can be specified via command line options (or in code via
+@code{AVOption}s).
 
-@example
-@var{options} srt://@var{hostname}:@var{port}
-@end example
-
-@var{options} contains a list of '-@var{key} @var{val}'
-options.
-
-This protocol accepts the following options.
+The list of supported options follows.
 
 @table @option
 @item connect_timeout=@var{milliseconds}
diff --git a/libavformat/libsrt.c b/libavformat/libsrt.c
index 9e860abccd..ba04d9f782 100644
--- a/libavformat/libsrt.c
+++ b/libavformat/libsrt.c
@@ -386,8 +386,6 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags)
     struct addrinfo hints = { 0 }, *ai, *cur_ai;
     int port, fd;
     SRTContext *s = h->priv_data;
-    const char *p;
-    char buf[256];
     int ret;
     char hostname[1024],proto[1024],path[1024];
     char portstr[10];
@@ -402,15 +400,6 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags)
         av_log(h, AV_LOG_ERROR, "Port missing in uri\n");
         return AVERROR(EINVAL);
     }
-    p = strchr(uri, '?');
-    if (p) {
-        if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
-            s->rw_timeout = strtoll(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
-            s->listen_timeout = strtoll(buf, NULL, 10);
-        }
-    }
     if (s->rw_timeout >= 0) {
         open_timeout = h->rw_timeout = s->rw_timeout;
     }
@@ -534,7 +523,6 @@ static int libsrt_open(URLContext *h, const char *uri, int flags)
 {
     SRTContext *s = h->priv_data;
     const char * p;
-    char buf[1024];
     int ret = 0;
 
     if (srt_startup() < 0) {
@@ -544,133 +532,9 @@ static int libsrt_open(URLContext *h, const char *uri, int flags)
     /* SRT options (srt/srt.h) */
     p = strchr(uri, '?');
     if (p) {
-        if (av_find_info_tag(buf, sizeof(buf), "maxbw", p)) {
-            s->maxbw = strtoll(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "pbkeylen", p)) {
-            s->pbkeylen = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "passphrase", p)) {
-            av_freep(&s->passphrase);
-            s->passphrase = ff_urldecode(buf, 1);
-            if (!s->passphrase) {
-                ret = AVERROR(ENOMEM);
-                goto err;
-            }
-        }
-#if SRT_VERSION_VALUE >= 0x010302
-        if (av_find_info_tag(buf, sizeof(buf), "enforced_encryption", p)) {
-            s->enforced_encryption = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "kmrefreshrate", p)) {
-            s->kmrefreshrate = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "kmpreannounce", p)) {
-            s->kmpreannounce = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "snddropdelay", p)) {
-            s->snddropdelay = strtoll(buf, NULL, 10);
-        }
-#endif
-        if (av_find_info_tag(buf, sizeof(buf), "mss", p)) {
-            s->mss = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "ffs", p)) {
-            s->ffs = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "ipttl", p)) {
-            s->ipttl = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "iptos", p)) {
-            s->iptos = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "inputbw", p)) {
-            s->inputbw = strtoll(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "oheadbw", p)) {
-            s->oheadbw = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "latency", p)) {
-            s->latency = strtoll(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "tsbpddelay", p)) {
-            s->latency = strtoll(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "rcvlatency", p)) {
-            s->rcvlatency = strtoll(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "peerlatency", p)) {
-            s->peerlatency = strtoll(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "tlpktdrop", p)) {
-            s->tlpktdrop = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "nakreport", p)) {
-            s->nakreport = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "connect_timeout", p)) {
-            s->connect_timeout = strtoll(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "payload_size", p) ||
-            av_find_info_tag(buf, sizeof(buf), "pkt_size", p)) {
-            s->payload_size = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "mode", p)) {
-            if (!strcmp(buf, "caller")) {
-                s->mode = SRT_MODE_CALLER;
-            } else if (!strcmp(buf, "listener")) {
-                s->mode = SRT_MODE_LISTENER;
-            } else if (!strcmp(buf, "rendezvous")) {
-                s->mode = SRT_MODE_RENDEZVOUS;
-            } else {
-                ret = AVERROR(EINVAL);
-                goto err;
-            }
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "sndbuf", p)) {
-            s->sndbuf = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "rcvbuf", p)) {
-            s->rcvbuf = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "lossmaxttl", p)) {
-            s->lossmaxttl = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "minversion", p)) {
-            s->minversion = strtol(buf, NULL, 0);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "streamid", p)) {
-            av_freep(&s->streamid);
-            s->streamid = ff_urldecode(buf, 1);
-            if (!s->streamid) {
-                ret = AVERROR(ENOMEM);
-                goto err;
-            }
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "smoother", p)) {
-            av_freep(&s->smoother);
-            s->smoother = ff_urldecode(buf, 1);
-            if(!s->smoother) {
-                ret = AVERROR(ENOMEM);
-                goto err;
-            }
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "messageapi", p)) {
-            s->messageapi = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "transtype", p)) {
-            if (!strcmp(buf, "live")) {
-                s->transtype = SRTT_LIVE;
-            } else if (!strcmp(buf, "file")) {
-                s->transtype = SRTT_FILE;
-            } else {
-                ret = AVERROR(EINVAL);
-                goto err;
-            }
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "linger", p)) {
-            s->linger = strtol(buf, NULL, 10);
-        }
+        ret = ff_parse_opts_from_query_string(s, p, 0);
+        if (ret < 0)
+            goto err;
     }
     ret = libsrt_setup(h, uri, flags);
     if (ret < 0)
-- 
2.49.1


>From bc5c0d24b3224170f898b8fc28e28c6277c1b145 Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Mon, 25 Aug 2025 01:51:15 +0200
Subject: [PATCH 05/10] avformat/udp: use ff_parse_opts_from_query_string() to
 set URL parameters

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 doc/protocols.texi |  7 +++-
 libavformat/udp.c  | 93 +++++++---------------------------------------
 2 files changed, 19 insertions(+), 81 deletions(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index 133574b4db..b4b6c25579 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -2114,7 +2114,12 @@ The required syntax for an UDP URL is:
 udp://@var{hostname}:@var{port}[?@var{options}]
 @end example
 
-@var{options} contains a list of &-separated options of the form @var{key}=@var{val}.
+@var{options} contains a list of &-separated options of the form
+@var{key}=@var{val}. Standard percent-encoding (and using the plus sign for
+space) can be used to escape keys and values.
+
+Options can also can be specified via command line options (or in code via
+@code{AVOption}s).
 
 In case threading is enabled on the system, a circular buffer is used
 to store the incoming data, which allows one to reduce loss of data due to
diff --git a/libavformat/udp.c b/libavformat/udp.c
index e1dec49010..05936fc055 100644
--- a/libavformat/udp.c
+++ b/libavformat/udp.c
@@ -36,6 +36,7 @@
 #include "libavutil/opt.h"
 #include "libavutil/log.h"
 #include "libavutil/time.h"
+#include "internal.h"
 #include "network.h"
 #include "os_support.h"
 #include "url.h"
@@ -697,7 +698,6 @@ static int udp_open(URLContext *h, const char *uri, int flags)
     UDPContext *s = h->priv_data;
     int is_output;
     const char *p;
-    char buf[256];
     struct sockaddr_storage my_addr;
     socklen_t len;
     int ret;
@@ -708,87 +708,11 @@ static int udp_open(URLContext *h, const char *uri, int flags)
     if (s->buffer_size < 0)
         s->buffer_size = is_output ? UDP_TX_BUF_SIZE : UDP_RX_BUF_SIZE;
 
-    if (s->sources) {
-        if ((ret = ff_ip_parse_sources(h, s->sources, &s->filters)) < 0)
-            goto fail;
-    }
-
-    if (s->block) {
-        if ((ret = ff_ip_parse_blocks(h, s->block, &s->filters)) < 0)
-            goto fail;
-    }
-
     p = strchr(uri, '?');
     if (p) {
-        if (av_find_info_tag(buf, sizeof(buf), "reuse", p)) {
-            char *endptr = NULL;
-            s->reuse_socket = strtol(buf, &endptr, 10);
-            /* assume if no digits were found it is a request to enable it */
-            if (buf == endptr)
-                s->reuse_socket = 1;
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "overrun_nonfatal", p)) {
-            char *endptr = NULL;
-            s->overrun_nonfatal = strtol(buf, &endptr, 10);
-            /* assume if no digits were found it is a request to enable it */
-            if (buf == endptr)
-                s->overrun_nonfatal = 1;
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "ttl", p)) {
-            s->ttl = strtol(buf, NULL, 10);
-            if (s->ttl < 0 || s->ttl > 255) {
-                av_log(h, AV_LOG_ERROR, "ttl(%d) should be in range [0,255]\n", s->ttl);
-                ret = AVERROR(EINVAL);
-                goto fail;
-            }
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "udplite_coverage", p)) {
-            s->udplite_coverage = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "localport", p)) {
-            s->local_port = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "pkt_size", p)) {
-            s->pkt_size = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "buffer_size", p)) {
-            s->buffer_size = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "connect", p)) {
-            s->is_connected = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "dscp", p)) {
-            s->dscp = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "fifo_size", p)) {
-            s->circular_buffer_size = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "bitrate", p)) {
-            s->bitrate = strtoll(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "burst_bits", p)) {
-            s->burst_bits = strtoll(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) {
-            av_freep(&s->localaddr);
-            s->localaddr = av_strdup(buf);
-            if (!s->localaddr) {
-                ret = AVERROR(ENOMEM);
-                goto fail;
-            }
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "sources", p)) {
-            if ((ret = ff_ip_parse_sources(h, buf, &s->filters)) < 0)
-                goto fail;
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "block", p)) {
-            if ((ret = ff_ip_parse_blocks(h, buf, &s->filters)) < 0)
-                goto fail;
-        }
-        if (!is_output && av_find_info_tag(buf, sizeof(buf), "timeout", p))
-            s->timeout = strtol(buf, NULL, 10);
-        if (is_output && av_find_info_tag(buf, sizeof(buf), "broadcast", p))
-            s->is_broadcast = strtol(buf, NULL, 10);
+        ret = ff_parse_opts_from_query_string(s, p, 1);
+        if (ret < 0)
+            goto fail;
     }
     if (!HAVE_PTHREAD_CANCEL) {
         int64_t      optvals[] = {s->overrun_nonfatal, s->bitrate, s->circular_buffer_size};
@@ -800,6 +724,15 @@ static int udp_open(URLContext *h, const char *uri, int flags)
                        "on this build (pthread support is required)\n", optnames[i]);
         }
     }
+    if (s->sources) {
+        if ((ret = ff_ip_parse_sources(h, s->sources, &s->filters)) < 0)
+            goto fail;
+    }
+    if (s->block) {
+        if ((ret = ff_ip_parse_blocks(h, s->block, &s->filters)) < 0)
+            goto fail;
+    }
+
     /* handling needed to support options picking from both AVOption and URL */
     s->circular_buffer_size *= 188;
     if (flags & AVIO_FLAG_WRITE) {
-- 
2.49.1


>From c899a1e142c03ab0c1d418d7fe61cb2d80d1085d Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Mon, 25 Aug 2025 01:52:00 +0200
Subject: [PATCH 06/10] avformat/tcp: use ff_parse_opts_from_query_string() to
 set URL parameters

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 doc/protocols.texi |  6 +++++-
 libavformat/tcp.c  | 36 ++++--------------------------------
 2 files changed, 9 insertions(+), 33 deletions(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index b4b6c25579..a329f59fe3 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -1913,7 +1913,11 @@ tcp://@var{hostname}:@var{port}[?@var{options}]
 @end example
 
 @var{options} contains a list of &-separated options of the form
-@var{key}=@var{val}.
+@var{key}=@var{val}. Standard percent-encoding (and using the plus sign for
+space) can be used to escape keys and values.
+
+Options can also can be specified via command line options (or in code via
+@code{AVOption}s).
 
 The list of supported options follows.
 
diff --git a/libavformat/tcp.c b/libavformat/tcp.c
index c286698d33..ce9f69a50b 100644
--- a/libavformat/tcp.c
+++ b/libavformat/tcp.c
@@ -25,6 +25,7 @@
 #include "libavutil/opt.h"
 #include "libavutil/time.h"
 
+#include "internal.h"
 #include "network.h"
 #include "os_support.h"
 #include "url.h"
@@ -151,7 +152,6 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
     int port, fd = -1;
     TCPContext *s = h->priv_data;
     const char *p;
-    char buf[256];
     int ret;
     char hostname[1024],proto[1024],path[1024];
     char portstr[10];
@@ -167,37 +167,9 @@ static int tcp_open(URLContext *h, const char *uri, int flags)
     }
     p = strchr(uri, '?');
     if (p) {
-        if (av_find_info_tag(buf, sizeof(buf), "listen", p)) {
-            char *endptr = NULL;
-            s->listen = strtol(buf, &endptr, 10);
-            /* assume if no digits were found it is a request to enable it */
-            if (buf == endptr)
-                s->listen = 1;
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "local_port", p)) {
-            av_freep(&s->local_port);
-            s->local_port = av_strdup(buf);
-            if (!s->local_port)
-                return AVERROR(ENOMEM);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "local_addr", p)) {
-            av_freep(&s->local_addr);
-            s->local_addr = av_strdup(buf);
-            if (!s->local_addr)
-                return AVERROR(ENOMEM);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
-            s->rw_timeout = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "listen_timeout", p)) {
-            s->listen_timeout = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "tcp_nodelay", p)) {
-            s->tcp_nodelay = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "tcp_keepalive", p)) {
-            s->tcp_keepalive = strtol(buf, NULL, 10);
-        }
+        int ret = ff_parse_opts_from_query_string(s, p, 1);
+        if (ret < 0)
+            return ret;
     }
     if (s->rw_timeout >= 0) {
         s->open_timeout =
-- 
2.49.1


>From cf1b430dba68d5854c452cb8af33710cbf0fe474 Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Mon, 25 Aug 2025 02:09:26 +0200
Subject: [PATCH 07/10] avformat/sctp: use ff_parse_opts_from_query_string() to
 set URL parameters

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 doc/protocols.texi | 10 +++++++++-
 libavformat/sctp.c |  8 +++-----
 2 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index a329f59fe3..563aee6cc7 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -1535,7 +1535,15 @@ The accepted URL syntax is:
 sctp://@var{host}:@var{port}[?@var{options}]
 @end example
 
-The protocol accepts the following options:
+@var{options} contains a list of &-separated options of the form
+@var{key}=@var{val}. Standard percent-encoding (and using the plus sign for
+space) can be used to escape keys and values.
+
+Options can also can be specified via command line options (or in code via
+@code{AVOption}s).
+
+The list of supported options follows.
+
 @table @option
 @item listen
 If set to any value, listen for an incoming connection. Outgoing connection is done by default.
diff --git a/libavformat/sctp.c b/libavformat/sctp.c
index 9d9e90097e..4122fbe312 100644
--- a/libavformat/sctp.c
+++ b/libavformat/sctp.c
@@ -185,7 +185,6 @@ static int sctp_open(URLContext *h, const char *uri, int flags)
     int fd         = -1;
     SCTPContext *s = h->priv_data;
     const char *p;
-    char buf[256];
     int ret;
     char hostname[1024], proto[1024], path[1024];
     char portstr[10];
@@ -201,10 +200,9 @@ static int sctp_open(URLContext *h, const char *uri, int flags)
 
     p = strchr(uri, '?');
     if (p) {
-        if (av_find_info_tag(buf, sizeof(buf), "listen", p))
-            s->listen = 1;
-        if (av_find_info_tag(buf, sizeof(buf), "max_streams", p))
-            s->max_streams = strtol(buf, NULL, 10);
+        ret = ff_parse_opts_from_query_string(s, p, 0);
+        if (ret < 0)
+            return ret;
     }
 
     hints.ai_family   = AF_UNSPEC;
-- 
2.49.1


>From 3aa50f4a5ec4298bc488cec4c9fcb332ae5ae891 Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Mon, 25 Aug 2025 21:35:40 +0200
Subject: [PATCH 08/10] avformat/rtpproto: use
 ff_parse_opts_from_query_string() to set URL parameters

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 doc/protocols.texi     |  8 +++--
 libavformat/rtpproto.c | 69 +++++++++---------------------------------
 2 files changed, 21 insertions(+), 56 deletions(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index 563aee6cc7..3d9685ed6d 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -1162,9 +1162,13 @@ rtp://@var{hostname}[:@var{port}][?@var{options}]
 @var{port} specifies the RTP port to use.
 
 @var{options} contains a list of &-separated options of the form
-@var{key}=@var{val}.
+@var{key}=@var{val}. Standard percent-encoding (and using the plus sign for
+space) can be used to escape keys and values.
 
-The following options are supported:
+Options can also can be specified via command line options (or in code via
+@code{AVOption}s).
+
+The list of supported options follows.
 
 @table @option
 
diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c
index 69879b7fa8..c4309c19e4 100644
--- a/libavformat/rtpproto.c
+++ b/libavformat/rtpproto.c
@@ -29,6 +29,7 @@
 #include "libavutil/avstring.h"
 #include "libavutil/opt.h"
 #include "avformat.h"
+#include "internal.h"
 #include "rtp.h"
 #include "rtpproto.h"
 #include "url.h"
@@ -232,8 +233,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
     RTPContext *s = h->priv_data;
     AVDictionary *fec_opts = NULL;
     int rtp_port;
-    char hostname[256], include_sources[1024] = "", exclude_sources[1024] = "";
-    char *sources = include_sources, *block = exclude_sources;
+    char hostname[256];
     char *fec_protocol = NULL;
     char buf[1024];
     char path[1024];
@@ -250,58 +250,17 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
 
     p = strchr(uri, '?');
     if (p) {
-        if (av_find_info_tag(buf, sizeof(buf), "ttl", p)) {
-            s->ttl = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "rtcpport", p)) {
-            s->rtcp_port = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "localport", p)) {
-            s->local_rtpport = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "localrtpport", p)) {
-            s->local_rtpport = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "localrtcpport", p)) {
-            s->local_rtcpport = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "pkt_size", p)) {
-            s->pkt_size = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "connect", p)) {
-            s->connect = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "write_to_source", p)) {
-            s->write_to_source = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "dscp", p)) {
-            s->dscp = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "timeout", p)) {
-            s->rw_timeout = strtol(buf, NULL, 10);
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "sources", p)) {
-            av_strlcpy(include_sources, buf, sizeof(include_sources));
-            ff_ip_parse_sources(h, buf, &s->filters);
-        } else {
-            ff_ip_parse_sources(h, s->sources, &s->filters);
-            sources = s->sources;
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "block", p)) {
-            av_strlcpy(exclude_sources, buf, sizeof(exclude_sources));
-            ff_ip_parse_blocks(h, buf, &s->filters);
-        } else {
-            ff_ip_parse_blocks(h, s->block, &s->filters);
-            block = s->block;
-        }
-        if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) {
-            av_freep(&s->localaddr);
-            s->localaddr = av_strdup(buf);
-            if (!s->localaddr) {
-                ret = AVERROR(ENOMEM);
-                goto fail;
-            }
-        }
+        ret = ff_parse_opts_from_query_string(s, p, 1);
+        if (ret < 0)
+            goto fail;
+    }
+    if (s->sources) {
+        if ((ret = ff_ip_parse_sources(h, s->sources, &s->filters)) < 0)
+            goto fail;
+    }
+    if (s->block) {
+        if ((ret = ff_ip_parse_blocks(h, s->block, &s->filters)) < 0)
+            goto fail;
     }
     if (s->rw_timeout >= 0)
         h->rw_timeout = s->rw_timeout;
@@ -334,6 +293,8 @@ static int rtp_open(URLContext *h, const char *uri, int flags)
     }
 
     for (i = 0; i < max_retry_count; i++) {
+        const char *sources = s->sources ? s->sources : "";
+        const char *block = s->block ? s->block : "";
         build_udp_url(s, buf, sizeof(buf),
                       hostname, s->localaddr, rtp_port, s->local_rtpport,
                       sources, block);
-- 
2.49.1


>From ee7094027e66b8b8d9dbfdd80e8e94da54307712 Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Tue, 26 Aug 2025 00:23:16 +0200
Subject: [PATCH 09/10] avformat/tls: use ff_parse_opts_from_query_string() to
 set URL parameters

Note that this changes the code to work the same way as other protocols where
an URL parameter can override an AVOption.

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 doc/protocols.texi        | 10 +++++++--
 libavformat/tls.c         | 46 +++++----------------------------------
 libavformat/tls_mbedtls.c | 14 ------------
 3 files changed, 14 insertions(+), 56 deletions(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index 3d9685ed6d..9f88f005b9 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -1984,8 +1984,14 @@ The required syntax for a TLS/SSL url is:
 tls://@var{hostname}:@var{port}[?@var{options}]
 @end example
 
-The following parameters can be set via command line options
-(or in code via @code{AVOption}s):
+@var{options} contains a list of &-separated options of the form
+@var{key}=@var{val}. Standard percent-encoding (and using the plus sign for
+space) can be used to escape keys and values.
+
+Options can also can be specified via command line options (or in code via
+@code{AVOption}s).
+
+The list of supported options follows.
 
 @table @option
 
diff --git a/libavformat/tls.c b/libavformat/tls.c
index bd9c05e6dc..2cf0c99a6b 100644
--- a/libavformat/tls.c
+++ b/libavformat/tls.c
@@ -31,41 +31,6 @@
 #include "libavutil/mem.h"
 #include "libavutil/parseutils.h"
 
-static int set_options(TLSShared *c, const char *uri)
-{
-    char buf[1024];
-    const char *p = strchr(uri, '?');
-    if (!p)
-        return 0;
-
-    if (!c->ca_file && av_find_info_tag(buf, sizeof(buf), "cafile", p)) {
-        c->ca_file = av_strdup(buf);
-        if (!c->ca_file)
-            return AVERROR(ENOMEM);
-    }
-
-    if (!c->verify && av_find_info_tag(buf, sizeof(buf), "verify", p)) {
-        char *endptr = NULL;
-        c->verify = strtol(buf, &endptr, 10);
-        if (buf == endptr)
-            c->verify = 1;
-    }
-
-    if (!c->cert_file && av_find_info_tag(buf, sizeof(buf), "cert", p)) {
-        c->cert_file = av_strdup(buf);
-        if (!c->cert_file)
-            return AVERROR(ENOMEM);
-    }
-
-    if (!c->key_file && av_find_info_tag(buf, sizeof(buf), "key", p)) {
-        c->key_file = av_strdup(buf);
-        if (!c->key_file)
-            return AVERROR(ENOMEM);
-    }
-
-    return 0;
-}
-
 int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AVDictionary **options)
 {
     int port;
@@ -77,17 +42,18 @@ int ff_tls_open_underlying(TLSShared *c, URLContext *parent, const char *uri, AV
     int use_proxy;
     int ret;
 
-    ret = set_options(c, uri);
-    if (ret < 0)
-        return ret;
+    p = strchr(uri, '?');
+    if (p) {
+        ret = ff_parse_opts_from_query_string(c, p, 1);
+        if (ret < 0)
+            return ret;
+    }
 
     if (c->listen && !c->is_dtls)
         snprintf(opts, sizeof(opts), "?listen=1");
 
     av_url_split(NULL, 0, NULL, 0, c->underlying_host, sizeof(c->underlying_host), &port, NULL, 0, uri);
 
-    p = strchr(uri, '?');
-
     if (!p) {
         p = opts;
     } else {
diff --git a/libavformat/tls_mbedtls.c b/libavformat/tls_mbedtls.c
index 2bcd3cca63..8aa142b9d2 100644
--- a/libavformat/tls_mbedtls.c
+++ b/libavformat/tls_mbedtls.c
@@ -174,17 +174,6 @@ static void handle_handshake_error(URLContext *h, int ret)
     }
 }
 
-static void parse_options(TLSContext *tls_ctxc, const char *uri)
-{
-    char buf[1024];
-    const char *p = strchr(uri, '?');
-    if (!p)
-        return;
-
-    if (!tls_ctxc->priv_key_pw && av_find_info_tag(buf, sizeof(buf), "key_password", p))
-        tls_ctxc->priv_key_pw = av_strdup(buf);
-}
-
 static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options)
 {
     TLSContext *tls_ctx = h->priv_data;
@@ -192,9 +181,6 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op
     uint32_t verify_res_flags;
     int ret;
 
-    // parse additional options
-    parse_options(tls_ctx, uri);
-
     if ((ret = ff_tls_open_underlying(shr, h, uri, options)) < 0)
         goto fail;
 
-- 
2.49.1


>From 594ea1b6017eed768a7de8422a068b87bffa6072 Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Wed, 3 Sep 2025 00:55:46 +0200
Subject: [PATCH 10/10] avformat/tls_openssl: initialize underlying protocol
 early for dtls_start()

The same way we do with TLS, so all tls URL options will be properly supported.

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 doc/protocols.texi        | 13 ++++++++++---
 libavformat/tls_openssl.c | 15 ++++++++-------
 2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/doc/protocols.texi b/doc/protocols.texi
index 9f88f005b9..b74383122a 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -2052,12 +2052,19 @@ Datagram Transport Layer Security (DTLS)
 
 The required syntax for a DTLS URL is:
 @example
-dtls://@var{hostname}:@var{port}
+dtls://@var{hostname}:@var{port}[?@var{options}]
 @end example
 
+@var{options} contains a list of &-separated options of the form
+@var{key}=@var{val}. Standard percent-encoding (and using the plus sign for
+space) can be used to escape keys and values.
+
+Options can also can be specified via command line options (or in code via
+@code{AVOption}s).
+
 DTLS shares most options with TLS, but operates over UDP instead of TCP.
-The following parameters can be set via command line options
-(or in code via @code{AVOption}s):
+
+The list of supported options follows.
 
 @table @option
 
diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c
index edfd657a3f..f724153da1 100644
--- a/libavformat/tls_openssl.c
+++ b/libavformat/tls_openssl.c
@@ -749,6 +749,13 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
     av_assert0(s);
     s->is_dtls = 1;
 
+    if (!c->tls_shared.external_sock) {
+        if ((ret = ff_tls_open_underlying(&c->tls_shared, h, url, options)) < 0) {
+            av_log(c, AV_LOG_ERROR, "Failed to connect %s\n", url);
+            return ret;
+        }
+    }
+
     c->ctx = SSL_CTX_new(s->listen ? DTLS_server_method() : DTLS_client_method());
     if (!c->ctx) {
         ret = AVERROR(ENOMEM);
@@ -801,13 +808,6 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
     DTLS_set_link_mtu(c->ssl, s->mtu);
     init_bio_method(h);
 
-    if (!c->tls_shared.external_sock) {
-        if ((ret = ff_tls_open_underlying(&c->tls_shared, h, url, options)) < 0) {
-            av_log(c, AV_LOG_ERROR, "Failed to connect %s\n", url);
-            return ret;
-        }
-    }
-
     /* This seems to be necessary despite explicitly setting client/server method above. */
     if (s->listen)
         SSL_set_accept_state(c->ssl);
@@ -838,6 +838,7 @@ static int dtls_start(URLContext *h, const char *url, int flags, AVDictionary **
 
     ret = 0;
 fail:
+    tls_close(h);
     return ret;
 }
 
-- 
2.49.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2025-09-19 19:08 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-19 19:07 [FFmpeg-devel] [PATCH] Use AVOption calls to parse URL options (PR #20555) Marton Balint via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git