PR #24997 opened by Zhao Zhili (quink)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24997
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24997.patch

# Summary of changes

Follow up of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24936

ff_network_wait_fd() can only return after its poll() slice, so a
thread blocked in it notices a request from another thread up to
POLLING_TIME late. Add a one-shot channel that a second thread can
signal to end the wait at once, together with a wait function that
polls it next to the socket.

The channel is an eventfd where available and a pipe otherwise. On
Windows neither can be waited on together with sockets, so it is a
loopback datagram socket connected to itself.

Now udp close() takes about 1 ms or less instead of 100 ms.

<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).

Attached filenames must match the sample's filename:

```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->



>From 4ae2f68d249b860c6da022ac5ee68e21a060c9b9 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Thu, 8 Oct 2026 14:37:18 +0800
Subject: [PATCH 1/5] avformat/sapdec: disable the udp circular buffer

sap_fetch_packet() polls the raw socket fd to find pending
announcements and reads them itself to detect stream deletion. With
the circular buffer enabled, the rx thread competes for the same
datagrams and usually reads them first, so poll() seldom reports
POLLIN and the check becomes unreliable.

Pass fifo_size=0 as rtpproto.c does.

Signed-off-by: Zhao Zhili <[email protected]>
---
 libavformat/sapdec.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavformat/sapdec.c b/libavformat/sapdec.c
index 715cd0656a..1be8d3234c 100644
--- a/libavformat/sapdec.c
+++ b/libavformat/sapdec.c
@@ -83,8 +83,8 @@ static int sap_read_header(AVFormatContext *s)
         av_strlcpy(host, "224.2.127.254", sizeof(host));
     }
 
-    ff_url_join(url, sizeof(url), "udp", NULL, host, port, "?localport=%d",
-                port);
+    ff_url_join(url, sizeof(url), "udp", NULL, host, port,
+                "?localport=%d&fifo_size=0", port);
     ret = ffurl_open_whitelist(&sap->ann_fd, url, AVIO_FLAG_READ,
                                &s->interrupt_callback, NULL,
                                s->protocol_whitelist, s->protocol_blacklist, 
NULL);
-- 
2.52.0


>From 2a2d13b3ca7f08464c688feff68c0dfe0369aa2c Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Thu, 8 Oct 2026 01:16:22 +0800
Subject: [PATCH 2/5] avformat/udp: stop the rx thread without pthread_cancel()

The rx thread blocks in recvfrom() and udp_close() cancelled it. That
only works if recvfrom() is a cancellation point and if a cancel
request made while cancellation is disabled is still acted on later.
Darwin loses such a request, so closing an input that is receiving
data hangs in pthread_join(). On Win32 recvfrom() is not a
cancellation point at all, which is why udp_close() needed a separate
shutdown() and CancelIoEx() path.

Wait with ff_network_wait_fd() instead, as other protocols do. Closing
an input that receives no data can take up to POLLING_TIME.

Signed-off-by: Zhao Zhili <[email protected]>
---
 libavformat/udp.c | 41 +++++++++++++----------------------------
 1 file changed, 13 insertions(+), 28 deletions(-)

diff --git a/libavformat/udp.c b/libavformat/udp.c
index 0874d18397..4516dab494 100644
--- a/libavformat/udp.c
+++ b/libavformat/udp.c
@@ -530,32 +530,30 @@ static void *circular_buffer_task_rx( void *_URLContext)
 {
     URLContext *h = _URLContext;
     UDPContext *s = h->priv_data;
-    int old_cancelstate;
 
     ff_thread_setname("udp-rx");
 
-    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
     pthread_mutex_lock(&s->mutex);
-    if (ff_socket_nonblock(s->udp_fd, 0) < 0) {
-        av_log(h, AV_LOG_ERROR, "Failed to set blocking mode");
-        s->circular_buffer_error = AVERROR(EIO);
-        goto end;
-    }
     while(1) {
         UDPQueuedPacketHeader pkt_header;
+        int err = 0;
+
+        if (s->close_req)
+            goto end;
+
         pkt_header.addr_len = sizeof(pkt_header.addr);
 
         pthread_mutex_unlock(&s->mutex);
-        /* Blocking operations are always cancellation points;
-           see "General Information" / "Thread Cancellation Overview"
-           in Single Unix. */
-        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancelstate);
         pkt_header.pkt_size = recvfrom(s->udp_fd, s->tmp + sizeof(pkt_header), 
sizeof(s->tmp) - sizeof(pkt_header), 0, (struct sockaddr *)&pkt_header.addr, 
&pkt_header.addr_len);
-        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_cancelstate);
+        if (pkt_header.pkt_size < 0) {
+            err = ff_neterrno();
+            if (err == AVERROR(EAGAIN))
+                err = ff_network_wait_fd(s->udp_fd, 0);
+        }
         pthread_mutex_lock(&s->mutex);
         if (pkt_header.pkt_size < 0) {
-            if (ff_neterrno() != AVERROR(EAGAIN) && ff_neterrno() != 
AVERROR(EINTR)) {
-                s->circular_buffer_error = ff_neterrno();
+            if (err < 0 && err != AVERROR(EAGAIN) && err != AVERROR(EINTR)) {
+                s->circular_buffer_error = err;
                 goto end;
             }
             continue;
@@ -1096,8 +1094,7 @@ static int udp_close(URLContext *h)
     UDPContext *s = h->priv_data;
 
 #if HAVE_PTHREAD_CANCEL
-    // Request close once writing is finished
-    if (s->thread_started && !(h->flags & AVIO_FLAG_READ)) {
+    if (s->thread_started) {
         pthread_mutex_lock(&s->mutex);
         s->close_req = 1;
         pthread_cond_signal(&s->cond);
@@ -1111,18 +1108,6 @@ static int udp_close(URLContext *h)
 #if HAVE_PTHREAD_CANCEL
     if (s->thread_started) {
         int ret;
-        // Cancel only read, as write has been signaled as success to the user
-        if (h->flags & AVIO_FLAG_READ) {
-#ifdef _WIN32
-            /* recvfrom() is not a cancellation point for win32, so we shutdown
-             * the socket and abort pending IO, subsequent recvfrom() calls
-             * will fail with WSAESHUTDOWN causing the thread to exit. */
-            shutdown(s->udp_fd, SD_RECEIVE);
-            CancelIoEx((HANDLE)(SOCKET)s->udp_fd, NULL);
-#else
-            pthread_cancel(s->circular_buffer_thread);
-#endif
-        }
         ret = pthread_join(s->circular_buffer_thread, NULL);
         if (ret != 0)
             av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", strerror(ret));
-- 
2.52.0


>From 913d78bbdb15c88deba48b235b9bc7738ccbc513 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Thu, 8 Oct 2026 01:19:10 +0800
Subject: [PATCH 3/5] avformat/udp: use the circular buffer whenever threads
 are available

The rx and tx threads no longer use pthread_cancel(), so gate the
circular buffer on HAVE_THREADS instead of HAVE_PTHREAD_CANCEL. This
also drops the w32threads override.

On targets that have threads but no pthread_cancel(), such as Android,
udp inputs now start a receive thread and allocate the default 7*4096
by 188 byte fifo, as on other platforms. Set fifo_size=0 to get the
previous behavior.

Signed-off-by: Zhao Zhili <[email protected]>
---
 libavformat/udp.c | 29 ++++++++++++-----------------
 1 file changed, 12 insertions(+), 17 deletions(-)

diff --git a/libavformat/udp.c b/libavformat/udp.c
index 4516dab494..74c9896e29 100644
--- a/libavformat/udp.c
+++ b/libavformat/udp.c
@@ -60,12 +60,7 @@
 #define IPPROTO_UDPLITE                                  136
 #endif
 
-#if HAVE_W32THREADS
-#undef HAVE_PTHREAD_CANCEL
-#define HAVE_PTHREAD_CANCEL 1
-#endif
-
-#if HAVE_PTHREAD_CANCEL
+#if HAVE_THREADS
 #include "libavutil/thread.h"
 #endif
 
@@ -109,7 +104,7 @@ typedef struct UDPContext {
     int64_t bitrate; /* number of bits to send per second */
     int64_t burst_bits;
     int close_req;
-#if HAVE_PTHREAD_CANCEL
+#if HAVE_THREADS
     pthread_t circular_buffer_thread;
     pthread_mutex_t mutex;
     pthread_cond_t cond;
@@ -146,7 +141,7 @@ static const AVOption options[] = {
     { "ttl",            "Time to live (multicast only)",                   
OFFSET(ttl),            AV_OPT_TYPE_INT,    { .i64 = 16 },     0, 255,     E },
     { "dscp",           "DSCP class for outgoing packets",                 
OFFSET(dscp),           AV_OPT_TYPE_INT,    { .i64 = -1 },    -1, 63,      E },
     { "connect",        "set if connect() should be called on socket",     
OFFSET(is_connected),   AV_OPT_TYPE_BOOL,   { .i64 =  0 },     0, 1,       
.flags = D|E },
-    { "fifo_size",      "set the UDP circular buffer size (in 188-byte 
packets)", OFFSET(circular_buffer_size), AV_OPT_TYPE_INT, {.i64 = 
HAVE_PTHREAD_CANCEL ? 7*4096 : 0}, 0, INT_MAX, D },
+    { "fifo_size",      "set the UDP circular buffer size (in 188-byte 
packets)", OFFSET(circular_buffer_size), AV_OPT_TYPE_INT, {.i64 = HAVE_THREADS 
? 7*4096 : 0}, 0, INT_MAX, D },
     { "overrun_nonfatal", "survive in case of UDP receiving circular buffer 
overrun", OFFSET(overrun_nonfatal), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1,    D },
     { "timeout",        "set raise error timeout, in microseconds (only in 
read mode)",OFFSET(timeout),         AV_OPT_TYPE_INT,  {.i64 = 0}, 0, INT_MAX, 
D },
     { "sources",        "Source list",                                     
OFFSET(sources),        AV_OPT_TYPE_STRING, { .str = NULL },               
.flags = D|E },
@@ -525,7 +520,7 @@ static int udp_get_file_handle(URLContext *h)
     return s->udp_fd;
 }
 
-#if HAVE_PTHREAD_CANCEL
+#if HAVE_THREADS
 static void *circular_buffer_task_rx( void *_URLContext)
 {
     URLContext *h = _URLContext;
@@ -712,14 +707,14 @@ static int udp_open(URLContext *h, const char *uri, int 
flags)
         if (ret < 0)
             goto fail;
     }
-    if (!HAVE_PTHREAD_CANCEL) {
+    if (!HAVE_THREADS) {
         int64_t      optvals[] = {s->overrun_nonfatal, s->bitrate, 
s->circular_buffer_size};
         const char* optnames[] = {  "overrun_nonfatal",  "bitrate",  
"fifo_size"};
         for (unsigned i = 0; i < FF_ARRAY_ELEMS(optvals); i++) {
             if (optvals[i])
                 av_log(h, AV_LOG_WARNING,
                        "'%s' option was set but it is not supported "
-                       "on this build (pthread support is required)\n", 
optnames[i]);
+                       "on this build (threading support is required)\n", 
optnames[i]);
         }
     }
     if (s->sources) {
@@ -901,7 +896,7 @@ static int udp_open(URLContext *h, const char *uri, int 
flags)
 
     s->udp_fd = udp_fd;
 
-#if HAVE_PTHREAD_CANCEL
+#if HAVE_THREADS
     /*
       Create thread in case of:
       1. Input and circular_buffer_size is set
@@ -947,7 +942,7 @@ static int udp_open(URLContext *h, const char *uri, int 
flags)
 #endif
 
     return 0;
-#if HAVE_PTHREAD_CANCEL
+#if HAVE_THREADS
  thread_fail:
     pthread_cond_destroy(&s->cond);
  cond_fail:
@@ -976,7 +971,7 @@ static int udp_read(URLContext *h, uint8_t *buf, int size)
 {
     UDPContext *s = h->priv_data;
     int ret;
-#if HAVE_PTHREAD_CANCEL
+#if HAVE_THREADS
     int avail, nonblock = h->flags & AVIO_FLAG_NONBLOCK;
 
     if (s->rx_fifo) {
@@ -1044,7 +1039,7 @@ static int udp_write(URLContext *h, const uint8_t *buf, 
int size)
     UDPContext *s = h->priv_data;
     int ret;
 
-#if HAVE_PTHREAD_CANCEL
+#if HAVE_THREADS
     if (s->tx_fifo) {
         uint8_t tmp[4];
 
@@ -1093,7 +1088,7 @@ static int udp_close(URLContext *h)
 {
     UDPContext *s = h->priv_data;
 
-#if HAVE_PTHREAD_CANCEL
+#if HAVE_THREADS
     if (s->thread_started) {
         pthread_mutex_lock(&s->mutex);
         s->close_req = 1;
@@ -1105,7 +1100,7 @@ static int udp_close(URLContext *h)
     if (s->is_multicast && (h->flags & AVIO_FLAG_READ))
         udp_leave_multicast_group(s->udp_fd, (struct sockaddr *)&s->dest_addr,
                                   (struct sockaddr *)&s->local_addr_storage, 
h);
-#if HAVE_PTHREAD_CANCEL
+#if HAVE_THREADS
     if (s->thread_started) {
         int ret;
         ret = pthread_join(s->circular_buffer_thread, NULL);
-- 
2.52.0


>From 760a0830c0b1e6f5e19b5d32b84c5804d190debf Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Sat, 10 Oct 2026 00:47:15 +0800
Subject: [PATCH 4/5] avformat/network: add a wakeup channel for blocking waits

ff_network_wait_fd() can only return after its poll() slice, so a
thread blocked in it notices a request from another thread up to
POLLING_TIME late. Add a one-shot channel that a second thread can
signal to end the wait at once, together with a wait function that
polls it next to the socket.

The channel is an eventfd where available and a pipe otherwise. On
Windows neither can be waited on together with sockets, so it is a
loopback datagram socket connected to itself.

Signed-off-by: Zhao Zhili <[email protected]>
---
 configure             |   2 +
 libavformat/network.c | 118 ++++++++++++++++++++++++++++++++++++++++++
 libavformat/network.h |  63 ++++++++++++++++++++++
 3 files changed, 183 insertions(+)

diff --git a/configure b/configure
index 08ff6892fb..c48bdf88d6 100755
--- a/configure
+++ b/configure
@@ -2503,6 +2503,7 @@ SYSTEM_FUNCS="
     closesocket
     CommandLineToArgvW
     elf_aux_info
+    eventfd
     fcntl
     getaddrinfo
     getauxval
@@ -6887,6 +6888,7 @@ check_func  mmap
 check_func  mprotect
 # Solaris has nanosleep in -lrt, OpenSolaris no longer needs that
 check_func_headers time.h nanosleep || check_lib nanosleep time.h nanosleep 
-lrt
+check_func_headers sys/eventfd.h eventfd
 check_func_headers sys/prctl.h prctl
 check_func  sched_getaffinity
 check_func  setrlimit
diff --git a/libavformat/network.c b/libavformat/network.c
index 2b4c78cfb1..4d2a2a9654 100644
--- a/libavformat/network.c
+++ b/libavformat/network.c
@@ -29,6 +29,9 @@
 #endif
 
 #include <fcntl.h>
+#if HAVE_EVENTFD
+#include <sys/eventfd.h>
+#endif
 #include "network.h"
 #include "tls.h"
 #include "url.h"
@@ -100,6 +103,121 @@ int ff_network_wait_fd_timeout(int fd, int write, int64_t 
timeout, AVIOInterrupt
     }
 }
 
+int ff_network_wakeup_open(FFNetWakeup *w)
+{
+#if HAVE_WINSOCK2_H
+    /* select() and WSAPoll() only accept sockets, so use a datagram socket
+     * connected to itself: sending a datagram to it makes it readable.
+     */
+    struct sockaddr_in addr = { .sin_family = AF_INET };
+    socklen_t len = sizeof(addr);
+    int ret, fd = ff_socket(AF_INET, SOCK_DGRAM, 0, NULL);
+
+    if (fd < 0)
+        return ff_neterrno();
+
+    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0 ||
+        getsockname(fd, (struct sockaddr *)&addr, &len) < 0 ||
+        connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+        ret = ff_neterrno();
+        closesocket(fd);
+        return ret;
+    }
+
+    w->rfd = w->wfd = fd;
+#elif HAVE_EVENTFD
+    int fd = eventfd(0, EFD_CLOEXEC);
+
+    if (fd < 0)
+        return AVERROR(errno);
+
+    w->rfd = w->wfd = fd;
+#else
+    int fds[2];
+
+    if (pipe(fds) < 0)
+        return AVERROR(errno);
+
+#if HAVE_FCNTL
+    for (int i = 0; i < 2; i++) {
+        if (fcntl(fds[i], F_SETFD, FD_CLOEXEC) < 0) {
+            int ret = AVERROR(errno);
+            closesocket(fds[0]);
+            closesocket(fds[1]);
+            return ret;
+        }
+    }
+#endif
+
+    w->rfd = fds[0];
+    w->wfd = fds[1];
+#endif
+
+    atomic_init(&w->signaled, 0);
+
+    return 0;
+}
+
+int ff_network_wakeup_signal(FFNetWakeup *w)
+{
+    /* An eventfd only accepts 8-byte writes; a pipe or a socket takes any
+     * payload, so the same value serves every channel type.
+     */
+    const uint64_t one = 1;
+    int ret;
+
+    /* wfd is blocking; writing at most once keeps the write from blocking.
+     * The flag publishes no other data, so relaxed ordering is enough.
+     */
+    if (atomic_exchange_explicit(&w->signaled, 1, memory_order_relaxed))
+        return 0;
+
+#if HAVE_WINSOCK2_H
+    ret = send(w->wfd, (const char *)&one, sizeof(one), 0);
+#else
+    ret = write(w->wfd, &one, sizeof(one));
+#endif
+
+    return ret < 0 ? ff_neterrno() : 0;
+}
+
+void ff_network_wakeup_close(FFNetWakeup *w)
+{
+    if (w->wfd != w->rfd)
+        closesocket(w->wfd);
+    closesocket(w->rfd);
+
+    w->rfd = w->wfd = -1;
+}
+
+int ff_network_wait_fd_wakeup(int fd, int write, int timeout_ms,
+                              const FFNetWakeup *w)
+{
+    int ev = write ? POLLOUT : POLLIN;
+    struct pollfd p[2] = {
+        { .fd = w->rfd, .events = POLLIN },
+        { .fd = fd,     .events = ev },
+    };
+    int ret;
+
+    /* Return without polling once signaled. This also ends the wait when
+     * the write in ff_network_wakeup_signal() failed and rfd never became
+     * readable.
+     */
+    if (atomic_load_explicit(&w->signaled, memory_order_relaxed))
+        return AVERROR_EXIT;
+
+    ret = poll(p, 2, timeout_ms);
+    if (ret < 0)
+        return ff_neterrno();
+
+    if (p[0].revents & POLLIN)
+        return AVERROR_EXIT;
+
+    return p[1].revents & (ev | POLLERR | POLLHUP) ? 0 : AVERROR(EAGAIN);
+}
+
 int ff_network_sleep_interruptible(int64_t timeout, AVIOInterruptCB *int_cb)
 {
     int64_t wait_start = av_gettime_relative();
diff --git a/libavformat/network.h b/libavformat/network.h
index 7c72c902aa..f48b4d7b2a 100644
--- a/libavformat/network.h
+++ b/libavformat/network.h
@@ -22,6 +22,7 @@
 #define AVFORMAT_NETWORK_H
 
 #include <errno.h>
+#include <stdatomic.h>
 #include <stdint.h>
 
 #include "config.h"
@@ -107,6 +108,68 @@ int ff_network_wait_fd_timeout(int fd, int write, int64_t 
timeout, AVIOInterrupt
  */
 int ff_network_sleep_interruptible(int64_t timeout, AVIOInterruptCB *int_cb);
 
+/**
+ * A channel for waking a thread that is blocked in
+ * ff_network_wait_fd_wakeup(), from another thread.
+ *
+ * Usage: the waiting thread calls ff_network_wait_fd_wakeup(); another
+ * thread calls ff_network_wakeup_signal() to end that wait; once the
+ * waiting thread has been joined, ff_network_wakeup_close() is called.
+ *
+ * rfd is pollable together with sockets on every platform. It is an
+ * eventfd, the read end of a pipe, or a loopback datagram socket on
+ * Windows, where only sockets can be waited on together with sockets.
+ * wfd is the end ff_network_wakeup_signal() writes to; it equals rfd
+ * unless a pipe is in use. On systems other than Windows both fds are
+ * close-on-exec.
+ */
+typedef struct FFNetWakeup {
+    int rfd;
+    int wfd;
+    atomic_int signaled;
+} FFNetWakeup;
+
+/**
+ * Create a wakeup channel. On Windows ff_network_init() must have been
+ * called first.
+ *
+ * @return 0 on success, a negative AVERROR code on failure
+ */
+int ff_network_wakeup_open(FFNetWakeup *w);
+
+/**
+ * Wake the thread blocked in ff_network_wait_fd_wakeup() on this channel,
+ * or the next one that waits on it: the signal is not consumed, so a
+ * signal sent before the wait starts is not lost. Only the first call has
+ * an effect; the function may be called any number of times, from any
+ * thread.
+ *
+ * @return 0 on success, a negative AVERROR code if the first call failed
+ *         to write to the channel
+ */
+int ff_network_wakeup_signal(FFNetWakeup *w);
+
+/**
+ * Close the channel. Call it after the waiting thread has been joined and
+ * no thread can signal any more.
+ */
+void ff_network_wakeup_close(FFNetWakeup *w);
+
+/**
+ * Like ff_network_wait_fd(), but also returns as soon as the wakeup
+ * channel is signaled, and with a caller chosen timeout.
+ *
+ * @param fd Socket descriptor
+ * @param write Set 1 to wait for the socket to be writable, 0 for readable
+ * @param timeout_ms Timeout in milliseconds, or -1 to wait indefinitely
+ * @param w Wakeup channel to watch, must have been opened
+ * @return 0 if fd is ready, AVERROR(EAGAIN) if the timeout expired,
+ *         AVERROR_EXIT if w was signaled (also when fd is ready),
+ *         or a negative error code
+ */
+int ff_network_wait_fd_wakeup(int fd, int write, int timeout_ms,
+                              const FFNetWakeup *w);
+
 #if !HAVE_STRUCT_SOCKADDR_STORAGE
 struct sockaddr_storage {
 #if HAVE_STRUCT_SOCKADDR_SA_LEN
-- 
2.52.0


>From 9dc00c3ae82063ff9e9af6ad9e318fadcd30e299 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Sat, 10 Oct 2026 00:05:19 +0800
Subject: [PATCH 5/5] avformat/udp: wake the rx thread when the input is closed

Closing an input that receives no data took up to POLLING_TIME, as
udp_close() could only wait for the rx thread to come back from its
poll() slice before joining it. Have the thread wait on a wakeup
channel next to the socket and signal that channel from udp_close()
after setting close_req.

Signed-off-by: Zhao Zhili <[email protected]>
---
 libavformat/udp.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/libavformat/udp.c b/libavformat/udp.c
index 74c9896e29..0f8cf442e6 100644
--- a/libavformat/udp.c
+++ b/libavformat/udp.c
@@ -108,6 +108,7 @@ typedef struct UDPContext {
     pthread_t circular_buffer_thread;
     pthread_mutex_t mutex;
     pthread_cond_t cond;
+    FFNetWakeup wakeup; /* ends the rx thread's socket wait on close */
     int thread_started;
 #endif
     uint8_t tmp[UDP_MAX_PKT_SIZE + sizeof(UDPQueuedPacketHeader)];
@@ -543,10 +544,14 @@ static void *circular_buffer_task_rx( void *_URLContext)
         if (pkt_header.pkt_size < 0) {
             err = ff_neterrno();
             if (err == AVERROR(EAGAIN))
-                err = ff_network_wait_fd(s->udp_fd, 0);
+                err = ff_network_wait_fd_wakeup(s->udp_fd, 0, -1, &s->wakeup);
         }
         pthread_mutex_lock(&s->mutex);
         if (pkt_header.pkt_size < 0) {
+            // ff_network_wakeup_signal to exit
+            if (err == AVERROR_EXIT)
+                goto end;
+
             if (err < 0 && err != AVERROR(EAGAIN) && err != AVERROR(EINTR)) {
                 s->circular_buffer_error = err;
                 goto end;
@@ -931,11 +936,18 @@ static int udp_open(URLContext *h, const char *uri, int 
flags)
             ret = AVERROR(ret);
             goto cond_fail;
         }
+        if (!is_output) {
+            ret = ff_network_wakeup_open(&s->wakeup);
+            if (ret < 0) {
+                av_log(h, AV_LOG_ERROR, "Failed to create the wakeup 
channel\n");
+                goto thread_fail;
+            }
+        }
         ret = pthread_create(&s->circular_buffer_thread, NULL, 
is_output?circular_buffer_task_tx:circular_buffer_task_rx, h);
         if (ret != 0) {
             av_log(h, AV_LOG_ERROR, "pthread_create failed : %s\n", 
strerror(ret));
             ret = AVERROR(ret);
-            goto thread_fail;
+            goto create_fail;
         }
         s->thread_started = 1;
     }
@@ -943,6 +955,9 @@ static int udp_open(URLContext *h, const char *uri, int 
flags)
 
     return 0;
 #if HAVE_THREADS
+ create_fail:
+    if (!is_output)
+        ff_network_wakeup_close(&s->wakeup);
  thread_fail:
     pthread_cond_destroy(&s->cond);
  cond_fail:
@@ -1094,6 +1109,8 @@ static int udp_close(URLContext *h)
         s->close_req = 1;
         pthread_cond_signal(&s->cond);
         pthread_mutex_unlock(&s->mutex);
+        if (s->rx_fifo)
+            ff_network_wakeup_signal(&s->wakeup);
     }
 #endif
 
@@ -1106,6 +1123,8 @@ static int udp_close(URLContext *h)
         ret = pthread_join(s->circular_buffer_thread, NULL);
         if (ret != 0)
             av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", strerror(ret));
+        if (s->rx_fifo)
+            ff_network_wakeup_close(&s->wakeup);
         pthread_mutex_destroy(&s->mutex);
         pthread_cond_destroy(&s->cond);
     }
-- 
2.52.0

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

Reply via email to