mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
.
This commit is contained in:
205
grd-rdp-gfx-framerate-log.c
Normal file
205
grd-rdp-gfx-framerate-log.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Pascal Nowack
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "grd-rdp-gfx-framerate-log.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "grd-rdp-frame-stats.h"
|
||||
|
||||
#define STABLE_ENCODING_RATE_THRESHOLD_PERCENT 0.8
|
||||
|
||||
#define MIN_N_ENC_RATES 4
|
||||
#define MIN_ENC_RATE_THRESHOLD 5
|
||||
#define MIN_VIDEO_FRAMERATE 24
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t enc_rate;
|
||||
int64_t tracked_time_us;
|
||||
} EncRateInfo;
|
||||
|
||||
struct _GrdRdpGfxFramerateLog
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GMutex framerate_log_mutex;
|
||||
GQueue *enc_rates;
|
||||
uint32_t last_ack_rate;
|
||||
|
||||
uint32_t missing_dual_frame_acks;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GrdRdpGfxFramerateLog, grd_rdp_gfx_framerate_log,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
clear_old_enc_rates (GrdRdpGfxFramerateLog *framerate_log)
|
||||
{
|
||||
EncRateInfo *enc_rate_info;
|
||||
int64_t current_time_us;
|
||||
|
||||
current_time_us = g_get_monotonic_time ();
|
||||
while ((enc_rate_info = g_queue_peek_head (framerate_log->enc_rates)) &&
|
||||
current_time_us - enc_rate_info->tracked_time_us >= G_USEC_PER_SEC >> 1)
|
||||
g_free (g_queue_pop_head (framerate_log->enc_rates));
|
||||
}
|
||||
|
||||
void
|
||||
grd_rdp_gfx_framerate_log_notify_frame_stats (GrdRdpGfxFramerateLog *framerate_log,
|
||||
GrdRdpFrameStats *frame_stats)
|
||||
{
|
||||
EncRateInfo *enc_rate_info;
|
||||
|
||||
enc_rate_info = g_new0 (EncRateInfo, 1);
|
||||
enc_rate_info->enc_rate = grd_rdp_frame_stats_get_enc_rate (frame_stats);
|
||||
enc_rate_info->tracked_time_us = g_get_monotonic_time ();
|
||||
|
||||
g_mutex_lock (&framerate_log->framerate_log_mutex);
|
||||
clear_old_enc_rates (framerate_log);
|
||||
g_queue_push_tail (framerate_log->enc_rates, enc_rate_info);
|
||||
|
||||
framerate_log->last_ack_rate = grd_rdp_frame_stats_get_ack_rate (frame_stats);
|
||||
framerate_log->missing_dual_frame_acks =
|
||||
grd_rdp_frame_stats_get_missing_dual_frame_acks (frame_stats);
|
||||
g_mutex_unlock (&framerate_log->framerate_log_mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
cmp_enc_rates (gconstpointer a,
|
||||
gconstpointer b,
|
||||
gpointer user_data)
|
||||
{
|
||||
const EncRateInfo *enc_rate_info_a = a;
|
||||
const EncRateInfo *enc_rate_info_b = b;
|
||||
int64_t enc_rate_a = enc_rate_info_a->enc_rate;
|
||||
int64_t enc_rate_b = enc_rate_info_b->enc_rate;
|
||||
|
||||
return enc_rate_a - enc_rate_b;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
has_stable_enc_rate (double enc_rate_min,
|
||||
double enc_rate_median)
|
||||
{
|
||||
return enc_rate_min >=
|
||||
floor (enc_rate_median * STABLE_ENCODING_RATE_THRESHOLD_PERCENT);
|
||||
}
|
||||
|
||||
gboolean
|
||||
grd_rdp_gfx_framerate_log_should_avoid_dual_frame (GrdRdpGfxFramerateLog *framerate_log)
|
||||
{
|
||||
g_autoptr (GMutexLocker) locker = NULL;
|
||||
g_autoptr (GQueue) tmp = NULL;
|
||||
EncRateInfo *enc_rate_info;
|
||||
uint32_t n_enc_rates;
|
||||
uint32_t enc_rate_min;
|
||||
uint32_t enc_rate_quartile3;
|
||||
uint32_t enc_rate_median;
|
||||
uint32_t last_ack_rate;
|
||||
uint32_t missing_dual_frame_acks;
|
||||
uint32_t i;
|
||||
|
||||
locker = g_mutex_locker_new (&framerate_log->framerate_log_mutex);
|
||||
clear_old_enc_rates (framerate_log);
|
||||
|
||||
n_enc_rates = g_queue_get_length (framerate_log->enc_rates);
|
||||
if (n_enc_rates < MIN_N_ENC_RATES)
|
||||
return FALSE;
|
||||
|
||||
tmp = g_queue_copy (framerate_log->enc_rates);
|
||||
g_queue_sort (tmp, cmp_enc_rates, NULL);
|
||||
|
||||
enc_rate_info = g_queue_peek_head (tmp);
|
||||
enc_rate_min = enc_rate_info->enc_rate;
|
||||
|
||||
for (i = 0; i < n_enc_rates >> 2; ++i)
|
||||
g_queue_pop_tail (tmp);
|
||||
|
||||
enc_rate_info = g_queue_peek_tail (tmp);
|
||||
enc_rate_quartile3 = enc_rate_info->enc_rate;
|
||||
|
||||
for (i = 0; i < n_enc_rates >> 2; ++i)
|
||||
g_queue_pop_tail (tmp);
|
||||
|
||||
enc_rate_info = g_queue_peek_tail (tmp);
|
||||
enc_rate_median = enc_rate_info->enc_rate;
|
||||
|
||||
last_ack_rate = framerate_log->last_ack_rate;
|
||||
missing_dual_frame_acks = framerate_log->missing_dual_frame_acks;
|
||||
g_clear_pointer (&locker, g_mutex_locker_free);
|
||||
|
||||
if (enc_rate_median < MIN_ENC_RATE_THRESHOLD)
|
||||
return FALSE;
|
||||
|
||||
if (enc_rate_median >= MIN_VIDEO_FRAMERATE ||
|
||||
has_stable_enc_rate (enc_rate_min, enc_rate_median))
|
||||
return enc_rate_quartile3 + 3 * missing_dual_frame_acks >= last_ack_rate;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GrdRdpGfxFramerateLog *
|
||||
grd_rdp_gfx_framerate_log_new (void)
|
||||
{
|
||||
return g_object_new (GRD_TYPE_RDP_GFX_FRAMERATE_LOG, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_gfx_framerate_log_dispose (GObject *object)
|
||||
{
|
||||
GrdRdpGfxFramerateLog *framerate_log = GRD_RDP_GFX_FRAMERATE_LOG (object);
|
||||
|
||||
if (framerate_log->enc_rates)
|
||||
{
|
||||
g_queue_free_full (framerate_log->enc_rates, g_free);
|
||||
framerate_log->enc_rates = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (grd_rdp_gfx_framerate_log_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_gfx_framerate_log_finalize (GObject *object)
|
||||
{
|
||||
GrdRdpGfxFramerateLog *framerate_log = GRD_RDP_GFX_FRAMERATE_LOG (object);
|
||||
|
||||
g_mutex_clear (&framerate_log->framerate_log_mutex);
|
||||
|
||||
G_OBJECT_CLASS (grd_rdp_gfx_framerate_log_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_gfx_framerate_log_init (GrdRdpGfxFramerateLog *framerate_log)
|
||||
{
|
||||
framerate_log->enc_rates = g_queue_new ();
|
||||
|
||||
g_mutex_init (&framerate_log->framerate_log_mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
grd_rdp_gfx_framerate_log_class_init (GrdRdpGfxFramerateLogClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = grd_rdp_gfx_framerate_log_dispose;
|
||||
object_class->finalize = grd_rdp_gfx_framerate_log_finalize;
|
||||
}
|
||||
Reference in New Issue
Block a user