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, \