From cbc548d3843404f95fab0d685cb6e32de70656a2 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Mon, 18 Jul 2022 15:09:00 +0200 Subject: [PATCH] Added+use dynamic rate limiter Added a rate limiter that occasionally allows excessive frames. It permits using 1.5x frame time for frame 2x bigger that moving average if 4 normal frames (using .75x frame time) were emitted inbetween. This mode is now default (for video, audio doesn't use rate limiter). --- src/host.h | 6 ++++-- src/main.cpp | 6 ++++-- src/transmit.cpp | 33 ++++++++++++++++++++++++++++----- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/host.h b/src/host.h index 0527db239..78f184c74 100644 --- a/src/host.h +++ b/src/host.h @@ -117,8 +117,10 @@ int get_audio_delay(void); void set_audio_delay(int val); #define RATE_UNLIMITED 0 -#define RATE_AUTO (-1) -#define RATE_DEFAULT (-2) +#define RATE_AUTO (-1) ///< spread packets evenly across frame time (currently 3/4) +#define RATE_DYNAMIC (-2) ///< same as @ref RATE_AUTO but occasional excess frame allowed +#define RATE_MIN RATE_DYNAMIC +#define RATE_DEFAULT (-3) ///< imaginary value, must not be passed to trasmit module #define RATE_FLAG_FIXED_RATE (1ll<<62ll) ///< use the bitrate as fixed, not capped struct init_data; diff --git a/src/main.cpp b/src/main.cpp index 796db7f2d..e04358176 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -505,6 +505,7 @@ static void *capture_thread(void *arg) static bool parse_bitrate(char *optarg, long long int *bitrate) { map bitrate_spec_map = { { "auto", RATE_AUTO }, + { "dynamic", RATE_DYNAMIC }, { "unlimited", RATE_UNLIMITED }, }; @@ -515,9 +516,10 @@ static bool parse_bitrate(char *optarg, long long int *bitrate) { if (strcmp(optarg, "help") == 0) { const char numeric_pattern[] = "{1-9}{0-9}*[kMG][!][E]"; cout << "Usage:\n" << - "\tuv " << BOLD("-l [auto | unlimited | " << numeric_pattern << "]\n") << + "\tuv " << BOLD("-l [auto | dynamic | unlimited | " << numeric_pattern << "]\n") << "\twhere\n" "\t\t" << BOLD("auto") << " - spread packets across frame time\n" + "\t\t" << BOLD("dynamic") << " - similar to \"auto\" but more relaxed - occasional huge frame can spread 1.5x frame time (default)\n" "\t\t" << BOLD("unlimited") << " - send packets at a wire speed (in bursts)\n" "\t\t" << BOLD(numeric_pattern) << " - send packets at most at specified bitrate\n\n" << BOLD("Notes: ") << "Use an exclamation mark to indicate intentionally very low bitrate. 'E' to use the value as a fixed bitrate, not cap /i. e. even the frames that may be sent at lower bitrate are sent at the nominal bitrate)\n" << @@ -1306,7 +1308,7 @@ static int adjust_params(struct ug_options *opt) { opt->bitrate = opt->bitrate == RATE_DEFAULT ? RATE_UNLIMITED : opt->bitrate; } else { opt->requested_mtu = opt->requested_mtu == 0 ? 1500 : opt->requested_mtu; - opt->bitrate = opt->bitrate == RATE_DEFAULT ? RATE_AUTO : opt->bitrate; + opt->bitrate = opt->bitrate == RATE_DEFAULT ? RATE_DYNAMIC : opt->bitrate; } if((strcmp("none", opt->audio.send_cfg) != 0 || strcmp("none", opt->audio.recv_cfg) != 0) && strcmp(opt->video_protocol, "rtsp") == 0){ diff --git a/src/transmit.cpp b/src/transmit.cpp index 24c761a07..71f168882 100644 --- a/src/transmit.cpp +++ b/src/transmit.cpp @@ -126,6 +126,12 @@ tx_send_base(struct tx *tx, struct video_frame *frame, struct rtp *rtp_session, static bool set_fec(struct tx *tx, const char *fec); static void fec_check_messages(struct tx *tx); +struct rate_limit_dyn { + long avg_frame_size; ///< moving average + long long last_excess; ///< nr of frames last excessive frame was emitted + static constexpr int EXCESS_GAP = 4; ///< minimal gap between excessive frames +}; + struct tx { struct module mod; @@ -150,6 +156,7 @@ struct tx { const struct openssl_encrypt_info *enc_funcs; struct openssl_encrypt *encryption; long long int bitrate; + struct rate_limit_dyn dyn_rate_limit_state; char tmp_packet[RTP_MAX_MTU]; }; @@ -199,6 +206,11 @@ struct tx *tx_init(struct module *parent, unsigned mtu, enum tx_media_type media return NULL; } + if (bitrate < RATE_MIN) { + log_msg(LOG_LEVEL_ERROR, "Invalid bitrate value %lld passed (either positive bitrate or magic values from %d supported)!\n", bitrate, RATE_MIN); + return NULL; + } + tx = (struct tx *) calloc(1, sizeof(struct tx)); if (tx != NULL) { module_init_default(&tx->mod); @@ -331,7 +343,7 @@ static void fec_check_messages(struct tx *tx) } else if (strstr(text, "rate ") == text) { text += strlen("rate "); auto new_rate = unit_evaluate(text); - if (new_rate > 0 || new_rate == RATE_UNLIMITED || new_rate == RATE_AUTO) { + if (new_rate >= RATE_MIN) { tx->bitrate = new_rate; r = new_response(RESPONSE_OK, nullptr); LOG(LOG_LEVEL_NOTICE) << "[Transmit] Bitrate set to: " << text << (new_rate > 0 ? "B" : "") << "\n"; @@ -566,6 +578,17 @@ get_packet_rate(struct tx *tx, struct video_frame *frame, int substream, long pa if (tx->bitrate == RATE_AUTO) { // adaptive (spread packets to 75% frame time) return packet_rate_auto; } + if (tx->bitrate == RATE_DYNAMIC) { + if (frame->tiles[substream].data_len > 2 * tx->dyn_rate_limit_state.avg_frame_size + && tx->dyn_rate_limit_state.last_excess > rate_limit_dyn::EXCESS_GAP) { + packet_rate_auto /= 2; // double packet rate for this frame + tx->dyn_rate_limit_state.last_excess = 0; + } else { + tx->dyn_rate_limit_state.last_excess += 1; + } + tx->dyn_rate_limit_state.avg_frame_size = (9 * tx->dyn_rate_limit_state.avg_frame_size + frame->tiles[substream].data_len) / 10; + return packet_rate_auto; + } long long int bitrate = tx->bitrate & ~RATE_FLAG_FIXED_RATE; int avg_packet_size = frame->tiles[substream].data_len / packet_count; long packet_rate = 1000'000'000L * avg_packet_size * 8 / bitrate; // fixed rate @@ -796,13 +819,14 @@ void audio_tx_send(struct tx* tx, struct rtp *rtp_session, const audio_frame2 * check_symbol_size(fec_symbol_size, tx->mtu - hdrs_len); } - int packet_rate; + int packet_rate = 0; +#if 0 if (tx->bitrate > 0) { //packet_rate = 1000ll * 1000 * 1000 * tx->mtu * 8 / tx->bitrate; packet_rate = 0; } else if (tx->bitrate == RATE_UNLIMITED) { packet_rate = 0; - } else if (tx->bitrate == RATE_AUTO) { + } else if (tx->bitrate == RATE_AUTO || tx->bitrate == RATE_DYNAMIC) { /** * @todo * Following code would actually work but seems to be useless in most of cases (eg. @@ -810,7 +834,6 @@ void audio_tx_send(struct tx* tx, struct rtp *rtp_session, const audio_frame2 * * unexpectable problems (I'd write them here but if I'd expect them they wouldn't * be unexpectable.) */ -#if 0 double time_for_frame = buffer->get_duration() / buffer->get_channel_count(); if (time_for_frame > 0.0) { long long req_bitrate = buffer->get_data_len(channel) * 8 / time_for_frame * tx->mult_count; @@ -820,11 +843,11 @@ void audio_tx_send(struct tx* tx, struct rtp *rtp_session, const audio_frame2 * } else { packet_rate = 0; } -#endif packet_rate = 0; } else { abort(); } +#endif do { if(tx->fec_scheme == FEC_MULT) {