Skip to content

Commit 29849d3

Browse files
committed
Update 5.2.1 - See changelog.txt for details
1 parent fd4601f commit 29849d3

12 files changed

+484
-64
lines changed

Installer.iss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
; =====================================================
2-
; StreamTweak v5.2.0 - GitHub Release Installer
2+
; StreamTweak v5.2.1 - GitHub Release Installer
33
; =====================================================
44
#define MyAppName "StreamTweak"
5-
#define MyAppVersion "5.2.0"
5+
#define MyAppVersion "5.2.1"
66
#define MyAppPublisher "FoggyBytes"
77
#define MyAppExeName "StreamTweak.exe"
88
#define MyAppURL "https://github.com/FoggyBytes/StreamTweak"

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,20 @@ StreamLight and StreamTweak are designed to work together, giving you full contr
6666

6767
### 📚 System Info & Diagnostics
6868
- **Logs Tab:** Full session history — every streaming session is recorded regardless of whether NIC throttle was applied, with NIC Throttle (Yes/No), Original NIC Speed, and timestamped date including year.
69-
- **Session quality report:** click the chart button on any session row to open a full-panel telemetry overlay with CLIENT stats (FPS, Drops, RTT, Decode, Bitrate), HOST stats (GPU, Encoder, GPU Temp, CPU, Net TX), four time-series sparkline charts with axes and scale labels, and a quality grade badge (High / Medium / Low).
69+
- **Session quality report:** click the chart button on any session row to open a full-panel telemetry overlay with CLIENT stats (Jitter, Drops, RTT, Decode, Bitrate), HOST stats (GPU, Encoder, GPU Temp, CPU, Net TX), four time-series sparkline charts (RTT, Frame Drops, Bitrate, Decode Latency) with axes and scale labels, and a quality grade badge (Excellent / Good / Poor).
7070
- **Home Dashboard:** Version info, GitHub link, license badge, donation button, and a real-time status overview for all six managed settings — all in the Home panel.
7171

72-
## ✨ What's New in Version 5.2.0 — The "Session Quality Update"
72+
## ✨ What's New in Version 5.2.1 — The "Chart Update"
7373

74-
* **Session quality report —** click the chart button on any session row in the Logs tab to open a full-panel telemetry overlay; CLIENT stats (FPS, Drops, RTT, Decode, Bitrate) and HOST stats (GPU, Encoder, GPU Temp, CPU, Net TX) appear side by side, followed by four time-series sparkline charts and a quality grade badge
75-
* **Four sparkline charts —** FPS, RTT (ms), Drops, and Bitrate (Mbps) rendered with Cartesian axes, Y-scale labels (max/mid/min), and X-axis time labels; the section is clearly labeled CLIENT (StreamLight) to distinguish client-side data from host metrics
76-
* **Quality grade system —** sessions are graded Excellent, Accettable, or Poor based on drop rate, RTT, and GPU encoder utilization; FPS is excluded because static screens and loading screens produce artificially low frame counts that do not reflect actual streaming quality
74+
* **Session telemetry charts revised —** the four sparklines now show RTT (ms), Frame Drops, Bitrate (Mbps), and Decode Latency (ms); FPS has been replaced by Decode Latency because FPS collapses to near-zero on static screens regardless of streaming quality, making it an unreliable indicator; the four retained metrics jointly cover the full quality picture: RTT (network latency), Frame Drops (packet loss), Bitrate (encoder output and bandwidth headroom), Decode Latency (client hardware load)
75+
* **Jitter in session stats —** RTT variance is now collected per sample from StreamLight 2.1.1+ and stored as jitter_avg / jitter_max per session; displayed in the CLIENT stats panel alongside RTT
76+
* **SparklineControl improvements —** bucket-average downsampling for long sessions; chart label rendered above the polyline with a semi-transparent background for readability at all times
77+
* **Glossary tab —** new dedicated tab in Settings with definitions of all technical terms used across StreamTweak and StreamLight, grouped by category in a terminal-style panel
78+
79+
### Previously in 5.2.0 — The "Session Quality Update"
80+
81+
* **Session quality report —** click the chart button on any session row in the Logs tab to open a full-panel telemetry overlay; CLIENT stats and HOST stats appear side by side, followed by four time-series sparkline charts and a quality grade badge
82+
* **Quality grade system —** sessions are graded Excellent, Good, or Poor based on drop rate, RTT, and GPU encoder utilization
7783
* **Animated overlay —** the telemetry panel opens and closes with a smooth CubicEase animation (200 ms / 150 ms) and covers the full Logs panel with an opaque background
7884

