Commit 1ae485ba98 for ffmpeg

commit 1ae485ba98381cbb2505e75a0666c40c77f3d8de
Author: Felipe Araújo <felipdsa21@outlook.com>
Date:   Sat Oct 10 03:16:05 2026 -0300

    avformat/file: add Windows handle protocol

    Like the fd protocol, but takes a raw Windows HANDLE instead of a CRT file descriptor.
    On Windows, CRT file descriptors cannot be reliably passed to child processes, so
    HANDLEs need to be used instead.

    Signed-off-by: Felipe Araújo <felipdsa21@outlook.com>

diff --git a/configure b/configure
index f542d4e0d6..f316113785 100755
--- a/configure
+++ b/configure
@@ -2534,6 +2534,7 @@ SYSTEM_FUNCS="
     mmap
     mprotect
     nanosleep
+    open_osfhandle
     PeekNamedPipe
     posix_memalign
     prctl
@@ -4097,6 +4098,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 open_osfhandle GetStdHandle"

 # external library protocols
 libamqp_protocol_deps="librabbitmq"
@@ -6899,6 +6901,7 @@ check_func  usleep

 check_func_headers conio.h kbhit
 check_func_headers io.h setmode
+check_func_headers io.h _open_osfhandle && enable open_osfhandle
 check_func_headers lzo/lzo1x.h lzo1x_999_compress
 check_func_headers mach/mach_time.h mach_absolute_time
 check_func_headers setjmp.h siglongjmp
diff --git a/doc/protocols.texi b/doc/protocols.texi
index dbb1f166c7..1d43fa6327 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -419,6 +419,40 @@ 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.
+It doesn't support passing the handle via URL.
+
+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.
+
+The supplied handle must already be valid in the calling process.
+When launching @command{ffmpeg}, arrange for the child process to
+inherit the handle before passing its numeric value with
+@option{handle}. Handles opened with @code{FILE_FLAG_OVERLAPPED}
+are not supported. Closing the protocol does not close the
+caller's original handle.
+@end table
+
 @section http

 HTTP (Hyper Text Transfer Protocol).
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 512e7ec214..02fb2b92f6 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -738,6 +738,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 fb344aef93..bb99198a6a 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[] = {
@@ -223,7 +226,7 @@ static int fd_dup(URLContext *h, int oldfd)
 }
 #endif

-#if CONFIG_FD_PROTOCOL || CONFIG_PIPE_PROTOCOL
+#if CONFIG_FD_PROTOCOL || CONFIG_PIPE_PROTOCOL || CONFIG_WINDOWS_HANDLE_PROTOCOL
 /* Without a check, avio_check() would open the descriptor, rewinding seekable
  * output. It cannot pass the option selecting the descriptor, so just fail. */
 static int fd_check(URLContext *h, int mask)
@@ -544,6 +547,85 @@ 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 (handle == INVALID_HANDLE_VALUE || !handle)
+            return AVERROR(EBADF);
+    }
+
+    if (!DuplicateHandle(GetCurrentProcess(), handle,
+                         GetCurrentProcess(), &dup_handle,
+                         0, FALSE, DUPLICATE_SAME_ACCESS))
+        return GetLastError() == ERROR_INVALID_HANDLE ? AVERROR(EBADF)
+                                                      : AVERROR(EIO);
+
+    fd = _open_osfhandle((intptr_t)dup_handle, O_BINARY);
+    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,
+    .url_check           = fd_check,
+    .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 367b40ee2c..aea09da498 100644
--- a/libavformat/protocols.c
+++ b/libavformat/protocols.c
@@ -80,6 +80,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 35d796d318..1b079ebce8 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -31,7 +31,7 @@

 #include "version_major.h"

-#define LIBAVFORMAT_VERSION_MINOR   8
+#define LIBAVFORMAT_VERSION_MINOR   9
 #define LIBAVFORMAT_VERSION_MICRO 100

 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \