From patchwork Thu Feb 1 16:40:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Momtchil Momtchev X-Patchwork-Id: 45960 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:1a28:b0:199:de12:6fa6 with SMTP id cj40csp395593pzb; Thu, 1 Feb 2024 08:40:47 -0800 (PST) X-Google-Smtp-Source: AGHT+IFppXB6ZutUPUPDXeQsOL1aPthhbSShO4ZpmfG026QDKXoIa5+OaN55T0nMJHcOMudmdjGj X-Received: by 2002:a17:907:78c5:b0:a36:f27b:a1d2 with SMTP id kv5-20020a17090778c500b00a36f27ba1d2mr21622ejc.75.1706805647153; Thu, 01 Feb 2024 08:40:47 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1706805647; cv=none; d=google.com; s=arc-20160816; b=QGkDyaogUY4DCCtQA/s6iUyv9Bpjw4eTzt99znRAB4Heu+nvSeJwIB3SS7ZlRPBGMq AWwP1PG3BOyAyY3w/X6/IXYNkO1ODEGpgLAqOKjWGNZOsVmAM0jT7dM4/2bbcdpc81fy Uc54zHEGPBa7AMLDjEWcpbSzgv/oeSQlZcf6b0vKlG+OeobeLfPh4PJF1d1gs3dTIpR6 xVSd6/08Lo/f3iaWCfmanALUaapVQT2b+aIahO9nQuXBISbkd7AWsVausWNtkSEuDnss xWhfZchMV0X43KP2H39Mw++WkfcLAFRosqRSmnMvSRq/w/9fwLGwKn1tThJv0ZhhkBx9 nTEQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject:from:to :content-language:user-agent:mime-version:date:message-id :dkim-signature:delivered-to; bh=voaJk/DvVp25RWUUGAEi+gpfZ7NYE0C8d91T9GybfJE=; fh=9yAZN0Od43VtOanI/Go/ZGIr86FW8zcSoiALwdzMmyc=; b=cfiXHAe3S1Ar3Rn9mE7ojsCJmVaMcrS47EUQA6TH56SEyS6owzySitwiwzf4UBBizR JZrd267qXzpIq91o82fupGeKn8MKYsAPl6ehGMk5LcYFXO1AiNDul9cpMQ0kXfeWrTMy 9jufcwWOEiBSvez8s7u9ZQ0oHMXqNK7xdwdB3pkrAo6UStw5o55h9/gaxuC8Ilsc1rlq m4QMsya6pmUdnIFp6EATUIhdVL31kBlX478WxzHlbo3bvHWp71dpLdOlWoGdYAQov4Ru 3ZHbs5L/bnkpIZKQGD9p3fTlWuJ9DWmnANWSIhQo11+uQcrb9AtKryUaQZnvwEKPDICz Uabw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@momtchev.com header.s=rsa header.b=M9ffTCni; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org X-Forwarded-Encrypted: i=0; AJvYcCXSruMhPZ13l/mLKepc3KgBxX6MENZd6CsxXbsY7lcukk5DhJLtkmAFoGh7f2VgfcJ3aqf5FUh6UMB+nyvhlAAuP3FknubyWbGa7p1qXJxglZDNpDrEeuP2IWD6///JAC/uWPJbeZNJ9Uonv0KFrOjqpwQHNnY2B9sygxuDLWQGZ1yWuDnxJe4aip4Q03U3F2l2WL++yTfgKi7vL/f6OVvfaTCMEEXjN0ACOPRdTtwu0HOAEntomelmxfBZF9CgJ7mOULq133/4uGVfL3DssNkl3888rK+BkiwtoocLdlmc+zAE0GuoDgG9eq2Dog49K/fI0jZbBdc+2NLkXvV9HxEl8BuyJ3YmF/SPAb5h70N9QcRTN4xBlXhYq4lH9HXcwQ89aOjcLebICiKhA2VAKof4EsxQhVlujF+uoDT2e89/z6tx1Ygts91BKKyPOZbmsrgvbcFrjHoHXnSaGPjcr+e2ZyhThQIpHoS+y0PEPmMxRUlO0SZTHOrxNl/vLKn4WUFApDzE0eNzPRaAl3OuoyHotoF2ES2xuotaRPXojjH9h48TampYAac3GOWucqbBat1da/wkMD6Y6MPeYIczXkKhHAk3nb9PdXRQLPC8vmukXI5C7ls3LLUaZABMik9RKG2cHMazg8HR7J1U9Vk50QrJaAxw18L9xRULjYzpqly1u5qnbrkMgxoOnqj/Fvo2pGF/ANXSnDjweWbap0sNkDQIiodtnspMzlDy//zKYfYNv55zY1niadohgBuchPW6yiXtuWSpXurBODMnBkzCOYV93TmUs/q2uwPwmhgT0OEVH6ZYnPt7yoSlHHeaUdFyAEtvCql/96kbMI9gXZzLkyO6t2LVgc+Aus9AKZURT1/XnU1z+WFiOTdOQHIgjwbK4DBeHimKakdnjOr9vmCvHq1MSIkP4G1nON6s8M02AzFpGqYuCt07V3Z4CiDQequzRh9ILy HqCitFYMj/dhrfSPC+ByYfe6crvPooiJpGUFZMBTbobNse86vspWQkyq0UkEmQYaTXZEdhzdP0QsJESwm+2x5kvA/heCZc3Yv5ZpdECHLwdGAiXhbq7VFXkOqnxWvDFrCfEBgarl74KoWpOyYx6suAn0yAZSbMKo0l7AHl+/kiLzdzuuTBekOBxBxfBejEdA5tDiqp+QCxLoFO55edXDd8TVBEWhUbhH62U3dHE1g6vpWuX2mGcSeYzZn2cTlGVaqQlLB2bSgUpQO8RMZcQso58aEwLxQLODE1W97HtSQA7unjxWv/9sjzHTxjIWESpYifxl1u9IV1bDT/uPY2/lABEG/cItlP8q63AyTqiyhBVh/cgXG0x1tQ6XWNgzbFby9BZVWlcw4RGx9jf1lu7SQ/qxsx0NyJgRqTDK0PlDiIuVAE8yR9gyaiAiFnfMrrWhf0Cm2cU9jGzKQf9bsAlg9urCLDiL0dBNs6hHjaKHQDMtgCcj38OiwMn+voCxooCyHsxtdJF3/yWFuR2e8eVN0Syc4mauHFFarc1tDFoj8L7P2eLPOAC9xEZlWTiXCkbT95MnNma24ZkxViSkvgItRl4Ov8N2jREsgHWL4WprZz/apF7kV9fja0IJK3isWvxCIxVHaxhmqjIteT8A== Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id cw4-20020a170906c78400b00a365d955448si2123405ejb.476.2024.02.01.08.40.46; Thu, 01 Feb 2024 08:40:47 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@momtchev.com header.s=rsa header.b=M9ffTCni; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id DA5EC68D0E1; Thu, 1 Feb 2024 18:40:42 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from orel.garga.net (orel.garga.net [51.15.4.221]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 32FB368B466 for ; Thu, 1 Feb 2024 18:40:36 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=momtchev.com; s=rsa; h=Subject:From:To:MIME-Version:Date:Message-ID: Content-Type:Sender:Reply-To:Cc: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=Y9WKF2M8IeUbku9PVXMvjY5gB8ma1RARtMhrScy8IkA=; b=M9ffTCniYcN3srma5TSMM79ZkQ Aitathw6SCJ9d/i0glkMVc8oTJhP5BOg7ZsHs+MPZXjTbl4V5as/S03ToRBNjDPQAsMA7SjV8p7m9 50pL9evNq96BdO+uKhivzMOblZh52G3TpqP1QWNMPBydF+uW36jTPCRlz2H2x2irB4JdSTGCYMs6+ tHKzjttC5GrogZsLaxDWVFvklQha5TWEoPOdssAg0XJBjDjB0Opv0ohVMjQbMOwcBqYaY5cWb794F ugmFGqM+PpWYPojHvaGdavL7tIFERZ3AaROyWx4pzt0Z19b1ZHAzuMopYzMaooLprW/XCd0vAK+up aJjGLltg==; Received: from [78.203.245.37] (helo=[192.168.0.133]) by orel.garga.net with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1rVa77-00Ewb8-2E for ffmpeg-devel@ffmpeg.org; Thu, 01 Feb 2024 17:40:35 +0100 Message-ID: <46c3739b-3edd-45d3-8c5a-262ce6cc03e4@momtchev.com> Date: Thu, 1 Feb 2024 17:40:33 +0100 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Content-Language: en-US To: ffmpeg-devel@ffmpeg.org From: Momtchil Momtchev X-Spam-score: -2.9 X-Spam-bar: -- Subject: [FFmpeg-devel] Implement RTMP flow control and proper (or least better) closing X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 91/mxW1bkAEM https://trac.ffmpeg.org/ticket/10838 Hello, This small PR implements:     * RTMP flow control on the server side - now the server will stop sending data if it has filled the window and the client hasn't ACKed     * Improves the tear-down by not returning an error when the server closes the connection (which seems to be the usual method of signalling the end of the connection)     * Fixes the client ignoring the last frames diff --git a/doc/protocols.texi b/doc/protocols.texi index f54600b846..642cde962a 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -948,6 +948,9 @@ URL to player swf file, compute hash/size automatically. @item rtmp_tcurl URL of the target stream. Defaults to proto://host[:port]/app. +@item rtmp_window +Size of the RTMP window. Defaults to 2500000. + @item tcp_nodelay=@var{1|0} Set TCP_NODELAY to disable Nagle's algorithm. Default value is 0. diff --git a/libavformat/rtmppkt.c b/libavformat/rtmppkt.c index a602bf6a96..3fcb4e8f14 100644 --- a/libavformat/rtmppkt.c +++ b/libavformat/rtmppkt.c @@ -159,7 +159,10 @@ int ff_rtmp_packet_read(URLContext *h, RTMPPacket *p, { uint8_t hdr; - if (ffurl_read(h, &hdr, 1) != 1) + int ret = ffurl_read(h, &hdr, 1); + if (ret == AVERROR_EOF) + return AVERROR_EOF; + if (ret != 1) return AVERROR(EIO); return ff_rtmp_packet_read_internal(h, p, chunk_size, prev_pkt, diff --git a/libavformat/rtmpproto.c b/libavformat/rtmpproto.c index 98718bc6da..6aed9db3b0 100644 --- a/libavformat/rtmpproto.c +++ b/libavformat/rtmpproto.c @@ -97,6 +97,8 @@ typedef struct RTMPContext { uint32_t receive_report_size; ///< number of bytes after which we should report the number of received bytes to the peer uint64_t bytes_read; ///< number of bytes read from server uint64_t last_bytes_read; ///< number of bytes read last reported to server + uint64_t bytes_sent; ///< number of bytes sent to the client + uint64_t last_bytes_sent; ///< number of bytes last acknowledged by the client uint32_t last_timestamp; ///< last timestamp received in a packet int skip_bytes; ///< number of bytes to skip from the input FLV stream in the next write call int has_audio; ///< presence of audio data @@ -251,6 +253,7 @@ static int rtmp_send_packet(RTMPContext *rt, RTMPPacket *pkt, int track) ret = ff_rtmp_packet_write(rt->stream, pkt, rt->out_chunk_size, &rt->prev_pkt[1], &rt->nb_prev_pkt[1]); + rt->bytes_sent += pkt->size; fail: ff_rtmp_packet_destroy(pkt); return ret; @@ -472,7 +475,8 @@ static int read_connect(URLContext *s, RTMPContext *rt) ff_rtmp_packet_destroy(&pkt); return AVERROR_UNKNOWN; } else if (pkt.type == RTMP_PT_BYTES_READ) { - av_log(s, AV_LOG_TRACE, "received acknowledgement\n"); + rt->last_bytes_read = AV_RB32(pkt.data); + av_log(s, AV_LOG_TRACE, "received acknowledgement (%lu)\n", rt->last_bytes_read); } else if (pkt.type == RTMP_PT_WINDOW_ACK_SIZE) { if ((ret = handle_window_ack_size(s, &pkt)) < 0) { ff_rtmp_packet_destroy(&pkt); @@ -2344,7 +2348,8 @@ static int rtmp_parse_result(URLContext *s, RTMPContext *rt, RTMPPacket *pkt) switch (pkt->type) { case RTMP_PT_BYTES_READ: - av_log(s, AV_LOG_TRACE, "received bytes read report\n"); + rt->last_bytes_sent = AV_RB32(pkt->data); + av_log(s, AV_LOG_TRACE, "received bytes read report (%lu)\n", rt->last_bytes_sent); break; case RTMP_PT_CHUNK_SIZE: if ((ret = handle_chunk_size(s, pkt)) < 0) @@ -2455,6 +2460,8 @@ static int get_packet(URLContext *s, int for_header) &rt->nb_prev_pkt[0])) <= 0) { if (ret == 0) { return AVERROR(EAGAIN); + } else if (ret == AVERROR_EOF) { + return AVERROR_EOF; } else { return AVERROR(EIO); } @@ -2468,7 +2475,7 @@ static int get_packet(URLContext *s, int for_header) av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n"); if ((ret = gen_bytes_read(s, rt, rpkt.timestamp + 1)) < 0) { ff_rtmp_packet_destroy(&rpkt); - return ret; + av_log(s, AV_LOG_DEBUG, "Failed ACKing to the server, connection finished?\n"); } rt->last_bytes_read = rt->bytes_read; } @@ -2835,11 +2842,12 @@ reconnect: rt->receive_report_size = 1048576; rt->bytes_read = 0; + rt->bytes_sent = 0; rt->has_audio = 0; rt->has_video = 0; rt->received_metadata = 0; rt->last_bytes_read = 0; - rt->max_sent_unacked = 2500000; + rt->last_bytes_sent = 0; rt->duration = 0; av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n", @@ -3095,8 +3103,14 @@ static int rtmp_write(URLContext *s, const uint8_t *buf, int size) return size; rt->flv_nb_packets = 0; - /* set stream into nonblocking mode */ - rt->stream->flags |= AVIO_FLAG_NONBLOCK; + /* it is time to start throttling? */ + if (rt->bytes_sent < rt->last_bytes_sent + rt->max_sent_unacked) { + /* set stream into nonblocking mode */ + rt->stream->flags |= AVIO_FLAG_NONBLOCK; + } else { + av_log(s, AV_LOG_DEBUG, "Throttling, sent %lu bytes, client has acknowledged %lu bytes\n", rt->bytes_sent, + rt->last_bytes_sent); + } /* try to read one byte from the stream */ ret = ffurl_read(rt->stream, &c, 1); @@ -3139,6 +3153,7 @@ static const AVOption rtmp_options[] = { {"rtmp_flush_interval", "Number of packets flushed in the same request (RTMPT only).", OFFSET(flush_interval), AV_OPT_TYPE_INT, {.i64 = 10}, 0, INT_MAX, ENC}, {"rtmp_enhanced_codecs", "Specify the codec(s) to use in an enhanced rtmp live stream", OFFSET(enhanced_codecs), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, ENC}, {"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {.i64 = -2}, INT_MIN, INT_MAX, DEC, "rtmp_live"}, + {"rtmp_window", "RTMP window size (server only).", OFFSET(max_sent_unacked), AV_OPT_TYPE_INT, {.i64 = 2500000}, INT_MIN, INT_MAX, DEC, "rtmp_window"}, {"any", "both", 0, AV_OPT_TYPE_CONST, {.i64 = -2}, 0, 0, DEC, "rtmp_live"}, {"live", "live stream", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, DEC, "rtmp_live"}, {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, DEC, "rtmp_live"},