7985
### Previously in 5.1.1 — The "Code Quality Update"

StreamTweak/App.xaml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -798,11 +798,11 @@ private async void HandleAutoStreamStop(string endReason = "User")
798798
string? sessionId = SessionLogger.ActiveSessionId;
799799
if (sessionId != null)
800800
{
801-
var (stats, fpsSeries, rttSeries, dropsSeries, bitrateSeries) = _telemetryAccumulator.Finalize();
801+
var (stats, rttSeries, dropsSeries, bitrateSeries, decodeSeries) = _telemetryAccumulator.Finalize();
802802
if (stats.SampleCount >= 2)
803803
{
804804
var grade = QualityGradeCalculator.Evaluate(stats, _telemetryAccumulator.TargetFps);
805-
SessionLogger.UpdateSessionTelemetry(sessionId, stats, grade, fpsSeries, rttSeries, dropsSeries, bitrateSeries);
805+
SessionLogger.UpdateSessionTelemetry(sessionId, stats, grade, rttSeries, dropsSeries, bitrateSeries, decodeSeries);
806806
}
807807
_telemetryAccumulator.Reset();
808808
}

StreamTweak/ChartDetailWindow.xaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<Window x:Class="StreamTweak.ChartDetailWindow"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:local="clr-namespace:StreamTweak"
5+
MinWidth="600" MinHeight="400"
6+
Width="920" Height="580"
7+
Background="#FF202020"
8+
WindowStyle="SingleBorderWindow"
9+
ResizeMode="CanResizeWithGrip"
10+
WindowStartupLocation="CenterOwner"
11+
Loaded="Window_Loaded">
12+
<Grid Margin="20,16,20,20">
13+
<Grid.RowDefinitions>
14+
<RowDefinition Height="Auto"/>
15+
<RowDefinition Height="4"/>
16+
<RowDefinition Height="Auto"/>
17+
<RowDefinition Height="14"/>
18+
<RowDefinition Height="*"/>
19+
</Grid.RowDefinitions>
20+
21+
<TextBlock x:Name="MetricLabel" Grid.Row="0"
22+
FontSize="15" FontWeight="SemiBold" Foreground="White"
23+
HorizontalAlignment="Center"/>
24+
25+
<TextBlock x:Name="SubtitleLabel" Grid.Row="2"
26+
FontSize="11"
27+
Foreground="#99FFFFFF"
28+
HorizontalAlignment="Center"/>
29+
30+
<Border Grid.Row="4"
31+
BorderBrush="#33FFFFFF" BorderThickness="1" CornerRadius="6"
32+
Padding="6,4">
33+
<local:SparklineControl x:Name="DetailChart"/>
34+
</Border>
35+
</Grid>
36+
</Window>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System.Collections.Generic;
2+
using System.Runtime.InteropServices;
3+
using System.Windows;
4+
using System.Windows.Interop;
5+
using System.Windows.Media;
6+
7+
namespace StreamTweak
8+
{
9+
public partial class ChartDetailWindow : Window
10+
{
11+
[DllImport("DwmApi")]
12+
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, int[] attrValue, int attrSize);
13+
private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
14+
15+
public ChartDetailWindow(IReadOnlyList<float> data, string label, Brush color, SessionEntry session)
16+
{
17+
InitializeComponent();
18+
19+
Title = $"{label} — StreamTweak";
20+
MetricLabel.Text = label;
21+
22+
int secs = data.Count;
23+
string dur = secs >= 3600
24+
? $"{secs / 3600}h {(secs % 3600) / 60}m {secs % 60:00}s"
25+
: secs >= 60
26+
? $"{secs / 60}m {secs % 60:00}s"
27+
: $"{secs}s";
28+
SubtitleLabel.Text = $"{session.StartTimeDisplay} · {dur}";
29+
30+
DetailChart.Points = data;
31+
DetailChart.Label = label;
32+
DetailChart.StrokeBrush = color;
33+
}
34+
35+
private void Window_Loaded(object sender, RoutedEventArgs e)
36+
{
37+
var helper = new WindowInteropHelper(this);
38+
DwmSetWindowAttribute(helper.Handle, DWMWA_USE_IMMERSIVE_DARK_MODE, new[] { 1 }, sizeof(int));
39+
}
40+
}
41+
}

