From 504360a77afc6cd87d806047179343138f9b8593 Mon Sep 17 00:00:00 2001 From: Luke Hoersten Date: Fri, 19 Jun 2026 19:15:28 -0500 Subject: disable Nagle on /frame socket + add min/max/worst-frame to script log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1: TCP_NODELAY for /frame ESP-IDF lwIP defaults Nagle ON. /frame is the worst Nagle workload: ~140KB body POST followed by an empty 204 response, no follow-up data either way. Both the final partial-MTU body packet and the response packet can sit in the kernel send buffer up to 40ms waiting for an ACK that the other side is delaying-ACKing — silently adding tens of ms to every frame's wall time and showing up as a fat net_up bucket on the Scrypted side. setsockopt(TCP_NODELAY) at handler entry on every /frame. Cheap and idempotent. Includes the /state and /config sockets too — those are infrequent but small responses also benefit. #2: min/max + worst-frame decomposition in the Scrypted log The p50/p95 line answered "what's typical" but not "what happened in the worst frame this window". Now every 10-fetch log adds: - min and max columns alongside p50/p95 per bucket - a worst-frame breakdown row: identifies the single slowest fetch in the window and decomposes its wall time across all stages. Answers "did the slow frame get gated by emit→post (ffmpeg backed up), net_up (TCP/handshake spike), or fw_dec (large JPEG)?" directly, instead of us inferring from percentiles. Together these are the prerequisites for evaluating whether further optimization (HTTP keep-alive, chunked streaming) is worth pursuing. --- main/http_api.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'main/http_api.c') diff --git a/main/http_api.c b/main/http_api.c index 69e555e..0192f7d 100644 --- a/main/http_api.c +++ b/main/http_api.c @@ -10,6 +10,7 @@ #include "esp_http_server.h" #include "esp_log.h" #include "esp_timer.h" +#include "lwip/sockets.h" // TCP_NODELAY setsockopt for /frame socket #include "display.h" #include "jpeg_decoder.h" @@ -334,6 +335,19 @@ static uint32_t s_last_painted_seq; static esp_err_t frame_post_handler(httpd_req_t *req) { + // Nagle off on this socket. /frame is a single large POST followed + // by a tiny (empty 204) response — the worst case for Nagle. The + // last partial-MTU packet of the body, and the response packet, + // both sit in the kernel send buffer up to 40ms waiting for an + // ACK that the peer's also delaying-ACKing. setsockopt is cheap + // and idempotent; if keep-alive is ever added the flag persists + // for the connection's life. + int sockfd = httpd_req_to_sockfd(req); + if (sockfd >= 0) { + int one = 1; + (void)setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + } + // Content-Type must be image/jpeg. char ct[40] = {0}; if (httpd_req_get_hdr_value_str(req, "Content-Type", ct, sizeof(ct)) != ESP_OK || -- cgit v1.2.3