[PR] avformat/file: add Windows handle protocol (PR #23678)

felipdsa21 via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178297438209.59.6983195671810564745@29965ddac10e>
PR #23678 opened by felipdsa21
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23678
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23678.patch

On Windows, CRT file descriptors cannot be reliably passed to child processes, so HANDLEs need to be used instead. This adds a handle protocol so ffmpeg.exe can accept a HANDLE directly as input or output.

The new protocol duplicates the given handle, or falls back to the standard input or output handle, then converts it to a CRT fd, and reuses the existing fd/file protocol code for I/O.

Used the fd protocol's own addition as reference: https://code.ffmpeg.org/FFmpeg/FFmpeg/compare/e9fe1634d4...195ccf7ea8

Also includes a small fix removing pipe and fd's `url_check` hook, since it treated the URL as a filesystem path, which is incorrect for these protocols and gave bogus results.


From cc82b086e500947e69e184742136c7adc06300f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felipe=20Ara=C3=BAjo?= <[email protected]>
Date: Thu, 2 Jul 2026 02:05:58 -0300
Subject: [PATCH 1/3] avformat/file: remove broken url_check on pipe and fd
 protocols
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

file_check treats the URL as a file path, which is incorrect for the pipe and fd protocols.

Signed-off-by: Felipe Araújo <[email protected]>
---
 libavformat/file.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libavformat/file.c b/libavformat/file.c
index 71d30a25c4..e58f00a9e5 100644
--- a/libavformat/file.c
+++ b/libavformat/file.c
@@ -481,7 +481,6 @@ const URLProtocol ff_pipe_protocol = {
     .url_write           = file_write,
     .url_close           = file_close,
     .url_get_file_handle = file_get_handle,
-    .url_check           = file_check,
     .priv_data_size      = sizeof(FileContext),
     .priv_data_class     = &pipe_class,
     .default_whitelist   = "crypto,data"
@@ -527,7 +526,6 @@ const URLProtocol ff_fd_protocol = {
     .url_seek            = file_seek,
     .url_close           = file_close,
     .url_get_file_handle = file_get_handle,
-    .url_check           = file_check,
     .priv_data_size      = sizeof(FileContext),
     .priv_data_class     = &fd_class,
     .default_whitelist   = "crypto,data"
@@ -687,7 +685,6 @@ const URLProtocol ff_android_content_protocol = {
     .url_seek            = file_seek,
     .url_close           = file_close,
     .url_get_file_handle = file_get_handle,
-    .url_check           = NULL,
     .priv_data_size      = sizeof(FileContext),
     .priv_data_class     = &android_content_class,
 };
-- 
2.52.0


From 4700990c9bf56c299b3ae1f444e7afa627c1689b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felipe=20Ara=C3=BAjo?= <[email protected]>
Date: Thu, 2 Jul 2026 02:05:58 -0300
Subject: [PATCH 2/3] avformat/file: add Windows handle protocol
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Like the fd protocol, but takes a raw Windows HANDLE instead of a CRT file descriptor.

Signed-off-by: Felipe Araújo <[email protected]>
---
 configure               |  1 +
 doc/protocols.texi      | 26 ++++++++++++++
 libavformat/Makefile    |  1 +
 libavformat/file.c      | 79 +++++++++++++++++++++++++++++++++++++++++
 libavformat/protocols.c |  1 +
 libavformat/version.h   |  2 +-
 6 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 4573d69f28..3fa3011798 100755
--- a/configure
+++ b/configure
@@ -4124,6 +4124,7 @@ unix_protocol_deps="sys_un_h"
 unix_protocol_select="network"
 ipfs_gateway_protocol_select="https_protocol"
 ipns_gateway_protocol_select="https_protocol"
+windows_handle_protocol_deps="windows_h"
 
 # external library protocols
 libamqp_protocol_deps="librabbitmq"
diff --git a/doc/protocols.texi b/doc/protocols.texi
index 5f8cc8094d..8ab61f4c08 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -419,6 +419,32 @@ Gophers protocol.
 
 The Gopher protocol with TLS encapsulation.
 
+@section handle
+
+Windows handle access protocol.
+
+The accepted syntax is:
+@example
+handle: -handle @var{handle}
+@end example
+
+If @option{handle} is not specified, by default the output handle will be
+used for writing, input handle for reading. This protocol is only available
+on Windows. It has seek support if the handle corresponds to a regular file.
+
+This protocol accepts the following options:
+
+@table @option
+@item blocksize
+Set I/O operation maximum block size, in bytes. Default value is
+@code{INT_MAX}, which results in not limiting the requested block size.
+Setting this value reasonably low improves user termination request reaction
+time, which is valuable if data transmission is slow.
+
+@item handle
+Set Windows handle.
+@end table
+
 @section http
 
 HTTP (Hyper Text Transfer Protocol).
diff --git a/libavformat/Makefile b/libavformat/Makefile
index b244eaabf9..d7d1046382 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -732,6 +732,7 @@ OBJS-$(CONFIG_DTLS_PROTOCOL)             += tls.o $(TLS-OBJS-yes)
 OBJS-$(CONFIG_UDP_PROTOCOL)              += udp.o ip.o
 OBJS-$(CONFIG_UDPLITE_PROTOCOL)          += udp.o ip.o
 OBJS-$(CONFIG_UNIX_PROTOCOL)             += unix.o
+OBJS-$(CONFIG_WINDOWS_HANDLE_PROTOCOL)   += file.o
 
 # external library protocols
 OBJS-$(CONFIG_LIBAMQP_PROTOCOL)          += libamqp.o
diff --git a/libavformat/file.c b/libavformat/file.c
index e58f00a9e5..6c80c4c815 100644
--- a/libavformat/file.c
+++ b/libavformat/file.c
@@ -103,6 +103,9 @@ typedef struct FileContext {
 #if HAVE_DIRENT_H
     DIR *dir;
 #endif
+#if CONFIG_WINDOWS_HANDLE_PROTOCOL
+    int64_t handle;
+#endif
 } FileContext;
 
 static const AVOption file_options[] = {
@@ -533,6 +536,82 @@ const URLProtocol ff_fd_protocol = {
 
 #endif /* CONFIG_FD_PROTOCOL */
 
+#if CONFIG_WINDOWS_HANDLE_PROTOCOL
+
+#include <windows.h>
+
+static const AVOption windows_handle_options[] = {
+    { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+    { "handle", "set Windows handle", offsetof(FileContext, handle), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+    { NULL }
+};
+
+static const AVClass windows_handle_class = {
+    .class_name = "windows_handle",
+    .item_name  = av_default_item_name,
+    .option     = windows_handle_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+static int windows_handle_open(URLContext *h, const char *filename, int flags)
+{
+    FileContext *c = h->priv_data;
+    HANDLE handle = (HANDLE)(intptr_t)c->handle;
+    HANDLE dup_handle;
+    int fd;
+    struct stat st;
+
+    if (strcmp(filename, "handle:") != 0) {
+        av_log(h, AV_LOG_ERROR, "Doesn't support passing Windows handles via URL,"
+                                " please set it via -handle {num}\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (handle == INVALID_HANDLE_VALUE || !handle) {
+        if (flags & AVIO_FLAG_WRITE)
+            handle = GetStdHandle(STD_OUTPUT_HANDLE);
+        else
+            handle = GetStdHandle(STD_INPUT_HANDLE);
+    }
+
+    if (!DuplicateHandle(GetCurrentProcess(), handle,
+                         GetCurrentProcess(), &dup_handle,
+                         0, FALSE, DUPLICATE_SAME_ACCESS))
+        return AVERROR(EIO);
+
+    fd = _open_osfhandle((intptr_t)dup_handle, O_BINARY |
+                         ((flags & AVIO_FLAG_WRITE) ? O_RDWR : O_RDONLY));
+    if (fd == -1) {
+        CloseHandle(dup_handle);
+        return AVERROR(errno);
+    }
+
+    if (fstat(fd, &st) < 0) {
+        close(fd);
+        return AVERROR(errno);
+    }
+
+    c->fd = fd;
+    h->is_streamed = !(S_ISREG(st.st_mode) || S_ISBLK(st.st_mode));
+
+    return 0;
+}
+
+const URLProtocol ff_windows_handle_protocol = {
+    .name                = "handle",
+    .url_open            = windows_handle_open,
+    .url_read            = file_read,
+    .url_write           = file_write,
+    .url_seek            = file_seek,
+    .url_close           = file_close,
+    .url_get_file_handle = file_get_handle,
+    .priv_data_size      = sizeof(FileContext),
+    .priv_data_class     = &windows_handle_class,
+    .default_whitelist   = "crypto,data"
+};
+
+#endif /* CONFIG_WINDOWS_HANDLE_PROTOCOL */
+
 #if CONFIG_ANDROID_CONTENT_PROTOCOL
 #include <jni.h>
 #include "libavcodec/ffjni.h"
diff --git a/libavformat/protocols.c b/libavformat/protocols.c
index 257b41970a..db5bc37aef 100644
--- a/libavformat/protocols.c
+++ b/libavformat/protocols.c
@@ -79,6 +79,7 @@ extern const URLProtocol ff_libsmbclient_protocol;
 extern const URLProtocol ff_libzmq_protocol;
 extern const URLProtocol ff_ipfs_gateway_protocol;
 extern const URLProtocol ff_ipns_gateway_protocol;
+extern const URLProtocol ff_windows_handle_protocol;
 
 #include "libavformat/protocol_list.c"
 
diff --git a/libavformat/version.h b/libavformat/version.h
index 7ff1483912..af7d0a1024 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -31,7 +31,7 @@
 
 #include "version_major.h"
 
-#define LIBAVFORMAT_VERSION_MINOR   3
+#define LIBAVFORMAT_VERSION_MINOR   4
 #define LIBAVFORMAT_VERSION_MICRO 100
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
-- 
2.52.0


From 725c210f2d7f01d5ae53999ca76f5acf0873b819 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felipe=20Ara=C3=BAjo?= <[email protected]>
Date: Thu, 2 Jul 2026 02:05:59 -0300
Subject: [PATCH 3/3] fftools/ffmpeg_demux: disable stdin interaction for
 Windows handle protocol
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Felipe Araújo <[email protected]>
---
 fftools/ffmpeg_demux.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c
index 38bd580bfc..cc98a0f8e0 100644
--- a/fftools/ffmpeg_demux.c
+++ b/fftools/ffmpeg_demux.c
@@ -2078,6 +2078,7 @@ int ifile_open(const OptionsContext *o, const char *filename, Scheduler *sch)
 
     stdin_interaction &= strncmp(filename, "pipe:", 5) &&
                          strcmp(filename, "fd:") &&
+                         strcmp(filename, "handle:") &&
                          strcmp(filename, "/dev/stdin");
 
     /* get default parameters from command line */
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.