From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id A12CB4ACCA for ; Fri, 21 Nov 2025 17:46:01 +0000 (UTC) Authentication-Results: ffbox; dkim=fail (body hash mismatch (got b'q5WM9fu/F3b1KwJCn32Ynt2RUyHzz+od/8qTtzIVypE=', expected b'7B9ByKw2XY8R76HUOPn5Hg/vsJIFKfE8oj8AoT3QJsw=')) header.d=gmail.com header.a=rsa-sha256 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1763747153; h=to : date : message-id : mime-version : reply-to : subject : list-id : list-archive : list-archive : list-help : list-owner : list-post : list-subscribe : list-unsubscribe : from : cc : content-type : content-transfer-encoding : from; bh=wcSsg1GYkVzX39evrfK6IojydGVfOu4utQIRrd3uw4c=; b=eXzroholRT9VKoQEcFXHKuFvsQRBEO1jfNXVVaNr8zGa7c/9B0gIDylWFyB7RO+xmkRfD Ke33Xx0M9Hag6zDy0UYtUs5l9nwLEFEIP7Cqv3ILiafizbxP5Z44CipLvizqiRpmwyqqYeR Eck6rFEGMWf2O6Ag4WcdadoeruBzcmKvAZLTuiIKcMCnEHjjaZyPQryPyeyCyGkVEhO82Fq hYGCC4VhXsdDxtWyMy+6cPPO9MWZptlOKsuuM9TEIY3XSvaFQ8RhX6CdR7UfurYPRIjpCi1 2cpimwGpjd7SA6stY0UKa214y13g04F9x5W3KBCaVfr2mIVIGhT+i8xknzmA== Received: from [172.19.0.2] (unknown [172.19.0.2]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 2CE4B68FFF7; Fri, 21 Nov 2025 19:45:53 +0200 (EET) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=ffmpeg.org; s=arc; t=1763747151; b=qZgfoOE5SrYOcMR03JFlQ3qHBAuDeRWX6dnvHNxa9VfNc/+yIYKX6OD/Lg/NbEYq9p1XV 7Y4Agrfgb3JHfrSfjebTuuUWG2hYOwNWDoRhze0Z1N86kt/wxyzPct5hXaRcl72CfU87aYr zIa04jHFKIxogml0ZsgHylEGlPtooLvHKs8U1hu8XE4PPEjbscoDQrBvCKggNe3yOe2sUgB rlL3ne5U6LL9wnZ8tV4DrnwtLUimFtIjOcu7KlpvC2EiLtLiTRi40gyKPUGFNInJVgCq/Ia M5rvmxycfeS0C5bLpzrEYm4NMBysiJ9MB8zsc2QZS31kmTORpuYvAbG3LmOg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=ffmpeg.org; s=arc; t=1763747151; h=from : sender : reply-to : subject : date : message-id : to : cc : mime-version : content-type : content-transfer-encoding : content-id : content-description : resent-date : resent-from : resent-sender : resent-to : resent-cc : resent-message-id : in-reply-to : references : list-id : list-help : list-unsubscribe : list-subscribe : list-post : list-owner : list-archive; bh=q5WM9fu/F3b1KwJCn32Ynt2RUyHzz+od/8qTtzIVypE=; b=SJhpO3U+ewThsal+2b4jfgooaj+4JWKS4azXxKo7BIKkY46PHbB1C46ua33aZLPJ4f9K7 c8XV+sw1QmBcnuUuMBxMsrrN4PJkQgTPMhRHWB1d13TAxdOkQd3PxhkqJLf3iCqxq/J3bfc dXAWR44pbh+SuD56RvFXCaVUNoOkjOBi0zdfoaBfhIzrcptyJzsFDF3g8j3PB8Li6Qnp7M7 lg7iU/eEEU7VhI0srlVpDkMSdIvSkMWGoXIiOG52MIJbo/i3vAgji7eeTucAwFc//LVEnd/ wAWI3YC2VHavGp3IY6hhz8EbmvGzg/cH7Z/7fnyzs5zdNU/pukIAXahUD4jQ== ARC-Authentication-Results: i=1; ffmpeg.org; dkim=pass header.d=gmail.com; arc=none; dmarc=pass header.from=gmail.com policy.dmarc=quarantine Authentication-Results: ffmpeg.org; dkim=pass header.d=gmail.com; arc=none (Message is not ARC signed); dmarc=pass (Used From Domain Record) header.from=gmail.com policy.dmarc=quarantine Received: from mail-pf1-f178.google.com (mail-pf1-f178.google.com [209.85.210.178]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id A132868FFE1 for ; Fri, 21 Nov 2025 19:45:37 +0200 (EET) Received: by mail-pf1-f178.google.com with SMTP id d2e1a72fcca58-7b7828bf7bcso2752742b3a.2 for ; Fri, 21 Nov 2025 09:45:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763747135; x=1764351935; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=7B9ByKw2XY8R76HUOPn5Hg/vsJIFKfE8oj8AoT3QJsw=; b=Ko8zWxHe1kkgx1mi8zwT3QfN7INcYC3cpqw45il++1lkzgddD3s3oAVTkJMnYkkGMp 97WZkfeoh/ZQaZmV5SXB1b9vTWIST2+C2MLQtKYmDC87WpP8DESDtAOntaYcpvCKgSgq 0RQbASos7oLlVP8bTYNXnAp1qxW7I5AdgGsxtKF4PZ+3yYml2jwvUUg8zLwBCKroR4LZ L/ZTb8W+K0WLlwRe8iczBerhvecKvgvTzcrKDp97parKJEuTWrrHxdIxWQcuY+uqjopP Gbvs+zoo5iOFzhFAt/1dxce11Ou0A3qNoGo8rEPO3SIe/msb4SNFnlqRcnqTSwTtlpL9 A9Lg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763747135; x=1764351935; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=7B9ByKw2XY8R76HUOPn5Hg/vsJIFKfE8oj8AoT3QJsw=; b=iDcZP7ImASoVVndQxFZjuJECKoyfDbze94Zdn5DGzpigVjMho9VZqv2FpcPi5Br8qJ VonYaJKwEfoQTbCq8tsmuWStMYbuOmBJmNe5nZbYK2rR/WwHg1eG7Hrn3jy8U1yeElvx PZvbytkTd+2uxKr1zMJhha+60x/lc5DSLDT7Br/2ZgJOCOzL9kufjGhdQ0ovxehQIFVS 8Al7NmBzT/VEyenS88ARhpjKEHRXSyif1QX9c1m2MSW6IF6KxTSqP3S+JtSP5C0P4qmQ esnNZ/h0uYsaXWPAYeyJvljQKgE7zVOeG/T83ESOMfFT12Pvkerc+5J53RrwEdaKPs8d ffDQ== X-Gm-Message-State: AOJu0Yw9GtMPJqNfwg8Qb1F85owHDZa/vMeXZWQXOfioqBqba994YVIL sFWtlrBOgmiY7llf+uA73Pj8DNFazBZQqeeavqux9ojMLaD1fCR+OoCQ1AcuTCKO X-Gm-Gg: ASbGncuPHwBhaVNL/NqWc0RkLKF0mNULnHWiYO/+og3gasS4Qq0QekwaRfSl6eG+yIL l/W2NFeetYCF3tRdNHxdNvLKNsPwwy5TkPlUxwkZMqbVjzgt4kfe0vN+QlNRFKYQKeQDdTgSVXT 8dmZRqKHoPBqvo4+fFAV9APJ6DdVuTqB1CY9O1XAPgwVvrfs9q1xXmT1TCvIm/qoWMe+2ykzjsL yYsUI6eojt1KpoP9lftd3olTj79ZV0qs3UOsDts2GczaQLD5/NxK/Ak8D+WW+7JHDGjKgVJ0488 rQSkOhTAY5QM/jYdFb7SjvNdfJ4/v1aDp7dN/EyLANwEyV9U3MABb1//sPcba7SZHSKo8Dqxomo HHSIvp6AQ3HtbiLl8QYXWyuMQcNXw2xa/yeUeIEdFtardFohoHzIjlWMRrec29i7iw5nVkRynDV LYrO2q8mKO2ciTw2t6RFRw X-Google-Smtp-Source: AGHT+IGol4z2CY/EkMQ53dfl9FkIrW9wLkaT8oxaCVJhiWtMCvGROfXLomy3FjMOsNk7fDZVWc60PA== X-Received: by 2002:a05:6a00:894:b0:7b8:383d:8706 with SMTP id d2e1a72fcca58-7c58e609849mr3530980b3a.18.1763747135007; Fri, 21 Nov 2025 09:45:35 -0800 (PST) Received: from Family.localdomain ([49.43.24.110]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7c3ecf7c29asm6696605b3a.9.2025.11.21.09.45.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Nov 2025 09:45:34 -0800 (PST) To: ffmpeg-devel@ffmpeg.org Date: Fri, 21 Nov 2025 23:15:25 +0530 Message-Id: <20251121174525.3784-1-cordacct2001@gmail.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Message-ID-Hash: PO6HWP7XY5FXW7KZESZAH4I5M4P3DDMH X-Message-ID-Hash: PO6HWP7XY5FXW7KZESZAH4I5M4P3DDMH X-MailFrom: SRS0=uZo7=55=gmail.com=cordacct2001@ffmpeg.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-ffmpeg-devel.ffmpeg.org-0; header-match-ffmpeg-devel.ffmpeg.org-1; header-match-ffmpeg-devel.ffmpeg.org-2; header-match-ffmpeg-devel.ffmpeg.org-3; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list Reply-To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PATCH] tcp: add TCP keepalive tuning options List-Id: FFmpeg development discussions and patches Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Practice2001 via ffmpeg-devel Cc: Practice2001 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Archived-At: List-Archive: List-Post: 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 --- 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 + /* 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