StreamTweak/SessionLogger.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public class SessionEntry
3636
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
3737
public List<float>? BitrateTimeSeries { get; set; }
3838

39+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
40+
public List<float>? DecodeTimeSeries { get; set; }
41+
3942
// ── Display properties ────────────────────────────────────────────────
4043

4144
[JsonIgnore]
@@ -61,7 +64,7 @@ public string TelemetryDurationDisplay
6164
{
6265
get
6366
{
64-
int secs = FpsTimeSeries?.Count ?? RttTimeSeries?.Count ?? 0;
67+
int secs = RttTimeSeries?.Count ?? DecodeTimeSeries?.Count ?? 0;
6568
if (secs == 0) return DurationDisplay;
6669
return secs >= 60
6770
? $"{secs / 60}m {secs % 60}s"
@@ -142,10 +145,10 @@ public static void UpdateSessionTelemetry(
142145
string sessionId,
143146
SessionQualityStats stats,
144147
QualityGrade grade,
145-
List<float> fpsSeries,
146148
List<float> rttSeries,
147149
List<float> dropsSeries,
148-
List<float> bitrateSeries)
150+
List<float> bitrateSeries,
151+
List<float> decodeSeries)
149152
{
150153
try
151154
{
@@ -155,10 +158,10 @@ public static void UpdateSessionTelemetry(
155158

156159
entry.QualityStats = stats;
157160
entry.Grade = grade;
158-
entry.FpsTimeSeries = fpsSeries.Count > 0 ? fpsSeries : null;
159161
entry.RttTimeSeries = rttSeries.Count > 0 ? rttSeries : null;
160162
entry.DropsTimeSeries = dropsSeries.Count > 0 ? dropsSeries : null;
161163
entry.BitrateTimeSeries = bitrateSeries.Count > 0 ? bitrateSeries : null;
164+
entry.DecodeTimeSeries = decodeSeries.Count > 0 ? decodeSeries : null;
162165
Save(sessions);
163166
}
164167
catch { }

StreamTweak/SessionTelemetry.cs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public sealed class ClientSample
2424
[JsonPropertyName("drops")] public int Drops { get; set; }
2525
[JsonPropertyName("rtt_avg")] public float RttAvg { get; set; }
2626
[JsonPropertyName("rtt_max")] public float RttMax { get; set; }
27+
[JsonPropertyName("jitter_avg")] public float JitterAvg { get; set; }
28+
[JsonPropertyName("jitter_max")] public float JitterMax { get; set; }
2729
[JsonPropertyName("decode_ms")] public float DecodeMs { get; set; }
2830
[JsonPropertyName("bitrate_mbps")] public float BitrateAvgMbps { get; set; }
2931
}
@@ -45,6 +47,8 @@ public sealed class SessionQualityStats
4547
public float DropRatePct { get; set; }
4648
public float RttAvgMs { get; set; }
4749
public float RttMaxMs { get; set; }
50+
public float JitterAvgMs { get; set; }
51+
public float JitterMaxMs { get; set; }
4852
public float DecodeAvgMs { get; set; }
4953
public float BitrateAvgMbps { get; set; }
5054

@@ -74,6 +78,8 @@ public sealed class TelemetryAccumulator
7478
private readonly List<int> _dropSamples = new();
7579
private readonly List<float> _rttAvgSamples = new();
7680
private readonly List<float> _rttMaxSamples = new();
81+
private readonly List<float> _jitterSamples = new();
82+
private readonly List<float> _jitterMaxSamples = new();
7783
private readonly List<float> _decodeSamples = new();
7884
private readonly List<float> _bitrateSamples = new();
7985

@@ -89,6 +95,7 @@ public sealed class TelemetryAccumulator
8995
private readonly List<float> _rttTimeSeries = new();
9096
private readonly List<float> _dropsTimeSeries = new();
9197
private readonly List<float> _bitrateTimeSeries = new();
98+
private readonly List<float> _decodeTimeSeries = new();
9299

93100
private int _targetFps;
94101
private long _totalFrames;
@@ -109,6 +116,8 @@ public void AddBatch(ClientBatch batch, HostMetricsSample host)
109116
_dropSamples.Add(s.Drops);
110117
_rttAvgSamples.Add(s.RttAvg);
111118
_rttMaxSamples.Add(s.RttMax);
119+
_jitterSamples.Add(s.JitterAvg);
120+
_jitterMaxSamples.Add(s.JitterMax);
112121
_decodeSamples.Add(s.DecodeMs);
113122
_bitrateSamples.Add(s.BitrateAvgMbps);
114123

@@ -119,6 +128,7 @@ public void AddBatch(ClientBatch batch, HostMetricsSample host)
119128
_rttTimeSeries.Add(s.RttAvg);
120129
_dropsTimeSeries.Add(s.Drops);
121130
_bitrateTimeSeries.Add(s.BitrateAvgMbps);
131+
_decodeTimeSeries.Add(s.DecodeMs);
122132
}
123133

124134
// Host: un campione per batch (snapshot al momento della ricezione)
@@ -130,7 +140,7 @@ public void AddBatch(ClientBatch batch, HostMetricsSample host)
130140
}
131141
}
132142

