PR #25396 opened by Niklas Haas (haasn) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25396 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25396.patch
Affects tcp, udp and libcurl >From 7173ede0875bf13d8a33403503fe79dfbb5fa94c Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Fri, 23 Oct 2026 18:54:06 +0200 Subject: [PATCH 1/4] avformat/network: add ff_socket_set_tos() Signed-off-by: Niklas Haas <[email protected]> --- libavformat/network.c | 9 +++++++++ libavformat/network.h | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/libavformat/network.c b/libavformat/network.c index 2b4c78cfb12..8fa0a1b84df 100644 --- a/libavformat/network.c +++ b/libavformat/network.c @@ -212,6 +212,15 @@ int ff_socket(int af, int type, int proto, void *logctx) return fd; } +int ff_socket_set_tos(int fd, int family, int tos) +{ +#if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS) + if (family == AF_INET6) + return setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) ? ff_neterrno() : 0; +#endif + return setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) ? ff_neterrno() : 0; +} + int ff_listen(int fd, const struct sockaddr *addr, socklen_t addrlen, void *logctx) { diff --git a/libavformat/network.h b/libavformat/network.h index 7c72c902aa3..195e198cd14 100644 --- a/libavformat/network.h +++ b/libavformat/network.h @@ -308,6 +308,13 @@ int ff_http_match_no_proxy(const char *no_proxy, const char *hostname); int ff_socket(int domain, int type, int protocol, void *logctx); +/** + * Set the IPv4 ToS or IPv6 traffic class byte of outgoing packets. + * + * @return 0 on success, AVERROR on failure. + */ +int ff_socket_set_tos(int fd, int family, int tos); + void ff_log_net_error(void *ctx, int level, const char* prefix); /** -- 2.54.0 >From 44a05ae253f4e5e27805487357ec263dee03a50e Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Fri, 23 Oct 2026 18:54:20 +0200 Subject: [PATCH 2/4] avformat/tcp: add ip_tos option Signed-off-by: Niklas Haas <[email protected]> --- doc/protocols.texi | 4 ++++ libavformat/tcp.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/doc/protocols.texi b/doc/protocols.texi index aaee2292e9d..00e77f708cb 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -2279,6 +2279,10 @@ Set TCP_NODELAY to disable Nagle's algorithm. Default value is 0. @item tcp_mss=@var{bytes} Set maximum segment size for outgoing TCP packets, expressed in bytes. + +@item ip_tos=@var{tos} +Set the IPv4 type of service or IPv6 traffic class byte for outgoing packets, +e.g. @code{0x88} for DSCP AF41. @end table The following example shows how to setup a listening TCP connection diff --git a/libavformat/tcp.c b/libavformat/tcp.c index 09dc8a96796..1c1d1741063 100644 --- a/libavformat/tcp.c +++ b/libavformat/tcp.c @@ -46,6 +46,7 @@ typedef struct TCPContext { int send_buffer_size; int tcp_nodelay; int tcp_keepalive; + int ip_tos; #if !HAVE_WINSOCK2_H int tcp_mss; #endif /* !HAVE_WINSOCK2_H */ @@ -64,6 +65,7 @@ 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 }, + { "ip_tos", "IP ToS / traffic class for outgoing packets", OFFSET(ip_tos), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, .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 */ @@ -133,6 +135,10 @@ static int customize_fd(void *ctx, int fd, int family) ff_log_net_error(ctx, AV_LOG_WARNING, "setsockopt(SO_KEEPALIVE)"); } } + if (s->ip_tos >= 0) { + if (ff_socket_set_tos(fd, family, s->ip_tos) < 0) + ff_log_net_error(ctx, AV_LOG_WARNING, "setsockopt(IP_TOS)"); + } #if !HAVE_WINSOCK2_H if (s->tcp_mss > 0) { -- 2.54.0 >From ed605fecdefab6d65d4a7e3a66f67c9dd0b47849 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Fri, 23 Oct 2026 18:54:32 +0200 Subject: [PATCH 3/4] avformat/udp: add ip_tos option Mirroring tcp.c. Supports IPv6 traffic class, unlike dscp. Signed-off-by: Niklas Haas <[email protected]> --- doc/protocols.texi | 4 ++++ libavformat/udp.c | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/doc/protocols.texi b/doc/protocols.texi index 00e77f708cb..6bb96ad1cb4 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -2507,6 +2507,10 @@ Set the time to live value (for multicast only). @item dscp=@var{dscp} Set the 6-bit DSCP field for outgoing packets. +@item ip_tos=@var{tos} +Set the IPv4 type of service or IPv6 traffic class byte for outgoing packets, +e.g. @code{0x88} for DSCP AF41. Takes precedence over @option{dscp}. + @item connect=@var{1|0} Initialize the UDP socket with @code{connect()}. In this case, the destination address can't be changed with ff_udp_set_remote_url later. diff --git a/libavformat/udp.c b/libavformat/udp.c index a21b04e89c3..7192aca77ee 100644 --- a/libavformat/udp.c +++ b/libavformat/udp.c @@ -115,6 +115,7 @@ typedef struct UDPContext { char *localaddr; int timeout; int dscp; + int ip_tos; struct sockaddr_storage local_addr_storage; char *sources; char *block; @@ -140,6 +141,7 @@ static const AVOption options[] = { { "broadcast", "explicitly allow or disallow broadcast destination", OFFSET(is_broadcast), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { "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 }, + { "ip_tos", "IP ToS / traffic class for outgoing packets", OFFSET(ip_tos), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, 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_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 }, @@ -809,6 +811,11 @@ static int udp_open(URLContext *h, const char *uri, int flags) } } + if (s->ip_tos >= 0) { + if ((ret = ff_socket_set_tos(udp_fd, my_addr.ss_family, s->ip_tos)) < 0) + goto fail; + } + /* If multicast, try binding the multicast address first, to avoid * receiving UDP packets from other sources aimed at the same UDP * port. This fails on windows. This makes sending to the same address -- 2.54.0 >From 070b2ecf81a542e958e96dc6068d3e8ef5e5e4b1 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Fri, 23 Oct 2026 18:58:36 +0200 Subject: [PATCH 4/4] avformat/libcurl: add ip_tos option Mirroring the libavformat native socket option. The other socket options are not very useful to set, or need to be set differently. libcurl already enables TCP_NODELAY, and already enables keepalive when -multiple_requests is set. With HTTP/3, it creates UDP sockets, so the TCP-specific options also fail. Signed-off-by: Niklas Haas <[email protected]> --- doc/protocols.texi | 4 ++++ libavformat/libcurl.c | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/doc/protocols.texi b/doc/protocols.texi index 6bb96ad1cb4..4c3e21861b5 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -1183,6 +1183,10 @@ Client certificate and private key files for mutual TLS. @item connect_timeout Connection timeout in seconds. @code{0} (the default) uses libcurl's own timeout. +@item ip_tos +Set the IPv4 type of service or IPv6 traffic class byte for outgoing packets, +e.g. @code{0x88} for DSCP AF41. + @item max_redirects Maximum number of redirects to follow. Default is @code{16}. diff --git a/libavformat/libcurl.c b/libavformat/libcurl.c index fc487602db9..a290c804c98 100644 --- a/libavformat/libcurl.c +++ b/libavformat/libcurl.c @@ -131,6 +131,7 @@ struct CurlContext { int tls_verify; int seekable_opt; int connect_timeout; + int ip_tos; int max_redirects; int multiple_requests; int http_version; @@ -1125,6 +1126,19 @@ void ff_curl_loop_free(struct CurlLoop **loop) static int libcurl_close(URLContext *h); +static int sockopt_callback(void *userdata, curl_socket_t fd, curlsocktype purpose) +{ + CurlContext *c = userdata; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + if (getsockname(fd, (struct sockaddr *)&addr, &addr_len)) + addr.ss_family = AF_UNSPEC; + if (ff_socket_set_tos(fd, addr.ss_family, c->ip_tos) < 0) + ff_log_net_error(c->h, AV_LOG_WARNING, "setsockopt(IP_TOS)"); + return CURL_SOCKOPT_OK; +} + static int debug_callback(CURL *easy, curl_infotype type, char *data, size_t size, void *userdata) { @@ -1293,6 +1307,10 @@ static int setup_curl(CurlContext *c) if (c->connect_timeout > 0) CURL_SETOPT(CURLOPT_CONNECTTIMEOUT_MS, (long)c->connect_timeout * 1000); + if (c->ip_tos >= 0) { + CURL_SETOPT(CURLOPT_SOCKOPTFUNCTION, sockopt_callback); + CURL_SETOPT(CURLOPT_SOCKOPTDATA, c); + } if (c->user_agent && c->user_agent[0]) CURL_SETOPT(CURLOPT_USERAGENT, c->user_agent); @@ -1733,6 +1751,7 @@ static const AVOption options[] = { { "cert_file", "client certificate file", OFFSET(cert_file), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E }, { "key_file", "client private key file", OFFSET(key_file), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E }, { "connect_timeout", "connection timeout in seconds (0 = libcurl default)", OFFSET(connect_timeout), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX / 1000, D | E }, + { "ip_tos", "IP TOS / traffic class for outgoing packets", OFFSET(ip_tos), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, D | E }, { "max_redirects", "maximum number of redirects to follow", OFFSET(max_redirects), AV_OPT_TYPE_INT, { .i64 = 16 }, 0, INT_MAX, D }, { "multiple_requests", "reuse the connection across requests (HTTP keep-alive)", OFFSET(multiple_requests), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D | E }, { "max_retries", "maximum number of retries after a recoverable error", OFFSET(max_retries), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, D }, -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
