From: Practice2001 via ffmpeg-devel <ffmpeg-devel@ffmpeg.org>
To: ffmpeg-devel@ffmpeg.org
Cc: Practice2001 <cordacct2001@gmail.com>
Subject: [FFmpeg-devel] [PATCH] tcp: add TCP keepalive tuning options
Date: Fri, 21 Nov 2025 03:55:34 +0530
Message-ID: <20251120222534.280127-1-cordacct2001@gmail.com> (raw)
Addition of tcp_keepalive, tcp_keepidle, tcp_keepintvl, and tcp_keepcnt
support to the TCP protocol. Exposeing these options to the HTTP protocol
so they can be used for HTTP(S) connections. Updated documentation.
Tested with: ./configure && make && make fate
Fixes ticket #11671.
Signed-off-by: Practice2001 <cordacct2001@gmail.com>
---
doc/protocols.texi | 26 ++++++++++++++-
libavformat/http.c | 36 +++++++++++++++++++++
libavformat/tcp.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 141 insertions(+), 1 deletion(-)
diff --git a/doc/protocols.texi b/doc/protocols.texi
index b74383122a..848f4820e2 100644
--- a/doc/protocols.texi
+++ b/doc/protocols.texi
@@ -989,7 +989,31 @@ Set TCP_NODELAY to disable Nagle's algorithm. Default value is 0.
@item tcp_keepalive=@var{1|0}
Enable the TCP keepalive mechanism to detect dead peers and help maintain long-lived idle connections. Default value is 0.
-Only the basic keepalive option (SO_KEEPALIVE) can be enabled or disabled. Platform-specific tuning parameters such as TCP_KEEPIDLE, TCP_KEEPINTVL, or TCP_KEEPCNT are not configurable and will use the operating system's default values.
+The basic keepalive option (SO_KEEPALIVE) can be enabled or disabled. Platform-specific tuning parameters such as TCP_KEEPIDLE, TCP_KEEPINTVL, or TCP_KEEPCNT are configurable.
+
+@item tcp_keepidle=@var{seconds}
+Set the TCP keepalive idle time (in seconds).
+
+This controls how long the connection must remain idle before the first
+keepalive probe is sent.
+
+Default is 0 (uses system default).
+
+@item tcp_keepintvl=@var{seconds}
+Set the interval (in seconds) between individual TCP keepalive probes.
+
+Default is 0 (uses system default).
+
+@item tcp_keepcnt=@var{count}
+Set the number of unacknowledged keepalive probes that must occur before
+the connection is considered dead.
+
+Default is 0 (uses system default).
+
+@emph{Note:}
+These platform-specific parameters are available on Linux and BSD-derived
+systems. On Windows, only basic keepalive configuration is supported and
+the underlying system will use its own values for probe timing and count.
@end table
diff --git a/libavformat/http.c b/libavformat/http.c
index c4e6292a95..1c264d845c 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -146,6 +146,11 @@ typedef struct HTTPContext {
unsigned int retry_after;
int reconnect_max_retries;
int reconnect_delay_total_max;
+ /* TCP keepalive forwarding */
+ int tcp_keepalive; /* -1 = unset, 0 = off, 1 = on */
+ int tcp_keepidle; /* seconds, 0 = unset */
+ int tcp_keepintvl; /* seconds, 0 = unset */
+ int tcp_keepcnt; /* probe count, 0 = unset */
} HTTPContext;
#define OFFSET(x) offsetof(HTTPContext, x)
@@ -191,6 +196,10 @@ static const AVOption options[] = {
{ "resource", "The resource requested by a client", OFFSET(resource), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
{ "reply_code", "The http status code to return to a client", OFFSET(reply_code), AV_OPT_TYPE_INT, { .i64 = 200}, INT_MIN, 599, E},
{ "short_seek_size", "Threshold to favor readahead over seek.", OFFSET(short_seek_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D },
+ { "tcp_keepalive", "Enable SO_KEEPALIVE on underlying TCP socket", OFFSET(tcp_keepalive), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, D | E },
+ { "tcp_keepidle", "TCP keepalive idle time (seconds) for underlying TCP socket", OFFSET(tcp_keepidle), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D | E },
+ { "tcp_keepintvl", "TCP keepalive interval (seconds) for underlying TCP socket", OFFSET(tcp_keepintvl), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D | E },
+ { "tcp_keepcnt", "TCP keepalive probe count for underlying TCP socket", OFFSET(tcp_keepcnt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D | E },
{ NULL }
};
@@ -282,6 +291,33 @@ static int http_open_cnx_internal(URLContext *h, AVDictionary **options)
ff_url_join(buf, sizeof(buf), lower_proto, NULL, hostname, port, NULL);
+
+ /* Forward TCP keepalive options to underlying protocol via options dict.
+ * Prefer using AVDictionary forwarding to modifying the URL (safer). */
+ if (options) {
+ int err = 0;
+ if (s->tcp_keepalive != -1) {
+ err = av_dict_set_int(options, "tcp_keepalive", s->tcp_keepalive ? 1 : 0, 0);
+ if (err < 0)
+ goto end; /* existing cleanup label in this function */
+ }
+ if (s->tcp_keepidle > 0) {
+ err = av_dict_set_int(options, "tcp_keepidle", s->tcp_keepidle, 0);
+ if (err < 0)
+ goto end;
+ }
+ if (s->tcp_keepintvl > 0) {
+ err = av_dict_set_int(options, "tcp_keepintvl", s->tcp_keepintvl, 0);
+ if (err < 0)
+ goto end;
+ }
+ if (s->tcp_keepcnt > 0) {
+ err = av_dict_set_int(options, "tcp_keepcnt", s->tcp_keepcnt, 0);
+ if (err < 0)
+ goto end;
+ }
+ }
+
if (!s->hd) {
err = ffurl_open_whitelist(&s->hd, buf, AVIO_FLAG_READ_WRITE,
&h->interrupt_callback, options,
diff --git a/libavformat/tcp.c b/libavformat/tcp.c
index ce9f69a50b..eb3dd0cc5b 100644
--- a/libavformat/tcp.c
+++ b/libavformat/tcp.c
@@ -46,6 +46,9 @@ typedef struct TCPContext {
int send_buffer_size;
int tcp_nodelay;
int tcp_keepalive;
+ int tcp_keepidle;
+ int tcp_keepintvl;
+ int tcp_keepcnt;
#if !HAVE_WINSOCK2_H
int tcp_mss;
#endif /* !HAVE_WINSOCK2_H */
@@ -54,6 +57,28 @@ typedef struct TCPContext {
#define OFFSET(x) offsetof(TCPContext, x)
#define D AV_OPT_FLAG_DECODING_PARAM
#define E AV_OPT_FLAG_ENCODING_PARAM
+
+/**
+ * @name TCP keepalive tuning options
+ *
+ * These AVOptions extend TCP keepalive controls beyond the basic SO_KEEPALIVE
+ * flag by exposing platform-supported tuning parameters.
+ *
+ * The following options may be set on the "tcp" protocol:
+ *
+ * - @ref tcp_keepalive : Enable or disable SO_KEEPALIVE.
+ * - @ref tcp_keepidle : Set idle time (seconds) before sending first probe.
+ * - @ref tcp_keepintvl : Set interval (seconds) between keepalive probes.
+ * - @ref tcp_keepcnt : Set number of failed probes before declaring dead.
+ *
+ * Notes:
+ * - Linux and BSD systems expose all parameters.
+ * - Windows only supports enabling keepalive; tuning values are ignored.
+ *
+ * These options improve robustness of long-lived idle connections and help
+ * detect dead peers sooner than system defaults.
+ */
+
static const AVOption options[] = {
{ "listen", "Listen for incoming connections", OFFSET(listen), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, .flags = D|E },
{ "local_port", "Local port", OFFSET(local_port), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, .flags = D|E },
@@ -64,6 +89,9 @@ static const AVOption options[] = {
{ "recv_buffer_size", "Socket receive buffer size (in bytes)", OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
{ "tcp_nodelay", "Use TCP_NODELAY to disable nagle's algorithm", OFFSET(tcp_nodelay), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = D|E },
{ "tcp_keepalive", "Use TCP keepalive to detect dead connections and keep long-lived connections active.", OFFSET(tcp_keepalive), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, .flags = D|E },
+ { "tcp_keepidle", "TCP keepalive idle time in seconds", OFFSET(tcp_keepidle), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = D|E },
+ { "tcp_keepintvl", "TCP keepalive probe interval in seconds", OFFSET(tcp_keepintvl), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = D|E },
+ { "tcp_keepcnt", "TCP keepalive probe count", OFFSET(tcp_keepcnt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, .flags = D|E },
#if !HAVE_WINSOCK2_H
{ "tcp_mss", "Maximum segment size for outgoing TCP packets", OFFSET(tcp_mss), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E },
#endif /* !HAVE_WINSOCK2_H */
@@ -132,6 +160,58 @@ static int customize_fd(void *ctx, int fd, int family)
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &s->tcp_keepalive, sizeof(s->tcp_keepalive))) {
ff_log_net_error(ctx, AV_LOG_WARNING, "setsockopt(SO_KEEPALIVE)");
}
+ /* Attempt to set keepalive tuning if requested */
+ /* POSIX/Linux/BSD style */
+ #if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE)
+ if (s->tcp_keepidle > 0) {
+# ifdef TCP_KEEPIDLE
+ int idle = s->tcp_keepidle;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) {
+ ff_log_net_error(ctx, AV_LOG_WARNING, "setsockopt(TCP_KEEPIDLE)");
+ }
+# elif defined(TCP_KEEPALIVE)
+ int idle = s->tcp_keepidle;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle))) {
+ ff_log_net_error(ctx, AV_LOG_WARNING, "setsockopt(TCP_KEEPALIVE)");
+ }
+# endif
+ }
+ if (s->tcp_keepintvl > 0) {
+# ifdef TCP_KEEPINTVL
+ int kv = s->tcp_keepintvl;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &kv, sizeof(kv))) {
+ ff_log_net_error(ctx, AV_LOG_WARNING, "setsockopt(TCP_KEEPINTVL)");
+ }
+# endif
+ }
+ if (s->tcp_keepcnt > 0) {
+# ifdef TCP_KEEPCNT
+ int kc = s->tcp_keepcnt;
+ if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &kc, sizeof(kc))) {
+ ff_log_net_error(ctx, AV_LOG_WARNING, "setsockopt(TCP_KEEPCNT)");
+ }
+# endif
+ }
+ #endif /* defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE) */
+
+ /* Windows SIO_KEEPALIVE_VALS via WSAIoctl */
+ #if HAVE_WINSOCK2_H
+ if (s->tcp_keepidle > 0 || s->tcp_keepintvl > 0) {
+# include <mstcpip.h>
+ /* windows expects milliseconds */
+ struct tcp_keepalive vals;
+ DWORD bytes;
+ vals.onoff = 1;
+ vals.keepalivetime = (DWORD)(s->tcp_keepidle > 0 ? (DWORD)s->tcp_keepidle * 1000 : 7200000);
+ vals.keepaliveinterval = (DWORD)(s->tcp_keepintvl > 0 ? (DWORD)s->tcp_keepintvl * 1000 : 1000);
+ /* WSAIoctl returns SOCKET_ERROR on failure */
+ if (WSAIoctl((SOCKET)fd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals),
+ NULL, 0, &bytes, NULL, NULL) == SOCKET_ERROR) {
+ ff_log_net_error(ctx, AV_LOG_WARNING, "WSAIoctl(SIO_KEEPALIVE_VALS)");
+ }
+ }
+ #endif /* HAVE_WINSOCK2_H */
+
}
#if !HAVE_WINSOCK2_H
--
2.34.1
_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
next reply other threads:[~2025-11-24 0:01 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-20 22:25 Practice2001 via ffmpeg-devel [this message]
2025-11-24 13:22 ` [FFmpeg-devel] " Nicolas George via ffmpeg-devel
2025-11-21 17:45 [FFmpeg-devel] " Practice2001 via ffmpeg-devel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251120222534.280127-1-cordacct2001@gmail.com \
--to=ffmpeg-devel@ffmpeg.org \
--cc=cordacct2001@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git