133-
public (SessionQualityStats Stats, List<float> FpsSeries, List<float> RttSeries, List<float> DropsSeries, List<float> BitrateSeries) Finalize()
143+
public (SessionQualityStats Stats, List<float> RttSeries, List<float> DropsSeries, List<float> BitrateSeries, List<float> DecodeSeries) Finalize()
134144
{
135145
lock (_lock)
136146
{
@@ -139,15 +149,17 @@ public void AddBatch(ClientBatch batch, HostMetricsSample host)
139149
var stats = new SessionQualityStats
140150
{
141151
SampleCount = count,
142-
FpsAvg = count > 0 ? _fpsAvgSamples.Average() : 0f,
143-
FpsMin = count > 0 ? _fpsMinSamples.Min() : 0,
152+
FpsAvg = count > 0 ? _fpsAvgSamples.Average() : 0f,
153+
FpsMin = count > 0 ? _fpsMinSamples.Min() : 0,
144154
DropRatePct = _totalFrames > 0
145155
? (float)_totalDrops / _totalFrames * 100f
146156
: 0f,
147-
RttAvgMs = count > 0 ? _rttAvgSamples.Average() : 0f,
148-
RttMaxMs = count > 0 ? _rttMaxSamples.Max() : 0f,
149-
DecodeAvgMs = count > 0 ? _decodeSamples.Average() : 0f,
150-
BitrateAvgMbps = count > 0 ? _bitrateSamples.Average() : 0f,
157+
RttAvgMs = count > 0 ? _rttAvgSamples.Average() : 0f,
158+
RttMaxMs = count > 0 ? _rttMaxSamples.Max() : 0f,
159+
JitterAvgMs = count > 0 ? _jitterSamples.Average() : 0f,
160+
JitterMaxMs = count > 0 ? _jitterMaxSamples.Max() : 0f,
161+
DecodeAvgMs = count > 0 ? _decodeSamples.Average() : 0f,
162+
BitrateAvgMbps = count > 0 ? _bitrateSamples.Average() : 0f,
151163

152164
HostGpuAvg = _gpuSamples.Count > 0 ? (int)_gpuSamples.Average() : -1,
153165
HostGpuPeak = _gpuSamples.Count > 0 ? _gpuSamples.Max() : -1,
@@ -161,10 +173,10 @@ public void AddBatch(ClientBatch batch, HostMetricsSample host)
161173
};
162174

163175
return (stats,
164-
new List<float>(_fpsTimeSeries),
165176
new List<float>(_rttTimeSeries),
166177
new List<float>(_dropsTimeSeries),
167-
new List<float>(_bitrateTimeSeries));
178+
new List<float>(_bitrateTimeSeries),
179+
new List<float>(_decodeTimeSeries));
168180
}
169181
}
170182

@@ -177,6 +189,8 @@ public void Reset()
177189
_dropSamples.Clear();
178190
_rttAvgSamples.Clear();
179191
_rttMaxSamples.Clear();
192+
_jitterSamples.Clear();
193+
_jitterMaxSamples.Clear();
180194
_decodeSamples.Clear();
181195
_bitrateSamples.Clear();
182196
_gpuSamples.Clear();
@@ -188,6 +202,7 @@ public void Reset()
188202
_rttTimeSeries.Clear();
189203
_dropsTimeSeries.Clear();
190204
_bitrateTimeSeries.Clear();
205+
_decodeTimeSeries.Clear();
191206
_targetFps = 0;
192207
_totalFrames = 0;
193208
_totalDrops = 0;

0 commit comments

Comments
 (0)