PR #24809 opened by Bruno Bezerra Trigueiro (kobzar)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24809
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24809.patch

# Summary of changes

With -prefer_libcurl 1, HLS/DASH/IMF segments are opened with libcurl's 
defaults for -http_version, -tls_verify and -ca_file, because 
ffio_copy_url_options() doesn't copy them from the playlist connection. The 
client certificate (-cert_file/-key_file) is lost the same way.

Adding the names to the list, as suggested in the issue, breaks two cases, so 
http_version is handled separately:

- native http has a read-only `http_version` option holding the response 
version. Copying it makes every segment open fail with `Error setting option 
http_version to value 1.1`, so plain HLS over native http stops working.
- libcurl's `http_version` is an INT and av_opt_get() prints the number, which 
parses back as a constant name: 1.1 (2) becomes "2" (HTTP/2) and 2 (3) becomes 
"3" (HTTP/3). It is copied by constant name instead, and only when 
prefer_libcurl is set, since with a `libcurl:` prefix the segments go through 
native http.

Tested with a local HLS server that logs each request line, over http, https 
with a private CA, and https requiring a client certificate:

- `-prefer_libcurl 1 -http_version 1.0/1.1/2`: segments use the same version as 
the playlist (1.1 without an h2c upgrade, 2 with one).
- `-tls_verify 0`, `-ca_file`, `-cert_file`/`-key_file`: segments open; before 
they failed.
- AES-128 playlist: the key request gets the version too.
- plain native HLS and `libcurl:` URLs behave as before.
- FATE hls/dash/imf/url tests pass for each commit.

This touches the same list as #24565. With both applied, `-tls_verify 0` and 
`-ca_file` also reach the segments of a native https playlist, which currently 
fail with `certificate verify failed`.

libcurl's default protocol whitelist (`http,https,libcurl`) doesn't include 
`crypto`, so AES-128 HLS with -prefer_libcurl 1 still fails unless 
-protocol_whitelist is given. I left that for a separate change.

Fixes: #24752

cc @kasper93 @haasn



>From d224fff64e65573e3111a98d7d52c2ed00eda447 Mon Sep 17 00:00:00 2001
From: Bruno Trigueiro <[email protected]>
Date: Wed, 30 Sep 2026 09:25:51 -0300
Subject: [PATCH 1/2] avformat/aviobuf: copy http_version, tls_verify and
 ca_file to children

HLS, DASH and IMF open their segment connections with the options
ffio_copy_url_options() copies from the playlist connection. With
-prefer_libcurl 1 the segments used libcurl's default HTTP version
and TLS settings, whatever -http_version, -tls_verify or -ca_file
were set to.

Add tls_verify and ca_file to the list. Native http does not expose
the options of its tls child, so nothing changes when the playlist
comes from native https.

http_version needs more care. Native http has a read-only option of
that name holding the response version, and setting it on a segment
connection makes the open fail. libcurl's option is an INT whose
named constants overlap with its values ("1.1" is 2, which reads
back as the "2" constant), so copy it by constant name, and only
when prefer_libcurl is set: with an explicit libcurl: prefix the
segments are opened by native http.

Fixes: #24752
Signed-off-by: Bruno Trigueiro <[email protected]>
---
 libavformat/aviobuf.c | 30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)

diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
index edbbc5ffd6..3a706b543e 100644
--- a/libavformat/aviobuf.c
+++ b/libavformat/aviobuf.c
@@ -950,10 +950,36 @@ void ffio_write_lines(AVIOContext *s, const unsigned char 
*buf, int size,
     }
 }
 
+/* Native http has a read-only option of the same name, so only libcurl 
children
+ * get it. The numeric value would parse as another constant ("1.1" is 2). */
+static int copy_http_version(AVIOContext *pb, AVDictionary **avio_opts)
+{
+    const AVOption *o, *c = NULL;
+    int64_t prefer_libcurl, version;
+    void *target;
+
+    o = av_opt_find2(pb, "http_version", NULL, 0, AV_OPT_SEARCH_CHILDREN, 
&target);
+    if (!o || !target || o->type != AV_OPT_TYPE_INT || !o->unit ||
+        o->flags & AV_OPT_FLAG_READONLY)
+        return 0;
+    if (av_opt_get_int(pb, "prefer_libcurl", AV_OPT_SEARCH_CHILDREN, 
&prefer_libcurl) < 0 ||
+        !prefer_libcurl ||
+        av_opt_get_int(target, "http_version", 0, &version) < 0)
+        return 0;
+
+    while ((c = av_opt_next(target, c)))
+        if (c->type == AV_OPT_TYPE_CONST && c->unit && !strcmp(c->unit, 
o->unit) &&
+            c->default_val.i64 == version)
+            return av_dict_set(avio_opts, "http_version", c->name, 0);
+
+    return av_dict_set_int(avio_opts, "http_version", version, 0);
+}
+
 int ffio_copy_url_options(AVIOContext* pb, AVDictionary** avio_opts)
 {
     const char *opts[] = {
-        "headers", "user_agent", "cookies", "http_proxy", "referer", 
"rw_timeout", "icy", "prefer_libcurl", NULL };
+        "headers", "user_agent", "cookies", "http_proxy", "referer", 
"rw_timeout", "icy", "prefer_libcurl",
+        "tls_verify", "ca_file", NULL };
     const char **opt = opts;
     uint8_t *buf = NULL;
     int ret = 0;
@@ -971,7 +997,7 @@ int ffio_copy_url_options(AVIOContext* pb, AVDictionary** 
avio_opts)
         opt++;
     }
 
-    return ret;
+    return copy_http_version(pb, avio_opts);
 }
 
 static void update_checksum(AVIOContext *s)
-- 
2.52.0


>From 71a276525d2f324181ec4f7bcea9602efbc8b9ab Mon Sep 17 00:00:00 2001
From: Bruno Trigueiro <[email protected]>
Date: Wed, 30 Sep 2026 09:25:51 -0300
Subject: [PATCH 2/2] avformat/aviobuf: copy cert_file and key_file to children

With -prefer_libcurl 1 and a server that requires a client
certificate, the playlist opened but every segment failed the TLS
handshake, since the certificate and key were not passed on.

Signed-off-by: Bruno Trigueiro <[email protected]>
---
 libavformat/aviobuf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c
index 3a706b543e..4d34819877 100644
--- a/libavformat/aviobuf.c
+++ b/libavformat/aviobuf.c
@@ -979,7 +979,7 @@ int ffio_copy_url_options(AVIOContext* pb, AVDictionary** 
avio_opts)
 {
     const char *opts[] = {
         "headers", "user_agent", "cookies", "http_proxy", "referer", 
"rw_timeout", "icy", "prefer_libcurl",
-        "tls_verify", "ca_file", NULL };
+        "tls_verify", "ca_file", "cert_file", "key_file", NULL };
     const char **opt = opts;
     uint8_t *buf = NULL;
     int ret = 0;
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to